"use client"; import React, { Suspense, useEffect, useMemo, useState } from "react"; import { useSearchParams, useRouter } from "next/navigation"; import { useAuth } from "@/hooks/useAuth"; import { apiFetch } from "@/lib/api"; import { Navbar } from "@/components/layout/Navbar"; import { Footer } from "@/components/layout/Footer"; import { FileText } from "lucide-react"; // Types for form fields type FormField = { id: string; type: 'yes_no'|'text'|'date'|'numeric'|'statement'|'paragraph'; label: string; isRequired?: boolean; helpText?: string|null }; function FormsContent() { const search = useSearchParams(); const router = useRouter(); const { token } = useAuth(); const registrationId = search.get("registrationId"); const [registration, setRegistration] = useState(null); const [eventForm, setEventForm] = useState<{ isRequired: boolean; fields: FormField[] } | null>(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [info, setInfo] = useState(null); // Local entry state for new responses const [formsData, setFormsData] = useState>>({}); useEffect(() => { (async () => { if (!registrationId) return; setLoading(true); setError(null); setInfo(null); try { // authToken optional — guests access via registrationId alone const reg = await apiFetch(`/api/registrations/${encodeURIComponent(registrationId)}`, { authToken: token || undefined }); setRegistration(reg); const ev = await apiFetch(`/api/events/${encodeURIComponent(reg.eventId)}`); if (ev?.form) setEventForm(ev.form); // Load any saved draft to prefill fields try { const draft = await apiFetch(`/api/registrations/${encodeURIComponent(registrationId)}/forms/draft`, { authToken: token || undefined }); if (draft && draft.data && typeof draft.data === 'object') setFormsData(draft.data); } catch {} } catch (e: any) { setError(e?.message || 'Failed to load registration'); } finally { setLoading(false); } })(); }, [token, registrationId]); const submittedCount = useMemo(() => { return (registration?.formResponses || []).length; }, [registration]); const requiredCount = useMemo(() => { if (!registration) return 0; try { return (registration.registrationOptions || []) .filter((ro: any) => ro.eventOption?.isMainTicket) .reduce((s: number, ro: any) => s + (ro.quantity || 0), 0); } catch { return 0; } }, [registration]); const remaining = Math.max(0, requiredCount - submittedCount); // Determine if all required fields are filled for each attendee form to be submitted const canSubmit = useMemo(() => { if (!eventForm || remaining <= 0) return false; const reqFields = (eventForm.fields || []) .filter(f => !!f.isRequired && f.type !== 'statement' && f.type !== 'paragraph') .map(f => f.id); for (let i = 0; i < remaining; i++) { const data = formsData[i] || {}; for (const fid of reqFields) { const v = data[fid]; if (v === undefined || v === null || String(v).trim() === '') { return false; } } } return true; }, [eventForm, formsData, remaining]); const submit = async () => { if (!registrationId) return; try { setError(null); setInfo(null); const payload: any[] = []; for (let i = 0; i < remaining; i++) { payload.push({ answers: formsData[i] || {} }); } if (payload.length === 0) { // Nothing left to submit – still go to success page to continue the flow const totalDue = (registration?.registrationOptions || []).reduce((s: number, ro: any) => s + ((ro.eventOption?.price || 0) * (ro.quantity || 0)), 0); router.push(`/registration/success?registrationId=${encodeURIComponent(registrationId)}${totalDue>0?`&totalDue=${encodeURIComponent(totalDue.toFixed(2))}`:''}`); return; } await apiFetch(`/api/registrations/${encodeURIComponent(registrationId)}/forms/responses`, { method: 'POST', authToken: token || undefined, body: { responses: payload } }); // On success, redirect to the success page const totalDue = (registration?.registrationOptions || []).reduce((s: number, ro: any) => s + ((ro.eventOption?.price || 0) * (ro.quantity || 0)), 0); router.push(`/registration/success?registrationId=${encodeURIComponent(registrationId)}${totalDue>0?`&totalDue=${encodeURIComponent(totalDue.toFixed(2))}`:''}`); } catch (e: any) { setError(e?.message || 'Failed to submit forms'); } }; const saveDraft = async () => { if (!registrationId) return; try { setError(null); setInfo(null); await apiFetch(`/api/registrations/${encodeURIComponent(registrationId)}/forms/draft`, { method: 'PUT', authToken: token || undefined, body: { data: formsData } }); setInfo('Draft saved. You can come back and finish later.'); if (!token || !registrationId) return; try { // Redirect to the success page const totalDue = (registration?.registrationOptions || []).reduce((s: number, ro: any) => s + ((ro.eventOption?.price || 0) * (ro.quantity || 0)), 0); router.push(`/registration/success?registrationId=${encodeURIComponent(registrationId)}${totalDue>0?`&totalDue=${encodeURIComponent(totalDue.toFixed(2))}`:''}`); } catch (e: any) { setError(e?.message || 'Failded to go to next step'); } } catch (e: any) { setError(e?.message || 'Failed to save draft'); } }; const skip = async () => { if (!registrationId) return; try { // Redirect to the success page const totalDue = (registration?.registrationOptions || []).reduce((s: number, ro: any) => s + ((ro.eventOption?.price || 0) * (ro.quantity || 0)), 0); router.push(`/registration/success?registrationId=${encodeURIComponent(registrationId)}${totalDue>0?`&totalDue=${encodeURIComponent(totalDue.toFixed(2))}`:''}`); } catch (e: any) { setError(e?.message || 'Failded to skip'); } }; if (!registrationId) { return (
Missing registrationId.
); } return (

Attendee forms

{loading &&
Loading…
} {error &&
{error}
} {info &&
{info}
} {registration && (
{registration.event?.title || registration.eventId}
Registration #{registration.id.slice(0,8)}
Forms submitted: {submittedCount} / {requiredCount}
)} {/* Existing responses (read-only list) */} {registration && (registration.formResponses || []).length > 0 && (
Submitted responses
    {(registration.formResponses || []).map((resp: any, idx: number) => (
  • Attendee {idx + 1}
    {(resp.answers || []).length === 0 ? (
    No answers recorded.
    ) : (
      {resp.answers.map((a: any) => (
    • {eventForm?.fields.find(f => f.id === a.fieldId)?.label || (`Field ${a.fieldId}`)}: {a.value}
    • ))}
    )}
  • ))}
)} {/* Remaining forms to fill */} {eventForm && remaining > 0 && (
Fill remaining attendee details ({remaining})
{Array.from({ length: remaining }, (_, idx) => (
Attendee {submittedCount + idx + 1}
{eventForm.fields.map((f) => (
{f.type === 'statement' ? (
{f.label}
) : f.type === 'paragraph' ? (
{f.label &&
{f.label}
} {f.helpText &&
{f.helpText}
}
) : ( <> {f.type === 'yes_no' ? ( ) : f.type === 'date' ? ( setFormsData(prev => ({ ...prev, [idx]: { ...(prev[idx]||{}), [f.id]: e.target.value } }))} /> ) : f.type === 'numeric' ? ( setFormsData(prev => ({ ...prev, [idx]: { ...(prev[idx]||{}), [f.id]: e.target.value } }))} /> ) : ( setFormsData(prev => ({ ...prev, [idx]: { ...(prev[idx]||{}), [f.id]: e.target.value } }))} /> )} {f.helpText &&
{f.helpText}
} )}
))}
))}
)} {eventForm && remaining === 0 && (
All required attendee forms are completed for this registration.
)}
); } export default function FormsPage() { return ( Loading…}> ); }