import { WORKSHOP_CHALLENGE_IDS, WORKSHOP_HONEYPOT_FIELD, WORKSHOP_MEMBER_COUNTS, WORKSHOP_ORGANIZATION_KINDS, WORKSHOP_ROLES, WORKSHOP_STARTED_AT_FIELD, type WorkshopFieldErrors, type WorkshopFormValues, } from "../../workshopSignup"; const CHALLENGE_SET = new Set(WORKSHOP_CHALLENGE_IDS); const KIND_SET = new Set(WORKSHOP_ORGANIZATION_KINDS); const MEMBER_SET = new Set(WORKSHOP_MEMBER_COUNTS); const ROLE_SET = new Set(WORKSHOP_ROLES); const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; export interface ParsedWorkshopSignup { values: WorkshopFormValues; fieldErrors: WorkshopFieldErrors; honeypot: string; startedAt: number | null; } function asString(value: unknown): string { return typeof value === "string" ? value : ""; } function asStringList(value: unknown): string[] { if (Array.isArray(value)) { return value.filter((item): item is string => typeof item === "string"); } if (typeof value === "string" && value.trim().length > 0) { return [value]; } return []; } export function parseWorkshopSignupPayload(raw: unknown): ParsedWorkshopSignup { const record = raw && typeof raw === "object" ? (raw as Record) : {}; const challenges = [ ...new Set(asStringList(record.challenges).map((item) => item.trim())), ].filter((item) => CHALLENGE_SET.has(item)); const values: WorkshopFormValues = { challenges, challengeOther: asString(record.challengeOther).trim().slice(0, 200), unspokenQuestion: asString(record.unspokenQuestion).trim(), organizationName: asString(record.organizationName).trim(), organizationKind: asString(record.organizationKind).trim(), memberCount: asString(record.memberCount).trim(), contactName: asString(record.contactName).trim(), email: asString(record.email).trim().toLowerCase(), role: asString(record.role).trim(), sourcePage: asString(record.sourcePage).trim() || "/workshops", sourceUrl: asString(record.sourceUrl).trim().slice(0, 2000), }; const fieldErrors: WorkshopFieldErrors = {}; if (values.challenges.length === 0) fieldErrors.challenges = "required"; if ( values.challenges.includes("something-else") && values.challengeOther.length === 0 ) { fieldErrors.challengeOther = "required"; } if (values.unspokenQuestion.length > 1000) fieldErrors.unspokenQuestion = "tooLong"; if (values.organizationName.length === 0) fieldErrors.organizationName = "required"; else if (values.organizationName.length > 150) fieldErrors.organizationName = "tooLong"; if (!KIND_SET.has(values.organizationKind)) fieldErrors.organizationKind = "required"; if (!MEMBER_SET.has(values.memberCount)) fieldErrors.memberCount = "required"; if (values.contactName.length === 0) fieldErrors.contactName = "required"; else if (values.contactName.length > 100) fieldErrors.contactName = "tooLong"; if (values.email.length === 0 || !EMAIL_PATTERN.test(values.email) || values.email.length > 254) { fieldErrors.email = values.email.length === 0 ? "required" : "invalid"; } if (values.role.length > 0 && !ROLE_SET.has(values.role)) fieldErrors.role = "invalid"; const startedRaw = record[WORKSHOP_STARTED_AT_FIELD]; const startedAt = typeof startedRaw === "number" ? startedRaw : typeof startedRaw === "string" && startedRaw.trim().length > 0 ? Number(startedRaw) : null; return { values, fieldErrors, honeypot: asString(record[WORKSHOP_HONEYPOT_FIELD]).trim(), startedAt: startedAt !== null && Number.isFinite(startedAt) ? startedAt : null, }; } export function workshopSignupFromFormData(formData: FormData): ParsedWorkshopSignup { return parseWorkshopSignupPayload({ challenges: formData.getAll("challenges"), challengeOther: formData.get("challengeOther"), unspokenQuestion: formData.get("unspokenQuestion"), organizationName: formData.get("organizationName"), organizationKind: formData.get("organizationKind"), memberCount: formData.get("memberCount"), contactName: formData.get("contactName"), email: formData.get("email"), role: formData.get("role"), sourcePage: formData.get("sourcePage"), sourceUrl: formData.get("sourceUrl"), [WORKSHOP_STARTED_AT_FIELD]: formData.get(WORKSHOP_STARTED_AT_FIELD), [WORKSHOP_HONEYPOT_FIELD]: formData.get(WORKSHOP_HONEYPOT_FIELD), }); }