'use client' import { useState, useEffect } from 'react' import { useParams } from 'next/navigation' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { trpc } from '@/lib/trpc/client' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Checkbox } from '@/components/ui/checkbox' import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' import { Skeleton } from '@/components/ui/skeleton' import { toast } from 'sonner' import { CheckCircle, AlertCircle, Loader2 } from 'lucide-react' type FormField = { id: string fieldType: string name: string label: string description?: string | null placeholder?: string | null required: boolean minLength?: number | null maxLength?: number | null minValue?: number | null maxValue?: number | null optionsJson: Array<{ value: string; label: string }> | null conditionJson: { fieldId: string; operator: string; value?: string } | null width: string } export default function PublicFormPage() { const params = useParams() const slug = params.slug as string const [submitted, setSubmitted] = useState(false) const [confirmationMessage, setConfirmationMessage] = useState(null) const { data: form, isLoading, error } = trpc.applicationForm.getBySlug.useQuery( { slug }, { retry: false } ) const submitMutation = trpc.applicationForm.submit.useMutation({ onSuccess: (result) => { setSubmitted(true) setConfirmationMessage(result.confirmationMessage || null) }, onError: (error) => { toast.error(error.message) }, }) const { register, handleSubmit, watch, formState: { errors, isSubmitting }, setValue, } = useForm() const watchedValues = watch() const onSubmit = async (data: Record) => { if (!form) return // Extract email and name if present const emailField = form.fields.find((f) => f.fieldType === 'EMAIL') const email = emailField ? (data[emailField.name] as string) : undefined // Find a name field (common patterns) const nameField = form.fields.find( (f) => f.name.toLowerCase().includes('name') && f.fieldType === 'TEXT' ) const name = nameField ? (data[nameField.name] as string) : undefined await submitMutation.mutateAsync({ formId: form.id, data, email, name, }) } if (isLoading) { return (
{[1, 2, 3, 4].map((i) => (
))}
) } if (error) { return (

Form Not Available

{error.message}

) } if (submitted) { return (

Thank You!

{confirmationMessage || 'Your submission has been received.'}

) } if (!form) return null // Check if a field should be visible based on conditions const isFieldVisible = (field: FormField): boolean => { if (!field.conditionJson) return true const condition = field.conditionJson const dependentValue = watchedValues[form.fields.find((f) => f.id === condition.fieldId)?.name || ''] switch (condition.operator) { case 'equals': return dependentValue === condition.value case 'not_equals': return dependentValue !== condition.value case 'not_empty': return !!dependentValue && dependentValue !== '' case 'contains': return typeof dependentValue === 'string' && dependentValue.includes(condition.value || '') default: return true } } const renderField = (field: FormField) => { if (!isFieldVisible(field)) return null const fieldError = errors[field.name] const errorMessage = fieldError?.message as string | undefined switch (field.fieldType) { case 'SECTION': return (

{field.label}

{field.description && (

{field.description}

)}
) case 'INSTRUCTIONS': return (

{field.description || field.label}

) case 'TEXT': case 'EMAIL': case 'PHONE': case 'URL': return (
{field.description && (

{field.description}

)} {errorMessage &&

{errorMessage}

}
) case 'NUMBER': return (
{field.description && (

{field.description}

)} {errorMessage &&

{errorMessage}

}
) case 'TEXTAREA': return (
{field.description && (

{field.description}

)}