@actuate-media/cms-admin 0.21.0 → 0.22.0
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.
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +24 -0
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/layout/sidebar-forms-badge.render.test.d.ts +2 -0
- package/dist/__tests__/layout/sidebar-forms-badge.render.test.d.ts.map +1 -0
- package/dist/__tests__/layout/sidebar-forms-badge.render.test.js +30 -0
- package/dist/__tests__/layout/sidebar-forms-badge.render.test.js.map +1 -0
- package/dist/__tests__/layout/sidebar-submenu.render.test.d.ts +2 -0
- package/dist/__tests__/layout/sidebar-submenu.render.test.d.ts.map +1 -0
- package/dist/__tests__/layout/sidebar-submenu.render.test.js +66 -0
- package/dist/__tests__/layout/sidebar-submenu.render.test.js.map +1 -0
- package/dist/__tests__/views/forms-list.render.test.d.ts +2 -0
- package/dist/__tests__/views/forms-list.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/forms-list.render.test.js +122 -0
- package/dist/__tests__/views/forms-list.render.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/forms/Drawer.d.ts +13 -0
- package/dist/components/forms/Drawer.d.ts.map +1 -0
- package/dist/components/forms/Drawer.js +13 -0
- package/dist/components/forms/Drawer.js.map +1 -0
- package/dist/components/forms/EmbedPanel.d.ts +7 -0
- package/dist/components/forms/EmbedPanel.d.ts.map +1 -0
- package/dist/components/forms/EmbedPanel.js +91 -0
- package/dist/components/forms/EmbedPanel.js.map +1 -0
- package/dist/components/forms/FieldsPanel.d.ts +8 -0
- package/dist/components/forms/FieldsPanel.d.ts.map +1 -0
- package/dist/components/forms/FieldsPanel.js +123 -0
- package/dist/components/forms/FieldsPanel.js.map +1 -0
- package/dist/components/forms/FormSchemaDrawer.d.ts +9 -0
- package/dist/components/forms/FormSchemaDrawer.d.ts.map +1 -0
- package/dist/components/forms/FormSchemaDrawer.js +96 -0
- package/dist/components/forms/FormSchemaDrawer.js.map +1 -0
- package/dist/components/forms/NotificationsPanel.d.ts +6 -0
- package/dist/components/forms/NotificationsPanel.d.ts.map +1 -0
- package/dist/components/forms/NotificationsPanel.js +80 -0
- package/dist/components/forms/NotificationsPanel.js.map +1 -0
- package/dist/components/forms/primitives.d.ts +42 -0
- package/dist/components/forms/primitives.d.ts.map +1 -0
- package/dist/components/forms/primitives.js +96 -0
- package/dist/components/forms/primitives.js.map +1 -0
- package/dist/layout/Sidebar.d.ts +5 -0
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +143 -10
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/lib/forms-events.d.ts +17 -0
- package/dist/lib/forms-events.d.ts.map +1 -0
- package/dist/lib/forms-events.js +20 -0
- package/dist/lib/forms-events.js.map +1 -0
- package/dist/lib/forms-service.d.ts +80 -0
- package/dist/lib/forms-service.d.ts.map +1 -0
- package/dist/lib/forms-service.js +144 -0
- package/dist/lib/forms-service.js.map +1 -0
- package/dist/views/Forms.d.ts.map +1 -1
- package/dist/views/Forms.js +119 -16
- package/dist/views/Forms.js.map +1 -1
- package/package.json +2 -2
- package/src/AdminRoot.tsx +22 -0
- package/src/__tests__/layout/sidebar-forms-badge.render.test.tsx +50 -0
- package/src/__tests__/layout/sidebar-submenu.render.test.tsx +93 -0
- package/src/__tests__/views/forms-list.render.test.tsx +141 -0
- package/src/components/forms/Drawer.tsx +70 -0
- package/src/components/forms/EmbedPanel.tsx +173 -0
- package/src/components/forms/FieldsPanel.tsx +385 -0
- package/src/components/forms/FormSchemaDrawer.tsx +185 -0
- package/src/components/forms/NotificationsPanel.tsx +240 -0
- package/src/components/forms/primitives.tsx +200 -0
- package/src/layout/Sidebar.tsx +295 -14
- package/src/lib/forms-events.ts +32 -0
- package/src/lib/forms-service.ts +244 -0
- package/src/views/Forms.tsx +343 -104
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Embed tab of the Form Schema drawer. Shows the public submission/schema
|
|
5
|
+
* endpoints and copy-paste embed snippets, and lets the editor restrict which
|
|
6
|
+
* origins may host the embedded form. Snippets are derived from the form slug;
|
|
7
|
+
* the live public endpoints land in a later phase (PR 5) but the paths shown
|
|
8
|
+
* here are the committed contract.
|
|
9
|
+
*/
|
|
10
|
+
import { Check, Copy } from 'lucide-react';
|
|
11
|
+
import { useEffect, useState } from 'react';
|
|
12
|
+
import { toast } from 'sonner';
|
|
13
|
+
import { fetchEmbedSettings, updateEmbedSettings, } from '../../lib/forms-service.js';
|
|
14
|
+
import { FormsErrorState, FormsLoading, btnSecondary } from './primitives.js';
|
|
15
|
+
function siteOrigin() {
|
|
16
|
+
if (typeof window !== 'undefined')
|
|
17
|
+
return window.location.origin;
|
|
18
|
+
return 'https://your-site.com';
|
|
19
|
+
}
|
|
20
|
+
export function EmbedPanel({ formId, slug, status, }) {
|
|
21
|
+
const [settings, setSettings] = useState(null);
|
|
22
|
+
const [loading, setLoading] = useState(true);
|
|
23
|
+
const [error, setError] = useState(null);
|
|
24
|
+
const [domains, setDomains] = useState('');
|
|
25
|
+
const [saving, setSaving] = useState(false);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
let active = true;
|
|
28
|
+
setLoading(true);
|
|
29
|
+
setError(null);
|
|
30
|
+
fetchEmbedSettings(formId)
|
|
31
|
+
.then((s) => {
|
|
32
|
+
if (!active)
|
|
33
|
+
return;
|
|
34
|
+
setSettings(s);
|
|
35
|
+
setDomains((s.allowedDomains ?? []).join('\n'));
|
|
36
|
+
})
|
|
37
|
+
.catch((e) => {
|
|
38
|
+
if (active)
|
|
39
|
+
setError(e instanceof Error ? e.message : 'Failed to load embed settings');
|
|
40
|
+
})
|
|
41
|
+
.finally(() => {
|
|
42
|
+
if (active)
|
|
43
|
+
setLoading(false);
|
|
44
|
+
});
|
|
45
|
+
return () => {
|
|
46
|
+
active = false;
|
|
47
|
+
};
|
|
48
|
+
}, [formId]);
|
|
49
|
+
const origin = siteOrigin();
|
|
50
|
+
const submitUrl = `${origin}/api/forms/${slug}/submit`;
|
|
51
|
+
const schemaUrl = `${origin}/api/forms/${slug}/schema`;
|
|
52
|
+
const embedUrl = `${origin}/forms/embed/${slug}`;
|
|
53
|
+
const iframeSnippet = `<iframe src="${embedUrl}" title="${slug}" style="width:100%;border:0;" loading="lazy"></iframe>`;
|
|
54
|
+
const scriptSnippet = `<div data-actuate-form="${slug}"></div>\n<script src="${origin}/api/forms/embed.js" async></script>`;
|
|
55
|
+
async function saveDomains() {
|
|
56
|
+
setSaving(true);
|
|
57
|
+
const list = domains
|
|
58
|
+
.split('\n')
|
|
59
|
+
.map((d) => d.trim())
|
|
60
|
+
.filter(Boolean);
|
|
61
|
+
const res = await updateEmbedSettings(formId, { allowedDomains: list });
|
|
62
|
+
setSaving(false);
|
|
63
|
+
if (res.error) {
|
|
64
|
+
toast.error(res.error);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (res.data)
|
|
68
|
+
setSettings(res.data);
|
|
69
|
+
toast.success('Embed settings saved.');
|
|
70
|
+
}
|
|
71
|
+
if (loading)
|
|
72
|
+
return _jsx(FormsLoading, { label: "Loading embed settings\u2026" });
|
|
73
|
+
if (error)
|
|
74
|
+
return _jsx(FormsErrorState, { message: error });
|
|
75
|
+
return (_jsxs("div", { className: "space-y-5", children: [status !== 'active' && (_jsxs("p", { className: "border-warning/30 bg-warning/10 text-warning rounded-md border px-3 py-2 text-sm", children: ["This form is ", _jsx("span", { className: "font-medium", children: status }), " and won't accept public submissions until it's set to Active."] })), _jsx(CopyField, { label: "Public submission endpoint", value: submitUrl }), _jsx(CopyField, { label: "Schema endpoint", value: schemaUrl }), _jsx(CopyField, { label: "Iframe embed", value: iframeSnippet, multiline: true }), _jsx(CopyField, { label: "Script embed", value: scriptSnippet, multiline: true }), _jsxs("div", { children: [_jsxs("label", { className: "text-foreground mb-1 block text-sm font-medium", children: ["Allowed domains", _jsx("span", { className: "text-muted-foreground ml-1 font-normal", children: "(one per line \u2014 leave empty to allow any)" })] }), _jsx("textarea", { rows: 3, value: domains, onChange: (e) => setDomains(e.target.value), placeholder: "example.com", className: "border-border bg-input-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 text-sm focus-visible:ring-2 focus-visible:outline-none" }), _jsx("div", { className: "mt-2 flex justify-end", children: _jsx("button", { className: btnSecondary, onClick: saveDomains, disabled: saving, children: saving ? 'Saving…' : 'Save domains' }) })] }), settings === null && null] }));
|
|
76
|
+
}
|
|
77
|
+
function CopyField({ label, value, multiline = false, }) {
|
|
78
|
+
const [copied, setCopied] = useState(false);
|
|
79
|
+
async function copy() {
|
|
80
|
+
try {
|
|
81
|
+
await navigator.clipboard.writeText(value);
|
|
82
|
+
setCopied(true);
|
|
83
|
+
setTimeout(() => setCopied(false), 1500);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
toast.error('Copy failed — your browser blocked clipboard access.');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return (_jsxs("div", { children: [_jsxs("div", { className: "mb-1 flex items-center justify-between", children: [_jsx("span", { className: "text-foreground text-sm font-medium", children: label }), _jsxs("button", { type: "button", onClick: copy, className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs font-medium focus-visible:ring-2 focus-visible:outline-none", "aria-label": `Copy ${label}`, children: [copied ? (_jsx(Check, { className: "h-3.5 w-3.5", "aria-hidden": true })) : (_jsx(Copy, { className: "h-3.5 w-3.5", "aria-hidden": true })), copied ? 'Copied' : 'Copy'] })] }), _jsx("pre", { className: `border-border bg-muted text-foreground overflow-x-auto rounded-md border px-2.5 py-2 font-mono text-xs ${multiline ? 'whitespace-pre-wrap' : 'whitespace-nowrap'}`, children: value })] }));
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=EmbedPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EmbedPanel.js","sourceRoot":"","sources":["../../../src/components/forms/EmbedPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FormField } from '../../lib/forms-service.js';
|
|
2
|
+
export declare function FieldsPanel({ fields, onChange, hasSubmissions, }: {
|
|
3
|
+
fields: FormField[];
|
|
4
|
+
onChange: (next: FormField[]) => void;
|
|
5
|
+
/** When the form already has submissions, key renames/deletes are riskier. */
|
|
6
|
+
hasSubmissions: boolean;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=FieldsPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldsPanel.d.ts","sourceRoot":"","sources":["../../../src/components/forms/FieldsPanel.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,4BAA4B,CAAA;AAqC1E,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,EACR,cAAc,GACf,EAAE;IACD,MAAM,EAAE,SAAS,EAAE,CAAA;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IACrC,8EAA8E;IAC9E,cAAc,EAAE,OAAO,CAAA;CACxB,2CAiFA"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Fields tab of the Form Schema drawer. Renders the form's field list with
|
|
5
|
+
* drag-to-reorder (dnd-kit, keyboard-accessible) plus an inline editor for the
|
|
6
|
+
* selected field. The parent owns the `fields` array and persistence; this
|
|
7
|
+
* panel is a controlled editor that calls `onChange` with the next array.
|
|
8
|
+
*/
|
|
9
|
+
import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors, } from '@dnd-kit/core';
|
|
10
|
+
import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from '@dnd-kit/sortable';
|
|
11
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
12
|
+
import { ChevronDown, GripVertical, Plus, Trash2 } from 'lucide-react';
|
|
13
|
+
import * as Switch from '@radix-ui/react-switch';
|
|
14
|
+
import { useId, useState } from 'react';
|
|
15
|
+
import { FieldTypeBadge, FormsEmptyState } from './primitives.js';
|
|
16
|
+
const FIELD_TYPES = [
|
|
17
|
+
{ value: 'text', label: 'Text' },
|
|
18
|
+
{ value: 'email', label: 'Email' },
|
|
19
|
+
{ value: 'phone', label: 'Phone' },
|
|
20
|
+
{ value: 'textarea', label: 'Textarea' },
|
|
21
|
+
{ value: 'select', label: 'Select' },
|
|
22
|
+
{ value: 'multiselect', label: 'Multi-select' },
|
|
23
|
+
{ value: 'radio', label: 'Radio' },
|
|
24
|
+
{ value: 'checkbox', label: 'Checkbox' },
|
|
25
|
+
{ value: 'date', label: 'Date' },
|
|
26
|
+
{ value: 'number', label: 'Number' },
|
|
27
|
+
{ value: 'url', label: 'URL' },
|
|
28
|
+
{ value: 'consent', label: 'Consent' },
|
|
29
|
+
{ value: 'file', label: 'File upload' },
|
|
30
|
+
{ value: 'hidden', label: 'Hidden' },
|
|
31
|
+
{ value: 'richtext', label: 'Content block' },
|
|
32
|
+
{ value: 'divider', label: 'Divider / heading' },
|
|
33
|
+
];
|
|
34
|
+
const OPTION_TYPES = ['select', 'multiselect', 'radio'];
|
|
35
|
+
function slugifyKey(label) {
|
|
36
|
+
return label
|
|
37
|
+
.toLowerCase()
|
|
38
|
+
.trim()
|
|
39
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
40
|
+
.replace(/^_+|_+$/g, '');
|
|
41
|
+
}
|
|
42
|
+
function makeId() {
|
|
43
|
+
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto)
|
|
44
|
+
return crypto.randomUUID();
|
|
45
|
+
return `f_${Math.random().toString(36).slice(2)}`;
|
|
46
|
+
}
|
|
47
|
+
export function FieldsPanel({ fields, onChange, hasSubmissions, }) {
|
|
48
|
+
const [editingId, setEditingId] = useState(null);
|
|
49
|
+
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 4 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
50
|
+
function handleDragEnd(event) {
|
|
51
|
+
const { active, over } = event;
|
|
52
|
+
if (!over || active.id === over.id)
|
|
53
|
+
return;
|
|
54
|
+
const ids = fields.map((f) => f.id);
|
|
55
|
+
const from = ids.indexOf(String(active.id));
|
|
56
|
+
const to = ids.indexOf(String(over.id));
|
|
57
|
+
if (from === -1 || to === -1)
|
|
58
|
+
return;
|
|
59
|
+
const next = fields.slice();
|
|
60
|
+
const [moved] = next.splice(from, 1);
|
|
61
|
+
next.splice(to, 0, moved);
|
|
62
|
+
onChange(next.map((f, i) => ({ ...f, sortOrder: i })));
|
|
63
|
+
}
|
|
64
|
+
function updateField(id, patch) {
|
|
65
|
+
onChange(fields.map((f) => (f.id === id ? { ...f, ...patch } : f)));
|
|
66
|
+
}
|
|
67
|
+
function deleteField(id) {
|
|
68
|
+
onChange(fields.filter((f) => f.id !== id));
|
|
69
|
+
if (editingId === id)
|
|
70
|
+
setEditingId(null);
|
|
71
|
+
}
|
|
72
|
+
function addField() {
|
|
73
|
+
const id = makeId();
|
|
74
|
+
const next = {
|
|
75
|
+
id,
|
|
76
|
+
label: 'New Field',
|
|
77
|
+
key: `field_${fields.length + 1}`,
|
|
78
|
+
type: 'text',
|
|
79
|
+
required: false,
|
|
80
|
+
sortOrder: fields.length,
|
|
81
|
+
};
|
|
82
|
+
onChange([...fields, next]);
|
|
83
|
+
setEditingId(id);
|
|
84
|
+
}
|
|
85
|
+
return (_jsxs("div", { className: "space-y-3", children: [fields.length === 0 ? (_jsx(FormsEmptyState, { title: "No fields yet", description: "Add your first field to start building this form." })) : (_jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: fields.map((f) => f.id), strategy: verticalListSortingStrategy, children: _jsx("ul", { className: "space-y-2", children: fields.map((field) => (_jsx(SortableFieldRow, { field: field, editing: editingId === field.id, hasSubmissions: hasSubmissions, onToggleEdit: () => setEditingId((cur) => (cur === field.id ? null : field.id)), onUpdate: (patch) => updateField(field.id, patch), onDelete: () => deleteField(field.id) }, field.id))) }) }) })), _jsxs("button", { type: "button", onClick: addField, className: "border-border text-foreground hover:bg-accent flex w-full items-center justify-center gap-2 rounded-lg border border-dashed px-3 py-2.5 text-sm font-medium transition-colors", children: [_jsx(Plus, { className: "h-4 w-4", "aria-hidden": true }), "Add Field"] })] }));
|
|
86
|
+
}
|
|
87
|
+
function SortableFieldRow({ field, editing, hasSubmissions, onToggleEdit, onUpdate, onDelete, }) {
|
|
88
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
|
89
|
+
id: field.id,
|
|
90
|
+
});
|
|
91
|
+
const style = {
|
|
92
|
+
transform: CSS.Transform.toString(transform),
|
|
93
|
+
transition,
|
|
94
|
+
zIndex: isDragging ? 10 : undefined,
|
|
95
|
+
};
|
|
96
|
+
const needsOptions = OPTION_TYPES.includes(field.type);
|
|
97
|
+
return (_jsx("li", { ref: setNodeRef, style: style, children: _jsxs("div", { className: `border-border bg-card rounded-lg border ${isDragging ? 'shadow-md' : ''} ${editing ? 'ring-primary/30 ring-1' : ''}`, children: [_jsxs("div", { className: "flex items-center gap-2 px-2.5 py-2", children: [_jsx("button", { type: "button", className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring shrink-0 cursor-grab touch-none rounded focus-visible:ring-2 focus-visible:outline-none", "aria-label": `Drag to reorder ${field.label}`, ...attributes, ...listeners, children: _jsx(GripVertical, { className: "h-4 w-4", "aria-hidden": true }) }), _jsx("button", { type: "button", onClick: onToggleEdit, className: "flex min-w-0 flex-1 items-center gap-2 text-left", "aria-expanded": editing, children: _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "text-foreground block truncate text-sm font-medium", children: field.label || '(untitled)' }), _jsx("span", { className: "text-muted-foreground block truncate font-mono text-xs", children: field.key })] }) }), _jsx(FieldTypeBadge, { type: field.type }), field.required && (_jsx("span", { className: "bg-muted text-muted-foreground rounded px-1.5 py-0.5 text-[10px] font-medium uppercase", children: "Required" })), _jsx("button", { type: "button", onClick: onToggleEdit, className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring shrink-0 rounded p-1 focus-visible:ring-2 focus-visible:outline-none", "aria-label": `${editing ? 'Collapse' : 'Edit'} ${field.label}`, children: _jsx(ChevronDown, { className: `h-4 w-4 transition-transform ${editing ? '' : '-rotate-90'}`, "aria-hidden": true }) }), _jsx("button", { type: "button", onClick: onDelete, className: "text-muted-foreground hover:text-destructive focus-visible:ring-ring shrink-0 rounded p-1 focus-visible:ring-2 focus-visible:outline-none", "aria-label": `Delete ${field.label}`, children: _jsx(Trash2, { className: "h-4 w-4", "aria-hidden": true }) })] }), editing && (_jsx(FieldEditor, { field: field, hasSubmissions: hasSubmissions, needsOptions: needsOptions, onUpdate: onUpdate }))] }) }));
|
|
98
|
+
}
|
|
99
|
+
function FieldEditor({ field, hasSubmissions, needsOptions, onUpdate, }) {
|
|
100
|
+
const reqId = useId();
|
|
101
|
+
const optionsText = (field.options ?? []).map((o) => o.label).join('\n');
|
|
102
|
+
return (_jsxs("div", { className: "border-border space-y-3 border-t px-3 py-3", children: [_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(Labeled, { label: "Label", children: _jsx("input", { type: "text", value: field.label, onChange: (e) => {
|
|
103
|
+
const label = e.target.value;
|
|
104
|
+
// Auto-derive the key from the label until the form has data and
|
|
105
|
+
// until the user has clearly customised the key.
|
|
106
|
+
const autoKey = slugifyKey(field.label);
|
|
107
|
+
const patch = { label };
|
|
108
|
+
if (!hasSubmissions && (field.key === autoKey || !field.key)) {
|
|
109
|
+
patch.key = slugifyKey(label);
|
|
110
|
+
}
|
|
111
|
+
onUpdate(patch);
|
|
112
|
+
}, className: "border-border bg-input-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 text-sm focus-visible:ring-2 focus-visible:outline-none" }) }), _jsx(Labeled, { label: "Key", hint: hasSubmissions ? 'Renaming affects existing entries & exports' : undefined, children: _jsx("input", { type: "text", value: field.key, onChange: (e) => onUpdate({ key: slugifyKey(e.target.value) }), className: "border-border bg-input-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 font-mono text-sm focus-visible:ring-2 focus-visible:outline-none" }) }), _jsx(Labeled, { label: "Type", children: _jsx("select", { value: field.type, onChange: (e) => onUpdate({ type: e.target.value }), className: "border-border bg-input-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 text-sm focus-visible:ring-2 focus-visible:outline-none", children: FIELD_TYPES.map((t) => (_jsx("option", { value: t.value, children: t.label }, t.value))) }) }), _jsx(Labeled, { label: "Placeholder", children: _jsx("input", { type: "text", value: field.placeholder ?? '', onChange: (e) => onUpdate({ placeholder: e.target.value }), className: "border-border bg-input-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 text-sm focus-visible:ring-2 focus-visible:outline-none" }) })] }), _jsx(Labeled, { label: "Help text", children: _jsx("input", { type: "text", value: field.helpText ?? '', onChange: (e) => onUpdate({ helpText: e.target.value }), className: "border-border bg-input-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 text-sm focus-visible:ring-2 focus-visible:outline-none" }) }), needsOptions && (_jsx(Labeled, { label: "Options", hint: "One per line", children: _jsx("textarea", { rows: 3, value: optionsText, onChange: (e) => onUpdate({
|
|
113
|
+
options: e.target.value
|
|
114
|
+
.split('\n')
|
|
115
|
+
.map((line) => line.trim())
|
|
116
|
+
.filter(Boolean)
|
|
117
|
+
.map((label) => ({ label, value: slugifyKey(label) })),
|
|
118
|
+
}), className: "border-border bg-input-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 text-sm focus-visible:ring-2 focus-visible:outline-none" }) })), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { htmlFor: reqId, className: "text-foreground text-sm font-medium", children: "Required" }), _jsx(Switch.Root, { id: reqId, checked: field.required, onCheckedChange: (v) => onUpdate({ required: v }), className: "bg-input-background data-[state=checked]:bg-primary focus-visible:ring-ring relative h-[18px] w-8 shrink-0 rounded-full transition-colors focus-visible:ring-2 focus-visible:outline-none", children: _jsx(Switch.Thumb, { className: "bg-background block h-3.5 w-3.5 translate-x-0.5 rounded-full shadow-sm transition-transform data-[state=checked]:translate-x-[14px]" }) })] })] }));
|
|
119
|
+
}
|
|
120
|
+
function Labeled({ label, hint, children, }) {
|
|
121
|
+
return (_jsxs("label", { className: "block", children: [_jsx("span", { className: "text-muted-foreground mb-1 block text-xs font-medium", children: label }), children, hint && _jsx("span", { className: "text-warning mt-1 block text-xs", children: hint })] }));
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=FieldsPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldsPanel.js","sourceRoot":"","sources":["../../../src/components/forms/FieldsPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FormListItem } from '../../lib/forms-service.js';
|
|
2
|
+
export declare function FormSchemaDrawer({ form, open, onOpenChange, onSaved, }: {
|
|
3
|
+
form: FormListItem | null;
|
|
4
|
+
open: boolean;
|
|
5
|
+
onOpenChange: (open: boolean) => void;
|
|
6
|
+
/** Called after a schema/settings change so the list can refetch. */
|
|
7
|
+
onSaved?: () => void;
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
9
|
+
//# sourceMappingURL=FormSchemaDrawer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormSchemaDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/forms/FormSchemaDrawer.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAe9D,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,IAAI,EACJ,YAAY,EACZ,OAAO,GACR,EAAE;IACD,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB,kDA+IA"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Form Schema drawer — the right-side panel opened from the Forms list "Schema"
|
|
5
|
+
* action. Three tabs: Fields (versioned schema editor), Embed (public snippets
|
|
6
|
+
* + domain restrictions), Notifications (per-form notification settings).
|
|
7
|
+
*
|
|
8
|
+
* Saving the Fields tab creates a new schema version server-side; existing
|
|
9
|
+
* submissions keep referencing the version they were captured against, so
|
|
10
|
+
* historical entries never break when the schema changes.
|
|
11
|
+
*/
|
|
12
|
+
import * as Tabs from '@radix-ui/react-tabs';
|
|
13
|
+
import { useEffect, useState } from 'react';
|
|
14
|
+
import { toast } from 'sonner';
|
|
15
|
+
import { fetchFormSchema, saveFormSchema } from '../../lib/forms-service.js';
|
|
16
|
+
import { emitFormsChanged } from '../../lib/forms-events.js';
|
|
17
|
+
import { ConfirmDialog } from '../ui/ConfirmDialog.js';
|
|
18
|
+
import { Drawer } from './Drawer.js';
|
|
19
|
+
import { FieldsPanel } from './FieldsPanel.js';
|
|
20
|
+
import { EmbedPanel } from './EmbedPanel.js';
|
|
21
|
+
import { NotificationsPanel } from './NotificationsPanel.js';
|
|
22
|
+
import { FormsErrorState, FormsLoading, btnPrimary, btnSecondary } from './primitives.js';
|
|
23
|
+
const TABS = [
|
|
24
|
+
{ id: 'fields', label: 'Fields' },
|
|
25
|
+
{ id: 'embed', label: 'Embed' },
|
|
26
|
+
{ id: 'notifications', label: 'Notifications' },
|
|
27
|
+
];
|
|
28
|
+
export function FormSchemaDrawer({ form, open, onOpenChange, onSaved, }) {
|
|
29
|
+
const [tab, setTab] = useState('fields');
|
|
30
|
+
const [fields, setFields] = useState([]);
|
|
31
|
+
const [loading, setLoading] = useState(false);
|
|
32
|
+
const [error, setError] = useState(null);
|
|
33
|
+
const [dirty, setDirty] = useState(false);
|
|
34
|
+
const [saving, setSaving] = useState(false);
|
|
35
|
+
const [confirmDiscard, setConfirmDiscard] = useState(false);
|
|
36
|
+
const formId = form?.id ?? null;
|
|
37
|
+
const hasSubmissions = (form?.totalEntries ?? 0) > 0;
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!open || !formId)
|
|
40
|
+
return;
|
|
41
|
+
setTab('fields');
|
|
42
|
+
setDirty(false);
|
|
43
|
+
setLoading(true);
|
|
44
|
+
setError(null);
|
|
45
|
+
let active = true;
|
|
46
|
+
fetchFormSchema(formId)
|
|
47
|
+
.then((schema) => {
|
|
48
|
+
if (active)
|
|
49
|
+
setFields(schema.fields);
|
|
50
|
+
})
|
|
51
|
+
.catch((e) => {
|
|
52
|
+
if (active)
|
|
53
|
+
setError(e instanceof Error ? e.message : 'Failed to load schema');
|
|
54
|
+
})
|
|
55
|
+
.finally(() => {
|
|
56
|
+
if (active)
|
|
57
|
+
setLoading(false);
|
|
58
|
+
});
|
|
59
|
+
return () => {
|
|
60
|
+
active = false;
|
|
61
|
+
};
|
|
62
|
+
}, [open, formId]);
|
|
63
|
+
function handleFieldsChange(next) {
|
|
64
|
+
setFields(next);
|
|
65
|
+
setDirty(true);
|
|
66
|
+
}
|
|
67
|
+
async function handleSave() {
|
|
68
|
+
if (!formId)
|
|
69
|
+
return;
|
|
70
|
+
setSaving(true);
|
|
71
|
+
const res = await saveFormSchema(formId, fields.map((f, i) => ({ ...f, sortOrder: i })));
|
|
72
|
+
setSaving(false);
|
|
73
|
+
if (res.error) {
|
|
74
|
+
toast.error(res.error);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setDirty(false);
|
|
78
|
+
toast.success('Schema saved.');
|
|
79
|
+
emitFormsChanged();
|
|
80
|
+
onSaved?.();
|
|
81
|
+
}
|
|
82
|
+
function requestClose(next) {
|
|
83
|
+
if (!next && dirty) {
|
|
84
|
+
setConfirmDiscard(true);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
onOpenChange(next);
|
|
88
|
+
}
|
|
89
|
+
if (!form)
|
|
90
|
+
return null;
|
|
91
|
+
return (_jsxs(_Fragment, { children: [_jsx(Drawer, { open: open, onOpenChange: requestClose, title: form.name, description: `/${form.slug}`, width: "max-w-2xl", footer: tab === 'fields' ? (_jsxs(_Fragment, { children: [_jsx("button", { className: btnSecondary, onClick: () => requestClose(false), children: "Close" }), _jsx("button", { className: btnPrimary, onClick: handleSave, disabled: saving || loading, children: saving ? 'Saving…' : 'Save Schema' })] })) : undefined, children: _jsxs(Tabs.Root, { value: tab, onValueChange: setTab, children: [_jsx(Tabs.List, { className: "border-border mb-4 flex gap-1 border-b", "aria-label": "Form schema sections", children: TABS.map((t) => (_jsx(Tabs.Trigger, { value: t.id, className: "text-muted-foreground hover:text-foreground data-[state=active]:text-foreground data-[state=active]:border-primary focus-visible:ring-ring -mb-px border-b-2 border-transparent px-3 py-2 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none", children: t.label }, t.id))) }), _jsx(Tabs.Content, { value: "fields", tabIndex: -1, children: loading ? (_jsx(FormsLoading, { label: "Loading schema\u2026" })) : error ? (_jsx(FormsErrorState, { message: error, onRetry: () => formId && setTab('fields') })) : (_jsx(FieldsPanel, { fields: fields, onChange: handleFieldsChange, hasSubmissions: hasSubmissions })) }), _jsx(Tabs.Content, { value: "embed", tabIndex: -1, children: formId && _jsx(EmbedPanel, { formId: formId, slug: form.slug, status: form.status }) }), _jsx(Tabs.Content, { value: "notifications", tabIndex: -1, children: formId && _jsx(NotificationsPanel, { formId: formId, onChanged: onSaved }) })] }) }), _jsx(ConfirmDialog, { open: confirmDiscard, onClose: () => setConfirmDiscard(false), onConfirm: () => {
|
|
92
|
+
setDirty(false);
|
|
93
|
+
onOpenChange(false);
|
|
94
|
+
}, title: "Discard unsaved changes?", description: "You have unsaved schema changes. Closing now will discard them.", confirmLabel: "Discard", cancelLabel: "Keep editing", destructive: true })] }));
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=FormSchemaDrawer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormSchemaDrawer.js","sourceRoot":"","sources":["../../../src/components/forms/FormSchemaDrawer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function NotificationsPanel({ formId, onChanged, }: {
|
|
2
|
+
formId: string;
|
|
3
|
+
/** Called after a successful save so the list can refresh the notify state. */
|
|
4
|
+
onChanged?: () => void;
|
|
5
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
6
|
+
//# sourceMappingURL=NotificationsPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationsPanel.d.ts","sourceRoot":"","sources":["../../../src/components/forms/NotificationsPanel.tsx"],"names":[],"mappings":"AAsBA,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,SAAS,GACV,EAAE;IACD,MAAM,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB,kDAmKA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Notifications tab of the Form Schema drawer. Persists per-form notification
|
|
5
|
+
* settings (recipients, reply-to, subject/message templates, field inclusion,
|
|
6
|
+
* autoresponder). Actual email/webhook delivery + the "send test" action are
|
|
7
|
+
* wired in a later phase (PR 6); this panel owns the configuration only.
|
|
8
|
+
*/
|
|
9
|
+
import { useEffect, useState } from 'react';
|
|
10
|
+
import * as Switch from '@radix-ui/react-switch';
|
|
11
|
+
import { toast } from 'sonner';
|
|
12
|
+
import { fetchNotificationSettings, updateNotificationSettings, } from '../../lib/forms-service.js';
|
|
13
|
+
import { emitFormsChanged } from '../../lib/forms-events.js';
|
|
14
|
+
import { FormsErrorState, FormsLoading, btnPrimary } from './primitives.js';
|
|
15
|
+
const inputCls = 'border-border bg-input-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 text-sm focus-visible:ring-2 focus-visible:outline-none';
|
|
16
|
+
export function NotificationsPanel({ formId, onChanged, }) {
|
|
17
|
+
const [settings, setSettings] = useState(null);
|
|
18
|
+
const [loading, setLoading] = useState(true);
|
|
19
|
+
const [error, setError] = useState(null);
|
|
20
|
+
const [saving, setSaving] = useState(false);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
let active = true;
|
|
23
|
+
setLoading(true);
|
|
24
|
+
setError(null);
|
|
25
|
+
fetchNotificationSettings(formId)
|
|
26
|
+
.then((s) => {
|
|
27
|
+
if (active)
|
|
28
|
+
setSettings(s);
|
|
29
|
+
})
|
|
30
|
+
.catch((e) => {
|
|
31
|
+
if (active)
|
|
32
|
+
setError(e instanceof Error ? e.message : 'Failed to load notification settings');
|
|
33
|
+
})
|
|
34
|
+
.finally(() => {
|
|
35
|
+
if (active)
|
|
36
|
+
setLoading(false);
|
|
37
|
+
});
|
|
38
|
+
return () => {
|
|
39
|
+
active = false;
|
|
40
|
+
};
|
|
41
|
+
}, [formId]);
|
|
42
|
+
function patch(p) {
|
|
43
|
+
setSettings((cur) => (cur ? { ...cur, ...p } : cur));
|
|
44
|
+
}
|
|
45
|
+
async function save() {
|
|
46
|
+
if (!settings)
|
|
47
|
+
return;
|
|
48
|
+
setSaving(true);
|
|
49
|
+
const res = await updateNotificationSettings(formId, settings);
|
|
50
|
+
setSaving(false);
|
|
51
|
+
if (res.error) {
|
|
52
|
+
toast.error(res.error);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (res.data)
|
|
56
|
+
setSettings(res.data);
|
|
57
|
+
toast.success('Notification settings saved.');
|
|
58
|
+
emitFormsChanged();
|
|
59
|
+
onChanged?.();
|
|
60
|
+
}
|
|
61
|
+
if (loading)
|
|
62
|
+
return _jsx(FormsLoading, { label: "Loading notification settings\u2026" });
|
|
63
|
+
if (error)
|
|
64
|
+
return _jsx(FormsErrorState, { message: error });
|
|
65
|
+
if (!settings)
|
|
66
|
+
return null;
|
|
67
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(ToggleRow, { label: "Enable notifications", help: "Email the recipients below when a new entry arrives.", checked: settings.enabled, onChange: (v) => patch({ enabled: v }) }), _jsxs("fieldset", { disabled: !settings.enabled, className: "space-y-4 transition-opacity disabled:opacity-50", children: [_jsx(Field, { label: "Recipients", hint: "Comma-separated email addresses", children: _jsx("input", { type: "text", value: settings.recipients.join(', '), onChange: (e) => patch({
|
|
68
|
+
recipients: e.target.value
|
|
69
|
+
.split(',')
|
|
70
|
+
.map((s) => s.trim())
|
|
71
|
+
.filter(Boolean),
|
|
72
|
+
}), placeholder: "team@example.com", className: inputCls }) }), _jsx(Field, { label: "Reply-to field key", hint: "Field whose value is used as the reply-to address", children: _jsx("input", { type: "text", value: settings.replyToFieldKey ?? '', onChange: (e) => patch({ replyToFieldKey: e.target.value || null }), placeholder: "email", className: inputCls }) }), _jsx(Field, { label: "Subject template", children: _jsx("input", { type: "text", value: settings.subjectTemplate ?? '', onChange: (e) => patch({ subjectTemplate: e.target.value }), placeholder: "New submission from {{name}}", className: inputCls }) }), _jsx(Field, { label: "Message template", children: _jsx("textarea", { rows: 3, value: settings.messageTemplate ?? '', onChange: (e) => patch({ messageTemplate: e.target.value }), className: inputCls }) }), _jsx(ToggleRow, { label: "Include all fields", help: "Append every submitted field to the notification body.", checked: settings.includeFields ?? true, onChange: (v) => patch({ includeFields: v }) }), _jsx(ToggleRow, { label: "Exclude sensitive fields", help: "Omit fields marked as sensitive/PII from the email.", checked: settings.excludeSensitiveFields ?? false, onChange: (v) => patch({ excludeSensitiveFields: v }) }), _jsxs("div", { className: "border-border border-t pt-4", children: [_jsx(ToggleRow, { label: "Auto-response email", help: "Send a confirmation email back to the submitter.", checked: settings.autoresponderEnabled ?? false, onChange: (v) => patch({ autoresponderEnabled: v }) }), settings.autoresponderEnabled && (_jsxs("div", { className: "mt-3 space-y-3", children: [_jsx(Field, { label: "Auto-response subject", children: _jsx("input", { type: "text", value: settings.autoresponderSubject ?? '', onChange: (e) => patch({ autoresponderSubject: e.target.value }), className: inputCls }) }), _jsx(Field, { label: "Auto-response body", children: _jsx("textarea", { rows: 3, value: settings.autoresponderBody ?? '', onChange: (e) => patch({ autoresponderBody: e.target.value }), className: inputCls }) })] }))] })] }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Email & webhook delivery (including the test action) is wired in an upcoming release. Settings saved here apply automatically once delivery is enabled." }), _jsx("div", { className: "flex justify-end", children: _jsx("button", { className: btnPrimary, onClick: save, disabled: saving, children: saving ? 'Saving…' : 'Save notifications' }) })] }));
|
|
73
|
+
}
|
|
74
|
+
function ToggleRow({ label, help, checked, onChange, }) {
|
|
75
|
+
return (_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "text-foreground block text-sm font-medium", children: label }), _jsx("span", { className: "text-muted-foreground block text-sm", children: help })] }), _jsx(Switch.Root, { checked: checked, onCheckedChange: onChange, "aria-label": label, className: "bg-input-background data-[state=checked]:bg-primary focus-visible:ring-ring relative h-[18px] w-8 shrink-0 rounded-full transition-colors focus-visible:ring-2 focus-visible:outline-none", children: _jsx(Switch.Thumb, { className: "bg-background block h-3.5 w-3.5 translate-x-0.5 rounded-full shadow-sm transition-transform data-[state=checked]:translate-x-[14px]" }) })] }));
|
|
76
|
+
}
|
|
77
|
+
function Field({ label, hint, children, }) {
|
|
78
|
+
return (_jsxs("label", { className: "block", children: [_jsx("span", { className: "text-foreground mb-1 block text-sm font-medium", children: label }), children, hint && _jsx("span", { className: "text-muted-foreground mt-1 block text-xs", children: hint })] }));
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=NotificationsPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationsPanel.js","sourceRoot":"","sources":["../../../src/components/forms/NotificationsPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Forms UI primitives — token-only, accessible building blocks used
|
|
3
|
+
* across the Forms list, schema drawer, and (later) the entries views. No raw
|
|
4
|
+
* palette colours: field-type and status hues map onto the theme's semantic
|
|
5
|
+
* tokens (success / warning / info / destructive / primary / muted) so they
|
|
6
|
+
* track light/dark mode and brand overrides automatically.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReactNode } from 'react';
|
|
9
|
+
import type { FormFieldType, FormStatus } from '../../lib/forms-service.js';
|
|
10
|
+
export declare function FieldTypeBadge({ type }: {
|
|
11
|
+
type: FormFieldType;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function FormStatusBadge({ status }: {
|
|
14
|
+
status: FormStatus;
|
|
15
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
/** Small red count badge for unread submissions. Hidden when count is 0. */
|
|
17
|
+
export declare function UnreadBadge({ count, className }: {
|
|
18
|
+
count: number;
|
|
19
|
+
className?: string;
|
|
20
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
21
|
+
export declare function SummaryChip({ value, label, tone, }: {
|
|
22
|
+
value: ReactNode;
|
|
23
|
+
label: string;
|
|
24
|
+
tone?: 'neutral' | 'success' | 'warning' | 'destructive';
|
|
25
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function FormsLoading({ label }: {
|
|
27
|
+
label?: string;
|
|
28
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
export declare function FormsEmptyState({ icon, title, description, action, }: {
|
|
30
|
+
icon?: ReactNode;
|
|
31
|
+
title: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
action?: ReactNode;
|
|
34
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
export declare function FormsErrorState({ message, onRetry }: {
|
|
36
|
+
message: string;
|
|
37
|
+
onRetry?: () => void;
|
|
38
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
export declare const btnPrimary = "inline-flex items-center justify-center gap-2 rounded-md bg-primary px-4 py-2 text-base font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-60";
|
|
40
|
+
export declare const btnSecondary = "inline-flex items-center justify-center gap-2 rounded-md border border-border bg-card px-4 py-2 text-base font-medium text-foreground transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-60";
|
|
41
|
+
export declare const btnGhostIcon = "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
42
|
+
//# sourceMappingURL=primitives.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/components/forms/primitives.tsx"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAiD3E,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,2CAQ/D;AAcD,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,2CAUjE;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,SAAc,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,kDAU3F;AAID,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,KAAK,EACL,IAAgB,GACjB,EAAE;IACD,KAAK,EAAE,SAAS,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAA;CACzD,2CAeA;AAID,wBAAgB,YAAY,CAAC,EAAE,KAAkB,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,2CAOtE;AAED,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,KAAK,EACL,WAAW,EACX,MAAM,GACP,EAAE;IACD,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB,2CAaA;AAED,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,2CAkB9F;AAID,eAAO,MAAM,UAAU,kQAC0O,CAAA;AAEjQ,eAAO,MAAM,YAAY,wQAC8O,CAAA;AAEvQ,eAAO,MAAM,YAAY,iNACuL,CAAA"}
|