Skip to content

Commit 2a683fd

Browse files
authored
Adding resend api & form (#27)
* Adding resend api & form Signed-off-by: Whelan Boyd <[email protected]> * desktop height Signed-off-by: Whelan Boyd <[email protected]> --------- Signed-off-by: Whelan Boyd <[email protected]>
1 parent d5c8a04 commit 2a683fd

File tree

7 files changed

+305
-38
lines changed

7 files changed

+305
-38
lines changed

package-lock.json

Lines changed: 103 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"react-dom": "^19.2.1",
2222
"react-markdown": "^10.1.0",
2323
"rehype-pretty-code": "^0.14.1",
24+
"resend": "^6.6.0",
2425
"use-scramble": "^2.2.15",
2526
"velite": "^0.3.1"
2627
},

src/app/api/subscribe/route.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { Resend } from "resend";
3+
4+
const resend = new Resend(process.env.RESEND_API_KEY);
5+
6+
export async function POST(request: NextRequest) {
7+
try {
8+
const { email } = await request.json();
9+
10+
if (!email || typeof email !== "string") {
11+
return NextResponse.json({ error: "Email is required" }, { status: 400 });
12+
}
13+
14+
// Basic email validation
15+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
16+
if (!emailRegex.test(email)) {
17+
return NextResponse.json(
18+
{ error: "Invalid email format" },
19+
{ status: 400 }
20+
);
21+
}
22+
23+
const { data, error } = await resend.contacts.create({
24+
email: email.toLowerCase().trim(),
25+
unsubscribed: false
26+
});
27+
28+
if (error) {
29+
// Resend returns an error if the contact already exists,
30+
// but we want to treat this as a success for UX
31+
if (error.message?.includes("already exists")) {
32+
return NextResponse.json({ success: true, alreadySubscribed: true });
33+
}
34+
35+
console.error("Resend error:", error);
36+
return NextResponse.json(
37+
{ error: "Failed to subscribe. Please try again." },
38+
{ status: 500 }
39+
);
40+
}
41+
42+
return NextResponse.json({ success: true, id: data?.id });
43+
} catch (error) {
44+
console.error("Subscribe error:", error);
45+
return NextResponse.json(
46+
{ error: "An unexpected error occurred" },
47+
{ status: 500 }
48+
);
49+
}
50+
}

