strapi-plugin-form-builder-cms 1.0.0-alpha.1

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.
@@ -0,0 +1,1429 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const admin = require("@strapi/strapi/admin");
5
+ const reactRouterDom = require("react-router-dom");
6
+ const react = require("react");
7
+ const designSystem = require("@strapi/design-system");
8
+ const icons = require("@strapi/icons");
9
+ const index = require("./index-DtKNB8Gg.js");
10
+ const uuid = require("uuid");
11
+ const core = require("@dnd-kit/core");
12
+ const sortable = require("@dnd-kit/sortable");
13
+ const utilities = require("@dnd-kit/utilities");
14
+ const BASE = "/strapi-plugin-form-builder-cms";
15
+ function useFormsApi() {
16
+ const { get, post, put, del } = admin.useFetchClient();
17
+ return {
18
+ async getForms() {
19
+ const { data } = await get(`${BASE}/forms`);
20
+ return data;
21
+ },
22
+ async getForm(id) {
23
+ const { data } = await get(`${BASE}/forms/${id}`);
24
+ return data;
25
+ },
26
+ async createForm(body) {
27
+ const { data } = await post(`${BASE}/forms`, body);
28
+ return data;
29
+ },
30
+ async updateForm(id, body) {
31
+ const { data } = await put(`${BASE}/forms/${id}`, body);
32
+ return data;
33
+ },
34
+ async deleteForm(id) {
35
+ await del(`${BASE}/forms/${id}`);
36
+ },
37
+ async duplicateForm(id) {
38
+ const { data } = await post(`${BASE}/forms/${id}/duplicate`, {});
39
+ return data;
40
+ },
41
+ async getSubmissions(formId, query = {}) {
42
+ const params = new URLSearchParams(query).toString();
43
+ const { data } = await get(`${BASE}/submissions/${formId}${params ? `?${params}` : ""}`);
44
+ return data;
45
+ },
46
+ async getSubmission(id) {
47
+ const { data } = await get(`${BASE}/submissions/entry/${id}`);
48
+ return data;
49
+ },
50
+ async updateSubmissionStatus(id, status) {
51
+ const { data } = await put(`${BASE}/submissions/${id}/status`, { status });
52
+ return data;
53
+ },
54
+ async deleteSubmission(id) {
55
+ await del(`${BASE}/submissions/${id}`);
56
+ },
57
+ async getStats(formId) {
58
+ const { data } = await get(`${BASE}/submissions/${formId}/stats`);
59
+ return data;
60
+ }
61
+ };
62
+ }
63
+ function FormListPage() {
64
+ const navigate = reactRouterDom.useNavigate();
65
+ const api = useFormsApi();
66
+ const [forms, setForms] = react.useState([]);
67
+ const [loading, setLoading] = react.useState(true);
68
+ const [deleteTarget, setDeleteTarget] = react.useState(null);
69
+ const load = async () => {
70
+ setLoading(true);
71
+ try {
72
+ const data = await api.getForms();
73
+ setForms(Array.isArray(data) ? data : []);
74
+ } catch (e) {
75
+ console.error(e);
76
+ } finally {
77
+ setLoading(false);
78
+ }
79
+ };
80
+ react.useEffect(() => {
81
+ load();
82
+ }, []);
83
+ const handleDelete = async () => {
84
+ if (!deleteTarget) return;
85
+ await api.deleteForm(deleteTarget);
86
+ setDeleteTarget(null);
87
+ load();
88
+ };
89
+ const handleDuplicate = async (id) => {
90
+ await api.duplicateForm(id);
91
+ load();
92
+ };
93
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, children: [
94
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 6, children: [
95
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", children: "Form Builder" }),
96
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}), onClick: () => navigate(`/plugins/${index.PLUGIN_ID}/builder/new`), children: "Create form" })
97
+ ] }),
98
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: "Loading..." }) : forms.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 10, background: "neutral100", borderRadius: "4px", textAlign: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", textColor: "neutral600", children: "No forms yet. Create your first one." }) }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 5, rowCount: forms.length, children: [
99
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
100
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Title" }) }),
101
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Slug" }) }),
102
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Status" }) }),
103
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Created" }) }),
104
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Actions" }) })
105
+ ] }) }),
106
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: forms.map((form) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
107
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: form.title }) }),
108
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: form.slug }) }),
109
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { active: !!form.publishedAt, children: form.publishedAt ? "Published" : "Draft" }) }),
110
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: new Date(form.createdAt).toLocaleDateString() }) }),
111
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, children: [
112
+ /* @__PURE__ */ jsxRuntime.jsx(
113
+ designSystem.IconButton,
114
+ {
115
+ label: "Edit",
116
+ onClick: () => navigate(`/plugins/${index.PLUGIN_ID}/builder/${form.id}`),
117
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {})
118
+ }
119
+ ),
120
+ /* @__PURE__ */ jsxRuntime.jsx(
121
+ designSystem.IconButton,
122
+ {
123
+ label: "View submissions",
124
+ onClick: () => navigate(`/plugins/${index.PLUGIN_ID}/submissions/${form.id}`),
125
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {})
126
+ }
127
+ ),
128
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: "Duplicate", onClick: () => handleDuplicate(form.id), children: /* @__PURE__ */ jsxRuntime.jsx(icons.Duplicate, {}) }),
129
+ /* @__PURE__ */ jsxRuntime.jsx(
130
+ designSystem.IconButton,
131
+ {
132
+ label: "Delete",
133
+ onClick: () => setDeleteTarget(form.id),
134
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
135
+ }
136
+ )
137
+ ] }) })
138
+ ] }, form.id)) })
139
+ ] }),
140
+ deleteTarget && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: true, onOpenChange: () => setDeleteTarget(null), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
141
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: "Confirm delete" }),
142
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: "Are you sure you want to delete this form?" }) }),
143
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
144
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: "Cancel" }) }),
145
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "danger", onClick: handleDelete, children: "Delete" }) })
146
+ ] })
147
+ ] }) })
148
+ ] });
149
+ }
150
+ const PALETTE_ITEMS = [
151
+ // Basic
152
+ { type: "text", label: "Text", icon: "📝", group: "Basic" },
153
+ { type: "email", label: "Email", icon: "✉️", group: "Basic" },
154
+ { type: "number", label: "Number", icon: "#", group: "Basic" },
155
+ { type: "phone", label: "Phone", icon: "📱", group: "Basic" },
156
+ { type: "textarea", label: "Long text", icon: "📄", group: "Basic" },
157
+ { type: "password", label: "Password", icon: "🔒", group: "Basic" },
158
+ // Selection
159
+ { type: "select", label: "Select", icon: "▼", group: "Selection" },
160
+ { type: "radio", label: "Radio", icon: "◉", group: "Selection" },
161
+ { type: "checkbox", label: "Checkbox", icon: "☑", group: "Selection" },
162
+ { type: "checkbox-group", label: "Checkbox group", icon: "☑☑", group: "Selection" },
163
+ // Advanced
164
+ { type: "date", label: "Date", icon: "📅", group: "Advanced" },
165
+ { type: "time", label: "Time", icon: "🕐", group: "Advanced" },
166
+ { type: "url", label: "URL", icon: "🔗", group: "Advanced" },
167
+ { type: "hidden", label: "Hidden", icon: "👁", group: "Advanced" },
168
+ // Layout
169
+ { type: "heading", label: "Heading", icon: "H", group: "Layout" },
170
+ { type: "paragraph", label: "Paragraph", icon: "¶", group: "Layout" },
171
+ { type: "divider", label: "Divider", icon: "—", group: "Layout" }
172
+ ];
173
+ const GROUPS = ["Basic", "Selection", "Advanced", "Layout"];
174
+ function FieldPalette({ onAdd }) {
175
+ const [hovered, setHovered] = react.useState(null);
176
+ return /* @__PURE__ */ jsxRuntime.jsxs(
177
+ designSystem.Box,
178
+ {
179
+ padding: 4,
180
+ background: "neutral100",
181
+ style: { width: 200, minWidth: 200, overflowY: "auto", alignSelf: "stretch", borderRight: "1px solid var(--strapi-neutral-200)" },
182
+ children: [
183
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 3, children: "FIELDS" }),
184
+ GROUPS.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 4, children: [
185
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral500", marginBottom: 2, children: group }),
186
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 1, alignItems: "stretch", children: PALETTE_ITEMS.filter((i) => i.group === group).map((item) => /* @__PURE__ */ jsxRuntime.jsx(
187
+ designSystem.Box,
188
+ {
189
+ padding: 2,
190
+ background: hovered === item.type ? "primary100" : "neutral0",
191
+ hasRadius: true,
192
+ style: {
193
+ cursor: "pointer",
194
+ border: "1px solid var(--strapi-neutral-200)",
195
+ userSelect: "none"
196
+ },
197
+ onClick: () => onAdd(item.type),
198
+ onMouseEnter: () => setHovered(item.type),
199
+ onMouseLeave: () => setHovered(null),
200
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
201
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 14 }, children: item.icon }),
202
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: item.label })
203
+ ] })
204
+ },
205
+ item.type
206
+ )) })
207
+ ] }, group))
208
+ ]
209
+ }
210
+ );
211
+ }
212
+ function SortableFieldRow({
213
+ field,
214
+ selected,
215
+ onSelect,
216
+ onDelete
217
+ }) {
218
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = sortable.useSortable({ id: field.id });
219
+ const style = {
220
+ transform: utilities.CSS.Transform.toString(transform),
221
+ transition,
222
+ opacity: isDragging ? 0.5 : 1
223
+ };
224
+ return /* @__PURE__ */ jsxRuntime.jsx(
225
+ designSystem.Box,
226
+ {
227
+ ref: setNodeRef,
228
+ padding: 3,
229
+ background: selected ? "primary100" : "neutral0",
230
+ marginBottom: 2,
231
+ onClick: onSelect,
232
+ hasRadius: true,
233
+ style: {
234
+ ...style,
235
+ border: `2px solid ${selected ? "var(--strapi-primary-600)" : "transparent"}`,
236
+ outline: selected ? "none" : "1px solid var(--strapi-neutral-200)",
237
+ outlineOffset: "-1px",
238
+ cursor: "pointer"
239
+ },
240
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
241
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
242
+ /* @__PURE__ */ jsxRuntime.jsx(
243
+ designSystem.Box,
244
+ {
245
+ ...attributes,
246
+ ...listeners,
247
+ style: { cursor: "grab", color: "var(--strapi-neutral-400)", padding: "0 4px" },
248
+ onClick: (e) => e.stopPropagation(),
249
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Drag, {})
250
+ }
251
+ ),
252
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
253
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", children: field.label || "(no label)" }),
254
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: [
255
+ " ",
256
+ "— ",
257
+ field.type
258
+ ] })
259
+ ] })
260
+ ] }),
261
+ /* @__PURE__ */ jsxRuntime.jsx(
262
+ designSystem.IconButton,
263
+ {
264
+ label: "Delete field",
265
+ onClick: (e) => {
266
+ e.stopPropagation();
267
+ onDelete();
268
+ },
269
+ variant: "ghost",
270
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
271
+ }
272
+ )
273
+ ] })
274
+ }
275
+ );
276
+ }
277
+ function DropZone({ fields, selectedId, onSelect, onDelete, onReorder }) {
278
+ const sensors = core.useSensors(
279
+ core.useSensor(core.PointerSensor),
280
+ core.useSensor(core.KeyboardSensor, { coordinateGetter: sortable.sortableKeyboardCoordinates })
281
+ );
282
+ const handleDragEnd = (event) => {
283
+ const { active, over } = event;
284
+ if (over && active.id !== over.id) {
285
+ const oldIndex = fields.findIndex((f) => f.id === active.id);
286
+ const newIndex = fields.findIndex((f) => f.id === over.id);
287
+ onReorder(sortable.arrayMove(fields, oldIndex, newIndex));
288
+ }
289
+ };
290
+ if (fields.length === 0) {
291
+ return /* @__PURE__ */ jsxRuntime.jsx(
292
+ designSystem.Box,
293
+ {
294
+ padding: 10,
295
+ background: "neutral100",
296
+ hasRadius: true,
297
+ style: { border: "2px dashed var(--strapi-neutral-300)", textAlign: "center", flex: 1 },
298
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", children: "Click on a field in the left panel to add it to the form" })
299
+ }
300
+ );
301
+ }
302
+ return /* @__PURE__ */ jsxRuntime.jsx(core.DndContext, { sensors, collisionDetection: core.closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsxRuntime.jsx(sortable.SortableContext, { items: fields.map((f) => f.id), strategy: sortable.verticalListSortingStrategy, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
303
+ SortableFieldRow,
304
+ {
305
+ field,
306
+ selected: selectedId === field.id,
307
+ onSelect: () => onSelect(field.id),
308
+ onDelete: () => onDelete(field.id)
309
+ },
310
+ field.id
311
+ )) }) }) });
312
+ }
313
+ function LabeledInput({
314
+ label,
315
+ value,
316
+ onChange,
317
+ placeholder,
318
+ size
319
+ }) {
320
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
321
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: label }),
322
+ /* @__PURE__ */ jsxRuntime.jsx(
323
+ designSystem.TextInput,
324
+ {
325
+ value,
326
+ onChange: (e) => onChange(e.target.value),
327
+ placeholder,
328
+ size
329
+ }
330
+ )
331
+ ] });
332
+ }
333
+ function FieldSettingsPanel({ field, onChange }) {
334
+ const update = (patch) => onChange({ ...field, ...patch });
335
+ const addOption = () => {
336
+ const options = [...field.options || [], { label: "", value: "" }];
337
+ update({ options });
338
+ };
339
+ const updateOption = (index2, patch) => {
340
+ const options = (field.options || []).map(
341
+ (o, i) => i === index2 ? { ...o, ...patch } : o
342
+ );
343
+ update({ options });
344
+ };
345
+ const removeOption = (index2) => {
346
+ update({ options: (field.options || []).filter((_, i) => i !== index2) });
347
+ };
348
+ const addValidation = () => {
349
+ const usedTypes = field.validation.map((r) => r.type);
350
+ const defaultType = field.type === "number" ? usedTypes.includes("min") ? usedTypes.includes("max") ? "pattern" : "max" : "min" : field.type === "email" ? usedTypes.includes("email") ? "minLength" : "email" : field.type === "url" ? usedTypes.includes("url") ? "minLength" : "url" : usedTypes.includes("minLength") ? usedTypes.includes("maxLength") ? "pattern" : "maxLength" : "minLength";
351
+ update({ validation: [...field.validation, { type: defaultType }] });
352
+ };
353
+ const updateValidation = (index2, patch) => {
354
+ const validation = field.validation.map(
355
+ (v, i) => i === index2 ? { ...v, ...patch } : v
356
+ );
357
+ update({ validation });
358
+ };
359
+ const removeValidation = (index2) => {
360
+ update({ validation: field.validation.filter((_, i) => i !== index2) });
361
+ };
362
+ const hasOptions = ["select", "radio", "checkbox-group"].includes(field.type);
363
+ const isDecorative = ["heading", "paragraph", "divider"].includes(field.type);
364
+ return /* @__PURE__ */ jsxRuntime.jsxs(
365
+ designSystem.Box,
366
+ {
367
+ padding: 4,
368
+ background: "neutral0",
369
+ style: { width: 280, minWidth: 280, overflowY: "auto", alignSelf: "stretch", borderLeft: "1px solid var(--strapi-neutral-200)" },
370
+ children: [
371
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", marginBottom: 4, children: "Field settings" }),
372
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, alignItems: "stretch", children: [
373
+ !isDecorative && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
374
+ /* @__PURE__ */ jsxRuntime.jsx(
375
+ LabeledInput,
376
+ {
377
+ label: "Label",
378
+ value: field.label,
379
+ onChange: (v) => update({ label: v })
380
+ }
381
+ ),
382
+ /* @__PURE__ */ jsxRuntime.jsx(
383
+ LabeledInput,
384
+ {
385
+ label: "Name (technical)",
386
+ value: field.name,
387
+ onChange: (v) => update({ name: v })
388
+ }
389
+ ),
390
+ /* @__PURE__ */ jsxRuntime.jsx(
391
+ LabeledInput,
392
+ {
393
+ label: "Placeholder",
394
+ value: field.placeholder || "",
395
+ onChange: (v) => update({ placeholder: v })
396
+ }
397
+ ),
398
+ /* @__PURE__ */ jsxRuntime.jsx(
399
+ LabeledInput,
400
+ {
401
+ label: "Help text",
402
+ value: field.helpText || "",
403
+ onChange: (v) => update({ helpText: v })
404
+ }
405
+ ),
406
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
407
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", marginBottom: 1, children: "Required" }),
408
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
409
+ /* @__PURE__ */ jsxRuntime.jsx(
410
+ designSystem.Button,
411
+ {
412
+ variant: !field.required ? "default" : "secondary",
413
+ size: "S",
414
+ onClick: () => update({ required: false }),
415
+ children: "No"
416
+ }
417
+ ),
418
+ /* @__PURE__ */ jsxRuntime.jsx(
419
+ designSystem.Button,
420
+ {
421
+ variant: field.required ? "default" : "secondary",
422
+ size: "S",
423
+ onClick: () => update({ required: true }),
424
+ children: "Yes"
425
+ }
426
+ )
427
+ ] })
428
+ ] }),
429
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
430
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", marginBottom: 1, children: "Width" }),
431
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
432
+ /* @__PURE__ */ jsxRuntime.jsx(
433
+ designSystem.Button,
434
+ {
435
+ variant: field.width === "full" ? "default" : "secondary",
436
+ size: "S",
437
+ onClick: () => update({ width: "full" }),
438
+ children: "Full"
439
+ }
440
+ ),
441
+ /* @__PURE__ */ jsxRuntime.jsx(
442
+ designSystem.Button,
443
+ {
444
+ variant: field.width === "half" ? "default" : "secondary",
445
+ size: "S",
446
+ onClick: () => update({ width: "half" }),
447
+ children: "Half"
448
+ }
449
+ )
450
+ ] })
451
+ ] })
452
+ ] }),
453
+ field.type === "heading" && /* @__PURE__ */ jsxRuntime.jsx(
454
+ LabeledInput,
455
+ {
456
+ label: "Heading text",
457
+ value: field.label,
458
+ onChange: (v) => update({ label: v })
459
+ }
460
+ ),
461
+ field.type === "paragraph" && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
462
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Content" }),
463
+ /* @__PURE__ */ jsxRuntime.jsx(
464
+ designSystem.Textarea,
465
+ {
466
+ value: field.label,
467
+ onChange: (e) => update({ label: e.target.value })
468
+ }
469
+ )
470
+ ] }),
471
+ hasOptions && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
472
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 2, children: [
473
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", children: "Options" }),
474
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "secondary", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}), onClick: addOption, children: "Add" })
475
+ ] }),
476
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: (field.options || []).map((opt, i) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
477
+ /* @__PURE__ */ jsxRuntime.jsx(
478
+ designSystem.TextInput,
479
+ {
480
+ "aria-label": "Label",
481
+ placeholder: "Label",
482
+ value: opt.label,
483
+ onChange: (e) => updateOption(i, { label: e.target.value })
484
+ }
485
+ ),
486
+ /* @__PURE__ */ jsxRuntime.jsx(
487
+ designSystem.TextInput,
488
+ {
489
+ "aria-label": "Value",
490
+ placeholder: "Value",
491
+ value: opt.value,
492
+ onChange: (e) => updateOption(i, { value: e.target.value })
493
+ }
494
+ ),
495
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: "Delete", onClick: () => removeOption(i), variant: "ghost", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}) })
496
+ ] }, i)) })
497
+ ] }),
498
+ !isDecorative && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
499
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 2, children: [
500
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", children: "Validations" }),
501
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "secondary", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}), onClick: addValidation, children: "Add" })
502
+ ] }),
503
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: field.validation.map((rule, i) => {
504
+ const usedTypes = field.validation.filter((_, j) => j !== i).map((r) => r.type);
505
+ const isNumber = field.type === "number";
506
+ const isEmail = field.type === "email";
507
+ const isUrl = field.type === "url";
508
+ const available = [
509
+ !isNumber && { value: "minLength", label: "Min. length" },
510
+ !isNumber && { value: "maxLength", label: "Max. length" },
511
+ isNumber && { value: "min", label: "Min. value" },
512
+ isNumber && { value: "max", label: "Max. value" },
513
+ !isEmail && { value: "email", label: "Email" },
514
+ !isUrl && { value: "url", label: "URL" },
515
+ { value: "pattern", label: "Pattern (regex)" }
516
+ ].filter(
517
+ (opt) => !!opt && (opt.value === rule.type || !usedTypes.includes(opt.value))
518
+ );
519
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 2, background: "neutral100", hasRadius: true, children: [
520
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", marginBottom: 2, children: [
521
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
522
+ designSystem.SingleSelect,
523
+ {
524
+ value: rule.type,
525
+ onChange: (val) => updateValidation(i, { type: String(val), value: void 0, message: "" }),
526
+ size: "S",
527
+ children: available.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: opt.value, children: opt.label }, opt.value))
528
+ }
529
+ ) }),
530
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: "Delete", onClick: () => removeValidation(i), variant: "ghost", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}) })
531
+ ] }),
532
+ ["minLength", "maxLength", "min", "max", "pattern"].includes(rule.type) && /* @__PURE__ */ jsxRuntime.jsx(
533
+ LabeledInput,
534
+ {
535
+ label: "Value",
536
+ value: String(rule.value ?? ""),
537
+ onChange: (v) => updateValidation(i, { value: v }),
538
+ size: "S"
539
+ }
540
+ ),
541
+ /* @__PURE__ */ jsxRuntime.jsx(
542
+ LabeledInput,
543
+ {
544
+ label: "Error message",
545
+ value: rule.message || "",
546
+ onChange: (v) => updateValidation(i, { message: v }),
547
+ placeholder: "Custom message (optional)",
548
+ size: "S"
549
+ }
550
+ )
551
+ ] }, i);
552
+ }) })
553
+ ] }),
554
+ /* @__PURE__ */ jsxRuntime.jsx(
555
+ LabeledInput,
556
+ {
557
+ label: "CSS class",
558
+ value: field.cssClass || "",
559
+ onChange: (v) => update({ cssClass: v }),
560
+ placeholder: "my-custom-class"
561
+ }
562
+ )
563
+ ] })
564
+ ]
565
+ }
566
+ );
567
+ }
568
+ const TOKEN = {
569
+ border: "1px solid #dcdce4",
570
+ radius: 4,
571
+ accent: "#4945ff",
572
+ text: "#32324d",
573
+ sub: "#666687",
574
+ danger: "#ee5e52",
575
+ bg: "#fff"
576
+ };
577
+ const inputBase = {
578
+ width: "100%",
579
+ height: 40,
580
+ padding: "0 12px",
581
+ border: TOKEN.border,
582
+ borderRadius: TOKEN.radius,
583
+ fontSize: 14,
584
+ color: TOKEN.text,
585
+ background: TOKEN.bg,
586
+ boxSizing: "border-box",
587
+ outline: "none",
588
+ fontFamily: "inherit"
589
+ };
590
+ const fieldWrap = {
591
+ display: "flex",
592
+ flexDirection: "column",
593
+ gap: 4
594
+ };
595
+ const labelStyle = {
596
+ fontSize: 12,
597
+ fontWeight: 600,
598
+ color: TOKEN.text,
599
+ lineHeight: "16px"
600
+ };
601
+ const helpStyle = {
602
+ fontSize: 11,
603
+ color: TOKEN.sub,
604
+ margin: 0
605
+ };
606
+ function Label({ field }) {
607
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: labelStyle, children: [
608
+ field.label || /* @__PURE__ */ jsxRuntime.jsx("em", { style: { color: TOKEN.sub }, children: "No label" }),
609
+ field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: TOKEN.danger, marginLeft: 2 }, children: "*" })
610
+ ] });
611
+ }
612
+ function TextField({ field }) {
613
+ const typeMap = {
614
+ text: "text",
615
+ email: "email",
616
+ number: "number",
617
+ phone: "tel",
618
+ password: "password",
619
+ url: "url",
620
+ date: "date",
621
+ time: "time"
622
+ };
623
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldWrap, children: [
624
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { field }),
625
+ /* @__PURE__ */ jsxRuntime.jsx(
626
+ "input",
627
+ {
628
+ type: typeMap[field.type] ?? "text",
629
+ placeholder: field.placeholder,
630
+ style: inputBase,
631
+ readOnly: true
632
+ }
633
+ ),
634
+ field.helpText && /* @__PURE__ */ jsxRuntime.jsx("p", { style: helpStyle, children: field.helpText })
635
+ ] });
636
+ }
637
+ function TextareaField({ field }) {
638
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldWrap, children: [
639
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { field }),
640
+ /* @__PURE__ */ jsxRuntime.jsx(
641
+ "textarea",
642
+ {
643
+ placeholder: field.placeholder,
644
+ style: { ...inputBase, height: 88, padding: "8px 12px", resize: "vertical" },
645
+ readOnly: true
646
+ }
647
+ ),
648
+ field.helpText && /* @__PURE__ */ jsxRuntime.jsx("p", { style: helpStyle, children: field.helpText })
649
+ ] });
650
+ }
651
+ function SelectField({ field }) {
652
+ const [val, setVal] = react.useState("");
653
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldWrap, children: [
654
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { field }),
655
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
656
+ /* @__PURE__ */ jsxRuntime.jsxs(
657
+ "select",
658
+ {
659
+ value: val,
660
+ onChange: (e) => setVal(e.target.value),
661
+ style: {
662
+ ...inputBase,
663
+ appearance: "none",
664
+ paddingRight: 32,
665
+ cursor: "pointer"
666
+ },
667
+ children: [
668
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: field.placeholder || "Select an option…" }),
669
+ (field.options || []).map((o, i) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label || o.value }, i))
670
+ ]
671
+ }
672
+ ),
673
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
674
+ position: "absolute",
675
+ right: 10,
676
+ top: "50%",
677
+ transform: "translateY(-50%)",
678
+ pointerEvents: "none",
679
+ color: TOKEN.sub,
680
+ fontSize: 10
681
+ }, children: "▼" })
682
+ ] }),
683
+ field.helpText && /* @__PURE__ */ jsxRuntime.jsx("p", { style: helpStyle, children: field.helpText })
684
+ ] });
685
+ }
686
+ function RadioField({ field }) {
687
+ const [val, setVal] = react.useState("");
688
+ const options = field.options || [];
689
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldWrap, children: [
690
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { field }),
691
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 2 }, children: options.map((o, i) => {
692
+ const checked = val === o.value;
693
+ return /* @__PURE__ */ jsxRuntime.jsxs(
694
+ "label",
695
+ {
696
+ style: { display: "flex", alignItems: "center", gap: 10, cursor: "pointer", fontSize: 14, color: TOKEN.text, userSelect: "none" },
697
+ onClick: () => setVal(o.value),
698
+ children: [
699
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
700
+ width: 18,
701
+ height: 18,
702
+ borderRadius: "50%",
703
+ border: `2px solid ${checked ? TOKEN.accent : "#c0c0cf"}`,
704
+ background: TOKEN.bg,
705
+ display: "flex",
706
+ alignItems: "center",
707
+ justifyContent: "center",
708
+ flexShrink: 0,
709
+ transition: "border-color .15s"
710
+ }, children: checked && /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
711
+ width: 8,
712
+ height: 8,
713
+ borderRadius: "50%",
714
+ background: TOKEN.accent
715
+ } }) }),
716
+ o.label || o.value
717
+ ]
718
+ },
719
+ i
720
+ );
721
+ }) }),
722
+ field.helpText && /* @__PURE__ */ jsxRuntime.jsx("p", { style: helpStyle, children: field.helpText })
723
+ ] });
724
+ }
725
+ function CheckboxField({ field }) {
726
+ const [checked, setChecked] = react.useState(false);
727
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldWrap, children: [
728
+ /* @__PURE__ */ jsxRuntime.jsxs(
729
+ "label",
730
+ {
731
+ style: { display: "flex", alignItems: "center", gap: 10, cursor: "pointer", fontSize: 14, color: TOKEN.text, userSelect: "none" },
732
+ onClick: () => setChecked((v) => !v),
733
+ children: [
734
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
735
+ width: 18,
736
+ height: 18,
737
+ borderRadius: 3,
738
+ border: `2px solid ${checked ? TOKEN.accent : "#c0c0cf"}`,
739
+ background: checked ? TOKEN.accent : TOKEN.bg,
740
+ display: "flex",
741
+ alignItems: "center",
742
+ justifyContent: "center",
743
+ flexShrink: 0,
744
+ transition: "all .15s"
745
+ }, children: checked && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#fff", fontSize: 11, lineHeight: 1, fontWeight: 700 }, children: "✓" }) }),
746
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
747
+ field.label,
748
+ field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: TOKEN.danger, marginLeft: 2 }, children: "*" })
749
+ ] })
750
+ ]
751
+ }
752
+ ),
753
+ field.helpText && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { ...helpStyle, marginLeft: 28 }, children: field.helpText })
754
+ ] });
755
+ }
756
+ function CheckboxGroupField({ field }) {
757
+ const [vals, setVals] = react.useState(/* @__PURE__ */ new Set());
758
+ const options = field.options || [];
759
+ const toggle = (v) => setVals((prev) => {
760
+ const s = new Set(prev);
761
+ s.has(v) ? s.delete(v) : s.add(v);
762
+ return s;
763
+ });
764
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldWrap, children: [
765
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { field }),
766
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 2 }, children: options.map((o, i) => {
767
+ const checked = vals.has(o.value);
768
+ return /* @__PURE__ */ jsxRuntime.jsxs(
769
+ "label",
770
+ {
771
+ style: { display: "flex", alignItems: "center", gap: 10, cursor: "pointer", fontSize: 14, color: TOKEN.text, userSelect: "none" },
772
+ onClick: () => toggle(o.value),
773
+ children: [
774
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
775
+ width: 18,
776
+ height: 18,
777
+ borderRadius: 3,
778
+ border: `2px solid ${checked ? TOKEN.accent : "#c0c0cf"}`,
779
+ background: checked ? TOKEN.accent : TOKEN.bg,
780
+ display: "flex",
781
+ alignItems: "center",
782
+ justifyContent: "center",
783
+ flexShrink: 0,
784
+ transition: "all .15s"
785
+ }, children: checked && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#fff", fontSize: 11, lineHeight: 1, fontWeight: 700 }, children: "✓" }) }),
786
+ o.label || o.value
787
+ ]
788
+ },
789
+ i
790
+ );
791
+ }) }),
792
+ field.helpText && /* @__PURE__ */ jsxRuntime.jsx("p", { style: helpStyle, children: field.helpText })
793
+ ] });
794
+ }
795
+ function PreviewField({ field }) {
796
+ const half = field.width === "half";
797
+ const colSpan = half ? 1 : 2;
798
+ if (field.type === "hidden") return null;
799
+ if (field.type === "divider") {
800
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { gridColumn: "1 / -1" }, children: /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { border: "none", borderTop: TOKEN.border, margin: "4px 0" } }) });
801
+ }
802
+ if (field.type === "heading") {
803
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { gridColumn: "1 / -1" }, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: 18, fontWeight: 700, color: TOKEN.text, margin: 0 }, children: field.label || "Heading" }) });
804
+ }
805
+ if (field.type === "paragraph") {
806
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { gridColumn: "1 / -1" }, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: 14, color: TOKEN.sub, margin: 0, lineHeight: 1.6 }, children: field.label || "Paragraph text" }) });
807
+ }
808
+ let content;
809
+ switch (field.type) {
810
+ case "textarea":
811
+ content = /* @__PURE__ */ jsxRuntime.jsx(TextareaField, { field });
812
+ break;
813
+ case "select":
814
+ content = /* @__PURE__ */ jsxRuntime.jsx(SelectField, { field });
815
+ break;
816
+ case "radio":
817
+ content = /* @__PURE__ */ jsxRuntime.jsx(RadioField, { field });
818
+ break;
819
+ case "checkbox":
820
+ content = /* @__PURE__ */ jsxRuntime.jsx(CheckboxField, { field });
821
+ break;
822
+ case "checkbox-group":
823
+ content = /* @__PURE__ */ jsxRuntime.jsx(CheckboxGroupField, { field });
824
+ break;
825
+ default:
826
+ content = /* @__PURE__ */ jsxRuntime.jsx(TextField, { field });
827
+ break;
828
+ }
829
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { gridColumn: `span ${colSpan}` }, children: content });
830
+ }
831
+ function FormPreview({ title, fields, settings, open, onClose }) {
832
+ if (!open) return null;
833
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open, onOpenChange: (v) => !v && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { style: { maxWidth: 760, width: "100%" }, children: [
834
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "beta", children: [
835
+ "Preview — ",
836
+ title
837
+ ] }) }),
838
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, background: "neutral0", hasRadius: true, style: { border: TOKEN.border }, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: (e) => e.preventDefault(), children: [
839
+ fields.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { textAlign: "center", color: TOKEN.sub, padding: "32px 0", margin: 0 }, children: "No fields added yet." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(PreviewField, { field }, field.id)) }),
840
+ fields.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 24 }, children: /* @__PURE__ */ jsxRuntime.jsx(
841
+ "button",
842
+ {
843
+ type: "submit",
844
+ style: {
845
+ background: TOKEN.accent,
846
+ color: "#fff",
847
+ border: "none",
848
+ borderRadius: TOKEN.radius,
849
+ padding: "10px 24px",
850
+ fontSize: 14,
851
+ fontWeight: 600,
852
+ cursor: "pointer",
853
+ fontFamily: "inherit"
854
+ },
855
+ children: settings.submitButtonText || "Submit"
856
+ }
857
+ ) })
858
+ ] }) }) }),
859
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: onClose, children: "Close" }) })
860
+ ] }) });
861
+ }
862
+ function EmbedModal({ formId, open, onClose }) {
863
+ const [copied, setCopied] = react.useState(false);
864
+ if (!open) return null;
865
+ const origin = window.location.origin;
866
+ const snippet = `<div id="sfb-form-${formId}"></div>
867
+ <script
868
+ src="${origin}/api/strapi-plugin-form-builder-cms/embed.js"
869
+ data-form-id="${formId}"
870
+ async
871
+ ><\/script>`;
872
+ const copy = () => {
873
+ navigator.clipboard.writeText(snippet).then(() => {
874
+ setCopied(true);
875
+ setTimeout(() => setCopied(false), 2e3);
876
+ });
877
+ };
878
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open, onOpenChange: (v) => !v && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { style: { maxWidth: 600, width: "100%" }, children: [
879
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: "Embed this form" }) }),
880
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, children: [
881
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Paste this snippet into your website where you want the form to appear." }),
882
+ /* @__PURE__ */ jsxRuntime.jsx(
883
+ "pre",
884
+ {
885
+ style: {
886
+ margin: 0,
887
+ background: "#1e1e2e",
888
+ borderRadius: 6,
889
+ padding: "16px 20px",
890
+ overflowX: "auto",
891
+ whiteSpace: "pre",
892
+ color: "#cdd6f4",
893
+ fontFamily: "'Fira Code', 'Cascadia Code', 'Consolas', monospace",
894
+ fontSize: 13,
895
+ lineHeight: 1.7,
896
+ cursor: "text",
897
+ userSelect: "all"
898
+ },
899
+ onClick: (e) => {
900
+ const sel = window.getSelection();
901
+ const range = document.createRange();
902
+ range.selectNodeContents(e.currentTarget);
903
+ sel?.removeAllRanges();
904
+ sel?.addRange(range);
905
+ },
906
+ children: snippet
907
+ }
908
+ )
909
+ ] }) }),
910
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
911
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: onClose, children: "Cancel" }),
912
+ /* @__PURE__ */ jsxRuntime.jsx(
913
+ designSystem.Button,
914
+ {
915
+ startIcon: copied ? /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}) : /* @__PURE__ */ jsxRuntime.jsx(icons.Duplicate, {}),
916
+ onClick: copy,
917
+ variant: copied ? "success" : "default",
918
+ children: copied ? "Copied!" : "Copy"
919
+ }
920
+ )
921
+ ] })
922
+ ] }) });
923
+ }
924
+ const DEFAULT_SETTINGS = {
925
+ submitButtonText: "Submit",
926
+ successMessage: "Form submitted successfully",
927
+ enableHoneypot: true,
928
+ enableRateLimit: true,
929
+ maxSubmissionsPerHour: 60,
930
+ notificationEmails: [],
931
+ redirectUrl: "",
932
+ customCss: "",
933
+ publicPage: false
934
+ };
935
+ function createField(type, order) {
936
+ return {
937
+ id: uuid.v4(),
938
+ type,
939
+ name: `field_${Date.now()}`,
940
+ label: type.charAt(0).toUpperCase() + type.slice(1),
941
+ placeholder: "",
942
+ helpText: "",
943
+ required: false,
944
+ order,
945
+ width: "full",
946
+ options: ["select", "radio", "checkbox-group"].includes(type) ? [{ label: "Option 1", value: "option_1" }] : void 0,
947
+ validation: []
948
+ };
949
+ }
950
+ function FormBuilderPage() {
951
+ const { id } = reactRouterDom.useParams();
952
+ const navigate = reactRouterDom.useNavigate();
953
+ const api = useFormsApi();
954
+ const isNew = !id || id === "new";
955
+ const [loading, setLoading] = react.useState(!isNew);
956
+ const [saving, setSaving] = react.useState(false);
957
+ const [publishing, setPublishing] = react.useState(false);
958
+ const [title, setTitle] = react.useState("New form");
959
+ const [description, setDescription] = react.useState("");
960
+ const [fields, setFields] = react.useState([]);
961
+ const [settings, setSettings] = react.useState(DEFAULT_SETTINGS);
962
+ const [selectedFieldId, setSelectedFieldId] = react.useState(null);
963
+ const [showSettings, setShowSettings] = react.useState(false);
964
+ const [showPreview, setShowPreview] = react.useState(false);
965
+ const [showEmbed, setShowEmbed] = react.useState(false);
966
+ const [publishedAt, setPublishedAt] = react.useState(null);
967
+ const [slug, setSlug] = react.useState(null);
968
+ react.useEffect(() => {
969
+ if (!isNew && id) {
970
+ api.getForm(Number(id)).then((form) => {
971
+ setTitle(form.title);
972
+ setDescription(form.description || "");
973
+ setFields(form.fields || []);
974
+ setSettings({ ...DEFAULT_SETTINGS, ...form.settings || {} });
975
+ setPublishedAt(form.publishedAt ?? null);
976
+ setSlug(form.slug ?? null);
977
+ setLoading(false);
978
+ });
979
+ }
980
+ }, [id]);
981
+ const addField = react.useCallback((type) => {
982
+ const newField = createField(type, fields.length);
983
+ setFields((prev) => [...prev, newField]);
984
+ setSelectedFieldId(newField.id);
985
+ }, [fields.length]);
986
+ const updateField = react.useCallback((updated) => {
987
+ setFields((prev) => prev.map((f) => f.id === updated.id ? updated : f));
988
+ }, []);
989
+ const deleteField = react.useCallback((fieldId) => {
990
+ setFields((prev) => prev.filter((f) => f.id !== fieldId));
991
+ setSelectedFieldId((prev) => prev === fieldId ? null : prev);
992
+ }, []);
993
+ const reorderFields = react.useCallback((reordered) => {
994
+ setFields(reordered.map((f, i) => ({ ...f, order: i })));
995
+ }, []);
996
+ const saveDraft = async () => {
997
+ setSaving(true);
998
+ try {
999
+ const payload = { title, description, fields, settings, conditionalLogic: [], publishedAt: null };
1000
+ if (isNew) {
1001
+ const created = await api.createForm(payload);
1002
+ setPublishedAt(null);
1003
+ setSlug(created.slug ?? null);
1004
+ navigate(`/plugins/${index.PLUGIN_ID}/builder/${created.id}`, { replace: true });
1005
+ } else {
1006
+ const updated = await api.updateForm(Number(id), payload);
1007
+ setPublishedAt(updated.publishedAt ?? null);
1008
+ }
1009
+ } finally {
1010
+ setSaving(false);
1011
+ }
1012
+ };
1013
+ const publish = async () => {
1014
+ setPublishing(true);
1015
+ try {
1016
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1017
+ const payload = { title, description, fields, settings, conditionalLogic: [], publishedAt: now };
1018
+ if (isNew) {
1019
+ const created = await api.createForm(payload);
1020
+ setPublishedAt(created.publishedAt ?? now);
1021
+ navigate(`/plugins/${index.PLUGIN_ID}/builder/${created.id}`, { replace: true });
1022
+ } else {
1023
+ const updated = await api.updateForm(Number(id), payload);
1024
+ setPublishedAt(updated.publishedAt ?? now);
1025
+ }
1026
+ } finally {
1027
+ setPublishing(false);
1028
+ }
1029
+ };
1030
+ const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1031
+ if (loading) {
1032
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 10, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Loading form..." }) });
1033
+ }
1034
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { display: "flex", flexDirection: "column", height: "100vh" }, children: [
1035
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "neutral0", style: { borderBottom: "1px solid var(--strapi-neutral-200)" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
1036
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", children: [
1037
+ /* @__PURE__ */ jsxRuntime.jsx(
1038
+ designSystem.Button,
1039
+ {
1040
+ variant: "ghost",
1041
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}),
1042
+ onClick: () => navigate(`/plugins/${index.PLUGIN_ID}`),
1043
+ children: "Back"
1044
+ }
1045
+ ),
1046
+ /* @__PURE__ */ jsxRuntime.jsx(
1047
+ designSystem.TextInput,
1048
+ {
1049
+ "aria-label": "Form title",
1050
+ value: title,
1051
+ onChange: (e) => setTitle(e.target.value),
1052
+ style: { minWidth: 300, fontWeight: "bold", fontSize: 18 }
1053
+ }
1054
+ )
1055
+ ] }),
1056
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1057
+ /* @__PURE__ */ jsxRuntime.jsx(
1058
+ designSystem.Typography,
1059
+ {
1060
+ variant: "pi",
1061
+ textColor: publishedAt ? "success600" : "warning600",
1062
+ style: { fontWeight: 600 },
1063
+ children: publishedAt ? "Published" : "Draft"
1064
+ }
1065
+ ),
1066
+ !isNew && /* @__PURE__ */ jsxRuntime.jsx(
1067
+ designSystem.Button,
1068
+ {
1069
+ variant: "ghost",
1070
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Duplicate, {}),
1071
+ onClick: () => setShowEmbed(true),
1072
+ children: "Embed"
1073
+ }
1074
+ ),
1075
+ /* @__PURE__ */ jsxRuntime.jsx(
1076
+ designSystem.Button,
1077
+ {
1078
+ variant: "ghost",
1079
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {}),
1080
+ onClick: () => setShowPreview(true),
1081
+ children: "Preview"
1082
+ }
1083
+ ),
1084
+ /* @__PURE__ */ jsxRuntime.jsx(
1085
+ designSystem.Button,
1086
+ {
1087
+ variant: "secondary",
1088
+ onClick: () => setShowSettings((v) => !v),
1089
+ children: showSettings ? "Hide settings" : "Settings"
1090
+ }
1091
+ ),
1092
+ /* @__PURE__ */ jsxRuntime.jsx(
1093
+ designSystem.Button,
1094
+ {
1095
+ variant: "secondary",
1096
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {}),
1097
+ onClick: saveDraft,
1098
+ loading: saving,
1099
+ children: "Save draft"
1100
+ }
1101
+ ),
1102
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}), onClick: publish, loading: publishing, children: "Publish" })
1103
+ ] })
1104
+ ] }) }),
1105
+ showSettings && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "primary100", style: { borderBottom: "1px solid var(--strapi-primary-200)" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "flex-start", style: { flexWrap: "wrap" }, children: [
1106
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { minWidth: 300 }, children: [
1107
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Description" }),
1108
+ /* @__PURE__ */ jsxRuntime.jsx(
1109
+ designSystem.TextInput,
1110
+ {
1111
+ value: description,
1112
+ onChange: (e) => setDescription(e.target.value)
1113
+ }
1114
+ )
1115
+ ] }),
1116
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
1117
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Submit button" }),
1118
+ /* @__PURE__ */ jsxRuntime.jsx(
1119
+ designSystem.TextInput,
1120
+ {
1121
+ value: settings.submitButtonText,
1122
+ onChange: (e) => setSettings((s) => ({ ...s, submitButtonText: e.target.value }))
1123
+ }
1124
+ )
1125
+ ] }),
1126
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { minWidth: 300 }, children: [
1127
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Success message" }),
1128
+ /* @__PURE__ */ jsxRuntime.jsx(
1129
+ designSystem.TextInput,
1130
+ {
1131
+ value: settings.successMessage,
1132
+ onChange: (e) => setSettings((s) => ({ ...s, successMessage: e.target.value }))
1133
+ }
1134
+ )
1135
+ ] }),
1136
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "center", children: [
1137
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
1138
+ /* @__PURE__ */ jsxRuntime.jsx(
1139
+ designSystem.Toggle,
1140
+ {
1141
+ checked: settings.enableHoneypot,
1142
+ onChange: () => setSettings((s) => ({ ...s, enableHoneypot: !s.enableHoneypot })),
1143
+ onLabel: "Yes",
1144
+ offLabel: "No"
1145
+ }
1146
+ ),
1147
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: "Honeypot anti-spam" })
1148
+ ] }),
1149
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
1150
+ /* @__PURE__ */ jsxRuntime.jsx(
1151
+ designSystem.Toggle,
1152
+ {
1153
+ checked: settings.publicPage,
1154
+ onChange: () => setSettings((s) => ({ ...s, publicPage: !s.publicPage })),
1155
+ onLabel: "Yes",
1156
+ offLabel: "No"
1157
+ }
1158
+ ),
1159
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: "Public page" })
1160
+ ] })
1161
+ ] })
1162
+ ] }) }),
1163
+ settings.publicPage && slug && /* @__PURE__ */ jsxRuntime.jsx(
1164
+ designSystem.Box,
1165
+ {
1166
+ paddingTop: 2,
1167
+ paddingBottom: 2,
1168
+ paddingLeft: 4,
1169
+ paddingRight: 4,
1170
+ background: "success100",
1171
+ style: { borderBottom: "1px solid var(--strapi-success-200)", flexShrink: 0 },
1172
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
1173
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "success700", fontWeight: "semiBold", children: "Public URL:" }),
1174
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "success600", style: { fontFamily: "monospace" }, children: [
1175
+ window.location.origin,
1176
+ "/api/strapi-plugin-form-builder-cms/page/",
1177
+ slug
1178
+ ] }),
1179
+ /* @__PURE__ */ jsxRuntime.jsx(
1180
+ designSystem.Button,
1181
+ {
1182
+ variant: "ghost",
1183
+ size: "S",
1184
+ onClick: () => window.open(`${window.location.origin}/api/strapi-plugin-form-builder-cms/page/${slug}`, "_blank"),
1185
+ children: "Open"
1186
+ }
1187
+ )
1188
+ ] })
1189
+ }
1190
+ ),
1191
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { style: { flex: 1, minHeight: 0 }, children: [
1192
+ /* @__PURE__ */ jsxRuntime.jsx(FieldPalette, { onAdd: addField }),
1193
+ /* @__PURE__ */ jsxRuntime.jsx(
1194
+ designSystem.Box,
1195
+ {
1196
+ padding: 4,
1197
+ style: { flex: 1, overflowY: "auto", minWidth: 0, alignSelf: "stretch" },
1198
+ children: fields.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1199
+ designSystem.Box,
1200
+ {
1201
+ padding: 10,
1202
+ background: "neutral100",
1203
+ hasRadius: true,
1204
+ style: { border: "2px dashed var(--strapi-neutral-300)", textAlign: "center" },
1205
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", children: "Click on a field in the left panel to add it to the form" })
1206
+ }
1207
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1208
+ DropZone,
1209
+ {
1210
+ fields,
1211
+ selectedId: selectedFieldId,
1212
+ onSelect: setSelectedFieldId,
1213
+ onDelete: deleteField,
1214
+ onReorder: reorderFields
1215
+ }
1216
+ )
1217
+ }
1218
+ ),
1219
+ selectedField && /* @__PURE__ */ jsxRuntime.jsx(
1220
+ FieldSettingsPanel,
1221
+ {
1222
+ field: selectedField,
1223
+ onChange: updateField
1224
+ }
1225
+ )
1226
+ ] }),
1227
+ /* @__PURE__ */ jsxRuntime.jsx(
1228
+ FormPreview,
1229
+ {
1230
+ title,
1231
+ fields,
1232
+ settings,
1233
+ open: showPreview,
1234
+ onClose: () => setShowPreview(false)
1235
+ }
1236
+ ),
1237
+ !isNew && /* @__PURE__ */ jsxRuntime.jsx(
1238
+ EmbedModal,
1239
+ {
1240
+ formId: id,
1241
+ open: showEmbed,
1242
+ onClose: () => setShowEmbed(false)
1243
+ }
1244
+ )
1245
+ ] });
1246
+ }
1247
+ function SubmissionsPage() {
1248
+ const { formId } = reactRouterDom.useParams();
1249
+ const navigate = reactRouterDom.useNavigate();
1250
+ const api = useFormsApi();
1251
+ const [form, setForm] = react.useState(null);
1252
+ const [submissions, setSubmissions] = react.useState([]);
1253
+ const [stats, setStats] = react.useState(null);
1254
+ const [loading, setLoading] = react.useState(true);
1255
+ const [statusFilter, setStatusFilter] = react.useState("");
1256
+ const [selected, setSelected] = react.useState(null);
1257
+ const [deleteTarget, setDeleteTarget] = react.useState(null);
1258
+ const load = async () => {
1259
+ setLoading(true);
1260
+ const [formData, subData, statsData] = await Promise.all([
1261
+ api.getForm(Number(formId)),
1262
+ api.getSubmissions(Number(formId), statusFilter ? { status: statusFilter } : {}),
1263
+ api.getStats(Number(formId))
1264
+ ]);
1265
+ setForm(formData);
1266
+ setSubmissions(subData?.results || subData || []);
1267
+ setStats(statsData);
1268
+ setLoading(false);
1269
+ };
1270
+ react.useEffect(() => {
1271
+ load();
1272
+ }, [formId, statusFilter]);
1273
+ const dataFields = (form?.fields || []).filter(
1274
+ (f) => !["heading", "paragraph", "divider"].includes(f.type)
1275
+ );
1276
+ const handleDelete = async () => {
1277
+ if (!deleteTarget) return;
1278
+ await api.deleteSubmission(deleteTarget);
1279
+ setDeleteTarget(null);
1280
+ load();
1281
+ };
1282
+ const statusColor = {
1283
+ new: "success",
1284
+ read: "secondary",
1285
+ archived: "neutral"
1286
+ };
1287
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, children: [
1288
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 6, children: [
1289
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", children: [
1290
+ /* @__PURE__ */ jsxRuntime.jsx(
1291
+ designSystem.Button,
1292
+ {
1293
+ variant: "ghost",
1294
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}),
1295
+ onClick: () => navigate(`/plugins/${index.PLUGIN_ID}`),
1296
+ children: "Back"
1297
+ }
1298
+ ),
1299
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "alpha", children: [
1300
+ "Submissions — ",
1301
+ form?.title || "..."
1302
+ ] })
1303
+ ] }),
1304
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", children: [
1305
+ stats && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1306
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { children: [
1307
+ "Total: ",
1308
+ stats.total
1309
+ ] }),
1310
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { active: true, children: [
1311
+ "New: ",
1312
+ stats.byStatus?.new || 0
1313
+ ] })
1314
+ ] }),
1315
+ /* @__PURE__ */ jsxRuntime.jsxs(
1316
+ designSystem.SingleSelect,
1317
+ {
1318
+ "aria-label": "Filter by status",
1319
+ value: statusFilter,
1320
+ onChange: (val) => setStatusFilter(String(val)),
1321
+ placeholder: "All statuses",
1322
+ size: "S",
1323
+ children: [
1324
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "", children: "All" }),
1325
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "new", children: "New" }),
1326
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "read", children: "Read" }),
1327
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "archived", children: "Archived" })
1328
+ ]
1329
+ }
1330
+ )
1331
+ ] })
1332
+ ] }),
1333
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: "Loading..." }) : submissions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 10, background: "neutral100", borderRadius: "4px", style: { textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: "No submissions yet." }) }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: dataFields.length + 4, rowCount: submissions.length, children: [
1334
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1335
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "ID" }) }),
1336
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Status" }) }),
1337
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Date" }) }),
1338
+ dataFields.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: f.label }) }, f.id)),
1339
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Actions" }) })
1340
+ ] }) }),
1341
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: submissions.map((sub) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1342
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: sub.id }) }),
1343
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: statusColor[sub.status], children: sub.status }) }),
1344
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: new Date(sub.createdAt).toLocaleString() }) }),
1345
+ dataFields.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { style: { maxWidth: 150, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: String(sub.data?.[f.name] ?? "") }) }, f.id)),
1346
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, children: [
1347
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: "View details", onClick: () => setSelected(sub), children: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {}) }),
1348
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: "Delete", onClick: () => setDeleteTarget(sub.id), children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}) })
1349
+ ] }) })
1350
+ ] }, sub.id)) })
1351
+ ] }),
1352
+ selected && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: true, onOpenChange: () => setSelected(null), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
1353
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Header, { children: [
1354
+ "Submission #",
1355
+ selected.id
1356
+ ] }),
1357
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "left" }, children: [
1358
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
1359
+ display: "grid",
1360
+ gridTemplateColumns: "80px 1fr",
1361
+ rowGap: 10,
1362
+ columnGap: 16,
1363
+ background: "var(--strapi-neutral-100)",
1364
+ borderRadius: 6,
1365
+ padding: "12px 16px",
1366
+ marginBottom: 16
1367
+ }, children: [
1368
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", textColor: "neutral500", children: "Status" }),
1369
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: statusColor[selected.status], children: selected.status.toUpperCase() }),
1370
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", textColor: "neutral500", children: "Date" }),
1371
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: new Date(selected.createdAt).toLocaleString() }),
1372
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", textColor: "neutral500", children: "IP" }),
1373
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: selected.ipAddress || "—" })
1374
+ ] }),
1375
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral400", style: { display: "block", marginBottom: 8 }, children: "SUBMITTED DATA" }),
1376
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1377
+ border: "1px solid var(--strapi-neutral-200)",
1378
+ borderRadius: 6,
1379
+ overflow: "hidden"
1380
+ }, children: dataFields.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", style: { padding: "12px 16px", display: "block" }, children: "No data fields." }) : dataFields.map((f, i) => {
1381
+ const val = String(selected.data?.[f.name] ?? "");
1382
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
1383
+ display: "grid",
1384
+ gridTemplateColumns: "140px 1fr",
1385
+ columnGap: 16,
1386
+ padding: "10px 16px",
1387
+ background: i % 2 === 0 ? "#fff" : "var(--strapi-neutral-100)",
1388
+ alignItems: "start"
1389
+ }, children: [
1390
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", textColor: "neutral600", style: { whiteSpace: "nowrap" }, children: f.label }),
1391
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: val ? "neutral800" : "neutral400", children: val || "—" })
1392
+ ] }, f.id);
1393
+ }) })
1394
+ ] }) }),
1395
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1396
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: "Close" }) }),
1397
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(
1398
+ designSystem.Button,
1399
+ {
1400
+ onClick: async () => {
1401
+ await api.updateSubmissionStatus(selected.id, "read");
1402
+ setSelected(null);
1403
+ load();
1404
+ },
1405
+ children: "Mark as read"
1406
+ }
1407
+ ) })
1408
+ ] })
1409
+ ] }) }),
1410
+ deleteTarget && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: true, onOpenChange: () => setDeleteTarget(null), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
1411
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: "Confirm delete" }),
1412
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: "Delete this submission?" }) }),
1413
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1414
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: "Cancel" }) }),
1415
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "danger", onClick: handleDelete, children: "Delete" }) })
1416
+ ] })
1417
+ ] }) })
1418
+ ] });
1419
+ }
1420
+ const App = () => {
1421
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
1422
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(FormListPage, {}) }),
1423
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "builder/new", element: /* @__PURE__ */ jsxRuntime.jsx(FormBuilderPage, {}) }),
1424
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "builder/:id", element: /* @__PURE__ */ jsxRuntime.jsx(FormBuilderPage, {}) }),
1425
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "submissions/:formId", element: /* @__PURE__ */ jsxRuntime.jsx(SubmissionsPage, {}) }),
1426
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
1427
+ ] });
1428
+ };
1429
+ exports.App = App;