Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test simpler form component and api route for mailchimp #75

Merged
merged 1 commit into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/app/api/newsletter/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ async function subscribeToMailchimp(email) {
};
}

// Detailed logging for unexpected errors
console.error("Unexpected Mailchimp error:", {
message: error.message,
stack: error.stack,
response: error.response,
});

throw error; // Let the main handler catch other errors
}
}
Expand Down
54 changes: 54 additions & 0 deletions src/app/api/subscribe/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { NextResponse } from "next/server";

export async function POST(req) {
try {
const { email } = await req.json();

if (!email || !email.includes("@")) {
return NextResponse.json({ error: "Invalid email address" }, { status: 400 });
}

const MAILCHIMP_API_KEY = process.env.MAILCHIMP_API_KEY;
const MAILCHIMP_LIST_ID = process.env.MAILCHIMP_LIST_ID;
const MAILCHIMP_SERVER_PREFIX = process.env.MAILCHIMP_SERVER_PREFIX;

if (!MAILCHIMP_API_KEY || !MAILCHIMP_LIST_ID || !MAILCHIMP_SERVER_PREFIX) {
return NextResponse.json({ error: "Missing Mailchimp configuration" }, { status: 500 });
}

const url = `https://${MAILCHIMP_SERVER_PREFIX}.api.mailchimp.com/3.0/lists/${MAILCHIMP_LIST_ID}/members`;

const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Basic ${Buffer.from(`anystring:${MAILCHIMP_API_KEY}`).toString("base64")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
email_address: email,
status: "subscribed",
}),
signal: AbortSignal.timeout(5000),
});

if (response.status === 400) {
const data = await response.json();
if (data.title === "Member Exists") {
return NextResponse.json({ error: "Already subscribed" }, { status: 400 });
}
}

const data = await response.json();

if (!response.ok) {
return NextResponse.json({ error: data.detail || "Failed to subscribe" }, { status: response.status });
}

return NextResponse.json({ success: true });
} catch (error) {
if (error.name === "TimeoutError") {
return NextResponse.json({ error: "Request timed out" }, { status: 408 });
}
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}
210 changes: 101 additions & 109 deletions src/components/Footer/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,117 +8,109 @@ import Link from "@/macros/Link/Link";
import Icon from "@/macros/Icons/Icon";
import ArrowLongSVG from "@/macros/SVGs/ArrowLongSVG";
import { isInternalLink } from "@/utils/isInternalLink";
import Newsletter from "@/components/Newsletter/Newsletter";
// import Newsletter from "@/components/Newsletter/Newsletter";
import NewsletterSimple from "@/components/NewsletterSimple/NewsletterSimple";