src/app/blog/page.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { NewsletterForm } from "@/components/newsletter-form";
12
import { getAllPosts } from "@/lib/blog";
23
import { Metadata } from "next";
34
import Link from "next/link";
@@ -24,15 +25,20 @@ export default function BlogPage() {
2425
const posts = getAllPosts();
2526

2627
return (
27-
<div className="min-h-screen w-full bg-background text-white">
28+
<div className="h-full w-full bg-background text-white">
2829
<div className="max-w-5xl mx-auto px-4 py-16">
29-
<div className="mb-12">
30-
<h1 className="text-4xl md:text-6xl font-funnel font-light text-white mb-4">
31-
Blog
32-
</h1>
33-
<p className="text-grey font-mono text-lg">
34-
Updates and insights from the Vortex team
35-
</p>
30+
<div className="mb-12 flex flex-col lg:flex-row lg:items-end lg:justify-between">
31+
<div>
32+
<h1 className="text-4xl md:text-6xl font-funnel font-light text-white mb-4">
33+
Blog
34+
</h1>
35+
<p className="text-grey font-mono text-lg">
36+
Updates and insights from the Vortex team
37+
</p>
38+
</div>
39+
<div className="lg:w-80 lg:flex-shrink-0 sm:pb-2">
40+
<NewsletterForm variant="compact" />
41+
</div>
3642
</div>
3743

3844
{posts.length === 0 ? (

src/components/hero/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { Overlay } from "../overlay";
1919
// Header: 108px (h-[108px]) + 40px (m-10 top) + 24px (mb-6) = 172px
2020
// Footer: 16px (mt-4) + 40px (m-10 bottom) + 84px (md:h-[84px]) + 36px (pt-4 + content) = 176px
2121
// Total: 172px + 176px = 348px
22-
const DESKTOP_HEIGHT = 348;
22+
const DESKTOP_HEIGHT = 334;
2323
// Header: 72px (h-[72px]) + 16px (m-4 top) + 8px (mb-2) = 96px
2424
// Footer: 8px (mt-2) + 16px (m-4 bottom) + 52px (py-4 + content) + 36px (pt-4 + content) = 112px
2525
// Total: 96px + 112px = 208px
@@ -295,7 +295,7 @@ void main() {
295295
}, []);
296296

297297
return (
298-
<div className="w-full overflow-clip h-[calc(100dvh-208px)] md:h-[calc(100dvh-354px)]">
298+
<div className="w-full overflow-clip md:h-[calc(100dvh-208px)] lg:h-[calc(100dvh-332px)]">
299299
<div
300300
ref={containerRef}
301301
className="w-[calc(100%-2rem)] md:w-[calc(100%-5rem)] mx-4 md:mx-10 h-full overflow-clip"

src/components/layout/footer.tsx

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,51 @@ import { Link } from "../link";
66

77
export const Footer = () => {
88
return (
9-
<div className="flex flex-col mx-4 mt-2 md:mt-4 md:mx-10">
10-
<div className="flex justify-center items-center gap-16 dashed-top dashed-bottom after:hidden md:after:block md:h-[84px]">
9+
<div className="flex flex-col m-4 mb-2 md:m-10">
10+
<div className="flex flex-col sm:flex-row justify-center items-center px-4 sm:px-0 gap-4 md:gap-16 dashed-top dashed-bottom after:hidden md:after:block py-4 md:py-0 md:h-[84px]">
1111
<div className="flex items-center h-full lg:px-10">
12-
<div className="flex flex-col md:flex-row items-center gap-2 py-4 md:py-0">
13-
<span className="text-white font-mono text-sm flex items-center gap-1">
14-
Copyright © Vortex a Series of{" "}
15-
<Link href="https://lfprojects.org" target="_blank">
16-
LF Projects, LLC.
17-
</Link>
12+
<div className="flex flex-col md:flex-row items-center gap-2">
13+
<span className="text-sm font-mono text-white items-center gap-2">
14+
Incubating @{" "}
15+
<Image
16+
src={LFLogo}
17+
alt="Linux Foundation logo"
18+
height={10}
19+
className="inline"
20+
/>
1821
</span>
1922
</div>
2023
</div>
21-
<div className="flex items-center h-full lg:px-10 py-4 pb-4 md:py-0">
22-
<span className="text-sm font-mono text-white flex items-center gap-2">
23-
Incubating @{" "}
24-
<Image
25-
src={LFLogo}
26-
alt="Linux Foundation logo"
27-
height={10}
28-
className="inline"
29-
/>
24+
<div className="flex items-center h-full lg:px-10">
25+
<span className="text-white font-mono text-sm items-center gap-1 text-center sm:text-left">
26+
Copyright © Vortex a Series of{" "}
27+
<Link href="https://lfprojects.org" target="_blank">
28+
LF Projects, LLC.
29+
</Link>
30+
</span>
31+
</div>
32+
<div className="flex items-center h-full lg:px-10">
33+
<span className="text-xs font-mono text-white items-center gap-2 flex">
34+
<NextLink href="https://spiraldb.com" target="_blank">
35+
<Image
36+
src={SpiralLogo}
37+
alt="Spiral Logo"
38+
width={15}
39+
height={18}
40+
/>
41+
</NextLink>
42+
Donated by{" "}
43+
<Link href="https://spiraldb.com" target="_blank">
44+
Spiral.
45+
</Link>
3046
</span>
3147
</div>
3248
</div>
33-
<div className="text-center py-8">
34-
<span className="text-xs font-mono text-gray-500 flex items-center justify-center gap-2">
35-
<NextLink href="https://spiraldb.com" target="_blank">
36-
<Image src={SpiralLogo} alt="Spiral Logo" width={15} height={18} />
37-
</NextLink>
38-
Donated by{" "}
39-
<Link href="https://spiraldb.com" target="_blank">
40-
Spiral.
41-
</Link>
42-
</span>
43-
</div>
49+
{/* <div className="py-6 flex justify-center">
50+
<div className="w-full max-w-md">
51+
<NewsletterForm />
52+
</div>
53+
</div> */}
4454
</div>
4555
);
4656
};

0 commit comments

Comments
 (0)