Some checks failed
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Has been cancelled
Centralize primary, secondary, tertiary, and neutral tokens and apply them across theme variables and UI components. Co-authored-by: Cursor <cursoragent@cursor.com>
265 lines
9.6 KiB
TypeScript
265 lines
9.6 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { boothSizes, exhibitorSectors } from "@/content/exhibit";
|
||
import { DataConsentField } from "@/components/forms/DataConsentField";
|
||
import { dataConsent } from "@/content/consent";
|
||
import { Button } from "@/components/ui/button";
|
||
import { Input } from "@/components/ui/input";
|
||
import { Label } from "@/components/ui/label";
|
||
import { Textarea } from "@/components/ui/textarea";
|
||
import { Checkbox } from "@/components/ui/checkbox";
|
||
import {
|
||
Select,
|
||
SelectContent,
|
||
SelectItem,
|
||
SelectTrigger,
|
||
SelectValue,
|
||
} from "@/components/ui/select";
|
||
|
||
export function ExhibitorBoothForm() {
|
||
const [status, setStatus] = useState<"idle" | "loading" | "success" | "error">("idle");
|
||
const [error, setError] = useState("");
|
||
const [consent, setConsent] = useState(false);
|
||
const [sector, setSector] = useState("");
|
||
const [boothSize, setBoothSize] = useState("");
|
||
|
||
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||
e.preventDefault();
|
||
if (!consent) {
|
||
setError(dataConsent.errorMessage);
|
||
return;
|
||
}
|
||
if (!sector) {
|
||
setError("Please select your industry / sector.");
|
||
return;
|
||
}
|
||
if (!boothSize) {
|
||
setError("Please select a preferred booth size.");
|
||
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: "exhibitor",
|
||
firstName: data.get("firstName"),
|
||
lastName: data.get("lastName"),
|
||
name: `${data.get("firstName")} ${data.get("lastName")}`,
|
||
email: data.get("email"),
|
||
phone: data.get("phone"),
|
||
jobTitle: data.get("jobTitle"),
|
||
company: data.get("company"),
|
||
companyWebsite: data.get("companyWebsite") || undefined,
|
||
companyDescription: data.get("companyDescription"),
|
||
sector,
|
||
productsToAdvertise: data.get("productsToAdvertise"),
|
||
boothSize,
|
||
powerRequired: data.get("powerRequired") === "on",
|
||
internetRequired: data.get("internetRequired") === "on",
|
||
staffCount: data.get("staffCount") || undefined,
|
||
marketingMaterials: data.get("marketingMaterials") || undefined,
|
||
specialRequirements: data.get("specialRequirements") || undefined,
|
||
message: data.get("boothGoals"),
|
||
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("");
|
||
setBoothSize("");
|
||
} catch (err) {
|
||
setStatus("error");
|
||
setError(err instanceof Error ? err.message : "Failed to send");
|
||
}
|
||
}
|
||
|
||
return (
|
||
<form onSubmit={onSubmit} className="space-y-6 rounded-2xl border border-border bg-white p-6 shadow-sm md:p-8">
|
||
<div className="space-y-1">
|
||
<h3 className="text-xl font-bold">Reserve your booth</h3>
|
||
<p className="text-sm text-muted-foreground">
|
||
Tell us about your company and what you plan to showcase. Our team will confirm
|
||
availability and send package details.
|
||
</p>
|
||
</div>
|
||
|
||
<fieldset className="space-y-4">
|
||
<legend className="text-sm font-semibold uppercase tracking-wider text-[#30614c]">
|
||
Contact
|
||
</legend>
|
||
<div className="grid gap-4 sm:grid-cols-2">
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-firstName">First name *</Label>
|
||
<Input id="booth-firstName" name="firstName" required autoComplete="given-name" />
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-lastName">Last name *</Label>
|
||
<Input id="booth-lastName" name="lastName" required autoComplete="family-name" />
|
||
</div>
|
||
</div>
|
||
<div className="grid gap-4 sm:grid-cols-2">
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-email">Work email *</Label>
|
||
<Input id="booth-email" name="email" type="email" required autoComplete="email" />
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-phone">Phone *</Label>
|
||
<Input id="booth-phone" name="phone" type="tel" required placeholder="+251 …" />
|
||
</div>
|
||
</div>
|
||
<div className="grid gap-4 sm:grid-cols-2">
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-jobTitle">Job title *</Label>
|
||
<Input id="booth-jobTitle" name="jobTitle" required placeholder="e.g. Marketing Director" />
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-company">Company / organization *</Label>
|
||
<Input id="booth-company" name="company" required />
|
||
</div>
|
||
</div>
|
||
</fieldset>
|
||
|
||
<fieldset className="space-y-4">
|
||
<legend className="text-sm font-semibold uppercase tracking-wider text-[#30614c]">
|
||
Company & products
|
||
</legend>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-website">Company website</Label>
|
||
<Input id="booth-website" name="companyWebsite" type="url" placeholder="https://" />
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label>Industry / sector *</Label>
|
||
<Select value={sector} onValueChange={setSector}>
|
||
<SelectTrigger>
|
||
<SelectValue placeholder="Select sector" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
{exhibitorSectors.map((s) => (
|
||
<SelectItem key={s} value={s}>
|
||
{s}
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-companyDescription">About your company *</Label>
|
||
<Textarea
|
||
id="booth-companyDescription"
|
||
name="companyDescription"
|
||
required
|
||
rows={3}
|
||
placeholder="What does your organization do? Who is your audience?"
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-products">Products & services to advertise *</Label>
|
||
<Textarea
|
||
id="booth-products"
|
||
name="productsToAdvertise"
|
||
required
|
||
rows={4}
|
||
placeholder="List products, demos, or services you plan to promote at your booth. Include models, SKUs, or launch items if relevant."
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-marketing">Marketing materials & displays</Label>
|
||
<Textarea
|
||
id="booth-marketing"
|
||
name="marketingMaterials"
|
||
rows={3}
|
||
placeholder="Banners, brochures, samples, screens, interactive demos…"
|
||
/>
|
||
</div>
|
||
</fieldset>
|
||
|
||
<fieldset className="space-y-4">
|
||
<legend className="text-sm font-semibold uppercase tracking-wider text-[#30614c]">
|
||
Booth requirements
|
||
</legend>
|
||
<div className="space-y-2">
|
||
<Label>Preferred booth size *</Label>
|
||
<Select value={boothSize} onValueChange={setBoothSize}>
|
||
<SelectTrigger>
|
||
<SelectValue placeholder="Select booth type" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
{boothSizes.map((b) => (
|
||
<SelectItem key={b.value} value={b.value}>
|
||
{b.label}
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
<div className="grid gap-4 sm:grid-cols-2">
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-staff">Staff attending (approx.)</Label>
|
||
<Input id="booth-staff" name="staffCount" type="number" min={1} max={50} placeholder="e.g. 3" />
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-wrap gap-6">
|
||
<label className="flex items-center gap-2 text-sm">
|
||
<Checkbox name="powerRequired" />
|
||
Need dedicated power
|
||
</label>
|
||
<label className="flex items-center gap-2 text-sm">
|
||
<Checkbox name="internetRequired" />
|
||
Need Wi‑Fi / internet
|
||
</label>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-goals">Booth goals *</Label>
|
||
<Textarea
|
||
id="booth-goals"
|
||
name="boothGoals"
|
||
required
|
||
rows={3}
|
||
placeholder="Lead generation, product launch, partnerships, hiring, etc."
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="booth-special">Special requirements</Label>
|
||
<Textarea
|
||
id="booth-special"
|
||
name="specialRequirements"
|
||
rows={2}
|
||
placeholder="Accessibility, refrigeration, extra storage, setup time…"
|
||
/>
|
||
</div>
|
||
</fieldset>
|
||
|
||
<DataConsentField id="booth-consent" checked={consent} onCheckedChange={setConsent} />
|
||
|
||
{error && <p className="text-sm text-destructive">{error}</p>}
|
||
{status === "success" && (
|
||
<p className="text-sm text-[#30614c]">
|
||
Thank you! We received your booth request and will follow up with availability and pricing.
|
||
</p>
|
||
)}
|
||
|
||
<Button
|
||
type="submit"
|
||
className="w-full rounded-full bg-[#37a47a] text-[#ffffff] hover:bg-[#37a47a]/90 sm:w-auto"
|
||
disabled={status === "loading"}
|
||
>
|
||
{status === "loading" ? "Submitting…" : "Submit booth request"}
|
||
</Button>
|
||
</form>
|
||
);
|
||
}
|