← back to Govarbitrage
src/app/newsletter/SubscribeForm.tsx
76 lines
"use client";
import { useState } from "react";
type State = { phase: "idle" | "busy" | "done" | "error"; message?: string };
export default function SubscribeForm() {
const [email, setEmail] = useState("");
const [website, setWebsite] = useState(""); // honeypot — humans never see it
const [state, setState] = useState<State>({ phase: "idle" });
async function submit(e: React.FormEvent) {
e.preventDefault();
setState({ phase: "busy" });
try {
const res = await fetch("/api/newsletter/subscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, website }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
setState({ phase: "error", message: data.error || "Something went wrong. Please try again." });
return;
}
setState({ phase: "done", message: data.message });
} catch {
setState({ phase: "error", message: "Network error. Please try again." });
}
}
if (state.phase === "done") {
return (
<div style={{ background: "#f0fdf4", border: "1px solid #bbf7d0", borderRadius: 12, padding: "22px 24px", textAlign: "center" }}>
<div style={{ fontSize: 20, fontWeight: 700, color: "#166534", marginBottom: 6 }}>Almost there — confirm your email</div>
<p style={{ color: "#15803d", fontSize: 14, margin: 0 }}>{state.message}</p>
</div>
);
}
return (
<form onSubmit={submit} style={{ display: "flex", gap: 10, justifyContent: "center", flexWrap: "wrap" }}>
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com"
aria-label="Email address"
style={{ flex: "1 1 260px", maxWidth: 340, padding: "11px 14px", fontSize: 15, border: "1px solid #cbd5e1", borderRadius: 10, background: "#fff", color: "#0f172a" }}
/>
{/* Honeypot: visually hidden, tab-skipped. Bots that fill it are dropped. */}
<input
type="text"
name="website"
value={website}
onChange={(e) => setWebsite(e.target.value)}
tabIndex={-1}
autoComplete="off"
aria-hidden="true"
style={{ position: "absolute", left: -9999, width: 1, height: 1, opacity: 0 }}
/>
<button
type="submit"
disabled={state.phase === "busy"}
style={{ padding: "11px 22px", fontSize: 15, fontWeight: 700, background: "#111827", color: "#fff", border: "none", borderRadius: 10, cursor: state.phase === "busy" ? "wait" : "pointer" }}
>
{state.phase === "busy" ? "Subscribing…" : "Get the digest"}
</button>
{state.phase === "error" && (
<div style={{ flexBasis: "100%", textAlign: "center", color: "#b91c1c", fontSize: 13 }}>{state.message}</div>
)}
</form>
);
}