aboutsummaryrefslogtreecommitdiff
path: root/components/request-form.tsx
diff options
context:
space:
mode:
authorArslaan Pathan <[email protected]>2026-08-31 17:00:12 +1200
committerArslaan Pathan <[email protected]>2026-08-31 17:00:12 +1200
commit5936aedd3d853514a31ebece23175255fb2415c3 (patch)
tree829560cffbbb4fde805319c08f984667e498eaf5 /components/request-form.tsx
downloadthe-agentic-cloud-5936aedd3d853514a31ebece23175255fb2415c3.tar.xz
the-agentic-cloud-5936aedd3d853514a31ebece23175255fb2415c3.zip
Initial commit
Diffstat (limited to 'components/request-form.tsx')
-rw-r--r--components/request-form.tsx145
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>
+ );
+}