import { ArrowDown, ArrowUp, Plus, Trash2 } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { Button, Dialog, DismissibleAlert, FormField as Field, IconButton, ToggleSwitch, type ApiSettings } from "@govoplan/core-webui"; import { saveFormDefinition, type FormDefinition, type FormFieldDefinition, type FormValueType } from "../../api/forms"; const VALUE_TYPES: Array<{ value: FormValueType; label: string }> = [ { value: "text", label: "Text" }, { value: "multiline_text", label: "Long text" }, { value: "email", label: "Email" }, { value: "integer", label: "Integer" }, { value: "number", label: "Number" }, { value: "boolean", label: "Yes / no" }, { value: "date", label: "Date" }, { value: "datetime", label: "Date and time" }, { value: "choice", label: "Single choice" }, { value: "multi_choice", label: "Multiple choice" }, { value: "object", label: "Structured object" }, { value: "list", label: "Structured list" } ]; export default function FormDefinitionDialog({ open, settings, tenantId, definition, canPublish, onClose, onSaved }: { open: boolean; settings: ApiSettings; tenantId: string; definition: FormDefinition | null; canPublish: boolean; onClose: () => void; onSaved: (definition: FormDefinition) => void; }) { const [draft, setDraft] = useState(() => initialDraft(tenantId, definition)); const [changeReason, setChangeReason] = useState(""); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); useEffect(() => { if (!open) return; setDraft(initialDraft(tenantId, definition)); setChangeReason(""); setBusy(false); setError(""); }, [definition, open, tenantId]); const valid = useMemo(() => Boolean( draft.title.trim() && draft.key.trim() && draft.fields.length > 0 && draft.fields.every((field) => field.key.trim() && field.label.trim()) && changeReason.trim() ), [changeReason, draft]); async function save() { if (!valid) return; setBusy(true); setError(""); const revision = crypto.randomUUID(); const recordedAt = new Date().toISOString(); const payload: FormDefinition = { ...draft, reference: { ...draft.reference, version: revision }, temporal: { ...draft.temporal, revision, recorded_at: recordedAt, superseded_at: null, change_reason: changeReason.trim() }, title: draft.title.trim(), key: draft.key.trim(), description: draft.description?.trim() || null, fields: draft.fields.map(normalizeField), policy_refs: draft.policy_refs.map((item) => item.trim()).filter(Boolean), metadata: { ...draft.metadata } }; try { const saved = await saveFormDefinition( settings, payload, definition?.reference.version ); onSaved(saved); } catch (reason) { setError(reason instanceof Error ? reason.message : "The Form definition could not be saved."); } finally { setBusy(false); } } function patchField(index: number, patch: Partial) { setDraft((current) => ({ ...current, fields: current.fields.map((field, fieldIndex) => fieldIndex === index ? { ...field, ...patch } : field) })); } function moveField(index: number, delta: -1 | 1) { setDraft((current) => { const target = index + delta; if (target < 0 || target >= current.fields.length) return current; const fields = [...current.fields]; [fields[index], fields[target]] = [fields[target], fields[index]]; return { ...current, fields }; }); } return ( }>
{error && {error}}
setDraft({ ...draft, title: event.target.value })} /> setDraft({ ...draft, key: event.target.value })} />