@nextlyhq/plugin-form-builder 0.0.2-alpha.62 → 0.0.2-alpha.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/admin/index.ts","../../src/admin/components/submissions/SubmissionsView.tsx","../../src/admin/components/submissions/SubmissionSheet.tsx","../../src/admin/FormBuilderView.tsx","../../src/admin/components/builder/FieldCards.tsx","../../src/admin/context/FormBuilderContext.tsx","../../src/admin/components/builder/AddFieldDialog.tsx","../../src/admin/components/builder/FieldEditor.tsx","../../src/admin/components/builder/ConditionalLogicEditor.tsx","../../src/admin/components/builder/FormNotificationsTab.tsx","../../src/admin/components/builder/notification-addresses.ts","../../src/admin/components/builder/FormPreview.tsx","../../src/admin/components/builder/FormSettingsTab.tsx","../../src/admin/components/builder/RedirectPagePicker.tsx"],"sourcesContent":["/**\n * Form Builder Admin Components\n *\n * Entry point for admin UI components used by the Nextly admin panel.\n * These components are referenced via component paths in collection configs.\n *\n * Component paths use the format: \"@nextlyhq/plugin-form-builder/admin#ExportName\"\n *\n * ## Auto-Registration\n *\n * This module automatically registers its components with the admin panel\n * when imported. No manual registration is required - just add the plugin\n * to your Nextly config and the admin will detect and load the components.\n *\n * @module admin\n * @since 0.1.0\n */\n\n// ============================================================================\n// Main View Components\n// ============================================================================\n\n// ============================================================================\n// Component Registration\n// ============================================================================\n\nimport {\n registerComponents,\n registerKnownPlugin,\n type ComponentPath,\n} from \"@nextlyhq/plugin-sdk/admin\";\n\nimport { SubmissionsView } from \"./components/submissions/SubmissionsView\";\nimport { FormBuilderView } from \"./FormBuilderView\";\n\nexport { FormBuilderView, type FormBuilderViewProps } from \"./FormBuilderView\";\nexport { SubmissionsView } from \"./components/submissions/SubmissionsView\";\nexport { SubmissionSheet } from \"./components/submissions/SubmissionSheet\";\n\n// ============================================================================\n// Builder Sub-Components\n// ============================================================================\n\nexport {\n FieldCards,\n AddFieldDialog,\n FieldEditor,\n FormPreview,\n type FieldCardsProps,\n type AddFieldDialogProps,\n type FieldEditorProps,\n type FormPreviewProps,\n} from \"./components/builder\";\n\n// ============================================================================\n// Context & Hooks\n// ============================================================================\n\nexport {\n FormBuilderProvider,\n useFormBuilder,\n useOptionalFormBuilder,\n createFieldFromType,\n createNotification,\n generateFieldName,\n generateFieldLabel,\n DEFAULT_SETTINGS,\n type FormBuilderState,\n type FormBuilderActions,\n type FormBuilderContextValue,\n type FormBuilderProviderProps,\n type FormSettings,\n type FormNotification,\n} from \"./context/FormBuilderContext\";\n\n// ============================================================================\n// Tab Components\n// ============================================================================\n\nexport { FormSettingsTab } from \"./components/builder/FormSettingsTab\";\nexport { FormNotificationsTab } from \"./components/builder/FormNotificationsTab\";\n\n/** Component path for FormBuilderView */\nexport const FORM_BUILDER_VIEW_PATH: ComponentPath =\n \"@nextlyhq/plugin-form-builder/admin#FormBuilderView\";\n\n/** Component path for SubmissionsView */\nexport const SUBMISSIONS_VIEW_PATH: ComponentPath =\n \"@nextlyhq/plugin-form-builder/admin#SubmissionsView\";\n\n// ============================================================================\n// Auto-Registration with Admin\n// ============================================================================\n\n/**\n * Eagerly register components at module load time.\n *\n * This ensures components are available immediately when the admin\n * tries to resolve them, without needing to wait for async registration.\n */\nregisterComponents({\n [FORM_BUILDER_VIEW_PATH]: FormBuilderView,\n [SUBMISSIONS_VIEW_PATH]: SubmissionsView,\n});\n\n/**\n * Also register with the known plugin system for consistency.\n *\n * This supports the auto-registration flow where the admin detects\n * component paths and triggers registration. Since we eagerly register\n * above, this callback is effectively a no-op but keeps the plugin\n * compatible with both eager and lazy registration patterns.\n */\nregisterKnownPlugin(\n \"@nextlyhq/plugin-form-builder\",\n // eslint-disable-next-line @typescript-eslint/require-await -- registerKnownPlugin's second arg is typed `() => Promise<void>` (strict, not a union); body is synchronous\n async () => {\n // Components already registered above, but re-register for safety\n registerComponents({\n [FORM_BUILDER_VIEW_PATH]: FormBuilderView,\n [SUBMISSIONS_VIEW_PATH]: SubmissionsView,\n });\n }\n);\n","\"use client\";\n\n/**\n * Submissions View\n *\n * The submissions list, replacing the generic collection table via the\n * List-view override. Submission data is stored keyed by field name, so\n * when one form is selected the table shows real per-field columns; across\n * all forms (where fields differ) it falls back to generic columns with a\n * data summary. Spam is a first-class status tab — flagged submissions are\n * reviewable and recoverable, never invisible.\n *\n * @module admin/components/submissions/SubmissionsView\n */\n\nimport {\n DataTableView,\n type NextlyColumn,\n type RowAction,\n} from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n Badge,\n Button,\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Tabs,\n TabsList,\n TabsTrigger,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@nextlyhq/ui\";\nimport { ChevronLeft, ChevronRight, Columns3, Download } from \"lucide-react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport type { FormField } from \"../../../types\";\nimport { formatExportValue } from \"../../../utils/export-formats\";\n\nimport { SubmissionSheet } from \"./SubmissionSheet\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SubmissionRow {\n id: string;\n form: string | { id: string; name?: string };\n data: Record<string, unknown>;\n status: string;\n spamReason?: string | null;\n notes?: string | null;\n ipAddress?: string | null;\n userAgent?: string | null;\n submittedAt?: string | null;\n editedAt?: string | null;\n editedBy?: string | null;\n}\n\nexport interface FormOption {\n id: string;\n name: string;\n fields: FormField[];\n}\n\ninterface SubmissionsViewProps {\n collectionSlug?: string;\n}\n\ntype StatusTab = \"all\" | \"new\" | \"read\" | \"archived\" | \"spam\";\n\nconst STATUS_TABS: Array<{ value: StatusTab; label: string }> = [\n { value: \"all\", label: \"All\" },\n { value: \"new\", label: \"New\" },\n { value: \"read\", label: \"Read\" },\n { value: \"archived\", label: \"Archived\" },\n { value: \"spam\", label: \"Spam\" },\n];\n\nconst PAGE_SIZE = 25;\n\n/** How many per-field columns are visible before the rest hide behind the selector. */\nconst VISIBLE_FIELD_COLUMNS = 4;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction formIdOf(row: SubmissionRow): string {\n return typeof row.form === \"object\" ? row.form.id : row.form;\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return \"\";\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return \"\";\n return date.toLocaleString(undefined, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n });\n}\n\n/** Status → badge styling. Tokens only; spam reads as a warning state. */\nfunction StatusBadge({ status }: { status: string }) {\n const label = status.charAt(0).toUpperCase() + status.slice(1);\n return (\n <Badge\n variant=\"outline\"\n className={`rounded-none px-1.5 py-0 text-[10px] font-semibold uppercase tracking-wider ${\n // Full-strength status borders (destructive/primary) so each badge boundary is perceivable; the default falls back to border-border, keeping the states distinct.\n status === \"spam\"\n ? \"border-destructive text-destructive\"\n : status === \"new\"\n ? \"border-primary text-primary\"\n : \"border-border text-muted-foreground\"\n }`}\n >\n {label}\n </Badge>\n );\n}\n\n/** Compact \"key: value\" preview of a submission for the all-forms table. */\nfunction summarizeData(data: Record<string, unknown>): string {\n return Object.entries(data)\n .slice(0, 3)\n .map(([key, value]) => `${key}: ${formatExportValue(value)}`)\n .join(\" · \");\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function SubmissionsView({ collectionSlug }: SubmissionsViewProps) {\n const [slugs, setSlugs] = useState<{ forms: string; submissions: string }>();\n const [forms, setForms] = useState<FormOption[]>([]);\n const [selectedFormId, setSelectedFormId] = useState<string>(() => {\n // Deep links (?form=<id>) preselect the form filter.\n if (typeof window === \"undefined\") return \"\";\n return new URLSearchParams(window.location.search).get(\"form\") ?? \"\";\n });\n const [statusTab, setStatusTab] = useState<StatusTab>(\"all\");\n const [rows, setRows] = useState<SubmissionRow[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [openId, setOpenId] = useState<string | null>(null);\n const [canUpdate, setCanUpdate] = useState(false);\n const [canDelete, setCanDelete] = useState(false);\n\n const submissionsSlug =\n slugs?.submissions ?? collectionSlug ?? \"form-submissions\";\n\n // --- bootstrap: resolved slugs, the forms list, and the user's rights ----\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n try {\n const configRes = await fetch(\n \"/admin/api/plugins/@nextlyhq/plugin-form-builder/builder-config\",\n { credentials: \"include\" }\n );\n const config = configRes.ok\n ? ((await configRes.json()) as {\n slugs?: { forms: string; submissions: string };\n })\n : null;\n const resolved = config?.slugs ?? {\n forms: \"forms\",\n submissions: \"form-submissions\",\n };\n if (cancelled) return;\n setSlugs(resolved);\n\n // Page through ALL forms — a capped single fetch would silently\n // drop forms from the filter and the per-field column lookup.\n const allForms: FormOption[] = [];\n const formsPageSize = 200;\n for (let formsPage = 1; ; formsPage += 1) {\n const formsRes = await fetch(\n `/admin/api/collections/${resolved.forms}/entries?pageSize=${formsPageSize}&page=${formsPage}`,\n { credentials: \"include\" }\n );\n if (!formsRes.ok) break;\n const json = (await formsRes.json()) as {\n items?: Array<{ id: string; name?: string; fields?: FormField[] }>;\n };\n const items = json.items ?? [];\n allForms.push(\n ...items.map(item => ({\n id: item.id,\n name: item.name ?? item.id,\n fields: Array.isArray(item.fields) ? item.fields : [],\n }))\n );\n if (items.length < formsPageSize) break;\n }\n if (!cancelled) setForms(allForms);\n\n const permsRes = await fetch(\"/admin/api/me/permissions\", {\n credentials: \"include\",\n });\n if (permsRes.ok) {\n const perms = (await permsRes.json()) as {\n permissions?: string[];\n isSuperAdmin?: boolean;\n };\n const has = (slug: string) =>\n Boolean(perms.isSuperAdmin) ||\n (perms.permissions ?? []).includes(slug);\n if (!cancelled) {\n setCanUpdate(has(`update-${resolved.submissions}`));\n setCanDelete(has(`delete-${resolved.submissions}`));\n }\n }\n } catch {\n // Bootstrap failures degrade to the default slugs — without them\n // the list fetch would wait forever and the loading state never end.\n if (!cancelled) {\n setSlugs(\n current =>\n current ?? { forms: \"forms\", submissions: \"form-submissions\" }\n );\n }\n }\n })();\n return () => {\n cancelled = true;\n };\n }, []);\n\n // --- the list fetch ------------------------------------------------------\n // Monotonic request sequence: rapid filter changes overlap fetches, and\n // only the LATEST request may commit rows — an older response finishing\n // last must never overwrite the current filter's data.\n const fetchSeqRef = useRef(0);\n const fetchRows = useCallback(async () => {\n if (!slugs) return;\n const seq = ++fetchSeqRef.current;\n setLoading(true);\n setError(null);\n try {\n const where: Record<string, unknown> = {};\n if (selectedFormId) where.form = { equals: selectedFormId };\n // \"All\" means every human status; spam only shows when asked for —\n // the same default the export route applies.\n if (statusTab === \"all\") where.status = { not_equals: \"spam\" };\n else where.status = { equals: statusTab };\n\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(PAGE_SIZE),\n sortBy: \"submittedAt\",\n sortOrder: \"desc\",\n where: JSON.stringify(where),\n });\n const res = await fetch(\n `/admin/api/collections/${slugs.submissions}/entries?${params}`,\n { credentials: \"include\" }\n );\n if (!res.ok)\n throw new Error(`Failed to load submissions (${res.status})`);\n const json = (await res.json()) as {\n items?: SubmissionRow[];\n meta?: { total?: number };\n };\n if (seq !== fetchSeqRef.current) return;\n setRows(json.items ?? []);\n setTotal(json.meta?.total ?? (json.items ?? []).length);\n } catch (err) {\n if (seq !== fetchSeqRef.current) return;\n setError(\n err instanceof Error ? err.message : \"Failed to load submissions\"\n );\n } finally {\n if (seq === fetchSeqRef.current) setLoading(false);\n }\n }, [slugs, selectedFormId, statusTab, page]);\n\n useEffect(() => {\n void fetchRows();\n }, [fetchRows]);\n\n const selectedForm = useMemo(\n () => forms.find(form => form.id === selectedFormId),\n [forms, selectedFormId]\n );\n\n const formNameById = useMemo(\n () => new Map(forms.map(form => [form.id, form.name])),\n [forms]\n );\n\n // --- columns --------------------------------------------------------------\n // Hidden field columns, per form. Wide forms start with everything past\n // the first few hidden; the toolbar's Columns menu toggles the rest in.\n const [hiddenFieldColumns, setHiddenFieldColumns] = useState<Set<string>>(\n new Set()\n );\n useEffect(() => {\n setHiddenFieldColumns(\n new Set(\n (selectedForm?.fields ?? [])\n .slice(VISIBLE_FIELD_COLUMNS)\n .map(field => field.name)\n )\n );\n }, [selectedForm]);\n\n const columns = useMemo<NextlyColumn<SubmissionRow>[]>(() => {\n if (selectedForm) {\n // One form selected: real per-field columns.\n const fieldColumns = selectedForm.fields\n .filter(field => !hiddenFieldColumns.has(field.name))\n .map<NextlyColumn<SubmissionRow>>(field => ({\n name: `data.${field.name}`,\n header: field.label || field.name,\n accessor: row => formatExportValue(row.data?.[field.name], field),\n }));\n return [\n ...fieldColumns,\n {\n name: \"status\",\n header: \"Status\",\n accessor: row => row.status,\n cell: ({ row }) => <StatusBadge status={row.status} />,\n },\n {\n name: \"submittedAt\",\n header: \"Submitted\",\n accessor: row => formatDate(row.submittedAt),\n },\n ];\n }\n return [\n {\n name: \"form\",\n header: \"Form\",\n accessor: row => formNameById.get(formIdOf(row)) ?? formIdOf(row),\n },\n {\n name: \"data\",\n header: \"Submission\",\n accessor: row => summarizeData(row.data ?? {}),\n },\n {\n name: \"status\",\n header: \"Status\",\n accessor: row => row.status,\n cell: ({ row }) => <StatusBadge status={row.status} />,\n },\n {\n name: \"submittedAt\",\n header: \"Submitted\",\n accessor: row => formatDate(row.submittedAt),\n },\n ];\n }, [selectedForm, formNameById, hiddenFieldColumns]);\n\n // --- actions ---------------------------------------------------------------\n const patchSubmission = useCallback(\n async (id: string, body: Record<string, unknown>) => {\n const res = await fetch(\n `/admin/api/collections/${submissionsSlug}/entries/${id}`,\n {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify(body),\n }\n );\n if (!res.ok) throw new Error(`Update failed (${res.status})`);\n await fetchRows();\n },\n [submissionsSlug, fetchRows]\n );\n\n const deleteSubmission = useCallback(\n async (id: string) => {\n const res = await fetch(\n `/admin/api/collections/${submissionsSlug}/entries/${id}`,\n { method: \"DELETE\", credentials: \"include\" }\n );\n if (!res.ok) throw new Error(`Delete failed (${res.status})`);\n setOpenId(null);\n await fetchRows();\n },\n [submissionsSlug, fetchRows]\n );\n\n const rowActions = useCallback(\n (row: SubmissionRow): RowAction<SubmissionRow>[] => {\n const actions: RowAction<SubmissionRow>[] = [];\n if (row.status === \"spam\" && canUpdate) {\n actions.push({\n id: \"not-spam\",\n label: \"Not spam\",\n onSelect: target =>\n void patchSubmission(target.id, {\n status: \"new\",\n spamReason: null,\n }),\n });\n }\n if (canDelete) {\n actions.push({\n id: \"delete\",\n label: \"Delete\",\n destructive: true,\n onSelect: target => void deleteSubmission(target.id),\n });\n }\n return actions;\n },\n [canUpdate, canDelete, patchSubmission, deleteSubmission]\n );\n\n // --- export ----------------------------------------------------------------\n const exportHref = useCallback(\n (format: \"csv\" | \"json\") => {\n const params = new URLSearchParams({ format });\n if (selectedFormId) params.set(\"form\", selectedFormId);\n if (statusTab !== \"all\") params.set(\"status\", statusTab);\n return `/admin/api/plugins/@nextlyhq/plugin-form-builder/submissions/export?${params}`;\n },\n [selectedFormId, statusTab]\n );\n\n const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));\n // The open record is tracked by ID: after a save refetch the row may\n // move or leave the filtered view entirely — index tracking would silently\n // show a different submission, while a missing ID just closes the drawer.\n const openIndex = openId ? rows.findIndex(row => row.id === openId) : -1;\n const openRow = openIndex === -1 ? null : rows[openIndex];\n\n return (\n <div className=\"space-y-4\">\n {/* Toolbar: form filter, status tabs, export */}\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <Select\n value={selectedFormId || \"__all\"}\n onValueChange={value => {\n setSelectedFormId(value === \"__all\" ? \"\" : value);\n setPage(1);\n setOpenId(null);\n }}\n >\n <SelectTrigger\n aria-label=\"Filter by form\"\n className=\"w-full sm:w-56 bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__all\">All forms</SelectItem>\n {forms.map(form => (\n <SelectItem key={form.id} value={form.id}>\n {form.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Tabs\n value={statusTab}\n onValueChange={value => {\n setStatusTab(value as StatusTab);\n setPage(1);\n setOpenId(null);\n }}\n >\n {/* No corner override: square is the primitive's guarantee, not\n this view's decision. */}\n <TabsList>\n {STATUS_TABS.map(tab => (\n <TabsTrigger key={tab.value} value={tab.value}>\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {selectedForm && selectedForm.fields.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button type=\"button\" variant=\"outline\">\n <Columns3 className=\"h-4 w-4\" aria-hidden=\"true\" />\n Columns\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"shadow-none border-border\"\n >\n {selectedForm.fields.map(field => (\n <DropdownMenuCheckboxItem\n key={field.name}\n checked={!hiddenFieldColumns.has(field.name)}\n onCheckedChange={checked =>\n setHiddenFieldColumns(prev => {\n const next = new Set(prev);\n if (checked) next.delete(field.name);\n else next.add(field.name);\n return next;\n })\n }\n // Keep the menu open while toggling several columns.\n onSelect={event => event.preventDefault()}\n >\n {field.label || field.name}\n </DropdownMenuCheckboxItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button type=\"button\" variant=\"outline\">\n <Download className=\"h-4 w-4\" aria-hidden=\"true\" />\n Export\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"shadow-none border-border\"\n >\n {selectedFormId ? (\n <DropdownMenuItem asChild className=\"cursor-pointer\">\n <a href={exportHref(\"csv\")} download>\n CSV\n </a>\n </DropdownMenuItem>\n ) : (\n <Tooltip>\n <TooltipTrigger asChild>\n {/* CSV columns come from one form's fields, so it needs a\n form selected; the tooltip says so instead of hiding it. */}\n <DropdownMenuItem\n disabled\n onSelect={event => event.preventDefault()}\n >\n CSV\n </DropdownMenuItem>\n </TooltipTrigger>\n <TooltipContent side=\"left\">\n Select a form first — CSV columns come from its fields.\n </TooltipContent>\n </Tooltip>\n )}\n <DropdownMenuItem asChild className=\"cursor-pointer\">\n <a href={exportHref(\"json\")} download>\n JSON\n </a>\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n <DataTableView<SubmissionRow>\n columns={columns}\n rows={rows}\n loading={loading}\n error={error}\n emptyMessage={\n statusTab === \"spam\"\n ? \"No spam — nothing has been flagged.\"\n : \"No submissions yet.\"\n }\n ariaLabel=\"Submissions\"\n onRowClick={row => setOpenId(row.id)}\n rowActions={rowActions}\n />\n\n {/* Pagination */}\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span role=\"status\">\n {total === 0\n ? \"0 submissions\"\n : `${(page - 1) * PAGE_SIZE + 1}–${Math.min(page * PAGE_SIZE, total)} of ${total}`}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8 rounded-none\"\n disabled={page <= 1}\n onClick={() => setPage(current => current - 1)}\n aria-label=\"Previous page\"\n >\n <ChevronLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n <span>\n Page {page} of {totalPages}\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8 rounded-none\"\n disabled={page >= totalPages}\n onClick={() => setPage(current => current + 1)}\n aria-label=\"Next page\"\n >\n <ChevronRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n </div>\n\n {/* Detail drawer with prev/next over the current page */}\n {openRow && (\n <SubmissionSheet\n submission={openRow}\n form={\n forms.find(form => form.id === formIdOf(openRow)) ?? {\n id: formIdOf(openRow),\n name: formNameById.get(formIdOf(openRow)) ?? \"Form\",\n fields: [],\n }\n }\n canUpdate={canUpdate}\n onClose={() => setOpenId(null)}\n onPrev={\n openIndex > 0 ? () => setOpenId(rows[openIndex - 1].id) : undefined\n }\n onNext={\n openIndex < rows.length - 1\n ? () => setOpenId(rows[openIndex + 1].id)\n : undefined\n }\n onSave={async changes => {\n await patchSubmission(openRow.id, changes);\n }}\n />\n )}\n </div>\n );\n}\n\nexport default SubmissionsView;\n","\"use client\";\n\n/**\n * Submission Sheet\n *\n * Drawer detail for one submission: the submitted values in field order,\n * metadata, status + internal notes, and — behind the update permission —\n * inline editing of the submitted data. Prev/next walks the current table\n * page without losing filter context.\n *\n * @module admin/components/submissions/SubmissionSheet\n */\n\nimport {\n Badge,\n Button,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Switch,\n Textarea,\n} from \"@nextlyhq/ui\";\nimport { ChevronLeft, ChevronRight, Pencil, TriangleAlert } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\nimport type { FormField } from \"../../../types\";\nimport { formatExportValue } from \"../../../utils/export-formats\";\n\nimport type { FormOption, SubmissionRow } from \"./SubmissionsView\";\n\ninterface SubmissionSheetProps {\n submission: SubmissionRow;\n form: FormOption;\n canUpdate: boolean;\n onClose: () => void;\n onPrev?: () => void;\n onNext?: () => void;\n onSave: (changes: Record<string, unknown>) => Promise<void>;\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return \"\";\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return \"\";\n return date.toLocaleString(undefined, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n });\n}\n\n/** One editable input per field type; unsupported types render read-only. */\nfunction FieldValueInput({\n field,\n value,\n onChange,\n}: {\n field: FormField;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const id = `submission-field-${field.name}`;\n switch (field.type) {\n case \"textarea\":\n return (\n <Textarea\n id={id}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n rows={3}\n />\n );\n case \"checkbox\":\n return (\n <Switch\n id={id}\n checked={Boolean(value)}\n onCheckedChange={checked => onChange(checked)}\n />\n );\n case \"select\":\n case \"radio\": {\n const options = \"options\" in field ? (field.options ?? []) : [];\n return (\n <Select\n value={typeof value === \"string\" ? value : \"\"}\n onValueChange={onChange}\n >\n <SelectTrigger\n id={id}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map(option => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n }\n case \"number\":\n return (\n <Input\n id={id}\n type=\"number\"\n value={\n typeof value === \"number\" || typeof value === \"string\"\n ? String(value)\n : \"\"\n }\n onChange={e =>\n onChange(e.target.value === \"\" ? \"\" : Number(e.target.value))\n }\n />\n );\n case \"date\":\n case \"time\":\n return (\n <Input\n id={id}\n type={field.type}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n />\n );\n case \"text\":\n case \"email\":\n case \"url\":\n case \"phone\":\n return (\n <Input\n id={id}\n type={field.type === \"email\" ? \"email\" : \"text\"}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n />\n );\n case \"file\":\n case \"hidden\":\n default:\n // Files are media references, hidden fields are machine values, and\n // unknown types may hold structured data — coercing any of them\n // through a text input would corrupt them, so they stay read-only.\n return (\n <p className=\"text-sm text-muted-foreground\">\n {formatExportValue(value, field) || \"—\"}\n </p>\n );\n }\n}\n\nexport function SubmissionSheet({\n submission,\n form,\n canUpdate,\n onClose,\n onPrev,\n onNext,\n onSave,\n}: SubmissionSheetProps) {\n const [editing, setEditing] = useState(false);\n const [draftData, setDraftData] = useState<Record<string, unknown>>({});\n const [status, setStatus] = useState(submission.status);\n const [notes, setNotes] = useState(submission.notes ?? \"\");\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n\n // Prev/next swaps the submission while the sheet stays open — every piece\n // of local state must follow the record it belongs to.\n useEffect(() => {\n setEditing(false);\n setDraftData(submission.data ?? {});\n setStatus(submission.status);\n setNotes(submission.notes ?? \"\");\n setSaveError(null);\n }, [submission]);\n\n const fieldNames = new Set(form.fields.map(field => field.name));\n // Forms evolve after submissions exist: keys no longer on the form are\n // shown honestly instead of silently dropped.\n const orphanedEntries = Object.entries(submission.data ?? {}).filter(\n ([key]) => !fieldNames.has(key)\n );\n\n // Only a real value difference counts as a data edit — entering edit mode\n // and saving untouched values must not write `data` or earn an edit stamp.\n const dataChanged =\n editing &&\n form.fields.some(field => {\n const before = (submission.data ?? {})[field.name];\n const after = draftData[field.name];\n return JSON.stringify(before ?? null) !== JSON.stringify(after ?? null);\n });\n\n const dirty =\n dataChanged ||\n status !== submission.status ||\n notes !== (submission.notes ?? \"\");\n\n const handleSave = async () => {\n if (!canUpdate) return;\n setSaving(true);\n setSaveError(null);\n try {\n const changes: Record<string, unknown> = {};\n if (status !== submission.status) {\n changes.status = status;\n // Recovering from spam clears the stale detection reason.\n if (submission.status === \"spam\" && status !== \"spam\") {\n changes.spamReason = null;\n }\n }\n if (notes !== (submission.notes ?? \"\")) changes.notes = notes;\n if (dataChanged) changes.data = draftData;\n if (Object.keys(changes).length > 0) await onSave(changes);\n setEditing(false);\n } catch (err) {\n setSaveError(err instanceof Error ? err.message : \"Save failed\");\n } finally {\n setSaving(false);\n }\n };\n\n return (\n <Sheet\n open\n onOpenChange={open => {\n if (!open) onClose();\n }}\n >\n <SheetContent\n side=\"right\"\n className=\"w-[560px] sm:max-w-[560px] p-0 flex flex-col\"\n >\n <SheetHeader className=\"p-4 border-b border-border\">\n <div className=\"flex items-center justify-between gap-2\">\n <SheetTitle className=\"min-w-0 truncate\">\n Submission — {form.name}\n </SheetTitle>\n <div className=\"flex shrink-0 items-center gap-1 mr-6\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 rounded-none\"\n disabled={!onPrev}\n onClick={onPrev}\n aria-label=\"Previous submission\"\n >\n <ChevronLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 rounded-none\"\n disabled={!onNext}\n onClick={onNext}\n aria-label=\"Next submission\"\n >\n <ChevronRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n </div>\n <SheetDescription>\n Submitted {formatDate(submission.submittedAt)}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"flex-1 overflow-y-auto p-4 space-y-6\">\n {/* Spam banner */}\n {submission.status === \"spam\" && (\n // Full-strength destructive border so the spam banner boundary is perceivable over its tinted fill.\n <div className=\"flex items-center gap-2 border border-destructive bg-destructive/5 p-3 text-sm text-destructive\">\n <TriangleAlert className=\"h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n Flagged as spam\n {submission.spamReason ? ` (${submission.spamReason})` : \"\"}. Set\n the status to New to recover it.\n </div>\n )}\n\n {/* Submitted values */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm font-medium text-foreground\">\n Submitted values\n </p>\n {canUpdate && !editing && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setEditing(true)}\n >\n <Pencil className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Edit\n </Button>\n )}\n </div>\n\n <div className=\"space-y-3 border border-border p-3\">\n {form.fields.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">\n The form&apos;s fields are unavailable.\n </p>\n )}\n {form.fields.map(field => (\n <div key={field.name} className=\"space-y-1\">\n <Label htmlFor={`submission-field-${field.name}`}>\n {field.label || field.name}\n </Label>\n {editing ? (\n <FieldValueInput\n field={field}\n value={draftData[field.name]}\n onChange={value =>\n setDraftData(prev => ({\n ...prev,\n [field.name]: value,\n }))\n }\n />\n ) : (\n <p className=\"text-sm text-foreground wrap-break-word\">\n {formatExportValue(\n (submission.data ?? {})[field.name],\n field\n ) || <span className=\"text-muted-foreground\">—</span>}\n </p>\n )}\n </div>\n ))}\n </div>\n\n {orphanedEntries.length > 0 && (\n <div className=\"space-y-2 border border-border bg-muted/40 p-3\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n No longer on the form\n </p>\n {orphanedEntries.map(([key, value]) => (\n <p key={key} className=\"text-sm text-muted-foreground\">\n <span className=\"font-mono\">{key}</span>:{\" \"}\n {formatExportValue(value)}\n </p>\n ))}\n </div>\n )}\n\n {submission.editedAt && (\n <p className=\"text-xs text-muted-foreground\">\n Edited {formatDate(submission.editedAt)}\n {submission.editedBy ? ` by ${submission.editedBy}` : \"\"} — the\n values above are not necessarily what the visitor sent.\n </p>\n )}\n </div>\n\n {/* Status + notes */}\n <div className=\"grid grid-cols-1 gap-4 border-t border-border pt-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"submission-status\">Status</Label>\n {/* Status/notes writes go through the same update permission\n as data edits — without it, everything here is read-only. */}\n <Select\n value={status}\n onValueChange={setStatus}\n disabled={!canUpdate}\n >\n <SelectTrigger\n id=\"submission-status\"\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"new\">New</SelectItem>\n <SelectItem value=\"read\">Read</SelectItem>\n <SelectItem value=\"archived\">Archived</SelectItem>\n <SelectItem value=\"spam\">Spam</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-1.5 sm:col-span-2\">\n <Label htmlFor=\"submission-notes\">Internal notes</Label>\n <Textarea\n id=\"submission-notes\"\n value={notes}\n onChange={e => setNotes(e.target.value)}\n rows={3}\n placeholder=\"Visible to admins only\"\n disabled={!canUpdate}\n />\n </div>\n </div>\n\n {/* Metadata */}\n <div className=\"space-y-1 border-t border-border pt-4 text-xs text-muted-foreground\">\n <p>\n IP: {submission.ipAddress || \"—\"}\n <span className=\"mx-1.5 text-muted-foreground\">·</span>\n ID: <span className=\"font-mono\">{submission.id}</span>\n </p>\n {submission.userAgent && (\n <p className=\"break-all\">Agent: {submission.userAgent}</p>\n )}\n {submission.spamReason && submission.status !== \"spam\" && (\n <p>\n Previously flagged:{\" \"}\n <Badge\n variant=\"outline\"\n className=\"rounded-none border-border px-1 py-0 text-[10px]\"\n >\n {submission.spamReason}\n </Badge>\n </p>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center justify-between gap-3 border-t border-border bg-muted p-4\">\n {saveError ? (\n <p className=\"text-xs text-destructive\">{saveError}</p>\n ) : (\n <span />\n )}\n <div className=\"flex items-center gap-3\">\n <Button type=\"button\" variant=\"outline\" onClick={onClose}>\n Close\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleSave()}\n disabled={!canUpdate || !dirty || saving}\n >\n {saving ? \"Saving…\" : \"Save\"}\n </Button>\n </div>\n </div>\n </SheetContent>\n </Sheet>\n );\n}\n\nexport default SubmissionSheet;\n","\"use client\";\n\n/**\n * Form Builder View\n *\n * Custom admin view that provides a visual drag-and-drop form builder.\n * Layout mirrors the Collection Builder for visual consistency.\n *\n * @module admin/FormBuilderView\n * @since 0.1.0\n */\n\n\"use client\";\n\nimport {\n Button,\n Card,\n FieldShell,\n FormActions,\n Input,\n Textarea,\n toast,\n Tabs,\n TabsList,\n TabsTrigger,\n Select,\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n} from \"@nextlyhq/ui\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport type { FormFieldCatalogType } from \"nextly/field-catalog\";\nimport { FORM_FIELD_TYPE_CATALOG } from \"nextly/field-catalog\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport type { FormField, FormFieldTypeId } from \"../types\";\n\nimport { FieldCards } from \"./components/builder/FieldCards\";\nimport {\n FormNotificationsTab,\n type NotificationDefaults,\n} from \"./components/builder/FormNotificationsTab\";\nimport { FormPreview } from \"./components/builder/FormPreview\";\nimport {\n FormSettingsTab,\n type SpamDefaults,\n} from \"./components/builder/FormSettingsTab\";\nimport { notificationsBlockingSave } from \"./components/builder/notification-addresses\";\nimport {\n FormBuilderProvider,\n useFormBuilder,\n createFieldFromType,\n createNotification,\n type FormNotification,\n} from \"./context/FormBuilderContext\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface FormBuilderViewProps {\n id?: string;\n entryId?: string;\n collection?: string;\n collectionSlug?: string;\n isCreating?: boolean;\n initialData?: {\n id?: string;\n name?: string;\n slug?: string;\n description?: string;\n status?: \"draft\" | \"published\" | \"closed\";\n closedMessage?: string;\n fields?: unknown[];\n settings?: Record<string, unknown>;\n notifications?: unknown[];\n };\n onSave?: (data: unknown) => void;\n onSuccess?: (entry?: Record<string, unknown>) => void;\n onCancel?: () => void;\n}\n\n// ============================================================================\n// Helper\n// ============================================================================\n\nfunction slugify(text: string): string {\n return text\n .toLowerCase()\n .trim()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/[\\s_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n// ============================================================================\n// Inner Component (uses context)\n// ============================================================================\n\nfunction FormBuilderViewInner({\n isCreating = false,\n collectionSlug = \"forms\",\n entryId,\n onSave,\n onSuccess,\n onCancel,\n}: Pick<\n FormBuilderViewProps,\n | \"isCreating\"\n | \"collectionSlug\"\n | \"entryId\"\n | \"onSave\"\n | \"onSuccess\"\n | \"onCancel\"\n>) {\n const {\n fields,\n activeTab,\n isDirty,\n formData,\n settings,\n notifications,\n setActiveTab,\n addField,\n selectField,\n seedNotifications,\n updateFormData,\n markAsSaved,\n } = useFormBuilder();\n\n const queryClient = useQueryClient();\n\n const [isSaving, setIsSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n\n // The host's resolved field enable/disable map. `null` until the config\n // request settles, so the Add dialog never flashes the unfiltered set. If\n // the request errors, every type is offered: the exclude list is an\n // authoring preference (the server accepts any field type in the form's\n // JSON regardless), so hiding all types on a transient failure would cost\n // more than a temporarily unfiltered picker.\n const [enabledTypes, setEnabledTypes] = useState<\n FormFieldCatalogType[] | null\n >(null);\n\n // Type ids the host explicitly disabled (`config.fields[type] === false`),\n // including plugin type ids — so the host exclude layer applies to\n // contributed field types too, not just built-ins.\n const [disabledFieldTypes, setDisabledFieldTypes] = useState<Set<string>>(\n () => new Set()\n );\n\n // The host's notification defaults (plugin options). `null` until the\n // config request settles; `{}` when the request failed or nothing is\n // configured, so consumers can distinguish \"loading\" from \"no defaults\".\n const [notificationDefaults, setNotificationDefaults] =\n useState<NotificationDefaults | null>(null);\n\n // Plugin-level spam defaults, so the Settings tab can show what\n // \"inherit\" resolves to. `null` while the config request settles.\n const [spamDefaults, setSpamDefaults] = useState<SpamDefaults | null>(null);\n\n // Collections a form may redirect to. `null` while the config request\n // settles, so the Settings tab can tell \"not configured\" from \"not known\n // yet\" and not flash the option away.\n const [redirectCollections, setRedirectCollections] = useState<\n string[] | null\n >(null);\n\n // Whether the configuration request FAILED, kept apart from what it\n // returned. A 403 or a 500 mapped to an empty list is indistinguishable from\n // \"this site configures no redirect targets\" — so the Settings tab would\n // state that as fact, hide the option, and offer no way to tell the\n // difference or try again.\n const [redirectConfigFailed, setRedirectConfigFailed] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n const allTypes = FORM_FIELD_TYPE_CATALOG.map(entry => entry.type);\n void fetch(\n \"/admin/api/plugins/@nextlyhq/plugin-form-builder/builder-config\",\n { credentials: \"include\" }\n )\n .then(response => (response.ok ? response.json() : null))\n .then(\n (\n config: {\n fields?: Record<string, boolean>;\n notifications?: NotificationDefaults;\n spamProtection?: SpamDefaults;\n redirectCollections?: string[];\n } | null\n ) => {\n if (cancelled) return;\n setEnabledTypes(\n config?.fields\n ? allTypes.filter(type => config.fields?.[type] !== false)\n : allTypes\n );\n setDisabledFieldTypes(\n new Set(\n Object.entries(config?.fields ?? {})\n .filter(([, enabled]) => enabled === false)\n .map(([type]) => type)\n )\n );\n setNotificationDefaults(config?.notifications ?? {});\n setSpamDefaults(config?.spamProtection ?? {});\n // `config === null` is the failure branch: the response was not ok.\n setRedirectConfigFailed(config === null);\n setRedirectCollections(config?.redirectCollections ?? []);\n }\n )\n .catch(() => {\n if (cancelled) return;\n setEnabledTypes(allTypes);\n setNotificationDefaults({});\n setSpamDefaults({});\n setRedirectConfigFailed(true);\n setRedirectCollections([]);\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n // Seed a new form with the default admin-notification rule once the host\n // defaults are known. Guarded by a ref so deleting the seeded rule is\n // final — the effect must never re-seed a list the user emptied.\n const seededRef = useRef(false);\n useEffect(() => {\n if (!isCreating || notificationDefaults === null || seededRef.current) {\n return;\n }\n seededRef.current = true;\n seedNotifications([\n {\n ...createNotification(),\n name: \"Admin notification\",\n to: notificationDefaults.defaultToEmail ?? \"\",\n },\n ]);\n }, [isCreating, notificationDefaults, seedNotifications]);\n\n const handleAddField = useCallback(\n (type: FormFieldTypeId) => {\n const newField = createFieldFromType(\n type,\n fields.map(f => f.name)\n );\n addField(newField);\n selectField(newField.name);\n },\n [fields, addField, selectField]\n );\n\n // ── Save / Cancel ─────────────────────────────────────────────────────────\n const handleSave = useCallback(async () => {\n // Check if there are fields\n if (fields.length === 0) {\n toast.error(\"Please add at least one field to the form\");\n return;\n }\n\n // A notification is written to the form as it is typed, so nothing between\n // the editor and here refuses one. The sheet this replaced disabled its own\n // commit for a blank name, and the field rejects a malformed address on\n // blur — both preconditions move to this save, which is now the only\n // commit, and both are answered by the module the editor itself asks.\n const blocked = notificationsBlockingSave(notifications);\n if (blocked) {\n toast.error(blocked);\n return;\n }\n\n const { id: _id, ...formDataWithoutId } = formData;\n const saveData = {\n ...formDataWithoutId,\n fields,\n settings,\n notifications,\n };\n\n if (onSave) {\n onSave(saveData);\n return;\n }\n\n setIsSaving(true);\n setSaveError(null);\n try {\n const url = isCreating\n ? `/admin/api/collections/${collectionSlug}/entries`\n : `/admin/api/collections/${collectionSlug}/entries/${entryId}`;\n const response = await fetch(url, {\n method: isCreating ? \"POST\" : \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify(saveData),\n });\n if (!response.ok) {\n throw new Error(\n saveErrorMessage(\n await response.json().catch(() => ({})),\n response.statusText\n )\n );\n }\n\n const result = (await response.json()) as {\n message?: string;\n item?: Record<string, unknown>;\n } & Record<string, unknown>;\n const savedEntry: Record<string, unknown> = result.item ?? result;\n void queryClient.invalidateQueries({\n queryKey: [\"entries\", \"list\", collectionSlug],\n });\n void queryClient.invalidateQueries({ queryKey: [\"entries\", \"count\"] });\n if (!isCreating && entryId) {\n void queryClient.invalidateQueries({\n queryKey: [\"entries\", \"detail\", collectionSlug, entryId],\n });\n }\n markAsSaved();\n toast.success(\n isCreating ? \"Form created successfully\" : \"Form updated successfully\"\n );\n if (onSuccess) onSuccess(savedEntry);\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Failed to save form\";\n setSaveError(message);\n toast.error(message);\n } finally {\n setIsSaving(false);\n }\n }, [\n formData,\n fields,\n settings,\n notifications,\n onSave,\n onSuccess,\n isCreating,\n collectionSlug,\n entryId,\n queryClient,\n markAsSaved,\n ]);\n\n const handleCancel = useCallback(() => {\n if (onCancel) onCancel();\n else if (typeof window !== \"undefined\") window.history.back();\n }, [onCancel]);\n\n const handleNameChange = useCallback(\n (name: string) => {\n updateFormData({ name });\n if (!formData.slug || formData.slug === slugify(formData.name)) {\n updateFormData({ slug: slugify(name) });\n }\n },\n [formData.name, formData.slug, updateFormData]\n );\n\n // Main tabs\n const mainTabs = [\n {\n value: \"builder\" as const,\n label: \"Builder\",\n count: fields.length > 0 ? fields.length : null,\n },\n {\n value: \"preview\" as const,\n label: \"Preview\",\n count: null,\n },\n { value: \"settings\" as const, label: \"Settings\", count: null },\n {\n value: \"notifications\" as const,\n label: \"Notifications\",\n count: notifications.length > 0 ? notifications.length : null,\n },\n ];\n\n // ── Render ────────────────────────────────────────────────────────────────\n // The host page owns the measure, the centring and the padding — this view\n // renders as content inside its frame, under its breadcrumb. Declaring a\n // shell here would nest one inset inside another and put this heading out of\n // line with that breadcrumb. Nothing below hand-rolls a card, a width cap or\n // a hack to escape either.\n return (\n <>\n {/* ── Page header ── */}\n <div className=\"mb-6\">\n <h1 className=\"text-2xl font-semibold text-foreground tracking-tight\">\n {isCreating ? \"Create Form\" : \"Edit Form\"}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {isCreating\n ? \"Design a new form with drag-and-drop fields.\"\n : \"Modify your form fields and settings.\"}\n </p>\n </div>\n\n {/* ── Form metadata ──\n A card, so the fields that identify the form read as one group and\n have a surface to sit on. The builder canvas below is deliberately\n NOT carded: it draws its own drop zone, and a frame around a frame\n reads as a nested box rather than as structure. */}\n {/* `pb-6` against `pt-5`: the box padding is equal at 20px, but the\n first thing inside is a LABEL and the last is a control. A label's\n line box carries 3px of leading above its glyphs, so the ink starts\n lower than the box does while the control's border ends flush —\n measured 24px of visible space above and 21px below. The extra step\n on the bottom is what makes the two read as equal. */}\n <Card className=\"mb-6 px-6 pt-5 pb-6\">\n <div className=\"flex flex-wrap gap-4\">\n <FieldShell\n label=\"Form Name\"\n htmlFor=\"form-name\"\n className=\"flex-1 min-w-[200px]\"\n >\n <Input\n type=\"text\"\n value={formData.name || \"\"}\n onChange={e => handleNameChange(e.target.value)}\n placeholder=\"e.g., Contact Form\"\n />\n </FieldShell>\n\n <FieldShell\n label=\"Slug\"\n htmlFor=\"form-slug\"\n className=\"flex-1 min-w-[160px]\"\n >\n <Input\n type=\"text\"\n value={formData.slug || \"\"}\n onChange={e => updateFormData({ slug: e.target.value })}\n placeholder=\"e.g., contact-form\"\n className=\"placeholder:text-muted-foreground\"\n />\n </FieldShell>\n\n {/* Status is a Radix Select: its Root accepts a fixed prop list and\n forwards none of the rest to the trigger DOM node, so a\n FieldShell clone's id/aria-describedby/aria-invalid would land\n on a component that drops them rather than on the focusable\n trigger. The render-function form of `children` sidesteps\n that: FieldShell hands the computed wiring to this function,\n which applies it to SelectTrigger — the actual focusable,\n ARIA-bearing element — instead of a clone that can never reach\n past `Select`'s root. */}\n <div className=\"w-36\">\n <FieldShell label=\"Status\" htmlFor=\"form-status\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={formData.status || \"draft\"}\n onValueChange={value =>\n updateFormData({\n status: value as \"draft\" | \"published\" | \"closed\",\n })\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n >\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"draft\">Draft</SelectItem>\n <SelectItem value=\"published\">Published</SelectItem>\n <SelectItem value=\"closed\">Closed</SelectItem>\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n </div>\n </div>\n\n {/* Shown only once the author has chosen Closed. The explanation is\n written at the moment the decision is made, which is when they know\n what it should say; a box that is always there would sit empty on\n the great majority of forms, which are never closed. */}\n {formData.status === \"closed\" && (\n <div className=\"mt-4\">\n <FieldShell\n label=\"Message for visitors\"\n htmlFor=\"form-closed-message\"\n // Prose, not a value: the half width the other fields take is\n // sized for a name or a slug, and wrapping a sentence every six\n // words makes it harder to read back what was written.\n width=\"full\"\n description=\"Shown to anyone who reaches this form now that it is closed. Left empty, they are told only that it is not accepting submissions.\"\n >\n <Textarea\n value={formData.closedMessage ?? \"\"}\n onChange={e =>\n updateFormData({ closedMessage: e.target.value })\n }\n placeholder=\"e.g., Applications closed on 31 March. Thank you to everyone who applied.\"\n rows={3}\n />\n </FieldShell>\n </div>\n )}\n </Card>\n\n {/* ── Main tab navigation ──\n On the page rather than inside the card above: the rail switches what\n is BELOW it, so grouping it with the metadata would attach it to the\n wrong thing. */}\n <div className=\"border-b border-border mb-6\">\n <Tabs\n value={activeTab}\n onValueChange={v => setActiveTab(v as typeof activeTab)}\n >\n {/* Layout only. The underline, the active and hover colours, the\n focus ring and the disabled state all come from the shared\n primitive; restating them here is how two copies of one\n appearance start drifting.\n\n The strip can overflow its container, so it asks for scrolling\n through `scrollable` rather than by putting `overflow-x-auto` on\n the list: that spelling makes the LIST the scroll container, which\n costs the rail its indicator. */}\n <TabsList scrollable className=\"bg-transparent justify-start gap-0\">\n {mainTabs.map(tab => (\n <TabsTrigger\n key={tab.value}\n value={tab.value}\n className=\"shrink-0 whitespace-nowrap\"\n >\n {tab.label}\n {/* `rounded-sm`, the adornment step, matching the shared\n Badge and the identically sized notification count.\n Square is the TAB's corner and it exists so the\n underline runs flush to the trigger's edges; a chip\n inside the label inherits none of that reasoning, and\n carrying it made the count read as a second tab. */}\n {tab.count !== null && (\n <span\n className={`inline-flex items-center justify-center min-w-[18px] h-[18px] px-1 text-[10px] font-semibold rounded-sm ml-2 transition-colors ${\n activeTab === tab.value\n ? \"bg-primary/5 text-primary\"\n : \"bg-primary/5 text-muted-foreground\"\n }`}\n >\n {tab.count}\n </span>\n )}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n </div>\n\n {/* ── Tab content ── */}\n\n {/* Builder tab */}\n {activeTab === \"builder\" && (\n <FieldCards\n enabledTypes={enabledTypes}\n disabledFieldTypes={disabledFieldTypes}\n onAddField={handleAddField}\n />\n )}\n\n {/* Preview tab */}\n {activeTab === \"preview\" && (\n <FormPreview fields={fields} formData={formData} />\n )}\n\n {/* Settings tab */}\n {activeTab === \"settings\" && (\n <FormSettingsTab\n spamDefaults={spamDefaults}\n redirectCollections={redirectCollections}\n redirectConfigFailed={redirectConfigFailed}\n />\n )}\n\n {/* Notifications tab */}\n {activeTab === \"notifications\" && (\n <FormNotificationsTab defaults={notificationDefaults} />\n )}\n\n {/* Save error */}\n {saveError && (\n // Full-strength destructive border so the error box boundary is perceivable over its tinted fill.\n <div className=\"mt-6 p-3 text-sm text-destructive bg-destructive/10 border border-destructive rounded-none\">\n {saveError}\n </div>\n )}\n\n {/* A form commits as one document, so there is exactly one action bar,\n fed the dirty flag the form state already tracks rather than a\n second computation of it. */}\n <FormActions dirty={isDirty}>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={handleCancel}\n disabled={isSaving}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => {\n void handleSave();\n }}\n disabled={isSaving}\n className=\"flex items-center gap-1.5\"\n >\n {isSaving ? (\n <>\n <svg\n className=\"animate-spin h-3.5 w-3.5\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"\n />\n </svg>\n Saving…\n </>\n ) : isCreating ? (\n \"Create\"\n ) : (\n \"Save Changes\"\n )}\n </Button>\n </FormActions>\n </>\n );\n}\n\n// ============================================================================\n// Main Component (provides context)\n// ============================================================================\n\n/**\n * What to tell an author when a save is refused.\n *\n * A validation refusal carries the generic `\"Validation failed.\"` at the top\n * level and puts the actionable part in `data.errors`. Reporting only the top\n * level tells an author their form was rejected and nothing about what to\n * change — every rule in the collection produces that same sentence.\n */\nexport function saveErrorMessage(body: unknown, statusText: string): string {\n const error = (\n body as {\n error?: {\n message?: string;\n data?: { errors?: { message?: string }[] };\n };\n }\n )?.error;\n\n const fieldIssues = (error?.data?.errors ?? [])\n .map(issue => issue.message)\n .filter((message): message is string => Boolean(message));\n\n return (\n fieldIssues.join(\" \") ||\n error?.message ||\n `Failed to save form: ${statusText}`\n );\n}\n\nexport function FormBuilderView({\n id,\n entryId,\n collection,\n collectionSlug,\n isCreating,\n initialData,\n onSave,\n onSuccess,\n onCancel,\n}: FormBuilderViewProps) {\n const resolvedEntryId = entryId || id || initialData?.id;\n const resolvedCollectionSlug = collectionSlug || collection || \"forms\";\n const resolvedIsCreating = isCreating ?? !resolvedEntryId;\n const providerKey = resolvedEntryId || \"new\";\n\n return (\n <FormBuilderProvider\n key={providerKey}\n initialData={{\n id: resolvedEntryId,\n name: initialData?.name,\n slug: initialData?.slug,\n description: initialData?.description,\n status: initialData?.status,\n closedMessage: initialData?.closedMessage,\n fields: initialData?.fields as FormField[],\n settings: initialData?.settings as Record<string, unknown>,\n notifications: initialData?.notifications as FormNotification[],\n }}\n >\n <FormBuilderViewInner\n isCreating={resolvedIsCreating}\n collectionSlug={resolvedCollectionSlug}\n entryId={resolvedEntryId}\n onSave={onSave}\n onSuccess={onSuccess}\n onCancel={onCancel}\n />\n </FormBuilderProvider>\n );\n}\n\nexport default FormBuilderView;\n","\"use client\";\n\n/**\n * Field Cards\n *\n * The form builder's field list: one collapsible card per field, edited\n * inline. This is the whole field-editing surface — there is no separate\n * palette or properties sidebar.\n *\n * Reordering is triple-redundant on purpose: drag handles (pointer), the\n * same handle with Space + arrow keys (dnd-kit keyboard sensor), and\n * explicit Move up / Move down menu items. Deleting a field that another\n * field's condition or a notification references is blocked with the list\n * of referrers instead of silently breaking them.\n *\n * @module admin/components/builder/FieldCards\n */\n\nimport {\n closestCenter,\n DndContext,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n sortableKeyboardCoordinates,\n useSortable,\n verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { usePluginFieldTypeEntries } from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@nextlyhq/ui\";\nimport * as Lucide from \"lucide-react\";\nimport type {\n FieldTypeCatalogEntry,\n FormFieldCatalogType,\n} from \"nextly/field-catalog\";\nimport { FORM_FIELD_TYPE_CATALOG } from \"nextly/field-catalog\";\nimport { useCallback, useMemo, useState } from \"react\";\n\nimport type { AnyFormField, FormFieldTypeId } from \"../../../types\";\nimport { buildFieldReferenceMap } from \"../../../utils/field-references\";\nimport { useFormBuilder } from \"../../context/FormBuilderContext\";\n\nimport { AddFieldDialog } from \"./AddFieldDialog\";\nimport { FieldEditor } from \"./FieldEditor\";\n\n// ---------------------------------------------------------------------------\n// Catalog helpers\n// ---------------------------------------------------------------------------\n\nconst CATALOG_BY_TYPE = new Map<\n string,\n FieldTypeCatalogEntry<FormFieldCatalogType>\n>(FORM_FIELD_TYPE_CATALOG.map(entry => [entry.type, entry]));\n\n/** Resolve a catalog icon name against lucide; unknown names fall back. */\nfunction CatalogIcon({\n name,\n className,\n}: {\n name: string | undefined;\n className?: string;\n}) {\n const icons = Lucide as unknown as Record<string, React.ElementType>;\n const Icon = (name && icons[name]) || Lucide.Type;\n return <Icon className={className} aria-hidden=\"true\" />;\n}\n\n// ---------------------------------------------------------------------------\n// One field card\n// ---------------------------------------------------------------------------\n\ninterface FieldCardProps {\n field: AnyFormField;\n index: number;\n total: number;\n expanded: boolean;\n onToggle: () => void;\n allFields: AnyFormField[];\n deleteBlockers: string[];\n onUpdate: (updates: Partial<AnyFormField>) => void;\n onDuplicate: () => void;\n onDelete: () => void;\n onMove: (direction: -1 | 1) => void;\n}\n\nfunction FieldCard({\n field,\n index,\n total,\n expanded,\n onToggle,\n allFields,\n deleteBlockers,\n onUpdate,\n onDuplicate,\n onDelete,\n onMove,\n}: FieldCardProps) {\n const {\n attributes,\n listeners,\n setNodeRef,\n setActivatorNodeRef,\n transform,\n transition,\n isDragging,\n } = useSortable({ id: field.name });\n\n const entry = CATALOG_BY_TYPE.get(field.type);\n const headerId = `field-card-header-${field.name}`;\n const bodyId = `field-card-body-${field.name}`;\n const deleteBlocked = deleteBlockers.length > 0;\n\n return (\n <div\n ref={setNodeRef}\n style={{ transform: CSS.Transform.toString(transform), transition }}\n className={\n // Dragging uses full-strength border-primary so the lifted card stands out; the resting state uses border-border, keeping the two distinct.\n isDragging\n ? \"border border-primary bg-card opacity-80 relative z-10\"\n : \"border border-border bg-card\"\n }\n >\n {/* Header row: every control is a real, separate interactive element —\n no nested-button markup. */}\n <div className=\"flex items-center gap-2 px-3 py-2.5\">\n <button\n type=\"button\"\n ref={setActivatorNodeRef}\n {...attributes}\n {...listeners}\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-none text-muted-foreground hover:text-foreground hover:bg-primary/5 cursor-grab active:cursor-grabbing focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n aria-label={`Reorder ${field.label || field.name}. Press Space to lift, arrow keys to move, Space to drop.`}\n >\n <Lucide.GripVertical className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={expanded}\n aria-controls={bodyId}\n id={headerId}\n className=\"flex min-w-0 flex-1 items-center gap-3 rounded-none py-1 text-left cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center bg-primary/5 text-primary\">\n <CatalogIcon name={entry?.icon} className=\"h-3.5 w-3.5\" />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-sm font-medium text-foreground\">\n {field.label || field.name}\n </span>\n <span className=\"block truncate text-xs text-muted-foreground\">\n {entry?.label ?? field.type}\n <span className=\"mx-1.5 text-muted-foreground\">·</span>\n <span className=\"font-mono\">{field.name}</span>\n </span>\n </span>\n {field.required && (\n <span className=\"shrink-0 border border-border bg-primary/5 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-primary\">\n Required\n </span>\n )}\n <Lucide.ChevronDown\n className={`h-4 w-4 shrink-0 text-muted-foreground transition-transform ${expanded ? \"rotate-180\" : \"\"}`}\n aria-hidden=\"true\"\n />\n </button>\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"h-8 w-8 shrink-0 rounded-none text-muted-foreground hover:text-foreground\"\n aria-label={`Actions for ${field.label || field.name}`}\n >\n <Lucide.MoreHorizontal className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"w-56 shadow-none border-border\"\n >\n <DropdownMenuItem\n disabled={index === 0}\n onClick={() => onMove(-1)}\n className=\"gap-2 cursor-pointer\"\n >\n <Lucide.ArrowUp className=\"h-4 w-4 text-muted-foreground\" />\n Move up\n </DropdownMenuItem>\n <DropdownMenuItem\n disabled={index === total - 1}\n onClick={() => onMove(1)}\n className=\"gap-2 cursor-pointer\"\n >\n <Lucide.ArrowDown className=\"h-4 w-4 text-muted-foreground\" />\n Move down\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={onDuplicate}\n className=\"gap-2 cursor-pointer\"\n >\n <Lucide.Copy className=\"h-4 w-4 text-muted-foreground\" />\n Duplicate\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n {deleteBlocked ? (\n <Tooltip delayDuration={0}>\n <TooltipTrigger asChild>\n <div>\n <DropdownMenuItem\n disabled\n className=\"gap-2 text-muted-foreground\"\n >\n <Lucide.Trash2 className=\"h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </div>\n </TooltipTrigger>\n <TooltipContent side=\"left\" className=\"max-w-64\">\n Used by {deleteBlockers.join(\", \")}. Remove those references\n first.\n </TooltipContent>\n </Tooltip>\n ) : (\n <DropdownMenuItem\n onClick={onDelete}\n className=\"gap-2 cursor-pointer text-destructive focus:text-destructive\"\n >\n <Lucide.Trash2 className=\"h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Inline properties: the card IS the properties panel. */}\n {expanded && (\n <div\n id={bodyId}\n role=\"region\"\n aria-labelledby={headerId}\n className=\"border-t border-border\"\n >\n <FieldEditor\n field={field}\n allFields={allFields}\n onUpdate={onUpdate}\n />\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// The list\n// ---------------------------------------------------------------------------\n\nexport interface FieldCardsProps {\n /**\n * Form-surface types the host has NOT excluded (the plugin's field\n * enable/disable option). `null` while the host configuration is still\n * loading — the Add dialog waits rather than flashing the unfiltered set.\n * Types outside this set stay renderable on existing fields but cannot be\n * added.\n */\n enabledTypes: readonly FormFieldCatalogType[] | null;\n /**\n * Type ids the host disabled (`config.fields[type] === false`). Applied to\n * plugin field types too, so the host exclude layer is not bypassed for\n * contributed types.\n */\n disabledFieldTypes?: ReadonlySet<string>;\n /** Creates a field of the given type and returns it (context helper). */\n onAddField: (type: FormFieldTypeId) => void;\n}\n\nexport function FieldCards({\n enabledTypes,\n disabledFieldTypes,\n onAddField,\n}: FieldCardsProps) {\n const {\n fields,\n notifications,\n selectedFieldId,\n selectField,\n updateField,\n deleteField,\n duplicateField,\n moveField,\n } = useFormBuilder();\n\n const [isAddOpen, setIsAddOpen] = useState(false);\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })\n );\n\n // Plugin field types that opted into the forms surface, offered alongside the\n // enabled built-ins. A plugin reusing a built-in id never shadows it.\n const pluginEntries = usePluginFieldTypeEntries(\"forms\");\n\n const entries = useMemo(() => {\n if (enabledTypes === null) return null;\n const allowed = new Set<string>(enabledTypes);\n const builtinTypes = new Set<string>(\n FORM_FIELD_TYPE_CATALOG.map(entry => entry.type)\n );\n const builtins: FieldTypeCatalogEntry<FormFieldTypeId>[] =\n FORM_FIELD_TYPE_CATALOG.filter(entry => allowed.has(entry.type));\n return [\n ...builtins,\n // A plugin type a built-in shadows, or one the host disabled, is excluded.\n ...pluginEntries.filter(\n entry =>\n !builtinTypes.has(entry.type) && !disabledFieldTypes?.has(entry.type)\n ),\n ];\n }, [enabledTypes, pluginEntries, disabledFieldTypes]);\n\n // One O(N^2) pass per fields/notifications change; each card then looks\n // its blockers up in O(1) instead of re-walking everything per render.\n const referenceMap = useMemo(\n () => buildFieldReferenceMap(fields, notifications),\n [fields, notifications]\n );\n\n const handleDragEnd = useCallback(\n (event: DragEndEvent) => {\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n const from = fields.findIndex(f => f.name === active.id);\n const to = fields.findIndex(f => f.name === over.id);\n if (from !== -1 && to !== -1) moveField(from, to);\n },\n [fields, moveField]\n );\n\n return (\n <div className=\"space-y-3\">\n {fields.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 border border-dashed border-border bg-card px-6 py-14 text-center\">\n <span className=\"flex h-10 w-10 items-center justify-center bg-primary/10 text-primary\">\n <Lucide.ListPlus className=\"h-5 w-5\" aria-hidden=\"true\" />\n </span>\n <div>\n <p className=\"text-sm font-medium text-foreground\">No fields yet</p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n Add the first field to start building this form.\n </p>\n </div>\n <Button type=\"button\" size=\"sm\" onClick={() => setIsAddOpen(true)}>\n Add field\n </Button>\n </div>\n ) : (\n <>\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragEnd={handleDragEnd}\n >\n <SortableContext\n items={fields.map(f => f.name)}\n strategy={verticalListSortingStrategy}\n >\n <ul className=\"space-y-2\" aria-label=\"Form fields\">\n {fields.map((field, index) => (\n <li key={field.name}>\n <FieldCard\n field={field}\n index={index}\n total={fields.length}\n expanded={selectedFieldId === field.name}\n onToggle={() =>\n selectField(\n selectedFieldId === field.name ? null : field.name\n )\n }\n allFields={fields}\n deleteBlockers={(referenceMap.get(field.name) ?? []).map(\n ref => ref.label\n )}\n onUpdate={updates => updateField(field.name, updates)}\n onDuplicate={() => duplicateField(field.name)}\n onDelete={() => deleteField(field.name)}\n onMove={direction => moveField(index, index + direction)}\n />\n </li>\n ))}\n </ul>\n </SortableContext>\n </DndContext>\n\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setIsAddOpen(true)}\n className=\"w-full border-dashed\"\n >\n <Lucide.Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Add field\n </Button>\n </>\n )}\n\n <AddFieldDialog\n open={isAddOpen}\n onOpenChange={setIsAddOpen}\n entries={entries}\n onAdd={onAddField}\n />\n </div>\n );\n}\n","\"use client\";\n\n/**\n * Form Builder Context\n *\n * Provides state management for the Form Builder UI.\n * Manages form fields, selection state, and form metadata.\n *\n * @module admin/context/FormBuilderContext\n * @since 0.1.0\n */\n\n\"use client\";\n\nimport { FORM_FIELD_TYPE_CATALOG } from \"nextly/field-catalog\";\nimport {\n createContext,\n useContext,\n useCallback,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport type {\n AnyFormField,\n FormFieldTypeId,\n FormNotification,\n FormSettings,\n} from \"../../types\";\nimport {\n DEFAULT_FORM_SETTINGS,\n normalizeFormSettings,\n} from \"../../utils/form-settings\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * The canonical settings shape lives in `types.ts` (the same shape the\n * collection group declares and the submit handler consumes); re-exported\n * here so admin components keep one import site.\n */\nexport type { FormSettings } from \"../../types\";\n\n/**\n * The notification rule type lives in `types.ts` (the same shape the send\n * path consumes); re-exported here so admin components keep one import site.\n */\nexport type { FormNotification } from \"../../types\";\n\nexport interface FormBuilderState {\n /** Form fields array */\n fields: AnyFormField[];\n /** Currently selected field ID (name) */\n selectedFieldId: string | null;\n /** Active tab: builder, preview, settings, or notifications */\n activeTab: \"builder\" | \"preview\" | \"settings\" | \"notifications\";\n /** Whether form has unsaved changes */\n isDirty: boolean;\n /** Form metadata */\n formData: {\n id?: string;\n name: string;\n slug: string;\n description?: string;\n status: \"draft\" | \"published\" | \"closed\";\n /** What a closed form tells a visitor who reaches it. */\n closedMessage?: string;\n };\n /** Form settings */\n settings: FormSettings;\n /** Email notifications */\n notifications: FormNotification[];\n}\n\nexport interface FormBuilderActions {\n /** Set all fields */\n setFields: (fields: AnyFormField[]) => void;\n /** Add a new field */\n addField: (field: AnyFormField, index?: number) => void;\n /** Update a field by name */\n updateField: (fieldName: string, updates: Partial<AnyFormField>) => void;\n /** Delete a field by name */\n deleteField: (fieldName: string) => void;\n /** Move a field from one index to another */\n moveField: (fromIndex: number, toIndex: number) => void;\n /** Duplicate a field */\n duplicateField: (fieldName: string) => void;\n /** Select a field */\n selectField: (fieldName: string | null) => void;\n /** Set active tab */\n setActiveTab: (\n tab: \"builder\" | \"preview\" | \"settings\" | \"notifications\"\n ) => void;\n /** Update form metadata */\n updateFormData: (updates: Partial<FormBuilderState[\"formData\"]>) => void;\n /** Update form settings */\n updateSettings: (updates: Partial<FormSettings>) => void;\n /** Add a notification */\n addNotification: (notification: FormNotification) => void;\n /** Duplicate a notification (the copy starts disabled) */\n duplicateNotification: (id: string) => void;\n /** Update a notification */\n updateNotification: (id: string, updates: Partial<FormNotification>) => void;\n /** Delete a notification */\n deleteNotification: (id: string) => void;\n /** Seed default notifications into an empty list without dirtying the form */\n seedNotifications: (rules: FormNotification[]) => void;\n /** Mark form as saved (clears dirty flag) */\n markAsSaved: () => void;\n}\n\nexport interface FormBuilderContextValue\n extends FormBuilderState,\n FormBuilderActions {}\n\nexport interface FormBuilderProviderProps {\n /** Initial form data (for editing existing forms) */\n initialData?: {\n id?: string;\n name?: string;\n slug?: string;\n description?: string;\n status?: \"draft\" | \"published\" | \"closed\";\n closedMessage?: string;\n fields?: AnyFormField[];\n settings?: Partial<FormSettings>;\n notifications?: FormNotification[];\n };\n /** Child components */\n children: ReactNode;\n}\n\n/** Default form settings (the canonical defaults from the settings reader) */\nexport const DEFAULT_SETTINGS: FormSettings = DEFAULT_FORM_SETTINGS;\n\n/** Create a new notification rule with default values */\nexport function createNotification(): FormNotification {\n const id = `notif_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 6)}`;\n return {\n id,\n name: \"New notification\",\n enabled: true,\n recipientType: \"static\",\n to: \"\",\n cc: [],\n bcc: [],\n };\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst FormBuilderContext = createContext<FormBuilderContextValue | null>(null);\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Generate a readable unique field name: the type itself for the first\n * field of that type, then a numeric suffix (`email`, `email_2`, ...).\n * Names key submission data, so they should read like keys a developer\n * would have chosen, not timestamps.\n */\nexport function generateFieldName(\n type: FormFieldTypeId,\n existingNames: readonly string[] = []\n): string {\n // A plugin type id can be hyphenated (`star-rating`) or start with a digit,\n // but a field name must match ^[a-zA-Z][a-zA-Z0-9_]*$ (it keys submission\n // data). Normalize to a safe key; built-in type ids are already valid and\n // pass through unchanged.\n const base =\n type\n .replace(/[^a-zA-Z0-9]+/g, \"_\")\n .replace(/^_+/, \"\")\n .replace(/^([0-9])/, \"field_$1\")\n .replace(/_+$/, \"\") || \"field\";\n const taken = new Set(existingNames);\n if (!taken.has(base)) return base;\n let suffix = 2;\n while (taken.has(`${base}_${suffix}`)) suffix += 1;\n return `${base}_${suffix}`;\n}\n\n/**\n * Generate a human-readable label from field type, using the shared catalog\n * so a new field's default label matches what the picker called the type. A\n * plugin type not in the built-in catalog falls back to a title-cased id.\n */\nexport function generateFieldLabel(type: FormFieldTypeId): string {\n const entry = FORM_FIELD_TYPE_CATALOG.find(row => row.type === type);\n if (entry) return entry.label;\n const titleCased = type\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n return titleCased || \"New Field\";\n}\n\n/**\n * Create a new field from a field type with default values.\n */\nexport function createFieldFromType(\n type: FormFieldTypeId,\n existingNames: readonly string[] = []\n): AnyFormField {\n const name = generateFieldName(type, existingNames);\n const label = generateFieldLabel(type);\n\n const baseField = {\n name,\n label,\n required: false,\n };\n\n switch (type) {\n case \"text\":\n return { ...baseField, type: \"text\" };\n\n case \"email\":\n return { ...baseField, type: \"email\" };\n\n case \"number\":\n return { ...baseField, type: \"number\" };\n\n case \"phone\":\n return { ...baseField, type: \"phone\" };\n\n case \"url\":\n return { ...baseField, type: \"url\" };\n\n case \"textarea\":\n return { ...baseField, type: \"textarea\", rows: 4 };\n\n case \"select\":\n return {\n ...baseField,\n type: \"select\",\n options: [\n { label: \"Option 1\", value: \"option_1\" },\n { label: \"Option 2\", value: \"option_2\" },\n ],\n };\n\n case \"checkbox\":\n return { ...baseField, type: \"checkbox\" };\n\n case \"radio\":\n return {\n ...baseField,\n type: \"radio\",\n options: [\n { label: \"Option 1\", value: \"option_1\" },\n { label: \"Option 2\", value: \"option_2\" },\n ],\n };\n\n case \"file\":\n return { ...baseField, type: \"file\" };\n\n case \"date\":\n return { ...baseField, type: \"date\" };\n\n case \"time\":\n return { ...baseField, type: \"time\" };\n\n case \"hidden\":\n return { ...baseField, type: \"hidden\" };\n\n default:\n // A plugin-contributed type keeps its own id; the plugin owns the editor\n // component and value semantics, so no built-in defaults are seeded.\n return { ...baseField, type };\n }\n}\n\n// ============================================================================\n// Provider\n// ============================================================================\n\n/**\n * FormBuilderProvider - Provides form builder state to child components\n *\n * Wraps the form builder UI to provide centralized state management\n * for fields, selection, and form metadata.\n *\n * @example\n * ```tsx\n * <FormBuilderProvider initialData={existingForm}>\n * <FormBuilderView />\n * </FormBuilderProvider>\n * ```\n */\nexport function FormBuilderProvider({\n initialData,\n children,\n}: FormBuilderProviderProps) {\n // State\n const [fields, setFieldsState] = useState<AnyFormField[]>(\n initialData?.fields || []\n );\n const [selectedFieldId, setSelectedFieldId] = useState<string | null>(null);\n const [activeTab, setActiveTab] = useState<\n \"builder\" | \"preview\" | \"settings\" | \"notifications\"\n >(\"builder\");\n const [isDirty, setIsDirty] = useState(false);\n const [formData, setFormData] = useState<FormBuilderState[\"formData\"]>({\n id: initialData?.id,\n name: initialData?.name || \"\",\n slug: initialData?.slug || \"\",\n description: initialData?.description,\n status: initialData?.status || \"draft\",\n closedMessage: initialData?.closedMessage,\n });\n // Stored settings may carry legacy keys from earlier builder versions —\n // the normalizer migrates them on read (nothing rewrites until save).\n const [settings, setSettings] = useState<FormSettings>(() =>\n normalizeFormSettings(initialData?.settings)\n );\n const [notifications, setNotifications] = useState<FormNotification[]>(\n initialData?.notifications || []\n );\n\n // Actions\n const setFields = useCallback((newFields: AnyFormField[]) => {\n setFieldsState(newFields);\n setIsDirty(true);\n }, []);\n\n const addField = useCallback((field: AnyFormField, index?: number) => {\n setFieldsState(prev => {\n if (index !== undefined && index >= 0 && index <= prev.length) {\n const newFields = [...prev];\n newFields.splice(index, 0, field);\n return newFields;\n }\n return [...prev, field];\n });\n setSelectedFieldId(field.name);\n setIsDirty(true);\n }, []);\n\n const updateField = useCallback(\n (fieldName: string, updates: Partial<AnyFormField>) => {\n setFieldsState(prev =>\n prev.map(f => (f.name === fieldName ? { ...f, ...updates } : f))\n );\n // If the field name is being changed, also update selectedFieldId\n // This prevents the sidebar from disappearing when renaming a field\n if (updates.name && updates.name !== fieldName) {\n setSelectedFieldId(updates.name);\n }\n setIsDirty(true);\n },\n []\n );\n\n const deleteField = useCallback(\n (fieldName: string) => {\n setFieldsState(prev => prev.filter(f => f.name !== fieldName));\n if (selectedFieldId === fieldName) {\n setSelectedFieldId(null);\n }\n setIsDirty(true);\n },\n [selectedFieldId]\n );\n\n const moveField = useCallback((fromIndex: number, toIndex: number) => {\n setFieldsState(prev => {\n if (\n fromIndex < 0 ||\n fromIndex >= prev.length ||\n toIndex < 0 ||\n toIndex >= prev.length\n ) {\n return prev;\n }\n const newFields = [...prev];\n const [removed] = newFields.splice(fromIndex, 1);\n newFields.splice(toIndex, 0, removed);\n return newFields;\n });\n setIsDirty(true);\n }, []);\n\n const duplicateField = useCallback(\n (fieldName: string) => {\n // Computed from the current fields (not inside the state updater):\n // updaters must stay pure, and the selection below needs the new name\n // deterministically.\n const fieldIndex = fields.findIndex(f => f.name === fieldName);\n if (fieldIndex === -1) return;\n\n const field = fields[fieldIndex];\n const newFieldName = generateFieldName(\n field.type,\n fields.map(f => f.name)\n );\n const newField: AnyFormField = {\n ...field,\n name: newFieldName,\n label: `${field.label} (Copy)`,\n };\n\n const newFields = [...fields];\n newFields.splice(fieldIndex + 1, 0, newField);\n setFieldsState(newFields);\n\n // Select the duplicated field so its card expands\n setSelectedFieldId(newFieldName);\n setIsDirty(true);\n },\n [fields]\n );\n\n const selectField = useCallback((fieldName: string | null) => {\n setSelectedFieldId(fieldName);\n }, []);\n\n const updateFormData = useCallback(\n (updates: Partial<FormBuilderState[\"formData\"]>) => {\n setFormData(prev => ({ ...prev, ...updates }));\n setIsDirty(true);\n },\n []\n );\n\n const updateSettings = useCallback((updates: Partial<FormSettings>) => {\n setSettings(prev => ({ ...prev, ...updates }));\n setIsDirty(true);\n }, []);\n\n const addNotification = useCallback((notification: FormNotification) => {\n setNotifications(prev => [...prev, notification]);\n setIsDirty(true);\n }, []);\n\n const duplicateNotification = useCallback((id: string) => {\n // The non-deterministic id is generated outside the updater (updaters\n // must stay pure and may re-run); the copy itself derives from `prev`\n // so rapid duplications never work from a stale snapshot.\n const copyId = `notif_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 6)}`;\n setNotifications(prev => {\n const index = prev.findIndex(n => n.id === id);\n if (index === -1) return prev;\n const source = prev[index];\n const copy: FormNotification = {\n ...source,\n id: copyId,\n name: `${source.name} (Copy)`,\n // Copies start disabled so duplicating never doubles the emails a\n // live rule sends until the copy is deliberately turned on.\n enabled: false,\n };\n const next = [...prev];\n next.splice(index + 1, 0, copy);\n return next;\n });\n setIsDirty(true);\n }, []);\n\n const seedNotifications = useCallback((rules: FormNotification[]) => {\n // Only fills an empty list, and does not mark the form dirty: the seed\n // is a starting point the user hasn't authored, not an unsaved edit.\n setNotifications(prev => (prev.length > 0 ? prev : rules));\n }, []);\n\n const updateNotification = useCallback(\n (id: string, updates: Partial<FormNotification>) => {\n setNotifications(prev =>\n prev.map(n => (n.id === id ? { ...n, ...updates } : n))\n );\n setIsDirty(true);\n },\n []\n );\n\n const deleteNotification = useCallback((id: string) => {\n setNotifications(prev => prev.filter(n => n.id !== id));\n setIsDirty(true);\n }, []);\n\n const markAsSaved = useCallback(() => {\n setIsDirty(false);\n }, []);\n\n // Memoized context value\n const value = useMemo<FormBuilderContextValue>(\n () => ({\n // State\n fields,\n selectedFieldId,\n activeTab,\n isDirty,\n formData,\n settings,\n notifications,\n // Actions\n setFields,\n addField,\n updateField,\n deleteField,\n moveField,\n duplicateField,\n selectField,\n setActiveTab,\n updateFormData,\n updateSettings,\n addNotification,\n duplicateNotification,\n updateNotification,\n deleteNotification,\n seedNotifications,\n markAsSaved,\n }),\n [\n fields,\n selectedFieldId,\n activeTab,\n isDirty,\n formData,\n settings,\n notifications,\n setFields,\n addField,\n updateField,\n deleteField,\n moveField,\n duplicateField,\n selectField,\n updateFormData,\n updateSettings,\n addNotification,\n duplicateNotification,\n updateNotification,\n deleteNotification,\n seedNotifications,\n markAsSaved,\n ]\n );\n\n return (\n <FormBuilderContext.Provider value={value}>\n {children}\n </FormBuilderContext.Provider>\n );\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * useFormBuilder - Access form builder context\n *\n * Hook to access form builder state and actions from within the builder UI.\n * Must be used within a FormBuilderProvider.\n *\n * @returns Form builder context value\n * @throws Error if used outside of FormBuilderProvider\n *\n * @example\n * ```tsx\n * function FieldList() {\n * const { fields, selectField, deleteField } = useFormBuilder();\n *\n * return (\n * <ul>\n * {fields.map(field => (\n * <li key={field.name} onClick={() => selectField(field.name)}>\n * {field.label}\n * <button onClick={() => deleteField(field.name)}>Delete</button>\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useFormBuilder(): FormBuilderContextValue {\n const context = useContext(FormBuilderContext);\n\n if (!context) {\n throw new Error(\"useFormBuilder must be used within a FormBuilderProvider\");\n }\n\n return context;\n}\n\n/**\n * useOptionalFormBuilder - Access form builder context safely\n *\n * Like useFormBuilder but returns null if not within a provider.\n * Useful for components that may be rendered outside of the form builder.\n *\n * @returns Form builder context value or null\n */\nexport function useOptionalFormBuilder(): FormBuilderContextValue | null {\n return useContext(FormBuilderContext);\n}\n","\"use client\";\n\n/**\n * Add Field Dialog\n *\n * Type selection for new form fields, rendered from the shared field-type\n * catalog via the SDK's FieldTypePicker so the form surface shows the same\n * labels, icons, and hints as every other field picker in the admin.\n *\n * @module admin/components/builder/AddFieldDialog\n */\n\nimport { FieldTypePicker } from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@nextlyhq/ui\";\nimport type { FieldTypeCatalogEntry } from \"nextly/field-catalog\";\nimport { useState } from \"react\";\n\nimport type { FormFieldTypeId } from \"../../../types\";\n\nexport interface AddFieldDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /**\n * The form surface's catalog — built-ins already filtered by the host's\n * excludes plus any plugin field types that opted into the forms surface;\n * `null` while the host configuration is still loading. `type` is the open\n * `FormFieldTypeId` so a plugin entry shares this shape with the built-ins.\n */\n entries: readonly FieldTypeCatalogEntry<FormFieldTypeId>[] | null;\n /** Called with the chosen type; the parent creates and selects the field. */\n onAdd: (type: FormFieldTypeId) => void;\n}\n\nexport function AddFieldDialog({\n open,\n onOpenChange,\n entries,\n onAdd,\n}: AddFieldDialogProps) {\n const [selected, setSelected] = useState<FormFieldTypeId>(\"text\");\n\n const selectable = entries?.some(entry => entry.type === selected)\n ? selected\n : entries?.[0]?.type;\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle>Add field</DialogTitle>\n <DialogDescription>\n Pick the field type. Everything else is edited on the field card.\n </DialogDescription>\n </DialogHeader>\n\n {entries === null ? (\n <p className=\"text-sm text-muted-foreground\" role=\"status\">\n Loading the available field types…\n </p>\n ) : entries.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n All field types are disabled by this site&apos;s form-builder\n configuration.\n </p>\n ) : (\n <FieldTypePicker\n entries={entries}\n value={selectable ?? \"text\"}\n onChange={setSelected}\n columns={3}\n ariaLabel=\"Field type\"\n />\n )}\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n disabled={!selectable}\n onClick={() => {\n if (selectable) onAdd(selectable);\n onOpenChange(false);\n }}\n >\n Add field\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","\"use client\";\n\n/**\n * Field Editor\n *\n * Properties panel for editing the selected field's configuration.\n * Provides tabbed interface for General, Validation, and Conditional settings.\n *\n * @module admin/components/builder/FieldEditor\n * @since 0.1.0\n */\n\n\"use client\";\nimport {\n drawsAnyValidationRule,\n FieldOptionsEditor,\n ValidationRulesEditor,\n withOptionIds,\n type FieldOptionsEditorProps,\n type ValidationRuleValues,\n} from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n FormLabelWithTooltip,\n Input,\n Checkbox,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@nextlyhq/ui\";\nimport { validationRulesForFieldType } from \"nextly/field-catalog\";\nimport { useState, useCallback, useMemo } from \"react\";\n\nimport type {\n AnyFieldValidation,\n AnyFormField,\n FormField,\n SelectFormField,\n RadioFormField,\n} from \"../../../types\";\nimport { isKnownFormField } from \"../../../types\";\n// From the metadata module rather than from `generate-schema`, which\n// implements it: the editor needs only to know WHICH rules bite for a\n// type, and importing that from the module that builds schemas pulls the\n// whole schema implementation and `zod` into every admin client bundle\n// before anyone opens a field.\nimport { enforcedValidationRules } from \"../../../utils/enforced-validation\";\n\nimport { ConditionalLogicEditor } from \"./ConditionalLogicEditor\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface FieldEditorProps {\n /** The field being edited (built-in or plugin-contributed). */\n field: AnyFormField;\n /** All fields in the form (for conditional logic references) */\n allFields: AnyFormField[];\n /** Callback when field is updated */\n onUpdate: (updates: Partial<AnyFormField>) => void;\n}\n\n// ============================================================================\n// Helper Components\n// ============================================================================\n\n/**\n * General properties tab - common field settings\n */\nfunction GeneralTab({\n field,\n allFields,\n onUpdate,\n}: {\n field: AnyFormField;\n allFields: AnyFormField[];\n onUpdate: (updates: Partial<AnyFormField>) => void;\n}) {\n // Check if other fields have conditional logic referencing this field\n const hasConditionalReferences = useMemo(() => {\n return allFields.some(f => {\n if (f.name === field.name || !f.conditionalLogic?.enabled) return false;\n return f.conditionalLogic.conditions.some(c => c.field === field.name);\n });\n }, [allFields, field.name]);\n\n return (\n <div className=\"space-y-6 py-4\">\n {/* Field Name */}\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Field Name (ID)\"\n htmlFor=\"field-name\"\n description=\"Used as the key in submission data. Should be unique and contain no spaces.\"\n />\n <Input\n id=\"field-name\"\n type=\"text\"\n value={field.name}\n onChange={e => onUpdate({ name: e.target.value })}\n pattern=\"^[a-zA-Z][a-zA-Z0-9_]*$\"\n className=\"bg-transparent\"\n />\n {hasConditionalReferences && (\n <p className=\"text-[11px] text-warning font-medium\">\n ⚠️ Changing this may break conditional logic referencing this field\n </p>\n )}\n </div>\n\n {/* Label - not shown for hidden fields */}\n {field.type !== \"hidden\" && (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Label\"\n htmlFor=\"field-label\"\n description=\"The text displayed above the field in the form.\"\n />\n <Input\n id=\"field-label\"\n type=\"text\"\n value={field.label}\n onChange={e => onUpdate({ label: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n )}\n\n {/* Placeholder - for text-like fields */}\n {(field.type === \"text\" ||\n field.type === \"email\" ||\n field.type === \"phone\" ||\n field.type === \"url\" ||\n field.type === \"number\" ||\n field.type === \"textarea\") && (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Placeholder\"\n htmlFor=\"field-placeholder\"\n description=\"The faint text shown inside the field when empty.\"\n />\n <Input\n id=\"field-placeholder\"\n type=\"text\"\n value={field.placeholder || \"\"}\n onChange={e => onUpdate({ placeholder: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n )}\n\n {/* Help Text - not shown for hidden fields */}\n {field.type !== \"hidden\" && (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Help Text\"\n htmlFor=\"field-help\"\n description=\"Additional instructions displayed below the field.\"\n />\n <Input\n id=\"field-help\"\n type=\"text\"\n value={field.helpText || \"\"}\n onChange={e => onUpdate({ helpText: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n )}\n\n {/* Required - not shown for hidden or checkbox fields */}\n {field.type !== \"hidden\" && field.type !== \"checkbox\" && (\n <div className=\"flex items-center gap-3 pt-1\">\n <Checkbox\n id=\"field-required\"\n checked={field.required || false}\n onCheckedChange={checked =>\n onUpdate({ required: checked === true })\n }\n />\n <FormLabelWithTooltip\n label=\"Required field\"\n htmlFor=\"field-required\"\n description=\"Forces the user to fill this field before submitting.\"\n />\n </div>\n )}\n\n {/* Width */}\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Width\"\n htmlFor=\"field-width\"\n description=\"How much space this field takes in a horizontal row.\"\n />\n <Select\n value={field.admin?.width || \"100%\"}\n onValueChange={value =>\n onUpdate({\n admin: {\n ...field.admin,\n width: value as \"25%\" | \"33%\" | \"50%\" | \"66%\" | \"75%\" | \"100%\",\n },\n })\n }\n >\n <SelectTrigger className=\"w-full bg-transparent border-input dark:bg-muted/50\">\n <SelectValue placeholder=\"Select width\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"100%\">Full Width (100%)</SelectItem>\n <SelectItem value=\"75%\">Three Quarters (75%)</SelectItem>\n <SelectItem value=\"66%\">Two Thirds (66%)</SelectItem>\n <SelectItem value=\"50%\">Half (50%)</SelectItem>\n <SelectItem value=\"33%\">One Third (33%)</SelectItem>\n <SelectItem value=\"25%\">One Quarter (25%)</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n {/* Type-specific options — built-ins only; a plugin field owns its own\n editor component and carries no built-in type-specific settings. */}\n {isKnownFormField(field) && (\n <TypeSpecificOptions field={field} onUpdate={onUpdate} />\n )}\n </div>\n );\n}\n\n/**\n * Type-specific field options\n */\nfunction TypeSpecificOptions({\n field,\n onUpdate,\n}: {\n field: FormField;\n onUpdate: (updates: Partial<FormField>) => void;\n}) {\n switch (field.type) {\n case \"select\":\n case \"radio\":\n return <OptionsEditor field={field} onUpdate={onUpdate} />;\n\n case \"textarea\": {\n const textareaField = field;\n return (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Rows\"\n htmlFor=\"field-rows\"\n description=\"Height of the textarea in text lines.\"\n />\n <Input\n id=\"field-rows\"\n type=\"number\"\n min={2}\n max={20}\n value={textareaField.rows || 4}\n onChange={e => onUpdate({ rows: parseInt(e.target.value) || 4 })}\n className=\"w-24 bg-transparent\"\n />\n </div>\n );\n }\n\n case \"file\": {\n const fileField = field;\n return (\n <>\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Accepted File Types\"\n htmlFor=\"field-accept\"\n description=\"Comma-separated MIME types (e.g., image/*, application/pdf).\"\n />\n <Input\n id=\"field-accept\"\n type=\"text\"\n value={fileField.accept || \"\"}\n onChange={e => onUpdate({ accept: e.target.value })}\n placeholder=\"image/*,application/pdf\"\n className=\"bg-transparent\"\n />\n </div>\n <div className=\"flex items-center gap-3 pt-1\">\n <Checkbox\n id=\"field-multiple\"\n checked={fileField.multiple || false}\n onCheckedChange={checked =>\n onUpdate({ multiple: checked === true })\n }\n />\n <FormLabelWithTooltip\n label=\"Allow multiple files\"\n htmlFor=\"field-multiple\"\n description=\"Users can upload more than one file at once.\"\n />\n </div>\n <div className=\"flex items-center gap-3 pt-1\">\n <Checkbox\n id=\"field-attach-to-email\"\n checked={fileField.attachToEmail || false}\n onCheckedChange={checked =>\n onUpdate({ attachToEmail: checked === true })\n }\n />\n <FormLabelWithTooltip\n label=\"Attach to notification emails\"\n htmlFor=\"field-attach-to-email\"\n description=\"Uploaded files will be attached to all notification emails for this form.\"\n />\n </div>\n </>\n );\n }\n\n case \"hidden\": {\n const hiddenField = field;\n return (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Default Value\"\n htmlFor=\"field-default\"\n description=\"This value will be submitted silently with the form.\"\n />\n <Input\n id=\"field-default\"\n type=\"text\"\n value={hiddenField.defaultValue || \"\"}\n onChange={e => onUpdate({ defaultValue: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n );\n }\n\n case \"date\": {\n const dateField = field;\n return (\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Minimum Date\"\n htmlFor=\"min-date\"\n description=\"Lower bound for date selection.\"\n />\n <Input\n id=\"min-date\"\n type=\"date\"\n value={dateField.min || \"\"}\n onChange={e => onUpdate({ min: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Maximum Date\"\n htmlFor=\"max-date\"\n description=\"Upper bound for date selection.\"\n />\n <Input\n id=\"max-date\"\n type=\"date\"\n value={dateField.max || \"\"}\n onChange={e => onUpdate({ max: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n </div>\n );\n }\n\n case \"number\": {\n const numField = field;\n return (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Step\"\n htmlFor=\"field-step\"\n description=\"Increment/decrement step (e.g., 0.01 for decimals).\"\n />\n <Input\n id=\"field-step\"\n type=\"number\"\n value={numField.validation?.step || \"\"}\n onChange={e =>\n onUpdate({\n validation: {\n ...numField.validation,\n step: parseFloat(e.target.value) || undefined,\n },\n })\n }\n placeholder=\"1\"\n className=\"w-24 bg-transparent\"\n />\n </div>\n );\n }\n\n default:\n return null;\n }\n}\n\ntype KitOption = FieldOptionsEditorProps[\"options\"][number];\n\n/**\n * Options editor for select/radio fields, delegating to the SDK's\n * FieldOptionsEditor (drag reorder, auto-generated values, CSV/JSON import,\n * duplicate-value reporting). The kit works on id-carrying rows for\n * drag-and-drop; the form stores plain {label, value} pairs, so ids live in\n * local state and are stripped before every write.\n */\nfunction OptionsEditor({\n field,\n onUpdate,\n}: {\n field: FormField;\n onUpdate: (updates: Partial<FormField>) => void;\n}) {\n const optionsField = field as SelectFormField | RadioFormField;\n\n const [kitOptions, setKitOptions] = useState<KitOption[]>(() =>\n withOptionIds(optionsField.options || [])\n );\n\n const handleOptionsChange = useCallback(\n (next: KitOption[]) => {\n setKitOptions(next);\n onUpdate({\n options: next.map(({ label, value }) => ({ label, value })),\n });\n },\n [onUpdate]\n );\n\n return (\n <div className=\"space-y-3 pt-6\">\n <FormLabelWithTooltip\n label=\"Options\"\n description=\"List of choices available for this field.\"\n />\n <FieldOptionsEditor\n options={kitOptions}\n onOptionsChange={handleOptionsChange}\n />\n </div>\n );\n}\n\n/**\n * Validation tab - field validation settings.\n *\n * Which rules a type accepts is ASKED of core rather than decided here. This\n * previously branched on `field.type === \"text\" || field.type === \"textarea\"`\n * and similar, which is a list that cannot see a type it was not written to\n * know about — a plugin-contributed field type was offered nothing at all, and\n * `url` and `phone` were handled by yet another list further down.\n *\n * The controls themselves are the shared kit's, so this surface and the schema\n * builder no longer drift on labels, help text or which rules exist. What stays\n * here is what is genuinely this surface's: its own tab chrome, its file-size\n * control (stored on the field rather than in `validation`), and the mapping of\n * its own storage key — this builder stores the custom message as\n * `errorMessage` where core names the rule `message`, and a shared component\n * that silently reconciled the two would make every stored value wrong.\n */\nfunction ValidationTab({\n field,\n onUpdate,\n}: {\n field: FormField;\n onUpdate: (updates: Partial<FormField>) => void;\n}) {\n // Read across field types rather than narrowing to one: the whole point of\n // the shared editor is that this tab no longer branches on `field.type`.\n const validation: AnyFieldValidation = useMemo(\n () => field.validation ?? {},\n [field.validation]\n );\n // What the type MEANS, narrowed to what this runtime enforces FOR THAT TYPE.\n // Both halves are asked rather than restated, so neither can drift behind the\n // other. The narrowing has to be per type: a single set of enforced rules\n // only says a rule is honoured somewhere, which would offer `min`/`max` on a\n // date whose schema reads its bounds from `field.min`/`field.max` and never\n // consults `validation` — a control storing a bound nothing reads, which is\n // worse than no control because the author believes the rule is in force.\n const allowed = useMemo(() => {\n const enforced = enforcedValidationRules(field.type);\n return validationRulesForFieldType(field.type).filter(rule =>\n enforced.includes(rule)\n );\n }, [field.type]);\n\n const applyRules = useCallback(\n (next: Partial<ValidationRuleValues>) => {\n const { message, ...rules } = next;\n onUpdate({\n validation: {\n ...validation,\n ...rules,\n // This surface's own key for the rule core calls `message`.\n ...(message === undefined ? {} : { errorMessage: message }),\n },\n });\n },\n [validation, onUpdate]\n );\n\n return (\n <div className=\"space-y-6 pt-2\">\n <ValidationRulesEditor\n allowed={allowed}\n value={{\n minLength: validation.minLength,\n maxLength: validation.maxLength,\n // No row bounds here: this surface neither stores nor enforces them,\n // and `allowed` above never admits them, so passing them would be a\n // value the editor could not draw and the runtime could not read.\n min: validation.min,\n max: validation.max,\n pattern: validation.pattern,\n message: validation.errorMessage,\n }}\n onChange={applyRules}\n />\n\n {/* Not a validation rule: a file's size limit is stored on the field\n itself rather than in `validation`, so it stays with this surface. */}\n {field.type === \"file\" && (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Max File Size (bytes)\"\n htmlFor=\"max-file-size\"\n description=\"Maximum allowed size in bytes (10MB ≈ 10485760).\"\n />\n <Input\n id=\"max-file-size\"\n type=\"number\"\n min={0}\n value={field.maxFileSize ?? \"\"}\n onChange={e =>\n onUpdate({\n maxFileSize: e.target.value\n ? parseInt(e.target.value)\n : undefined,\n })\n }\n placeholder=\"10485760\"\n className=\"bg-transparent\"\n />\n </div>\n )}\n\n {/* Asked, not restated: naming the types with no options here would be a\n third copy of the reasoning this tab just removed. */}\n {!drawsAnyValidationRule(allowed) && field.type !== \"file\" && (\n // Semantic border token so this info-box boundary is visible at the 3:1 UI minimum.\n <div className=\"p-3 bg-muted rounded-none text-xs text-muted-foreground text-center border border-dashed border-border\">\n No additional validation options for this field type.\n </div>\n )}\n </div>\n );\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * FieldEditor - Field properties configuration panel\n *\n * Provides a tabbed interface for editing field properties:\n * - General: name, label, placeholder, help text, required, width, type-specific options\n * - Validation: error messages, min/max length, patterns, etc.\n * - Conditional: show/hide logic based on other field values\n *\n * @example\n * ```tsx\n * <FieldEditor\n * field={selectedField}\n * allFields={allFields}\n * onUpdate={handleUpdate}\n * onDelete={handleDelete}\n * onDuplicate={handleDuplicate}\n * />\n * ```\n */\nexport function FieldEditor({ field, allFields, onUpdate }: FieldEditorProps) {\n const [activeTab, setActiveTab] = useState<string>(\"general\");\n\n return (\n <div className=\"flex flex-col\">\n {/* Tabs */}\n <Tabs\n value={activeTab}\n onValueChange={setActiveTab}\n className=\"flex-1 flex flex-col\"\n >\n {/* Semantic border token so the tab-bar bottom edge is visible at the 3:1 UI minimum. */}\n <div className=\"border-b bg-muted border-border\">\n {/* Layout only. The underline, its colours and the square corner come\n from the shared primitive; `w-full` spreads the triggers evenly and\n the active tint is this panel's own surface treatment. */}\n <TabsList className=\"w-full justify-start gap-0\">\n <TabsTrigger\n value=\"general\"\n className=\"w-full data-[state=active]:bg-background/50\"\n >\n General\n </TabsTrigger>\n <TabsTrigger\n value=\"validation\"\n className=\"w-full data-[state=active]:bg-background/50\"\n >\n Validation\n </TabsTrigger>\n <TabsTrigger\n value=\"conditional\"\n className=\"w-full data-[state=active]:bg-background/50\"\n >\n Conditional\n </TabsTrigger>\n </TabsList>\n </div>\n\n {/* Content */}\n <div className=\"p-4\">\n <TabsContent value=\"general\" className=\"mt-0\">\n <GeneralTab\n field={field}\n allFields={allFields}\n onUpdate={onUpdate}\n />\n </TabsContent>\n\n <TabsContent value=\"validation\" className=\"mt-0\">\n {isKnownFormField(field) ? (\n <ValidationTab field={field} onUpdate={onUpdate} />\n ) : (\n <p className=\"py-4 text-sm text-muted-foreground\">\n This field type manages its own validation.\n </p>\n )}\n </TabsContent>\n\n <TabsContent value=\"conditional\" className=\"mt-0\">\n <ConditionalLogicEditor\n field={field}\n allFields={allFields}\n onUpdate={onUpdate}\n />\n </TabsContent>\n </div>\n </Tabs>\n </div>\n );\n}\n\nexport default FieldEditor;\n","\"use client\";\n\n/**\n * Conditional Logic Editor\n *\n * Component for configuring conditional show/hide logic for form fields.\n * Allows users to define conditions based on other field values.\n *\n * @module admin/components/builder/ConditionalLogicEditor\n * @since 0.1.0\n */\n\n\"use client\";\n\nimport {\n ConditionRow,\n type ConditionOperatorName,\n type ConditionSource,\n} from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n FormLabelWithTooltip,\n Button,\n Checkbox,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@nextlyhq/ui\";\nimport { useCallback, useMemo } from \"react\";\n\nimport type {\n AnyFormField,\n ConditionalLogic,\n ConditionalLogicCondition,\n} from \"../../../types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ConditionalLogicEditorProps {\n /** The field being edited (built-in or plugin-contributed). */\n field: AnyFormField;\n /** All fields in the form (for field references) */\n allFields: AnyFormField[];\n /** Callback when conditional logic is updated */\n onUpdate: (updates: Partial<AnyFormField>) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * The comparisons `evaluateConditions` implements.\n *\n * The shared row offers seventeen operators, and this runtime understands\n * seven of them. Every operator offered has to be one this evaluator can\n * answer: a field configured with `isTrue` would store fine, render fine, and\n * then never match at form time, with nothing anywhere to say why. So the\n * vocabulary is pinned here rather than taken from the row's default.\n *\n * Typed as the shared row's operator names AND as this module's stored\n * comparisons, so the day either list moves the other stops compiling.\n */\nconst SUPPORTED_COMPARISONS = [\n \"equals\",\n \"notEquals\",\n \"contains\",\n \"isEmpty\",\n \"isNotEmpty\",\n \"greaterThan\",\n \"lessThan\",\n] as const satisfies readonly ConditionOperatorName[] &\n readonly ConditionalLogicCondition[\"comparison\"][];\n\nconst SUPPORTED_SET: ReadonlySet<string> = new Set(SUPPORTED_COMPARISONS);\n\n/**\n * Whether an operator the row reported is one this runtime can evaluate.\n *\n * `operatorsFor` already narrows what the row offers, so this should always\n * hold; it is a guard rather than an assertion so that if the two ever drift,\n * the condition is left as it was instead of stored in a shape the evaluator\n * will not understand.\n */\nfunction isSupportedComparison(\n operator: string\n): operator is ConditionalLogicCondition[\"comparison\"] {\n return SUPPORTED_SET.has(operator);\n}\n\n// The field types whose values are text, named as `FormFieldType` spells them.\n// `tel` and `password` were guesses and match nothing; the builder's telephone\n// field is `phone`, so it was falling through to the full comparison set and\n// offering \"greater than\" for a phone number.\nconst TEXT_TYPES = new Set([\n \"text\",\n \"textarea\",\n \"email\",\n \"url\",\n \"phone\",\n \"hidden\",\n]);\n\n/**\n * The comparisons worth offering for a source type, within what the evaluator\n * supports.\n *\n * Narrower than the full seven where the type makes some of them meaningless:\n * \"contains\" on a number compares substrings of a formatted value, which is\n * not what anyone building that condition means.\n */\nfunction comparisonsForType(\n sourceType: string | undefined\n): ConditionOperatorName[] {\n if (sourceType === \"checkbox\") return [\"equals\", \"notEquals\"];\n if (sourceType === \"number\") {\n return [\"equals\", \"notEquals\", \"greaterThan\", \"lessThan\"];\n }\n if (sourceType !== undefined && TEXT_TYPES.has(sourceType)) {\n return [\"equals\", \"notEquals\", \"contains\", \"isEmpty\", \"isNotEmpty\"];\n }\n return [...SUPPORTED_COMPARISONS];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * A form field as the shared row needs to see it.\n *\n * Choice fields hand over their options so the value editor becomes a dropdown\n * of exactly those, rather than a text box in which an option can be misspelled\n * into a condition that never matches.\n */\nfunction toSource(field: AnyFormField): ConditionSource {\n // Only the choice field types carry an option list, and `AnyFormField` is a\n // union over every type, so the property has to be probed rather than read.\n // An option with an empty value is dropped: a Select cannot render one, and\n // it is a placeholder row in the editor rather than a value to compare with.\n const options =\n \"options\" in field && Array.isArray(field.options)\n ? field.options\n .filter(option => option.value !== \"\")\n .map(option => ({ value: option.value, label: option.label }))\n : undefined;\n return {\n name: field.name,\n label: field.label || field.name,\n type: field.type,\n options,\n };\n}\n\n/**\n * A stored condition's value as the row edits it.\n *\n * Storage types this `unknown`, and the row edits scalars. An object that got\n * in some other way is shown as nothing rather than as `[object Object]`,\n * which is not a value anyone typed and not one the evaluator can use.\n */\nfunction toRowValue(value: unknown): string | number | boolean | undefined {\n if (value === null || value === undefined) return undefined;\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n return undefined;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * ConditionalLogicEditor - Configure conditional show/hide logic\n *\n * Allows users to define when a field should be shown or hidden\n * based on the values of other fields in the form.\n *\n * @example\n * ```tsx\n * <ConditionalLogicEditor\n * field={selectedField}\n * allFields={allFields}\n * onUpdate={handleUpdate}\n * />\n * ```\n */\nexport function ConditionalLogicEditor({\n field,\n allFields,\n onUpdate,\n}: ConditionalLogicEditorProps) {\n // Get current conditional logic or create default.\n // Wrapped in useMemo so dependent useCallbacks don't re-create on every render.\n const logic: ConditionalLogic = useMemo(\n () =>\n field.conditionalLogic || {\n enabled: false,\n action: \"show\",\n operator: \"AND\",\n conditions: [],\n },\n [field.conditionalLogic]\n );\n\n // Get other fields that can be referenced (exclude current field)\n const availableFields = allFields.filter(f => f.name !== field.name);\n\n // Update the entire conditional logic object\n const updateLogic = useCallback(\n (updates: Partial<ConditionalLogic>) => {\n onUpdate({\n conditionalLogic: { ...logic, ...updates },\n });\n },\n [logic, onUpdate]\n );\n\n // Toggle enabled state\n const handleToggleEnabled = useCallback(() => {\n updateLogic({ enabled: !logic.enabled });\n }, [logic.enabled, updateLogic]);\n\n // Add a new condition\n const handleAddCondition = useCallback(() => {\n const firstField = availableFields[0];\n if (!firstField) return;\n\n const newCondition: ConditionalLogicCondition = {\n field: firstField.name,\n comparison: \"equals\",\n value: \"\",\n };\n\n updateLogic({\n conditions: [...logic.conditions, newCondition],\n });\n }, [availableFields, logic.conditions, updateLogic]);\n\n // Update a specific condition\n const handleUpdateCondition = useCallback(\n (index: number, updates: Partial<ConditionalLogicCondition>) => {\n const newConditions = logic.conditions.map((c, i) =>\n i === index ? { ...c, ...updates } : c\n );\n updateLogic({ conditions: newConditions });\n },\n [logic.conditions, updateLogic]\n );\n\n // Remove a condition\n const handleRemoveCondition = useCallback(\n (index: number) => {\n const newConditions = logic.conditions.filter((_, i) => i !== index);\n updateLogic({ conditions: newConditions });\n },\n [logic.conditions, updateLogic]\n );\n\n return (\n <div className=\"space-y-6 pt-2\">\n {/* Enable toggle */}\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3\">\n <Checkbox\n id=\"logic-enabled\"\n checked={logic.enabled}\n onCheckedChange={handleToggleEnabled}\n />\n <FormLabelWithTooltip\n label=\"Enable conditional logic\"\n htmlFor=\"logic-enabled\"\n description=\"Show or hide this field based on other field values.\"\n />\n </div>\n </div>\n\n {logic.enabled && (\n // Semantic border token so this top divider is visible at the 3:1 UI minimum.\n <div className=\"space-y-4 pt-4 border-t border-border\">\n {/* Action and operator row */}\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\">\n <Select\n value={logic.action}\n onValueChange={value =>\n updateLogic({ action: value as \"show\" | \"hide\" })\n }\n >\n <SelectTrigger className=\"w-[100px] h-9 bg-transparent border-input dark:bg-muted/50\">\n <SelectValue placeholder=\"Show/Hide\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"show\">Show</SelectItem>\n <SelectItem value=\"hide\">Hide</SelectItem>\n </SelectContent>\n </Select>\n <span>this field when</span>\n <Select\n value={logic.operator}\n onValueChange={value =>\n updateLogic({ operator: value as \"AND\" | \"OR\" })\n }\n >\n <SelectTrigger className=\"w-[80px] h-9 bg-transparent border-input dark:bg-muted/50\">\n <SelectValue placeholder=\"All/Any\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"AND\">All</SelectItem>\n <SelectItem value=\"OR\">Any</SelectItem>\n </SelectContent>\n </Select>\n <span>of the following conditions are met:</span>\n </div>\n\n {/* Conditions list */}\n <div className=\"space-y-3 pt-4\">\n {logic.conditions.length === 0 ? (\n // Semantic border token so this empty-state boundary is visible at the 3:1 UI minimum.\n <div className=\"p-4 bg-muted rounded-none border border-dashed border-border text-center text-xs text-muted-foreground\">\n No conditions defined. Add a condition to get started.\n </div>\n ) : (\n logic.conditions.map((condition, index) => (\n // Semantic border token so this condition card boundary is visible at the 3:1 UI minimum.\n <div\n key={index}\n className=\"flex flex-col gap-2 p-3 rounded-none bg-muted border border-border relative group\"\n >\n {/* Source, comparison and value, shared with the schema\n builder. Stacked rather than in three columns: this card\n sits in a builder sidebar, where three columns leave each\n control too narrow to read its own selection. */}\n <ConditionRow\n className=\"grid grid-cols-1 gap-2\"\n condition={{\n field: condition.field,\n operator: condition.comparison,\n value: toRowValue(condition.value),\n }}\n sources={availableFields.map(toSource)}\n operatorsFor={comparisonsForType}\n onChange={next => {\n if (!next || !isSupportedComparison(next.operator))\n return;\n handleUpdateCondition(index, {\n field: next.field,\n comparison: next.operator,\n value: toRowValue(next.value),\n });\n }}\n />\n\n {/* Remove button */}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => handleRemoveCondition(index)}\n // Semantic border token so the remove button's boundary is visible at the 3:1 UI minimum.\n className=\"absolute -top-2 -right-2 h-6 w-6 rounded-full bg-background border border-border opacity-0 group-hover:opacity-100 transition-opacity\"\n title=\"Remove condition\"\n >\n <span className=\"text-xs\">×</span>\n </Button>\n </div>\n ))\n )}\n </div>\n\n {/* Add condition button */}\n {availableFields.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={handleAddCondition}\n className=\"w-full mt-4 border-dashed\"\n >\n + Add Condition\n </Button>\n ) : (\n // Full-strength warning border so this notice boundary is perceivable over its tinted fill.\n // The 600 shade rather than the base token: the base measures 4.37:1\n // once its own 10% fill composites over the page container, short of\n // the 4.5:1 text needs. The 600 shade holds 5.13:1 at its worst\n // surface in either mode.\n <p className=\"mt-4 text-center text-xs text-warning-600 font-medium bg-warning/10 p-2 rounded-none border border-warning\">\n Add more fields to the form to create conditions.\n </p>\n )}\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * Form Notifications Tab\n *\n * Per-form email notification rules: a list of cards, each editing in a\n * side sheet. A rule targets a static address or the visitor (via one of\n * the form's email fields), can set Reply-To from a field so replies reach\n * the visitor, and can carry a single send-condition evaluated against the\n * submitted data. An autoresponder is just a rule whose recipient is the\n * visitor — there is no separate feature.\n *\n * @module admin/components/builder/FormNotificationsTab\n */\n\nimport {\n Badge,\n Card,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n FieldShell,\n Grid,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n} from \"@nextlyhq/ui\";\nimport {\n Copy,\n Filter,\n Mail,\n MoreHorizontal,\n Pencil,\n Plus,\n Trash2,\n TriangleAlert,\n X,\n} from \"lucide-react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\n\nimport type { ConditionalLogicCondition } from \"../../../types\";\nimport {\n useFormBuilder,\n createNotification,\n type FormNotification,\n} from \"../../context/FormBuilderContext\";\n\nimport {\n addressError,\n addressErrorsIn,\n isValidEmail,\n parseFieldRef,\n type AddressErrors,\n type AddressField,\n} from \"./notification-addresses\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Host-level notification defaults surfaced by the builder-config route. */\nexport interface NotificationDefaults {\n defaultFrom?: string;\n defaultToEmail?: string;\n}\n\ninterface FormNotificationsTabProps {\n /** `null` while the builder-config request is still settling. */\n defaults: NotificationDefaults | null;\n}\n\ninterface ProviderOption {\n id: string;\n name: string;\n type: string;\n isDefault: boolean;\n}\n\ninterface TemplateOption {\n id: string;\n name: string;\n slug: string;\n isActive: boolean;\n /** \"template\" or \"layout\" — layouts wrap templates and cannot be sent. */\n kind?: string;\n}\n\ninterface FieldOption {\n name: string;\n label: string;\n type: string;\n}\n\n// ============================================================================\n// API helpers\n// ============================================================================\n\nasync function fetchProviders(): Promise<ProviderOption[]> {\n try {\n const res = await fetch(\"/admin/api/email-providers?pageSize=100&page=1\", {\n credentials: \"include\",\n });\n if (!res.ok) return [];\n const json = (await res.json()) as { providers?: ProviderOption[] };\n return json.providers ?? [];\n } catch {\n return [];\n }\n}\n\nasync function fetchTemplates(): Promise<TemplateOption[]> {\n try {\n const res = await fetch(\"/admin/api/email-templates\", {\n credentials: \"include\",\n });\n if (!res.ok) return [];\n const json = (await res.json()) as { templates?: TemplateOption[] };\n return json.templates ?? [];\n } catch {\n return [];\n }\n}\n\n// ============================================================================\n// Field-reference helpers\n// ============================================================================\n\nfunction toFieldRef(fieldName: string): string {\n return `{{${fieldName}}}`;\n}\n\n/**\n * Options for address dropdowns: the form's email fields, plus the\n * currently-referenced field even when it is not an email type, so a legacy\n * value keeps displaying instead of silently vanishing from the select.\n */\nfunction buildEmailFieldOptions(\n fields: readonly FieldOption[],\n currentRef: string | null\n): FieldOption[] {\n const emailFields = fields.filter(field => field.type === \"email\");\n if (currentRef && !emailFields.some(field => field.name === currentRef)) {\n const current = fields.find(field => field.name === currentRef);\n if (current) return [...emailFields, current];\n }\n return emailFields;\n}\n\n// ============================================================================\n// Condition labels\n// ============================================================================\n\nconst COMPARISON_LABELS: Record<\n ConditionalLogicCondition[\"comparison\"],\n string\n> = {\n equals: \"Equals\",\n notEquals: \"Does not equal\",\n contains: \"Contains\",\n isEmpty: \"Is empty\",\n isNotEmpty: \"Is not empty\",\n greaterThan: \"Greater than\",\n lessThan: \"Less than\",\n};\n\nconst VALUELESS_COMPARISONS: ReadonlySet<\n ConditionalLogicCondition[\"comparison\"]\n> = new Set([\"isEmpty\", \"isNotEmpty\"]);\n\n// ============================================================================\n// Notification Card\n// ============================================================================\n\ninterface NotificationCardProps {\n notification: FormNotification;\n providerName: string;\n fields: readonly FieldOption[];\n /** Whether this row's editor is showing. */\n expanded: boolean;\n /** The editor region's id, so the summary can point at what it opens. */\n editorId: string;\n onToggle: () => void;\n onDuplicate: () => void;\n onDelete: () => void;\n onToggleEnabled: (enabled: boolean) => void;\n /** The editor, rendered under the summary while `expanded`. */\n children: React.ReactNode;\n}\n\nfunction describeRecipient(\n notification: FormNotification,\n fields: readonly FieldOption[]\n): { text: string; missing: boolean } {\n if (notification.recipientType === \"field\") {\n const ref = parseFieldRef(notification.to);\n if (!ref) return { text: \"No recipient field selected\", missing: true };\n const field = fields.find(f => f.name === ref);\n if (!field) {\n // The referenced field was deleted (allowed while the rule was\n // disabled) — say so instead of presenting the dead name as valid.\n return {\n text: `Recipient field \"${ref}\" no longer exists`,\n missing: true,\n };\n }\n return {\n text: `To the visitor (${field.label})`,\n missing: false,\n };\n }\n if (!notification.to.trim()) {\n return { text: \"No recipient address\", missing: true };\n }\n return { text: `To ${notification.to}`, missing: false };\n}\n\nfunction NotificationCard({\n notification,\n providerName,\n fields,\n expanded,\n editorId,\n onToggle,\n onDuplicate,\n onDelete,\n onToggleEnabled,\n children,\n}: NotificationCardProps) {\n const recipient = describeRecipient(notification, fields);\n const ccCount =\n (notification.cc?.length ?? 0) + (notification.bcc?.length ?? 0);\n\n // Deleting a referenced field is only blocked for enabled rules, so a\n // disabled rule can legitimately hold references to fields that no longer\n // exist — surface that on the card instead of sending broken email later.\n const fieldNames = new Set(fields.map(f => f.name));\n const replyToRef = parseFieldRef(notification.replyTo);\n const hasStaleReference =\n (replyToRef !== null && !fieldNames.has(replyToRef)) ||\n (notification.condition !== undefined &&\n !fieldNames.has(notification.condition.field));\n\n const warnings: string[] = [];\n if (!notification.templateSlug) {\n warnings.push(\"No template — will not send\");\n }\n if (hasStaleReference) {\n warnings.push(\"References a deleted field — edit to repair\");\n }\n\n return (\n // `Card` rather than its classes written out again: the primitive owns the\n // surface colour, the radius, the foreground and the transition, and a\n // second spelling of them drifts the moment the shared surface changes.\n //\n // `overflow-hidden` because an expanded editor paints a full-width tinted\n // child; without clipping it squares off the corners the card just rounded.\n <Card className=\"overflow-hidden p-0\">\n {/* The dimming is the SUMMARY's, not the card's. A disabled rule reads as\n inactive at a glance, but an expanded editor inside a faded card fades\n every label, input and validation message the author is reading while\n they fix it — and takes the already-muted help text below its intended\n contrast. */}\n <div\n className={`flex items-center gap-2 px-3 py-2.5 ${notification.enabled ? \"\" : \"opacity-60\"}`}\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center bg-primary/5 text-primary\">\n <Mail className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </span>\n\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={expanded}\n aria-controls={editorId}\n className=\"flex min-w-0 flex-1 items-center gap-3 rounded-none py-1 text-left cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n // The NAME stays put while the state changes. `aria-expanded` above\n // already says open or closed, and a control whose name moves with\n // its state is one a screen-reader user cannot refer to twice — and\n // one no test can hold a handle on across a click.\n aria-label={`Edit notification ${notification.name}`}\n >\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-foreground\">\n {notification.name}\n </span>\n {notification.condition && (\n <Badge\n variant=\"outline\"\n className=\"shrink-0 gap-1 rounded-none border-border px-1.5 py-0 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground\"\n >\n <Filter className=\"h-2.5 w-2.5\" aria-hidden=\"true\" />\n Conditional\n </Badge>\n )}\n </span>\n <span className=\"block truncate text-xs text-muted-foreground\">\n <span className={recipient.missing ? \"text-destructive\" : \"\"}>\n {recipient.text}\n </span>\n {ccCount > 0 && (\n <span>\n <span className=\"mx-1.5 text-muted-foreground\">·</span>+\n {ccCount} cc/bcc\n </span>\n )}\n <span className=\"mx-1.5 text-muted-foreground\">·</span>\n {providerName}\n </span>\n </span>\n {warnings.length > 0 && (\n <span className=\"flex shrink-0 flex-col items-end gap-0.5\">\n {warnings.map(warning => (\n <span\n key={warning}\n className=\"flex items-center gap-1 text-xs font-medium text-destructive\"\n >\n <TriangleAlert className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {warning}\n </span>\n ))}\n </span>\n )}\n </button>\n\n <Switch\n checked={notification.enabled}\n onCheckedChange={onToggleEnabled}\n aria-label={`${notification.enabled ? \"Disable\" : \"Enable\"} notification ${notification.name}`}\n />\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 shrink-0 rounded-none text-muted-foreground hover:text-foreground\"\n aria-label={`Notification actions for ${notification.name}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"w-56 shadow-none border-border\"\n >\n <DropdownMenuItem\n onClick={onToggle}\n className=\"gap-2 cursor-pointer\"\n >\n <Pencil className=\"h-4 w-4 text-muted-foreground\" />\n {expanded ? \"Collapse\" : \"Edit\"}\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={onDuplicate}\n className=\"gap-2 cursor-pointer\"\n >\n <Copy className=\"h-4 w-4 text-muted-foreground\" />\n Duplicate\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n onClick={onDelete}\n className=\"gap-2 cursor-pointer text-destructive focus:text-destructive\"\n >\n <Trash2 className=\"h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Unmounted while collapsed rather than hidden. The editor holds a\n draft of the rule in its own state, seeded from the row; keeping a\n closed one mounted would keep a stale draft alive behind a summary\n that has moved on. */}\n {expanded && children}\n </Card>\n );\n}\n\n// ============================================================================\n// Recipient chip list (cc / bcc)\n// ============================================================================\n\ninterface AddressChipListProps {\n id: string;\n label: string;\n addresses: string[];\n placeholder: string;\n onChange: (addresses: string[]) => void;\n}\n\nfunction AddressChipList({\n id,\n label,\n addresses,\n placeholder,\n onChange,\n}: AddressChipListProps) {\n const [draft, setDraft] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n const add = useCallback(() => {\n const email = draft.trim();\n if (!email) return;\n // Nothing here goes through form submission, so constraint validation\n // never runs — reject malformed addresses before they can be persisted\n // and fail at delivery.\n if (!isValidEmail(email)) {\n setError(\"Enter a valid email address.\");\n return;\n }\n onChange([...addresses, email]);\n setDraft(\"\");\n setError(null);\n }, [draft, addresses, onChange]);\n\n return (\n <div className=\"space-y-1.5\">\n <Label htmlFor={id}>{label}</Label>\n {addresses.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {addresses.map((email, index) => (\n <Badge\n key={`${email}-${index}`}\n variant=\"outline\"\n className=\"gap-1.5 rounded-none border-border bg-primary/5 px-2 py-0.5 text-xs font-medium\"\n >\n <span>{email}</span>\n <button\n type=\"button\"\n className=\"text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary\"\n onClick={() =>\n onChange(addresses.filter((_, i) => i !== index))\n }\n aria-label={`Remove ${email}`}\n >\n <X className=\"h-3 w-3\" aria-hidden=\"true\" />\n </button>\n </Badge>\n ))}\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Input\n id={id}\n type=\"email\"\n value={draft}\n onChange={e => {\n setDraft(e.target.value);\n setError(null);\n }}\n onKeyDown={e => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n add();\n }\n }}\n placeholder={placeholder}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? `${id}-error` : undefined}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={add}\n className=\"shrink-0 px-3\"\n >\n Add\n </Button>\n </div>\n {error && (\n <p id={`${id}-error`} className=\"text-xs text-destructive\">\n {error}\n </p>\n )}\n </div>\n );\n}\n\ninterface RecipientFieldsProps {\n form: FormNotification;\n fields: readonly FieldOption[];\n defaults: NotificationDefaults | null;\n update: <K extends keyof FormNotification>(\n key: K,\n value: FormNotification[K]\n ) => void;\n /**\n * Switching target kinds invalidates the previous `to` value shape, so both\n * keys move together. It goes back to the editor rather than being done here\n * because the editor owns the write-through to the form.\n */\n onRecipientTypeChange: (kind: \"static\" | \"field\") => void;\n addressErrors: AddressErrors;\n validateAddress: (key: AddressField, value: string | undefined) => void;\n replyToMode: ReplyToMode;\n setReplyToMode: (mode: ReplyToMode) => void;\n}\n\n/**\n * Who the notification goes to: the recipient itself, the reply-to, and the\n * optional cc/bcc lists.\n *\n * Extracted from the editor because it is the one part of a notification that\n * is entirely about addresses — the editor around it is about identity,\n * sending and conditions — and because leaving it inline made a single\n * component long enough that no reader could hold it at once.\n */\nfunction RecipientFields({\n form,\n fields,\n defaults,\n update,\n onRecipientTypeChange,\n addressErrors,\n validateAddress,\n replyToMode,\n setReplyToMode,\n}: RecipientFieldsProps) {\n // Derived here rather than passed in: each of these is a function of `form`\n // and `fields`, which this component already has, and computing them in the\n // caller would be the same answer worked out somewhere else.\n const toRef = parseFieldRef(form.to);\n const toFieldOptions = buildEmailFieldOptions(fields, toRef);\n const replyToRef = parseFieldRef(form.replyTo);\n const replyToFieldOptions = buildEmailFieldOptions(fields, replyToRef);\n\n return (\n <>\n <div className=\"space-y-4 pt-4 border-t border-border\">\n <Grid cols={2} responsive>\n <FieldShell label=\"Send to\" htmlFor=\"notification-recipient-type\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={form.recipientType}\n onValueChange={value => {\n // Switching target kinds invalidates the previous `to`\n // value shape, so it resets rather than leaking a\n // {{ref}} into the static input (or vice versa).\n onRecipientTypeChange(value as \"static\" | \"field\");\n }}\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"static\">A specific address</SelectItem>\n <SelectItem value=\"field\">\n The visitor (email field)\n </SelectItem>\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n {form.recipientType === \"field\" ? (\n <div className=\"space-y-1.5\">\n <FieldShell label=\"Visitor email field\" htmlFor=\"notification-to\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={toRef ?? \"__none\"}\n onValueChange={value =>\n update(\"to\", value === \"__none\" ? \"\" : toFieldRef(value))\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none\">Select a field</SelectItem>\n {toFieldOptions.map(f => (\n <SelectItem key={f.name} value={f.name}>\n {f.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n {toFieldOptions.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">\n Add an email field to the form first.\n </p>\n )}\n </div>\n ) : (\n <FieldShell\n label=\"Recipient address\"\n htmlFor=\"notification-to\"\n width=\"half\"\n error={addressErrors.to}\n >\n <Input\n type=\"email\"\n value={form.to}\n onChange={e => update(\"to\", e.target.value)}\n onBlur={e => validateAddress(\"to\", e.target.value)}\n placeholder={defaults?.defaultToEmail || \"admin@example.com\"}\n />\n </FieldShell>\n )}\n </Grid>\n\n {/* Reply-To */}\n <Grid cols={2} responsive>\n <FieldShell label=\"Reply-To\" htmlFor=\"notification-replyto-mode\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={replyToMode}\n onValueChange={value => {\n const mode = value as ReplyToMode;\n setReplyToMode(mode);\n // A mode change always clears the stored value (the old\n // shape can't be represented in the new mode); it stays\n // absent — not an empty string — until the user picks a\n // field or types an address.\n update(\"replyTo\", undefined);\n }}\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">None</SelectItem>\n <SelectItem value=\"field\">\n The visitor (email field)\n </SelectItem>\n <SelectItem value=\"custom\">A custom address</SelectItem>\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n {replyToMode === \"field\" && (\n <FieldShell\n label=\"Visitor email field\"\n htmlFor=\"notification-replyto\"\n >\n {({ id, describedBy, invalid }) => (\n <Select\n value={replyToRef ?? \"__none\"}\n onValueChange={value =>\n update(\n \"replyTo\",\n value === \"__none\" ? undefined : toFieldRef(value)\n )\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none\">Select a field</SelectItem>\n {replyToFieldOptions.map(f => (\n <SelectItem key={f.name} value={f.name}>\n {f.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n )}\n {replyToMode === \"custom\" && (\n <FieldShell\n label=\"Reply-To address\"\n htmlFor=\"notification-replyto\"\n width=\"half\"\n error={addressErrors.replyTo}\n >\n <Input\n type=\"email\"\n value={form.replyTo ?? \"\"}\n onBlur={e => validateAddress(\"replyTo\", e.target.value)}\n onChange={e => update(\"replyTo\", e.target.value || undefined)}\n placeholder=\"replies@example.com\"\n />\n </FieldShell>\n )}\n </Grid>\n {replyToMode === \"field\" && (\n <p className=\"text-xs text-muted-foreground -mt-2\">\n Replying to this email answers the person who submitted the form.\n </p>\n )}\n\n <AddressChipList\n id=\"notification-cc\"\n label=\"CC (optional)\"\n addresses={form.cc}\n placeholder=\"cc@example.com\"\n onChange={cc => update(\"cc\", cc)}\n />\n <AddressChipList\n id=\"notification-bcc\"\n label=\"BCC (optional)\"\n addresses={form.bcc}\n placeholder=\"bcc@example.com\"\n onChange={bcc => update(\"bcc\", bcc)}\n />\n </div>\n </>\n );\n}\n\ninterface SendConditionFieldProps {\n condition: ConditionalLogicCondition | undefined;\n fields: readonly FieldOption[];\n update: <K extends keyof FormNotification>(\n key: K,\n value: FormNotification[K]\n ) => void;\n}\n\n/**\n * The optional rule that decides whether a notification sends at all.\n *\n * Its own component because it is the one part of the editor with a shape of\n * its own — absent, or a field/comparison/value triple — and inline it made\n * the editor read as two unrelated forms sharing a scope.\n */\nfunction SendConditionField({\n condition,\n fields,\n update,\n}: SendConditionFieldProps) {\n return (\n <>\n <div className=\"space-y-3 pt-4 border-t border-border\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n Send condition\n </p>\n <p className=\"text-xs text-muted-foreground\">\n Only send this notification when a submitted value matches.\n </p>\n </div>\n {!condition && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() =>\n update(\"condition\", {\n field: fields[0]?.name ?? \"\",\n comparison: \"equals\",\n value: \"\",\n })\n }\n disabled={fields.length === 0}\n >\n <Plus className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Add condition\n </Button>\n )}\n </div>\n\n {condition && (\n <div className=\"flex flex-wrap items-end gap-2 border border-border bg-muted/40 p-3\">\n <FieldShell\n label=\"Field\"\n htmlFor=\"notification-condition-field\"\n className=\"min-w-36 flex-1\"\n >\n {({ id, describedBy, invalid }) => (\n <Select\n value={condition.field || \"__none\"}\n onValueChange={value =>\n update(\"condition\", {\n ...condition,\n field: value === \"__none\" ? \"\" : value,\n })\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none\">Select a field</SelectItem>\n {fields.map(f => (\n <SelectItem key={f.name} value={f.name}>\n {f.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n <FieldShell\n label=\"Comparison\"\n htmlFor=\"notification-condition-comparison\"\n className=\"min-w-36 flex-1\"\n >\n {({ id, describedBy, invalid }) => (\n <Select\n value={condition.comparison}\n onValueChange={value =>\n update(\"condition\", {\n ...condition,\n comparison:\n value as ConditionalLogicCondition[\"comparison\"],\n })\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(COMPARISON_LABELS).map(([value, label]) => (\n <SelectItem key={value} value={value}>\n {label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n {!VALUELESS_COMPARISONS.has(condition.comparison) && (\n <FieldShell\n label=\"Value\"\n htmlFor=\"notification-condition-value\"\n className=\"min-w-36 flex-1\"\n >\n <Input\n type=\"text\"\n value={\n typeof condition.value === \"string\" ||\n typeof condition.value === \"number\"\n ? String(condition.value)\n : \"\"\n }\n onChange={e =>\n update(\"condition\", {\n ...condition,\n value: e.target.value,\n })\n }\n />\n </FieldShell>\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-9 w-9 rounded-none text-muted-foreground hover:text-destructive\"\n onClick={() => update(\"condition\", undefined)}\n aria-label=\"Remove send condition\"\n >\n <Trash2 className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n )}\n </div>\n </>\n );\n}\n\ninterface NotificationIdentityFieldsProps {\n form: FormNotification;\n providers: ProviderOption[];\n defaults: NotificationDefaults | null;\n templates: TemplateOption[];\n update: <K extends keyof FormNotification>(\n key: K,\n value: FormNotification[K]\n ) => void;\n addressErrors: AddressErrors;\n validateAddress: (key: AddressField, value: string | undefined) => void;\n}\n\n/**\n * What the notification IS: its name, which provider and template carry it,\n * and who it comes from.\n *\n * Separated from the recipients and the condition because those answer\n * different questions — where it goes, and whether it goes at all — and one\n * component answering all three was long enough that no reader could hold it.\n */\nfunction NotificationIdentityFields({\n form,\n providers,\n defaults,\n templates,\n update,\n addressErrors,\n validateAddress,\n}: NotificationIdentityFieldsProps) {\n // Derived here, from props this component already holds. Passing them in\n // would be the same answer worked out in the caller.\n const defaultProvider = providers.find(p => p.isDefault);\n const defaultProviderLabel = defaultProvider\n ? `System default (${defaultProvider.name})`\n : \"System default\";\n const senderPlaceholder = defaults?.defaultFrom || \"Provider default\";\n const senderHelp = defaults?.defaultFrom\n ? `Leave blank to send from ${defaults.defaultFrom} (the configured default).`\n : \"Leave blank to use the template or provider default address.\";\n\n return (\n <>\n {/* Name */}\n <FieldShell label=\"Name\" htmlFor=\"notification-name\" width=\"half\">\n <Input\n type=\"text\"\n value={form.name}\n onChange={e => update(\"name\", e.target.value)}\n placeholder=\"e.g. Admin notification\"\n />\n </FieldShell>\n\n {/* Provider & Template. A container-query grid rather than the\n viewport's `sm:` breakpoint: the admin content region is\n narrower than the window whenever both sidebars are open, so a\n viewport breakpoint promises columns this sheet does not have. */}\n <Grid cols={2} responsive>\n {/* Both `Select`-driven: FieldShell's render-function `children`\n applies the computed id/aria-describedby/aria-invalid to\n SelectTrigger, the actual focusable element, rather than to\n `Select`'s root (which accepts a fixed prop list and forwards\n none of the rest). */}\n <FieldShell label=\"Email provider\" htmlFor=\"notification-provider\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={form.providerId ?? \"__default\"}\n onValueChange={value =>\n update(\"providerId\", value === \"__default\" ? undefined : value)\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__default\">\n {defaultProviderLabel}\n </SelectItem>\n {providers.map(p => (\n <SelectItem key={p.id} value={p.id}>\n {p.name}\n {p.isDefault ? \" (Default)\" : \"\"}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n <div className=\"space-y-1.5\">\n <FieldShell label=\"Email template\" htmlFor=\"notification-template\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={form.templateSlug ?? \"__none\"}\n onValueChange={value =>\n update(\"templateSlug\", value === \"__none\" ? undefined : value)\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none\">Select a template</SelectItem>\n {templates\n .filter(t => t.isActive && t.kind !== \"layout\")\n .map(t => (\n <SelectItem key={t.id} value={t.slug}>\n {t.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n {!form.templateSlug && (\n <p className=\"flex items-center gap-1 text-xs text-destructive\">\n <TriangleAlert className=\"h-3 w-3\" aria-hidden=\"true\" />\n Required — a notification without a template is never sent.\n </p>\n )}\n </div>\n </Grid>\n\n {/* Sender */}\n <FieldShell\n label=\"Sender email\"\n htmlFor=\"notification-sender\"\n width=\"half\"\n description={addressErrors.senderEmail ? undefined : senderHelp}\n error={addressErrors.senderEmail}\n >\n <Input\n type=\"email\"\n value={form.senderEmail ?? \"\"}\n onChange={e => update(\"senderEmail\", e.target.value || undefined)}\n onBlur={e => validateAddress(\"senderEmail\", e.target.value)}\n placeholder={senderPlaceholder}\n />\n </FieldShell>\n </>\n );\n}\n\n// ============================================================================\n// Notification editor\n// ============================================================================\n\ntype ReplyToMode = \"none\" | \"field\" | \"custom\";\n\ninterface NotificationEditorProps {\n /**\n * The rule as the form holds it. Read on every render rather than copied\n * into local state: the summary row edits the same rule (its Enabled switch\n * writes straight to the form), and a second copy here would go stale the\n * moment it did — the next edit would then send the whole stale rule back\n * and undo it.\n */\n notification: FormNotification;\n /** Ties the row's toggle to this region for assistive tech. */\n editorId: string;\n providers: ProviderOption[];\n templates: TemplateOption[];\n fields: readonly FieldOption[];\n defaults: NotificationDefaults | null;\n /**\n * Every edit, as it happens. There is no save button here on purpose: the\n * page's own action bar commits the form, and a second one beside it could\n * only mean the same thing said twice or two different things unlabelled.\n */\n onChange: (notification: FormNotification) => void;\n}\n\nfunction initialReplyToMode(replyTo: string | undefined): ReplyToMode {\n if (!replyTo) return \"none\";\n return parseFieldRef(replyTo) ? \"field\" : \"custom\";\n}\n\nfunction NotificationEditor({\n notification,\n editorId,\n providers,\n templates,\n fields,\n defaults,\n onChange,\n}: NotificationEditorProps) {\n const form = notification;\n const [replyToMode, setReplyToMode] = useState<ReplyToMode>(\n initialReplyToMode(notification.replyTo)\n );\n // Save-time address errors, keyed by field. Nothing here goes through\n // form submission (every control is a type=\"button\" callback), so\n // constraint validation never runs — this is its replacement.\n // Seeded from the rule as it stands, so a row opened onto an address saved\n // earlier shows the problem straight away rather than waiting to be touched.\n const [addressErrors, setAddressErrors] = useState<AddressErrors>(() =>\n addressErrorsIn(notification)\n );\n\n const update = useCallback(\n <K extends keyof FormNotification>(key: K, value: FormNotification[K]) => {\n onChange({ ...notification, [key]: value });\n setAddressErrors(prev =>\n key in prev ? { ...prev, [key]: undefined } : prev\n );\n },\n [notification, onChange]\n );\n\n const changeRecipientType = useCallback(\n (kind: \"static\" | \"field\") => {\n onChange({ ...notification, recipientType: kind, to: \"\" });\n },\n [notification, onChange]\n );\n\n /**\n * Address validity, checked when the field is LEFT rather than on a save\n * press.\n *\n * Nothing here goes through form submission — every control is a\n * `type=\"button\"` callback — so constraint validation never runs and this is\n * its replacement. Blur rather than change, because an address is invalid\n * for most of the time it is being typed.\n */\n const validateAddress = useCallback(\n (key: AddressField, value: string | undefined) => {\n setAddressErrors(prev => ({ ...prev, [key]: addressError(key, value) }));\n },\n []\n );\n\n const condition = form.condition;\n\n return (\n <div id={editorId} className=\"border-t border-border bg-muted/20 px-4 py-5\">\n <div className=\"space-y-6\">\n <NotificationIdentityFields\n form={form}\n providers={providers}\n defaults={defaults}\n templates={templates}\n update={update}\n addressErrors={addressErrors}\n validateAddress={validateAddress}\n />\n {/* Recipients */}\n <RecipientFields\n form={form}\n fields={fields}\n defaults={defaults}\n update={update}\n onRecipientTypeChange={changeRecipientType}\n addressErrors={addressErrors}\n validateAddress={validateAddress}\n replyToMode={replyToMode}\n setReplyToMode={setReplyToMode}\n />\n {/* Send condition */}\n <SendConditionField\n condition={condition}\n fields={fields}\n update={update}\n />\n {/* Enabled */}\n <div className=\"flex items-center justify-between pt-4 border-t border-border\">\n <div>\n <Label htmlFor=\"notification-enabled\">Enabled</Label>\n <p className=\"text-xs text-muted-foreground\">\n Turn off to keep the rule without sending.\n </p>\n </div>\n <Switch\n id=\"notification-enabled\"\n checked={form.enabled}\n onCheckedChange={checked => update(\"enabled\", checked)}\n />\n </div>\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * FormNotificationsTab - the form's notification rules.\n *\n * Each rule references an existing email provider and template rather than\n * duplicating provider credentials or embedding a second template editor.\n */\nexport function FormNotificationsTab({ defaults }: FormNotificationsTabProps) {\n const {\n notifications,\n fields,\n addNotification,\n duplicateNotification,\n updateNotification,\n deleteNotification,\n } = useFormBuilder();\n\n const [providers, setProviders] = useState<ProviderOption[]>([]);\n const [templates, setTemplates] = useState<TemplateOption[]>([]);\n // Which row is open. One at a time: several open at once turns the list\n // into a wall of fields and loses the overview the summaries exist to give.\n const [expandedId, setExpandedId] = useState<string | null>(null);\n\n useEffect(() => {\n void fetchProviders().then(setProviders);\n void fetchTemplates().then(setTemplates);\n }, []);\n\n const fieldList = useMemo<FieldOption[]>(\n () => fields.map(f => ({ name: f.name, label: f.label, type: f.type })),\n [fields]\n );\n\n /**\n * A new rule is added immediately and opened, rather than composed in a\n * draft and committed on a button.\n *\n * That is the trade the inline editor makes: there is no save press to\n * attach a creation to, so the row exists from the moment it is asked for.\n * An unwanted one is removed the same way any other is, and an incomplete\n * one is inert — a notification without a template never sends, which the\n * row says on its face.\n */\n const addAndOpen = useCallback(() => {\n const created = createNotification();\n addNotification(created);\n setExpandedId(created.id);\n }, [addNotification]);\n\n const toggleExpanded = useCallback((id: string) => {\n setExpandedId(current => (current === id ? null : id));\n }, []);\n\n const getProviderName = useCallback(\n (providerId?: string) => {\n if (!providerId) {\n const def = providers.find(p => p.isDefault);\n return def ? `${def.name} (default)` : \"System default\";\n }\n return providers.find(p => p.id === providerId)?.name ?? \"Unknown\";\n },\n [providers]\n );\n\n return (\n // The measure belongs to the page's shell, not to this tab.\n <div>\n <div className=\"flex items-center justify-between mb-8 pb-4 border-b border-border\">\n <div>\n <h3 className=\"text-xl font-semibold text-foreground\">\n Notifications\n </h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Emails sent when someone submits this form.\n </p>\n </div>\n <Button type=\"button\" onClick={addAndOpen}>\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Add notification\n </Button>\n </div>\n\n {/* List */}\n {notifications.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center p-12 mt-6 border-2 border-dashed border-border rounded-none bg-muted\">\n <div className=\"flex items-center justify-center w-12 h-12 rounded-none border border-border bg-background mb-4 text-primary\">\n <Mail className=\"h-6 w-6\" aria-hidden=\"true\" />\n </div>\n <p className=\"text-base font-medium text-foreground mb-1\">\n No notifications yet\n </p>\n <p className=\"text-sm text-muted-foreground mb-6 text-center max-w-sm\">\n Add a notification to email someone when this form is submitted.\n </p>\n <Button type=\"button\" onClick={addAndOpen}>\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Add notification\n </Button>\n </div>\n ) : (\n <div className=\"space-y-2\">\n {notifications.map(notification => (\n <NotificationCard\n key={notification.id}\n notification={notification}\n providerName={getProviderName(notification.providerId)}\n fields={fieldList}\n expanded={expandedId === notification.id}\n editorId={`notification-editor-${notification.id}`}\n onToggle={() => toggleExpanded(notification.id)}\n onDuplicate={() => duplicateNotification(notification.id)}\n onDelete={() => deleteNotification(notification.id)}\n onToggleEnabled={enabled =>\n updateNotification(notification.id, { enabled })\n }\n >\n <NotificationEditor\n notification={notification}\n editorId={`notification-editor-${notification.id}`}\n providers={providers}\n templates={templates}\n fields={fieldList}\n defaults={defaults}\n onChange={next => updateNotification(notification.id, next)}\n />\n </NotificationCard>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport default FormNotificationsTab;\n","import type { FormNotification } from \"../../../types\";\nimport { parseFieldRef } from \"../../../utils/field-references\";\n\n/**\n * Deliberately loose email shape check (something@something.tld): the goal is\n * catching typos before they fail at delivery, not RFC 5322 conformance.\n */\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport function isValidEmail(value: string): boolean {\n return EMAIL_PATTERN.test(value.trim());\n}\n\n// Re-exported rather than re-implemented. Whether a value is a field reference\n// is the SEND path's question — a value the editor treats as one and delivery\n// does not goes out with its braces intact — so both read the same parser.\nexport { parseFieldRef };\n\n/** The address fields a notification can get wrong. */\nexport type AddressField = \"senderEmail\" | \"to\" | \"replyTo\";\n\nexport type AddressErrors = Partial<Record<AddressField, string>>;\n\nconst MESSAGE = \"Enter a valid email address.\";\n\n/**\n * Whether ONE address is unusable, by the single rule the whole feature uses.\n *\n * A blank address is not an error — every one of these is optional or has an\n * inherited default — and a `{{field}}` reference is not an address at all: it\n * resolves against each submission at send time, so it cannot be checked now.\n */\nexport function addressError(\n field: AddressField,\n value: string | undefined\n): string | undefined {\n if (field === \"replyTo\" && parseFieldRef(value) !== null) return undefined;\n if (!value?.trim()) return undefined;\n return isValidEmail(value) ? undefined : MESSAGE;\n}\n\n/**\n * Every unusable address in a notification.\n *\n * The editor asks this about one field as it is left; the page asks it about\n * every rule before it saves. The same function answers both, because the\n * alternative is a field that rejects an address and a save that accepts it —\n * which is what happened while the editor held the only copy of the answer.\n *\n * `to` is checked only when the recipient is a literal address: a `field`\n * recipient names a form field, which is not an address to validate.\n */\nexport function addressErrorsIn(notification: FormNotification): AddressErrors {\n const errors: AddressErrors = {};\n\n const sender = addressError(\"senderEmail\", notification.senderEmail);\n if (sender) errors.senderEmail = sender;\n\n if (notification.recipientType === \"static\") {\n const to = addressError(\"to\", notification.to);\n if (to) errors.to = to;\n }\n\n const replyTo = addressError(\"replyTo\", notification.replyTo);\n if (replyTo) errors.replyTo = replyTo;\n\n return errors;\n}\n\n/** The rules a save must refuse, named so the message can say which. */\nexport function notificationsWithBadAddresses(\n notifications: readonly FormNotification[]\n): string[] {\n return notifications\n .filter(n => Object.keys(addressErrorsIn(n)).length > 0)\n .map(n => n.name.trim() || \"Untitled notification\");\n}\n\n/**\n * What to tell an author who pressed Save with a malformed address, or `null`\n * when there is nothing to tell them.\n *\n * The wording lives beside the rule that produces it so the caller has one\n * question to ask and one branch to write — a save path that composed this\n * itself would be a second place deciding what counts as wrong.\n */\nexport function badAddressMessage(\n notifications: readonly FormNotification[]\n): string | null {\n const names = notificationsWithBadAddresses(notifications);\n if (names.length === 0) return null;\n return names.length === 1\n ? `${names[0]} has an invalid email address`\n : `${names.length} notifications have invalid email addresses`;\n}\n\n/**\n * The one reason a save must refuse the notifications, or `null`.\n *\n * A single question so the save has a single branch. Naming comes first: an\n * address complaint that says \"Untitled notification\" twice cannot be acted on,\n * whereas a named rule can then be pointed at.\n */\nexport function notificationsBlockingSave(\n notifications: readonly FormNotification[]\n): string | null {\n const unnamed = unnamedNotificationCount(notifications);\n if (unnamed > 0) {\n return unnamed === 1\n ? \"A notification needs a name\"\n : `${unnamed} notifications need a name`;\n }\n return badAddressMessage(notifications);\n}\n\n/**\n * Rules a save must refuse for having no name.\n *\n * The sheet this editor replaced disabled its own commit while the name was\n * blank. There is no commit here any more — the page's action bar is the only\n * one — so the precondition moves to it rather than disappearing with the\n * button that used to carry it. A nameless rule leaves its summary and its\n * menu labelled with nothing.\n */\nexport function unnamedNotificationCount(\n notifications: readonly FormNotification[]\n): number {\n return notifications.filter(n => !n.name.trim()).length;\n}\n","\"use client\";\n\n/**\n * Form Preview\n *\n * An interactive simulation of the form: real enabled inputs, conditional\n * logic evaluated live against what you type (the same evaluator the\n * runtime uses), the form's actual settings (button text, confirmation\n * behavior), and a device-width toggle. It is honest about what it is —\n * a faithful simulation inside the admin, not the published form; nothing\n * here submits anywhere.\n *\n * @module admin/components/builder/FormPreview\n */\n\nimport {\n Badge,\n Button,\n Checkbox,\n Input,\n Card,\n Label,\n RadioGroup,\n RadioGroupItem,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Tabs,\n TabsList,\n TabsTrigger,\n Textarea,\n} from \"@nextlyhq/ui\";\nimport { Eye, Monitor, Paperclip, RotateCcw, Smartphone } from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\n\nimport type { AnyFormField, CustomFormField, FormField } from \"../../../types\";\nimport { isKnownFormField } from \"../../../types\";\nimport { evaluateConditions } from \"../../../utils/evaluate-conditions\";\nimport { useFormBuilder } from \"../../context/FormBuilderContext\";\n\nimport { hasStoredRedirectPage } from \"./FormSettingsTab\";\n\n// ---------------------------------------------------------------------------\n// Field rendering\n// ---------------------------------------------------------------------------\n\nfunction PreviewFieldInput({\n field,\n value,\n onChange,\n}: {\n field: FormField;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const id = `preview-${field.name}`;\n switch (field.type) {\n case \"textarea\":\n return (\n <Textarea\n id={id}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n rows={\"rows\" in field ? (field.rows ?? 4) : 4}\n placeholder={field.placeholder}\n />\n );\n case \"checkbox\":\n // A real checkbox, not a toggle switch — the preview should look like\n // what a frontend form renders for this type.\n return (\n <Checkbox\n id={id}\n checked={Boolean(value)}\n onCheckedChange={checked => onChange(checked === true)}\n />\n );\n case \"select\": {\n const options = \"options\" in field ? (field.options ?? []) : [];\n return (\n <Select\n value={typeof value === \"string\" ? value : \"\"}\n onValueChange={onChange}\n >\n <SelectTrigger\n id={id}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue placeholder={field.placeholder ?? \"Select…\"} />\n </SelectTrigger>\n <SelectContent>\n {options.map(option => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n }\n case \"radio\": {\n const options = \"options\" in field ? (field.options ?? []) : [];\n return (\n <RadioGroup\n value={typeof value === \"string\" ? value : \"\"}\n onValueChange={onChange}\n className=\"flex flex-col gap-2\"\n >\n {options.map(option => (\n <div key={option.value} className=\"flex items-center gap-2\">\n <RadioGroupItem\n value={option.value}\n id={`${id}-${option.value}`}\n />\n <Label htmlFor={`${id}-${option.value}`} className=\"font-normal\">\n {option.label}\n </Label>\n </div>\n ))}\n </RadioGroup>\n );\n }\n case \"number\":\n return (\n <Input\n id={id}\n type=\"number\"\n value={\n typeof value === \"number\" || typeof value === \"string\"\n ? String(value)\n : \"\"\n }\n onChange={e =>\n onChange(e.target.value === \"\" ? \"\" : Number(e.target.value))\n }\n placeholder={field.placeholder}\n />\n );\n case \"date\":\n case \"time\":\n return (\n <Input\n id={id}\n type={field.type}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n />\n );\n case \"file\":\n // Nothing uploads from a preview; the affordance is shown, disabled.\n return (\n <div className=\"flex items-center gap-2 border border-dashed border-border bg-muted/40 px-3 py-2 text-sm text-muted-foreground\">\n <Paperclip className=\"h-4 w-4\" aria-hidden=\"true\" />\n File upload (disabled in preview)\n </div>\n );\n default:\n return (\n <Input\n id={id}\n type={\n field.type === \"email\"\n ? \"email\"\n : field.type === \"url\"\n ? \"url\"\n : field.type === \"phone\"\n ? \"tel\"\n : \"text\"\n }\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n placeholder={field.placeholder}\n />\n );\n }\n}\n\n/**\n * Preview for a plugin-contributed field. The plugin owns the real input, which\n * renders from its own component in the live form; the builder preview shows a\n * labeled placeholder so the field's presence and order stay visible.\n */\nfunction PluginFieldPreview({ field }: { field: CustomFormField }) {\n return (\n <div className=\"space-y-1.5\">\n <Label htmlFor={`preview-${field.name}`}>\n {field.label || field.name}\n {field.required && <span className=\"ml-1 text-destructive\">*</span>}\n </Label>\n <div className=\"flex items-center gap-2 border border-dashed border-border bg-muted/40 px-3 py-2 text-sm text-muted-foreground\">\n Custom field ({field.type}) — renders from its plugin component in the\n live form.\n </div>\n {field.helpText && (\n <p className=\"text-xs text-muted-foreground\">{field.helpText}</p>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface FormPreviewProps {\n /** Array of form fields to preview (built-in or plugin-contributed). */\n fields: AnyFormField[];\n /** Form metadata */\n formData?: {\n name?: string;\n slug?: string;\n description?: string;\n };\n}\n\n/**\n * What the visitor is told after a successful submission.\n *\n * Its own component because the fall-through belongs to the MESSAGE branch: a\n * confirmation this does not name is previewed as a thank-you note, which is\n * how the page redirect came to be previewed as something it is not. Each\n * confirmation that is not a message has to be named here.\n */\nfunction Confirmation({\n settings,\n}: {\n settings: {\n confirmationType?: string;\n redirectUrl?: string;\n successMessage?: string;\n redirectPage?: unknown;\n };\n}) {\n const panel = \"border border-border bg-muted/40 p-4 text-sm text-foreground\";\n\n if (settings.confirmationType === \"redirect\") {\n return (\n <p className={panel}>\n The visitor would now be redirected to{\" \"}\n <span className=\"font-mono\">\n {settings.redirectUrl || \"(no redirect URL set)\"}\n </span>\n .\n </p>\n );\n }\n\n if (settings.confirmationType === \"relationship\") {\n // Whether a page is actually named, not merely that this confirmation is\n // chosen. Claiming a redirect with no page saved describes something that\n // cannot happen: the save is refused, and a form that reached this state\n // another way resolves to no redirect at all.\n const named = hasStoredRedirectPage(settings);\n return (\n <p className={panel}>\n {named\n ? \"The visitor would now be redirected to the linked page. Its URL is built when the form is submitted, from the pattern the site configured for that collection.\"\n : \"No page is selected yet, so this form has no destination. Choose one under Settings, or pick a different confirmation.\"}\n </p>\n );\n }\n\n return (\n <p className={panel}>\n {settings.successMessage || \"Thank you for your submission!\"}\n </p>\n );\n}\n\nexport function FormPreview({ fields, formData }: FormPreviewProps) {\n const { settings } = useFormBuilder();\n const [values, setValues] = useState<Record<string, unknown>>({});\n const [device, setDevice] = useState<\"desktop\" | \"mobile\">(\"desktop\");\n const [confirmed, setConfirmed] = useState(false);\n const [missingRequired, setMissingRequired] = useState<Set<string>>(\n new Set()\n );\n\n // Conditional logic runs against the live values with the SAME evaluator\n // the runtime uses — typing into the preview shows/hides fields for real.\n // Visibility is computed in field order against the values of fields that\n // are themselves visible: a hidden field's leftover value must not keep\n // satisfying a downstream condition (chained show/hide would misbehave).\n const visibleFields = useMemo(() => {\n const effectiveValues: Record<string, unknown> = {};\n const visible: AnyFormField[] = [];\n for (const field of fields) {\n if (field.type === \"hidden\") continue;\n const isVisible =\n !field.conditionalLogic?.enabled ||\n evaluateConditions(field.conditionalLogic, effectiveValues);\n if (isVisible) {\n visible.push(field);\n if (field.name in values) {\n effectiveValues[field.name] = values[field.name];\n }\n }\n }\n return visible;\n }, [fields, values]);\n\n const hiddenCount = fields.filter(field => field.type === \"hidden\").length;\n\n const reset = () => {\n setValues({});\n setConfirmed(false);\n setMissingRequired(new Set());\n };\n\n // The real form would refuse an empty required field, so the simulation\n // does too — an admin previewing required fields sees the requirement.\n const simulateSubmit = () => {\n const missing = new Set(\n visibleFields\n .filter(field => {\n // A plugin field renders a non-interactive placeholder in the preview\n // (its real input lives in the plugin's component in the live form),\n // so it can never be filled here — excluded like `file`, otherwise the\n // simulated submit would block forever with no way to satisfy it.\n if (!field.required || field.type === \"file\") return false;\n if (!isKnownFormField(field)) return false;\n const value = values[field.name];\n return value === undefined || value === \"\" || value === false;\n })\n .map(field => field.name)\n );\n setMissingRequired(missing);\n if (missing.size === 0) setConfirmed(true);\n };\n\n return (\n // One surface for the whole tab. The chrome and the stage are the same\n // panel seen twice, and leaving the chrome unbacked put it on the page's\n // grey while the stage below it had a frame — reading as a control strip\n // that had lost its box.\n <Card className=\"overflow-hidden p-0\">\n {/* Preview chrome: honest framing + device toggle + reset */}\n <div className=\"flex flex-col gap-3 border-b border-border px-6 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Eye className=\"h-4 w-4\" aria-hidden=\"true\" />\n Preview — a simulation of this form. Nothing here submits anywhere.\n </div>\n {/* Wraps rather than holding one row. The card around this clips, and\n at a ~360px panel the inset leaves less width than the device\n toggle and Reset need side by side — so an unwrapped row loses its\n right edge instead of overflowing visibly. */}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <Tabs\n value={device}\n onValueChange={value => setDevice(value as \"desktop\" | \"mobile\")}\n >\n {/* No corner override: the primitive already guarantees square,\n and a call site restating it is how the guarantee gets forked. */}\n <TabsList>\n <TabsTrigger value=\"desktop\" className=\"gap-1.5\">\n <Monitor className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Desktop\n </TabsTrigger>\n <TabsTrigger value=\"mobile\" className=\"gap-1.5\">\n <Smartphone className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Mobile\n </TabsTrigger>\n </TabsList>\n </Tabs>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={reset}>\n <RotateCcw className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Reset\n </Button>\n </div>\n </div>\n\n {/* The simulated form, on a tinted stage so the sheet inside reads as a\n page rather than as more of this panel. The stage draws no border of\n its own — the card around it already does. */}\n <div className=\"flex justify-center bg-muted/30 p-6\">\n <div\n className={`w-full space-y-5 border border-border bg-background p-6 ${\n device === \"mobile\" ? \"max-w-95\" : \"max-w-2xl\"\n }`}\n >\n <div>\n <h3 className=\"text-lg font-semibold text-foreground\">\n {formData?.name || \"Untitled form\"}\n </h3>\n {formData?.description && (\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {formData.description}\n </p>\n )}\n </div>\n\n {confirmed ? (\n <div className=\"space-y-4\">\n <Confirmation settings={settings} />\n <Button type=\"button\" variant=\"outline\" onClick={reset}>\n Fill again\n </Button>\n </div>\n ) : (\n <>\n {visibleFields.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">\n No visible fields — add fields in the Builder tab.\n </p>\n )}\n {visibleFields.map(field =>\n !isKnownFormField(field) ? (\n <PluginFieldPreview key={field.name} field={field} />\n ) : (\n <div key={field.name} className=\"space-y-1.5\">\n {field.type !== \"checkbox\" && (\n <Label htmlFor={`preview-${field.name}`}>\n {field.label || field.name}\n {field.required && (\n <span className=\"ml-1 text-destructive\">*</span>\n )}\n </Label>\n )}\n {field.type === \"checkbox\" ? (\n <div className=\"flex items-center gap-2\">\n <PreviewFieldInput\n field={field}\n value={values[field.name]}\n onChange={value =>\n setValues(prev => ({\n ...prev,\n [field.name]: value,\n }))\n }\n />\n <Label htmlFor={`preview-${field.name}`}>\n {field.label || field.name}\n {field.required && (\n <span className=\"ml-1 text-destructive\">*</span>\n )}\n </Label>\n </div>\n ) : (\n <PreviewFieldInput\n field={field}\n value={values[field.name]}\n onChange={value =>\n setValues(prev => ({ ...prev, [field.name]: value }))\n }\n />\n )}\n {field.helpText && (\n <p className=\"text-xs text-muted-foreground\">\n {field.helpText}\n </p>\n )}\n {missingRequired.has(field.name) && (\n <p className=\"text-xs text-destructive\">\n This field is required.\n </p>\n )}\n </div>\n )\n )}\n\n <Button\n type=\"button\"\n onClick={simulateSubmit}\n disabled={visibleFields.length === 0}\n >\n {settings.submitButtonText || \"Submit\"}\n </Button>\n </>\n )}\n\n {hiddenCount > 0 && !confirmed && (\n <p className=\"text-xs text-muted-foreground\">\n <Badge\n variant=\"outline\"\n className=\"mr-1.5 rounded-none border-border px-1 py-0 text-[10px]\"\n >\n {hiddenCount}\n </Badge>\n hidden {hiddenCount === 1 ? \"field\" : \"fields\"} will be submitted\n invisibly.\n </p>\n )}\n </div>\n </div>\n </Card>\n );\n}\n\nexport default FormPreview;\n","\"use client\";\n\n/**\n * Form Settings Tab\n *\n * Per-form behavior on the canonical settings shape: every control here is\n * consumed somewhere (the submit handler or the confirmation flow) — a\n * setting that does nothing does not get a toggle. Spam controls are\n * per-form OVERRIDES of the plugin's global config: blank means inherit,\n * and the inherited value is shown rather than hidden.\n *\n * @module admin/components/builder/FormSettingsTab\n */\n\nimport {\n FormSection,\n Input,\n Label,\n RadioGroup,\n RadioGroupItem,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n Textarea,\n} from \"@nextlyhq/ui\";\nimport type React from \"react\";\n\nimport { parseRedirectReference } from \"../../../utils/redirect-reference\";\nimport { useFormBuilder } from \"../../context/FormBuilderContext\";\n\nimport { RedirectPagePicker } from \"./RedirectPagePicker\";\n\n// ---------------------------------------------------------------------------\n// Layout helpers\n// ---------------------------------------------------------------------------\n\nfunction SettingRow({\n label,\n description,\n htmlFor,\n children,\n}: {\n label: string;\n description?: string;\n htmlFor?: string;\n children: React.ReactNode;\n}) {\n return (\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-6\">\n <div className=\"space-y-1\">\n <Label\n htmlFor={htmlFor}\n className=\"text-[13px] font-semibold text-foreground tracking-tight\"\n >\n {label}\n </Label>\n {description && (\n <p className=\"text-[12px] text-muted-foreground leading-relaxed\">\n {description}\n </p>\n )}\n </div>\n <div className=\"shrink-0 flex items-center justify-end min-w-25\">\n {children}\n </div>\n </div>\n );\n}\n\n/**\n * Tri-state override select: inherit from the plugin config (showing the\n * effective value), or force on/off for this form.\n */\nfunction InheritToggle({\n id,\n value,\n inherited,\n onChange,\n}: {\n id: string;\n value: boolean | undefined;\n inherited: boolean | undefined;\n onChange: (value: boolean | undefined) => void;\n}) {\n const inheritedLabel =\n inherited === undefined\n ? \"Inherit\"\n : `Inherit (${inherited ? \"on\" : \"off\"})`;\n return (\n <Select\n value={value === undefined ? \"inherit\" : value ? \"on\" : \"off\"}\n onValueChange={selected =>\n onChange(selected === \"inherit\" ? undefined : selected === \"on\")\n }\n >\n <SelectTrigger\n id={id}\n className=\"w-40 bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"inherit\">{inheritedLabel}</SelectItem>\n <SelectItem value=\"on\">On</SelectItem>\n <SelectItem value=\"off\">Off</SelectItem>\n </SelectContent>\n </Select>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface SpamDefaults {\n honeypot?: boolean;\n recaptchaEnabled?: boolean;\n}\n\ninterface FormSettingsTabProps {\n /** Plugin-level spam defaults (from builder-config); null while loading. */\n spamDefaults: SpamDefaults | null;\n /**\n * Collections a form may redirect to (from builder-config); null while\n * loading. Empty means the site configured none, and the option is not\n * offered — choosing it could only ever produce a form with no destination.\n */\n redirectCollections: string[] | null;\n /**\n * The configuration request failed, as distinct from returning nothing. An\n * empty list is a valid answer; a failure is not an answer at all, and\n * saying \"no collection is configured\" on a 403 states something false.\n */\n redirectConfigFailed: boolean;\n}\n\n/**\n * The \"Redirect to a page\" choice and its picker.\n *\n * Shown when the site configures a redirect collection, AND whenever this form\n * ALREADY redirects to a page. Hiding a stored confirmation leaves the radio\n * group holding a value none of its items carry, so nothing appears selected —\n * while that stored value is still posted on the next save. The author would\n * be looking at a form that says one thing and saves another.\n *\n * Configuration can be removed after a form was saved, and the builder-config\n * request can fail; both arrive here as an empty list, which is why the stored\n * value rather than the configuration decides whether this renders.\n */\n/**\n * What the \"Redirect to a page\" option should show.\n *\n * Four outcomes, named because they were four nested conditions and the\n * difference between two of them is not visual — an empty configuration is an\n * ANSWER, a failed configuration read is not, and stating the first when the\n * second happened tells an author something false about their site.\n */\n/**\n * Whether these settings actually NAME a page — not whether the page-redirect\n * option happens to be selected.\n *\n * Conflating the two let an author choose the option while the configuration\n * was unknown, which classified the state as \"a page is stored\" with nothing\n * stored: a picker with no collections to offer, and a save that could only be\n * refused. Selecting a confirmation is not a destination.\n *\n * A BARE id counts. `parseRedirectReference` resolves one only when exactly\n * one collection is configured, which is the right rule for \"which document is\n * this\" — but the question here is \"is anything stored at all\", and the answer\n * must not depend on configuration that may have failed to load or been\n * removed. Those are precisely the states where an author needs to be shown\n * what is already saved.\n */\nexport function hasStoredRedirectPage(settings: {\n redirectPage?: unknown;\n}): boolean {\n const value = settings.redirectPage;\n if (typeof value === \"string\") return value.trim().length > 0;\n return parseRedirectReference(value, []) !== null;\n}\n\nexport type RedirectOptionState =\n /** Not offered: nothing stored, nothing configured, nothing unknown. */\n | \"hidden\"\n /** Offered but not selectable: the choices could not be loaded. */\n | \"unknown\"\n /** Offered because a page IS stored, though none can be chosen now. */\n | \"stored-only\"\n /** Offered with choices. */\n | \"ready\";\n\nexport function redirectOptionState(input: {\n /** A page is actually stored — not merely that this option is selected. */\n hasStoredPage: boolean;\n collections: string[] | null;\n configFailed: boolean;\n}): RedirectOptionState {\n const configured = input.collections !== null && input.collections.length > 0;\n if (configured) return \"ready\";\n // A STORED page outranks a failed request: it is a fact about this form,\n // and the author needs to see it to change it.\n if (input.hasStoredPage) return \"stored-only\";\n return input.configFailed ? \"unknown\" : \"hidden\";\n}\n\nfunction PageRedirectOption({\n redirectCollections,\n configFailed,\n settings,\n updateSettings,\n}: {\n redirectCollections: string[] | null;\n configFailed: boolean;\n settings: { confirmationType?: string; redirectPage?: unknown };\n updateSettings: (patch: { redirectPage?: unknown }) => void;\n}) {\n const state = redirectOptionState({\n hasStoredPage: hasStoredRedirectPage(settings),\n collections: redirectCollections,\n configFailed,\n });\n if (state === \"hidden\") return null;\n\n return (\n <div className=\"flex items-start gap-3\">\n {/* Selectable only when there are collections to pick from. In every\n other state choosing it leads to a save the server must refuse: the\n rule requires a page, and a page can only come from a configured\n collection. `stored-only` shows what is already saved; it is not an\n invitation to switch a different form into that state. */}\n <RadioGroupItem\n value=\"relationship\"\n id=\"settings-confirm-page\"\n className=\"mt-0.5\"\n disabled={state !== \"ready\"}\n />\n <div className=\"w-full space-y-2\">\n <Label htmlFor=\"settings-confirm-page\">Redirect to a page</Label>\n\n {state !== \"ready\" && (\n <p className=\"text-[12px] text-muted-foreground\">\n {configFailed\n ? \"The redirect configuration could not be loaded, so the pages you can choose from are not listed. Reload to try again.\"\n : \"No collection is configured as a redirect target right now. The page below is still saved, and stays until you choose a different confirmation.\"}\n </p>\n )}\n\n {/* Mounted whenever a page is stored, including with NO configured\n collections: the picker is what recovers the stored target by id\n and labels it, so leaving it out there tells an author a page is\n saved without telling them which. */}\n {state === \"stored-only\" || state === \"ready\"\n ? settings.confirmationType === \"relationship\" && (\n <RedirectPagePicker\n collections={redirectCollections ?? []}\n value={settings.redirectPage}\n onChange={next => updateSettings({ redirectPage: next })}\n />\n )\n : null}\n </div>\n </div>\n );\n}\n\nexport function FormSettingsTab({\n spamDefaults,\n redirectCollections,\n redirectConfigFailed,\n}: FormSettingsTabProps) {\n const { settings, updateSettings } = useFormBuilder();\n\n return (\n // The measure belongs to the page's shell, not to this tab.\n <div className=\"flex flex-col gap-10\">\n {/* Submission */}\n <FormSection label=\"Submission\">\n <SettingRow\n label=\"Submit button text\"\n description=\"Label on the form's primary action button\"\n htmlFor=\"settings-submit-text\"\n >\n <Input\n id=\"settings-submit-text\"\n type=\"text\"\n className=\"w-56\"\n value={settings.submitButtonText ?? \"\"}\n onChange={e => updateSettings({ submitButtonText: e.target.value })}\n />\n </SettingRow>\n\n <SettingRow\n label=\"Allow multiple submissions\"\n description=\"When off, the same visitor (by IP) can submit this form only once. Best-effort: IP-based, so shared networks count as one visitor.\"\n htmlFor=\"settings-multiple\"\n >\n <Switch\n id=\"settings-multiple\"\n checked={settings.allowMultipleSubmissions ?? true}\n onCheckedChange={checked =>\n updateSettings({ allowMultipleSubmissions: checked })\n }\n />\n </SettingRow>\n </FormSection>\n\n {/* After submission */}\n <FormSection label=\"After submission\">\n <RadioGroup\n value={settings.confirmationType ?? \"message\"}\n onValueChange={value =>\n updateSettings({\n confirmationType: value as\n | \"message\"\n | \"redirect\"\n | \"relationship\",\n })\n }\n className=\"flex flex-col gap-3\"\n >\n <div className=\"flex items-start gap-3\">\n <RadioGroupItem\n value=\"message\"\n id=\"settings-confirm-message\"\n className=\"mt-0.5\"\n />\n <div className=\"w-full space-y-2\">\n <Label htmlFor=\"settings-confirm-message\">Show a message</Label>\n {(settings.confirmationType ?? \"message\") === \"message\" && (\n <Textarea\n aria-label=\"Success message\"\n value={settings.successMessage ?? \"\"}\n onChange={e =>\n updateSettings({ successMessage: e.target.value })\n }\n rows={3}\n placeholder=\"Thank you for your submission!\"\n />\n )}\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <RadioGroupItem\n value=\"redirect\"\n id=\"settings-confirm-redirect\"\n className=\"mt-0.5\"\n />\n <div className=\"w-full space-y-2\">\n <Label htmlFor=\"settings-confirm-redirect\">\n Redirect to a URL\n </Label>\n {settings.confirmationType === \"redirect\" && (\n <Input\n aria-label=\"Redirect URL\"\n type=\"url\"\n value={settings.redirectUrl ?? \"\"}\n onChange={e =>\n updateSettings({\n redirectUrl: e.target.value || undefined,\n })\n }\n placeholder=\"https://example.com/thanks\"\n />\n )}\n </div>\n </div>\n <PageRedirectOption\n redirectCollections={redirectCollections}\n configFailed={redirectConfigFailed}\n settings={settings}\n updateSettings={updateSettings}\n />\n </RadioGroup>\n </FormSection>\n\n {/* Spam protection */}\n <FormSection label=\"Spam protection\">\n <SettingRow\n label=\"Honeypot\"\n description=\"Invisible trap field for bots. Inherits the plugin default unless overridden here.\"\n htmlFor=\"settings-honeypot\"\n >\n <InheritToggle\n id=\"settings-honeypot\"\n value={settings.honeypotEnabled}\n inherited={spamDefaults?.honeypot}\n onChange={honeypotEnabled => updateSettings({ honeypotEnabled })}\n />\n </SettingRow>\n\n <SettingRow\n label=\"reCAPTCHA\"\n description=\"Challenge-based bot check. Inherits the plugin default unless overridden here.\"\n htmlFor=\"settings-captcha\"\n >\n <InheritToggle\n id=\"settings-captcha\"\n value={settings.captchaEnabled}\n inherited={spamDefaults?.recaptchaEnabled}\n onChange={captchaEnabled => updateSettings({ captchaEnabled })}\n />\n </SettingRow>\n\n {settings.captchaEnabled === true && (\n <SettingRow\n label=\"reCAPTCHA site key\"\n description=\"The client-facing site key for this form\"\n htmlFor=\"settings-captcha-key\"\n >\n <Input\n id=\"settings-captcha-key\"\n type=\"text\"\n // Full width below the breakpoint: 288px does not fit inside the\n // section's 24px insets on a ~360px panel, and the card clips\n // rather than scrolling.\n className=\"w-full font-mono sm:w-72\"\n value={settings.captchaSiteKey ?? \"\"}\n onChange={e =>\n updateSettings({\n captchaSiteKey: e.target.value || undefined,\n })\n }\n />\n </SettingRow>\n )}\n </FormSection>\n </div>\n );\n}\n\nexport default FormSettingsTab;\n","\"use client\";\n\n/**\n * Picks the document a form redirects to after submission.\n *\n * The plugin cannot reach the admin's own relationship input: `@nextlyhq/admin`\n * is not on the plugin import boundary, and `plugin-sdk/admin` does not\n * re-export it. This is the narrow version of that control — one value, chosen\n * from the collections the site configured as redirect targets.\n *\n * @module admin/components/builder/RedirectPagePicker\n */\n\nimport {\n Input,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectTrigger,\n SelectValue,\n} from \"@nextlyhq/ui\";\nimport { encodeSelectParam } from \"nextly/query\";\nimport { useEffect, useState } from \"react\";\n\nimport {\n documentReachability,\n type Reachability,\n parseRedirectReference,\n} from \"../../../utils/redirect-reference\";\n\n/** Rows per request. Search, not paging, is how the rest are reached. */\nconst PAGE_SIZE = 50;\n\n/**\n * The conventional title fields, tried in order when a collection does not say\n * which of its fields names a document.\n */\nconst FALLBACK_TITLE_FIELDS = [\"title\", \"name\", \"label\", \"slug\"] as const;\n\n/**\n * The fields this control reads for one collection, in the order\n * `documentLabel` prefers them.\n *\n * A collection declares what names its documents through `admin.useAsTitle`,\n * and that declaration leads here: a collection titled by `headline` is listed\n * by its headlines rather than by opaque ids, which is the difference between\n * choosing a redirect target and guessing at one. The conventional names still\n * follow it, so a row whose configured title field happens to be empty reads\n * as something recognisable instead of falling straight through to an id.\n *\n * `id` is always first because the control stores it.\n *\n * One declaration, used to build the request AND to choose the label, so a\n * field added to one cannot go missing from the other.\n */\nexport function labelFieldsFor(useAsTitle?: string): readonly string[] {\n const configured = useAsTitle?.trim();\n if (!configured) return [\"id\", ...FALLBACK_TITLE_FIELDS];\n return [\n \"id\",\n configured,\n ...FALLBACK_TITLE_FIELDS.filter(field => field !== configured),\n ];\n}\n\n/**\n * The `select` query value that projects a read down to `fields`.\n *\n * The dispatcher reads this parameter with `JSON.parse` and keeps only the\n * keys whose values are booleans (`parseSelectParam`,\n * packages/nextly/src/dispatcher/helpers/validation.ts). A value it cannot\n * parse becomes `undefined` — no error, no warning, and a response carrying\n * every field of every row. A comma-separated list is precisely such a value,\n * so the encoding is the whole difference between projecting a read and\n * appearing to project it.\n */\n/**\n * The fields to REQUEST, which is the label fields plus what decides\n * reachability.\n *\n * Kept apart from `labelFieldsFor` on purpose. `status` decides whether a page\n * is marked as a draft; it must never become a document's name, and\n * `documentLabel` walks whatever list it is given — so a page with no title\n * would otherwise be offered to an author as \"draft\".\n *\n * Derived from the label list rather than written out again, so a field added\n * for labelling is still requested.\n */\nexport function selectFieldsFor(useAsTitle?: string): readonly string[] {\n // `firstPublishedAt` as well as `status`: it is what says the collection has\n // a publish lifecycle at all, and without it a page whose collection merely\n // has a field called `status` would be marked as a draft. See\n // `hasPublishLifecycle`.\n return [...labelFieldsFor(useAsTitle), \"status\", \"firstPublishedAt\"];\n}\n\n/**\n * The `select` parameter, ready to sit in a hand-built query string.\n *\n * The format itself comes from core rather than from here. This module once\n * spelled it out, having first shipped a comma list the server accepted and\n * discarded — every field of up to fifty documents per collection, to fill a\n * dropdown reading five.\n */\nexport function selectParam(fields: readonly string[]): string {\n return encodeURIComponent(encodeSelectParam(fields));\n}\n\n/** One selectable document, reduced to what the control shows and stores. */\ninterface Choice {\n collection: string;\n id: string;\n label: string;\n /**\n * Whether a visitor could reach this page today. Unpublished pages are\n * offered on purpose — a form is usually configured beside the page it\n * points at — but an author choosing one should be able to see that it is\n * not live yet.\n *\n * Kept as the three-way answer rather than a boolean: a localized page whose\n * translation may be public is not the same as one that is definitely a\n * draft, and only the definite case earns a marker.\n */\n reachability: Reachability;\n}\n\ninterface RedirectPagePickerProps {\n /** Collections configured as redirect targets, in configuration order. */\n collections: readonly string[];\n /** The stored relationship value, in whatever shape it was saved. */\n value: unknown;\n onChange: (next: { relationTo: string; value: string } | undefined) => void;\n}\n\n/**\n * The document a stored value points at, as `collection:id`.\n *\n * The reading is `parseRedirectReference`, not a second copy of it: the picker\n * and the submit handler have to agree about which document a stored value\n * names, and two readers that agree today would drift — silently, because a\n * control showing the wrong page still shows a page.\n */\nexport function selectionKey(\n value: unknown,\n collections: readonly string[]\n): string | undefined {\n const reference = parseRedirectReference(value, collections);\n return reference ? `${reference.collection}:${reference.id}` : undefined;\n}\n\n/** What to call a document, preferring the field an author would recognise. */\nexport function documentLabel(\n row: Record<string, unknown>,\n fields: readonly string[] = labelFieldsFor()\n): string {\n for (const field of fields.filter(name => name !== \"id\")) {\n // A collection only has to point `useAsTitle` at a field that EXISTS, so\n // the configured title can be a number or a boolean. Skipping those falls\n // through to a conventional field or to the id, which is the opaque label\n // reading the configuration was meant to replace. `0` and `false` are\n // titles like any other, so emptiness is decided after stringifying, not\n // by truthiness.\n const candidate = row[field];\n const text =\n typeof candidate === \"string\"\n ? candidate\n : typeof candidate === \"number\" && Number.isFinite(candidate)\n ? String(candidate)\n : typeof candidate === \"boolean\"\n ? String(candidate)\n : undefined;\n if (text !== undefined && text.trim()) return text;\n }\n return typeof row.id === \"string\" ? row.id : \"Untitled\";\n}\n\n/**\n * One collection's matching documents, or null when it could not be read.\n *\n * `limit`, not `pageSize` — the other name is accepted and ignored, leaving\n * the default of 10. `status=all` because a form is usually configured\n * alongside the page it points at, and filtering to published shows \"create\n * one first\" while the page sits in the next tab. `depth=0` because this reads\n * a handful of scalar fields and would otherwise inherit the collection API's\n * expansion, pulling every nested relation of every row to fill a dropdown.\n */\nasync function fetchChoices(\n collection: string,\n query: string,\n page: number,\n meta: CollectionMeta\n): Promise<{ rows: Choice[]; hasMore: boolean } | null> {\n const search = query ? `&search=${encodeURIComponent(query)}` : \"\";\n try {\n // `select` as well as `depth=0`. Without one the server sends every scalar\n // and JSON field of up to fifty documents per collection — for\n // page-builder documents that is the whole block tree — to fill a dropdown\n // that reads a handful of them.\n const response = await fetch(\n `/admin/api/collections/${collection}/entries` +\n `?limit=${PAGE_SIZE}&page=${page}&status=all&depth=0` +\n `&select=${selectParam(selectFieldsFor(meta.titleField))}${search}`,\n { credentials: \"include\" }\n );\n if (!response.ok) return null;\n const body = (await response.json()) as {\n items?: Record<string, unknown>[];\n meta?: { totalPages?: number };\n };\n return {\n rows: (body.items ?? [])\n .filter(row => typeof row.id === \"string\")\n .map(row => ({\n collection,\n id: row.id as string,\n label: documentLabel(row, labelFieldsFor(meta.titleField)),\n reachability: documentReachability(\n row as { id: string },\n meta.localized\n ),\n })),\n // Reported rather than assumed, so the control can offer the rest\n // instead of truncating where the author cannot see it.\n hasMore: page < (body.meta?.totalPages ?? 1),\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Existing choices plus new ones, de-duplicated on the pair that identifies a\n * document. Order is preserved so a list does not reshuffle under the author.\n */\nfunction mergeChoices(\n existing: readonly Choice[],\n incoming: readonly Choice[]\n): Choice[] {\n const seen = new Set(existing.map(c => `${c.collection}:${c.id}`));\n const merged = [...existing];\n for (const choice of incoming) {\n const key = `${choice.collection}:${choice.id}`;\n if (seen.has(key)) continue;\n seen.add(key);\n merged.push(choice);\n }\n return merged;\n}\n\n/** One option, keyed and valued by the pair the caller stores. */\nfunction renderChoice(choice: Choice) {\n return (\n <SelectItem\n key={`${choice.collection}:${choice.id}`}\n value={`${choice.collection}:${choice.id}`}\n >\n <span className=\"inline-flex items-center gap-2\">\n <span>{choice.label}</span>\n {/* Unpublished pages are offered deliberately — a form is usually\n configured beside the page it points at — so the marker is what\n keeps \"offered\" from reading as \"live\". A published form saved\n against one is refused, and an author who cannot see which pages\n are drafts has no way to predict that. */}\n {choice.reachability === \"unreachable\" && (\n <span className=\"shrink-0 rounded-sm border px-1 py-px text-[10px] uppercase tracking-wide text-muted-foreground\">\n Draft\n </span>\n )}\n </span>\n </SelectItem>\n );\n}\n\n/**\n * Which collections the picker needs metadata for, as the key the lookup is\n * cached on.\n *\n * The configured ones, plus the collection the STORED target names when that\n * is no longer among them. A polymorphic value can point at a collection since\n * removed from `redirectRelationships`, and the picker recovers and displays\n * that target on purpose — looking up only the configured ones would label it\n * by the conventional names while the listing uses each collection's own title\n * field, showing one document under two rules.\n */\nexport function metadataCollections(\n collections: readonly string[],\n value: unknown\n): string {\n const stored = selectionKey(value, collections)?.split(\":\")[0];\n const wanted =\n stored && !collections.includes(stored)\n ? [...collections, stored]\n : collections;\n return wanted.join(\",\");\n}\n\n/**\n * The field a collection says names its documents, or undefined when it does\n * not say or cannot be asked.\n *\n * Undefined covers both \"this collection declares no title field\" and \"the\n * metadata could not be read\", because the control does the same thing in\n * either case: fall back to the conventional names. Nothing on screen depends\n * on telling them apart, and the listing itself still reports its own\n * failures.\n */\ninterface CollectionMeta {\n /** The field the collection says names its documents. */\n titleField: string | undefined;\n /**\n * The collection's `localized` setting, or undefined when it could not be\n * read. Undefined is NOT false: a localized collection read as plain marks\n * reachable translations as drafts.\n */\n localized: boolean | undefined;\n}\n\nasync function fetchCollectionMeta(\n collection: string\n): Promise<CollectionMeta> {\n try {\n // The bare document, not a `{ message, item }` envelope — that shape is\n // for mutations, and this is a read.\n const response = await fetch(`/admin/api/collections/${collection}`, {\n credentials: \"include\",\n });\n if (!response.ok) return { titleField: undefined, localized: undefined };\n const body = (await response.json()) as {\n admin?: { useAsTitle?: unknown };\n localized?: unknown;\n };\n const configured = body.admin?.useAsTitle;\n return {\n titleField:\n typeof configured === \"string\" && configured.trim()\n ? configured.trim()\n : undefined,\n localized:\n typeof body.localized === \"boolean\" ? body.localized : undefined,\n };\n } catch {\n return { titleField: undefined, localized: undefined };\n }\n}\n\n/**\n * The fields to read and to label from, per configured collection.\n *\n * Resolved once per collection list rather than per search: what a collection\n * calls its title does not change while an author types, and folding this into\n * the listing effect would spend a request per keystroke to re-learn a\n * constant.\n *\n * `null` means not resolved yet. The listing waits for it, because a first\n * page fetched under the fallback fields would have to be refetched or else\n * left labelled differently from every page after it.\n */\nfunction useTitleFields(key: string) {\n const [fields, setFields] = useState<Record<string, CollectionMeta> | null>(\n null\n );\n\n useEffect(() => {\n let cancelled = false;\n const wanted = key ? key.split(\",\") : [];\n setFields(null);\n\n void Promise.all(\n wanted.map(async collection => {\n return [collection, await fetchCollectionMeta(collection)] as const;\n })\n ).then(pairs => {\n if (!cancelled) setFields(Object.fromEntries(pairs));\n });\n\n return () => {\n cancelled = true;\n };\n }, [key]);\n\n return fields;\n}\n\n/**\n * The matching documents for every configured collection, and which of them\n * could not be read.\n */\nfunction useChoices(\n key: string,\n applied: string,\n titleFields: Record<string, CollectionMeta> | null\n) {\n const [choices, setChoices] = useState<Choice[] | null>(null);\n const [failed, setFailed] = useState<readonly string[]>([]);\n const [hasMore, setHasMore] = useState(false);\n const [loading, setLoading] = useState(false);\n // Per collection, not shared. A shared counter advances every collection\n // when any one of them has more, so a collection whose page N failed is\n // never asked for page N again — and if its N+1 succeeds the warning clears\n // too, leaving those documents absent with nothing on screen saying so.\n const [pages, setPages] = useState<Record<string, number>>({});\n\n // A new query is a new list, not more of the old one.\n //\n // The SAME reference is returned when there is nothing to clear. Writing a\n // fresh `{}` every time is a state change even when the map is already\n // empty, and the fetch effect below depends on `pages` — so each query\n // change ran the fetch once with the old map, cancelled it, and ran it\n // again. Every collection was requested twice per search and the first\n // answer discarded.\n useEffect(() => {\n setPages(current => (Object.keys(current).length === 0 ? current : {}));\n }, [key, applied]);\n\n useEffect(() => {\n // Nothing to request until each collection's title field is known —\n // see `useTitleFields`. `choices` stays null, which the control already\n // renders as loading.\n if (titleFields === null) return;\n\n let cancelled = false;\n const wanted = key ? key.split(\",\") : [];\n setLoading(true);\n\n void Promise.all(\n wanted.map(collection =>\n fetchChoices(\n collection,\n applied,\n pages[collection] ?? 1,\n titleFields[collection] ?? {\n titleField: undefined,\n localized: undefined,\n }\n )\n )\n ).then(results => {\n if (cancelled) return;\n setLoading(false);\n\n // WHICH collections failed, not merely whether any did: one unreadable\n // collection beside a readable-but-empty one otherwise reports \"nothing\n // here\" about a collection nobody could see.\n setFailed(wanted.filter((_, index) => results[index] === null));\n\n const loaded = results.filter(\n (result): result is { rows: Choice[]; hasMore: boolean } =>\n result !== null\n );\n setHasMore(loaded.some(result => result.hasMore));\n\n const rows = loaded.flatMap(result => result.rows);\n const first = wanted.every(collection => (pages[collection] ?? 1) === 1);\n // Merged by identity rather than appended. A retry re-requests a page\n // that already succeeded for its siblings, and appending would list\n // those documents twice — duplicate options, and duplicate React keys.\n // Keyed, a repeated page is a no-op whatever caused the repeat.\n setChoices(current => mergeChoices(first ? [] : (current ?? []), rows));\n });\n\n return () => {\n cancelled = true;\n };\n }, [key, applied, pages, titleFields]);\n\n return {\n choices,\n setChoices,\n failed,\n hasMore,\n loading,\n /**\n * Advances only the collections that have more to give, and RETRIES the\n * ones whose last page failed rather than stepping over it.\n *\n * Guarded on the in-flight flag: two clicks before the first resolves\n * either batch into a double increment or cancel the pending effect, and a\n * skipped page is then absent from a list that reports having loaded it.\n */\n loadMore: () => {\n if (loading) return;\n setPages(current => {\n const next: Record<string, number> = { ...current };\n for (const collection of key ? key.split(\",\") : []) {\n // A failed collection stays on its page so the retry asks for the\n // page it never got.\n if (failed.includes(collection)) continue;\n next[collection] = (current[collection] ?? 1) + 1;\n }\n return next;\n });\n },\n /** Ask the failed collections again, without advancing anyone. */\n retryFailed: () => {\n if (!loading) setPages(current => ({ ...current }));\n },\n };\n}\n\n/**\n * Adds the selected document when the listing did not reach it.\n *\n * Keyed on the selection rather than on the collection list, because the\n * stored value arrives with the form and often AFTER this mounts — a hook\n * watching only the collections would capture the moment when nothing was\n * selected and never look again.\n *\n * A control that silently drops its own value is worse than one that cannot\n * list everything: the author sees blank, re-picks, and saves a different\n * destination than the one that was stored.\n */\nfunction useSelectedChoice(\n selected: string | undefined,\n choices: Choice[] | null,\n setChoices: (update: (current: Choice[] | null) => Choice[]) => void,\n titleFields: Record<string, CollectionMeta> | null\n) {\n // Whether the stored selection could not be read. A controlled `Select`\n // holding a value with no matching option renders BLANK, which looks\n // identical to \"nothing chosen\" — so an author with a perfectly good\n // destination is invited to pick over it. Reported rather than swallowed.\n const [unreadable, setUnreadable] = useState(false);\n\n useEffect(() => {\n if (!selected || choices === null) return;\n const separator = selected.indexOf(\":\");\n const collection = selected.slice(0, separator);\n const id = selected.slice(separator + 1);\n // Collection AND id: two configured collections can hold the same id, and\n // matching on id alone skips the fetch while the option that is actually\n // selected is absent.\n if (\n !id ||\n choices.some(\n choice => choice.id === id && choice.collection === collection\n )\n ) {\n setUnreadable(false);\n return;\n }\n\n let cancelled = false;\n void (async () => {\n try {\n // `status=all` for the same reason the listing carries it: a stored\n // target may be a draft, and a published-only read answers 404.\n const response = await fetch(\n `/admin/api/collections/${collection}/entries/${id}?depth=0&status=all`,\n { credentials: \"include\" }\n );\n if (!response.ok) {\n if (!cancelled) setUnreadable(true);\n return;\n }\n // A by-id read answers with the document ITSELF. Only mutations carry\n // the `{ message, item }` envelope.\n const row = (await response.json()) as Record<string, unknown>;\n if (cancelled) return;\n if (!row || typeof row.id !== \"string\") {\n setUnreadable(true);\n return;\n }\n setUnreadable(false);\n setChoices(current => [\n {\n collection,\n id: row.id as string,\n // The same fields the listing labels by. Labelling the recovered\n // document differently would show one document under two names\n // depending on whether the listing happened to reach it.\n label: documentLabel(\n row,\n labelFieldsFor(titleFields?.[collection]?.titleField)\n ),\n reachability: documentReachability(\n row as { id: string },\n titleFields?.[collection]?.localized\n ),\n },\n ...(current ?? []),\n ]);\n } catch {\n if (!cancelled) setUnreadable(true);\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [selected, choices, setChoices, titleFields]);\n\n return unreadable;\n}\n\n/**\n * The options, grouped by the collection each one came from.\n *\n * The groups are derived from the CHOICES, not from the configured list. A\n * stored target whose collection was removed since the form was saved is\n * recovered by id and belongs in the list — filtering by configuration\n * dropped it, leaving the control blank over a value that is still stored,\n * with the unreadable warning cleared because the recovery had succeeded.\n *\n * That divergence was visible in the code before it was visible on screen: the\n * single-collection branch rendered every choice and the grouped branch\n * filtered them, so the same state produced different pickers depending on how\n * many collections a site happened to configure. One path now.\n */\nexport interface ChoiceGroup {\n collection: string;\n label: string;\n /** True when this collection is no longer a configured redirect target. */\n removed: boolean;\n choices: Choice[];\n}\n\n/**\n * The choices arranged into groups, in configuration order, with collections\n * that are no longer configured last and marked.\n *\n * Derived from the CHOICES rather than from the configured list, which is the\n * whole point: a stored target whose collection was removed is recovered by id\n * and belongs in the list.\n */\nexport function groupChoices(\n choices: readonly Choice[],\n collections: readonly string[]\n): ChoiceGroup[] {\n const present = [...new Set(choices.map(choice => choice.collection))];\n const order = [\n ...collections.filter(name => present.includes(name)),\n ...present.filter(name => !collections.includes(name)),\n ];\n\n return order.map(collection => {\n const removed = !collections.includes(collection);\n return {\n collection,\n // Named as unavailable rather than hidden: the value is still stored and\n // the server refuses a save into a collection with no pattern, so an\n // author needs to see what is set in order to change it.\n label: removed ? `${collection} — no longer configured` : collection,\n removed,\n choices: choices.filter(choice => choice.collection === collection),\n };\n });\n}\n\nfunction Options({\n choices,\n collections,\n}: {\n choices: Choice[];\n collections: readonly string[];\n}) {\n const groups = groupChoices(choices, collections);\n if (groups.length <= 1 && !groups[0]?.removed) {\n return <>{choices.map(renderChoice)}</>;\n }\n return (\n <>\n {groups.map(group => (\n <SelectGroup key={group.collection}>\n <SelectLabel>{group.label}</SelectLabel>\n {group.choices.map(renderChoice)}\n </SelectGroup>\n ))}\n </>\n );\n}\n\n/**\n * What to say when there is nothing to choose, and why.\n *\n * `collections` here is the READABLE ones. Passing the full configured list\n * asserted that a collection which could not be read is empty — printed\n * directly beneath the notice saying it could not be read.\n */\nfunction Empty({\n applied,\n collections,\n}: {\n applied: string;\n collections: readonly string[];\n}) {\n return (\n <p className=\"text-[12px] text-muted-foreground\">\n {applied\n ? `No documents match “${applied}”.`\n : collections.length === 0\n ? \"No collection is configured as a redirect target.\"\n : `No documents to redirect to yet. Create one in ${collections.join(\" or \")} first.`}\n </p>\n );\n}\n\n/**\n * The things this control has to say that its options cannot.\n *\n * Three separate states, kept apart deliberately. A collection that could not\n * be read, a saved page that cannot be IDENTIFIED because no collection is\n * configured to look it up in, and a saved page whose lookup FAILED are three\n * different situations, and each one previously appeared as an empty control —\n * which reads as \"nothing is set\" and invites an author to overwrite a\n * destination they were never shown.\n */\nfunction PickerNotices({\n failed,\n loading,\n onRetry,\n unidentifiable,\n unreadableSelection,\n}: {\n failed: readonly string[];\n loading: boolean;\n onRetry: () => void;\n unidentifiable: boolean;\n unreadableSelection: boolean;\n}) {\n return (\n <>\n {failed.length > 0 && (\n <p className=\"text-[12px] text-destructive\">\n Could not read {failed.join(\" or \")}. You may not have permission to\n list {failed.length > 1 ? \"them\" : \"it\"}, or the request failed.{\" \"}\n <button\n type=\"button\"\n onClick={onRetry}\n disabled={loading}\n className=\"underline underline-offset-2 disabled:no-underline\"\n >\n Try again\n </button>\n </p>\n )}\n\n {unidentifiable && (\n <p className=\"text-[12px] text-muted-foreground\">\n A page is saved on this form, but it cannot be identified without a\n configured redirect collection. Choosing a different confirmation\n replaces it.\n </p>\n )}\n\n {unreadableSelection && (\n <p className=\"text-[12px] text-destructive\">\n This form has a page selected that could not be read. Leave it as it\n is unless you mean to change it — saving a new choice replaces the\n stored one.\n </p>\n )}\n </>\n );\n}\n\nexport function RedirectPagePicker({\n collections,\n value,\n onChange,\n}: RedirectPagePickerProps) {\n const [query, setQuery] = useState(\"\");\n const [applied, setApplied] = useState(\"\");\n\n // Typing should not issue a request per keystroke.\n useEffect(() => {\n const timer = setTimeout(() => setApplied(query), 250);\n return () => clearTimeout(timer);\n }, [query]);\n\n // `collections` is an array prop, so a new identity every render would\n // refetch forever; the join is the value that actually decides the request.\n const titleFields = useTitleFields(metadataCollections(collections, value));\n const {\n choices,\n setChoices,\n failed,\n hasMore,\n loading,\n loadMore,\n retryFailed,\n } = useChoices(collections.join(\",\"), applied, titleFields);\n const selected = selectionKey(value, collections);\n // A value is stored and this control cannot say WHICH document it is: a bare\n // id names no collection, and with none configured there is nowhere to look\n // it up. Distinct from an unreadable selection, which is a failed request\n // rather than missing information.\n const unidentifiable = value != null && selected === undefined;\n const selectionUnreadable = useSelectedChoice(\n selected,\n choices,\n setChoices,\n titleFields\n );\n\n if (choices === null) {\n return <p className=\"text-[12px] text-muted-foreground\">Loading pages…</p>;\n }\n\n return (\n <div className=\"space-y-2\">\n {/* Search rather than paging: a dropdown cannot usefully list a large\n collection, and a ceiling on how many pages to walk would leave the\n documents past it unreachable however high it were set. */}\n {/* Nothing to search when no collection is listed — the only entry in\n the control then is the stored target, recovered by id. */}\n {collections.length > 0 && (\n <Input\n aria-label=\"Search pages\"\n value={query}\n onChange={event => setQuery(event.target.value)}\n placeholder=\"Search…\"\n />\n )}\n\n {/* Reported BESIDE the choices rather than instead of them: a collection\n the author cannot read is a different problem from one that is empty,\n and the collections that did load are still choosable. */}\n <PickerNotices\n failed={failed}\n loading={loading}\n onRetry={retryFailed}\n unidentifiable={unidentifiable}\n unreadableSelection={selectionUnreadable}\n />\n\n {choices.length === 0 ? (\n failed.length < collections.length && (\n <Empty\n applied={applied}\n collections={collections.filter(name => !failed.includes(name))}\n />\n )\n ) : (\n <Select\n value={selected ?? \"\"}\n onValueChange={next => {\n const separator = next.indexOf(\":\");\n if (separator < 0) return onChange(undefined);\n onChange({\n relationTo: next.slice(0, separator),\n value: next.slice(separator + 1),\n });\n }}\n >\n <SelectTrigger aria-label=\"Redirect page\" className=\"w-full\">\n <SelectValue placeholder=\"Choose a page\" />\n </SelectTrigger>\n <SelectContent>\n <Options choices={choices} collections={collections} />\n </SelectContent>\n </Select>\n )}\n\n {/* The rest of the matches, reachable rather than silently absent. A cap\n with no way past it makes a document beyond it unselectable however\n high the cap is set. */}\n {hasMore && (\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"text-[12px] text-muted-foreground underline underline-offset-2 hover:text-foreground disabled:no-underline disabled:opacity-60\"\n >\n {loading ? \"Loading…\" : \"Load more\"}\n </button>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["Badge","Button","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","ChevronLeft","ChevronRight","useEffect","useState","jsx","jsxs","formatDate","Badge","useState","useEffect","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Button","ChevronLeft","ChevronRight","Button","Card","FieldShell","Input","Textarea","Tabs","TabsList","TabsTrigger","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","FORM_FIELD_TYPE_CATALOG","useCallback","useEffect","useRef","useState","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","Tooltip","TooltipContent","TooltipTrigger","FORM_FIELD_TYPE_CATALOG","useCallback","useMemo","useState","useCallback","useMemo","useState","jsx","useState","useCallback","useMemo","Button","useState","jsx","jsxs","FormLabelWithTooltip","Input","Checkbox","Tabs","TabsList","TabsTrigger","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","useState","useCallback","useMemo","Button","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","useCallback","useMemo","jsx","jsxs","jsx","jsxs","useMemo","FormLabelWithTooltip","Input","Checkbox","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","useState","useCallback","Tabs","TabsList","TabsTrigger","Fragment","jsx","jsxs","FORM_FIELD_TYPE_CATALOG","DropdownMenu","DropdownMenuTrigger","Button","DropdownMenuContent","DropdownMenuItem","Tooltip","TooltipTrigger","TooltipContent","useState","useMemo","useCallback","Badge","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuSeparator","DropdownMenuTrigger","Input","Label","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","Switch","Copy","MoreHorizontal","Pencil","Plus","Trash2","TriangleAlert","useCallback","useEffect","useMemo","useState","Fragment","jsx","jsxs","Badge","TriangleAlert","Switch","DropdownMenu","DropdownMenuTrigger","Button","MoreHorizontal","DropdownMenuContent","DropdownMenuItem","Pencil","Copy","DropdownMenuSeparator","Trash2","useState","useCallback","Label","Input","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Plus","useEffect","useMemo","Badge","Button","Checkbox","Input","Card","Label","RadioGroup","RadioGroupItem","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","Tabs","TabsList","TabsTrigger","Textarea","useMemo","useState","Input","Label","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","Switch","Textarea","Input","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","useEffect","useState","Fragment","jsx","jsxs","PAGE_SIZE","SelectItem","useState","useEffect","Input","Select","SelectTrigger","SelectValue","SelectContent","jsx","jsxs","Label","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Input","Switch","Textarea","Fragment","jsx","jsxs","Textarea","Checkbox","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","RadioGroup","RadioGroupItem","Label","Input","useState","useMemo","Card","Tabs","TabsList","TabsTrigger","Button","Badge","Fragment","jsx","jsxs","useState","useEffect","FORM_FIELD_TYPE_CATALOG","useRef","useCallback","Card","FieldShell","Input","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Textarea","Tabs","TabsList","TabsTrigger","Button"]}
1
+ {"version":3,"sources":["../../src/admin/index.ts","../../src/admin/components/submissions/SubmissionsView.tsx","../../src/admin/components/submissions/SubmissionSheet.tsx","../../src/admin/FormBuilderView.tsx","../../src/admin/components/builder/FieldCards.tsx","../../src/admin/context/FormBuilderContext.tsx","../../src/admin/components/builder/AddFieldDialog.tsx","../../src/admin/components/builder/FieldEditor.tsx","../../src/admin/components/builder/ConditionalLogicEditor.tsx","../../src/admin/components/builder/FormNotificationsTab.tsx","../../src/admin/components/builder/notification-addresses.ts","../../src/admin/components/builder/FormPreview.tsx","../../src/admin/components/builder/FormSettingsTab.tsx","../../src/admin/components/builder/RedirectPagePicker.tsx"],"sourcesContent":["/**\n * Form Builder Admin Components\n *\n * Entry point for admin UI components used by the Nextly admin panel.\n * These components are referenced via component paths in collection configs.\n *\n * Component paths use the format: \"@nextlyhq/plugin-form-builder/admin#ExportName\"\n *\n * ## Auto-Registration\n *\n * This module automatically registers its components with the admin panel\n * when imported. No manual registration is required - just add the plugin\n * to your Nextly config and the admin will detect and load the components.\n *\n * @module admin\n * @since 0.1.0\n */\n\n// ============================================================================\n// Main View Components\n// ============================================================================\n\n// ============================================================================\n// Component Registration\n// ============================================================================\n\nimport {\n registerComponents,\n registerKnownPlugin,\n type ComponentPath,\n} from \"@nextlyhq/plugin-sdk/admin\";\n\nimport { SubmissionsView } from \"./components/submissions/SubmissionsView\";\nimport { FormBuilderView } from \"./FormBuilderView\";\n\nexport { FormBuilderView, type FormBuilderViewProps } from \"./FormBuilderView\";\nexport { SubmissionsView } from \"./components/submissions/SubmissionsView\";\nexport { SubmissionSheet } from \"./components/submissions/SubmissionSheet\";\n\n// ============================================================================\n// Builder Sub-Components\n// ============================================================================\n\nexport {\n FieldCards,\n AddFieldDialog,\n FieldEditor,\n FormPreview,\n type FieldCardsProps,\n type AddFieldDialogProps,\n type FieldEditorProps,\n type FormPreviewProps,\n} from \"./components/builder\";\n\n// ============================================================================\n// Context & Hooks\n// ============================================================================\n\nexport {\n FormBuilderProvider,\n useFormBuilder,\n useOptionalFormBuilder,\n createFieldFromType,\n createNotification,\n generateFieldName,\n generateFieldLabel,\n DEFAULT_SETTINGS,\n type FormBuilderState,\n type FormBuilderActions,\n type FormBuilderContextValue,\n type FormBuilderProviderProps,\n type FormSettings,\n type FormNotification,\n} from \"./context/FormBuilderContext\";\n\n// ============================================================================\n// Tab Components\n// ============================================================================\n\nexport { FormSettingsTab } from \"./components/builder/FormSettingsTab\";\nexport { FormNotificationsTab } from \"./components/builder/FormNotificationsTab\";\n\n/** Component path for FormBuilderView */\nexport const FORM_BUILDER_VIEW_PATH: ComponentPath =\n \"@nextlyhq/plugin-form-builder/admin#FormBuilderView\";\n\n/** Component path for SubmissionsView */\nexport const SUBMISSIONS_VIEW_PATH: ComponentPath =\n \"@nextlyhq/plugin-form-builder/admin#SubmissionsView\";\n\n// ============================================================================\n// Auto-Registration with Admin\n// ============================================================================\n\n/**\n * Eagerly register components at module load time.\n *\n * This ensures components are available immediately when the admin\n * tries to resolve them, without needing to wait for async registration.\n */\nregisterComponents({\n [FORM_BUILDER_VIEW_PATH]: FormBuilderView,\n [SUBMISSIONS_VIEW_PATH]: SubmissionsView,\n});\n\n/**\n * Also register with the known plugin system for consistency.\n *\n * This supports the auto-registration flow where the admin detects\n * component paths and triggers registration. Since we eagerly register\n * above, this callback is effectively a no-op but keeps the plugin\n * compatible with both eager and lazy registration patterns.\n */\nregisterKnownPlugin(\n \"@nextlyhq/plugin-form-builder\",\n // eslint-disable-next-line @typescript-eslint/require-await -- registerKnownPlugin's second arg is typed `() => Promise<void>` (strict, not a union); body is synchronous\n async () => {\n // Components already registered above, but re-register for safety\n registerComponents({\n [FORM_BUILDER_VIEW_PATH]: FormBuilderView,\n [SUBMISSIONS_VIEW_PATH]: SubmissionsView,\n });\n }\n);\n","\"use client\";\n\n/**\n * Submissions View\n *\n * The submissions list, replacing the generic collection table via the\n * List-view override. Submission data is stored keyed by field name, so\n * when one form is selected the table shows real per-field columns; across\n * all forms (where fields differ) it falls back to generic columns with a\n * data summary. Spam is a first-class status tab — flagged submissions are\n * reviewable and recoverable, never invisible.\n *\n * @module admin/components/submissions/SubmissionsView\n */\n\nimport {\n DataTableView,\n type NextlyColumn,\n type RowAction,\n} from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n Badge,\n Button,\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Tabs,\n TabsList,\n TabsTrigger,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@nextlyhq/ui\";\nimport { ChevronLeft, ChevronRight, Columns3, Download } from \"lucide-react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport type { FormField } from \"../../../types\";\nimport { formatExportValue } from \"../../../utils/export-formats\";\n\nimport { SubmissionSheet } from \"./SubmissionSheet\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SubmissionRow {\n id: string;\n form: string | { id: string; name?: string };\n data: Record<string, unknown>;\n status: string;\n spamReason?: string | null;\n notes?: string | null;\n ipAddress?: string | null;\n userAgent?: string | null;\n submittedAt?: string | null;\n editedAt?: string | null;\n editedBy?: string | null;\n}\n\nexport interface FormOption {\n id: string;\n name: string;\n fields: FormField[];\n}\n\ninterface SubmissionsViewProps {\n collectionSlug?: string;\n}\n\ntype StatusTab = \"all\" | \"new\" | \"read\" | \"archived\" | \"spam\";\n\nconst STATUS_TABS: Array<{ value: StatusTab; label: string }> = [\n { value: \"all\", label: \"All\" },\n { value: \"new\", label: \"New\" },\n { value: \"read\", label: \"Read\" },\n { value: \"archived\", label: \"Archived\" },\n { value: \"spam\", label: \"Spam\" },\n];\n\nconst PAGE_SIZE = 25;\n\n/** How many per-field columns are visible before the rest hide behind the selector. */\nconst VISIBLE_FIELD_COLUMNS = 4;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction formIdOf(row: SubmissionRow): string {\n return typeof row.form === \"object\" ? row.form.id : row.form;\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return \"\";\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return \"\";\n return date.toLocaleString(undefined, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n });\n}\n\n/** Status → badge styling. Tokens only; spam reads as a warning state. */\nfunction StatusBadge({ status }: { status: string }) {\n const label = status.charAt(0).toUpperCase() + status.slice(1);\n return (\n <Badge\n variant=\"outline\"\n className={`rounded-none px-1.5 py-0 text-[10px] font-semibold uppercase tracking-wider ${\n // Full-strength status borders (destructive/primary) so each badge boundary is perceivable; the default falls back to border-border, keeping the states distinct.\n status === \"spam\"\n ? \"border-destructive text-destructive\"\n : status === \"new\"\n ? \"border-primary text-primary\"\n : \"border-border text-muted-foreground\"\n }`}\n >\n {label}\n </Badge>\n );\n}\n\n/** Compact \"key: value\" preview of a submission for the all-forms table. */\nfunction summarizeData(data: Record<string, unknown>): string {\n return Object.entries(data)\n .slice(0, 3)\n .map(([key, value]) => `${key}: ${formatExportValue(value)}`)\n .join(\" · \");\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function SubmissionsView({ collectionSlug }: SubmissionsViewProps) {\n const [slugs, setSlugs] = useState<{ forms: string; submissions: string }>();\n const [forms, setForms] = useState<FormOption[]>([]);\n const [selectedFormId, setSelectedFormId] = useState<string>(() => {\n // Deep links (?form=<id>) preselect the form filter.\n if (typeof window === \"undefined\") return \"\";\n return new URLSearchParams(window.location.search).get(\"form\") ?? \"\";\n });\n const [statusTab, setStatusTab] = useState<StatusTab>(\"all\");\n const [rows, setRows] = useState<SubmissionRow[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [openId, setOpenId] = useState<string | null>(null);\n const [canUpdate, setCanUpdate] = useState(false);\n const [canDelete, setCanDelete] = useState(false);\n\n const submissionsSlug =\n slugs?.submissions ?? collectionSlug ?? \"form-submissions\";\n\n // --- bootstrap: resolved slugs, the forms list, and the user's rights ----\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n try {\n const configRes = await fetch(\n \"/admin/api/plugins/@nextlyhq/plugin-form-builder/builder-config\",\n { credentials: \"include\" }\n );\n const config = configRes.ok\n ? ((await configRes.json()) as {\n slugs?: { forms: string; submissions: string };\n })\n : null;\n const resolved = config?.slugs ?? {\n forms: \"forms\",\n submissions: \"form-submissions\",\n };\n if (cancelled) return;\n setSlugs(resolved);\n\n // Page through ALL forms — a capped single fetch would silently\n // drop forms from the filter and the per-field column lookup.\n const allForms: FormOption[] = [];\n const formsPageSize = 200;\n for (let formsPage = 1; ; formsPage += 1) {\n const formsRes = await fetch(\n `/admin/api/collections/${resolved.forms}/entries?pageSize=${formsPageSize}&page=${formsPage}`,\n { credentials: \"include\" }\n );\n if (!formsRes.ok) break;\n const json = (await formsRes.json()) as {\n items?: Array<{ id: string; name?: string; fields?: FormField[] }>;\n };\n const items = json.items ?? [];\n allForms.push(\n ...items.map(item => ({\n id: item.id,\n name: item.name ?? item.id,\n fields: Array.isArray(item.fields) ? item.fields : [],\n }))\n );\n if (items.length < formsPageSize) break;\n }\n if (!cancelled) setForms(allForms);\n\n const permsRes = await fetch(\"/admin/api/me/permissions\", {\n credentials: \"include\",\n });\n if (permsRes.ok) {\n const perms = (await permsRes.json()) as {\n permissions?: string[];\n isSuperAdmin?: boolean;\n };\n const has = (slug: string) =>\n Boolean(perms.isSuperAdmin) ||\n (perms.permissions ?? []).includes(slug);\n if (!cancelled) {\n setCanUpdate(has(`update-${resolved.submissions}`));\n setCanDelete(has(`delete-${resolved.submissions}`));\n }\n }\n } catch {\n // Bootstrap failures degrade to the default slugs — without them\n // the list fetch would wait forever and the loading state never end.\n if (!cancelled) {\n setSlugs(\n current =>\n current ?? { forms: \"forms\", submissions: \"form-submissions\" }\n );\n }\n }\n })();\n return () => {\n cancelled = true;\n };\n }, []);\n\n // --- the list fetch ------------------------------------------------------\n // Monotonic request sequence: rapid filter changes overlap fetches, and\n // only the LATEST request may commit rows — an older response finishing\n // last must never overwrite the current filter's data.\n const fetchSeqRef = useRef(0);\n const fetchRows = useCallback(async () => {\n if (!slugs) return;\n const seq = ++fetchSeqRef.current;\n setLoading(true);\n setError(null);\n try {\n const where: Record<string, unknown> = {};\n if (selectedFormId) where.form = { equals: selectedFormId };\n // \"All\" means every human status; spam only shows when asked for —\n // the same default the export route applies.\n if (statusTab === \"all\") where.status = { not_equals: \"spam\" };\n else where.status = { equals: statusTab };\n\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(PAGE_SIZE),\n sortBy: \"submittedAt\",\n sortOrder: \"desc\",\n where: JSON.stringify(where),\n });\n const res = await fetch(\n `/admin/api/collections/${slugs.submissions}/entries?${params}`,\n { credentials: \"include\" }\n );\n if (!res.ok)\n throw new Error(`Failed to load submissions (${res.status})`);\n const json = (await res.json()) as {\n items?: SubmissionRow[];\n meta?: { total?: number };\n };\n if (seq !== fetchSeqRef.current) return;\n setRows(json.items ?? []);\n setTotal(json.meta?.total ?? (json.items ?? []).length);\n } catch (err) {\n if (seq !== fetchSeqRef.current) return;\n setError(\n err instanceof Error ? err.message : \"Failed to load submissions\"\n );\n } finally {\n if (seq === fetchSeqRef.current) setLoading(false);\n }\n }, [slugs, selectedFormId, statusTab, page]);\n\n useEffect(() => {\n void fetchRows();\n }, [fetchRows]);\n\n const selectedForm = useMemo(\n () => forms.find(form => form.id === selectedFormId),\n [forms, selectedFormId]\n );\n\n const formNameById = useMemo(\n () => new Map(forms.map(form => [form.id, form.name])),\n [forms]\n );\n\n // --- columns --------------------------------------------------------------\n // Hidden field columns, per form. Wide forms start with everything past\n // the first few hidden; the toolbar's Columns menu toggles the rest in.\n const [hiddenFieldColumns, setHiddenFieldColumns] = useState<Set<string>>(\n new Set()\n );\n useEffect(() => {\n setHiddenFieldColumns(\n new Set(\n (selectedForm?.fields ?? [])\n .slice(VISIBLE_FIELD_COLUMNS)\n .map(field => field.name)\n )\n );\n }, [selectedForm]);\n\n const columns = useMemo<NextlyColumn<SubmissionRow>[]>(() => {\n if (selectedForm) {\n // One form selected: real per-field columns.\n const fieldColumns = selectedForm.fields\n .filter(field => !hiddenFieldColumns.has(field.name))\n .map<NextlyColumn<SubmissionRow>>(field => ({\n name: `data.${field.name}`,\n header: field.label || field.name,\n accessor: row => formatExportValue(row.data?.[field.name], field),\n }));\n return [\n ...fieldColumns,\n {\n name: \"status\",\n header: \"Status\",\n accessor: row => row.status,\n cell: ({ row }) => <StatusBadge status={row.status} />,\n },\n {\n name: \"submittedAt\",\n header: \"Submitted\",\n accessor: row => formatDate(row.submittedAt),\n },\n ];\n }\n return [\n {\n name: \"form\",\n header: \"Form\",\n accessor: row => formNameById.get(formIdOf(row)) ?? formIdOf(row),\n },\n {\n name: \"data\",\n header: \"Submission\",\n accessor: row => summarizeData(row.data ?? {}),\n },\n {\n name: \"status\",\n header: \"Status\",\n accessor: row => row.status,\n cell: ({ row }) => <StatusBadge status={row.status} />,\n },\n {\n name: \"submittedAt\",\n header: \"Submitted\",\n accessor: row => formatDate(row.submittedAt),\n },\n ];\n }, [selectedForm, formNameById, hiddenFieldColumns]);\n\n // --- actions ---------------------------------------------------------------\n const patchSubmission = useCallback(\n async (id: string, body: Record<string, unknown>) => {\n const res = await fetch(\n `/admin/api/collections/${submissionsSlug}/entries/${id}`,\n {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify(body),\n }\n );\n if (!res.ok) throw new Error(`Update failed (${res.status})`);\n await fetchRows();\n },\n [submissionsSlug, fetchRows]\n );\n\n const deleteSubmission = useCallback(\n async (id: string) => {\n const res = await fetch(\n `/admin/api/collections/${submissionsSlug}/entries/${id}`,\n { method: \"DELETE\", credentials: \"include\" }\n );\n if (!res.ok) throw new Error(`Delete failed (${res.status})`);\n setOpenId(null);\n await fetchRows();\n },\n [submissionsSlug, fetchRows]\n );\n\n const rowActions = useCallback(\n (row: SubmissionRow): RowAction<SubmissionRow>[] => {\n const actions: RowAction<SubmissionRow>[] = [];\n if (row.status === \"spam\" && canUpdate) {\n actions.push({\n id: \"not-spam\",\n label: \"Not spam\",\n onSelect: target =>\n void patchSubmission(target.id, {\n status: \"new\",\n spamReason: null,\n }),\n });\n }\n if (canDelete) {\n actions.push({\n id: \"delete\",\n label: \"Delete\",\n destructive: true,\n onSelect: target => void deleteSubmission(target.id),\n });\n }\n return actions;\n },\n [canUpdate, canDelete, patchSubmission, deleteSubmission]\n );\n\n // --- export ----------------------------------------------------------------\n const exportHref = useCallback(\n (format: \"csv\" | \"json\") => {\n const params = new URLSearchParams({ format });\n if (selectedFormId) params.set(\"form\", selectedFormId);\n if (statusTab !== \"all\") params.set(\"status\", statusTab);\n return `/admin/api/plugins/@nextlyhq/plugin-form-builder/submissions/export?${params}`;\n },\n [selectedFormId, statusTab]\n );\n\n const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));\n // The open record is tracked by ID: after a save refetch the row may\n // move or leave the filtered view entirely — index tracking would silently\n // show a different submission, while a missing ID just closes the drawer.\n const openIndex = openId ? rows.findIndex(row => row.id === openId) : -1;\n const openRow = openIndex === -1 ? null : rows[openIndex];\n\n return (\n <div className=\"space-y-4\">\n {/* Toolbar: form filter, status tabs, export */}\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <Select\n value={selectedFormId || \"__all\"}\n onValueChange={value => {\n setSelectedFormId(value === \"__all\" ? \"\" : value);\n setPage(1);\n setOpenId(null);\n }}\n >\n <SelectTrigger\n aria-label=\"Filter by form\"\n className=\"w-full sm:w-56 bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__all\">All forms</SelectItem>\n {forms.map(form => (\n <SelectItem key={form.id} value={form.id}>\n {form.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Tabs\n value={statusTab}\n onValueChange={value => {\n setStatusTab(value as StatusTab);\n setPage(1);\n setOpenId(null);\n }}\n >\n {/* No corner override: square is the primitive's guarantee, not\n this view's decision. */}\n <TabsList>\n {STATUS_TABS.map(tab => (\n <TabsTrigger key={tab.value} value={tab.value}>\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {selectedForm && selectedForm.fields.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button type=\"button\" variant=\"outline\">\n <Columns3 className=\"h-4 w-4\" aria-hidden=\"true\" />\n Columns\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"shadow-none border-border\"\n >\n {selectedForm.fields.map(field => (\n <DropdownMenuCheckboxItem\n key={field.name}\n checked={!hiddenFieldColumns.has(field.name)}\n onCheckedChange={checked =>\n setHiddenFieldColumns(prev => {\n const next = new Set(prev);\n if (checked) next.delete(field.name);\n else next.add(field.name);\n return next;\n })\n }\n // Keep the menu open while toggling several columns.\n onSelect={event => event.preventDefault()}\n >\n {field.label || field.name}\n </DropdownMenuCheckboxItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button type=\"button\" variant=\"outline\">\n <Download className=\"h-4 w-4\" aria-hidden=\"true\" />\n Export\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"shadow-none border-border\"\n >\n {selectedFormId ? (\n <DropdownMenuItem asChild className=\"cursor-pointer\">\n <a href={exportHref(\"csv\")} download>\n CSV\n </a>\n </DropdownMenuItem>\n ) : (\n <Tooltip>\n <TooltipTrigger asChild>\n {/* CSV columns come from one form's fields, so it needs a\n form selected; the tooltip says so instead of hiding it. */}\n <DropdownMenuItem\n disabled\n onSelect={event => event.preventDefault()}\n >\n CSV\n </DropdownMenuItem>\n </TooltipTrigger>\n <TooltipContent side=\"left\">\n Select a form first — CSV columns come from its fields.\n </TooltipContent>\n </Tooltip>\n )}\n <DropdownMenuItem asChild className=\"cursor-pointer\">\n <a href={exportHref(\"json\")} download>\n JSON\n </a>\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n <DataTableView<SubmissionRow>\n columns={columns}\n rows={rows}\n loading={loading}\n error={error}\n emptyMessage={\n statusTab === \"spam\"\n ? \"No spam — nothing has been flagged.\"\n : \"No submissions yet.\"\n }\n ariaLabel=\"Submissions\"\n onRowClick={row => setOpenId(row.id)}\n rowActions={rowActions}\n />\n\n {/* Pagination */}\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span role=\"status\">\n {total === 0\n ? \"0 submissions\"\n : `${(page - 1) * PAGE_SIZE + 1}–${Math.min(page * PAGE_SIZE, total)} of ${total}`}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8 rounded-none\"\n disabled={page <= 1}\n onClick={() => setPage(current => current - 1)}\n aria-label=\"Previous page\"\n >\n <ChevronLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n <span>\n Page {page} of {totalPages}\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8 rounded-none\"\n disabled={page >= totalPages}\n onClick={() => setPage(current => current + 1)}\n aria-label=\"Next page\"\n >\n <ChevronRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n </div>\n\n {/* Detail drawer with prev/next over the current page */}\n {openRow && (\n <SubmissionSheet\n submission={openRow}\n form={\n forms.find(form => form.id === formIdOf(openRow)) ?? {\n id: formIdOf(openRow),\n name: formNameById.get(formIdOf(openRow)) ?? \"Form\",\n fields: [],\n }\n }\n canUpdate={canUpdate}\n onClose={() => setOpenId(null)}\n onPrev={\n openIndex > 0 ? () => setOpenId(rows[openIndex - 1].id) : undefined\n }\n onNext={\n openIndex < rows.length - 1\n ? () => setOpenId(rows[openIndex + 1].id)\n : undefined\n }\n onSave={async changes => {\n await patchSubmission(openRow.id, changes);\n }}\n />\n )}\n </div>\n );\n}\n\nexport default SubmissionsView;\n","\"use client\";\n\n/**\n * Submission Sheet\n *\n * Drawer detail for one submission: the submitted values in field order,\n * metadata, status + internal notes, and — behind the update permission —\n * inline editing of the submitted data. Prev/next walks the current table\n * page without losing filter context.\n *\n * @module admin/components/submissions/SubmissionSheet\n */\n\nimport {\n Badge,\n Button,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Switch,\n Textarea,\n} from \"@nextlyhq/ui\";\nimport { ChevronLeft, ChevronRight, Pencil, TriangleAlert } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\nimport type { FormField } from \"../../../types\";\nimport { formatExportValue } from \"../../../utils/export-formats\";\n\nimport type { FormOption, SubmissionRow } from \"./SubmissionsView\";\n\ninterface SubmissionSheetProps {\n submission: SubmissionRow;\n form: FormOption;\n canUpdate: boolean;\n onClose: () => void;\n onPrev?: () => void;\n onNext?: () => void;\n onSave: (changes: Record<string, unknown>) => Promise<void>;\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return \"\";\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return \"\";\n return date.toLocaleString(undefined, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n });\n}\n\n/** One editable input per field type; unsupported types render read-only. */\nfunction FieldValueInput({\n field,\n value,\n onChange,\n}: {\n field: FormField;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const id = `submission-field-${field.name}`;\n switch (field.type) {\n case \"textarea\":\n return (\n <Textarea\n id={id}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n rows={3}\n />\n );\n case \"checkbox\":\n return (\n <Switch\n id={id}\n checked={Boolean(value)}\n onCheckedChange={checked => onChange(checked)}\n />\n );\n case \"select\":\n case \"radio\": {\n const options = \"options\" in field ? (field.options ?? []) : [];\n return (\n <Select\n value={typeof value === \"string\" ? value : \"\"}\n onValueChange={onChange}\n >\n <SelectTrigger\n id={id}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map(option => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n }\n case \"number\":\n return (\n <Input\n id={id}\n type=\"number\"\n value={\n typeof value === \"number\" || typeof value === \"string\"\n ? String(value)\n : \"\"\n }\n onChange={e =>\n onChange(e.target.value === \"\" ? \"\" : Number(e.target.value))\n }\n />\n );\n case \"date\":\n case \"time\":\n return (\n <Input\n id={id}\n type={field.type}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n />\n );\n case \"text\":\n case \"email\":\n case \"url\":\n case \"phone\":\n return (\n <Input\n id={id}\n type={field.type === \"email\" ? \"email\" : \"text\"}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n />\n );\n case \"file\":\n case \"hidden\":\n default:\n // Files are media references, hidden fields are machine values, and\n // unknown types may hold structured data — coercing any of them\n // through a text input would corrupt them, so they stay read-only.\n return (\n <p className=\"text-sm text-muted-foreground\">\n {formatExportValue(value, field) || \"—\"}\n </p>\n );\n }\n}\n\nexport function SubmissionSheet({\n submission,\n form,\n canUpdate,\n onClose,\n onPrev,\n onNext,\n onSave,\n}: SubmissionSheetProps) {\n const [editing, setEditing] = useState(false);\n const [draftData, setDraftData] = useState<Record<string, unknown>>({});\n const [status, setStatus] = useState(submission.status);\n const [notes, setNotes] = useState(submission.notes ?? \"\");\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n\n // Prev/next swaps the submission while the sheet stays open — every piece\n // of local state must follow the record it belongs to.\n useEffect(() => {\n setEditing(false);\n setDraftData(submission.data ?? {});\n setStatus(submission.status);\n setNotes(submission.notes ?? \"\");\n setSaveError(null);\n }, [submission]);\n\n const fieldNames = new Set(form.fields.map(field => field.name));\n // Forms evolve after submissions exist: keys no longer on the form are\n // shown honestly instead of silently dropped.\n const orphanedEntries = Object.entries(submission.data ?? {}).filter(\n ([key]) => !fieldNames.has(key)\n );\n\n // Only a real value difference counts as a data edit — entering edit mode\n // and saving untouched values must not write `data` or earn an edit stamp.\n const dataChanged =\n editing &&\n form.fields.some(field => {\n const before = (submission.data ?? {})[field.name];\n const after = draftData[field.name];\n return JSON.stringify(before ?? null) !== JSON.stringify(after ?? null);\n });\n\n const dirty =\n dataChanged ||\n status !== submission.status ||\n notes !== (submission.notes ?? \"\");\n\n const handleSave = async () => {\n if (!canUpdate) return;\n setSaving(true);\n setSaveError(null);\n try {\n const changes: Record<string, unknown> = {};\n if (status !== submission.status) {\n changes.status = status;\n // Recovering from spam clears the stale detection reason.\n if (submission.status === \"spam\" && status !== \"spam\") {\n changes.spamReason = null;\n }\n }\n if (notes !== (submission.notes ?? \"\")) changes.notes = notes;\n if (dataChanged) changes.data = draftData;\n if (Object.keys(changes).length > 0) await onSave(changes);\n setEditing(false);\n } catch (err) {\n setSaveError(err instanceof Error ? err.message : \"Save failed\");\n } finally {\n setSaving(false);\n }\n };\n\n return (\n <Sheet\n open\n onOpenChange={open => {\n if (!open) onClose();\n }}\n >\n <SheetContent\n side=\"right\"\n className=\"w-[560px] sm:max-w-[560px] p-0 flex flex-col\"\n >\n <SheetHeader className=\"p-4 border-b border-border\">\n <div className=\"flex items-center justify-between gap-2\">\n <SheetTitle className=\"min-w-0 truncate\">\n Submission — {form.name}\n </SheetTitle>\n <div className=\"flex shrink-0 items-center gap-1 mr-6\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 rounded-none\"\n disabled={!onPrev}\n onClick={onPrev}\n aria-label=\"Previous submission\"\n >\n <ChevronLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 rounded-none\"\n disabled={!onNext}\n onClick={onNext}\n aria-label=\"Next submission\"\n >\n <ChevronRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n </div>\n <SheetDescription>\n Submitted {formatDate(submission.submittedAt)}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"flex-1 overflow-y-auto p-4 space-y-6\">\n {/* Spam banner */}\n {submission.status === \"spam\" && (\n // Full-strength destructive border so the spam banner boundary is perceivable over its tinted fill.\n <div className=\"flex items-center gap-2 border border-destructive bg-destructive/5 p-3 text-sm text-destructive\">\n <TriangleAlert className=\"h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n Flagged as spam\n {submission.spamReason ? ` (${submission.spamReason})` : \"\"}. Set\n the status to New to recover it.\n </div>\n )}\n\n {/* Submitted values */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm font-medium text-foreground\">\n Submitted values\n </p>\n {canUpdate && !editing && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setEditing(true)}\n >\n <Pencil className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Edit\n </Button>\n )}\n </div>\n\n <div className=\"space-y-3 border border-border p-3\">\n {form.fields.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">\n The form&apos;s fields are unavailable.\n </p>\n )}\n {form.fields.map(field => (\n <div key={field.name} className=\"space-y-1\">\n <Label htmlFor={`submission-field-${field.name}`}>\n {field.label || field.name}\n </Label>\n {editing ? (\n <FieldValueInput\n field={field}\n value={draftData[field.name]}\n onChange={value =>\n setDraftData(prev => ({\n ...prev,\n [field.name]: value,\n }))\n }\n />\n ) : (\n <p className=\"text-sm text-foreground wrap-break-word\">\n {formatExportValue(\n (submission.data ?? {})[field.name],\n field\n ) || <span className=\"text-muted-foreground\">—</span>}\n </p>\n )}\n </div>\n ))}\n </div>\n\n {orphanedEntries.length > 0 && (\n <div className=\"space-y-2 border border-border bg-muted/40 p-3\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n No longer on the form\n </p>\n {orphanedEntries.map(([key, value]) => (\n <p key={key} className=\"text-sm text-muted-foreground\">\n <span className=\"font-mono\">{key}</span>:{\" \"}\n {formatExportValue(value)}\n </p>\n ))}\n </div>\n )}\n\n {submission.editedAt && (\n <p className=\"text-xs text-muted-foreground\">\n Edited {formatDate(submission.editedAt)}\n {submission.editedBy ? ` by ${submission.editedBy}` : \"\"} — the\n values above are not necessarily what the visitor sent.\n </p>\n )}\n </div>\n\n {/* Status + notes */}\n <div className=\"grid grid-cols-1 gap-4 border-t border-border pt-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"submission-status\">Status</Label>\n {/* Status/notes writes go through the same update permission\n as data edits — without it, everything here is read-only. */}\n <Select\n value={status}\n onValueChange={setStatus}\n disabled={!canUpdate}\n >\n <SelectTrigger\n id=\"submission-status\"\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"new\">New</SelectItem>\n <SelectItem value=\"read\">Read</SelectItem>\n <SelectItem value=\"archived\">Archived</SelectItem>\n <SelectItem value=\"spam\">Spam</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-1.5 sm:col-span-2\">\n <Label htmlFor=\"submission-notes\">Internal notes</Label>\n <Textarea\n id=\"submission-notes\"\n value={notes}\n onChange={e => setNotes(e.target.value)}\n rows={3}\n placeholder=\"Visible to admins only\"\n disabled={!canUpdate}\n />\n </div>\n </div>\n\n {/* Metadata */}\n <div className=\"space-y-1 border-t border-border pt-4 text-xs text-muted-foreground\">\n <p>\n IP: {submission.ipAddress || \"—\"}\n <span className=\"mx-1.5 text-muted-foreground\">·</span>\n ID: <span className=\"font-mono\">{submission.id}</span>\n </p>\n {submission.userAgent && (\n <p className=\"break-all\">Agent: {submission.userAgent}</p>\n )}\n {submission.spamReason && submission.status !== \"spam\" && (\n <p>\n Previously flagged:{\" \"}\n <Badge\n variant=\"outline\"\n className=\"rounded-none border-border px-1 py-0 text-[10px]\"\n >\n {submission.spamReason}\n </Badge>\n </p>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center justify-between gap-3 border-t border-border bg-muted p-4\">\n {saveError ? (\n <p className=\"text-xs text-destructive\">{saveError}</p>\n ) : (\n <span />\n )}\n <div className=\"flex items-center gap-3\">\n <Button type=\"button\" variant=\"outline\" onClick={onClose}>\n Close\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleSave()}\n disabled={!canUpdate || !dirty || saving}\n >\n {saving ? \"Saving…\" : \"Save\"}\n </Button>\n </div>\n </div>\n </SheetContent>\n </Sheet>\n );\n}\n\nexport default SubmissionSheet;\n","\"use client\";\n\n/**\n * Form Builder View\n *\n * Custom admin view that provides a visual drag-and-drop form builder.\n * Layout mirrors the Collection Builder for visual consistency.\n *\n * @module admin/FormBuilderView\n * @since 0.1.0\n */\n\n\"use client\";\n\nimport type { DocumentLockGates } from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n Button,\n Card,\n FieldShell,\n FormActions,\n Input,\n Textarea,\n toast,\n Tabs,\n TabsList,\n TabsTrigger,\n Select,\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n} from \"@nextlyhq/ui\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport type { FormFieldCatalogType } from \"nextly/field-catalog\";\nimport { FORM_FIELD_TYPE_CATALOG } from \"nextly/field-catalog\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport type { FormField, FormFieldTypeId } from \"../types\";\n\nimport { FieldCards } from \"./components/builder/FieldCards\";\nimport {\n FormNotificationsTab,\n type NotificationDefaults,\n} from \"./components/builder/FormNotificationsTab\";\nimport { FormPreview } from \"./components/builder/FormPreview\";\nimport {\n FormSettingsTab,\n type SpamDefaults,\n} from \"./components/builder/FormSettingsTab\";\nimport { notificationsBlockingSave } from \"./components/builder/notification-addresses\";\nimport {\n FormBuilderProvider,\n useFormBuilder,\n createFieldFromType,\n createNotification,\n type FormNotification,\n} from \"./context/FormBuilderContext\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface FormBuilderViewProps {\n id?: string;\n entryId?: string;\n collection?: string;\n collectionSlug?: string;\n isCreating?: boolean;\n initialData?: {\n id?: string;\n name?: string;\n slug?: string;\n description?: string;\n status?: \"draft\" | \"published\" | \"closed\";\n closedMessage?: string;\n fields?: unknown[];\n settings?: Record<string, unknown>;\n notifications?: unknown[];\n };\n onSave?: (data: unknown) => void;\n onSuccess?: (entry?: Record<string, unknown>) => void;\n onCancel?: () => void;\n /**\n * What a colleague's claim on this form withholds, handed over by the admin\n * page that renders this view.\n *\n * The strip above this builder says in words that unsaved changes cannot be\n * saved while someone else holds the document. Committing anyway would make\n * that sentence false and would overwrite the holder's row, so the save reads\n * it too.\n */\n documentLock?: DocumentLockGates;\n}\n\n// ============================================================================\n// Helper\n// ============================================================================\n\nfunction slugify(text: string): string {\n return text\n .toLowerCase()\n .trim()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/[\\s_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n// ============================================================================\n// Inner Component (uses context)\n// ============================================================================\n\nfunction FormBuilderViewInner({\n isCreating = false,\n collectionSlug = \"forms\",\n entryId,\n onSave,\n onSuccess,\n onCancel,\n documentLock,\n}: Pick<\n FormBuilderViewProps,\n | \"isCreating\"\n | \"collectionSlug\"\n | \"entryId\"\n | \"onSave\"\n | \"onSuccess\"\n | \"onCancel\"\n | \"documentLock\"\n>) {\n const {\n fields,\n activeTab,\n isDirty,\n formData,\n settings,\n notifications,\n setActiveTab,\n addField,\n selectField,\n seedNotifications,\n updateFormData,\n markAsSaved,\n } = useFormBuilder();\n\n const queryClient = useQueryClient();\n\n const [isSaving, setIsSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n\n // The host's resolved field enable/disable map. `null` until the config\n // request settles, so the Add dialog never flashes the unfiltered set. If\n // the request errors, every type is offered: the exclude list is an\n // authoring preference (the server accepts any field type in the form's\n // JSON regardless), so hiding all types on a transient failure would cost\n // more than a temporarily unfiltered picker.\n const [enabledTypes, setEnabledTypes] = useState<\n FormFieldCatalogType[] | null\n >(null);\n\n // Type ids the host explicitly disabled (`config.fields[type] === false`),\n // including plugin type ids — so the host exclude layer applies to\n // contributed field types too, not just built-ins.\n const [disabledFieldTypes, setDisabledFieldTypes] = useState<Set<string>>(\n () => new Set()\n );\n\n // The host's notification defaults (plugin options). `null` until the\n // config request settles; `{}` when the request failed or nothing is\n // configured, so consumers can distinguish \"loading\" from \"no defaults\".\n const [notificationDefaults, setNotificationDefaults] =\n useState<NotificationDefaults | null>(null);\n\n // Plugin-level spam defaults, so the Settings tab can show what\n // \"inherit\" resolves to. `null` while the config request settles.\n const [spamDefaults, setSpamDefaults] = useState<SpamDefaults | null>(null);\n\n // Collections a form may redirect to. `null` while the config request\n // settles, so the Settings tab can tell \"not configured\" from \"not known\n // yet\" and not flash the option away.\n const [redirectCollections, setRedirectCollections] = useState<\n string[] | null\n >(null);\n\n // Whether the configuration request FAILED, kept apart from what it\n // returned. A 403 or a 500 mapped to an empty list is indistinguishable from\n // \"this site configures no redirect targets\" — so the Settings tab would\n // state that as fact, hide the option, and offer no way to tell the\n // difference or try again.\n const [redirectConfigFailed, setRedirectConfigFailed] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n const allTypes = FORM_FIELD_TYPE_CATALOG.map(entry => entry.type);\n void fetch(\n \"/admin/api/plugins/@nextlyhq/plugin-form-builder/builder-config\",\n { credentials: \"include\" }\n )\n .then(response => (response.ok ? response.json() : null))\n .then(\n (\n config: {\n fields?: Record<string, boolean>;\n notifications?: NotificationDefaults;\n spamProtection?: SpamDefaults;\n redirectCollections?: string[];\n } | null\n ) => {\n if (cancelled) return;\n setEnabledTypes(\n config?.fields\n ? allTypes.filter(type => config.fields?.[type] !== false)\n : allTypes\n );\n setDisabledFieldTypes(\n new Set(\n Object.entries(config?.fields ?? {})\n .filter(([, enabled]) => enabled === false)\n .map(([type]) => type)\n )\n );\n setNotificationDefaults(config?.notifications ?? {});\n setSpamDefaults(config?.spamProtection ?? {});\n // `config === null` is the failure branch: the response was not ok.\n setRedirectConfigFailed(config === null);\n setRedirectCollections(config?.redirectCollections ?? []);\n }\n )\n .catch(() => {\n if (cancelled) return;\n setEnabledTypes(allTypes);\n setNotificationDefaults({});\n setSpamDefaults({});\n setRedirectConfigFailed(true);\n setRedirectCollections([]);\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n // Seed a new form with the default admin-notification rule once the host\n // defaults are known. Guarded by a ref so deleting the seeded rule is\n // final — the effect must never re-seed a list the user emptied.\n const seededRef = useRef(false);\n useEffect(() => {\n if (!isCreating || notificationDefaults === null || seededRef.current) {\n return;\n }\n seededRef.current = true;\n seedNotifications([\n {\n ...createNotification(),\n name: \"Admin notification\",\n to: notificationDefaults.defaultToEmail ?? \"\",\n },\n ]);\n }, [isCreating, notificationDefaults, seedNotifications]);\n\n const handleAddField = useCallback(\n (type: FormFieldTypeId) => {\n const newField = createFieldFromType(\n type,\n fields.map(f => f.name)\n );\n addField(newField);\n selectField(newField.name);\n },\n [fields, addField, selectField]\n );\n\n // ── Save / Cancel ─────────────────────────────────────────────────────────\n const handleSave = useCallback(async () => {\n // The commit is where the refusal lives, following the rule the entry\n // editor sets: a write passes one gate rather than each control being\n // disabled separately. The button below reads the same fact, so the\n // interface does not offer what this would refuse.\n //\n // Today that button is this callback's only caller, so the two cannot be\n // told apart by a test. What the gate is for is the next caller - a\n // shortcut, a second action bar - arriving without one.\n if (documentLock?.actionsDisabled === true) return;\n\n // Check if there are fields\n if (fields.length === 0) {\n toast.error(\"Please add at least one field to the form\");\n return;\n }\n\n // A notification is written to the form as it is typed, so nothing between\n // the editor and here refuses one. The sheet this replaced disabled its own\n // commit for a blank name, and the field rejects a malformed address on\n // blur — both preconditions move to this save, which is now the only\n // commit, and both are answered by the module the editor itself asks.\n const blocked = notificationsBlockingSave(notifications);\n if (blocked) {\n toast.error(blocked);\n return;\n }\n\n const { id: _id, ...formDataWithoutId } = formData;\n const saveData = {\n ...formDataWithoutId,\n fields,\n settings,\n notifications,\n };\n\n if (onSave) {\n onSave(saveData);\n return;\n }\n\n setIsSaving(true);\n setSaveError(null);\n try {\n const url = isCreating\n ? `/admin/api/collections/${collectionSlug}/entries`\n : `/admin/api/collections/${collectionSlug}/entries/${entryId}`;\n const response = await fetch(url, {\n method: isCreating ? \"POST\" : \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify(saveData),\n });\n if (!response.ok) {\n throw new Error(\n saveErrorMessage(\n await response.json().catch(() => ({})),\n response.statusText\n )\n );\n }\n\n const result = (await response.json()) as {\n message?: string;\n item?: Record<string, unknown>;\n } & Record<string, unknown>;\n const savedEntry: Record<string, unknown> = result.item ?? result;\n void queryClient.invalidateQueries({\n queryKey: [\"entries\", \"list\", collectionSlug],\n });\n void queryClient.invalidateQueries({ queryKey: [\"entries\", \"count\"] });\n if (!isCreating && entryId) {\n void queryClient.invalidateQueries({\n queryKey: [\"entries\", \"detail\", collectionSlug, entryId],\n });\n }\n markAsSaved();\n toast.success(\n isCreating ? \"Form created successfully\" : \"Form updated successfully\"\n );\n if (onSuccess) onSuccess(savedEntry);\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Failed to save form\";\n setSaveError(message);\n toast.error(message);\n } finally {\n setIsSaving(false);\n }\n }, [\n formData,\n fields,\n settings,\n notifications,\n onSave,\n onSuccess,\n isCreating,\n collectionSlug,\n entryId,\n queryClient,\n markAsSaved,\n // Read inside, so it belongs here. Without it the callback closes over the\n // claim as it stood when the form mounted, and a colleague taking the\n // document over mid-session would leave this still committing.\n documentLock?.actionsDisabled,\n ]);\n\n const handleCancel = useCallback(() => {\n if (onCancel) onCancel();\n else if (typeof window !== \"undefined\") window.history.back();\n }, [onCancel]);\n\n const handleNameChange = useCallback(\n (name: string) => {\n updateFormData({ name });\n if (!formData.slug || formData.slug === slugify(formData.name)) {\n updateFormData({ slug: slugify(name) });\n }\n },\n [formData.name, formData.slug, updateFormData]\n );\n\n // Main tabs\n const mainTabs = [\n {\n value: \"builder\" as const,\n label: \"Builder\",\n count: fields.length > 0 ? fields.length : null,\n },\n {\n value: \"preview\" as const,\n label: \"Preview\",\n count: null,\n },\n { value: \"settings\" as const, label: \"Settings\", count: null },\n {\n value: \"notifications\" as const,\n label: \"Notifications\",\n count: notifications.length > 0 ? notifications.length : null,\n },\n ];\n\n // ── Render ────────────────────────────────────────────────────────────────\n // The host page owns the measure, the centring and the padding — this view\n // renders as content inside its frame, under its breadcrumb. Declaring a\n // shell here would nest one inset inside another and put this heading out of\n // line with that breadcrumb. Nothing below hand-rolls a card, a width cap or\n // a hack to escape either.\n return (\n <>\n {/* ── Page header ── */}\n <div className=\"mb-6\">\n <h1 className=\"text-2xl font-semibold text-foreground tracking-tight\">\n {isCreating ? \"Create Form\" : \"Edit Form\"}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {isCreating\n ? \"Design a new form with drag-and-drop fields.\"\n : \"Modify your form fields and settings.\"}\n </p>\n </div>\n\n {/* ── Form metadata ──\n A card, so the fields that identify the form read as one group and\n have a surface to sit on. The builder canvas below is deliberately\n NOT carded: it draws its own drop zone, and a frame around a frame\n reads as a nested box rather than as structure. */}\n {/* `pb-6` against `pt-5`: the box padding is equal at 20px, but the\n first thing inside is a LABEL and the last is a control. A label's\n line box carries 3px of leading above its glyphs, so the ink starts\n lower than the box does while the control's border ends flush —\n measured 24px of visible space above and 21px below. The extra step\n on the bottom is what makes the two read as equal. */}\n <Card className=\"mb-6 px-6 pt-5 pb-6\">\n <div className=\"flex flex-wrap gap-4\">\n <FieldShell\n label=\"Form Name\"\n htmlFor=\"form-name\"\n className=\"flex-1 min-w-[200px]\"\n >\n <Input\n type=\"text\"\n value={formData.name || \"\"}\n onChange={e => handleNameChange(e.target.value)}\n placeholder=\"e.g., Contact Form\"\n />\n </FieldShell>\n\n <FieldShell\n label=\"Slug\"\n htmlFor=\"form-slug\"\n className=\"flex-1 min-w-[160px]\"\n >\n <Input\n type=\"text\"\n value={formData.slug || \"\"}\n onChange={e => updateFormData({ slug: e.target.value })}\n placeholder=\"e.g., contact-form\"\n className=\"placeholder:text-muted-foreground\"\n />\n </FieldShell>\n\n {/* Status is a Radix Select: its Root accepts a fixed prop list and\n forwards none of the rest to the trigger DOM node, so a\n FieldShell clone's id/aria-describedby/aria-invalid would land\n on a component that drops them rather than on the focusable\n trigger. The render-function form of `children` sidesteps\n that: FieldShell hands the computed wiring to this function,\n which applies it to SelectTrigger — the actual focusable,\n ARIA-bearing element — instead of a clone that can never reach\n past `Select`'s root. */}\n <div className=\"w-36\">\n <FieldShell label=\"Status\" htmlFor=\"form-status\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={formData.status || \"draft\"}\n onValueChange={value =>\n updateFormData({\n status: value as \"draft\" | \"published\" | \"closed\",\n })\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n >\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"draft\">Draft</SelectItem>\n <SelectItem value=\"published\">Published</SelectItem>\n <SelectItem value=\"closed\">Closed</SelectItem>\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n </div>\n </div>\n\n {/* Shown only once the author has chosen Closed. The explanation is\n written at the moment the decision is made, which is when they know\n what it should say; a box that is always there would sit empty on\n the great majority of forms, which are never closed. */}\n {formData.status === \"closed\" && (\n <div className=\"mt-4\">\n <FieldShell\n label=\"Message for visitors\"\n htmlFor=\"form-closed-message\"\n // Prose, not a value: the half width the other fields take is\n // sized for a name or a slug, and wrapping a sentence every six\n // words makes it harder to read back what was written.\n width=\"full\"\n description=\"Shown to anyone who reaches this form now that it is closed. Left empty, they are told only that it is not accepting submissions.\"\n >\n <Textarea\n value={formData.closedMessage ?? \"\"}\n onChange={e =>\n updateFormData({ closedMessage: e.target.value })\n }\n placeholder=\"e.g., Applications closed on 31 March. Thank you to everyone who applied.\"\n rows={3}\n />\n </FieldShell>\n </div>\n )}\n </Card>\n\n {/* ── Main tab navigation ──\n On the page rather than inside the card above: the rail switches what\n is BELOW it, so grouping it with the metadata would attach it to the\n wrong thing. */}\n <div className=\"border-b border-border mb-6\">\n <Tabs\n value={activeTab}\n onValueChange={v => setActiveTab(v as typeof activeTab)}\n >\n {/* Layout only. The underline, the active and hover colours, the\n focus ring and the disabled state all come from the shared\n primitive; restating them here is how two copies of one\n appearance start drifting.\n\n The strip can overflow its container, so it asks for scrolling\n through `scrollable` rather than by putting `overflow-x-auto` on\n the list: that spelling makes the LIST the scroll container, which\n costs the rail its indicator. */}\n <TabsList scrollable className=\"bg-transparent justify-start gap-0\">\n {mainTabs.map(tab => (\n <TabsTrigger\n key={tab.value}\n value={tab.value}\n className=\"shrink-0 whitespace-nowrap\"\n >\n {tab.label}\n {/* `rounded-sm`, the adornment step, matching the shared\n Badge and the identically sized notification count.\n Square is the TAB's corner and it exists so the\n underline runs flush to the trigger's edges; a chip\n inside the label inherits none of that reasoning, and\n carrying it made the count read as a second tab. */}\n {tab.count !== null && (\n <span\n className={`inline-flex items-center justify-center min-w-[18px] h-[18px] px-1 text-[10px] font-semibold rounded-sm ml-2 transition-colors ${\n activeTab === tab.value\n ? \"bg-primary/5 text-primary\"\n : \"bg-primary/5 text-muted-foreground\"\n }`}\n >\n {tab.count}\n </span>\n )}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n </div>\n\n {/* ── Tab content ── */}\n\n {/* Builder tab */}\n {activeTab === \"builder\" && (\n <FieldCards\n enabledTypes={enabledTypes}\n disabledFieldTypes={disabledFieldTypes}\n onAddField={handleAddField}\n />\n )}\n\n {/* Preview tab */}\n {activeTab === \"preview\" && (\n <FormPreview fields={fields} formData={formData} />\n )}\n\n {/* Settings tab */}\n {activeTab === \"settings\" && (\n <FormSettingsTab\n spamDefaults={spamDefaults}\n redirectCollections={redirectCollections}\n redirectConfigFailed={redirectConfigFailed}\n />\n )}\n\n {/* Notifications tab */}\n {activeTab === \"notifications\" && (\n <FormNotificationsTab defaults={notificationDefaults} />\n )}\n\n {/* Save error */}\n {saveError && (\n // Full-strength destructive border so the error box boundary is perceivable over its tinted fill.\n <div className=\"mt-6 p-3 text-sm text-destructive bg-destructive/10 border border-destructive rounded-none\">\n {saveError}\n </div>\n )}\n\n {/* A form commits as one document, so there is exactly one action bar,\n fed the dirty flag the form state already tracks rather than a\n second computation of it. */}\n <FormActions dirty={isDirty}>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={handleCancel}\n disabled={isSaving}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => {\n void handleSave();\n }}\n disabled={isSaving || documentLock?.actionsDisabled === true}\n className=\"flex items-center gap-1.5\"\n >\n {isSaving ? (\n <>\n <svg\n className=\"animate-spin h-3.5 w-3.5\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"\n />\n </svg>\n Saving…\n </>\n ) : isCreating ? (\n \"Create\"\n ) : (\n \"Save Changes\"\n )}\n </Button>\n </FormActions>\n </>\n );\n}\n\n// ============================================================================\n// Main Component (provides context)\n// ============================================================================\n\n/**\n * What to tell an author when a save is refused.\n *\n * A validation refusal carries the generic `\"Validation failed.\"` at the top\n * level and puts the actionable part in `data.errors`. Reporting only the top\n * level tells an author their form was rejected and nothing about what to\n * change — every rule in the collection produces that same sentence.\n */\nexport function saveErrorMessage(body: unknown, statusText: string): string {\n const error = (\n body as {\n error?: {\n message?: string;\n data?: { errors?: { message?: string }[] };\n };\n }\n )?.error;\n\n const fieldIssues = (error?.data?.errors ?? [])\n .map(issue => issue.message)\n .filter((message): message is string => Boolean(message));\n\n return (\n fieldIssues.join(\" \") ||\n error?.message ||\n `Failed to save form: ${statusText}`\n );\n}\n\nexport function FormBuilderView({\n id,\n entryId,\n collection,\n collectionSlug,\n isCreating,\n initialData,\n onSave,\n onSuccess,\n onCancel,\n documentLock,\n}: FormBuilderViewProps) {\n const resolvedEntryId = entryId || id || initialData?.id;\n const resolvedCollectionSlug = collectionSlug || collection || \"forms\";\n const resolvedIsCreating = isCreating ?? !resolvedEntryId;\n const providerKey = resolvedEntryId || \"new\";\n\n return (\n <FormBuilderProvider\n key={providerKey}\n readOnly={documentLock?.readOnly === true}\n initialData={{\n id: resolvedEntryId,\n name: initialData?.name,\n slug: initialData?.slug,\n description: initialData?.description,\n status: initialData?.status,\n closedMessage: initialData?.closedMessage,\n fields: initialData?.fields as FormField[],\n settings: initialData?.settings as Record<string, unknown>,\n notifications: initialData?.notifications as FormNotification[],\n }}\n >\n <FormBuilderViewInner\n isCreating={resolvedIsCreating}\n collectionSlug={resolvedCollectionSlug}\n entryId={resolvedEntryId}\n onSave={onSave}\n onSuccess={onSuccess}\n onCancel={onCancel}\n documentLock={documentLock}\n />\n </FormBuilderProvider>\n );\n}\n\nexport default FormBuilderView;\n","\"use client\";\n\n/**\n * Field Cards\n *\n * The form builder's field list: one collapsible card per field, edited\n * inline. This is the whole field-editing surface — there is no separate\n * palette or properties sidebar.\n *\n * Reordering is triple-redundant on purpose: drag handles (pointer), the\n * same handle with Space + arrow keys (dnd-kit keyboard sensor), and\n * explicit Move up / Move down menu items. Deleting a field that another\n * field's condition or a notification references is blocked with the list\n * of referrers instead of silently breaking them.\n *\n * @module admin/components/builder/FieldCards\n */\n\nimport {\n closestCenter,\n DndContext,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n sortableKeyboardCoordinates,\n useSortable,\n verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { usePluginFieldTypeEntries } from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@nextlyhq/ui\";\nimport * as Lucide from \"lucide-react\";\nimport type {\n FieldTypeCatalogEntry,\n FormFieldCatalogType,\n} from \"nextly/field-catalog\";\nimport { FORM_FIELD_TYPE_CATALOG } from \"nextly/field-catalog\";\nimport { useCallback, useMemo, useState } from \"react\";\n\nimport type { AnyFormField, FormFieldTypeId } from \"../../../types\";\nimport { buildFieldReferenceMap } from \"../../../utils/field-references\";\nimport { useFormBuilder } from \"../../context/FormBuilderContext\";\n\nimport { AddFieldDialog } from \"./AddFieldDialog\";\nimport { FieldEditor } from \"./FieldEditor\";\n\n// ---------------------------------------------------------------------------\n// Catalog helpers\n// ---------------------------------------------------------------------------\n\nconst CATALOG_BY_TYPE = new Map<\n string,\n FieldTypeCatalogEntry<FormFieldCatalogType>\n>(FORM_FIELD_TYPE_CATALOG.map(entry => [entry.type, entry]));\n\n/** Resolve a catalog icon name against lucide; unknown names fall back. */\nfunction CatalogIcon({\n name,\n className,\n}: {\n name: string | undefined;\n className?: string;\n}) {\n const icons = Lucide as unknown as Record<string, React.ElementType>;\n const Icon = (name && icons[name]) || Lucide.Type;\n return <Icon className={className} aria-hidden=\"true\" />;\n}\n\n// ---------------------------------------------------------------------------\n// One field card\n// ---------------------------------------------------------------------------\n\ninterface FieldCardProps {\n field: AnyFormField;\n index: number;\n total: number;\n expanded: boolean;\n onToggle: () => void;\n allFields: AnyFormField[];\n deleteBlockers: string[];\n onUpdate: (updates: Partial<AnyFormField>) => void;\n onDuplicate: () => void;\n onDelete: () => void;\n onMove: (direction: -1 | 1) => void;\n}\n\nfunction FieldCard({\n field,\n index,\n total,\n expanded,\n onToggle,\n allFields,\n deleteBlockers,\n onUpdate,\n onDuplicate,\n onDelete,\n onMove,\n}: FieldCardProps) {\n const {\n attributes,\n listeners,\n setNodeRef,\n setActivatorNodeRef,\n transform,\n transition,\n isDragging,\n } = useSortable({ id: field.name });\n\n const entry = CATALOG_BY_TYPE.get(field.type);\n const headerId = `field-card-header-${field.name}`;\n const bodyId = `field-card-body-${field.name}`;\n const deleteBlocked = deleteBlockers.length > 0;\n\n return (\n <div\n ref={setNodeRef}\n style={{ transform: CSS.Transform.toString(transform), transition }}\n className={\n // Dragging uses full-strength border-primary so the lifted card stands out; the resting state uses border-border, keeping the two distinct.\n isDragging\n ? \"border border-primary bg-card opacity-80 relative z-10\"\n : \"border border-border bg-card\"\n }\n >\n {/* Header row: every control is a real, separate interactive element —\n no nested-button markup. */}\n <div className=\"flex items-center gap-2 px-3 py-2.5\">\n <button\n type=\"button\"\n ref={setActivatorNodeRef}\n {...attributes}\n {...listeners}\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-none text-muted-foreground hover:text-foreground hover:bg-primary/5 cursor-grab active:cursor-grabbing focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n aria-label={`Reorder ${field.label || field.name}. Press Space to lift, arrow keys to move, Space to drop.`}\n >\n <Lucide.GripVertical className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={expanded}\n aria-controls={bodyId}\n id={headerId}\n className=\"flex min-w-0 flex-1 items-center gap-3 rounded-none py-1 text-left cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center bg-primary/5 text-primary\">\n <CatalogIcon name={entry?.icon} className=\"h-3.5 w-3.5\" />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-sm font-medium text-foreground\">\n {field.label || field.name}\n </span>\n <span className=\"block truncate text-xs text-muted-foreground\">\n {entry?.label ?? field.type}\n <span className=\"mx-1.5 text-muted-foreground\">·</span>\n <span className=\"font-mono\">{field.name}</span>\n </span>\n </span>\n {field.required && (\n <span className=\"shrink-0 border border-border bg-primary/5 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-primary\">\n Required\n </span>\n )}\n <Lucide.ChevronDown\n className={`h-4 w-4 shrink-0 text-muted-foreground transition-transform ${expanded ? \"rotate-180\" : \"\"}`}\n aria-hidden=\"true\"\n />\n </button>\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"h-8 w-8 shrink-0 rounded-none text-muted-foreground hover:text-foreground\"\n aria-label={`Actions for ${field.label || field.name}`}\n >\n <Lucide.MoreHorizontal className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"w-56 shadow-none border-border\"\n >\n <DropdownMenuItem\n disabled={index === 0}\n onClick={() => onMove(-1)}\n className=\"gap-2 cursor-pointer\"\n >\n <Lucide.ArrowUp className=\"h-4 w-4 text-muted-foreground\" />\n Move up\n </DropdownMenuItem>\n <DropdownMenuItem\n disabled={index === total - 1}\n onClick={() => onMove(1)}\n className=\"gap-2 cursor-pointer\"\n >\n <Lucide.ArrowDown className=\"h-4 w-4 text-muted-foreground\" />\n Move down\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={onDuplicate}\n className=\"gap-2 cursor-pointer\"\n >\n <Lucide.Copy className=\"h-4 w-4 text-muted-foreground\" />\n Duplicate\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n {deleteBlocked ? (\n <Tooltip delayDuration={0}>\n <TooltipTrigger asChild>\n <div>\n <DropdownMenuItem\n disabled\n className=\"gap-2 text-muted-foreground\"\n >\n <Lucide.Trash2 className=\"h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </div>\n </TooltipTrigger>\n <TooltipContent side=\"left\" className=\"max-w-64\">\n Used by {deleteBlockers.join(\", \")}. Remove those references\n first.\n </TooltipContent>\n </Tooltip>\n ) : (\n <DropdownMenuItem\n onClick={onDelete}\n className=\"gap-2 cursor-pointer text-destructive focus:text-destructive\"\n >\n <Lucide.Trash2 className=\"h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Inline properties: the card IS the properties panel. */}\n {expanded && (\n <div\n id={bodyId}\n role=\"region\"\n aria-labelledby={headerId}\n className=\"border-t border-border\"\n >\n <FieldEditor\n field={field}\n allFields={allFields}\n onUpdate={onUpdate}\n />\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// The list\n// ---------------------------------------------------------------------------\n\nexport interface FieldCardsProps {\n /**\n * Form-surface types the host has NOT excluded (the plugin's field\n * enable/disable option). `null` while the host configuration is still\n * loading — the Add dialog waits rather than flashing the unfiltered set.\n * Types outside this set stay renderable on existing fields but cannot be\n * added.\n */\n enabledTypes: readonly FormFieldCatalogType[] | null;\n /**\n * Type ids the host disabled (`config.fields[type] === false`). Applied to\n * plugin field types too, so the host exclude layer is not bypassed for\n * contributed types.\n */\n disabledFieldTypes?: ReadonlySet<string>;\n /** Creates a field of the given type and returns it (context helper). */\n onAddField: (type: FormFieldTypeId) => void;\n}\n\nexport function FieldCards({\n enabledTypes,\n disabledFieldTypes,\n onAddField,\n}: FieldCardsProps) {\n const {\n fields,\n notifications,\n selectedFieldId,\n selectField,\n updateField,\n deleteField,\n duplicateField,\n moveField,\n } = useFormBuilder();\n\n const [isAddOpen, setIsAddOpen] = useState(false);\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })\n );\n\n // Plugin field types that opted into the forms surface, offered alongside the\n // enabled built-ins. A plugin reusing a built-in id never shadows it.\n const pluginEntries = usePluginFieldTypeEntries(\"forms\");\n\n const entries = useMemo(() => {\n if (enabledTypes === null) return null;\n const allowed = new Set<string>(enabledTypes);\n const builtinTypes = new Set<string>(\n FORM_FIELD_TYPE_CATALOG.map(entry => entry.type)\n );\n const builtins: FieldTypeCatalogEntry<FormFieldTypeId>[] =\n FORM_FIELD_TYPE_CATALOG.filter(entry => allowed.has(entry.type));\n return [\n ...builtins,\n // A plugin type a built-in shadows, or one the host disabled, is excluded.\n ...pluginEntries.filter(\n entry =>\n !builtinTypes.has(entry.type) && !disabledFieldTypes?.has(entry.type)\n ),\n ];\n }, [enabledTypes, pluginEntries, disabledFieldTypes]);\n\n // One O(N^2) pass per fields/notifications change; each card then looks\n // its blockers up in O(1) instead of re-walking everything per render.\n const referenceMap = useMemo(\n () => buildFieldReferenceMap(fields, notifications),\n [fields, notifications]\n );\n\n const handleDragEnd = useCallback(\n (event: DragEndEvent) => {\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n const from = fields.findIndex(f => f.name === active.id);\n const to = fields.findIndex(f => f.name === over.id);\n if (from !== -1 && to !== -1) moveField(from, to);\n },\n [fields, moveField]\n );\n\n return (\n <div className=\"space-y-3\">\n {fields.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 border border-dashed border-border bg-card px-6 py-14 text-center\">\n <span className=\"flex h-10 w-10 items-center justify-center bg-primary/10 text-primary\">\n <Lucide.ListPlus className=\"h-5 w-5\" aria-hidden=\"true\" />\n </span>\n <div>\n <p className=\"text-sm font-medium text-foreground\">No fields yet</p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n Add the first field to start building this form.\n </p>\n </div>\n <Button type=\"button\" size=\"sm\" onClick={() => setIsAddOpen(true)}>\n Add field\n </Button>\n </div>\n ) : (\n <>\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragEnd={handleDragEnd}\n >\n <SortableContext\n items={fields.map(f => f.name)}\n strategy={verticalListSortingStrategy}\n >\n <ul className=\"space-y-2\" aria-label=\"Form fields\">\n {fields.map((field, index) => (\n <li key={field.name}>\n <FieldCard\n field={field}\n index={index}\n total={fields.length}\n expanded={selectedFieldId === field.name}\n onToggle={() =>\n selectField(\n selectedFieldId === field.name ? null : field.name\n )\n }\n allFields={fields}\n deleteBlockers={(referenceMap.get(field.name) ?? []).map(\n ref => ref.label\n )}\n onUpdate={updates => updateField(field.name, updates)}\n onDuplicate={() => duplicateField(field.name)}\n onDelete={() => deleteField(field.name)}\n onMove={direction => moveField(index, index + direction)}\n />\n </li>\n ))}\n </ul>\n </SortableContext>\n </DndContext>\n\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setIsAddOpen(true)}\n className=\"w-full border-dashed\"\n >\n <Lucide.Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Add field\n </Button>\n </>\n )}\n\n <AddFieldDialog\n open={isAddOpen}\n onOpenChange={setIsAddOpen}\n entries={entries}\n onAdd={onAddField}\n />\n </div>\n );\n}\n","\"use client\";\n\n/**\n * Form Builder Context\n *\n * Provides state management for the Form Builder UI.\n * Manages form fields, selection state, and form metadata.\n *\n * @module admin/context/FormBuilderContext\n * @since 0.1.0\n */\n\n\"use client\";\n\nimport { FORM_FIELD_TYPE_CATALOG } from \"nextly/field-catalog\";\nimport {\n createContext,\n useContext,\n useCallback,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport type {\n AnyFormField,\n FormFieldTypeId,\n FormNotification,\n FormSettings,\n} from \"../../types\";\nimport {\n DEFAULT_FORM_SETTINGS,\n normalizeFormSettings,\n} from \"../../utils/form-settings\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * The canonical settings shape lives in `types.ts` (the same shape the\n * collection group declares and the submit handler consumes); re-exported\n * here so admin components keep one import site.\n */\nexport type { FormSettings } from \"../../types\";\n\n/**\n * The notification rule type lives in `types.ts` (the same shape the send\n * path consumes); re-exported here so admin components keep one import site.\n */\nexport type { FormNotification } from \"../../types\";\n\nexport interface FormBuilderState {\n /** Form fields array */\n fields: AnyFormField[];\n /** Currently selected field ID (name) */\n selectedFieldId: string | null;\n /** Active tab: builder, preview, settings, or notifications */\n activeTab: \"builder\" | \"preview\" | \"settings\" | \"notifications\";\n /** Whether form has unsaved changes */\n isDirty: boolean;\n /** Form metadata */\n formData: {\n id?: string;\n name: string;\n slug: string;\n description?: string;\n status: \"draft\" | \"published\" | \"closed\";\n /** What a closed form tells a visitor who reaches it. */\n closedMessage?: string;\n };\n /** Form settings */\n settings: FormSettings;\n /** Email notifications */\n notifications: FormNotification[];\n}\n\nexport interface FormBuilderActions {\n /** Set all fields */\n setFields: (fields: AnyFormField[]) => void;\n /** Add a new field */\n addField: (field: AnyFormField, index?: number) => void;\n /** Update a field by name */\n updateField: (fieldName: string, updates: Partial<AnyFormField>) => void;\n /** Delete a field by name */\n deleteField: (fieldName: string) => void;\n /** Move a field from one index to another */\n moveField: (fromIndex: number, toIndex: number) => void;\n /** Duplicate a field */\n duplicateField: (fieldName: string) => void;\n /** Select a field */\n selectField: (fieldName: string | null) => void;\n /** Set active tab */\n setActiveTab: (\n tab: \"builder\" | \"preview\" | \"settings\" | \"notifications\"\n ) => void;\n /** Update form metadata */\n updateFormData: (updates: Partial<FormBuilderState[\"formData\"]>) => void;\n /** Update form settings */\n updateSettings: (updates: Partial<FormSettings>) => void;\n /** Add a notification */\n addNotification: (notification: FormNotification) => void;\n /** Duplicate a notification (the copy starts disabled) */\n duplicateNotification: (id: string) => void;\n /** Update a notification */\n updateNotification: (id: string, updates: Partial<FormNotification>) => void;\n /** Delete a notification */\n deleteNotification: (id: string) => void;\n /** Seed default notifications into an empty list without dirtying the form */\n seedNotifications: (rules: FormNotification[]) => void;\n /** Mark form as saved (clears dirty flag) */\n markAsSaved: () => void;\n}\n\nexport interface FormBuilderContextValue\n extends FormBuilderState,\n FormBuilderActions {}\n\nexport interface FormBuilderProviderProps {\n /** Initial form data (for editing existing forms) */\n initialData?: {\n id?: string;\n name?: string;\n slug?: string;\n description?: string;\n status?: \"draft\" | \"published\" | \"closed\";\n closedMessage?: string;\n fields?: AnyFormField[];\n settings?: Partial<FormSettings>;\n notifications?: FormNotification[];\n };\n /** Child components */\n children: ReactNode;\n /**\n * The document belongs to someone else right now, so nothing here may change\n * it.\n *\n * Held here rather than at each control because the builder has dozens of\n * them across four tabs, and a rule spelled at every one is a rule one new\n * control forgets. Reading is untouched: the editor still browses fields,\n * switches tabs and opens settings, which is exactly what the strip above\n * offers them.\n */\n readOnly?: boolean;\n}\n\n/**\n * The actions that survive a read-only document.\n *\n * Everything else on the context changes the form, and every one of those marks\n * it dirty - the same question asked twice, so this is the complement of it\n * rather than a second list of what to block. `markAsSaved` is the save's own\n * bookkeeping and cannot run while the save is withheld anyway.\n */\nconst READS_ONLY = new Set([\"selectField\", \"setActiveTab\", \"markAsSaved\"]);\n\n/** Default form settings (the canonical defaults from the settings reader) */\nexport const DEFAULT_SETTINGS: FormSettings = DEFAULT_FORM_SETTINGS;\n\n/** Create a new notification rule with default values */\nexport function createNotification(): FormNotification {\n const id = `notif_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 6)}`;\n return {\n id,\n name: \"New notification\",\n enabled: true,\n recipientType: \"static\",\n to: \"\",\n cc: [],\n bcc: [],\n };\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst FormBuilderContext = createContext<FormBuilderContextValue | null>(null);\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Generate a readable unique field name: the type itself for the first\n * field of that type, then a numeric suffix (`email`, `email_2`, ...).\n * Names key submission data, so they should read like keys a developer\n * would have chosen, not timestamps.\n */\nexport function generateFieldName(\n type: FormFieldTypeId,\n existingNames: readonly string[] = []\n): string {\n // A plugin type id can be hyphenated (`star-rating`) or start with a digit,\n // but a field name must match ^[a-zA-Z][a-zA-Z0-9_]*$ (it keys submission\n // data). Normalize to a safe key; built-in type ids are already valid and\n // pass through unchanged.\n const base =\n type\n .replace(/[^a-zA-Z0-9]+/g, \"_\")\n .replace(/^_+/, \"\")\n .replace(/^([0-9])/, \"field_$1\")\n .replace(/_+$/, \"\") || \"field\";\n const taken = new Set(existingNames);\n if (!taken.has(base)) return base;\n let suffix = 2;\n while (taken.has(`${base}_${suffix}`)) suffix += 1;\n return `${base}_${suffix}`;\n}\n\n/**\n * Generate a human-readable label from field type, using the shared catalog\n * so a new field's default label matches what the picker called the type. A\n * plugin type not in the built-in catalog falls back to a title-cased id.\n */\nexport function generateFieldLabel(type: FormFieldTypeId): string {\n const entry = FORM_FIELD_TYPE_CATALOG.find(row => row.type === type);\n if (entry) return entry.label;\n const titleCased = type\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n return titleCased || \"New Field\";\n}\n\n/**\n * Create a new field from a field type with default values.\n */\nexport function createFieldFromType(\n type: FormFieldTypeId,\n existingNames: readonly string[] = []\n): AnyFormField {\n const name = generateFieldName(type, existingNames);\n const label = generateFieldLabel(type);\n\n const baseField = {\n name,\n label,\n required: false,\n };\n\n switch (type) {\n case \"text\":\n return { ...baseField, type: \"text\" };\n\n case \"email\":\n return { ...baseField, type: \"email\" };\n\n case \"number\":\n return { ...baseField, type: \"number\" };\n\n case \"phone\":\n return { ...baseField, type: \"phone\" };\n\n case \"url\":\n return { ...baseField, type: \"url\" };\n\n case \"textarea\":\n return { ...baseField, type: \"textarea\", rows: 4 };\n\n case \"select\":\n return {\n ...baseField,\n type: \"select\",\n options: [\n { label: \"Option 1\", value: \"option_1\" },\n { label: \"Option 2\", value: \"option_2\" },\n ],\n };\n\n case \"checkbox\":\n return { ...baseField, type: \"checkbox\" };\n\n case \"radio\":\n return {\n ...baseField,\n type: \"radio\",\n options: [\n { label: \"Option 1\", value: \"option_1\" },\n { label: \"Option 2\", value: \"option_2\" },\n ],\n };\n\n case \"file\":\n return { ...baseField, type: \"file\" };\n\n case \"date\":\n return { ...baseField, type: \"date\" };\n\n case \"time\":\n return { ...baseField, type: \"time\" };\n\n case \"hidden\":\n return { ...baseField, type: \"hidden\" };\n\n default:\n // A plugin-contributed type keeps its own id; the plugin owns the editor\n // component and value semantics, so no built-in defaults are seeded.\n return { ...baseField, type };\n }\n}\n\n// ============================================================================\n// Provider\n// ============================================================================\n\n/**\n * FormBuilderProvider - Provides form builder state to child components\n *\n * Wraps the form builder UI to provide centralized state management\n * for fields, selection, and form metadata.\n *\n * @example\n * ```tsx\n * <FormBuilderProvider initialData={existingForm}>\n * <FormBuilderView />\n * </FormBuilderProvider>\n * ```\n */\nexport function FormBuilderProvider({\n initialData,\n children,\n readOnly = false,\n}: FormBuilderProviderProps) {\n // State\n const [fields, setFieldsState] = useState<AnyFormField[]>(\n initialData?.fields || []\n );\n const [selectedFieldId, setSelectedFieldId] = useState<string | null>(null);\n const [activeTab, setActiveTab] = useState<\n \"builder\" | \"preview\" | \"settings\" | \"notifications\"\n >(\"builder\");\n const [isDirty, setIsDirty] = useState(false);\n const [formData, setFormData] = useState<FormBuilderState[\"formData\"]>({\n id: initialData?.id,\n name: initialData?.name || \"\",\n slug: initialData?.slug || \"\",\n description: initialData?.description,\n status: initialData?.status || \"draft\",\n closedMessage: initialData?.closedMessage,\n });\n // Stored settings may carry legacy keys from earlier builder versions —\n // the normalizer migrates them on read (nothing rewrites until save).\n const [settings, setSettings] = useState<FormSettings>(() =>\n normalizeFormSettings(initialData?.settings)\n );\n const [notifications, setNotifications] = useState<FormNotification[]>(\n initialData?.notifications || []\n );\n\n // Actions\n const setFields = useCallback((newFields: AnyFormField[]) => {\n setFieldsState(newFields);\n setIsDirty(true);\n }, []);\n\n const addField = useCallback((field: AnyFormField, index?: number) => {\n setFieldsState(prev => {\n if (index !== undefined && index >= 0 && index <= prev.length) {\n const newFields = [...prev];\n newFields.splice(index, 0, field);\n return newFields;\n }\n return [...prev, field];\n });\n setSelectedFieldId(field.name);\n setIsDirty(true);\n }, []);\n\n const updateField = useCallback(\n (fieldName: string, updates: Partial<AnyFormField>) => {\n setFieldsState(prev =>\n prev.map(f => (f.name === fieldName ? { ...f, ...updates } : f))\n );\n // If the field name is being changed, also update selectedFieldId\n // This prevents the sidebar from disappearing when renaming a field\n if (updates.name && updates.name !== fieldName) {\n setSelectedFieldId(updates.name);\n }\n setIsDirty(true);\n },\n []\n );\n\n const deleteField = useCallback(\n (fieldName: string) => {\n setFieldsState(prev => prev.filter(f => f.name !== fieldName));\n if (selectedFieldId === fieldName) {\n setSelectedFieldId(null);\n }\n setIsDirty(true);\n },\n [selectedFieldId]\n );\n\n const moveField = useCallback((fromIndex: number, toIndex: number) => {\n setFieldsState(prev => {\n if (\n fromIndex < 0 ||\n fromIndex >= prev.length ||\n toIndex < 0 ||\n toIndex >= prev.length\n ) {\n return prev;\n }\n const newFields = [...prev];\n const [removed] = newFields.splice(fromIndex, 1);\n newFields.splice(toIndex, 0, removed);\n return newFields;\n });\n setIsDirty(true);\n }, []);\n\n const duplicateField = useCallback(\n (fieldName: string) => {\n // Computed from the current fields (not inside the state updater):\n // updaters must stay pure, and the selection below needs the new name\n // deterministically.\n const fieldIndex = fields.findIndex(f => f.name === fieldName);\n if (fieldIndex === -1) return;\n\n const field = fields[fieldIndex];\n const newFieldName = generateFieldName(\n field.type,\n fields.map(f => f.name)\n );\n const newField: AnyFormField = {\n ...field,\n name: newFieldName,\n label: `${field.label} (Copy)`,\n };\n\n const newFields = [...fields];\n newFields.splice(fieldIndex + 1, 0, newField);\n setFieldsState(newFields);\n\n // Select the duplicated field so its card expands\n setSelectedFieldId(newFieldName);\n setIsDirty(true);\n },\n [fields]\n );\n\n const selectField = useCallback((fieldName: string | null) => {\n setSelectedFieldId(fieldName);\n }, []);\n\n const updateFormData = useCallback(\n (updates: Partial<FormBuilderState[\"formData\"]>) => {\n setFormData(prev => ({ ...prev, ...updates }));\n setIsDirty(true);\n },\n []\n );\n\n const updateSettings = useCallback((updates: Partial<FormSettings>) => {\n setSettings(prev => ({ ...prev, ...updates }));\n setIsDirty(true);\n }, []);\n\n const addNotification = useCallback((notification: FormNotification) => {\n setNotifications(prev => [...prev, notification]);\n setIsDirty(true);\n }, []);\n\n const duplicateNotification = useCallback((id: string) => {\n // The non-deterministic id is generated outside the updater (updaters\n // must stay pure and may re-run); the copy itself derives from `prev`\n // so rapid duplications never work from a stale snapshot.\n const copyId = `notif_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 6)}`;\n setNotifications(prev => {\n const index = prev.findIndex(n => n.id === id);\n if (index === -1) return prev;\n const source = prev[index];\n const copy: FormNotification = {\n ...source,\n id: copyId,\n name: `${source.name} (Copy)`,\n // Copies start disabled so duplicating never doubles the emails a\n // live rule sends until the copy is deliberately turned on.\n enabled: false,\n };\n const next = [...prev];\n next.splice(index + 1, 0, copy);\n return next;\n });\n setIsDirty(true);\n }, []);\n\n const seedNotifications = useCallback((rules: FormNotification[]) => {\n // Only fills an empty list, and does not mark the form dirty: the seed\n // is a starting point the user hasn't authored, not an unsaved edit.\n setNotifications(prev => (prev.length > 0 ? prev : rules));\n }, []);\n\n const updateNotification = useCallback(\n (id: string, updates: Partial<FormNotification>) => {\n setNotifications(prev =>\n prev.map(n => (n.id === id ? { ...n, ...updates } : n))\n );\n setIsDirty(true);\n },\n []\n );\n\n const deleteNotification = useCallback((id: string) => {\n setNotifications(prev => prev.filter(n => n.id !== id));\n setIsDirty(true);\n }, []);\n\n const markAsSaved = useCallback(() => {\n setIsDirty(false);\n }, []);\n\n // Memoized context value\n const value = useMemo<FormBuilderContextValue>(\n () => ({\n // State\n fields,\n selectedFieldId,\n activeTab,\n isDirty,\n formData,\n settings,\n notifications,\n // Actions\n setFields,\n addField,\n updateField,\n deleteField,\n moveField,\n duplicateField,\n selectField,\n setActiveTab,\n updateFormData,\n updateSettings,\n addNotification,\n duplicateNotification,\n updateNotification,\n deleteNotification,\n seedNotifications,\n markAsSaved,\n }),\n [\n fields,\n selectedFieldId,\n activeTab,\n isDirty,\n formData,\n settings,\n notifications,\n setFields,\n addField,\n updateField,\n deleteField,\n moveField,\n duplicateField,\n selectField,\n updateFormData,\n updateSettings,\n addNotification,\n duplicateNotification,\n updateNotification,\n deleteNotification,\n seedNotifications,\n markAsSaved,\n ]\n );\n\n /**\n * The same value with every document-changing action withheld.\n *\n * 🔴 Fail-closed, and deliberately so: it withholds every function EXCEPT the\n * ones named, rather than naming the ones to withhold. An action added here\n * later is inert under a colleague's claim without anyone remembering this\n * exists, which is the opposite of how a list of things-to-disable ages.\n *\n * What stays is navigation and the save's own bookkeeping. Neither changes\n * the document, and both are what \"you can read it\" has to mean.\n */\n const guarded = useMemo<FormBuilderContextValue>(() => {\n if (!readOnly) return value;\n return Object.fromEntries(\n Object.entries(value).map(([name, member]) =>\n typeof member === \"function\" && !READS_ONLY.has(name)\n ? [name, () => undefined]\n : [name, member]\n )\n ) as FormBuilderContextValue;\n }, [value, readOnly]);\n\n return (\n <FormBuilderContext.Provider value={guarded}>\n {children}\n </FormBuilderContext.Provider>\n );\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * useFormBuilder - Access form builder context\n *\n * Hook to access form builder state and actions from within the builder UI.\n * Must be used within a FormBuilderProvider.\n *\n * @returns Form builder context value\n * @throws Error if used outside of FormBuilderProvider\n *\n * @example\n * ```tsx\n * function FieldList() {\n * const { fields, selectField, deleteField } = useFormBuilder();\n *\n * return (\n * <ul>\n * {fields.map(field => (\n * <li key={field.name} onClick={() => selectField(field.name)}>\n * {field.label}\n * <button onClick={() => deleteField(field.name)}>Delete</button>\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useFormBuilder(): FormBuilderContextValue {\n const context = useContext(FormBuilderContext);\n\n if (!context) {\n throw new Error(\"useFormBuilder must be used within a FormBuilderProvider\");\n }\n\n return context;\n}\n\n/**\n * useOptionalFormBuilder - Access form builder context safely\n *\n * Like useFormBuilder but returns null if not within a provider.\n * Useful for components that may be rendered outside of the form builder.\n *\n * @returns Form builder context value or null\n */\nexport function useOptionalFormBuilder(): FormBuilderContextValue | null {\n return useContext(FormBuilderContext);\n}\n","\"use client\";\n\n/**\n * Add Field Dialog\n *\n * Type selection for new form fields, rendered from the shared field-type\n * catalog via the SDK's FieldTypePicker so the form surface shows the same\n * labels, icons, and hints as every other field picker in the admin.\n *\n * @module admin/components/builder/AddFieldDialog\n */\n\nimport { FieldTypePicker } from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@nextlyhq/ui\";\nimport type { FieldTypeCatalogEntry } from \"nextly/field-catalog\";\nimport { useState } from \"react\";\n\nimport type { FormFieldTypeId } from \"../../../types\";\n\nexport interface AddFieldDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /**\n * The form surface's catalog — built-ins already filtered by the host's\n * excludes plus any plugin field types that opted into the forms surface;\n * `null` while the host configuration is still loading. `type` is the open\n * `FormFieldTypeId` so a plugin entry shares this shape with the built-ins.\n */\n entries: readonly FieldTypeCatalogEntry<FormFieldTypeId>[] | null;\n /** Called with the chosen type; the parent creates and selects the field. */\n onAdd: (type: FormFieldTypeId) => void;\n}\n\nexport function AddFieldDialog({\n open,\n onOpenChange,\n entries,\n onAdd,\n}: AddFieldDialogProps) {\n const [selected, setSelected] = useState<FormFieldTypeId>(\"text\");\n\n const selectable = entries?.some(entry => entry.type === selected)\n ? selected\n : entries?.[0]?.type;\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle>Add field</DialogTitle>\n <DialogDescription>\n Pick the field type. Everything else is edited on the field card.\n </DialogDescription>\n </DialogHeader>\n\n {entries === null ? (\n <p className=\"text-sm text-muted-foreground\" role=\"status\">\n Loading the available field types…\n </p>\n ) : entries.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n All field types are disabled by this site&apos;s form-builder\n configuration.\n </p>\n ) : (\n <FieldTypePicker\n entries={entries}\n value={selectable ?? \"text\"}\n onChange={setSelected}\n columns={3}\n ariaLabel=\"Field type\"\n />\n )}\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n disabled={!selectable}\n onClick={() => {\n if (selectable) onAdd(selectable);\n onOpenChange(false);\n }}\n >\n Add field\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","\"use client\";\n\n/**\n * Field Editor\n *\n * Properties panel for editing the selected field's configuration.\n * Provides tabbed interface for General, Validation, and Conditional settings.\n *\n * @module admin/components/builder/FieldEditor\n * @since 0.1.0\n */\n\n\"use client\";\nimport {\n drawsAnyValidationRule,\n FieldOptionsEditor,\n ValidationRulesEditor,\n withOptionIds,\n type FieldOptionsEditorProps,\n type ValidationRuleValues,\n} from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n FormLabelWithTooltip,\n Input,\n Checkbox,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@nextlyhq/ui\";\nimport { validationRulesForFieldType } from \"nextly/field-catalog\";\nimport { useState, useCallback, useMemo } from \"react\";\n\nimport type {\n AnyFieldValidation,\n AnyFormField,\n FormField,\n SelectFormField,\n RadioFormField,\n} from \"../../../types\";\nimport { isKnownFormField } from \"../../../types\";\n// From the metadata module rather than from `generate-schema`, which\n// implements it: the editor needs only to know WHICH rules bite for a\n// type, and importing that from the module that builds schemas pulls the\n// whole schema implementation and `zod` into every admin client bundle\n// before anyone opens a field.\nimport { enforcedValidationRules } from \"../../../utils/enforced-validation\";\n\nimport { ConditionalLogicEditor } from \"./ConditionalLogicEditor\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface FieldEditorProps {\n /** The field being edited (built-in or plugin-contributed). */\n field: AnyFormField;\n /** All fields in the form (for conditional logic references) */\n allFields: AnyFormField[];\n /** Callback when field is updated */\n onUpdate: (updates: Partial<AnyFormField>) => void;\n}\n\n// ============================================================================\n// Helper Components\n// ============================================================================\n\n/**\n * General properties tab - common field settings\n */\nfunction GeneralTab({\n field,\n allFields,\n onUpdate,\n}: {\n field: AnyFormField;\n allFields: AnyFormField[];\n onUpdate: (updates: Partial<AnyFormField>) => void;\n}) {\n // Check if other fields have conditional logic referencing this field\n const hasConditionalReferences = useMemo(() => {\n return allFields.some(f => {\n if (f.name === field.name || !f.conditionalLogic?.enabled) return false;\n return f.conditionalLogic.conditions.some(c => c.field === field.name);\n });\n }, [allFields, field.name]);\n\n return (\n <div className=\"space-y-6 py-4\">\n {/* Field Name */}\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Field Name (ID)\"\n htmlFor=\"field-name\"\n description=\"Used as the key in submission data. Should be unique and contain no spaces.\"\n />\n <Input\n id=\"field-name\"\n type=\"text\"\n value={field.name}\n onChange={e => onUpdate({ name: e.target.value })}\n pattern=\"^[a-zA-Z][a-zA-Z0-9_]*$\"\n className=\"bg-transparent\"\n />\n {hasConditionalReferences && (\n <p className=\"text-[11px] text-warning font-medium\">\n ⚠️ Changing this may break conditional logic referencing this field\n </p>\n )}\n </div>\n\n {/* Label - not shown for hidden fields */}\n {field.type !== \"hidden\" && (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Label\"\n htmlFor=\"field-label\"\n description=\"The text displayed above the field in the form.\"\n />\n <Input\n id=\"field-label\"\n type=\"text\"\n value={field.label}\n onChange={e => onUpdate({ label: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n )}\n\n {/* Placeholder - for text-like fields */}\n {(field.type === \"text\" ||\n field.type === \"email\" ||\n field.type === \"phone\" ||\n field.type === \"url\" ||\n field.type === \"number\" ||\n field.type === \"textarea\") && (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Placeholder\"\n htmlFor=\"field-placeholder\"\n description=\"The faint text shown inside the field when empty.\"\n />\n <Input\n id=\"field-placeholder\"\n type=\"text\"\n value={field.placeholder || \"\"}\n onChange={e => onUpdate({ placeholder: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n )}\n\n {/* Help Text - not shown for hidden fields */}\n {field.type !== \"hidden\" && (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Help Text\"\n htmlFor=\"field-help\"\n description=\"Additional instructions displayed below the field.\"\n />\n <Input\n id=\"field-help\"\n type=\"text\"\n value={field.helpText || \"\"}\n onChange={e => onUpdate({ helpText: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n )}\n\n {/* Required - not shown for hidden or checkbox fields */}\n {field.type !== \"hidden\" && field.type !== \"checkbox\" && (\n <div className=\"flex items-center gap-3 pt-1\">\n <Checkbox\n id=\"field-required\"\n checked={field.required || false}\n onCheckedChange={checked =>\n onUpdate({ required: checked === true })\n }\n />\n <FormLabelWithTooltip\n label=\"Required field\"\n htmlFor=\"field-required\"\n description=\"Forces the user to fill this field before submitting.\"\n />\n </div>\n )}\n\n {/* Width */}\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Width\"\n htmlFor=\"field-width\"\n description=\"How much space this field takes in a horizontal row.\"\n />\n <Select\n value={field.admin?.width || \"100%\"}\n onValueChange={value =>\n onUpdate({\n admin: {\n ...field.admin,\n width: value as \"25%\" | \"33%\" | \"50%\" | \"66%\" | \"75%\" | \"100%\",\n },\n })\n }\n >\n <SelectTrigger className=\"w-full bg-transparent border-input dark:bg-muted/50\">\n <SelectValue placeholder=\"Select width\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"100%\">Full Width (100%)</SelectItem>\n <SelectItem value=\"75%\">Three Quarters (75%)</SelectItem>\n <SelectItem value=\"66%\">Two Thirds (66%)</SelectItem>\n <SelectItem value=\"50%\">Half (50%)</SelectItem>\n <SelectItem value=\"33%\">One Third (33%)</SelectItem>\n <SelectItem value=\"25%\">One Quarter (25%)</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n {/* Type-specific options — built-ins only; a plugin field owns its own\n editor component and carries no built-in type-specific settings. */}\n {isKnownFormField(field) && (\n <TypeSpecificOptions field={field} onUpdate={onUpdate} />\n )}\n </div>\n );\n}\n\n/**\n * Type-specific field options\n */\nfunction TypeSpecificOptions({\n field,\n onUpdate,\n}: {\n field: FormField;\n onUpdate: (updates: Partial<FormField>) => void;\n}) {\n switch (field.type) {\n case \"select\":\n case \"radio\":\n return <OptionsEditor field={field} onUpdate={onUpdate} />;\n\n case \"textarea\": {\n const textareaField = field;\n return (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Rows\"\n htmlFor=\"field-rows\"\n description=\"Height of the textarea in text lines.\"\n />\n <Input\n id=\"field-rows\"\n type=\"number\"\n min={2}\n max={20}\n value={textareaField.rows || 4}\n onChange={e => onUpdate({ rows: parseInt(e.target.value) || 4 })}\n className=\"w-24 bg-transparent\"\n />\n </div>\n );\n }\n\n case \"file\": {\n const fileField = field;\n return (\n <>\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Accepted File Types\"\n htmlFor=\"field-accept\"\n description=\"Comma-separated MIME types (e.g., image/*, application/pdf).\"\n />\n <Input\n id=\"field-accept\"\n type=\"text\"\n value={fileField.accept || \"\"}\n onChange={e => onUpdate({ accept: e.target.value })}\n placeholder=\"image/*,application/pdf\"\n className=\"bg-transparent\"\n />\n </div>\n <div className=\"flex items-center gap-3 pt-1\">\n <Checkbox\n id=\"field-multiple\"\n checked={fileField.multiple || false}\n onCheckedChange={checked =>\n onUpdate({ multiple: checked === true })\n }\n />\n <FormLabelWithTooltip\n label=\"Allow multiple files\"\n htmlFor=\"field-multiple\"\n description=\"Users can upload more than one file at once.\"\n />\n </div>\n <div className=\"flex items-center gap-3 pt-1\">\n <Checkbox\n id=\"field-attach-to-email\"\n checked={fileField.attachToEmail || false}\n onCheckedChange={checked =>\n onUpdate({ attachToEmail: checked === true })\n }\n />\n <FormLabelWithTooltip\n label=\"Attach to notification emails\"\n htmlFor=\"field-attach-to-email\"\n description=\"Uploaded files will be attached to all notification emails for this form.\"\n />\n </div>\n </>\n );\n }\n\n case \"hidden\": {\n const hiddenField = field;\n return (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Default Value\"\n htmlFor=\"field-default\"\n description=\"This value will be submitted silently with the form.\"\n />\n <Input\n id=\"field-default\"\n type=\"text\"\n value={hiddenField.defaultValue || \"\"}\n onChange={e => onUpdate({ defaultValue: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n );\n }\n\n case \"date\": {\n const dateField = field;\n return (\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Minimum Date\"\n htmlFor=\"min-date\"\n description=\"Lower bound for date selection.\"\n />\n <Input\n id=\"min-date\"\n type=\"date\"\n value={dateField.min || \"\"}\n onChange={e => onUpdate({ min: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Maximum Date\"\n htmlFor=\"max-date\"\n description=\"Upper bound for date selection.\"\n />\n <Input\n id=\"max-date\"\n type=\"date\"\n value={dateField.max || \"\"}\n onChange={e => onUpdate({ max: e.target.value })}\n className=\"bg-transparent\"\n />\n </div>\n </div>\n );\n }\n\n case \"number\": {\n const numField = field;\n return (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Step\"\n htmlFor=\"field-step\"\n description=\"Increment/decrement step (e.g., 0.01 for decimals).\"\n />\n <Input\n id=\"field-step\"\n type=\"number\"\n value={numField.validation?.step || \"\"}\n onChange={e =>\n onUpdate({\n validation: {\n ...numField.validation,\n step: parseFloat(e.target.value) || undefined,\n },\n })\n }\n placeholder=\"1\"\n className=\"w-24 bg-transparent\"\n />\n </div>\n );\n }\n\n default:\n return null;\n }\n}\n\ntype KitOption = FieldOptionsEditorProps[\"options\"][number];\n\n/**\n * Options editor for select/radio fields, delegating to the SDK's\n * FieldOptionsEditor (drag reorder, auto-generated values, CSV/JSON import,\n * duplicate-value reporting). The kit works on id-carrying rows for\n * drag-and-drop; the form stores plain {label, value} pairs, so ids live in\n * local state and are stripped before every write.\n */\nfunction OptionsEditor({\n field,\n onUpdate,\n}: {\n field: FormField;\n onUpdate: (updates: Partial<FormField>) => void;\n}) {\n const optionsField = field as SelectFormField | RadioFormField;\n\n const [kitOptions, setKitOptions] = useState<KitOption[]>(() =>\n withOptionIds(optionsField.options || [])\n );\n\n const handleOptionsChange = useCallback(\n (next: KitOption[]) => {\n setKitOptions(next);\n onUpdate({\n options: next.map(({ label, value }) => ({ label, value })),\n });\n },\n [onUpdate]\n );\n\n return (\n <div className=\"space-y-3 pt-6\">\n <FormLabelWithTooltip\n label=\"Options\"\n description=\"List of choices available for this field.\"\n />\n <FieldOptionsEditor\n options={kitOptions}\n onOptionsChange={handleOptionsChange}\n />\n </div>\n );\n}\n\n/**\n * Validation tab - field validation settings.\n *\n * Which rules a type accepts is ASKED of core rather than decided here. This\n * previously branched on `field.type === \"text\" || field.type === \"textarea\"`\n * and similar, which is a list that cannot see a type it was not written to\n * know about — a plugin-contributed field type was offered nothing at all, and\n * `url` and `phone` were handled by yet another list further down.\n *\n * The controls themselves are the shared kit's, so this surface and the schema\n * builder no longer drift on labels, help text or which rules exist. What stays\n * here is what is genuinely this surface's: its own tab chrome, its file-size\n * control (stored on the field rather than in `validation`), and the mapping of\n * its own storage key — this builder stores the custom message as\n * `errorMessage` where core names the rule `message`, and a shared component\n * that silently reconciled the two would make every stored value wrong.\n */\nfunction ValidationTab({\n field,\n onUpdate,\n}: {\n field: FormField;\n onUpdate: (updates: Partial<FormField>) => void;\n}) {\n // Read across field types rather than narrowing to one: the whole point of\n // the shared editor is that this tab no longer branches on `field.type`.\n const validation: AnyFieldValidation = useMemo(\n () => field.validation ?? {},\n [field.validation]\n );\n // What the type MEANS, narrowed to what this runtime enforces FOR THAT TYPE.\n // Both halves are asked rather than restated, so neither can drift behind the\n // other. The narrowing has to be per type: a single set of enforced rules\n // only says a rule is honoured somewhere, which would offer `min`/`max` on a\n // date whose schema reads its bounds from `field.min`/`field.max` and never\n // consults `validation` — a control storing a bound nothing reads, which is\n // worse than no control because the author believes the rule is in force.\n const allowed = useMemo(() => {\n const enforced = enforcedValidationRules(field.type);\n return validationRulesForFieldType(field.type).filter(rule =>\n enforced.includes(rule)\n );\n }, [field.type]);\n\n const applyRules = useCallback(\n (next: Partial<ValidationRuleValues>) => {\n const { message, ...rules } = next;\n onUpdate({\n validation: {\n ...validation,\n ...rules,\n // This surface's own key for the rule core calls `message`.\n ...(message === undefined ? {} : { errorMessage: message }),\n },\n });\n },\n [validation, onUpdate]\n );\n\n return (\n <div className=\"space-y-6 pt-2\">\n <ValidationRulesEditor\n allowed={allowed}\n value={{\n minLength: validation.minLength,\n maxLength: validation.maxLength,\n // No row bounds here: this surface neither stores nor enforces them,\n // and `allowed` above never admits them, so passing them would be a\n // value the editor could not draw and the runtime could not read.\n min: validation.min,\n max: validation.max,\n pattern: validation.pattern,\n message: validation.errorMessage,\n }}\n onChange={applyRules}\n />\n\n {/* Not a validation rule: a file's size limit is stored on the field\n itself rather than in `validation`, so it stays with this surface. */}\n {field.type === \"file\" && (\n <div className=\"space-y-2\">\n <FormLabelWithTooltip\n label=\"Max File Size (bytes)\"\n htmlFor=\"max-file-size\"\n description=\"Maximum allowed size in bytes (10MB ≈ 10485760).\"\n />\n <Input\n id=\"max-file-size\"\n type=\"number\"\n min={0}\n value={field.maxFileSize ?? \"\"}\n onChange={e =>\n onUpdate({\n maxFileSize: e.target.value\n ? parseInt(e.target.value)\n : undefined,\n })\n }\n placeholder=\"10485760\"\n className=\"bg-transparent\"\n />\n </div>\n )}\n\n {/* Asked, not restated: naming the types with no options here would be a\n third copy of the reasoning this tab just removed. */}\n {!drawsAnyValidationRule(allowed) && field.type !== \"file\" && (\n // Semantic border token so this info-box boundary is visible at the 3:1 UI minimum.\n <div className=\"p-3 bg-muted rounded-none text-xs text-muted-foreground text-center border border-dashed border-border\">\n No additional validation options for this field type.\n </div>\n )}\n </div>\n );\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * FieldEditor - Field properties configuration panel\n *\n * Provides a tabbed interface for editing field properties:\n * - General: name, label, placeholder, help text, required, width, type-specific options\n * - Validation: error messages, min/max length, patterns, etc.\n * - Conditional: show/hide logic based on other field values\n *\n * @example\n * ```tsx\n * <FieldEditor\n * field={selectedField}\n * allFields={allFields}\n * onUpdate={handleUpdate}\n * onDelete={handleDelete}\n * onDuplicate={handleDuplicate}\n * />\n * ```\n */\nexport function FieldEditor({ field, allFields, onUpdate }: FieldEditorProps) {\n const [activeTab, setActiveTab] = useState<string>(\"general\");\n\n return (\n <div className=\"flex flex-col\">\n {/* Tabs */}\n <Tabs\n value={activeTab}\n onValueChange={setActiveTab}\n className=\"flex-1 flex flex-col\"\n >\n {/* Semantic border token so the tab-bar bottom edge is visible at the 3:1 UI minimum. */}\n <div className=\"border-b bg-muted border-border\">\n {/* Layout only. The underline, its colours and the square corner come\n from the shared primitive; `w-full` spreads the triggers evenly and\n the active tint is this panel's own surface treatment. */}\n <TabsList className=\"w-full justify-start gap-0\">\n <TabsTrigger\n value=\"general\"\n className=\"w-full data-[state=active]:bg-background/50\"\n >\n General\n </TabsTrigger>\n <TabsTrigger\n value=\"validation\"\n className=\"w-full data-[state=active]:bg-background/50\"\n >\n Validation\n </TabsTrigger>\n <TabsTrigger\n value=\"conditional\"\n className=\"w-full data-[state=active]:bg-background/50\"\n >\n Conditional\n </TabsTrigger>\n </TabsList>\n </div>\n\n {/* Content */}\n <div className=\"p-4\">\n <TabsContent value=\"general\" className=\"mt-0\">\n <GeneralTab\n field={field}\n allFields={allFields}\n onUpdate={onUpdate}\n />\n </TabsContent>\n\n <TabsContent value=\"validation\" className=\"mt-0\">\n {isKnownFormField(field) ? (\n <ValidationTab field={field} onUpdate={onUpdate} />\n ) : (\n <p className=\"py-4 text-sm text-muted-foreground\">\n This field type manages its own validation.\n </p>\n )}\n </TabsContent>\n\n <TabsContent value=\"conditional\" className=\"mt-0\">\n <ConditionalLogicEditor\n field={field}\n allFields={allFields}\n onUpdate={onUpdate}\n />\n </TabsContent>\n </div>\n </Tabs>\n </div>\n );\n}\n\nexport default FieldEditor;\n","\"use client\";\n\n/**\n * Conditional Logic Editor\n *\n * Component for configuring conditional show/hide logic for form fields.\n * Allows users to define conditions based on other field values.\n *\n * @module admin/components/builder/ConditionalLogicEditor\n * @since 0.1.0\n */\n\n\"use client\";\n\nimport {\n ConditionRow,\n type ConditionOperatorName,\n type ConditionSource,\n} from \"@nextlyhq/plugin-sdk/admin\";\nimport {\n FormLabelWithTooltip,\n Button,\n Checkbox,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@nextlyhq/ui\";\nimport { useCallback, useMemo } from \"react\";\n\nimport type {\n AnyFormField,\n ConditionalLogic,\n ConditionalLogicCondition,\n} from \"../../../types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ConditionalLogicEditorProps {\n /** The field being edited (built-in or plugin-contributed). */\n field: AnyFormField;\n /** All fields in the form (for field references) */\n allFields: AnyFormField[];\n /** Callback when conditional logic is updated */\n onUpdate: (updates: Partial<AnyFormField>) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * The comparisons `evaluateConditions` implements.\n *\n * The shared row offers seventeen operators, and this runtime understands\n * seven of them. Every operator offered has to be one this evaluator can\n * answer: a field configured with `isTrue` would store fine, render fine, and\n * then never match at form time, with nothing anywhere to say why. So the\n * vocabulary is pinned here rather than taken from the row's default.\n *\n * Typed as the shared row's operator names AND as this module's stored\n * comparisons, so the day either list moves the other stops compiling.\n */\nconst SUPPORTED_COMPARISONS = [\n \"equals\",\n \"notEquals\",\n \"contains\",\n \"isEmpty\",\n \"isNotEmpty\",\n \"greaterThan\",\n \"lessThan\",\n] as const satisfies readonly ConditionOperatorName[] &\n readonly ConditionalLogicCondition[\"comparison\"][];\n\nconst SUPPORTED_SET: ReadonlySet<string> = new Set(SUPPORTED_COMPARISONS);\n\n/**\n * Whether an operator the row reported is one this runtime can evaluate.\n *\n * `operatorsFor` already narrows what the row offers, so this should always\n * hold; it is a guard rather than an assertion so that if the two ever drift,\n * the condition is left as it was instead of stored in a shape the evaluator\n * will not understand.\n */\nfunction isSupportedComparison(\n operator: string\n): operator is ConditionalLogicCondition[\"comparison\"] {\n return SUPPORTED_SET.has(operator);\n}\n\n// The field types whose values are text, named as `FormFieldType` spells them.\n// `tel` and `password` were guesses and match nothing; the builder's telephone\n// field is `phone`, so it was falling through to the full comparison set and\n// offering \"greater than\" for a phone number.\nconst TEXT_TYPES = new Set([\n \"text\",\n \"textarea\",\n \"email\",\n \"url\",\n \"phone\",\n \"hidden\",\n]);\n\n/**\n * The comparisons worth offering for a source type, within what the evaluator\n * supports.\n *\n * Narrower than the full seven where the type makes some of them meaningless:\n * \"contains\" on a number compares substrings of a formatted value, which is\n * not what anyone building that condition means.\n */\nfunction comparisonsForType(\n sourceType: string | undefined\n): ConditionOperatorName[] {\n if (sourceType === \"checkbox\") return [\"equals\", \"notEquals\"];\n if (sourceType === \"number\") {\n return [\"equals\", \"notEquals\", \"greaterThan\", \"lessThan\"];\n }\n if (sourceType !== undefined && TEXT_TYPES.has(sourceType)) {\n return [\"equals\", \"notEquals\", \"contains\", \"isEmpty\", \"isNotEmpty\"];\n }\n return [...SUPPORTED_COMPARISONS];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * A form field as the shared row needs to see it.\n *\n * Choice fields hand over their options so the value editor becomes a dropdown\n * of exactly those, rather than a text box in which an option can be misspelled\n * into a condition that never matches.\n */\nfunction toSource(field: AnyFormField): ConditionSource {\n // Only the choice field types carry an option list, and `AnyFormField` is a\n // union over every type, so the property has to be probed rather than read.\n // An option with an empty value is dropped: a Select cannot render one, and\n // it is a placeholder row in the editor rather than a value to compare with.\n const options =\n \"options\" in field && Array.isArray(field.options)\n ? field.options\n .filter(option => option.value !== \"\")\n .map(option => ({ value: option.value, label: option.label }))\n : undefined;\n return {\n name: field.name,\n label: field.label || field.name,\n type: field.type,\n options,\n };\n}\n\n/**\n * A stored condition's value as the row edits it.\n *\n * Storage types this `unknown`, and the row edits scalars. An object that got\n * in some other way is shown as nothing rather than as `[object Object]`,\n * which is not a value anyone typed and not one the evaluator can use.\n */\nfunction toRowValue(value: unknown): string | number | boolean | undefined {\n if (value === null || value === undefined) return undefined;\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n return undefined;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * ConditionalLogicEditor - Configure conditional show/hide logic\n *\n * Allows users to define when a field should be shown or hidden\n * based on the values of other fields in the form.\n *\n * @example\n * ```tsx\n * <ConditionalLogicEditor\n * field={selectedField}\n * allFields={allFields}\n * onUpdate={handleUpdate}\n * />\n * ```\n */\nexport function ConditionalLogicEditor({\n field,\n allFields,\n onUpdate,\n}: ConditionalLogicEditorProps) {\n // Get current conditional logic or create default.\n // Wrapped in useMemo so dependent useCallbacks don't re-create on every render.\n const logic: ConditionalLogic = useMemo(\n () =>\n field.conditionalLogic || {\n enabled: false,\n action: \"show\",\n operator: \"AND\",\n conditions: [],\n },\n [field.conditionalLogic]\n );\n\n // Get other fields that can be referenced (exclude current field)\n const availableFields = allFields.filter(f => f.name !== field.name);\n\n // Update the entire conditional logic object\n const updateLogic = useCallback(\n (updates: Partial<ConditionalLogic>) => {\n onUpdate({\n conditionalLogic: { ...logic, ...updates },\n });\n },\n [logic, onUpdate]\n );\n\n // Toggle enabled state\n const handleToggleEnabled = useCallback(() => {\n updateLogic({ enabled: !logic.enabled });\n }, [logic.enabled, updateLogic]);\n\n // Add a new condition\n const handleAddCondition = useCallback(() => {\n const firstField = availableFields[0];\n if (!firstField) return;\n\n const newCondition: ConditionalLogicCondition = {\n field: firstField.name,\n comparison: \"equals\",\n value: \"\",\n };\n\n updateLogic({\n conditions: [...logic.conditions, newCondition],\n });\n }, [availableFields, logic.conditions, updateLogic]);\n\n // Update a specific condition\n const handleUpdateCondition = useCallback(\n (index: number, updates: Partial<ConditionalLogicCondition>) => {\n const newConditions = logic.conditions.map((c, i) =>\n i === index ? { ...c, ...updates } : c\n );\n updateLogic({ conditions: newConditions });\n },\n [logic.conditions, updateLogic]\n );\n\n // Remove a condition\n const handleRemoveCondition = useCallback(\n (index: number) => {\n const newConditions = logic.conditions.filter((_, i) => i !== index);\n updateLogic({ conditions: newConditions });\n },\n [logic.conditions, updateLogic]\n );\n\n return (\n <div className=\"space-y-6 pt-2\">\n {/* Enable toggle */}\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3\">\n <Checkbox\n id=\"logic-enabled\"\n checked={logic.enabled}\n onCheckedChange={handleToggleEnabled}\n />\n <FormLabelWithTooltip\n label=\"Enable conditional logic\"\n htmlFor=\"logic-enabled\"\n description=\"Show or hide this field based on other field values.\"\n />\n </div>\n </div>\n\n {logic.enabled && (\n // Semantic border token so this top divider is visible at the 3:1 UI minimum.\n <div className=\"space-y-4 pt-4 border-t border-border\">\n {/* Action and operator row */}\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\">\n <Select\n value={logic.action}\n onValueChange={value =>\n updateLogic({ action: value as \"show\" | \"hide\" })\n }\n >\n <SelectTrigger className=\"w-[100px] h-9 bg-transparent border-input dark:bg-muted/50\">\n <SelectValue placeholder=\"Show/Hide\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"show\">Show</SelectItem>\n <SelectItem value=\"hide\">Hide</SelectItem>\n </SelectContent>\n </Select>\n <span>this field when</span>\n <Select\n value={logic.operator}\n onValueChange={value =>\n updateLogic({ operator: value as \"AND\" | \"OR\" })\n }\n >\n <SelectTrigger className=\"w-[80px] h-9 bg-transparent border-input dark:bg-muted/50\">\n <SelectValue placeholder=\"All/Any\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"AND\">All</SelectItem>\n <SelectItem value=\"OR\">Any</SelectItem>\n </SelectContent>\n </Select>\n <span>of the following conditions are met:</span>\n </div>\n\n {/* Conditions list */}\n <div className=\"space-y-3 pt-4\">\n {logic.conditions.length === 0 ? (\n // Semantic border token so this empty-state boundary is visible at the 3:1 UI minimum.\n <div className=\"p-4 bg-muted rounded-none border border-dashed border-border text-center text-xs text-muted-foreground\">\n No conditions defined. Add a condition to get started.\n </div>\n ) : (\n logic.conditions.map((condition, index) => (\n // Semantic border token so this condition card boundary is visible at the 3:1 UI minimum.\n <div\n key={index}\n className=\"flex flex-col gap-2 p-3 rounded-none bg-muted border border-border relative group\"\n >\n {/* Source, comparison and value, shared with the schema\n builder. Stacked rather than in three columns: this card\n sits in a builder sidebar, where three columns leave each\n control too narrow to read its own selection. */}\n <ConditionRow\n className=\"grid grid-cols-1 gap-2\"\n condition={{\n field: condition.field,\n operator: condition.comparison,\n value: toRowValue(condition.value),\n }}\n sources={availableFields.map(toSource)}\n operatorsFor={comparisonsForType}\n onChange={next => {\n if (!next || !isSupportedComparison(next.operator))\n return;\n handleUpdateCondition(index, {\n field: next.field,\n comparison: next.operator,\n value: toRowValue(next.value),\n });\n }}\n />\n\n {/* Remove button */}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => handleRemoveCondition(index)}\n // Semantic border token so the remove button's boundary is visible at the 3:1 UI minimum.\n className=\"absolute -top-2 -right-2 h-6 w-6 rounded-full bg-background border border-border opacity-0 group-hover:opacity-100 transition-opacity\"\n title=\"Remove condition\"\n >\n <span className=\"text-xs\">×</span>\n </Button>\n </div>\n ))\n )}\n </div>\n\n {/* Add condition button */}\n {availableFields.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={handleAddCondition}\n className=\"w-full mt-4 border-dashed\"\n >\n + Add Condition\n </Button>\n ) : (\n // Full-strength warning border so this notice boundary is perceivable over its tinted fill.\n // The 600 shade rather than the base token: the base measures 4.37:1\n // once its own 10% fill composites over the page container, short of\n // the 4.5:1 text needs. The 600 shade holds 5.13:1 at its worst\n // surface in either mode.\n <p className=\"mt-4 text-center text-xs text-warning-600 font-medium bg-warning/10 p-2 rounded-none border border-warning\">\n Add more fields to the form to create conditions.\n </p>\n )}\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * Form Notifications Tab\n *\n * Per-form email notification rules: a list of cards, each editing in a\n * side sheet. A rule targets a static address or the visitor (via one of\n * the form's email fields), can set Reply-To from a field so replies reach\n * the visitor, and can carry a single send-condition evaluated against the\n * submitted data. An autoresponder is just a rule whose recipient is the\n * visitor — there is no separate feature.\n *\n * @module admin/components/builder/FormNotificationsTab\n */\n\nimport {\n Badge,\n Card,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n FieldShell,\n Grid,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n} from \"@nextlyhq/ui\";\nimport {\n Copy,\n Filter,\n Mail,\n MoreHorizontal,\n Pencil,\n Plus,\n Trash2,\n TriangleAlert,\n X,\n} from \"lucide-react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\n\nimport type { ConditionalLogicCondition } from \"../../../types\";\nimport {\n useFormBuilder,\n createNotification,\n type FormNotification,\n} from \"../../context/FormBuilderContext\";\n\nimport {\n addressError,\n addressErrorsIn,\n isValidEmail,\n parseFieldRef,\n type AddressErrors,\n type AddressField,\n} from \"./notification-addresses\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Host-level notification defaults surfaced by the builder-config route. */\nexport interface NotificationDefaults {\n defaultFrom?: string;\n defaultToEmail?: string;\n}\n\ninterface FormNotificationsTabProps {\n /** `null` while the builder-config request is still settling. */\n defaults: NotificationDefaults | null;\n}\n\ninterface ProviderOption {\n id: string;\n name: string;\n type: string;\n isDefault: boolean;\n}\n\ninterface TemplateOption {\n id: string;\n name: string;\n slug: string;\n isActive: boolean;\n /** \"template\" or \"layout\" — layouts wrap templates and cannot be sent. */\n kind?: string;\n}\n\ninterface FieldOption {\n name: string;\n label: string;\n type: string;\n}\n\n// ============================================================================\n// API helpers\n// ============================================================================\n\nasync function fetchProviders(): Promise<ProviderOption[]> {\n try {\n const res = await fetch(\"/admin/api/email-providers?pageSize=100&page=1\", {\n credentials: \"include\",\n });\n if (!res.ok) return [];\n const json = (await res.json()) as { providers?: ProviderOption[] };\n return json.providers ?? [];\n } catch {\n return [];\n }\n}\n\nasync function fetchTemplates(): Promise<TemplateOption[]> {\n try {\n const res = await fetch(\"/admin/api/email-templates\", {\n credentials: \"include\",\n });\n if (!res.ok) return [];\n const json = (await res.json()) as { templates?: TemplateOption[] };\n return json.templates ?? [];\n } catch {\n return [];\n }\n}\n\n// ============================================================================\n// Field-reference helpers\n// ============================================================================\n\nfunction toFieldRef(fieldName: string): string {\n return `{{${fieldName}}}`;\n}\n\n/**\n * Options for address dropdowns: the form's email fields, plus the\n * currently-referenced field even when it is not an email type, so a legacy\n * value keeps displaying instead of silently vanishing from the select.\n */\nfunction buildEmailFieldOptions(\n fields: readonly FieldOption[],\n currentRef: string | null\n): FieldOption[] {\n const emailFields = fields.filter(field => field.type === \"email\");\n if (currentRef && !emailFields.some(field => field.name === currentRef)) {\n const current = fields.find(field => field.name === currentRef);\n if (current) return [...emailFields, current];\n }\n return emailFields;\n}\n\n// ============================================================================\n// Condition labels\n// ============================================================================\n\nconst COMPARISON_LABELS: Record<\n ConditionalLogicCondition[\"comparison\"],\n string\n> = {\n equals: \"Equals\",\n notEquals: \"Does not equal\",\n contains: \"Contains\",\n isEmpty: \"Is empty\",\n isNotEmpty: \"Is not empty\",\n greaterThan: \"Greater than\",\n lessThan: \"Less than\",\n};\n\nconst VALUELESS_COMPARISONS: ReadonlySet<\n ConditionalLogicCondition[\"comparison\"]\n> = new Set([\"isEmpty\", \"isNotEmpty\"]);\n\n// ============================================================================\n// Notification Card\n// ============================================================================\n\ninterface NotificationCardProps {\n notification: FormNotification;\n providerName: string;\n fields: readonly FieldOption[];\n /** Whether this row's editor is showing. */\n expanded: boolean;\n /** The editor region's id, so the summary can point at what it opens. */\n editorId: string;\n onToggle: () => void;\n onDuplicate: () => void;\n onDelete: () => void;\n onToggleEnabled: (enabled: boolean) => void;\n /** The editor, rendered under the summary while `expanded`. */\n children: React.ReactNode;\n}\n\nfunction describeRecipient(\n notification: FormNotification,\n fields: readonly FieldOption[]\n): { text: string; missing: boolean } {\n if (notification.recipientType === \"field\") {\n const ref = parseFieldRef(notification.to);\n if (!ref) return { text: \"No recipient field selected\", missing: true };\n const field = fields.find(f => f.name === ref);\n if (!field) {\n // The referenced field was deleted (allowed while the rule was\n // disabled) — say so instead of presenting the dead name as valid.\n return {\n text: `Recipient field \"${ref}\" no longer exists`,\n missing: true,\n };\n }\n return {\n text: `To the visitor (${field.label})`,\n missing: false,\n };\n }\n if (!notification.to.trim()) {\n return { text: \"No recipient address\", missing: true };\n }\n return { text: `To ${notification.to}`, missing: false };\n}\n\nfunction NotificationCard({\n notification,\n providerName,\n fields,\n expanded,\n editorId,\n onToggle,\n onDuplicate,\n onDelete,\n onToggleEnabled,\n children,\n}: NotificationCardProps) {\n const recipient = describeRecipient(notification, fields);\n const ccCount =\n (notification.cc?.length ?? 0) + (notification.bcc?.length ?? 0);\n\n // Deleting a referenced field is only blocked for enabled rules, so a\n // disabled rule can legitimately hold references to fields that no longer\n // exist — surface that on the card instead of sending broken email later.\n const fieldNames = new Set(fields.map(f => f.name));\n const replyToRef = parseFieldRef(notification.replyTo);\n const hasStaleReference =\n (replyToRef !== null && !fieldNames.has(replyToRef)) ||\n (notification.condition !== undefined &&\n !fieldNames.has(notification.condition.field));\n\n const warnings: string[] = [];\n if (!notification.templateSlug) {\n warnings.push(\"No template — will not send\");\n }\n if (hasStaleReference) {\n warnings.push(\"References a deleted field — edit to repair\");\n }\n\n return (\n // `Card` rather than its classes written out again: the primitive owns the\n // surface colour, the radius, the foreground and the transition, and a\n // second spelling of them drifts the moment the shared surface changes.\n //\n // `overflow-hidden` because an expanded editor paints a full-width tinted\n // child; without clipping it squares off the corners the card just rounded.\n <Card className=\"overflow-hidden p-0\">\n {/* The dimming is the SUMMARY's, not the card's. A disabled rule reads as\n inactive at a glance, but an expanded editor inside a faded card fades\n every label, input and validation message the author is reading while\n they fix it — and takes the already-muted help text below its intended\n contrast. */}\n <div\n className={`flex items-center gap-2 px-3 py-2.5 ${notification.enabled ? \"\" : \"opacity-60\"}`}\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center bg-primary/5 text-primary\">\n <Mail className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </span>\n\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={expanded}\n aria-controls={editorId}\n className=\"flex min-w-0 flex-1 items-center gap-3 rounded-none py-1 text-left cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n // The NAME stays put while the state changes. `aria-expanded` above\n // already says open or closed, and a control whose name moves with\n // its state is one a screen-reader user cannot refer to twice — and\n // one no test can hold a handle on across a click.\n aria-label={`Edit notification ${notification.name}`}\n >\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-foreground\">\n {notification.name}\n </span>\n {notification.condition && (\n <Badge\n variant=\"outline\"\n className=\"shrink-0 gap-1 rounded-none border-border px-1.5 py-0 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground\"\n >\n <Filter className=\"h-2.5 w-2.5\" aria-hidden=\"true\" />\n Conditional\n </Badge>\n )}\n </span>\n <span className=\"block truncate text-xs text-muted-foreground\">\n <span className={recipient.missing ? \"text-destructive\" : \"\"}>\n {recipient.text}\n </span>\n {ccCount > 0 && (\n <span>\n <span className=\"mx-1.5 text-muted-foreground\">·</span>+\n {ccCount} cc/bcc\n </span>\n )}\n <span className=\"mx-1.5 text-muted-foreground\">·</span>\n {providerName}\n </span>\n </span>\n {warnings.length > 0 && (\n <span className=\"flex shrink-0 flex-col items-end gap-0.5\">\n {warnings.map(warning => (\n <span\n key={warning}\n className=\"flex items-center gap-1 text-xs font-medium text-destructive\"\n >\n <TriangleAlert className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {warning}\n </span>\n ))}\n </span>\n )}\n </button>\n\n <Switch\n checked={notification.enabled}\n onCheckedChange={onToggleEnabled}\n aria-label={`${notification.enabled ? \"Disable\" : \"Enable\"} notification ${notification.name}`}\n />\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 shrink-0 rounded-none text-muted-foreground hover:text-foreground\"\n aria-label={`Notification actions for ${notification.name}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"w-56 shadow-none border-border\"\n >\n <DropdownMenuItem\n onClick={onToggle}\n className=\"gap-2 cursor-pointer\"\n >\n <Pencil className=\"h-4 w-4 text-muted-foreground\" />\n {expanded ? \"Collapse\" : \"Edit\"}\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={onDuplicate}\n className=\"gap-2 cursor-pointer\"\n >\n <Copy className=\"h-4 w-4 text-muted-foreground\" />\n Duplicate\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n onClick={onDelete}\n className=\"gap-2 cursor-pointer text-destructive focus:text-destructive\"\n >\n <Trash2 className=\"h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Unmounted while collapsed rather than hidden. The editor holds a\n draft of the rule in its own state, seeded from the row; keeping a\n closed one mounted would keep a stale draft alive behind a summary\n that has moved on. */}\n {expanded && children}\n </Card>\n );\n}\n\n// ============================================================================\n// Recipient chip list (cc / bcc)\n// ============================================================================\n\ninterface AddressChipListProps {\n id: string;\n label: string;\n addresses: string[];\n placeholder: string;\n onChange: (addresses: string[]) => void;\n}\n\nfunction AddressChipList({\n id,\n label,\n addresses,\n placeholder,\n onChange,\n}: AddressChipListProps) {\n const [draft, setDraft] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n const add = useCallback(() => {\n const email = draft.trim();\n if (!email) return;\n // Nothing here goes through form submission, so constraint validation\n // never runs — reject malformed addresses before they can be persisted\n // and fail at delivery.\n if (!isValidEmail(email)) {\n setError(\"Enter a valid email address.\");\n return;\n }\n onChange([...addresses, email]);\n setDraft(\"\");\n setError(null);\n }, [draft, addresses, onChange]);\n\n return (\n <div className=\"space-y-1.5\">\n <Label htmlFor={id}>{label}</Label>\n {addresses.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {addresses.map((email, index) => (\n <Badge\n key={`${email}-${index}`}\n variant=\"outline\"\n className=\"gap-1.5 rounded-none border-border bg-primary/5 px-2 py-0.5 text-xs font-medium\"\n >\n <span>{email}</span>\n <button\n type=\"button\"\n className=\"text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary\"\n onClick={() =>\n onChange(addresses.filter((_, i) => i !== index))\n }\n aria-label={`Remove ${email}`}\n >\n <X className=\"h-3 w-3\" aria-hidden=\"true\" />\n </button>\n </Badge>\n ))}\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Input\n id={id}\n type=\"email\"\n value={draft}\n onChange={e => {\n setDraft(e.target.value);\n setError(null);\n }}\n onKeyDown={e => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n add();\n }\n }}\n placeholder={placeholder}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? `${id}-error` : undefined}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={add}\n className=\"shrink-0 px-3\"\n >\n Add\n </Button>\n </div>\n {error && (\n <p id={`${id}-error`} className=\"text-xs text-destructive\">\n {error}\n </p>\n )}\n </div>\n );\n}\n\ninterface RecipientFieldsProps {\n form: FormNotification;\n fields: readonly FieldOption[];\n defaults: NotificationDefaults | null;\n update: <K extends keyof FormNotification>(\n key: K,\n value: FormNotification[K]\n ) => void;\n /**\n * Switching target kinds invalidates the previous `to` value shape, so both\n * keys move together. It goes back to the editor rather than being done here\n * because the editor owns the write-through to the form.\n */\n onRecipientTypeChange: (kind: \"static\" | \"field\") => void;\n addressErrors: AddressErrors;\n validateAddress: (key: AddressField, value: string | undefined) => void;\n replyToMode: ReplyToMode;\n setReplyToMode: (mode: ReplyToMode) => void;\n}\n\n/**\n * Who the notification goes to: the recipient itself, the reply-to, and the\n * optional cc/bcc lists.\n *\n * Extracted from the editor because it is the one part of a notification that\n * is entirely about addresses — the editor around it is about identity,\n * sending and conditions — and because leaving it inline made a single\n * component long enough that no reader could hold it at once.\n */\nfunction RecipientFields({\n form,\n fields,\n defaults,\n update,\n onRecipientTypeChange,\n addressErrors,\n validateAddress,\n replyToMode,\n setReplyToMode,\n}: RecipientFieldsProps) {\n // Derived here rather than passed in: each of these is a function of `form`\n // and `fields`, which this component already has, and computing them in the\n // caller would be the same answer worked out somewhere else.\n const toRef = parseFieldRef(form.to);\n const toFieldOptions = buildEmailFieldOptions(fields, toRef);\n const replyToRef = parseFieldRef(form.replyTo);\n const replyToFieldOptions = buildEmailFieldOptions(fields, replyToRef);\n\n return (\n <>\n <div className=\"space-y-4 pt-4 border-t border-border\">\n <Grid cols={2} responsive>\n <FieldShell label=\"Send to\" htmlFor=\"notification-recipient-type\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={form.recipientType}\n onValueChange={value => {\n // Switching target kinds invalidates the previous `to`\n // value shape, so it resets rather than leaking a\n // {{ref}} into the static input (or vice versa).\n onRecipientTypeChange(value as \"static\" | \"field\");\n }}\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"static\">A specific address</SelectItem>\n <SelectItem value=\"field\">\n The visitor (email field)\n </SelectItem>\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n {form.recipientType === \"field\" ? (\n <div className=\"space-y-1.5\">\n <FieldShell label=\"Visitor email field\" htmlFor=\"notification-to\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={toRef ?? \"__none\"}\n onValueChange={value =>\n update(\"to\", value === \"__none\" ? \"\" : toFieldRef(value))\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none\">Select a field</SelectItem>\n {toFieldOptions.map(f => (\n <SelectItem key={f.name} value={f.name}>\n {f.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n {toFieldOptions.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">\n Add an email field to the form first.\n </p>\n )}\n </div>\n ) : (\n <FieldShell\n label=\"Recipient address\"\n htmlFor=\"notification-to\"\n width=\"half\"\n error={addressErrors.to}\n >\n <Input\n type=\"email\"\n value={form.to}\n onChange={e => update(\"to\", e.target.value)}\n onBlur={e => validateAddress(\"to\", e.target.value)}\n placeholder={defaults?.defaultToEmail || \"admin@example.com\"}\n />\n </FieldShell>\n )}\n </Grid>\n\n {/* Reply-To */}\n <Grid cols={2} responsive>\n <FieldShell label=\"Reply-To\" htmlFor=\"notification-replyto-mode\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={replyToMode}\n onValueChange={value => {\n const mode = value as ReplyToMode;\n setReplyToMode(mode);\n // A mode change always clears the stored value (the old\n // shape can't be represented in the new mode); it stays\n // absent — not an empty string — until the user picks a\n // field or types an address.\n update(\"replyTo\", undefined);\n }}\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">None</SelectItem>\n <SelectItem value=\"field\">\n The visitor (email field)\n </SelectItem>\n <SelectItem value=\"custom\">A custom address</SelectItem>\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n {replyToMode === \"field\" && (\n <FieldShell\n label=\"Visitor email field\"\n htmlFor=\"notification-replyto\"\n >\n {({ id, describedBy, invalid }) => (\n <Select\n value={replyToRef ?? \"__none\"}\n onValueChange={value =>\n update(\n \"replyTo\",\n value === \"__none\" ? undefined : toFieldRef(value)\n )\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none\">Select a field</SelectItem>\n {replyToFieldOptions.map(f => (\n <SelectItem key={f.name} value={f.name}>\n {f.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n )}\n {replyToMode === \"custom\" && (\n <FieldShell\n label=\"Reply-To address\"\n htmlFor=\"notification-replyto\"\n width=\"half\"\n error={addressErrors.replyTo}\n >\n <Input\n type=\"email\"\n value={form.replyTo ?? \"\"}\n onBlur={e => validateAddress(\"replyTo\", e.target.value)}\n onChange={e => update(\"replyTo\", e.target.value || undefined)}\n placeholder=\"replies@example.com\"\n />\n </FieldShell>\n )}\n </Grid>\n {replyToMode === \"field\" && (\n <p className=\"text-xs text-muted-foreground -mt-2\">\n Replying to this email answers the person who submitted the form.\n </p>\n )}\n\n <AddressChipList\n id=\"notification-cc\"\n label=\"CC (optional)\"\n addresses={form.cc}\n placeholder=\"cc@example.com\"\n onChange={cc => update(\"cc\", cc)}\n />\n <AddressChipList\n id=\"notification-bcc\"\n label=\"BCC (optional)\"\n addresses={form.bcc}\n placeholder=\"bcc@example.com\"\n onChange={bcc => update(\"bcc\", bcc)}\n />\n </div>\n </>\n );\n}\n\ninterface SendConditionFieldProps {\n condition: ConditionalLogicCondition | undefined;\n fields: readonly FieldOption[];\n update: <K extends keyof FormNotification>(\n key: K,\n value: FormNotification[K]\n ) => void;\n}\n\n/**\n * The optional rule that decides whether a notification sends at all.\n *\n * Its own component because it is the one part of the editor with a shape of\n * its own — absent, or a field/comparison/value triple — and inline it made\n * the editor read as two unrelated forms sharing a scope.\n */\nfunction SendConditionField({\n condition,\n fields,\n update,\n}: SendConditionFieldProps) {\n return (\n <>\n <div className=\"space-y-3 pt-4 border-t border-border\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n Send condition\n </p>\n <p className=\"text-xs text-muted-foreground\">\n Only send this notification when a submitted value matches.\n </p>\n </div>\n {!condition && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() =>\n update(\"condition\", {\n field: fields[0]?.name ?? \"\",\n comparison: \"equals\",\n value: \"\",\n })\n }\n disabled={fields.length === 0}\n >\n <Plus className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Add condition\n </Button>\n )}\n </div>\n\n {condition && (\n <div className=\"flex flex-wrap items-end gap-2 border border-border bg-muted/40 p-3\">\n <FieldShell\n label=\"Field\"\n htmlFor=\"notification-condition-field\"\n className=\"min-w-36 flex-1\"\n >\n {({ id, describedBy, invalid }) => (\n <Select\n value={condition.field || \"__none\"}\n onValueChange={value =>\n update(\"condition\", {\n ...condition,\n field: value === \"__none\" ? \"\" : value,\n })\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none\">Select a field</SelectItem>\n {fields.map(f => (\n <SelectItem key={f.name} value={f.name}>\n {f.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n <FieldShell\n label=\"Comparison\"\n htmlFor=\"notification-condition-comparison\"\n className=\"min-w-36 flex-1\"\n >\n {({ id, describedBy, invalid }) => (\n <Select\n value={condition.comparison}\n onValueChange={value =>\n update(\"condition\", {\n ...condition,\n comparison:\n value as ConditionalLogicCondition[\"comparison\"],\n })\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(COMPARISON_LABELS).map(([value, label]) => (\n <SelectItem key={value} value={value}>\n {label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n {!VALUELESS_COMPARISONS.has(condition.comparison) && (\n <FieldShell\n label=\"Value\"\n htmlFor=\"notification-condition-value\"\n className=\"min-w-36 flex-1\"\n >\n <Input\n type=\"text\"\n value={\n typeof condition.value === \"string\" ||\n typeof condition.value === \"number\"\n ? String(condition.value)\n : \"\"\n }\n onChange={e =>\n update(\"condition\", {\n ...condition,\n value: e.target.value,\n })\n }\n />\n </FieldShell>\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-9 w-9 rounded-none text-muted-foreground hover:text-destructive\"\n onClick={() => update(\"condition\", undefined)}\n aria-label=\"Remove send condition\"\n >\n <Trash2 className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n )}\n </div>\n </>\n );\n}\n\ninterface NotificationIdentityFieldsProps {\n form: FormNotification;\n providers: ProviderOption[];\n defaults: NotificationDefaults | null;\n templates: TemplateOption[];\n update: <K extends keyof FormNotification>(\n key: K,\n value: FormNotification[K]\n ) => void;\n addressErrors: AddressErrors;\n validateAddress: (key: AddressField, value: string | undefined) => void;\n}\n\n/**\n * What the notification IS: its name, which provider and template carry it,\n * and who it comes from.\n *\n * Separated from the recipients and the condition because those answer\n * different questions — where it goes, and whether it goes at all — and one\n * component answering all three was long enough that no reader could hold it.\n */\nfunction NotificationIdentityFields({\n form,\n providers,\n defaults,\n templates,\n update,\n addressErrors,\n validateAddress,\n}: NotificationIdentityFieldsProps) {\n // Derived here, from props this component already holds. Passing them in\n // would be the same answer worked out in the caller.\n const defaultProvider = providers.find(p => p.isDefault);\n const defaultProviderLabel = defaultProvider\n ? `System default (${defaultProvider.name})`\n : \"System default\";\n const senderPlaceholder = defaults?.defaultFrom || \"Provider default\";\n const senderHelp = defaults?.defaultFrom\n ? `Leave blank to send from ${defaults.defaultFrom} (the configured default).`\n : \"Leave blank to use the template or provider default address.\";\n\n return (\n <>\n {/* Name */}\n <FieldShell label=\"Name\" htmlFor=\"notification-name\" width=\"half\">\n <Input\n type=\"text\"\n value={form.name}\n onChange={e => update(\"name\", e.target.value)}\n placeholder=\"e.g. Admin notification\"\n />\n </FieldShell>\n\n {/* Provider & Template. A container-query grid rather than the\n viewport's `sm:` breakpoint: the admin content region is\n narrower than the window whenever both sidebars are open, so a\n viewport breakpoint promises columns this sheet does not have. */}\n <Grid cols={2} responsive>\n {/* Both `Select`-driven: FieldShell's render-function `children`\n applies the computed id/aria-describedby/aria-invalid to\n SelectTrigger, the actual focusable element, rather than to\n `Select`'s root (which accepts a fixed prop list and forwards\n none of the rest). */}\n <FieldShell label=\"Email provider\" htmlFor=\"notification-provider\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={form.providerId ?? \"__default\"}\n onValueChange={value =>\n update(\"providerId\", value === \"__default\" ? undefined : value)\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__default\">\n {defaultProviderLabel}\n </SelectItem>\n {providers.map(p => (\n <SelectItem key={p.id} value={p.id}>\n {p.name}\n {p.isDefault ? \" (Default)\" : \"\"}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n\n <div className=\"space-y-1.5\">\n <FieldShell label=\"Email template\" htmlFor=\"notification-template\">\n {({ id, describedBy, invalid }) => (\n <Select\n value={form.templateSlug ?? \"__none\"}\n onValueChange={value =>\n update(\"templateSlug\", value === \"__none\" ? undefined : value)\n }\n >\n <SelectTrigger\n id={id}\n aria-describedby={describedBy}\n aria-invalid={invalid}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none\">Select a template</SelectItem>\n {templates\n .filter(t => t.isActive && t.kind !== \"layout\")\n .map(t => (\n <SelectItem key={t.id} value={t.slug}>\n {t.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FieldShell>\n {!form.templateSlug && (\n <p className=\"flex items-center gap-1 text-xs text-destructive\">\n <TriangleAlert className=\"h-3 w-3\" aria-hidden=\"true\" />\n Required — a notification without a template is never sent.\n </p>\n )}\n </div>\n </Grid>\n\n {/* Sender */}\n <FieldShell\n label=\"Sender email\"\n htmlFor=\"notification-sender\"\n width=\"half\"\n description={addressErrors.senderEmail ? undefined : senderHelp}\n error={addressErrors.senderEmail}\n >\n <Input\n type=\"email\"\n value={form.senderEmail ?? \"\"}\n onChange={e => update(\"senderEmail\", e.target.value || undefined)}\n onBlur={e => validateAddress(\"senderEmail\", e.target.value)}\n placeholder={senderPlaceholder}\n />\n </FieldShell>\n </>\n );\n}\n\n// ============================================================================\n// Notification editor\n// ============================================================================\n\ntype ReplyToMode = \"none\" | \"field\" | \"custom\";\n\ninterface NotificationEditorProps {\n /**\n * The rule as the form holds it. Read on every render rather than copied\n * into local state: the summary row edits the same rule (its Enabled switch\n * writes straight to the form), and a second copy here would go stale the\n * moment it did — the next edit would then send the whole stale rule back\n * and undo it.\n */\n notification: FormNotification;\n /** Ties the row's toggle to this region for assistive tech. */\n editorId: string;\n providers: ProviderOption[];\n templates: TemplateOption[];\n fields: readonly FieldOption[];\n defaults: NotificationDefaults | null;\n /**\n * Every edit, as it happens. There is no save button here on purpose: the\n * page's own action bar commits the form, and a second one beside it could\n * only mean the same thing said twice or two different things unlabelled.\n */\n onChange: (notification: FormNotification) => void;\n}\n\nfunction initialReplyToMode(replyTo: string | undefined): ReplyToMode {\n if (!replyTo) return \"none\";\n return parseFieldRef(replyTo) ? \"field\" : \"custom\";\n}\n\nfunction NotificationEditor({\n notification,\n editorId,\n providers,\n templates,\n fields,\n defaults,\n onChange,\n}: NotificationEditorProps) {\n const form = notification;\n const [replyToMode, setReplyToMode] = useState<ReplyToMode>(\n initialReplyToMode(notification.replyTo)\n );\n // Save-time address errors, keyed by field. Nothing here goes through\n // form submission (every control is a type=\"button\" callback), so\n // constraint validation never runs — this is its replacement.\n // Seeded from the rule as it stands, so a row opened onto an address saved\n // earlier shows the problem straight away rather than waiting to be touched.\n const [addressErrors, setAddressErrors] = useState<AddressErrors>(() =>\n addressErrorsIn(notification)\n );\n\n const update = useCallback(\n <K extends keyof FormNotification>(key: K, value: FormNotification[K]) => {\n onChange({ ...notification, [key]: value });\n setAddressErrors(prev =>\n key in prev ? { ...prev, [key]: undefined } : prev\n );\n },\n [notification, onChange]\n );\n\n const changeRecipientType = useCallback(\n (kind: \"static\" | \"field\") => {\n onChange({ ...notification, recipientType: kind, to: \"\" });\n },\n [notification, onChange]\n );\n\n /**\n * Address validity, checked when the field is LEFT rather than on a save\n * press.\n *\n * Nothing here goes through form submission — every control is a\n * `type=\"button\"` callback — so constraint validation never runs and this is\n * its replacement. Blur rather than change, because an address is invalid\n * for most of the time it is being typed.\n */\n const validateAddress = useCallback(\n (key: AddressField, value: string | undefined) => {\n setAddressErrors(prev => ({ ...prev, [key]: addressError(key, value) }));\n },\n []\n );\n\n const condition = form.condition;\n\n return (\n <div id={editorId} className=\"border-t border-border bg-muted/20 px-4 py-5\">\n <div className=\"space-y-6\">\n <NotificationIdentityFields\n form={form}\n providers={providers}\n defaults={defaults}\n templates={templates}\n update={update}\n addressErrors={addressErrors}\n validateAddress={validateAddress}\n />\n {/* Recipients */}\n <RecipientFields\n form={form}\n fields={fields}\n defaults={defaults}\n update={update}\n onRecipientTypeChange={changeRecipientType}\n addressErrors={addressErrors}\n validateAddress={validateAddress}\n replyToMode={replyToMode}\n setReplyToMode={setReplyToMode}\n />\n {/* Send condition */}\n <SendConditionField\n condition={condition}\n fields={fields}\n update={update}\n />\n {/* Enabled */}\n <div className=\"flex items-center justify-between pt-4 border-t border-border\">\n <div>\n <Label htmlFor=\"notification-enabled\">Enabled</Label>\n <p className=\"text-xs text-muted-foreground\">\n Turn off to keep the rule without sending.\n </p>\n </div>\n <Switch\n id=\"notification-enabled\"\n checked={form.enabled}\n onCheckedChange={checked => update(\"enabled\", checked)}\n />\n </div>\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * FormNotificationsTab - the form's notification rules.\n *\n * Each rule references an existing email provider and template rather than\n * duplicating provider credentials or embedding a second template editor.\n */\nexport function FormNotificationsTab({ defaults }: FormNotificationsTabProps) {\n const {\n notifications,\n fields,\n addNotification,\n duplicateNotification,\n updateNotification,\n deleteNotification,\n } = useFormBuilder();\n\n const [providers, setProviders] = useState<ProviderOption[]>([]);\n const [templates, setTemplates] = useState<TemplateOption[]>([]);\n // Which row is open. One at a time: several open at once turns the list\n // into a wall of fields and loses the overview the summaries exist to give.\n const [expandedId, setExpandedId] = useState<string | null>(null);\n\n useEffect(() => {\n void fetchProviders().then(setProviders);\n void fetchTemplates().then(setTemplates);\n }, []);\n\n const fieldList = useMemo<FieldOption[]>(\n () => fields.map(f => ({ name: f.name, label: f.label, type: f.type })),\n [fields]\n );\n\n /**\n * A new rule is added immediately and opened, rather than composed in a\n * draft and committed on a button.\n *\n * That is the trade the inline editor makes: there is no save press to\n * attach a creation to, so the row exists from the moment it is asked for.\n * An unwanted one is removed the same way any other is, and an incomplete\n * one is inert — a notification without a template never sends, which the\n * row says on its face.\n */\n const addAndOpen = useCallback(() => {\n const created = createNotification();\n addNotification(created);\n setExpandedId(created.id);\n }, [addNotification]);\n\n const toggleExpanded = useCallback((id: string) => {\n setExpandedId(current => (current === id ? null : id));\n }, []);\n\n const getProviderName = useCallback(\n (providerId?: string) => {\n if (!providerId) {\n const def = providers.find(p => p.isDefault);\n return def ? `${def.name} (default)` : \"System default\";\n }\n return providers.find(p => p.id === providerId)?.name ?? \"Unknown\";\n },\n [providers]\n );\n\n return (\n // The measure belongs to the page's shell, not to this tab.\n <div>\n <div className=\"flex items-center justify-between mb-8 pb-4 border-b border-border\">\n <div>\n <h3 className=\"text-xl font-semibold text-foreground\">\n Notifications\n </h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Emails sent when someone submits this form.\n </p>\n </div>\n <Button type=\"button\" onClick={addAndOpen}>\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Add notification\n </Button>\n </div>\n\n {/* List */}\n {notifications.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center p-12 mt-6 border-2 border-dashed border-border rounded-none bg-muted\">\n <div className=\"flex items-center justify-center w-12 h-12 rounded-none border border-border bg-background mb-4 text-primary\">\n <Mail className=\"h-6 w-6\" aria-hidden=\"true\" />\n </div>\n <p className=\"text-base font-medium text-foreground mb-1\">\n No notifications yet\n </p>\n <p className=\"text-sm text-muted-foreground mb-6 text-center max-w-sm\">\n Add a notification to email someone when this form is submitted.\n </p>\n <Button type=\"button\" onClick={addAndOpen}>\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Add notification\n </Button>\n </div>\n ) : (\n <div className=\"space-y-2\">\n {notifications.map(notification => (\n <NotificationCard\n key={notification.id}\n notification={notification}\n providerName={getProviderName(notification.providerId)}\n fields={fieldList}\n expanded={expandedId === notification.id}\n editorId={`notification-editor-${notification.id}`}\n onToggle={() => toggleExpanded(notification.id)}\n onDuplicate={() => duplicateNotification(notification.id)}\n onDelete={() => deleteNotification(notification.id)}\n onToggleEnabled={enabled =>\n updateNotification(notification.id, { enabled })\n }\n >\n <NotificationEditor\n notification={notification}\n editorId={`notification-editor-${notification.id}`}\n providers={providers}\n templates={templates}\n fields={fieldList}\n defaults={defaults}\n onChange={next => updateNotification(notification.id, next)}\n />\n </NotificationCard>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport default FormNotificationsTab;\n","import type { FormNotification } from \"../../../types\";\nimport { parseFieldRef } from \"../../../utils/field-references\";\n\n/**\n * Deliberately loose email shape check (something@something.tld): the goal is\n * catching typos before they fail at delivery, not RFC 5322 conformance.\n */\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport function isValidEmail(value: string): boolean {\n return EMAIL_PATTERN.test(value.trim());\n}\n\n// Re-exported rather than re-implemented. Whether a value is a field reference\n// is the SEND path's question — a value the editor treats as one and delivery\n// does not goes out with its braces intact — so both read the same parser.\nexport { parseFieldRef };\n\n/** The address fields a notification can get wrong. */\nexport type AddressField = \"senderEmail\" | \"to\" | \"replyTo\";\n\nexport type AddressErrors = Partial<Record<AddressField, string>>;\n\nconst MESSAGE = \"Enter a valid email address.\";\n\n/**\n * Whether ONE address is unusable, by the single rule the whole feature uses.\n *\n * A blank address is not an error — every one of these is optional or has an\n * inherited default — and a `{{field}}` reference is not an address at all: it\n * resolves against each submission at send time, so it cannot be checked now.\n */\nexport function addressError(\n field: AddressField,\n value: string | undefined\n): string | undefined {\n if (field === \"replyTo\" && parseFieldRef(value) !== null) return undefined;\n if (!value?.trim()) return undefined;\n return isValidEmail(value) ? undefined : MESSAGE;\n}\n\n/**\n * Every unusable address in a notification.\n *\n * The editor asks this about one field as it is left; the page asks it about\n * every rule before it saves. The same function answers both, because the\n * alternative is a field that rejects an address and a save that accepts it —\n * which is what happened while the editor held the only copy of the answer.\n *\n * `to` is checked only when the recipient is a literal address: a `field`\n * recipient names a form field, which is not an address to validate.\n */\nexport function addressErrorsIn(notification: FormNotification): AddressErrors {\n const errors: AddressErrors = {};\n\n const sender = addressError(\"senderEmail\", notification.senderEmail);\n if (sender) errors.senderEmail = sender;\n\n if (notification.recipientType === \"static\") {\n const to = addressError(\"to\", notification.to);\n if (to) errors.to = to;\n }\n\n const replyTo = addressError(\"replyTo\", notification.replyTo);\n if (replyTo) errors.replyTo = replyTo;\n\n return errors;\n}\n\n/** The rules a save must refuse, named so the message can say which. */\nexport function notificationsWithBadAddresses(\n notifications: readonly FormNotification[]\n): string[] {\n return notifications\n .filter(n => Object.keys(addressErrorsIn(n)).length > 0)\n .map(n => n.name.trim() || \"Untitled notification\");\n}\n\n/**\n * What to tell an author who pressed Save with a malformed address, or `null`\n * when there is nothing to tell them.\n *\n * The wording lives beside the rule that produces it so the caller has one\n * question to ask and one branch to write — a save path that composed this\n * itself would be a second place deciding what counts as wrong.\n */\nexport function badAddressMessage(\n notifications: readonly FormNotification[]\n): string | null {\n const names = notificationsWithBadAddresses(notifications);\n if (names.length === 0) return null;\n return names.length === 1\n ? `${names[0]} has an invalid email address`\n : `${names.length} notifications have invalid email addresses`;\n}\n\n/**\n * The one reason a save must refuse the notifications, or `null`.\n *\n * A single question so the save has a single branch. Naming comes first: an\n * address complaint that says \"Untitled notification\" twice cannot be acted on,\n * whereas a named rule can then be pointed at.\n */\nexport function notificationsBlockingSave(\n notifications: readonly FormNotification[]\n): string | null {\n const unnamed = unnamedNotificationCount(notifications);\n if (unnamed > 0) {\n return unnamed === 1\n ? \"A notification needs a name\"\n : `${unnamed} notifications need a name`;\n }\n return badAddressMessage(notifications);\n}\n\n/**\n * Rules a save must refuse for having no name.\n *\n * The sheet this editor replaced disabled its own commit while the name was\n * blank. There is no commit here any more — the page's action bar is the only\n * one — so the precondition moves to it rather than disappearing with the\n * button that used to carry it. A nameless rule leaves its summary and its\n * menu labelled with nothing.\n */\nexport function unnamedNotificationCount(\n notifications: readonly FormNotification[]\n): number {\n return notifications.filter(n => !n.name.trim()).length;\n}\n","\"use client\";\n\n/**\n * Form Preview\n *\n * An interactive simulation of the form: real enabled inputs, conditional\n * logic evaluated live against what you type (the same evaluator the\n * runtime uses), the form's actual settings (button text, confirmation\n * behavior), and a device-width toggle. It is honest about what it is —\n * a faithful simulation inside the admin, not the published form; nothing\n * here submits anywhere.\n *\n * @module admin/components/builder/FormPreview\n */\n\nimport {\n Badge,\n Button,\n Checkbox,\n Input,\n Card,\n Label,\n RadioGroup,\n RadioGroupItem,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Tabs,\n TabsList,\n TabsTrigger,\n Textarea,\n} from \"@nextlyhq/ui\";\nimport { Eye, Monitor, Paperclip, RotateCcw, Smartphone } from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\n\nimport type { AnyFormField, CustomFormField, FormField } from \"../../../types\";\nimport { isKnownFormField } from \"../../../types\";\nimport { evaluateConditions } from \"../../../utils/evaluate-conditions\";\nimport { useFormBuilder } from \"../../context/FormBuilderContext\";\n\nimport { hasStoredRedirectPage } from \"./FormSettingsTab\";\n\n// ---------------------------------------------------------------------------\n// Field rendering\n// ---------------------------------------------------------------------------\n\nfunction PreviewFieldInput({\n field,\n value,\n onChange,\n}: {\n field: FormField;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const id = `preview-${field.name}`;\n switch (field.type) {\n case \"textarea\":\n return (\n <Textarea\n id={id}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n rows={\"rows\" in field ? (field.rows ?? 4) : 4}\n placeholder={field.placeholder}\n />\n );\n case \"checkbox\":\n // A real checkbox, not a toggle switch — the preview should look like\n // what a frontend form renders for this type.\n return (\n <Checkbox\n id={id}\n checked={Boolean(value)}\n onCheckedChange={checked => onChange(checked === true)}\n />\n );\n case \"select\": {\n const options = \"options\" in field ? (field.options ?? []) : [];\n return (\n <Select\n value={typeof value === \"string\" ? value : \"\"}\n onValueChange={onChange}\n >\n <SelectTrigger\n id={id}\n className=\"w-full bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue placeholder={field.placeholder ?? \"Select…\"} />\n </SelectTrigger>\n <SelectContent>\n {options.map(option => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n }\n case \"radio\": {\n const options = \"options\" in field ? (field.options ?? []) : [];\n return (\n <RadioGroup\n value={typeof value === \"string\" ? value : \"\"}\n onValueChange={onChange}\n className=\"flex flex-col gap-2\"\n >\n {options.map(option => (\n <div key={option.value} className=\"flex items-center gap-2\">\n <RadioGroupItem\n value={option.value}\n id={`${id}-${option.value}`}\n />\n <Label htmlFor={`${id}-${option.value}`} className=\"font-normal\">\n {option.label}\n </Label>\n </div>\n ))}\n </RadioGroup>\n );\n }\n case \"number\":\n return (\n <Input\n id={id}\n type=\"number\"\n value={\n typeof value === \"number\" || typeof value === \"string\"\n ? String(value)\n : \"\"\n }\n onChange={e =>\n onChange(e.target.value === \"\" ? \"\" : Number(e.target.value))\n }\n placeholder={field.placeholder}\n />\n );\n case \"date\":\n case \"time\":\n return (\n <Input\n id={id}\n type={field.type}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n />\n );\n case \"file\":\n // Nothing uploads from a preview; the affordance is shown, disabled.\n return (\n <div className=\"flex items-center gap-2 border border-dashed border-border bg-muted/40 px-3 py-2 text-sm text-muted-foreground\">\n <Paperclip className=\"h-4 w-4\" aria-hidden=\"true\" />\n File upload (disabled in preview)\n </div>\n );\n default:\n return (\n <Input\n id={id}\n type={\n field.type === \"email\"\n ? \"email\"\n : field.type === \"url\"\n ? \"url\"\n : field.type === \"phone\"\n ? \"tel\"\n : \"text\"\n }\n value={typeof value === \"string\" ? value : \"\"}\n onChange={e => onChange(e.target.value)}\n placeholder={field.placeholder}\n />\n );\n }\n}\n\n/**\n * Preview for a plugin-contributed field. The plugin owns the real input, which\n * renders from its own component in the live form; the builder preview shows a\n * labeled placeholder so the field's presence and order stay visible.\n */\nfunction PluginFieldPreview({ field }: { field: CustomFormField }) {\n return (\n <div className=\"space-y-1.5\">\n <Label htmlFor={`preview-${field.name}`}>\n {field.label || field.name}\n {field.required && <span className=\"ml-1 text-destructive\">*</span>}\n </Label>\n <div className=\"flex items-center gap-2 border border-dashed border-border bg-muted/40 px-3 py-2 text-sm text-muted-foreground\">\n Custom field ({field.type}) — renders from its plugin component in the\n live form.\n </div>\n {field.helpText && (\n <p className=\"text-xs text-muted-foreground\">{field.helpText}</p>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface FormPreviewProps {\n /** Array of form fields to preview (built-in or plugin-contributed). */\n fields: AnyFormField[];\n /** Form metadata */\n formData?: {\n name?: string;\n slug?: string;\n description?: string;\n };\n}\n\n/**\n * What the visitor is told after a successful submission.\n *\n * Its own component because the fall-through belongs to the MESSAGE branch: a\n * confirmation this does not name is previewed as a thank-you note, which is\n * how the page redirect came to be previewed as something it is not. Each\n * confirmation that is not a message has to be named here.\n */\nfunction Confirmation({\n settings,\n}: {\n settings: {\n confirmationType?: string;\n redirectUrl?: string;\n successMessage?: string;\n redirectPage?: unknown;\n };\n}) {\n const panel = \"border border-border bg-muted/40 p-4 text-sm text-foreground\";\n\n if (settings.confirmationType === \"redirect\") {\n return (\n <p className={panel}>\n The visitor would now be redirected to{\" \"}\n <span className=\"font-mono\">\n {settings.redirectUrl || \"(no redirect URL set)\"}\n </span>\n .\n </p>\n );\n }\n\n if (settings.confirmationType === \"relationship\") {\n // Whether a page is actually named, not merely that this confirmation is\n // chosen. Claiming a redirect with no page saved describes something that\n // cannot happen: the save is refused, and a form that reached this state\n // another way resolves to no redirect at all.\n const named = hasStoredRedirectPage(settings);\n return (\n <p className={panel}>\n {named\n ? \"The visitor would now be redirected to the linked page. Its URL is built when the form is submitted, from the pattern the site configured for that collection.\"\n : \"No page is selected yet, so this form has no destination. Choose one under Settings, or pick a different confirmation.\"}\n </p>\n );\n }\n\n return (\n <p className={panel}>\n {settings.successMessage || \"Thank you for your submission!\"}\n </p>\n );\n}\n\nexport function FormPreview({ fields, formData }: FormPreviewProps) {\n const { settings } = useFormBuilder();\n const [values, setValues] = useState<Record<string, unknown>>({});\n const [device, setDevice] = useState<\"desktop\" | \"mobile\">(\"desktop\");\n const [confirmed, setConfirmed] = useState(false);\n const [missingRequired, setMissingRequired] = useState<Set<string>>(\n new Set()\n );\n\n // Conditional logic runs against the live values with the SAME evaluator\n // the runtime uses — typing into the preview shows/hides fields for real.\n // Visibility is computed in field order against the values of fields that\n // are themselves visible: a hidden field's leftover value must not keep\n // satisfying a downstream condition (chained show/hide would misbehave).\n const visibleFields = useMemo(() => {\n const effectiveValues: Record<string, unknown> = {};\n const visible: AnyFormField[] = [];\n for (const field of fields) {\n if (field.type === \"hidden\") continue;\n const isVisible =\n !field.conditionalLogic?.enabled ||\n evaluateConditions(field.conditionalLogic, effectiveValues);\n if (isVisible) {\n visible.push(field);\n if (field.name in values) {\n effectiveValues[field.name] = values[field.name];\n }\n }\n }\n return visible;\n }, [fields, values]);\n\n const hiddenCount = fields.filter(field => field.type === \"hidden\").length;\n\n const reset = () => {\n setValues({});\n setConfirmed(false);\n setMissingRequired(new Set());\n };\n\n // The real form would refuse an empty required field, so the simulation\n // does too — an admin previewing required fields sees the requirement.\n const simulateSubmit = () => {\n const missing = new Set(\n visibleFields\n .filter(field => {\n // A plugin field renders a non-interactive placeholder in the preview\n // (its real input lives in the plugin's component in the live form),\n // so it can never be filled here — excluded like `file`, otherwise the\n // simulated submit would block forever with no way to satisfy it.\n if (!field.required || field.type === \"file\") return false;\n if (!isKnownFormField(field)) return false;\n const value = values[field.name];\n return value === undefined || value === \"\" || value === false;\n })\n .map(field => field.name)\n );\n setMissingRequired(missing);\n if (missing.size === 0) setConfirmed(true);\n };\n\n return (\n // One surface for the whole tab. The chrome and the stage are the same\n // panel seen twice, and leaving the chrome unbacked put it on the page's\n // grey while the stage below it had a frame — reading as a control strip\n // that had lost its box.\n <Card className=\"overflow-hidden p-0\">\n {/* Preview chrome: honest framing + device toggle + reset */}\n <div className=\"flex flex-col gap-3 border-b border-border px-6 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Eye className=\"h-4 w-4\" aria-hidden=\"true\" />\n Preview — a simulation of this form. Nothing here submits anywhere.\n </div>\n {/* Wraps rather than holding one row. The card around this clips, and\n at a ~360px panel the inset leaves less width than the device\n toggle and Reset need side by side — so an unwrapped row loses its\n right edge instead of overflowing visibly. */}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <Tabs\n value={device}\n onValueChange={value => setDevice(value as \"desktop\" | \"mobile\")}\n >\n {/* No corner override: the primitive already guarantees square,\n and a call site restating it is how the guarantee gets forked. */}\n <TabsList>\n <TabsTrigger value=\"desktop\" className=\"gap-1.5\">\n <Monitor className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Desktop\n </TabsTrigger>\n <TabsTrigger value=\"mobile\" className=\"gap-1.5\">\n <Smartphone className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Mobile\n </TabsTrigger>\n </TabsList>\n </Tabs>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={reset}>\n <RotateCcw className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Reset\n </Button>\n </div>\n </div>\n\n {/* The simulated form, on a tinted stage so the sheet inside reads as a\n page rather than as more of this panel. The stage draws no border of\n its own — the card around it already does. */}\n <div className=\"flex justify-center bg-muted/30 p-6\">\n <div\n className={`w-full space-y-5 border border-border bg-background p-6 ${\n device === \"mobile\" ? \"max-w-95\" : \"max-w-2xl\"\n }`}\n >\n <div>\n <h3 className=\"text-lg font-semibold text-foreground\">\n {formData?.name || \"Untitled form\"}\n </h3>\n {formData?.description && (\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {formData.description}\n </p>\n )}\n </div>\n\n {confirmed ? (\n <div className=\"space-y-4\">\n <Confirmation settings={settings} />\n <Button type=\"button\" variant=\"outline\" onClick={reset}>\n Fill again\n </Button>\n </div>\n ) : (\n <>\n {visibleFields.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">\n No visible fields — add fields in the Builder tab.\n </p>\n )}\n {visibleFields.map(field =>\n !isKnownFormField(field) ? (\n <PluginFieldPreview key={field.name} field={field} />\n ) : (\n <div key={field.name} className=\"space-y-1.5\">\n {field.type !== \"checkbox\" && (\n <Label htmlFor={`preview-${field.name}`}>\n {field.label || field.name}\n {field.required && (\n <span className=\"ml-1 text-destructive\">*</span>\n )}\n </Label>\n )}\n {field.type === \"checkbox\" ? (\n <div className=\"flex items-center gap-2\">\n <PreviewFieldInput\n field={field}\n value={values[field.name]}\n onChange={value =>\n setValues(prev => ({\n ...prev,\n [field.name]: value,\n }))\n }\n />\n <Label htmlFor={`preview-${field.name}`}>\n {field.label || field.name}\n {field.required && (\n <span className=\"ml-1 text-destructive\">*</span>\n )}\n </Label>\n </div>\n ) : (\n <PreviewFieldInput\n field={field}\n value={values[field.name]}\n onChange={value =>\n setValues(prev => ({ ...prev, [field.name]: value }))\n }\n />\n )}\n {field.helpText && (\n <p className=\"text-xs text-muted-foreground\">\n {field.helpText}\n </p>\n )}\n {missingRequired.has(field.name) && (\n <p className=\"text-xs text-destructive\">\n This field is required.\n </p>\n )}\n </div>\n )\n )}\n\n <Button\n type=\"button\"\n onClick={simulateSubmit}\n disabled={visibleFields.length === 0}\n >\n {settings.submitButtonText || \"Submit\"}\n </Button>\n </>\n )}\n\n {hiddenCount > 0 && !confirmed && (\n <p className=\"text-xs text-muted-foreground\">\n <Badge\n variant=\"outline\"\n className=\"mr-1.5 rounded-none border-border px-1 py-0 text-[10px]\"\n >\n {hiddenCount}\n </Badge>\n hidden {hiddenCount === 1 ? \"field\" : \"fields\"} will be submitted\n invisibly.\n </p>\n )}\n </div>\n </div>\n </Card>\n );\n}\n\nexport default FormPreview;\n","\"use client\";\n\n/**\n * Form Settings Tab\n *\n * Per-form behavior on the canonical settings shape: every control here is\n * consumed somewhere (the submit handler or the confirmation flow) — a\n * setting that does nothing does not get a toggle. Spam controls are\n * per-form OVERRIDES of the plugin's global config: blank means inherit,\n * and the inherited value is shown rather than hidden.\n *\n * @module admin/components/builder/FormSettingsTab\n */\n\nimport {\n FormSection,\n Input,\n Label,\n RadioGroup,\n RadioGroupItem,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n Textarea,\n} from \"@nextlyhq/ui\";\nimport type React from \"react\";\n\nimport { parseRedirectReference } from \"../../../utils/redirect-reference\";\nimport { useFormBuilder } from \"../../context/FormBuilderContext\";\n\nimport { RedirectPagePicker } from \"./RedirectPagePicker\";\n\n// ---------------------------------------------------------------------------\n// Layout helpers\n// ---------------------------------------------------------------------------\n\nfunction SettingRow({\n label,\n description,\n htmlFor,\n children,\n}: {\n label: string;\n description?: string;\n htmlFor?: string;\n children: React.ReactNode;\n}) {\n return (\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-6\">\n <div className=\"space-y-1\">\n <Label\n htmlFor={htmlFor}\n className=\"text-[13px] font-semibold text-foreground tracking-tight\"\n >\n {label}\n </Label>\n {description && (\n <p className=\"text-[12px] text-muted-foreground leading-relaxed\">\n {description}\n </p>\n )}\n </div>\n <div className=\"shrink-0 flex items-center justify-end min-w-25\">\n {children}\n </div>\n </div>\n );\n}\n\n/**\n * Tri-state override select: inherit from the plugin config (showing the\n * effective value), or force on/off for this form.\n */\nfunction InheritToggle({\n id,\n value,\n inherited,\n onChange,\n}: {\n id: string;\n value: boolean | undefined;\n inherited: boolean | undefined;\n onChange: (value: boolean | undefined) => void;\n}) {\n const inheritedLabel =\n inherited === undefined\n ? \"Inherit\"\n : `Inherit (${inherited ? \"on\" : \"off\"})`;\n return (\n <Select\n value={value === undefined ? \"inherit\" : value ? \"on\" : \"off\"}\n onValueChange={selected =>\n onChange(selected === \"inherit\" ? undefined : selected === \"on\")\n }\n >\n <SelectTrigger\n id={id}\n className=\"w-40 bg-transparent border-input dark:bg-muted/50\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"inherit\">{inheritedLabel}</SelectItem>\n <SelectItem value=\"on\">On</SelectItem>\n <SelectItem value=\"off\">Off</SelectItem>\n </SelectContent>\n </Select>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface SpamDefaults {\n honeypot?: boolean;\n recaptchaEnabled?: boolean;\n}\n\ninterface FormSettingsTabProps {\n /** Plugin-level spam defaults (from builder-config); null while loading. */\n spamDefaults: SpamDefaults | null;\n /**\n * Collections a form may redirect to (from builder-config); null while\n * loading. Empty means the site configured none, and the option is not\n * offered — choosing it could only ever produce a form with no destination.\n */\n redirectCollections: string[] | null;\n /**\n * The configuration request failed, as distinct from returning nothing. An\n * empty list is a valid answer; a failure is not an answer at all, and\n * saying \"no collection is configured\" on a 403 states something false.\n */\n redirectConfigFailed: boolean;\n}\n\n/**\n * The \"Redirect to a page\" choice and its picker.\n *\n * Shown when the site configures a redirect collection, AND whenever this form\n * ALREADY redirects to a page. Hiding a stored confirmation leaves the radio\n * group holding a value none of its items carry, so nothing appears selected —\n * while that stored value is still posted on the next save. The author would\n * be looking at a form that says one thing and saves another.\n *\n * Configuration can be removed after a form was saved, and the builder-config\n * request can fail; both arrive here as an empty list, which is why the stored\n * value rather than the configuration decides whether this renders.\n */\n/**\n * What the \"Redirect to a page\" option should show.\n *\n * Four outcomes, named because they were four nested conditions and the\n * difference between two of them is not visual — an empty configuration is an\n * ANSWER, a failed configuration read is not, and stating the first when the\n * second happened tells an author something false about their site.\n */\n/**\n * Whether these settings actually NAME a page — not whether the page-redirect\n * option happens to be selected.\n *\n * Conflating the two let an author choose the option while the configuration\n * was unknown, which classified the state as \"a page is stored\" with nothing\n * stored: a picker with no collections to offer, and a save that could only be\n * refused. Selecting a confirmation is not a destination.\n *\n * A BARE id counts. `parseRedirectReference` resolves one only when exactly\n * one collection is configured, which is the right rule for \"which document is\n * this\" — but the question here is \"is anything stored at all\", and the answer\n * must not depend on configuration that may have failed to load or been\n * removed. Those are precisely the states where an author needs to be shown\n * what is already saved.\n */\nexport function hasStoredRedirectPage(settings: {\n redirectPage?: unknown;\n}): boolean {\n const value = settings.redirectPage;\n if (typeof value === \"string\") return value.trim().length > 0;\n return parseRedirectReference(value, []) !== null;\n}\n\nexport type RedirectOptionState =\n /** Not offered: nothing stored, nothing configured, nothing unknown. */\n | \"hidden\"\n /** Offered but not selectable: the choices could not be loaded. */\n | \"unknown\"\n /** Offered because a page IS stored, though none can be chosen now. */\n | \"stored-only\"\n /** Offered with choices. */\n | \"ready\";\n\nexport function redirectOptionState(input: {\n /** A page is actually stored — not merely that this option is selected. */\n hasStoredPage: boolean;\n collections: string[] | null;\n configFailed: boolean;\n}): RedirectOptionState {\n const configured = input.collections !== null && input.collections.length > 0;\n if (configured) return \"ready\";\n // A STORED page outranks a failed request: it is a fact about this form,\n // and the author needs to see it to change it.\n if (input.hasStoredPage) return \"stored-only\";\n return input.configFailed ? \"unknown\" : \"hidden\";\n}\n\nfunction PageRedirectOption({\n redirectCollections,\n configFailed,\n settings,\n updateSettings,\n}: {\n redirectCollections: string[] | null;\n configFailed: boolean;\n settings: { confirmationType?: string; redirectPage?: unknown };\n updateSettings: (patch: { redirectPage?: unknown }) => void;\n}) {\n const state = redirectOptionState({\n hasStoredPage: hasStoredRedirectPage(settings),\n collections: redirectCollections,\n configFailed,\n });\n if (state === \"hidden\") return null;\n\n return (\n <div className=\"flex items-start gap-3\">\n {/* Selectable only when there are collections to pick from. In every\n other state choosing it leads to a save the server must refuse: the\n rule requires a page, and a page can only come from a configured\n collection. `stored-only` shows what is already saved; it is not an\n invitation to switch a different form into that state. */}\n <RadioGroupItem\n value=\"relationship\"\n id=\"settings-confirm-page\"\n className=\"mt-0.5\"\n disabled={state !== \"ready\"}\n />\n <div className=\"w-full space-y-2\">\n <Label htmlFor=\"settings-confirm-page\">Redirect to a page</Label>\n\n {state !== \"ready\" && (\n <p className=\"text-[12px] text-muted-foreground\">\n {configFailed\n ? \"The redirect configuration could not be loaded, so the pages you can choose from are not listed. Reload to try again.\"\n : \"No collection is configured as a redirect target right now. The page below is still saved, and stays until you choose a different confirmation.\"}\n </p>\n )}\n\n {/* Mounted whenever a page is stored, including with NO configured\n collections: the picker is what recovers the stored target by id\n and labels it, so leaving it out there tells an author a page is\n saved without telling them which. */}\n {state === \"stored-only\" || state === \"ready\"\n ? settings.confirmationType === \"relationship\" && (\n <RedirectPagePicker\n collections={redirectCollections ?? []}\n value={settings.redirectPage}\n onChange={next => updateSettings({ redirectPage: next })}\n />\n )\n : null}\n </div>\n </div>\n );\n}\n\nexport function FormSettingsTab({\n spamDefaults,\n redirectCollections,\n redirectConfigFailed,\n}: FormSettingsTabProps) {\n const { settings, updateSettings } = useFormBuilder();\n\n return (\n // The measure belongs to the page's shell, not to this tab.\n <div className=\"flex flex-col gap-10\">\n {/* Submission */}\n <FormSection label=\"Submission\">\n <SettingRow\n label=\"Submit button text\"\n description=\"Label on the form's primary action button\"\n htmlFor=\"settings-submit-text\"\n >\n <Input\n id=\"settings-submit-text\"\n type=\"text\"\n className=\"w-56\"\n value={settings.submitButtonText ?? \"\"}\n onChange={e => updateSettings({ submitButtonText: e.target.value })}\n />\n </SettingRow>\n\n <SettingRow\n label=\"Allow multiple submissions\"\n description=\"When off, the same visitor (by IP) can submit this form only once. Best-effort: IP-based, so shared networks count as one visitor.\"\n htmlFor=\"settings-multiple\"\n >\n <Switch\n id=\"settings-multiple\"\n checked={settings.allowMultipleSubmissions ?? true}\n onCheckedChange={checked =>\n updateSettings({ allowMultipleSubmissions: checked })\n }\n />\n </SettingRow>\n </FormSection>\n\n {/* After submission */}\n <FormSection label=\"After submission\">\n <RadioGroup\n value={settings.confirmationType ?? \"message\"}\n onValueChange={value =>\n updateSettings({\n confirmationType: value as\n | \"message\"\n | \"redirect\"\n | \"relationship\",\n })\n }\n className=\"flex flex-col gap-3\"\n >\n <div className=\"flex items-start gap-3\">\n <RadioGroupItem\n value=\"message\"\n id=\"settings-confirm-message\"\n className=\"mt-0.5\"\n />\n <div className=\"w-full space-y-2\">\n <Label htmlFor=\"settings-confirm-message\">Show a message</Label>\n {(settings.confirmationType ?? \"message\") === \"message\" && (\n <Textarea\n aria-label=\"Success message\"\n value={settings.successMessage ?? \"\"}\n onChange={e =>\n updateSettings({ successMessage: e.target.value })\n }\n rows={3}\n placeholder=\"Thank you for your submission!\"\n />\n )}\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <RadioGroupItem\n value=\"redirect\"\n id=\"settings-confirm-redirect\"\n className=\"mt-0.5\"\n />\n <div className=\"w-full space-y-2\">\n <Label htmlFor=\"settings-confirm-redirect\">\n Redirect to a URL\n </Label>\n {settings.confirmationType === \"redirect\" && (\n <Input\n aria-label=\"Redirect URL\"\n type=\"url\"\n value={settings.redirectUrl ?? \"\"}\n onChange={e =>\n updateSettings({\n redirectUrl: e.target.value || undefined,\n })\n }\n placeholder=\"https://example.com/thanks\"\n />\n )}\n </div>\n </div>\n <PageRedirectOption\n redirectCollections={redirectCollections}\n configFailed={redirectConfigFailed}\n settings={settings}\n updateSettings={updateSettings}\n />\n </RadioGroup>\n </FormSection>\n\n {/* Spam protection */}\n <FormSection label=\"Spam protection\">\n <SettingRow\n label=\"Honeypot\"\n description=\"Invisible trap field for bots. Inherits the plugin default unless overridden here.\"\n htmlFor=\"settings-honeypot\"\n >\n <InheritToggle\n id=\"settings-honeypot\"\n value={settings.honeypotEnabled}\n inherited={spamDefaults?.honeypot}\n onChange={honeypotEnabled => updateSettings({ honeypotEnabled })}\n />\n </SettingRow>\n\n <SettingRow\n label=\"reCAPTCHA\"\n description=\"Challenge-based bot check. Inherits the plugin default unless overridden here.\"\n htmlFor=\"settings-captcha\"\n >\n <InheritToggle\n id=\"settings-captcha\"\n value={settings.captchaEnabled}\n inherited={spamDefaults?.recaptchaEnabled}\n onChange={captchaEnabled => updateSettings({ captchaEnabled })}\n />\n </SettingRow>\n\n {settings.captchaEnabled === true && (\n <SettingRow\n label=\"reCAPTCHA site key\"\n description=\"The client-facing site key for this form\"\n htmlFor=\"settings-captcha-key\"\n >\n <Input\n id=\"settings-captcha-key\"\n type=\"text\"\n // Full width below the breakpoint: 288px does not fit inside the\n // section's 24px insets on a ~360px panel, and the card clips\n // rather than scrolling.\n className=\"w-full font-mono sm:w-72\"\n value={settings.captchaSiteKey ?? \"\"}\n onChange={e =>\n updateSettings({\n captchaSiteKey: e.target.value || undefined,\n })\n }\n />\n </SettingRow>\n )}\n </FormSection>\n </div>\n );\n}\n\nexport default FormSettingsTab;\n","\"use client\";\n\n/**\n * Picks the document a form redirects to after submission.\n *\n * The plugin cannot reach the admin's own relationship input: `@nextlyhq/admin`\n * is not on the plugin import boundary, and `plugin-sdk/admin` does not\n * re-export it. This is the narrow version of that control — one value, chosen\n * from the collections the site configured as redirect targets.\n *\n * @module admin/components/builder/RedirectPagePicker\n */\n\nimport {\n Input,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectTrigger,\n SelectValue,\n} from \"@nextlyhq/ui\";\nimport { encodeSelectParam } from \"nextly/query\";\nimport { useEffect, useState } from \"react\";\n\nimport {\n documentReachability,\n type Reachability,\n parseRedirectReference,\n} from \"../../../utils/redirect-reference\";\n\n/** Rows per request. Search, not paging, is how the rest are reached. */\nconst PAGE_SIZE = 50;\n\n/**\n * The conventional title fields, tried in order when a collection does not say\n * which of its fields names a document.\n */\nconst FALLBACK_TITLE_FIELDS = [\"title\", \"name\", \"label\", \"slug\"] as const;\n\n/**\n * The fields this control reads for one collection, in the order\n * `documentLabel` prefers them.\n *\n * A collection declares what names its documents through `admin.useAsTitle`,\n * and that declaration leads here: a collection titled by `headline` is listed\n * by its headlines rather than by opaque ids, which is the difference between\n * choosing a redirect target and guessing at one. The conventional names still\n * follow it, so a row whose configured title field happens to be empty reads\n * as something recognisable instead of falling straight through to an id.\n *\n * `id` is always first because the control stores it.\n *\n * One declaration, used to build the request AND to choose the label, so a\n * field added to one cannot go missing from the other.\n */\nexport function labelFieldsFor(useAsTitle?: string): readonly string[] {\n const configured = useAsTitle?.trim();\n if (!configured) return [\"id\", ...FALLBACK_TITLE_FIELDS];\n return [\n \"id\",\n configured,\n ...FALLBACK_TITLE_FIELDS.filter(field => field !== configured),\n ];\n}\n\n/**\n * The `select` query value that projects a read down to `fields`.\n *\n * The dispatcher reads this parameter with `JSON.parse` and keeps only the\n * keys whose values are booleans (`parseSelectParam`,\n * packages/nextly/src/dispatcher/helpers/validation.ts). A value it cannot\n * parse becomes `undefined` — no error, no warning, and a response carrying\n * every field of every row. A comma-separated list is precisely such a value,\n * so the encoding is the whole difference between projecting a read and\n * appearing to project it.\n */\n/**\n * The fields to REQUEST, which is the label fields plus what decides\n * reachability.\n *\n * Kept apart from `labelFieldsFor` on purpose. `status` decides whether a page\n * is marked as a draft; it must never become a document's name, and\n * `documentLabel` walks whatever list it is given — so a page with no title\n * would otherwise be offered to an author as \"draft\".\n *\n * Derived from the label list rather than written out again, so a field added\n * for labelling is still requested.\n */\nexport function selectFieldsFor(useAsTitle?: string): readonly string[] {\n // `firstPublishedAt` as well as `status`: it is what says the collection has\n // a publish lifecycle at all, and without it a page whose collection merely\n // has a field called `status` would be marked as a draft. See\n // `hasPublishLifecycle`.\n return [...labelFieldsFor(useAsTitle), \"status\", \"firstPublishedAt\"];\n}\n\n/**\n * The `select` parameter, ready to sit in a hand-built query string.\n *\n * The format itself comes from core rather than from here. This module once\n * spelled it out, having first shipped a comma list the server accepted and\n * discarded — every field of up to fifty documents per collection, to fill a\n * dropdown reading five.\n */\nexport function selectParam(fields: readonly string[]): string {\n return encodeURIComponent(encodeSelectParam(fields));\n}\n\n/** One selectable document, reduced to what the control shows and stores. */\ninterface Choice {\n collection: string;\n id: string;\n label: string;\n /**\n * Whether a visitor could reach this page today. Unpublished pages are\n * offered on purpose — a form is usually configured beside the page it\n * points at — but an author choosing one should be able to see that it is\n * not live yet.\n *\n * Kept as the three-way answer rather than a boolean: a localized page whose\n * translation may be public is not the same as one that is definitely a\n * draft, and only the definite case earns a marker.\n */\n reachability: Reachability;\n}\n\ninterface RedirectPagePickerProps {\n /** Collections configured as redirect targets, in configuration order. */\n collections: readonly string[];\n /** The stored relationship value, in whatever shape it was saved. */\n value: unknown;\n onChange: (next: { relationTo: string; value: string } | undefined) => void;\n}\n\n/**\n * The document a stored value points at, as `collection:id`.\n *\n * The reading is `parseRedirectReference`, not a second copy of it: the picker\n * and the submit handler have to agree about which document a stored value\n * names, and two readers that agree today would drift — silently, because a\n * control showing the wrong page still shows a page.\n */\nexport function selectionKey(\n value: unknown,\n collections: readonly string[]\n): string | undefined {\n const reference = parseRedirectReference(value, collections);\n return reference ? `${reference.collection}:${reference.id}` : undefined;\n}\n\n/** What to call a document, preferring the field an author would recognise. */\nexport function documentLabel(\n row: Record<string, unknown>,\n fields: readonly string[] = labelFieldsFor()\n): string {\n for (const field of fields.filter(name => name !== \"id\")) {\n // A collection only has to point `useAsTitle` at a field that EXISTS, so\n // the configured title can be a number or a boolean. Skipping those falls\n // through to a conventional field or to the id, which is the opaque label\n // reading the configuration was meant to replace. `0` and `false` are\n // titles like any other, so emptiness is decided after stringifying, not\n // by truthiness.\n const candidate = row[field];\n const text =\n typeof candidate === \"string\"\n ? candidate\n : typeof candidate === \"number\" && Number.isFinite(candidate)\n ? String(candidate)\n : typeof candidate === \"boolean\"\n ? String(candidate)\n : undefined;\n if (text !== undefined && text.trim()) return text;\n }\n return typeof row.id === \"string\" ? row.id : \"Untitled\";\n}\n\n/**\n * One collection's matching documents, or null when it could not be read.\n *\n * `limit`, not `pageSize` — the other name is accepted and ignored, leaving\n * the default of 10. `status=all` because a form is usually configured\n * alongside the page it points at, and filtering to published shows \"create\n * one first\" while the page sits in the next tab. `depth=0` because this reads\n * a handful of scalar fields and would otherwise inherit the collection API's\n * expansion, pulling every nested relation of every row to fill a dropdown.\n */\nasync function fetchChoices(\n collection: string,\n query: string,\n page: number,\n meta: CollectionMeta\n): Promise<{ rows: Choice[]; hasMore: boolean } | null> {\n const search = query ? `&search=${encodeURIComponent(query)}` : \"\";\n try {\n // `select` as well as `depth=0`. Without one the server sends every scalar\n // and JSON field of up to fifty documents per collection — for\n // page-builder documents that is the whole block tree — to fill a dropdown\n // that reads a handful of them.\n const response = await fetch(\n `/admin/api/collections/${collection}/entries` +\n `?limit=${PAGE_SIZE}&page=${page}&status=all&depth=0` +\n `&select=${selectParam(selectFieldsFor(meta.titleField))}${search}`,\n { credentials: \"include\" }\n );\n if (!response.ok) return null;\n const body = (await response.json()) as {\n items?: Record<string, unknown>[];\n meta?: { totalPages?: number };\n };\n return {\n rows: (body.items ?? [])\n .filter(row => typeof row.id === \"string\")\n .map(row => ({\n collection,\n id: row.id as string,\n label: documentLabel(row, labelFieldsFor(meta.titleField)),\n reachability: documentReachability(\n row as { id: string },\n meta.localized\n ),\n })),\n // Reported rather than assumed, so the control can offer the rest\n // instead of truncating where the author cannot see it.\n hasMore: page < (body.meta?.totalPages ?? 1),\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Existing choices plus new ones, de-duplicated on the pair that identifies a\n * document. Order is preserved so a list does not reshuffle under the author.\n */\nfunction mergeChoices(\n existing: readonly Choice[],\n incoming: readonly Choice[]\n): Choice[] {\n const seen = new Set(existing.map(c => `${c.collection}:${c.id}`));\n const merged = [...existing];\n for (const choice of incoming) {\n const key = `${choice.collection}:${choice.id}`;\n if (seen.has(key)) continue;\n seen.add(key);\n merged.push(choice);\n }\n return merged;\n}\n\n/** One option, keyed and valued by the pair the caller stores. */\nfunction renderChoice(choice: Choice) {\n return (\n <SelectItem\n key={`${choice.collection}:${choice.id}`}\n value={`${choice.collection}:${choice.id}`}\n >\n <span className=\"inline-flex items-center gap-2\">\n <span>{choice.label}</span>\n {/* Unpublished pages are offered deliberately — a form is usually\n configured beside the page it points at — so the marker is what\n keeps \"offered\" from reading as \"live\". A published form saved\n against one is refused, and an author who cannot see which pages\n are drafts has no way to predict that. */}\n {choice.reachability === \"unreachable\" && (\n <span className=\"shrink-0 rounded-sm border px-1 py-px text-[10px] uppercase tracking-wide text-muted-foreground\">\n Draft\n </span>\n )}\n </span>\n </SelectItem>\n );\n}\n\n/**\n * Which collections the picker needs metadata for, as the key the lookup is\n * cached on.\n *\n * The configured ones, plus the collection the STORED target names when that\n * is no longer among them. A polymorphic value can point at a collection since\n * removed from `redirectRelationships`, and the picker recovers and displays\n * that target on purpose — looking up only the configured ones would label it\n * by the conventional names while the listing uses each collection's own title\n * field, showing one document under two rules.\n */\nexport function metadataCollections(\n collections: readonly string[],\n value: unknown\n): string {\n const stored = selectionKey(value, collections)?.split(\":\")[0];\n const wanted =\n stored && !collections.includes(stored)\n ? [...collections, stored]\n : collections;\n return wanted.join(\",\");\n}\n\n/**\n * The field a collection says names its documents, or undefined when it does\n * not say or cannot be asked.\n *\n * Undefined covers both \"this collection declares no title field\" and \"the\n * metadata could not be read\", because the control does the same thing in\n * either case: fall back to the conventional names. Nothing on screen depends\n * on telling them apart, and the listing itself still reports its own\n * failures.\n */\ninterface CollectionMeta {\n /** The field the collection says names its documents. */\n titleField: string | undefined;\n /**\n * The collection's `localized` setting, or undefined when it could not be\n * read. Undefined is NOT false: a localized collection read as plain marks\n * reachable translations as drafts.\n */\n localized: boolean | undefined;\n}\n\nasync function fetchCollectionMeta(\n collection: string\n): Promise<CollectionMeta> {\n try {\n // The bare document, not a `{ message, item }` envelope — that shape is\n // for mutations, and this is a read.\n const response = await fetch(`/admin/api/collections/${collection}`, {\n credentials: \"include\",\n });\n if (!response.ok) return { titleField: undefined, localized: undefined };\n const body = (await response.json()) as {\n admin?: { useAsTitle?: unknown };\n localized?: unknown;\n };\n const configured = body.admin?.useAsTitle;\n return {\n titleField:\n typeof configured === \"string\" && configured.trim()\n ? configured.trim()\n : undefined,\n localized:\n typeof body.localized === \"boolean\" ? body.localized : undefined,\n };\n } catch {\n return { titleField: undefined, localized: undefined };\n }\n}\n\n/**\n * The fields to read and to label from, per configured collection.\n *\n * Resolved once per collection list rather than per search: what a collection\n * calls its title does not change while an author types, and folding this into\n * the listing effect would spend a request per keystroke to re-learn a\n * constant.\n *\n * `null` means not resolved yet. The listing waits for it, because a first\n * page fetched under the fallback fields would have to be refetched or else\n * left labelled differently from every page after it.\n */\nfunction useTitleFields(key: string) {\n const [fields, setFields] = useState<Record<string, CollectionMeta> | null>(\n null\n );\n\n useEffect(() => {\n let cancelled = false;\n const wanted = key ? key.split(\",\") : [];\n setFields(null);\n\n void Promise.all(\n wanted.map(async collection => {\n return [collection, await fetchCollectionMeta(collection)] as const;\n })\n ).then(pairs => {\n if (!cancelled) setFields(Object.fromEntries(pairs));\n });\n\n return () => {\n cancelled = true;\n };\n }, [key]);\n\n return fields;\n}\n\n/**\n * The matching documents for every configured collection, and which of them\n * could not be read.\n */\nfunction useChoices(\n key: string,\n applied: string,\n titleFields: Record<string, CollectionMeta> | null\n) {\n const [choices, setChoices] = useState<Choice[] | null>(null);\n const [failed, setFailed] = useState<readonly string[]>([]);\n const [hasMore, setHasMore] = useState(false);\n const [loading, setLoading] = useState(false);\n // Per collection, not shared. A shared counter advances every collection\n // when any one of them has more, so a collection whose page N failed is\n // never asked for page N again — and if its N+1 succeeds the warning clears\n // too, leaving those documents absent with nothing on screen saying so.\n const [pages, setPages] = useState<Record<string, number>>({});\n\n // A new query is a new list, not more of the old one.\n //\n // The SAME reference is returned when there is nothing to clear. Writing a\n // fresh `{}` every time is a state change even when the map is already\n // empty, and the fetch effect below depends on `pages` — so each query\n // change ran the fetch once with the old map, cancelled it, and ran it\n // again. Every collection was requested twice per search and the first\n // answer discarded.\n useEffect(() => {\n setPages(current => (Object.keys(current).length === 0 ? current : {}));\n }, [key, applied]);\n\n useEffect(() => {\n // Nothing to request until each collection's title field is known —\n // see `useTitleFields`. `choices` stays null, which the control already\n // renders as loading.\n if (titleFields === null) return;\n\n let cancelled = false;\n const wanted = key ? key.split(\",\") : [];\n setLoading(true);\n\n void Promise.all(\n wanted.map(collection =>\n fetchChoices(\n collection,\n applied,\n pages[collection] ?? 1,\n titleFields[collection] ?? {\n titleField: undefined,\n localized: undefined,\n }\n )\n )\n ).then(results => {\n if (cancelled) return;\n setLoading(false);\n\n // WHICH collections failed, not merely whether any did: one unreadable\n // collection beside a readable-but-empty one otherwise reports \"nothing\n // here\" about a collection nobody could see.\n setFailed(wanted.filter((_, index) => results[index] === null));\n\n const loaded = results.filter(\n (result): result is { rows: Choice[]; hasMore: boolean } =>\n result !== null\n );\n setHasMore(loaded.some(result => result.hasMore));\n\n const rows = loaded.flatMap(result => result.rows);\n const first = wanted.every(collection => (pages[collection] ?? 1) === 1);\n // Merged by identity rather than appended. A retry re-requests a page\n // that already succeeded for its siblings, and appending would list\n // those documents twice — duplicate options, and duplicate React keys.\n // Keyed, a repeated page is a no-op whatever caused the repeat.\n setChoices(current => mergeChoices(first ? [] : (current ?? []), rows));\n });\n\n return () => {\n cancelled = true;\n };\n }, [key, applied, pages, titleFields]);\n\n return {\n choices,\n setChoices,\n failed,\n hasMore,\n loading,\n /**\n * Advances only the collections that have more to give, and RETRIES the\n * ones whose last page failed rather than stepping over it.\n *\n * Guarded on the in-flight flag: two clicks before the first resolves\n * either batch into a double increment or cancel the pending effect, and a\n * skipped page is then absent from a list that reports having loaded it.\n */\n loadMore: () => {\n if (loading) return;\n setPages(current => {\n const next: Record<string, number> = { ...current };\n for (const collection of key ? key.split(\",\") : []) {\n // A failed collection stays on its page so the retry asks for the\n // page it never got.\n if (failed.includes(collection)) continue;\n next[collection] = (current[collection] ?? 1) + 1;\n }\n return next;\n });\n },\n /** Ask the failed collections again, without advancing anyone. */\n retryFailed: () => {\n if (!loading) setPages(current => ({ ...current }));\n },\n };\n}\n\n/**\n * Adds the selected document when the listing did not reach it.\n *\n * Keyed on the selection rather than on the collection list, because the\n * stored value arrives with the form and often AFTER this mounts — a hook\n * watching only the collections would capture the moment when nothing was\n * selected and never look again.\n *\n * A control that silently drops its own value is worse than one that cannot\n * list everything: the author sees blank, re-picks, and saves a different\n * destination than the one that was stored.\n */\nfunction useSelectedChoice(\n selected: string | undefined,\n choices: Choice[] | null,\n setChoices: (update: (current: Choice[] | null) => Choice[]) => void,\n titleFields: Record<string, CollectionMeta> | null\n) {\n // Whether the stored selection could not be read. A controlled `Select`\n // holding a value with no matching option renders BLANK, which looks\n // identical to \"nothing chosen\" — so an author with a perfectly good\n // destination is invited to pick over it. Reported rather than swallowed.\n const [unreadable, setUnreadable] = useState(false);\n\n useEffect(() => {\n if (!selected || choices === null) return;\n const separator = selected.indexOf(\":\");\n const collection = selected.slice(0, separator);\n const id = selected.slice(separator + 1);\n // Collection AND id: two configured collections can hold the same id, and\n // matching on id alone skips the fetch while the option that is actually\n // selected is absent.\n if (\n !id ||\n choices.some(\n choice => choice.id === id && choice.collection === collection\n )\n ) {\n setUnreadable(false);\n return;\n }\n\n let cancelled = false;\n void (async () => {\n try {\n // `status=all` for the same reason the listing carries it: a stored\n // target may be a draft, and a published-only read answers 404.\n const response = await fetch(\n `/admin/api/collections/${collection}/entries/${id}?depth=0&status=all`,\n { credentials: \"include\" }\n );\n if (!response.ok) {\n if (!cancelled) setUnreadable(true);\n return;\n }\n // A by-id read answers with the document ITSELF. Only mutations carry\n // the `{ message, item }` envelope.\n const row = (await response.json()) as Record<string, unknown>;\n if (cancelled) return;\n if (!row || typeof row.id !== \"string\") {\n setUnreadable(true);\n return;\n }\n setUnreadable(false);\n setChoices(current => [\n {\n collection,\n id: row.id as string,\n // The same fields the listing labels by. Labelling the recovered\n // document differently would show one document under two names\n // depending on whether the listing happened to reach it.\n label: documentLabel(\n row,\n labelFieldsFor(titleFields?.[collection]?.titleField)\n ),\n reachability: documentReachability(\n row as { id: string },\n titleFields?.[collection]?.localized\n ),\n },\n ...(current ?? []),\n ]);\n } catch {\n if (!cancelled) setUnreadable(true);\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [selected, choices, setChoices, titleFields]);\n\n return unreadable;\n}\n\n/**\n * The options, grouped by the collection each one came from.\n *\n * The groups are derived from the CHOICES, not from the configured list. A\n * stored target whose collection was removed since the form was saved is\n * recovered by id and belongs in the list — filtering by configuration\n * dropped it, leaving the control blank over a value that is still stored,\n * with the unreadable warning cleared because the recovery had succeeded.\n *\n * That divergence was visible in the code before it was visible on screen: the\n * single-collection branch rendered every choice and the grouped branch\n * filtered them, so the same state produced different pickers depending on how\n * many collections a site happened to configure. One path now.\n */\nexport interface ChoiceGroup {\n collection: string;\n label: string;\n /** True when this collection is no longer a configured redirect target. */\n removed: boolean;\n choices: Choice[];\n}\n\n/**\n * The choices arranged into groups, in configuration order, with collections\n * that are no longer configured last and marked.\n *\n * Derived from the CHOICES rather than from the configured list, which is the\n * whole point: a stored target whose collection was removed is recovered by id\n * and belongs in the list.\n */\nexport function groupChoices(\n choices: readonly Choice[],\n collections: readonly string[]\n): ChoiceGroup[] {\n const present = [...new Set(choices.map(choice => choice.collection))];\n const order = [\n ...collections.filter(name => present.includes(name)),\n ...present.filter(name => !collections.includes(name)),\n ];\n\n return order.map(collection => {\n const removed = !collections.includes(collection);\n return {\n collection,\n // Named as unavailable rather than hidden: the value is still stored and\n // the server refuses a save into a collection with no pattern, so an\n // author needs to see what is set in order to change it.\n label: removed ? `${collection} — no longer configured` : collection,\n removed,\n choices: choices.filter(choice => choice.collection === collection),\n };\n });\n}\n\nfunction Options({\n choices,\n collections,\n}: {\n choices: Choice[];\n collections: readonly string[];\n}) {\n const groups = groupChoices(choices, collections);\n if (groups.length <= 1 && !groups[0]?.removed) {\n return <>{choices.map(renderChoice)}</>;\n }\n return (\n <>\n {groups.map(group => (\n <SelectGroup key={group.collection}>\n <SelectLabel>{group.label}</SelectLabel>\n {group.choices.map(renderChoice)}\n </SelectGroup>\n ))}\n </>\n );\n}\n\n/**\n * What to say when there is nothing to choose, and why.\n *\n * `collections` here is the READABLE ones. Passing the full configured list\n * asserted that a collection which could not be read is empty — printed\n * directly beneath the notice saying it could not be read.\n */\nfunction Empty({\n applied,\n collections,\n}: {\n applied: string;\n collections: readonly string[];\n}) {\n return (\n <p className=\"text-[12px] text-muted-foreground\">\n {applied\n ? `No documents match “${applied}”.`\n : collections.length === 0\n ? \"No collection is configured as a redirect target.\"\n : `No documents to redirect to yet. Create one in ${collections.join(\" or \")} first.`}\n </p>\n );\n}\n\n/**\n * The things this control has to say that its options cannot.\n *\n * Three separate states, kept apart deliberately. A collection that could not\n * be read, a saved page that cannot be IDENTIFIED because no collection is\n * configured to look it up in, and a saved page whose lookup FAILED are three\n * different situations, and each one previously appeared as an empty control —\n * which reads as \"nothing is set\" and invites an author to overwrite a\n * destination they were never shown.\n */\nfunction PickerNotices({\n failed,\n loading,\n onRetry,\n unidentifiable,\n unreadableSelection,\n}: {\n failed: readonly string[];\n loading: boolean;\n onRetry: () => void;\n unidentifiable: boolean;\n unreadableSelection: boolean;\n}) {\n return (\n <>\n {failed.length > 0 && (\n <p className=\"text-[12px] text-destructive\">\n Could not read {failed.join(\" or \")}. You may not have permission to\n list {failed.length > 1 ? \"them\" : \"it\"}, or the request failed.{\" \"}\n <button\n type=\"button\"\n onClick={onRetry}\n disabled={loading}\n className=\"underline underline-offset-2 disabled:no-underline\"\n >\n Try again\n </button>\n </p>\n )}\n\n {unidentifiable && (\n <p className=\"text-[12px] text-muted-foreground\">\n A page is saved on this form, but it cannot be identified without a\n configured redirect collection. Choosing a different confirmation\n replaces it.\n </p>\n )}\n\n {unreadableSelection && (\n <p className=\"text-[12px] text-destructive\">\n This form has a page selected that could not be read. Leave it as it\n is unless you mean to change it — saving a new choice replaces the\n stored one.\n </p>\n )}\n </>\n );\n}\n\nexport function RedirectPagePicker({\n collections,\n value,\n onChange,\n}: RedirectPagePickerProps) {\n const [query, setQuery] = useState(\"\");\n const [applied, setApplied] = useState(\"\");\n\n // Typing should not issue a request per keystroke.\n useEffect(() => {\n const timer = setTimeout(() => setApplied(query), 250);\n return () => clearTimeout(timer);\n }, [query]);\n\n // `collections` is an array prop, so a new identity every render would\n // refetch forever; the join is the value that actually decides the request.\n const titleFields = useTitleFields(metadataCollections(collections, value));\n const {\n choices,\n setChoices,\n failed,\n hasMore,\n loading,\n loadMore,\n retryFailed,\n } = useChoices(collections.join(\",\"), applied, titleFields);\n const selected = selectionKey(value, collections);\n // A value is stored and this control cannot say WHICH document it is: a bare\n // id names no collection, and with none configured there is nowhere to look\n // it up. Distinct from an unreadable selection, which is a failed request\n // rather than missing information.\n const unidentifiable = value != null && selected === undefined;\n const selectionUnreadable = useSelectedChoice(\n selected,\n choices,\n setChoices,\n titleFields\n );\n\n if (choices === null) {\n return <p className=\"text-[12px] text-muted-foreground\">Loading pages…</p>;\n }\n\n return (\n <div className=\"space-y-2\">\n {/* Search rather than paging: a dropdown cannot usefully list a large\n collection, and a ceiling on how many pages to walk would leave the\n documents past it unreachable however high it were set. */}\n {/* Nothing to search when no collection is listed — the only entry in\n the control then is the stored target, recovered by id. */}\n {collections.length > 0 && (\n <Input\n aria-label=\"Search pages\"\n value={query}\n onChange={event => setQuery(event.target.value)}\n placeholder=\"Search…\"\n />\n )}\n\n {/* Reported BESIDE the choices rather than instead of them: a collection\n the author cannot read is a different problem from one that is empty,\n and the collections that did load are still choosable. */}\n <PickerNotices\n failed={failed}\n loading={loading}\n onRetry={retryFailed}\n unidentifiable={unidentifiable}\n unreadableSelection={selectionUnreadable}\n />\n\n {choices.length === 0 ? (\n failed.length < collections.length && (\n <Empty\n applied={applied}\n collections={collections.filter(name => !failed.includes(name))}\n />\n )\n ) : (\n <Select\n value={selected ?? \"\"}\n onValueChange={next => {\n const separator = next.indexOf(\":\");\n if (separator < 0) return onChange(undefined);\n onChange({\n relationTo: next.slice(0, separator),\n value: next.slice(separator + 1),\n });\n }}\n >\n <SelectTrigger aria-label=\"Redirect page\" className=\"w-full\">\n <SelectValue placeholder=\"Choose a page\" />\n </SelectTrigger>\n <SelectContent>\n <Options choices={choices} collections={collections} />\n </SelectContent>\n </Select>\n )}\n\n {/* The rest of the matches, reachable rather than silently absent. A cap\n with no way past it makes a document beyond it unselectable however\n high the cap is set. */}\n {hasMore && (\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"text-[12px] text-muted-foreground underline underline-offset-2 hover:text-foreground disabled:no-underline disabled:opacity-60\"\n >\n {loading ? \"Loading…\" : \"Load more\"}\n </button>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["Badge","Button","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","ChevronLeft","ChevronRight","useEffect","useState","jsx","jsxs","formatDate","Badge","useState","useEffect","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Button","ChevronLeft","ChevronRight","Button","Card","FieldShell","Input","Textarea","Tabs","TabsList","TabsTrigger","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","FORM_FIELD_TYPE_CATALOG","useCallback","useEffect","useRef","useState","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","Tooltip","TooltipContent","TooltipTrigger","FORM_FIELD_TYPE_CATALOG","useCallback","useMemo","useState","useCallback","useMemo","useState","jsx","useState","useCallback","useMemo","Button","useState","jsx","jsxs","FormLabelWithTooltip","Input","Checkbox","Tabs","TabsList","TabsTrigger","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","useState","useCallback","useMemo","Button","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","useCallback","useMemo","jsx","jsxs","jsx","jsxs","useMemo","FormLabelWithTooltip","Input","Checkbox","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","useState","useCallback","Tabs","TabsList","TabsTrigger","Fragment","jsx","jsxs","FORM_FIELD_TYPE_CATALOG","DropdownMenu","DropdownMenuTrigger","Button","DropdownMenuContent","DropdownMenuItem","Tooltip","TooltipTrigger","TooltipContent","useState","useMemo","useCallback","Badge","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuSeparator","DropdownMenuTrigger","Input","Label","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","Switch","Copy","MoreHorizontal","Pencil","Plus","Trash2","TriangleAlert","useCallback","useEffect","useMemo","useState","Fragment","jsx","jsxs","Badge","TriangleAlert","Switch","DropdownMenu","DropdownMenuTrigger","Button","MoreHorizontal","DropdownMenuContent","DropdownMenuItem","Pencil","Copy","DropdownMenuSeparator","Trash2","useState","useCallback","Label","Input","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Plus","useEffect","useMemo","Badge","Button","Checkbox","Input","Card","Label","RadioGroup","RadioGroupItem","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","Tabs","TabsList","TabsTrigger","Textarea","useMemo","useState","Input","Label","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","Switch","Textarea","Input","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","useEffect","useState","Fragment","jsx","jsxs","PAGE_SIZE","SelectItem","useState","useEffect","Input","Select","SelectTrigger","SelectValue","SelectContent","jsx","jsxs","Label","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Input","Switch","Textarea","Fragment","jsx","jsxs","Textarea","Checkbox","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","RadioGroup","RadioGroupItem","Label","Input","useState","useMemo","Card","Tabs","TabsList","TabsTrigger","Button","Badge","Fragment","jsx","jsxs","useState","useEffect","FORM_FIELD_TYPE_CATALOG","useRef","useCallback","Card","FieldShell","Input","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Textarea","Tabs","TabsList","TabsTrigger","Button"]}