Files
adilalloandCursor c0ba3a3798 Add the Workshops page and keep each sign-up on the site.
The form is built once to the settled field set, and a valid send is saved before anyone is emailed.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-03 19:56:49 -07:00

95 lines
3.1 KiB
TypeScript

"use client";
import { memo, useState, type FormEvent } from "react";
import {
EMPTY_WORKSHOP_FORM_VALUES,
WORKSHOP_HONEYPOT_FIELD,
WORKSHOP_STARTED_AT_FIELD,
} from "../../../../lib/workshopSignup";
import WorkshopSignupView from "./WorkshopSignup.view";
import type { WorkshopSignupProps, WorkshopSignupStatus } from "./WorkshopSignup.types";
/** Figma: "Section / Workshop Signup" (24323:55538). Full field set at every width. */
function WorkshopSignupContainer({
className,
issuedAt,
schedulingUrl = "",
sourceUrl = "",
initialStatus = "idle",
initialLarge = false,
initialValues = EMPTY_WORKSHOP_FORM_VALUES,
initialFieldErrors = {},
}: WorkshopSignupProps) {
const [status, setStatus] = useState<WorkshopSignupStatus>(initialStatus);
const [largeOrganization, setLargeOrganization] = useState(initialLarge);
const [fieldErrors, setFieldErrors] = useState(initialFieldErrors);
const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
setStatus("sending");
try {
const response = await fetch("/api/workshop-signup", {
method: "POST",
headers: { "content-type": "application/json", accept: "application/json" },
body: JSON.stringify({
challenges: data.getAll("challenges"),
challengeOther: data.get("challengeOther"),
unspokenQuestion: data.get("unspokenQuestion"),
organizationName: data.get("organizationName"),
organizationKind: data.get("organizationKind"),
memberCount: data.get("memberCount"),
contactName: data.get("contactName"),
email: data.get("email"),
role: data.get("role"),
sourcePage: "/workshops",
sourceUrl,
[WORKSHOP_HONEYPOT_FIELD]: data.get(WORKSHOP_HONEYPOT_FIELD),
[WORKSHOP_STARTED_AT_FIELD]: Number(data.get(WORKSHOP_STARTED_AT_FIELD) ?? issuedAt),
}),
});
const body = (await response.json()) as {
status?: string;
largeOrganization?: boolean;
fieldErrors?: WorkshopSignupProps["initialFieldErrors"];
};
if (body.status === "sent") {
setLargeOrganization(Boolean(body.largeOrganization));
setFieldErrors({});
setStatus("sent");
return;
}
if (body.status === "invalid") {
setFieldErrors(body.fieldErrors ?? {});
setStatus("invalid");
return;
}
if (body.status === "rate_limited" || response.status === 429) {
setStatus("rate_limited");
return;
}
setStatus("failed");
} catch {
setStatus("failed");
}
};
return (
<WorkshopSignupView
className={className ?? ""}
issuedAt={issuedAt}
schedulingUrl={schedulingUrl}
sourceUrl={sourceUrl}
status={status}
largeOrganization={largeOrganization}
values={initialValues}
fieldErrors={fieldErrors}
onSubmit={onSubmit}
/>
);
}
WorkshopSignupContainer.displayName = "WorkshopSignup";
export default memo(WorkshopSignupContainer);