229 lines
8.2 KiB
TypeScript
229 lines
8.2 KiB
TypeScript
"use client";
|
||
|
||
import { FormEvent, useState } from "react";
|
||
import Image from "next/image";
|
||
|
||
export default function HomePage() {
|
||
const [email, setEmail] = useState("");
|
||
const [useCase, setUseCase] = useState("");
|
||
const [status, setStatus] = useState<
|
||
"idle" | "loading" | "success" | "error"
|
||
>("idle");
|
||
const [message, setMessage] = useState<string | null>(null);
|
||
|
||
async function handleSubmit(e: FormEvent) {
|
||
e.preventDefault();
|
||
if (!email) {
|
||
setMessage("Drop an email in first, operator.");
|
||
setStatus("error");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
setStatus("loading");
|
||
setMessage(null);
|
||
|
||
const res = await fetch("/api/beta-signup", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ email, useCase }),
|
||
});
|
||
|
||
if (!res.ok) {
|
||
throw new Error("Failed to save your signup.");
|
||
}
|
||
|
||
setStatus("success");
|
||
setMessage("You’re locked in. Watch your inbox.");
|
||
setEmail("");
|
||
setUseCase("");
|
||
} catch (err) {
|
||
console.error(err);
|
||
setStatus("error");
|
||
setMessage("Something broke. Try again in a minute.");
|
||
}
|
||
}
|
||
|
||
return (
|
||
<main className="min-h-screen bg-black text-zinc-50">
|
||
{/* Abstract Brand Background */}
|
||
<div className="pointer-events-none absolute inset-0 overflow-hidden opacity-[0.55]">
|
||
<svg
|
||
width="100%"
|
||
height="100%"
|
||
preserveAspectRatio="xMidYMid slice"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
>
|
||
<defs>
|
||
<pattern
|
||
id="battlGrid"
|
||
x="0"
|
||
y="0"
|
||
width="80"
|
||
height="80"
|
||
patternUnits="userSpaceOnUse"
|
||
>
|
||
<path
|
||
d="M 80 0 L 0 0 0 80"
|
||
fill="none"
|
||
stroke="#fbbf24"
|
||
strokeWidth="0.5"
|
||
strokeOpacity="0.35"
|
||
/>
|
||
</pattern>
|
||
|
||
<pattern
|
||
id="battlHex"
|
||
width="140"
|
||
height="140"
|
||
patternUnits="userSpaceOnUse"
|
||
>
|
||
<path
|
||
d="M70 4 L136 40 L136 110 L70 146 L4 110 L4 40 Z"
|
||
fill="none"
|
||
stroke="#fbbf24"
|
||
strokeWidth="0.4"
|
||
strokeOpacity="0.25"
|
||
/>
|
||
</pattern>
|
||
</defs>
|
||
|
||
<rect width="100%" height="100%" fill="url(#battlGrid)" />
|
||
<rect width="100%" height="100%" fill="url(#battlHex)" />
|
||
</svg>
|
||
</div>
|
||
<div className="mx-auto flex min-h-screen max-w-5xl flex-col px-4 py-10">
|
||
{/* Top Bar / Logo */}
|
||
<header className="flex items-center justify-between gap-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="flex items-center">
|
||
<Image
|
||
src="/battl/battl-logo-mark-f.svg"
|
||
alt="Battl Builders Logo"
|
||
width={260} // adjust to taste
|
||
height={48} // adjust to taste
|
||
className="block"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<span className="rounded-full border border-amber-500/40 bg-amber-500/10 px-3 py-1 text-xs font-medium text-amber-300">
|
||
Private Beta • Coming Soon
|
||
</span>
|
||
</header>
|
||
|
||
{/* Hero */}
|
||
<section className="mt-16 grid gap-10 md:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)] md:items-center">
|
||
<div>
|
||
<h1 className="text-balance text-3xl font-semibold tracking-tight sm:text-4xl md:text-5xl">
|
||
Stop Guessing.
|
||
<span className="block text-amber-300 py-0.16em">
|
||
Start Building.
|
||
</span>
|
||
</h1>
|
||
<p className="mt-4 max-w-xl text-sm text-zinc-400 sm:text-base">
|
||
From part comparisons to full-build matchups, Battl Builders helps
|
||
you find the right components, catch compatibility issues, and
|
||
score the best deals — all without juggling tabs or spreadsheets.
|
||
</p>
|
||
|
||
<ul className="mt-6 space-y-2 text-sm text-zinc-300">
|
||
<li>• Side-by-side part & build comparisons</li>
|
||
<li>• Smart compatibility checks</li>
|
||
<li>• Live pricing from vetted merchants</li>
|
||
<li>• Save & share builds with your crew</li>
|
||
<li>• Vote on the cleanest, meanest setups</li>
|
||
</ul>
|
||
</div>
|
||
|
||
{/* Beta Form */}
|
||
<div className="rounded-lg border border-zinc-800 bg-zinc-950/70 p-5 shadow-[0_0_0_1px_rgba(24,24,27,0.9)]">
|
||
<h2 className="text-sm font-semibold uppercase tracking-[0.18em] text-zinc-500">
|
||
Join the Beta List
|
||
</h2>
|
||
<p className="mt-2 text-xs text-zinc-400">
|
||
Drop your email and we'll send early access when the Builder
|
||
goes hot. No spam, just builds.
|
||
</p>
|
||
|
||
<form onSubmit={handleSubmit} className="mt-4 space-y-3">
|
||
<div>
|
||
<label
|
||
htmlFor="email"
|
||
className="text-xs font-medium text-zinc-300"
|
||
>
|
||
Email
|
||
</label>
|
||
<input
|
||
id="email"
|
||
type="email"
|
||
value={email}
|
||
onChange={(e) => setEmail(e.target.value)}
|
||
placeholder="you@gearjunkie.com"
|
||
className="mt-1 w-full rounded-md border border-zinc-800 bg-zinc-950 px-3 py-2 text-sm text-zinc-50 outline-none ring-amber-500/30 placeholder:text-zinc-600 focus:border-amber-400/80 focus:ring-2"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label
|
||
htmlFor="useCase"
|
||
className="text-xs font-medium text-zinc-300"
|
||
>
|
||
What will you use Battl Builders for?
|
||
<span className="text-zinc-500"> (optional)</span>
|
||
</label>
|
||
<textarea
|
||
id="useCase"
|
||
value={useCase}
|
||
onChange={(e) => setUseCase(e.target.value)}
|
||
rows={3}
|
||
placeholder="E.g. Comparing build costs, finding the best deals, sharing my builds, weird influencer shit..."
|
||
className="mt-1 w-full rounded-md border border-zinc-800 bg-zinc-950 px-3 py-2 text-xs text-zinc-50 outline-none ring-amber-500/30 placeholder:text-zinc-600 focus:border-amber-400/80 focus:ring-2"
|
||
/>
|
||
</div>
|
||
|
||
<button
|
||
type="submit"
|
||
disabled={status === "loading"}
|
||
className="flex w-full items-center justify-center rounded-md border border-amber-500/70 bg-amber-500/90 px-3 py-2 text-sm font-medium text-black transition hover:bg-amber-400 disabled:cursor-not-allowed disabled:opacity-70"
|
||
>
|
||
{status === "loading" ? "Enlisting…" : "Get Beta Access"}
|
||
</button>
|
||
|
||
{message && (
|
||
<p
|
||
className={`text-xs ${
|
||
status === "success"
|
||
? "text-emerald-400"
|
||
: status === "error"
|
||
? "text-red-400"
|
||
: "text-zinc-400"
|
||
}`}
|
||
>
|
||
{message}
|
||
</p>
|
||
)}
|
||
|
||
<p className="pt-1 text-[10px] leading-relaxed text-zinc-500">
|
||
You can one-click unsubscribe any time with one-click. No data
|
||
games, no surprise newsletters.
|
||
</p>
|
||
</form>
|
||
</div>
|
||
</section>
|
||
|
||
{/* Footer strip */}
|
||
<footer className="mt-12 border-t border-zinc-900 pt-4 text-xs text-zinc-500">
|
||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||
<span>© {new Date().getFullYear()} BATTL BUILDERS<span className="align-super text-[0.9em] ml-0.5">™</span></span>
|
||
<span className="text-zinc-600">
|
||
v0.1 • Import engine online • Builder UI in progress
|
||
</span>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|