'use client'; import { useState } from 'react'; import { useMutation } from '@tanstack/react-query'; import { Plus, Trash2 } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTitle } from '@/components/ui/sheet'; import { apiFetch } from '@/lib/api/client'; import { toastError } from '@/lib/api/toast-error'; import type { FormField } from '@/lib/validators/form-templates'; interface FormTemplate { id: string; name: string; description: string | null; fields: FormField[]; isActive: boolean; } interface Props { open: boolean; onOpenChange: (open: boolean) => void; template: FormTemplate | null; onSaved: () => void; } const DEFAULT_FIELD: FormField = { key: '', label: '', type: 'text', required: false, }; const FIELD_TYPES: Array<{ value: FormField['type']; label: string }> = [ { value: 'text', label: 'Text' }, { value: 'textarea', label: 'Long text' }, { value: 'email', label: 'Email' }, { value: 'phone', label: 'Phone' }, { value: 'number', label: 'Number' }, { value: 'select', label: 'Select' }, { value: 'checkbox', label: 'Checkbox' }, ]; export function FormTemplateForm(props: Props) { // Key-based remount seeds state on each open + template change. return ( ); } function FormTemplateFormBody({ open, onOpenChange, template, onSaved }: Props) { const [name, setName] = useState(template?.name ?? ''); const [description, setDescription] = useState(template?.description ?? ''); const [isActive, setIsActive] = useState(template?.isActive ?? true); const [fields, setFields] = useState( template && template.fields.length > 0 ? template.fields : [{ ...DEFAULT_FIELD }], ); const saveMutation = useMutation({ mutationFn: () => { const payload = { name, description: description || undefined, fields, isActive, }; if (template) { return apiFetch(`/api/v1/admin/form-templates/${template.id}`, { method: 'PATCH', body: payload, }); } return apiFetch('/api/v1/admin/form-templates', { method: 'POST', body: payload, }); }, onSuccess: () => { toast.success(template ? 'Template saved' : 'Template created'); onSaved(); onOpenChange(false); }, onError: (err) => toastError(err), }); function updateField(idx: number, patch: Partial) { setFields((prev) => prev.map((f, i) => (i === idx ? { ...f, ...patch } : f))); } function addField() { setFields((prev) => [...prev, { ...DEFAULT_FIELD }]); } function removeField(idx: number) { setFields((prev) => (prev.length === 1 ? prev : prev.filter((_, i) => i !== idx))); } return ( {template ? 'Edit form template' : 'New form template'} Name setName(e.target.value)} /> Description setDescription(e.target.value)} /> Active Fields Add field {fields.map((f, i) => ( Field {i + 1} {fields.length > 1 && ( removeField(i)} > )} Key (no spaces) updateField(i, { key: e.target.value })} placeholder="email" /> Label updateField(i, { label: e.target.value })} placeholder="Email address" /> Type updateField(i, { type: v as FormField['type'] })} > {FIELD_TYPES.map((ft) => ( {ft.label} ))} updateField(i, { required: v })} /> Required {f.type === 'select' && ( Options (comma-separated) updateField(i, { options: e.target.value .split(',') .map((s) => s.trim()) .filter(Boolean), }) } /> )} ))} onOpenChange(false)}> Cancel saveMutation.mutate()} disabled={ saveMutation.isPending || !name.trim() || fields.some((f) => !f.key.trim() || !f.label.trim()) } > {saveMutation.isPending ? 'Saving…' : 'Save template'} ); }