const Footer = () => {
const columns = footerData();
const copywriteYear = new Date().getFullYear();
return (
<footer
id={"footer"}
className={`bg-black w-full rounded-tl-3xl rounded-tr-3xl py-5 text-white z-30 relative`}
>
<div className={`px-4`}>
<div
className={`h-[225px] md:h-[400px] block w-full rounded-2xl bg-white overflow-hidden z-0 relative`}
>
<VideoPlayer src={"/videos/footer.mp4"} />
</div>
</div>
<div className={`px-4 mb-8`}>
<Container
size={`lg`}
className={`pt-20 pb-6 px-8 lg:pt-[10.25rem] lg:px-[6.25rem] lg:pb-20 rounded-2xl z-10 relative frosted-container -mt-10 lg:-mt-[100px]`}
>
<div className="relative z-10 lg:flex">
<div className="pb-12 lg:pb-0 w-full lg:1/2">
<Heading size="lg" className={`mb-12`}>
Build whatever with Celestia underneath
</Heading>
<Newsletter />
</div>
<div
className={`flex flex-wrap lg:flex-nowrap w-full lg:1/2 lg:gap-6 lg:justify-end ml-auto mr-0`}
>
{columns.map((column, index) => {
return (
<ul
key={index}
className={`block ${
index === columns.length - 1
? "w-full flex flex-row justify-center lg:justify-end gap-4 order-last lg:order-none lg:flex-col lg:w-auto"
: "w-1/2 lg:w-1/5"
} ${index === columns.length - 1 ? "" : "mb-10 lg:mb-0"}`}
>
{column.links.map((link, linkIndex) => {
const isInternal = isInternalLink(link.url);
return (
<li key={`col-${index}-link-${linkIndex}`}>
<Link
href={link.url}
target={isInternal ? "_self" : "_blank"}
rel={isInternal ? "" : "noopener noreferrer"}
className={`flex items-center group mb-4`}
>
{link.icon ? (
<Icon
Icon={<link.icon dark />}
hover
HoverIcon={<link.icon dark className="opacity-50" />}
size="sm"
border={false}
transparentBg
direction="up"
/>
) : (
<>
<Body className={`mr-1`} size={"md"}>
{link.title}
</Body>
<Icon
className={"flex-grow-0 flex-shrink-0"}
border={false}
transparentBg
size={"xs"}
Icon={<div className={"block h-4 w-4"}></div>}
hover
HoverIcon={<ArrowLongSVG />}
direction={isInternal ? "down-right" : "up-right"}
/>
</>
)}
</Link>
</li>
);
})}
</ul>
);
})}
</div>
</div>
</Container>
</div>
<Container
size="lg"
className={`lg:flex lg:justify-between px-4 lg:mb-3`}
>
<div className="text-center lg:text-left w-full mb-2 ">
<Link href={"/privacy"} className={`inline-block mr-2`}>
<Body size="sm">Privacy Policy</Body>
</Link>
·
<Link href={"/tos"} className={`inline-block ml-2`}>
<Body size="sm">Terms of Service</Body>
</Link>
</div>
<div className={`w-full`}>
<Body size="sm" className={`text-center lg:text-right`}>
© {copywriteYear} Celestia Labs
</Body>
</div>
</Container>
</footer>
);
const columns = footerData();
const copywriteYear = new Date().getFullYear();
return (
<footer id={"footer"} className={`bg-black w-full rounded-tl-3xl rounded-tr-3xl py-5 text-white z-30 relative`}>
<div className={`px-4`}>
<div className={`h-[225px] md:h-[400px] block w-full rounded-2xl bg-white overflow-hidden z-0 relative`}>
<VideoPlayer src={"/videos/footer.mp4"} />
</div>
</div>
<div className={`px-4 mb-8`}>
<Container
size={`lg`}
className={`pt-20 pb-6 px-8 lg:pt-[10.25rem] lg:px-[6.25rem] lg:pb-20 rounded-2xl z-10 relative frosted-container -mt-10 lg:-mt-[100px]`}
>
<div className='relative z-10 lg:flex'>
<div className='w-full pb-12 lg:pb-0 lg:1/2'>
<Heading size='lg' className={`mb-12`}>
Build whatever with Celestia underneath
</Heading>
{/* <Newsletter /> */}
<NewsletterSimple />
</div>
<div className={`flex flex-wrap lg:flex-nowrap w-full lg:1/2 lg:gap-6 lg:justify-end ml-auto mr-0`}>
{columns.map((column, index) => {
return (
<ul
key={index}
className={`block ${
index === columns.length - 1
? "w-full flex flex-row justify-center lg:justify-end gap-4 order-last lg:order-none lg:flex-col lg:w-auto"
: "w-1/2 lg:w-1/5"
} ${index === columns.length - 1 ? "" : "mb-10 lg:mb-0"}`}
>
{column.links.map((link, linkIndex) => {
const isInternal = isInternalLink(link.url);
return (
<li key={`col-${index}-link-${linkIndex}`}>
<Link
href={link.url}
target={isInternal ? "_self" : "_blank"}
rel={isInternal ? "" : "noopener noreferrer"}
className={`flex items-center group mb-4`}
>
{link.icon ? (
<Icon
Icon={<link.icon dark />}
hover
HoverIcon={<link.icon dark className='opacity-50' />}
size='sm'
border={false}
transparentBg
direction='up'
/>
) : (
<>
<Body className={`mr-1`} size={"md"}>
{link.title}
</Body>
<Icon
className={"flex-grow-0 flex-shrink-0"}
border={false}
transparentBg
size={"xs"}
Icon={<div className={"block h-4 w-4"}></div>}
hover
HoverIcon={<ArrowLongSVG />}
direction={isInternal ? "down-right" : "up-right"}
/>
</>
)}
</Link>
</li>
);
})}
</ul>
);
})}
</div>
</div>
</Container>
</div>
<Container size='lg' className={`lg:flex lg:justify-between px-4 lg:mb-3`}>
<div className='w-full mb-2 text-center lg:text-left '>
<Link href={"/privacy"} className={`inline-block mr-2`}>
<Body size='sm'>Privacy Policy</Body>
</Link>
·
<Link href={"/tos"} className={`inline-block ml-2`}>
<Body size='sm'>Terms of Service</Body>
</Link>
</div>
<div className={`w-full`}>
<Body size='sm' className={`text-center lg:text-right`}>
© {copywriteYear} Celestia Labs
</Body>
</div>
</Container>
</footer>
);
};

export default Footer;
Loading