Some checks are pending
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Waiting to run
Use mainwhite.svg on white sections with curvy green transitions into flat green bands, improve text and button contrast, and deploy via OpenNext on Cloudflare Workers. Co-authored-by: Cursor <cursoragent@cursor.com>
173 lines
5.8 KiB
TypeScript
173 lines
5.8 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { Rocket } from "lucide-react";
|
|
import { championStartupCopy } from "@/content/partners";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
} from "@/components/ui/dialog";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { DataConsentField } from "@/components/forms/DataConsentField";
|
|
import { dataConsent } from "@/content/consent";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
|
|
const sectors = ["Agriculture", "Healthcare", "Education", "Other"];
|
|
|
|
export function ChampionStartupModal() {
|
|
const [open, setOpen] = useState(false);
|
|
const [status, setStatus] = useState<"idle" | "loading" | "success" | "error">("idle");
|
|
const [error, setError] = useState("");
|
|
const [consent, setConsent] = useState(false);
|
|
const [sector, setSector] = useState<string>("");
|
|
|
|
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
|
|
e.preventDefault();
|
|
if (!consent) {
|
|
setError(dataConsent.errorMessage);
|
|
return;
|
|
}
|
|
|
|
setStatus("loading");
|
|
setError("");
|
|
|
|
const form = e.currentTarget;
|
|
const data = new FormData(form);
|
|
|
|
try {
|
|
const res = await fetch("/api/inquiry", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
intent: "startup_referral",
|
|
name: data.get("name"),
|
|
email: data.get("email"),
|
|
startupName: data.get("startupName"),
|
|
startupWebsite: data.get("startupWebsite") || undefined,
|
|
startupSector: sector || undefined,
|
|
whyRecommend: data.get("whyRecommend"),
|
|
consent: true,
|
|
}),
|
|
});
|
|
const json = await res.json();
|
|
if (!res.ok || !json.ok) {
|
|
throw new Error(json.error || "Something went wrong");
|
|
}
|
|
setStatus("success");
|
|
form.reset();
|
|
setConsent(false);
|
|
setSector("");
|
|
setTimeout(() => {
|
|
setOpen(false);
|
|
setStatus("idle");
|
|
}, 2500);
|
|
} catch (err) {
|
|
setStatus("error");
|
|
setError(err instanceof Error ? err.message : "Failed to send");
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button
|
|
variant="outline"
|
|
className="rounded-full border-white/75 bg-transparent text-white hover:bg-white/12 hover:text-white"
|
|
>
|
|
<Rocket className="mr-2 size-4" />
|
|
{championStartupCopy.title}
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className="max-h-[90vh] max-w-lg overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle>{championStartupCopy.title}</DialogTitle>
|
|
<DialogDescription>{championStartupCopy.intro}</DialogDescription>
|
|
</DialogHeader>
|
|
<p className="rounded-lg bg-muted/60 p-3 text-sm text-muted-foreground leading-relaxed">
|
|
{championStartupCopy.disclaimer}
|
|
</p>
|
|
<form onSubmit={onSubmit} className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="champion-name">
|
|
Your name <span className="text-destructive">*</span>
|
|
</Label>
|
|
<Input id="champion-name" name="name" required />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="champion-email">
|
|
Your email <span className="text-destructive">*</span>
|
|
</Label>
|
|
<Input id="champion-email" name="email" type="email" required />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="startupName">
|
|
Startup name <span className="text-destructive">*</span>
|
|
</Label>
|
|
<Input id="startupName" name="startupName" required />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="startupWebsite">Startup website (optional)</Label>
|
|
<Input id="startupWebsite" name="startupWebsite" type="url" placeholder="https://" />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Sector</Label>
|
|
<Select value={sector} onValueChange={setSector}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select sector" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{sectors.map((s) => (
|
|
<SelectItem key={s} value={s}>
|
|
{s}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="whyRecommend">
|
|
Why do you recommend them? <span className="text-destructive">*</span>
|
|
</Label>
|
|
<Textarea
|
|
id="whyRecommend"
|
|
name="whyRecommend"
|
|
required
|
|
rows={4}
|
|
placeholder="Impact, traction, team, and fit with agriculture, health, or education…"
|
|
/>
|
|
</div>
|
|
<DataConsentField
|
|
id="champion-consent"
|
|
checked={consent}
|
|
onCheckedChange={setConsent}
|
|
/>
|
|
{error && <p className="text-sm text-destructive">{error}</p>}
|
|
{status === "success" && (
|
|
<p className="text-sm text-[#1a5c38]">Thank you for championing this startup!</p>
|
|
)}
|
|
<Button
|
|
type="submit"
|
|
className="w-full rounded-full bg-[#1f3d7e] hover:bg-[#1f3d7e]/90"
|
|
disabled={status === "loading"}
|
|
>
|
|
{status === "loading" ? "Submitting…" : "Submit recommendation"}
|
|
</Button>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|