diff options
Diffstat (limited to 'components/request-form.tsx')
| -rw-r--r-- | components/request-form.tsx | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/components/request-form.tsx b/components/request-form.tsx new file mode 100644 index 0000000..8f61337 --- /dev/null +++ b/components/request-form.tsx @@ -0,0 +1,145 @@ +"use client"; + +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; + +const consumerDomains = [ + "gmail.com", + "yahoo.com", + "hotmail.com", + "outlook.com", + "icloud.com", + "aol.com", + "proton.me", + "protonmail.com", +]; + +type Status = "idle" | "loading" | "success" | "error"; + +export function RequestForm() { + const [status, setStatus] = useState<Status>("idle"); + const [error, setError] = useState(""); + + async function onSubmit(e: React.FormEvent<HTMLFormElement>) { + e.preventDefault(); + setError(""); + const form = e.currentTarget; + const data = new FormData(form); + const name = String(data.get("name") || "").trim(); + const email = String(data.get("email") || "").trim(); + const company = String(data.get("company") || "").trim(); + const role = String(data.get("role") || "").trim(); + const outcomes = String(data.get("outcomes") || "").trim(); + + if (!name || !email || !company || !role || !outcomes) { + setStatus("error"); + setError("Every field is required. Alignment begins with completeness."); + return; + } + + const domain = email.split("@")[1]?.toLowerCase(); + if (!domain || !email.includes("@")) { + setStatus("error"); + setError("Please provide an institutional email."); + return; + } + if (consumerDomains.includes(domain)) { + setStatus("error"); + setError( + "Consumer inboxes cannot attest PPoE. Please use your institutional email." + ); + return; + } + + setStatus("loading"); + await new Promise((r) => setTimeout(r, 900)); + setStatus("success"); + } + + if (status === "success") { + return ( + <div className="rounded-3xl border border-fuchsia-400/30 bg-gradient-to-br from-violet-500/20 to-cyan-500/10 p-8"> + <p className="font-mono text-[11px] uppercase tracking-[0.2em] text-primary"> + Request received + </p> + <h2 className="mt-3 font-heading text-3xl text-foreground"> + An outcomes partner will be in touch. + </h2> + <p className="mt-3 text-sm leading-relaxed text-muted-foreground"> + Fit is a mutual decision. If your operating thesis, your lake, and + our fabric are prepared to compound, we will schedule a working + session — not a demo. + </p> + </div> + ); + } + + return ( + <form onSubmit={onSubmit} className="space-y-5" noValidate> + <Field id="name" label="Full name" autoComplete="name" /> + <Field + id="email" + label="Institutional email" + type="email" + autoComplete="email" + /> + <Field id="company" label="Institution" autoComplete="organization" /> + <Field id="role" label="Role" placeholder="Chief Transformation Officer" /> + <div className="space-y-2"> + <Label htmlFor="outcomes"> + What outcomes are you looking to operationalize? + </Label> + <Textarea + id="outcomes" + name="outcomes" + rows={5} + className="min-h-28 text-sm" + placeholder="Be specific about the thesis, not the tools. The fabric will meet you there." + /> + </div> + {status === "error" && error ? ( + <p className="text-sm text-destructive" role="alert"> + {error} + </p> + ) : null} + <Button + type="submit" + disabled={status === "loading"} + className="h-11 w-full px-5 sm:w-auto" + > + {status === "loading" ? "Attesting request…" : "Request access"} + </Button> + </form> + ); +} + +function Field({ + id, + label, + type = "text", + autoComplete, + placeholder, +}: { + id: string; + label: string; + type?: string; + autoComplete?: string; + placeholder?: string; +}) { + return ( + <div className="space-y-2"> + <Label htmlFor={id}>{label}</Label> + <Input + id={id} + name={id} + type={type} + autoComplete={autoComplete} + placeholder={placeholder} + className="h-10" + /> + </div> + ); +} |
