Files
ipv6-sims/app/components/forms/RegistrationForm.tsx
T

201 lines
8.2 KiB
TypeScript

"use client";
import SubmitToast from "@/app/components/ui/SubmitToast";
import { useMemo, useState } from "react";
type IndustryOption = { value: string; label: string };
const emptyRegistration = {
fullName: "",
phone: "",
email: "",
company: "",
jobTitle: "",
industry: "",
notes: "",
};
export default function RegistrationForm({
data,
}: {
data: {
sections: {
personal: { title: string; fullName: string; phone: string; email: string };
professional: { title: string; company: string; jobTitle: string };
industry: { title: string; label: string; placeholder: string; options: IndustryOption[] };
additional: { title: string; label: string; placeholder: string };
};
submit: { label: string; disclaimer: string };
};
}) {
const industries = useMemo(() => data.sections.industry.options, [data.sections.industry.options]);
const [form, setForm] = useState(emptyRegistration);
const [submitState, setSubmitState] = useState<"idle" | "sending" | "ok" | "error">("idle");
return (
<form
className="space-y-[32px]"
onSubmit={async (e) => {
e.preventDefault();
setSubmitState("sending");
const industryLabel = industries.find((o) => o.value === form.industry)?.label ?? "";
try {
const res = await fetch("/ipv6/api/submissions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
type: "registration",
...form,
industryLabel,
}),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(typeof err?.error === "string" ? err.error : res.statusText);
}
setSubmitState("ok");
} catch {
setSubmitState("error");
}
}}
>
<div className="space-y-[16px]">
<h2 className="font-[var(--font-display-lg)] text-[20px] text-primary border-l-2 border-primary pl-3">
{data.sections.personal.title}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-[16px]">
<div className="space-y-1">
<label className="text-outline uppercase tracking-wider">{data.sections.personal.fullName}</label>
<input
className="w-full bg-surface-container-low border border-white/10 rounded-lg px-4 py-3 text-on-surface placeholder:text-outline/40 transition-all"
placeholder="John Doe"
value={form.fullName}
onChange={(e) => setForm((s) => ({ ...s, fullName: e.target.value }))}
/>
</div>
<div className="space-y-1">
<label className="text-outline uppercase tracking-wider">{data.sections.personal.phone}</label>
<input
className="w-full bg-surface-container-low border border-white/10 rounded-lg px-4 py-3 text-on-surface placeholder:text-outline/40 transition-all"
placeholder="+84 ..."
value={form.phone}
onChange={(e) => setForm((s) => ({ ...s, phone: e.target.value }))}
/>
</div>
<div className="md:col-span-2 space-y-1">
<label className="text-outline uppercase tracking-wider">{data.sections.personal.email}</label>
<input
className="w-full bg-surface-container-low border border-white/10 rounded-lg px-4 py-3 text-on-surface placeholder:text-outline/40 transition-all"
placeholder="john.doe@company.com"
type="email"
value={form.email}
onChange={(e) => setForm((s) => ({ ...s, email: e.target.value }))}
/>
</div>
</div>
</div>
<hr className="border-white/5" />
<div className="space-y-[16px]">
<h2 className="font-[var(--font-display-lg)] text-[20px] text-primary border-l-2 border-primary pl-3">
{data.sections.professional.title}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-[16px]">
<div className="space-y-1">
<label className="text-outline uppercase tracking-wider">{data.sections.professional.company}</label>
<input
className="w-full bg-surface-container-low border border-white/10 rounded-lg px-4 py-3 text-on-surface placeholder:text-outline/40 transition-all"
placeholder="Company"
value={form.company}
onChange={(e) => setForm((s) => ({ ...s, company: e.target.value }))}
/>
</div>
<div className="space-y-1">
<label className="text-outline uppercase tracking-wider">{data.sections.professional.jobTitle}</label>
<input
className="w-full bg-surface-container-low border border-white/10 rounded-lg px-4 py-3 text-on-surface placeholder:text-outline/40 transition-all"
placeholder="Network Architect"
value={form.jobTitle}
onChange={(e) => setForm((s) => ({ ...s, jobTitle: e.target.value }))}
/>
</div>
</div>
</div>
<hr className="border-white/5" />
<div className="space-y-[16px]">
<h2 className="font-[var(--font-display-lg)] text-[20px] text-primary border-l-2 border-primary pl-3">
{data.sections.industry.title}
</h2>
<div className="space-y-1">
<label className="text-outline uppercase tracking-wider">{data.sections.industry.label}</label>
<div className="relative">
<select
className="w-full bg-surface-container-low border border-white/10 rounded-lg px-4 py-3 text-on-surface appearance-none transition-all"
value={form.industry}
onChange={(e) => setForm((s) => ({ ...s, industry: e.target.value }))}
>
<option value="" disabled>
{data.sections.industry.placeholder}
</option>
{industries.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
<span className="material-symbols-outlined absolute right-4 top-1/2 -translate-y-1/2 text-outline pointer-events-none">
expand_more
</span>
</div>
</div>
</div>
<hr className="border-white/5" />
<div className="space-y-[16px]">
<h2 className="font-[var(--font-display-lg)] text-[20px] text-primary border-l-2 border-primary pl-3">
{data.sections.additional.title}
</h2>
<div className="space-y-1">
<label className="text-outline uppercase tracking-wider">{data.sections.additional.label}</label>
<textarea
className="w-full bg-surface-container-low border border-white/10 rounded-lg px-4 py-3 text-on-surface placeholder:text-outline/40 transition-all resize-none"
placeholder={data.sections.additional.placeholder}
rows={4}
value={form.notes}
onChange={(e) => setForm((s) => ({ ...s, notes: e.target.value }))}
/>
</div>
</div>
<div className="pt-10 border-t border-white/5 flex flex-col items-center gap-6">
<button
className="gold-gradient-bg text-on-primary w-full md:w-auto px-16 py-5 rounded-xl font-[var(--font-display-lg)] text-xl uppercase tracking-wider hover:shadow-[0_0_30px_rgba(197,160,89,0.4)] transition-all duration-300 active:scale-[0.98] disabled:opacity-60 disabled:pointer-events-none"
type="submit"
disabled={submitState === "sending"}
>
{submitState === "sending" ? "Submitting…" : data.submit.label}
</button>
<SubmitToast
state={submitState}
successTitle="Registration received"
successMessage="Thank you. Your registration was saved and will appear in the admin console under Pending."
onDismiss={() => {
setSubmitState("idle");
setForm({ ...emptyRegistration });
}}
/>
<p className="mt-[16px] text-center text-outline text-[11px] tracking-widest opacity-60 uppercase">
{data.submit.disclaimer}
</p>
</div>
</form>
);
}