feat: add newsletter signup form powered by leads API

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-02-24 22:33:55 +00:00
parent 619d51ecaf
commit 177ddaeecb
2 changed files with 90 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
"use client";
import { useState } from "react";
const API_URL = "https://alezmad-nuc.tail58f5ad.ts.net";
export function NewsletterForm({ project = "cladm" }: { project?: string }) {
const [email, setEmail] = useState("");
const [status, setStatus] = useState<"idle" | "loading" | "ok" | "error">("idle");
const [msg, setMsg] = useState("");
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (!email.trim()) return;
setStatus("loading");
try {
const res = await fetch(`${API_URL}/subscribe`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: email.trim(), project }),
});
const data = await res.json();
if (data.ok) {
setStatus("ok");
setMsg("subscribed");
setEmail("");
} else {
setStatus("error");
setMsg(data.error || "failed");
}
} catch {
setStatus("error");
setMsg("network error");
}
}
if (status === "ok") {
return (
<div className="pixel-border bg-surface p-6 text-center">
<div className="font-[family-name:var(--font-pixel)] text-green text-sm mb-2">
SUBSCRIBED
</div>
<p className="font-[family-name:var(--font-mono)] text-dim text-xs">
You&apos;ll hear about new features and launches.
</p>
</div>
);
}
return (
<form onSubmit={handleSubmit} className="pixel-border bg-surface p-6">
<div className="font-[family-name:var(--font-pixel)] text-accent text-xs uppercase tracking-[0.3em] mb-3 text-center">
// STAY IN THE LOOP
</div>
<p className="font-[family-name:var(--font-mono)] text-dim text-xs text-center mb-5">
Get notified about new features, releases, and tools I&apos;m building.
</p>
<div className="flex gap-2">
<input
type="email"
value={email}
onChange={(e) => { setEmail(e.target.value); setStatus("idle"); }}
placeholder="you@example.com"
required
className="flex-1 bg-bg border-2 border-border px-4 py-2 font-[family-name:var(--font-mono)] text-text text-xs outline-none focus:border-accent transition-colors placeholder:text-dim/50"
/>
<button
type="submit"
disabled={status === "loading"}
className="bg-accent text-bg px-5 py-2 font-[family-name:var(--font-pixel)] text-xs uppercase tracking-wider hover:brightness-110 transition-all disabled:opacity-50 cursor-pointer"
>
{status === "loading" ? "..." : "SUBSCRIBE"}
</button>
</div>
{status === "error" && (
<p className="font-[family-name:var(--font-mono)] text-red text-[10px] mt-2 text-center">
{msg}
</p>
)}
</form>
);
}

View File

@@ -1,5 +1,6 @@
import Image from "next/image";
import { EmailReveal } from "./email-reveal";
import { NewsletterForm } from "./newsletter-form";
import { TerminalCascade } from "./terminal-cascade";
import {
SearchIcon,
@@ -596,6 +597,13 @@ export default function Home() {
</div>
</section>
{/* ══════ NEWSLETTER ══════ */}
<section className="max-w-5xl mx-auto px-6 py-16">
<div className="max-w-md mx-auto">
<NewsletterForm />
</div>
</section>
<PixelDivider />
{/* ══════ AUTHOR ══════ */}