@squaredr/fieldcraft-pro 0.0.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/index.js ADDED
@@ -0,0 +1,4111 @@
1
+ 'use strict';
2
+
3
+ var fieldcraftProLicense = require('@squaredr/fieldcraft-pro-license');
4
+ var react = require('react');
5
+ var core = require('@dnd-kit/core');
6
+ var lucideReact = require('lucide-react');
7
+ var fieldcraftReact = require('@squaredr/fieldcraft-react');
8
+ var clsx = require('clsx');
9
+ var tailwindMerge = require('tailwind-merge');
10
+ var jsxRuntime = require('react/jsx-runtime');
11
+
12
+ // src/index.ts
13
+ var MAX_HISTORY = 50;
14
+ function useUndoRedo(currentSchema, setSchema) {
15
+ const historyRef = react.useRef([currentSchema]);
16
+ const [currentIndex, setCurrentIndex] = react.useState(0);
17
+ const canUndo = currentIndex > 0;
18
+ const canRedo = currentIndex < historyRef.current.length - 1;
19
+ const push = react.useCallback(
20
+ (schema) => {
21
+ historyRef.current = historyRef.current.slice(0, currentIndex + 1);
22
+ historyRef.current.push(schema);
23
+ if (historyRef.current.length > MAX_HISTORY) {
24
+ historyRef.current.shift();
25
+ setCurrentIndex(historyRef.current.length - 1);
26
+ } else {
27
+ setCurrentIndex((prev) => prev + 1);
28
+ }
29
+ },
30
+ [currentIndex]
31
+ );
32
+ const undo = react.useCallback(() => {
33
+ if (currentIndex > 0) {
34
+ const newIndex = currentIndex - 1;
35
+ setCurrentIndex(newIndex);
36
+ setSchema(historyRef.current[newIndex]);
37
+ }
38
+ }, [currentIndex, setSchema]);
39
+ const redo = react.useCallback(() => {
40
+ if (currentIndex < historyRef.current.length - 1) {
41
+ const newIndex = currentIndex + 1;
42
+ setCurrentIndex(newIndex);
43
+ setSchema(historyRef.current[newIndex]);
44
+ }
45
+ }, [currentIndex, setSchema]);
46
+ const clear = react.useCallback(() => {
47
+ historyRef.current = [currentSchema];
48
+ setCurrentIndex(0);
49
+ }, [currentSchema]);
50
+ return {
51
+ canUndo,
52
+ canRedo,
53
+ undo,
54
+ redo,
55
+ push,
56
+ clear
57
+ };
58
+ }
59
+
60
+ // src/form-builder/utils/id-generator.ts
61
+ var counter = 0;
62
+ function generateId(prefix) {
63
+ const timestamp = Date.now().toString(36);
64
+ const random = Math.random().toString(36).substring(2, 7);
65
+ counter = (counter + 1) % 1e4;
66
+ const count = counter.toString(36);
67
+ return `${prefix}_${timestamp}${count}${random}`;
68
+ }
69
+ function generateSectionId() {
70
+ return generateId("section");
71
+ }
72
+ function generateQuestionId() {
73
+ return generateId("question");
74
+ }
75
+ function generateOptionId() {
76
+ return generateId("option");
77
+ }
78
+
79
+ // src/form-builder/utils/schema-mutations.ts
80
+ function deepClone(obj) {
81
+ return structuredClone(obj);
82
+ }
83
+ function addSection(schema, section, index) {
84
+ const newSchema = deepClone(schema);
85
+ newSchema.sections.splice(index, 0, section);
86
+ return newSchema;
87
+ }
88
+ function removeSection(schema, sectionId) {
89
+ const newSchema = deepClone(schema);
90
+ newSchema.sections = newSchema.sections.filter((s) => s.id !== sectionId);
91
+ return newSchema;
92
+ }
93
+ function updateSection(schema, sectionId, updates) {
94
+ const newSchema = deepClone(schema);
95
+ const sectionIndex = newSchema.sections.findIndex((s) => s.id === sectionId);
96
+ if (sectionIndex === -1) return schema;
97
+ newSchema.sections[sectionIndex] = {
98
+ ...newSchema.sections[sectionIndex],
99
+ ...updates
100
+ };
101
+ return newSchema;
102
+ }
103
+ function moveSection(schema, sectionId, newIndex) {
104
+ const newSchema = deepClone(schema);
105
+ const oldIndex = newSchema.sections.findIndex((s) => s.id === sectionId);
106
+ if (oldIndex === -1) return schema;
107
+ const [section] = newSchema.sections.splice(oldIndex, 1);
108
+ newSchema.sections.splice(newIndex, 0, section);
109
+ return newSchema;
110
+ }
111
+ function duplicateSection(schema, sectionId) {
112
+ const newSchema = deepClone(schema);
113
+ const sectionIndex = newSchema.sections.findIndex((s) => s.id === sectionId);
114
+ if (sectionIndex === -1) return schema;
115
+ const original = newSchema.sections[sectionIndex];
116
+ const duplicate = {
117
+ ...original,
118
+ id: generateSectionId(),
119
+ title: `${original.title} (Copy)`,
120
+ questions: original.questions.map((q) => ({
121
+ ...q,
122
+ id: generateQuestionId()
123
+ }))
124
+ };
125
+ newSchema.sections.splice(sectionIndex + 1, 0, duplicate);
126
+ return newSchema;
127
+ }
128
+ function addQuestion(schema, sectionId, question, index) {
129
+ const newSchema = deepClone(schema);
130
+ const section = newSchema.sections.find((s) => s.id === sectionId);
131
+ if (!section) return schema;
132
+ section.questions.splice(index, 0, question);
133
+ return newSchema;
134
+ }
135
+ function removeQuestion(schema, sectionId, questionId) {
136
+ const newSchema = deepClone(schema);
137
+ const section = newSchema.sections.find((s) => s.id === sectionId);
138
+ if (!section) return schema;
139
+ section.questions = section.questions.filter((q) => q.id !== questionId);
140
+ return newSchema;
141
+ }
142
+ function updateQuestion(schema, sectionId, questionId, updates) {
143
+ const newSchema = deepClone(schema);
144
+ const section = newSchema.sections.find((s) => s.id === sectionId);
145
+ if (!section) return schema;
146
+ const questionIndex = section.questions.findIndex((q) => q.id === questionId);
147
+ if (questionIndex === -1) return schema;
148
+ section.questions[questionIndex] = {
149
+ ...section.questions[questionIndex],
150
+ ...updates
151
+ };
152
+ return newSchema;
153
+ }
154
+ function moveQuestion(schema, sectionId, questionId, targetSectionId, newIndex) {
155
+ const newSchema = deepClone(schema);
156
+ const sourceSection = newSchema.sections.find((s) => s.id === sectionId);
157
+ const targetSection = newSchema.sections.find((s) => s.id === targetSectionId);
158
+ if (!sourceSection || !targetSection) return schema;
159
+ const questionIndex = sourceSection.questions.findIndex((q) => q.id === questionId);
160
+ if (questionIndex === -1) return schema;
161
+ const [question] = sourceSection.questions.splice(questionIndex, 1);
162
+ targetSection.questions.splice(newIndex, 0, question);
163
+ return newSchema;
164
+ }
165
+ function duplicateQuestion(schema, sectionId, questionId) {
166
+ const newSchema = deepClone(schema);
167
+ const section = newSchema.sections.find((s) => s.id === sectionId);
168
+ if (!section) return schema;
169
+ const questionIndex = section.questions.findIndex((q) => q.id === questionId);
170
+ if (questionIndex === -1) return schema;
171
+ const original = section.questions[questionIndex];
172
+ const duplicate = {
173
+ ...original,
174
+ id: generateQuestionId(),
175
+ label: `${original.label} (Copy)`
176
+ };
177
+ section.questions.splice(questionIndex + 1, 0, duplicate);
178
+ return newSchema;
179
+ }
180
+ function addOption(schema, sectionId, questionId, option, index) {
181
+ const newSchema = deepClone(schema);
182
+ const section = newSchema.sections.find((s) => s.id === sectionId);
183
+ if (!section) return schema;
184
+ const question = section.questions.find((q) => q.id === questionId);
185
+ if (!question) return schema;
186
+ if (!question.options) question.options = [];
187
+ question.options.splice(index, 0, option);
188
+ return newSchema;
189
+ }
190
+ function removeOption(schema, sectionId, questionId, optionIndex) {
191
+ const newSchema = deepClone(schema);
192
+ const section = newSchema.sections.find((s) => s.id === sectionId);
193
+ if (!section) return schema;
194
+ const question = section.questions.find((q) => q.id === questionId);
195
+ if (!question || !question.options) return schema;
196
+ question.options.splice(optionIndex, 1);
197
+ return newSchema;
198
+ }
199
+ function updateOption(schema, sectionId, questionId, optionIndex, updates) {
200
+ const newSchema = deepClone(schema);
201
+ const section = newSchema.sections.find((s) => s.id === sectionId);
202
+ if (!section) return schema;
203
+ const question = section.questions.find((q) => q.id === questionId);
204
+ if (!question || !question.options) return schema;
205
+ question.options[optionIndex] = {
206
+ ...question.options[optionIndex],
207
+ ...updates
208
+ };
209
+ return newSchema;
210
+ }
211
+ function moveOption(schema, sectionId, questionId, oldIndex, newIndex) {
212
+ const newSchema = deepClone(schema);
213
+ const section = newSchema.sections.find((s) => s.id === sectionId);
214
+ if (!section) return schema;
215
+ const question = section.questions.find((q) => q.id === questionId);
216
+ if (!question || !question.options) return schema;
217
+ const [option] = question.options.splice(oldIndex, 1);
218
+ question.options.splice(newIndex, 0, option);
219
+ return newSchema;
220
+ }
221
+ function findQuestion(schema, sectionId, questionId) {
222
+ const section = schema.sections.find((s) => s.id === sectionId);
223
+ if (!section) return null;
224
+ const questionIndex = section.questions.findIndex((q) => q.id === questionId);
225
+ if (questionIndex === -1) return null;
226
+ return { section, question: section.questions[questionIndex], questionIndex };
227
+ }
228
+ function findSection(schema, sectionId) {
229
+ const sectionIndex = schema.sections.findIndex((s) => s.id === sectionId);
230
+ if (sectionIndex === -1) return null;
231
+ return { section: schema.sections[sectionIndex], sectionIndex };
232
+ }
233
+
234
+ // src/form-builder/hooks/use-builder-state.ts
235
+ function useBuilderState(initialSchema) {
236
+ const [schema, setSchema] = react.useState(initialSchema);
237
+ const [selectedItem, setSelectedItem] = react.useState(null);
238
+ const [isDirty, setIsDirty] = react.useState(false);
239
+ const schemaRef = react.useRef(schema);
240
+ schemaRef.current = schema;
241
+ const undoRedo = useUndoRedo(schema, (newSchema) => {
242
+ setSchema(newSchema);
243
+ setIsDirty(true);
244
+ });
245
+ const updateSchema = react.useCallback(
246
+ (newSchema) => {
247
+ setSchema(newSchema);
248
+ undoRedo.push(newSchema);
249
+ setIsDirty(true);
250
+ },
251
+ [undoRedo]
252
+ );
253
+ const applyMutation = react.useCallback(
254
+ (mutate) => {
255
+ const result = mutate(schemaRef.current);
256
+ updateSchema(result);
257
+ },
258
+ [updateSchema]
259
+ );
260
+ const addSection2 = react.useCallback(
261
+ (section, index) => {
262
+ applyMutation((s) => addSection(s, section, index));
263
+ },
264
+ [applyMutation]
265
+ );
266
+ const removeSection2 = react.useCallback(
267
+ (sectionId) => {
268
+ applyMutation((s) => removeSection(s, sectionId));
269
+ setSelectedItem((prev) => {
270
+ if (prev?.type === "section" && prev.sectionId === sectionId) return null;
271
+ return prev;
272
+ });
273
+ },
274
+ [applyMutation]
275
+ );
276
+ const updateSection2 = react.useCallback(
277
+ (sectionId, updates) => {
278
+ applyMutation((s) => updateSection(s, sectionId, updates));
279
+ },
280
+ [applyMutation]
281
+ );
282
+ const moveSection2 = react.useCallback(
283
+ (sectionId, newIndex) => {
284
+ applyMutation((s) => moveSection(s, sectionId, newIndex));
285
+ },
286
+ [applyMutation]
287
+ );
288
+ const duplicateSection2 = react.useCallback(
289
+ (sectionId) => {
290
+ applyMutation((s) => duplicateSection(s, sectionId));
291
+ },
292
+ [applyMutation]
293
+ );
294
+ const addQuestion2 = react.useCallback(
295
+ (sectionId, question, index) => {
296
+ applyMutation((s) => addQuestion(s, sectionId, question, index));
297
+ },
298
+ [applyMutation]
299
+ );
300
+ const removeQuestion2 = react.useCallback(
301
+ (sectionId, questionId) => {
302
+ applyMutation((s) => removeQuestion(s, sectionId, questionId));
303
+ setSelectedItem((prev) => {
304
+ if (prev?.type === "question" && prev.sectionId === sectionId && prev.questionId === questionId) {
305
+ return null;
306
+ }
307
+ return prev;
308
+ });
309
+ },
310
+ [applyMutation]
311
+ );
312
+ const updateQuestion2 = react.useCallback(
313
+ (sectionId, questionId, updates) => {
314
+ applyMutation((s) => updateQuestion(s, sectionId, questionId, updates));
315
+ },
316
+ [applyMutation]
317
+ );
318
+ const moveQuestion2 = react.useCallback(
319
+ (sectionId, questionId, targetSectionId, newIndex) => {
320
+ applyMutation((s) => moveQuestion(s, sectionId, questionId, targetSectionId, newIndex));
321
+ },
322
+ [applyMutation]
323
+ );
324
+ const duplicateQuestion2 = react.useCallback(
325
+ (sectionId, questionId) => {
326
+ applyMutation((s) => duplicateQuestion(s, sectionId, questionId));
327
+ },
328
+ [applyMutation]
329
+ );
330
+ const selectQuestion = react.useCallback((sectionId, questionId) => {
331
+ setSelectedItem({ type: "question", sectionId, questionId });
332
+ }, []);
333
+ const selectSection = react.useCallback((sectionId) => {
334
+ setSelectedItem({ type: "section", sectionId });
335
+ }, []);
336
+ const clearSelection = react.useCallback(() => {
337
+ setSelectedItem(null);
338
+ }, []);
339
+ const resetSchema = react.useCallback(
340
+ (newSchema) => {
341
+ setSchema(newSchema);
342
+ undoRedo.clear();
343
+ setIsDirty(false);
344
+ setSelectedItem(null);
345
+ },
346
+ [undoRedo]
347
+ );
348
+ const markClean = react.useCallback(() => {
349
+ setIsDirty(false);
350
+ }, []);
351
+ return {
352
+ // State
353
+ schema,
354
+ selectedItem,
355
+ isDirty,
356
+ // Schema-level mutation
357
+ updateSchema,
358
+ // Section operations
359
+ addSection: addSection2,
360
+ removeSection: removeSection2,
361
+ updateSection: updateSection2,
362
+ moveSection: moveSection2,
363
+ duplicateSection: duplicateSection2,
364
+ // Question operations
365
+ addQuestion: addQuestion2,
366
+ removeQuestion: removeQuestion2,
367
+ updateQuestion: updateQuestion2,
368
+ moveQuestion: moveQuestion2,
369
+ duplicateQuestion: duplicateQuestion2,
370
+ // Selection
371
+ selectQuestion,
372
+ selectSection,
373
+ clearSelection,
374
+ // Undo/Redo
375
+ canUndo: undoRedo.canUndo,
376
+ canRedo: undoRedo.canRedo,
377
+ undo: undoRedo.undo,
378
+ redo: undoRedo.redo,
379
+ // Reset
380
+ resetSchema,
381
+ markClean
382
+ };
383
+ }
384
+
385
+ // src/form-builder/constants.ts
386
+ var QUESTION_TYPE_INFO = {
387
+ // ── Text ──
388
+ short_text: {
389
+ type: "short_text",
390
+ label: "Short Text",
391
+ category: "text",
392
+ icon: "Type",
393
+ description: "Single-line text input",
394
+ defaultConfig: { type: "short_text", maxLength: 255 }
395
+ },
396
+ long_text: {
397
+ type: "long_text",
398
+ label: "Long Text",
399
+ category: "text",
400
+ icon: "AlignLeft",
401
+ description: "Multi-line text area",
402
+ defaultConfig: { type: "long_text", rows: 4 }
403
+ },
404
+ email: {
405
+ type: "email",
406
+ label: "Email",
407
+ category: "text",
408
+ icon: "Mail",
409
+ description: "Email address input with validation"
410
+ },
411
+ phone: {
412
+ type: "phone",
413
+ label: "Phone",
414
+ category: "text",
415
+ icon: "Phone",
416
+ description: "US phone number input"
417
+ },
418
+ url: {
419
+ type: "url",
420
+ label: "URL",
421
+ category: "text",
422
+ icon: "Link",
423
+ description: "Website URL input"
424
+ },
425
+ // ── Numeric ──
426
+ number: {
427
+ type: "number",
428
+ label: "Number",
429
+ category: "numeric",
430
+ icon: "Hash",
431
+ description: "Numeric input with min/max",
432
+ defaultConfig: { type: "number", step: 1 }
433
+ },
434
+ slider: {
435
+ type: "slider",
436
+ label: "Slider",
437
+ category: "numeric",
438
+ icon: "SlidersHorizontal",
439
+ description: "Range slider",
440
+ defaultConfig: { type: "slider", min: 0, max: 100, step: 1 }
441
+ },
442
+ rating: {
443
+ type: "rating",
444
+ label: "Rating",
445
+ category: "numeric",
446
+ icon: "Star",
447
+ description: "Star rating (1-5 or custom)",
448
+ defaultConfig: { type: "rating", max: 5, icon: "star" }
449
+ },
450
+ nps: {
451
+ type: "nps",
452
+ label: "NPS",
453
+ category: "numeric",
454
+ icon: "BarChart3",
455
+ description: "Net Promoter Score (0-10)",
456
+ defaultConfig: { type: "nps", lowLabel: "Not likely", highLabel: "Very likely" }
457
+ },
458
+ opinion_scale: {
459
+ type: "opinion_scale",
460
+ label: "Opinion Scale",
461
+ category: "numeric",
462
+ icon: "TrendingUp",
463
+ description: "Custom numeric scale with labels",
464
+ defaultConfig: { type: "opinion_scale", min: 1, max: 5 }
465
+ },
466
+ // ── Selection ──
467
+ single_select: {
468
+ type: "single_select",
469
+ label: "Single Select",
470
+ category: "selection",
471
+ icon: "CircleDot",
472
+ description: "Radio buttons or vertical list",
473
+ requiresOptions: true,
474
+ defaultConfig: { type: "single_select", layout: "vertical" }
475
+ },
476
+ multi_select: {
477
+ type: "multi_select",
478
+ label: "Multi Select",
479
+ category: "selection",
480
+ icon: "CheckSquare",
481
+ description: "Checkboxes - select multiple",
482
+ requiresOptions: true,
483
+ defaultConfig: { type: "multi_select", layout: "vertical" }
484
+ },
485
+ dropdown: {
486
+ type: "dropdown",
487
+ label: "Dropdown",
488
+ category: "selection",
489
+ icon: "ChevronDown",
490
+ description: "Select from dropdown menu",
491
+ requiresOptions: true,
492
+ defaultConfig: { type: "dropdown", searchable: false }
493
+ },
494
+ boolean: {
495
+ type: "boolean",
496
+ label: "Yes/No",
497
+ category: "selection",
498
+ icon: "ToggleLeft",
499
+ description: "Toggle, radio, or checkbox",
500
+ defaultConfig: { type: "boolean", style: "toggle" }
501
+ },
502
+ ranking: {
503
+ type: "ranking",
504
+ label: "Ranking",
505
+ category: "selection",
506
+ icon: "ArrowUpDown",
507
+ description: "Drag to rank items in order",
508
+ requiresOptions: true,
509
+ defaultConfig: { type: "ranking", items: [] }
510
+ },
511
+ // ── Date/Time ──
512
+ date: {
513
+ type: "date",
514
+ label: "Date",
515
+ category: "datetime",
516
+ icon: "Calendar",
517
+ description: "Date picker",
518
+ defaultConfig: { type: "date" }
519
+ },
520
+ time: {
521
+ type: "time",
522
+ label: "Time",
523
+ category: "datetime",
524
+ icon: "Clock",
525
+ description: "Time picker",
526
+ defaultConfig: { type: "time", format: "12h" }
527
+ },
528
+ // ── Media ──
529
+ file_upload: {
530
+ type: "file_upload",
531
+ label: "File Upload",
532
+ category: "media",
533
+ icon: "Paperclip",
534
+ description: "Upload files",
535
+ defaultConfig: { type: "file_upload", maxFiles: 1, maxSizeMb: 10 }
536
+ },
537
+ // ── Advanced ──
538
+ matrix: {
539
+ type: "matrix",
540
+ label: "Matrix",
541
+ category: "advanced",
542
+ icon: "Grid3X3",
543
+ description: "Grid of inputs (rows x columns)",
544
+ defaultConfig: {
545
+ type: "matrix",
546
+ rows: [{ label: "Row 1", value: "row1" }],
547
+ columns: [{ label: "Column 1", value: "col1" }],
548
+ inputType: "radio"
549
+ }
550
+ },
551
+ calculated: {
552
+ type: "calculated",
553
+ label: "Calculated",
554
+ category: "advanced",
555
+ icon: "Calculator",
556
+ description: "Computed value from other fields",
557
+ defaultConfig: { type: "calculated", expression: "", format: "number" }
558
+ },
559
+ hidden: {
560
+ type: "hidden",
561
+ label: "Hidden Field",
562
+ category: "advanced",
563
+ icon: "EyeOff",
564
+ description: "Hidden value from URL or static",
565
+ defaultConfig: { type: "hidden", source: "static" }
566
+ },
567
+ // ── Structural ──
568
+ section_header: {
569
+ type: "section_header",
570
+ label: "Section Header",
571
+ category: "structural",
572
+ icon: "Heading",
573
+ description: "Heading within a section",
574
+ defaultConfig: { type: "section_header", level: "h3" }
575
+ },
576
+ info_block: {
577
+ type: "info_block",
578
+ label: "Info Block",
579
+ category: "structural",
580
+ icon: "Info",
581
+ description: "Informational message box",
582
+ defaultConfig: { type: "info_block", content: "", variant: "info" }
583
+ },
584
+ page_break: {
585
+ type: "page_break",
586
+ label: "Page Break",
587
+ category: "structural",
588
+ icon: "SeparatorHorizontal",
589
+ description: "Visual separator for print",
590
+ defaultConfig: { type: "page_break" }
591
+ },
592
+ consent: {
593
+ type: "consent",
594
+ label: "Consent",
595
+ category: "structural",
596
+ icon: "ShieldCheck",
597
+ description: "Agreement checkbox",
598
+ defaultConfig: { type: "consent", text: "", checkboxLabel: "I agree" }
599
+ },
600
+ // ── Content & Visual ──
601
+ "welcome-screen": {
602
+ type: "welcome-screen",
603
+ label: "Welcome Screen",
604
+ category: "content",
605
+ icon: "Hand",
606
+ description: "Full-width welcome card",
607
+ defaultConfig: {
608
+ type: "welcome-screen",
609
+ heading: "Welcome",
610
+ buttonText: "Start",
611
+ alignment: "center"
612
+ }
613
+ },
614
+ "thank-you-screen": {
615
+ type: "thank-you-screen",
616
+ label: "Thank You Screen",
617
+ category: "content",
618
+ icon: "PartyPopper",
619
+ description: "Completion screen",
620
+ defaultConfig: {
621
+ type: "thank-you-screen",
622
+ heading: "Thank You!",
623
+ description: "Your response has been recorded."
624
+ }
625
+ },
626
+ "rich-text": {
627
+ type: "rich-text",
628
+ label: "Rich Text",
629
+ category: "content",
630
+ icon: "FileText",
631
+ description: "HTML or Markdown content",
632
+ defaultConfig: { type: "rich-text", content: "", format: "html" }
633
+ },
634
+ image: {
635
+ type: "image",
636
+ label: "Image",
637
+ category: "content",
638
+ icon: "Image",
639
+ description: "Display an image",
640
+ defaultConfig: { type: "image", src: "", alt: "", alignment: "center" }
641
+ },
642
+ video: {
643
+ type: "video",
644
+ label: "Video",
645
+ category: "content",
646
+ icon: "Video",
647
+ description: "Embed YouTube/Vimeo video",
648
+ defaultConfig: { type: "video", src: "", provider: "youtube" }
649
+ },
650
+ divider: {
651
+ type: "divider",
652
+ label: "Divider",
653
+ category: "content",
654
+ icon: "Minus",
655
+ description: "Horizontal line separator",
656
+ defaultConfig: { type: "divider", style: "solid" }
657
+ },
658
+ spacer: {
659
+ type: "spacer",
660
+ label: "Spacer",
661
+ category: "content",
662
+ icon: "MoveVertical",
663
+ description: "Vertical spacing",
664
+ defaultConfig: { type: "spacer", height: 32 }
665
+ }
666
+ };
667
+ var DEFAULT_PALETTE = [
668
+ {
669
+ category: "text",
670
+ label: "Text Input",
671
+ types: ["short_text", "long_text", "email", "phone", "url"]
672
+ },
673
+ {
674
+ category: "numeric",
675
+ label: "Numeric",
676
+ types: ["number", "slider", "rating", "nps", "opinion_scale"]
677
+ },
678
+ {
679
+ category: "selection",
680
+ label: "Selection",
681
+ types: ["single_select", "multi_select", "dropdown", "boolean", "ranking"]
682
+ },
683
+ {
684
+ category: "datetime",
685
+ label: "Date & Time",
686
+ types: ["date", "time"]
687
+ },
688
+ {
689
+ category: "media",
690
+ label: "Media",
691
+ types: ["file_upload"]
692
+ },
693
+ {
694
+ category: "content",
695
+ label: "Content & Visual",
696
+ types: ["welcome-screen", "thank-you-screen", "rich-text", "image", "video", "divider", "spacer"]
697
+ },
698
+ {
699
+ category: "structural",
700
+ label: "Structural",
701
+ types: ["section_header", "info_block", "page_break", "consent"]
702
+ },
703
+ {
704
+ category: "advanced",
705
+ label: "Advanced",
706
+ types: ["matrix", "calculated", "hidden"]
707
+ }
708
+ ];
709
+
710
+ // src/form-builder/hooks/use-drag-drop.ts
711
+ function useDragDrop(builderState) {
712
+ const [activeDragItem, setActiveDragItem] = react.useState(null);
713
+ const sensors = core.useSensors(
714
+ core.useSensor(core.MouseSensor, {
715
+ activationConstraint: {
716
+ distance: 8
717
+ // 8px movement to activate drag
718
+ }
719
+ }),
720
+ core.useSensor(core.TouchSensor, {
721
+ activationConstraint: {
722
+ delay: 200,
723
+ tolerance: 5
724
+ }
725
+ })
726
+ );
727
+ const parseDragItem = (active) => {
728
+ const data = active.data.current;
729
+ if (!data) return null;
730
+ if (data.type === "palette-item") {
731
+ return { type: "palette-item", questionType: data.questionType };
732
+ }
733
+ if (data.type === "question") {
734
+ return {
735
+ type: "question",
736
+ sectionId: data.sectionId,
737
+ questionId: data.questionId,
738
+ questionIndex: data.questionIndex
739
+ };
740
+ }
741
+ if (data.type === "section") {
742
+ return {
743
+ type: "section",
744
+ sectionId: data.sectionId,
745
+ sectionIndex: data.sectionIndex
746
+ };
747
+ }
748
+ return null;
749
+ };
750
+ const parseDropTarget = (over) => {
751
+ if (!over) return null;
752
+ const data = over.data.current;
753
+ if (!data) return null;
754
+ if (data.type === "section") {
755
+ return { type: "section", sectionId: data.sectionId, index: data.index };
756
+ }
757
+ if (data.type === "canvas") {
758
+ return { type: "canvas", index: data.index };
759
+ }
760
+ return null;
761
+ };
762
+ const handleDragStart = react.useCallback((event) => {
763
+ const item = parseDragItem(event.active);
764
+ setActiveDragItem(item);
765
+ }, []);
766
+ const handleDragCancel = react.useCallback(() => {
767
+ setActiveDragItem(null);
768
+ }, []);
769
+ const handleDragEnd = (event) => {
770
+ setActiveDragItem(null);
771
+ const { active, over } = event;
772
+ if (!over) return;
773
+ const dragItem = parseDragItem(active);
774
+ const dropTarget = parseDropTarget(over);
775
+ if (!dragItem || !dropTarget) return;
776
+ if (dragItem.type === "palette-item") {
777
+ if (dropTarget.type === "section") {
778
+ const typeInfo = QUESTION_TYPE_INFO[dragItem.questionType];
779
+ const newQuestion = {
780
+ id: generateQuestionId(),
781
+ type: dragItem.questionType,
782
+ label: typeInfo?.label ?? "New Question",
783
+ config: typeInfo?.defaultConfig,
784
+ options: typeInfo?.requiresOptions ? [
785
+ { label: "Option 1", value: "option1" },
786
+ { label: "Option 2", value: "option2" }
787
+ ] : void 0
788
+ };
789
+ builderState.addQuestion(dropTarget.sectionId, newQuestion, dropTarget.index);
790
+ builderState.selectQuestion(dropTarget.sectionId, newQuestion.id);
791
+ }
792
+ return;
793
+ }
794
+ if (dragItem.type === "question" && dropTarget.type === "section") {
795
+ if (dragItem.sectionId === dropTarget.sectionId) {
796
+ if (dragItem.questionIndex !== dropTarget.index) {
797
+ builderState.moveQuestion(
798
+ dragItem.sectionId,
799
+ dragItem.questionId,
800
+ dropTarget.sectionId,
801
+ dropTarget.index
802
+ );
803
+ }
804
+ } else {
805
+ builderState.moveQuestion(
806
+ dragItem.sectionId,
807
+ dragItem.questionId,
808
+ dropTarget.sectionId,
809
+ dropTarget.index
810
+ );
811
+ }
812
+ return;
813
+ }
814
+ if (dragItem.type === "section" && dropTarget.type === "canvas") {
815
+ if (dragItem.sectionIndex !== dropTarget.index) {
816
+ builderState.moveSection(dragItem.sectionId, dropTarget.index);
817
+ }
818
+ }
819
+ };
820
+ return {
821
+ sensors,
822
+ handleDragStart,
823
+ handleDragEnd,
824
+ handleDragCancel,
825
+ activeDragItem
826
+ };
827
+ }
828
+
829
+ // src/form-builder/default-schema.ts
830
+ var DEFAULT_SCHEMA = {
831
+ id: "new-form",
832
+ version: "1.0.0",
833
+ title: "Untitled Form",
834
+ description: "Create your form by dragging fields from the palette.",
835
+ sections: [
836
+ {
837
+ id: "section_default",
838
+ title: "Section 1",
839
+ description: "",
840
+ questions: [
841
+ {
842
+ id: "question_default",
843
+ type: "short_text",
844
+ label: "Your first question",
845
+ placeholder: "Enter your answer here",
846
+ required: false,
847
+ config: {
848
+ type: "short_text",
849
+ maxLength: 255
850
+ }
851
+ }
852
+ ]
853
+ }
854
+ ],
855
+ submitAction: {
856
+ type: "callback"
857
+ }
858
+ };
859
+ var ICON_MAP = {
860
+ Type: lucideReact.Type,
861
+ AlignLeft: lucideReact.AlignLeft,
862
+ Mail: lucideReact.Mail,
863
+ Phone: lucideReact.Phone,
864
+ PhoneCall: lucideReact.PhoneCall,
865
+ Link: lucideReact.Link,
866
+ UserCheck: lucideReact.UserCheck,
867
+ Hash: lucideReact.Hash,
868
+ SlidersHorizontal: lucideReact.SlidersHorizontal,
869
+ Star: lucideReact.Star,
870
+ BarChart3: lucideReact.BarChart3,
871
+ TrendingUp: lucideReact.TrendingUp,
872
+ ListOrdered: lucideReact.ListOrdered,
873
+ CircleDot: lucideReact.CircleDot,
874
+ CheckSquare: lucideReact.CheckSquare,
875
+ ChevronDown: lucideReact.ChevronDown,
876
+ ToggleLeft: lucideReact.ToggleLeft,
877
+ Globe: lucideReact.Globe,
878
+ ArrowUpDown: lucideReact.ArrowUpDown,
879
+ Calendar: lucideReact.Calendar,
880
+ Clock: lucideReact.Clock,
881
+ CalendarRange: lucideReact.CalendarRange,
882
+ CalendarCheck: lucideReact.CalendarCheck,
883
+ Paperclip: lucideReact.Paperclip,
884
+ PenTool: lucideReact.PenTool,
885
+ Camera: lucideReact.Camera,
886
+ Grid3X3: lucideReact.Grid3X3,
887
+ Repeat: lucideReact.Repeat,
888
+ MapPin: lucideReact.MapPin,
889
+ CreditCard: lucideReact.CreditCard,
890
+ Calculator: lucideReact.Calculator,
891
+ EyeOff: lucideReact.EyeOff,
892
+ Trophy: lucideReact.Trophy,
893
+ Heading: lucideReact.Heading,
894
+ Info: lucideReact.Info,
895
+ SeparatorHorizontal: lucideReact.SeparatorHorizontal,
896
+ ShieldCheck: lucideReact.ShieldCheck,
897
+ Hand: lucideReact.Hand,
898
+ PartyPopper: lucideReact.PartyPopper,
899
+ FileText: lucideReact.FileText,
900
+ Image: lucideReact.Image,
901
+ Video: lucideReact.Video,
902
+ Minus: lucideReact.Minus,
903
+ MoveVertical: lucideReact.MoveVertical,
904
+ HelpCircle: lucideReact.HelpCircle
905
+ };
906
+ function getIcon(name) {
907
+ return ICON_MAP[name] ?? lucideReact.HelpCircle;
908
+ }
909
+ function cn(...inputs) {
910
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
911
+ }
912
+ function PaletteItem({ questionType, typeInfo }) {
913
+ const { attributes, listeners, setNodeRef, isDragging } = core.useDraggable({
914
+ id: `palette-${questionType}`,
915
+ data: { type: "palette-item", questionType }
916
+ });
917
+ const IconComponent = getIcon(typeInfo.icon);
918
+ return /* @__PURE__ */ jsxRuntime.jsxs(
919
+ "div",
920
+ {
921
+ ref: setNodeRef,
922
+ ...listeners,
923
+ ...attributes,
924
+ className: cn(
925
+ "group/item flex items-center gap-3 px-2 py-1.5 mb-0.5 rounded-md text-sm text-foreground cursor-grab transition-colors",
926
+ isDragging ? "fcb-dragging border border-primary" : "border border-transparent hover:bg-accent hover:border-border"
927
+ ),
928
+ children: [
929
+ /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 14, className: "shrink-0 text-muted-foreground group-hover/item:text-primary transition-colors", strokeWidth: 1.75 }),
930
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: typeInfo.label })
931
+ ]
932
+ }
933
+ );
934
+ }
935
+ function QuestionPalette({ questionTypes, palette }) {
936
+ const [collapsed, setCollapsed] = react.useState({});
937
+ const [search, setSearch] = react.useState("");
938
+ const mergedPalette = react.useMemo(
939
+ () => palette ? [...DEFAULT_PALETTE, ...palette] : DEFAULT_PALETTE,
940
+ [palette]
941
+ );
942
+ const toggleCategory = (category) => {
943
+ setCollapsed((prev) => ({ ...prev, [category]: !prev[category] }));
944
+ };
945
+ const searchLower = search.toLowerCase();
946
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-60 h-full flex flex-col border-r border-border bg-card", role: "region", "aria-label": "Field palette", children: [
947
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 pb-3", children: [
948
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-foreground mb-3", children: "Fields" }),
949
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
950
+ /* @__PURE__ */ jsxRuntime.jsx(
951
+ lucideReact.Search,
952
+ {
953
+ size: 13,
954
+ className: "absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none"
955
+ }
956
+ ),
957
+ /* @__PURE__ */ jsxRuntime.jsx(
958
+ fieldcraftReact.Input,
959
+ {
960
+ value: search,
961
+ onChange: (e) => setSearch(e.target.value),
962
+ placeholder: "Search fields...",
963
+ className: "h-8 pl-8 pr-3 text-xs",
964
+ "aria-label": "Search field types"
965
+ }
966
+ )
967
+ ] })
968
+ ] }),
969
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto px-3 pb-4 scrollbar-thin", children: mergedPalette.map((category) => {
970
+ const filteredTypes = search ? category.types.filter((t) => {
971
+ const info = questionTypes[t];
972
+ return info && (info.label.toLowerCase().includes(searchLower) || info.description.toLowerCase().includes(searchLower));
973
+ }) : category.types;
974
+ if (filteredTypes.length === 0) return null;
975
+ const isCollapsed = collapsed[category.category] && !search;
976
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3", children: [
977
+ /* @__PURE__ */ jsxRuntime.jsxs(
978
+ "button",
979
+ {
980
+ type: "button",
981
+ onClick: () => toggleCategory(category.category),
982
+ className: "flex items-center gap-1.5 w-full px-1 py-1 text-left text-[11px] font-semibold uppercase tracking-widest text-muted-foreground bg-transparent border-0 cursor-pointer",
983
+ "aria-expanded": !isCollapsed,
984
+ children: [
985
+ /* @__PURE__ */ jsxRuntime.jsx(
986
+ lucideReact.ChevronRight,
987
+ {
988
+ size: 12,
989
+ className: cn(
990
+ "shrink-0 transition-transform duration-150",
991
+ !isCollapsed && "rotate-90"
992
+ )
993
+ }
994
+ ),
995
+ category.label
996
+ ]
997
+ }
998
+ ),
999
+ !isCollapsed && /* @__PURE__ */ jsxRuntime.jsx("div", { children: filteredTypes.map((type) => {
1000
+ const info = questionTypes[type];
1001
+ if (!info) return null;
1002
+ return /* @__PURE__ */ jsxRuntime.jsx(PaletteItem, { questionType: type, typeInfo: info }, type);
1003
+ }) })
1004
+ ] }, category.category);
1005
+ }) })
1006
+ ] });
1007
+ }
1008
+ function QuestionBlock({
1009
+ question,
1010
+ sectionId,
1011
+ questionIndex,
1012
+ isSelected,
1013
+ builderState
1014
+ }) {
1015
+ const typeInfo = QUESTION_TYPE_INFO[question.type];
1016
+ const IconComponent = typeInfo ? getIcon(typeInfo.icon) : null;
1017
+ const [isEditing, setIsEditing] = react.useState(false);
1018
+ const [editValue, setEditValue] = react.useState(question.label);
1019
+ const inputRef = react.useRef(null);
1020
+ react.useEffect(() => {
1021
+ if (!isEditing) setEditValue(question.label);
1022
+ }, [question.label, isEditing]);
1023
+ react.useEffect(() => {
1024
+ if (isEditing && inputRef.current) {
1025
+ inputRef.current.focus();
1026
+ inputRef.current.select();
1027
+ }
1028
+ }, [isEditing]);
1029
+ const { attributes, listeners, setNodeRef: setDragRef, isDragging } = core.useDraggable({
1030
+ id: `question-${question.id}`,
1031
+ data: { type: "question", sectionId, questionId: question.id, questionIndex }
1032
+ });
1033
+ const { setNodeRef: setDropRef } = core.useDroppable({
1034
+ id: `question-drop-${question.id}`,
1035
+ data: { type: "section", sectionId, index: questionIndex }
1036
+ });
1037
+ const handleSelect = () => builderState.selectQuestion(sectionId, question.id);
1038
+ const handleDelete = (e) => {
1039
+ e.stopPropagation();
1040
+ builderState.removeQuestion(sectionId, question.id);
1041
+ };
1042
+ const handleDuplicate = (e) => {
1043
+ e.stopPropagation();
1044
+ builderState.duplicateQuestion(sectionId, question.id);
1045
+ };
1046
+ const handleLabelDoubleClick = (e) => {
1047
+ e.stopPropagation();
1048
+ setIsEditing(true);
1049
+ };
1050
+ const commitEdit = () => {
1051
+ const trimmed = editValue.trim();
1052
+ if (trimmed && trimmed !== question.label) {
1053
+ builderState.updateQuestion(sectionId, question.id, { label: trimmed });
1054
+ } else {
1055
+ setEditValue(question.label);
1056
+ }
1057
+ setIsEditing(false);
1058
+ };
1059
+ const handleEditKeyDown = (e) => {
1060
+ if (e.key === "Enter") {
1061
+ e.preventDefault();
1062
+ commitEdit();
1063
+ }
1064
+ if (e.key === "Escape") {
1065
+ setEditValue(question.label);
1066
+ setIsEditing(false);
1067
+ }
1068
+ e.stopPropagation();
1069
+ };
1070
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setDropRef, children: /* @__PURE__ */ jsxRuntime.jsxs(
1071
+ "div",
1072
+ {
1073
+ ref: setDragRef,
1074
+ onClick: handleSelect,
1075
+ className: cn(
1076
+ "group p-3 mb-1.5 rounded-md border cursor-pointer transition-colors",
1077
+ isSelected ? "fcb-selected border-primary" : "bg-secondary border-border hover:border-fcb-border-strong hover:bg-accent",
1078
+ isDragging && "opacity-40"
1079
+ ),
1080
+ children: [
1081
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1.5", children: [
1082
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1083
+ /* @__PURE__ */ jsxRuntime.jsx(
1084
+ "div",
1085
+ {
1086
+ ...listeners,
1087
+ ...attributes,
1088
+ className: "cursor-grab text-muted-foreground opacity-40 group-hover:opacity-100 transition-opacity",
1089
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { size: 14, strokeWidth: 1.5 })
1090
+ }
1091
+ ),
1092
+ /* @__PURE__ */ jsxRuntime.jsxs(fieldcraftReact.Badge, { variant: "secondary", className: "gap-1.5 text-muted-foreground", children: [
1093
+ IconComponent && /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 11, strokeWidth: 1.75 }),
1094
+ typeInfo?.label ?? question.type
1095
+ ] })
1096
+ ] }),
1097
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
1098
+ "flex gap-0.5 transition-opacity",
1099
+ isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100"
1100
+ ), children: [
1101
+ /* @__PURE__ */ jsxRuntime.jsx(
1102
+ fieldcraftReact.Button,
1103
+ {
1104
+ variant: "ghost",
1105
+ size: "icon-xs",
1106
+ onClick: handleDuplicate,
1107
+ title: "Duplicate",
1108
+ "aria-label": "Duplicate field",
1109
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { size: 12, strokeWidth: 1.75 })
1110
+ }
1111
+ ),
1112
+ /* @__PURE__ */ jsxRuntime.jsx(
1113
+ fieldcraftReact.Button,
1114
+ {
1115
+ variant: "ghost",
1116
+ size: "icon-xs",
1117
+ onClick: handleDelete,
1118
+ className: "hover:bg-destructive/10 hover:text-destructive",
1119
+ title: "Delete",
1120
+ "aria-label": "Delete field",
1121
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, strokeWidth: 1.75 })
1122
+ }
1123
+ )
1124
+ ] })
1125
+ ] }),
1126
+ isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
1127
+ "input",
1128
+ {
1129
+ ref: inputRef,
1130
+ value: editValue,
1131
+ onChange: (e) => setEditValue(e.target.value),
1132
+ onBlur: commitEdit,
1133
+ onKeyDown: handleEditKeyDown,
1134
+ onClick: (e) => e.stopPropagation(),
1135
+ className: "w-full text-sm font-medium text-foreground bg-transparent border-0 border-b border-primary outline-none py-0.5 px-0"
1136
+ }
1137
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
1138
+ "div",
1139
+ {
1140
+ className: "text-sm font-medium text-foreground cursor-text",
1141
+ onDoubleClick: handleLabelDoubleClick,
1142
+ title: "Double-click to edit",
1143
+ children: [
1144
+ question.label,
1145
+ question.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-xs text-primary", children: "*" })
1146
+ ]
1147
+ }
1148
+ ),
1149
+ question.helpText && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: question.helpText })
1150
+ ]
1151
+ }
1152
+ ) });
1153
+ }
1154
+ function ConfirmDialog({ open, title, message, onConfirm, onCancel }) {
1155
+ if (!open) return null;
1156
+ return /* @__PURE__ */ jsxRuntime.jsx(
1157
+ "div",
1158
+ {
1159
+ className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",
1160
+ onClick: onCancel,
1161
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1162
+ "div",
1163
+ {
1164
+ className: "w-96 max-w-[90vw] bg-card border border-border rounded-xl p-6 fcb-shadow-lg",
1165
+ onClick: (e) => e.stopPropagation(),
1166
+ children: [
1167
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground mb-2", children: title }),
1168
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-6 leading-relaxed", children: message }),
1169
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 justify-end", children: [
1170
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { variant: "outline", onClick: onCancel, children: "Cancel" }),
1171
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { variant: "destructive", onClick: onConfirm, children: "Delete" })
1172
+ ] })
1173
+ ]
1174
+ }
1175
+ )
1176
+ }
1177
+ );
1178
+ }
1179
+ function SectionBlock({ section, builderState }) {
1180
+ const [confirmDelete, setConfirmDelete] = react.useState(false);
1181
+ const { setNodeRef } = core.useDroppable({
1182
+ id: `section-end-${section.id}`,
1183
+ data: { type: "section", sectionId: section.id, index: section.questions.length }
1184
+ });
1185
+ const handleAddQuestion = () => {
1186
+ const newQuestion = {
1187
+ id: generateQuestionId(),
1188
+ type: "short_text",
1189
+ label: "New Question",
1190
+ config: { type: "short_text", maxLength: 255 }
1191
+ };
1192
+ builderState.addQuestion(section.id, newQuestion, section.questions.length);
1193
+ builderState.selectQuestion(section.id, newQuestion.id);
1194
+ };
1195
+ const isSelected = builderState.selectedItem?.type === "section" && builderState.selectedItem.sectionId === section.id;
1196
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1197
+ /* @__PURE__ */ jsxRuntime.jsxs(
1198
+ "div",
1199
+ {
1200
+ className: cn(
1201
+ "mb-4 p-4 rounded-lg border transition-colors",
1202
+ isSelected ? "fcb-selected border-primary" : "bg-card border-fcb-border-strong"
1203
+ ),
1204
+ onClick: (e) => {
1205
+ if (e.target === e.currentTarget) builderState.selectSection(section.id);
1206
+ },
1207
+ children: [
1208
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-4 pb-3 border-b border-fcb-border-strong", children: [
1209
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
1210
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground mb-0.5", children: section.title }),
1211
+ section.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: section.description })
1212
+ ] }),
1213
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 shrink-0 ml-3", children: [
1214
+ /* @__PURE__ */ jsxRuntime.jsxs(fieldcraftReact.Button, { variant: "secondary", size: "sm", onClick: handleAddQuestion, children: [
1215
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 13, strokeWidth: 2 }),
1216
+ "Add Field"
1217
+ ] }),
1218
+ /* @__PURE__ */ jsxRuntime.jsx(
1219
+ fieldcraftReact.Button,
1220
+ {
1221
+ variant: "ghost",
1222
+ size: "icon-sm",
1223
+ onClick: () => builderState.duplicateSection(section.id),
1224
+ title: "Duplicate section",
1225
+ "aria-label": "Duplicate section",
1226
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { size: 14, strokeWidth: 1.75 })
1227
+ }
1228
+ ),
1229
+ /* @__PURE__ */ jsxRuntime.jsx(
1230
+ fieldcraftReact.Button,
1231
+ {
1232
+ variant: "ghost",
1233
+ size: "icon-sm",
1234
+ onClick: () => setConfirmDelete(true),
1235
+ className: "hover:bg-destructive/10 hover:text-destructive",
1236
+ title: "Delete section",
1237
+ "aria-label": "Delete section",
1238
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 14, strokeWidth: 1.75 })
1239
+ }
1240
+ )
1241
+ ] })
1242
+ ] }),
1243
+ section.questions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1244
+ "div",
1245
+ {
1246
+ ref: setNodeRef,
1247
+ className: "py-8 text-center text-sm text-muted-foreground border border-dashed border-fcb-border-strong rounded-md",
1248
+ children: 'Drag a field from the palette or click "Add Field"'
1249
+ }
1250
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1251
+ section.questions.map((question, index) => {
1252
+ const isQuestionSelected = builderState.selectedItem?.type === "question" && builderState.selectedItem.sectionId === section.id && builderState.selectedItem.questionId === question.id;
1253
+ return /* @__PURE__ */ jsxRuntime.jsx(
1254
+ QuestionBlock,
1255
+ {
1256
+ question,
1257
+ sectionId: section.id,
1258
+ questionIndex: index,
1259
+ isSelected: isQuestionSelected,
1260
+ builderState
1261
+ },
1262
+ question.id
1263
+ );
1264
+ }),
1265
+ /* @__PURE__ */ jsxRuntime.jsx(
1266
+ "div",
1267
+ {
1268
+ ref: setNodeRef,
1269
+ className: "h-2 mt-1 rounded-sm transition-colors"
1270
+ }
1271
+ )
1272
+ ] })
1273
+ ]
1274
+ }
1275
+ ),
1276
+ /* @__PURE__ */ jsxRuntime.jsx(
1277
+ ConfirmDialog,
1278
+ {
1279
+ open: confirmDelete,
1280
+ title: "Delete Section",
1281
+ message: `Delete "${section.title}" and all its ${section.questions.length} question${section.questions.length !== 1 ? "s" : ""}? This cannot be undone.`,
1282
+ onConfirm: () => {
1283
+ setConfirmDelete(false);
1284
+ builderState.removeSection(section.id);
1285
+ },
1286
+ onCancel: () => setConfirmDelete(false)
1287
+ }
1288
+ )
1289
+ ] });
1290
+ }
1291
+ function FormCanvas({ builderState }) {
1292
+ const { schema } = builderState;
1293
+ const handleAddSection = () => {
1294
+ const newSection = {
1295
+ id: generateSectionId(),
1296
+ title: `Section ${schema.sections.length + 1}`,
1297
+ description: "",
1298
+ questions: []
1299
+ };
1300
+ builderState.addSection(newSection, schema.sections.length);
1301
+ builderState.selectSection(newSection.id);
1302
+ };
1303
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-full overflow-y-auto bg-background fcb-canvas scrollbar-thin", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 max-w-3xl mx-auto", children: [
1304
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-8", children: [
1305
+ /* @__PURE__ */ jsxRuntime.jsx(
1306
+ "input",
1307
+ {
1308
+ type: "text",
1309
+ value: schema.title,
1310
+ onChange: (e) => {
1311
+ builderState.updateSchema({ ...schema, title: e.target.value });
1312
+ },
1313
+ className: "w-full text-xl font-bold bg-transparent text-foreground border-0 outline-none p-1.5 rounded-md focus:bg-card transition-colors",
1314
+ placeholder: "Form Title"
1315
+ }
1316
+ ),
1317
+ /* @__PURE__ */ jsxRuntime.jsx(
1318
+ "textarea",
1319
+ {
1320
+ value: schema.description ?? "",
1321
+ onChange: (e) => {
1322
+ builderState.updateSchema({ ...schema, description: e.target.value });
1323
+ },
1324
+ className: "w-full text-sm text-muted-foreground bg-transparent border-0 outline-none p-1.5 resize-y min-h-12 rounded-md leading-relaxed focus:bg-card transition-colors",
1325
+ placeholder: "Form description (optional)"
1326
+ }
1327
+ )
1328
+ ] }),
1329
+ schema.sections.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-16 text-center border border-dashed border-fcb-border-strong rounded-lg text-muted-foreground", children: [
1330
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base mb-4", children: "No sections yet" }),
1331
+ /* @__PURE__ */ jsxRuntime.jsxs(fieldcraftReact.Button, { onClick: handleAddSection, className: "fcb-glow", children: [
1332
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 16, strokeWidth: 2 }),
1333
+ "Add First Section"
1334
+ ] })
1335
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1336
+ schema.sections.map((section, index) => /* @__PURE__ */ jsxRuntime.jsx(
1337
+ SectionBlock,
1338
+ {
1339
+ section,
1340
+ sectionIndex: index,
1341
+ builderState
1342
+ },
1343
+ section.id
1344
+ )),
1345
+ /* @__PURE__ */ jsxRuntime.jsxs(
1346
+ "button",
1347
+ {
1348
+ type: "button",
1349
+ onClick: handleAddSection,
1350
+ className: "w-full py-4 text-sm font-medium rounded-lg border border-dashed border-fcb-border-strong text-muted-foreground bg-transparent flex items-center justify-center gap-2 cursor-pointer transition-colors hover:border-primary hover:text-primary hover:bg-primary/5",
1351
+ children: [
1352
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 14, strokeWidth: 2 }),
1353
+ "Add Section"
1354
+ ]
1355
+ }
1356
+ )
1357
+ ] })
1358
+ ] }) });
1359
+ }
1360
+ function useConfigUpdater(question, onUpdate) {
1361
+ return (field, value) => {
1362
+ const current = question.config ?? {};
1363
+ onUpdate({ config: { ...current, type: question.type, [field]: value } });
1364
+ };
1365
+ }
1366
+ function QuestionConfigEditor({ question, onUpdate }) {
1367
+ const updateConfig = useConfigUpdater(question, onUpdate);
1368
+ const config = question.config ?? {};
1369
+ switch (question.type) {
1370
+ // ── Text ──
1371
+ case "short_text":
1372
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Text Settings", children: [
1373
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max Length", value: config.maxLength, placeholder: "255", onChange: (v) => updateConfig("maxLength", v) }),
1374
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Input Type", value: config.inputType ?? "text", options: [{ label: "Text", value: "text" }, { label: "Password", value: "password" }], onChange: (v) => updateConfig("inputType", v) }),
1375
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Prefix", value: config.prefix, placeholder: "e.g. $", onChange: (v) => updateConfig("prefix", v) }),
1376
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Suffix", value: config.suffix, placeholder: "e.g. USD", onChange: (v) => updateConfig("suffix", v) })
1377
+ ] });
1378
+ case "long_text":
1379
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Text Settings", children: [
1380
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max Length", value: config.maxLength, placeholder: "No limit", onChange: (v) => updateConfig("maxLength", v) }),
1381
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Rows", value: config.rows, placeholder: "4", onChange: (v) => updateConfig("rows", v) }),
1382
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Show Character Count", checked: !!config.showCharCount, onChange: (v) => updateConfig("showCharCount", v) })
1383
+ ] });
1384
+ case "legal_name":
1385
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Name Fields", children: [
1386
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Show Middle Name", checked: !!config.showMiddleName, onChange: (v) => updateConfig("showMiddleName", v) }),
1387
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Show Suffix", checked: !!config.showSuffix, onChange: (v) => updateConfig("showSuffix", v) })
1388
+ ] });
1389
+ // ── Numeric ──
1390
+ case "number":
1391
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Number Settings", children: [
1392
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Min", value: config.min, onChange: (v) => updateConfig("min", v) }),
1393
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max", value: config.max, onChange: (v) => updateConfig("max", v) }),
1394
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig("step", v) }),
1395
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Decimal Places", value: config.decimalPlaces, placeholder: "0", onChange: (v) => updateConfig("decimalPlaces", v) }),
1396
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Prefix", value: config.prefix, placeholder: "e.g. $", onChange: (v) => updateConfig("prefix", v) }),
1397
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Suffix", value: config.suffix, placeholder: "e.g. kg", onChange: (v) => updateConfig("suffix", v) })
1398
+ ] });
1399
+ case "slider":
1400
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Slider Settings", children: [
1401
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Min", value: config.min, placeholder: "0", onChange: (v) => updateConfig("min", v) }),
1402
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max", value: config.max, placeholder: "100", onChange: (v) => updateConfig("max", v) }),
1403
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig("step", v) }),
1404
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Show Value", checked: !!config.showValue, onChange: (v) => updateConfig("showValue", v) }),
1405
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Min Label", value: config.minLabel, onChange: (v) => updateConfig("minLabel", v) }),
1406
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Max Label", value: config.maxLabel, onChange: (v) => updateConfig("maxLabel", v) })
1407
+ ] });
1408
+ case "rating":
1409
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Rating Settings", children: [
1410
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max Stars", value: config.max, placeholder: "5", onChange: (v) => updateConfig("max", v) }),
1411
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Icon", value: config.icon ?? "star", options: [{ label: "Star", value: "star" }, { label: "Heart", value: "heart" }, { label: "Circle", value: "circle" }], onChange: (v) => updateConfig("icon", v) }),
1412
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Show Labels", checked: !!config.showLabels, onChange: (v) => updateConfig("showLabels", v) })
1413
+ ] });
1414
+ case "nps":
1415
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "NPS Settings", children: [
1416
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Low Label", value: config.lowLabel ?? "Not likely", onChange: (v) => updateConfig("lowLabel", v) }),
1417
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "High Label", value: config.highLabel ?? "Very likely", onChange: (v) => updateConfig("highLabel", v) })
1418
+ ] });
1419
+ case "opinion_scale":
1420
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Scale Settings", children: [
1421
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Min", value: config.min, placeholder: "1", onChange: (v) => updateConfig("min", v) }),
1422
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max", value: config.max, placeholder: "5", onChange: (v) => updateConfig("max", v) }),
1423
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig("step", v) }),
1424
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Min Label", value: config.minLabel, onChange: (v) => updateConfig("minLabel", v) }),
1425
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Max Label", value: config.maxLabel, onChange: (v) => updateConfig("maxLabel", v) })
1426
+ ] });
1427
+ case "likert":
1428
+ return /* @__PURE__ */ jsxRuntime.jsx(ConfigSection, { title: "Likert Settings", children: /* @__PURE__ */ jsxRuntime.jsx(LikertLabelsEditor, { labels: config.labels ?? ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"], onChange: (v) => updateConfig("labels", v) }) });
1429
+ // ── Selection ──
1430
+ case "single_select":
1431
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Select Settings", children: [
1432
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Layout", value: config.layout ?? "vertical", options: [{ label: "Vertical", value: "vertical" }, { label: "Horizontal", value: "horizontal" }, { label: "Grid", value: "grid" }], onChange: (v) => updateConfig("layout", v) }),
1433
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Allow Other", checked: !!config.allowOther, onChange: (v) => updateConfig("allowOther", v) }),
1434
+ !!config.allowOther && /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Other Label", value: config.otherLabel ?? "Other", onChange: (v) => updateConfig("otherLabel", v) })
1435
+ ] });
1436
+ case "multi_select":
1437
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Multi-Select Settings", children: [
1438
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Layout", value: config.layout ?? "vertical", options: [{ label: "Vertical", value: "vertical" }, { label: "Horizontal", value: "horizontal" }, { label: "Grid", value: "grid" }], onChange: (v) => updateConfig("layout", v) }),
1439
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Min Selections", value: config.minSelections, onChange: (v) => updateConfig("minSelections", v) }),
1440
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max Selections", value: config.maxSelections, onChange: (v) => updateConfig("maxSelections", v) }),
1441
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Allow Other", checked: !!config.allowOther, onChange: (v) => updateConfig("allowOther", v) }),
1442
+ !!config.allowOther && /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Other Label", value: config.otherLabel ?? "Other", onChange: (v) => updateConfig("otherLabel", v) })
1443
+ ] });
1444
+ case "dropdown":
1445
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Dropdown Settings", children: [
1446
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Searchable", checked: !!config.searchable, onChange: (v) => updateConfig("searchable", v) }),
1447
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Allow Other", checked: !!config.allowOther, onChange: (v) => updateConfig("allowOther", v) }),
1448
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Multiple", checked: !!config.multiple, onChange: (v) => updateConfig("multiple", v) })
1449
+ ] });
1450
+ case "boolean":
1451
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Yes/No Settings", children: [
1452
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Style", value: config.style ?? "toggle", options: [{ label: "Toggle", value: "toggle" }, { label: "Radio", value: "radio" }, { label: "Checkbox", value: "checkbox" }], onChange: (v) => updateConfig("style", v) }),
1453
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "True Label", value: config.trueLabel ?? "Yes", onChange: (v) => updateConfig("trueLabel", v) }),
1454
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "False Label", value: config.falseLabel ?? "No", onChange: (v) => updateConfig("falseLabel", v) })
1455
+ ] });
1456
+ case "country_select":
1457
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Country Settings", children: [
1458
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Show Flags", checked: config.showFlags !== false, onChange: (v) => updateConfig("showFlags", v) }),
1459
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Priority Countries", value: config.priorityCountries?.join(", "), placeholder: "e.g. US, GB, CA", onChange: (v) => updateConfig("priorityCountries", v ? v.split(",").map((s) => s.trim()).filter(Boolean) : void 0) }),
1460
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Exclude Countries", value: config.excludeCountries?.join(", "), placeholder: "e.g. XX, YY", onChange: (v) => updateConfig("excludeCountries", v ? v.split(",").map((s) => s.trim()).filter(Boolean) : void 0) })
1461
+ ] });
1462
+ // ── Date/Time ──
1463
+ case "date":
1464
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Date Settings", children: [
1465
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Min Date", value: config.minDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig("minDate", v) }),
1466
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Max Date", value: config.maxDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig("maxDate", v) }),
1467
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Disable Past Dates", checked: !!config.disablePast, onChange: (v) => updateConfig("disablePast", v) }),
1468
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Disable Future Dates", checked: !!config.disableFuture, onChange: (v) => updateConfig("disableFuture", v) }),
1469
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Format", value: config.format, placeholder: "e.g. MM/DD/YYYY", onChange: (v) => updateConfig("format", v) })
1470
+ ] });
1471
+ case "time":
1472
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Time Settings", children: [
1473
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Format", value: config.format ?? "12h", options: [{ label: "12 Hour", value: "12h" }, { label: "24 Hour", value: "24h" }], onChange: (v) => updateConfig("format", v) }),
1474
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Minute Step", value: config.minuteStep, placeholder: "1", onChange: (v) => updateConfig("minuteStep", v) })
1475
+ ] });
1476
+ case "date_range":
1477
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Date Range Settings", children: [
1478
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Min Date", value: config.minDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig("minDate", v) }),
1479
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Max Date", value: config.maxDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig("maxDate", v) }),
1480
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max Range (days)", value: config.maxRangeDays, onChange: (v) => updateConfig("maxRangeDays", v) })
1481
+ ] });
1482
+ case "appointment":
1483
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Appointment Settings", children: [
1484
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Duration (min)", value: config.duration ?? 30, onChange: (v) => updateConfig("duration", v) }),
1485
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Timezone", value: config.timezone, placeholder: "e.g. America/New_York", onChange: (v) => updateConfig("timezone", v) }),
1486
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Slots URL", value: config.slotsUrl, placeholder: "API endpoint for available slots", onChange: (v) => updateConfig("slotsUrl", v) })
1487
+ ] });
1488
+ // ── Media ──
1489
+ case "file_upload":
1490
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Upload Settings", children: [
1491
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max Files", value: config.maxFiles ?? 1, onChange: (v) => updateConfig("maxFiles", v) }),
1492
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max Size (MB)", value: config.maxSizeMb ?? 10, onChange: (v) => updateConfig("maxSizeMb", v) }),
1493
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Accepted Types", value: config.accept?.join(", "), placeholder: "e.g. .pdf, .jpg, .png", onChange: (v) => updateConfig("accept", v ? v.split(",").map((s) => s.trim()) : void 0) }),
1494
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Upload URL", value: config.uploadUrl, placeholder: "Server upload endpoint", onChange: (v) => updateConfig("uploadUrl", v) })
1495
+ ] });
1496
+ case "signature":
1497
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Signature Settings", children: [
1498
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Pen Color", value: config.penColor, placeholder: "#000000", onChange: (v) => updateConfig("penColor", v) }),
1499
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Background", value: config.backgroundColor, placeholder: "#ffffff", onChange: (v) => updateConfig("backgroundColor", v) }),
1500
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Width (px)", value: config.width, onChange: (v) => updateConfig("width", v) }),
1501
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Height (px)", value: config.height, onChange: (v) => updateConfig("height", v) })
1502
+ ] });
1503
+ case "image_capture":
1504
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Camera Settings", children: [
1505
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Camera", value: config.camera ?? "any", options: [{ label: "Any", value: "any" }, { label: "Front", value: "front" }, { label: "Back", value: "back" }], onChange: (v) => updateConfig("camera", v) }),
1506
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max Size (MB)", value: config.maxSizeMb, onChange: (v) => updateConfig("maxSizeMb", v) }),
1507
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Allow Gallery", checked: config.allowGallery !== false, onChange: (v) => updateConfig("allowGallery", v) })
1508
+ ] });
1509
+ // ── Content & Visual ──
1510
+ case "welcome-screen":
1511
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Welcome Screen", children: [
1512
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Heading", value: config.heading ?? "Welcome", onChange: (v) => updateConfig("heading", v) }),
1513
+ /* @__PURE__ */ jsxRuntime.jsx(TextareaField, { label: "Description", value: config.description, onChange: (v) => updateConfig("description", v) }),
1514
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Button Text", value: config.buttonText ?? "Start", onChange: (v) => updateConfig("buttonText", v) }),
1515
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Image URL", value: config.imageUrl, placeholder: "https://...", onChange: (v) => updateConfig("imageUrl", v) }),
1516
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Alignment", value: config.alignment ?? "center", options: [{ label: "Left", value: "left" }, { label: "Center", value: "center" }, { label: "Right", value: "right" }], onChange: (v) => updateConfig("alignment", v) })
1517
+ ] });
1518
+ case "thank-you-screen":
1519
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Thank You Screen", children: [
1520
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Heading", value: config.heading ?? "Thank You!", onChange: (v) => updateConfig("heading", v) }),
1521
+ /* @__PURE__ */ jsxRuntime.jsx(TextareaField, { label: "Description", value: config.description ?? "", onChange: (v) => updateConfig("description", v) }),
1522
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Image URL", value: config.imageUrl, placeholder: "https://...", onChange: (v) => updateConfig("imageUrl", v) }),
1523
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Redirect URL", value: config.redirectUrl, placeholder: "https://...", onChange: (v) => updateConfig("redirectUrl", v) }),
1524
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Redirect Delay (s)", value: config.redirectDelay, placeholder: "0", onChange: (v) => updateConfig("redirectDelay", v) }),
1525
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Show Response Summary", checked: !!config.showSummary, onChange: (v) => updateConfig("showSummary", v) })
1526
+ ] });
1527
+ case "rich-text":
1528
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Rich Text", children: [
1529
+ /* @__PURE__ */ jsxRuntime.jsx(TextareaField, { label: "Content", value: config.content ?? "", rows: 6, onChange: (v) => updateConfig("content", v) }),
1530
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Format", value: config.format ?? "html", options: [{ label: "HTML", value: "html" }, { label: "Markdown", value: "markdown" }], onChange: (v) => updateConfig("format", v) })
1531
+ ] });
1532
+ case "image":
1533
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Image Settings", children: [
1534
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Source URL", value: config.src ?? "", placeholder: "https://...", onChange: (v) => updateConfig("src", v) }),
1535
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Alt Text", value: config.alt ?? "", onChange: (v) => updateConfig("alt", v) }),
1536
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Caption", value: config.caption, onChange: (v) => updateConfig("caption", v) }),
1537
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Alignment", value: config.alignment ?? "center", options: [{ label: "Left", value: "left" }, { label: "Center", value: "center" }, { label: "Right", value: "right" }], onChange: (v) => updateConfig("alignment", v) }),
1538
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Width", value: config.width, placeholder: "e.g. 100% or 300px", onChange: (v) => updateConfig("width", v) }),
1539
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Link URL", value: config.link, placeholder: "Click opens this URL", onChange: (v) => updateConfig("link", v) })
1540
+ ] });
1541
+ case "video":
1542
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Video Settings", children: [
1543
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Source URL", value: config.src ?? "", placeholder: "YouTube/Vimeo URL", onChange: (v) => updateConfig("src", v) }),
1544
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Provider", value: config.provider ?? "youtube", options: [{ label: "YouTube", value: "youtube" }, { label: "Vimeo", value: "vimeo" }, { label: "Direct URL", value: "url" }], onChange: (v) => updateConfig("provider", v) }),
1545
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Autoplay", checked: !!config.autoplay, onChange: (v) => updateConfig("autoplay", v) }),
1546
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Muted", checked: !!config.muted, onChange: (v) => updateConfig("muted", v) }),
1547
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Width", value: config.width, placeholder: "e.g. 100% or 640px", onChange: (v) => updateConfig("width", v) }),
1548
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Height", value: config.height, placeholder: "e.g. 360px", onChange: (v) => updateConfig("height", v) })
1549
+ ] });
1550
+ case "divider":
1551
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Divider Settings", children: [
1552
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Style", value: config.style ?? "solid", options: [{ label: "Solid", value: "solid" }, { label: "Dashed", value: "dashed" }, { label: "Dotted", value: "dotted" }], onChange: (v) => updateConfig("style", v) }),
1553
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Color", value: config.color, placeholder: "#e5e7eb", onChange: (v) => updateConfig("color", v) }),
1554
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Thickness (px)", value: config.thickness, placeholder: "1", onChange: (v) => updateConfig("thickness", v) }),
1555
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Spacing (px)", value: config.spacing, placeholder: "16", onChange: (v) => updateConfig("spacing", v) })
1556
+ ] });
1557
+ case "spacer":
1558
+ return /* @__PURE__ */ jsxRuntime.jsx(ConfigSection, { title: "Spacer Settings", children: /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Height (px)", value: config.height ?? 32, onChange: (v) => updateConfig("height", v) }) });
1559
+ // ── Structural ──
1560
+ case "section_header":
1561
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Header Settings", children: [
1562
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Level", value: config.level ?? "h3", options: [{ label: "H2", value: "h2" }, { label: "H3", value: "h3" }, { label: "H4", value: "h4" }], onChange: (v) => updateConfig("level", v) }),
1563
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Show Divider", checked: !!config.showDivider, onChange: (v) => updateConfig("showDivider", v) })
1564
+ ] });
1565
+ case "info_block":
1566
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Info Block", children: [
1567
+ /* @__PURE__ */ jsxRuntime.jsx(TextareaField, { label: "Content", value: config.content ?? "", rows: 4, onChange: (v) => updateConfig("content", v) }),
1568
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Variant", value: config.variant ?? "info", options: [{ label: "Info", value: "info" }, { label: "Warning", value: "warning" }, { label: "Success", value: "success" }, { label: "Error", value: "error" }], onChange: (v) => updateConfig("variant", v) })
1569
+ ] });
1570
+ case "page_break":
1571
+ return /* @__PURE__ */ jsxRuntime.jsx(ConfigSection, { title: "Page Break", children: /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Label", value: config.label, placeholder: "Next page label", onChange: (v) => updateConfig("label", v) }) });
1572
+ case "consent":
1573
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Consent Settings", children: [
1574
+ /* @__PURE__ */ jsxRuntime.jsx(TextareaField, { label: "Consent Text", value: config.text ?? "", rows: 4, onChange: (v) => updateConfig("text", v) }),
1575
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Checkbox Label", value: config.checkboxLabel ?? "I agree", onChange: (v) => updateConfig("checkboxLabel", v) }),
1576
+ /* @__PURE__ */ jsxRuntime.jsx(TextareaField, { label: "Expandable Text", value: config.expandableText, rows: 3, placeholder: "Additional text shown on expand", onChange: (v) => updateConfig("expandableText", v) })
1577
+ ] });
1578
+ // ── Advanced ──
1579
+ case "matrix":
1580
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Matrix Settings", children: [
1581
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Input Type", value: config.inputType ?? "radio", options: [{ label: "Radio", value: "radio" }, { label: "Checkbox", value: "checkbox" }, { label: "Text", value: "text" }, { label: "Number", value: "number" }], onChange: (v) => updateConfig("inputType", v) }),
1582
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Required", value: config.required ?? "none", options: [{ label: "All rows", value: "all" }, { label: "Any row", value: "any" }, { label: "None", value: "none" }], onChange: (v) => updateConfig("required", v) }),
1583
+ /* @__PURE__ */ jsxRuntime.jsx(MatrixItemsEditor, { label: "Rows", items: config.rows ?? [], onChange: (v) => updateConfig("rows", v) }),
1584
+ /* @__PURE__ */ jsxRuntime.jsx(MatrixItemsEditor, { label: "Columns", items: config.columns ?? [], onChange: (v) => updateConfig("columns", v) })
1585
+ ] });
1586
+ case "repeater":
1587
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Repeater Settings", children: [
1588
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Min Entries", value: config.minEntries, placeholder: "1", onChange: (v) => updateConfig("minEntries", v) }),
1589
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Max Entries", value: config.maxEntries, onChange: (v) => updateConfig("maxEntries", v) }),
1590
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Default Entries", value: config.defaultEntries, placeholder: "1", onChange: (v) => updateConfig("defaultEntries", v) }),
1591
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Add Button Label", value: config.addLabel, placeholder: "Add Entry", onChange: (v) => updateConfig("addLabel", v) }),
1592
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Remove Button Label", value: config.removeLabel, placeholder: "Remove", onChange: (v) => updateConfig("removeLabel", v) }),
1593
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-[11px] text-muted-foreground leading-relaxed", children: "Sub-fields for each repeater entry are configured by nesting questions inside the repeater in the schema JSON." })
1594
+ ] });
1595
+ case "address": {
1596
+ const ADDRESS_FIELDS = [
1597
+ { label: "Street", value: "street" },
1598
+ { label: "Street 2", value: "street2" },
1599
+ { label: "City", value: "city" },
1600
+ { label: "State", value: "state" },
1601
+ { label: "ZIP Code", value: "zip" },
1602
+ { label: "Country", value: "country" }
1603
+ ];
1604
+ const activeFields = config.fields ?? ["street", "city", "state", "zip", "country"];
1605
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Address Settings", children: [
1606
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Provider", value: config.provider ?? "none", options: [{ label: "None", value: "none" }, { label: "Google", value: "google" }, { label: "Mapbox", value: "mapbox" }], onChange: (v) => updateConfig("provider", v) }),
1607
+ config.provider !== "none" && /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "API Key", value: config.apiKey, placeholder: "Provider API key", onChange: (v) => updateConfig("apiKey", v) }),
1608
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Default Country", value: config.defaultCountry, placeholder: "e.g. US", onChange: (v) => updateConfig("defaultCountry", v) }),
1609
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1610
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-2 block", children: "Fields" }),
1611
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: ADDRESS_FIELDS.map((f) => /* @__PURE__ */ jsxRuntime.jsx(
1612
+ ToggleField,
1613
+ {
1614
+ label: f.label,
1615
+ checked: activeFields.includes(f.value),
1616
+ onChange: (checked) => {
1617
+ const next = checked ? [...activeFields, f.value] : activeFields.filter((v) => v !== f.value);
1618
+ updateConfig("fields", next.length > 0 ? next : void 0);
1619
+ }
1620
+ },
1621
+ f.value
1622
+ )) })
1623
+ ] })
1624
+ ] });
1625
+ }
1626
+ case "payment":
1627
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Payment Settings", children: [
1628
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Provider", value: config.provider ?? "stripe", options: [{ label: "Stripe", value: "stripe" }, { label: "PayPal", value: "paypal" }], onChange: (v) => updateConfig("provider", v) }),
1629
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Public Key", value: config.publicKey ?? "", placeholder: "Provider public/client key", onChange: (v) => updateConfig("publicKey", v) }),
1630
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Amount", value: config.amount, onChange: (v) => updateConfig("amount", v) }),
1631
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Amount Field", value: config.amountField, placeholder: "Field ID for dynamic amount", onChange: (v) => updateConfig("amountField", v) }),
1632
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Currency", value: config.currency, placeholder: "USD", onChange: (v) => updateConfig("currency", v) }),
1633
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Description", value: config.description, onChange: (v) => updateConfig("description", v) })
1634
+ ] });
1635
+ case "calculated":
1636
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Calculated Field", children: [
1637
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Expression", value: config.expression ?? "", placeholder: "e.g. {q1} + {q2}", onChange: (v) => updateConfig("expression", v) }),
1638
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Format", value: config.format ?? "number", options: [{ label: "Number", value: "number" }, { label: "Currency", value: "currency" }, { label: "Percentage", value: "percentage" }], onChange: (v) => updateConfig("format", v) }),
1639
+ /* @__PURE__ */ jsxRuntime.jsx(NumberField, { label: "Decimal Places", value: config.decimalPlaces, placeholder: "2", onChange: (v) => updateConfig("decimalPlaces", v) }),
1640
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Prefix", value: config.prefix, onChange: (v) => updateConfig("prefix", v) }),
1641
+ /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Suffix", value: config.suffix, onChange: (v) => updateConfig("suffix", v) }),
1642
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Visible to User", checked: config.visible !== false, onChange: (v) => updateConfig("visible", v) })
1643
+ ] });
1644
+ case "hidden":
1645
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Hidden Field", children: [
1646
+ /* @__PURE__ */ jsxRuntime.jsx(SelectField, { label: "Source", value: config.source ?? "static", options: [{ label: "Static Value", value: "static" }, { label: "URL Parameter", value: "url_param" }, { label: "Cookie", value: "cookie" }, { label: "Referrer", value: "referrer" }], onChange: (v) => updateConfig("source", v) }),
1647
+ config.source === "static" && /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Default Value", value: config.defaultValue, onChange: (v) => updateConfig("defaultValue", v) }),
1648
+ config.source === "url_param" && /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Parameter Name", value: config.paramName, placeholder: "e.g. utm_source", onChange: (v) => updateConfig("paramName", v) }),
1649
+ config.source === "cookie" && /* @__PURE__ */ jsxRuntime.jsx(TextField, { label: "Cookie Name", value: config.cookieName, onChange: (v) => updateConfig("cookieName", v) })
1650
+ ] });
1651
+ case "scoring":
1652
+ return /* @__PURE__ */ jsxRuntime.jsxs(ConfigSection, { title: "Scoring Settings", children: [
1653
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleField, { label: "Show Score", checked: !!config.showScore, onChange: (v) => updateConfig("showScore", v) }),
1654
+ /* @__PURE__ */ jsxRuntime.jsx(ScoringOptionsEditor, { options: config.options ?? [], onChange: (v) => updateConfig("options", v) }),
1655
+ /* @__PURE__ */ jsxRuntime.jsx(ScoreRangesEditor, { ranges: config.scoreRanges ?? [], onChange: (v) => updateConfig("scoreRanges", v) })
1656
+ ] });
1657
+ case "ranking":
1658
+ return /* @__PURE__ */ jsxRuntime.jsx(ConfigSection, { title: "Ranking Settings", children: /* @__PURE__ */ jsxRuntime.jsx(MatrixItemsEditor, { label: "Items", items: config.items ?? [], onChange: (v) => updateConfig("items", v) }) });
1659
+ // Types with no additional config
1660
+ case "email":
1661
+ case "phone":
1662
+ case "phone_international":
1663
+ case "url":
1664
+ return null;
1665
+ default:
1666
+ return null;
1667
+ }
1668
+ }
1669
+ function ConfigSection({ title, children }) {
1670
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1671
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { className: "mb-4" }),
1672
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3 block", children: title }),
1673
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children })
1674
+ ] });
1675
+ }
1676
+ function TextField({
1677
+ label,
1678
+ value,
1679
+ placeholder,
1680
+ onChange
1681
+ }) {
1682
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1683
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
1684
+ /* @__PURE__ */ jsxRuntime.jsx(
1685
+ fieldcraftReact.Input,
1686
+ {
1687
+ value: value ?? "",
1688
+ onChange: (e) => onChange(e.target.value || void 0),
1689
+ placeholder
1690
+ }
1691
+ )
1692
+ ] });
1693
+ }
1694
+ function TextareaField({
1695
+ label,
1696
+ value,
1697
+ placeholder,
1698
+ rows,
1699
+ onChange
1700
+ }) {
1701
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1702
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
1703
+ /* @__PURE__ */ jsxRuntime.jsx(
1704
+ fieldcraftReact.Textarea,
1705
+ {
1706
+ value: value ?? "",
1707
+ onChange: (e) => onChange(e.target.value || void 0),
1708
+ placeholder,
1709
+ rows: rows ?? 3
1710
+ }
1711
+ )
1712
+ ] });
1713
+ }
1714
+ function NumberField({
1715
+ label,
1716
+ value,
1717
+ placeholder,
1718
+ onChange
1719
+ }) {
1720
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1721
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
1722
+ /* @__PURE__ */ jsxRuntime.jsx(
1723
+ fieldcraftReact.Input,
1724
+ {
1725
+ type: "number",
1726
+ value: value ?? "",
1727
+ onChange: (e) => {
1728
+ const v = e.target.value;
1729
+ onChange(v === "" ? void 0 : Number(v));
1730
+ },
1731
+ placeholder
1732
+ }
1733
+ )
1734
+ ] });
1735
+ }
1736
+ function ToggleField({
1737
+ label,
1738
+ checked,
1739
+ onChange
1740
+ }) {
1741
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1742
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: label }),
1743
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Switch, { checked, onCheckedChange: onChange })
1744
+ ] });
1745
+ }
1746
+ function SelectField({
1747
+ label,
1748
+ value,
1749
+ options,
1750
+ onChange
1751
+ }) {
1752
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1753
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
1754
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1755
+ /* @__PURE__ */ jsxRuntime.jsx(
1756
+ "select",
1757
+ {
1758
+ value,
1759
+ onChange: (e) => onChange(e.target.value),
1760
+ className: "flex h-9 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
1761
+ children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
1762
+ }
1763
+ ),
1764
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) })
1765
+ ] })
1766
+ ] });
1767
+ }
1768
+ function LikertLabelsEditor({
1769
+ labels,
1770
+ onChange
1771
+ }) {
1772
+ const handleUpdate = (index, value) => {
1773
+ const updated = labels.map((l, i) => i === index ? value : l);
1774
+ onChange(updated);
1775
+ };
1776
+ const handleAdd = () => {
1777
+ onChange([...labels, `Label ${labels.length + 1}`]);
1778
+ };
1779
+ const handleRemove = (index) => {
1780
+ if (labels.length <= 2) return;
1781
+ onChange(labels.filter((_, i) => i !== index));
1782
+ };
1783
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1784
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
1785
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: "Scale Labels" }),
1786
+ /* @__PURE__ */ jsxRuntime.jsx(
1787
+ "button",
1788
+ {
1789
+ type: "button",
1790
+ onClick: handleAdd,
1791
+ className: "text-xs text-primary hover:underline",
1792
+ children: "+ Add"
1793
+ }
1794
+ )
1795
+ ] }),
1796
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: labels.map((label, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 group", children: [
1797
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground w-4 text-right shrink-0", children: index + 1 }),
1798
+ /* @__PURE__ */ jsxRuntime.jsx(
1799
+ fieldcraftReact.Input,
1800
+ {
1801
+ value: label,
1802
+ onChange: (e) => handleUpdate(index, e.target.value),
1803
+ className: "h-7 text-xs flex-1"
1804
+ }
1805
+ ),
1806
+ labels.length > 2 && /* @__PURE__ */ jsxRuntime.jsx(
1807
+ "button",
1808
+ {
1809
+ type: "button",
1810
+ onClick: () => handleRemove(index),
1811
+ className: "text-xs text-destructive opacity-0 group-hover:opacity-100 transition-opacity px-1",
1812
+ children: "x"
1813
+ }
1814
+ )
1815
+ ] }, index)) })
1816
+ ] });
1817
+ }
1818
+ function MatrixItemsEditor({
1819
+ label,
1820
+ items,
1821
+ onChange
1822
+ }) {
1823
+ const handleUpdate = (index, newLabel) => {
1824
+ const updated = items.map(
1825
+ (item, i) => i === index ? { label: newLabel, value: newLabel.toLowerCase().replace(/\s+/g, "_") } : item
1826
+ );
1827
+ onChange(updated);
1828
+ };
1829
+ const handleAdd = () => {
1830
+ const n = items.length + 1;
1831
+ onChange([...items, { label: `${label.slice(0, -1)} ${n}`, value: `${label.toLowerCase().slice(0, -1)}${n}` }]);
1832
+ };
1833
+ const handleRemove = (index) => {
1834
+ if (items.length <= 1) return;
1835
+ onChange(items.filter((_, i) => i !== index));
1836
+ };
1837
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1838
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
1839
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: label }),
1840
+ /* @__PURE__ */ jsxRuntime.jsx(
1841
+ "button",
1842
+ {
1843
+ type: "button",
1844
+ onClick: handleAdd,
1845
+ className: "text-xs text-primary hover:underline",
1846
+ children: "+ Add"
1847
+ }
1848
+ )
1849
+ ] }),
1850
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 group", children: [
1851
+ /* @__PURE__ */ jsxRuntime.jsx(
1852
+ fieldcraftReact.Input,
1853
+ {
1854
+ value: item.label,
1855
+ onChange: (e) => handleUpdate(index, e.target.value),
1856
+ className: "h-7 text-xs flex-1"
1857
+ }
1858
+ ),
1859
+ items.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
1860
+ "button",
1861
+ {
1862
+ type: "button",
1863
+ onClick: () => handleRemove(index),
1864
+ className: "text-xs text-destructive opacity-0 group-hover:opacity-100 transition-opacity px-1",
1865
+ children: "x"
1866
+ }
1867
+ )
1868
+ ] }, index)) })
1869
+ ] });
1870
+ }
1871
+ function ScoringOptionsEditor({
1872
+ options,
1873
+ onChange
1874
+ }) {
1875
+ const handleUpdate = (index, field, val) => {
1876
+ const updated = options.map((opt, i) => {
1877
+ if (i !== index) return opt;
1878
+ if (field === "label") {
1879
+ const label = val;
1880
+ return { ...opt, label, value: label.toLowerCase().replace(/\s+/g, "_") };
1881
+ }
1882
+ return { ...opt, score: val };
1883
+ });
1884
+ onChange(updated);
1885
+ };
1886
+ const handleAdd = () => {
1887
+ const n = options.length + 1;
1888
+ onChange([...options, { label: `Option ${n}`, value: `option_${n}`, score: 0 }]);
1889
+ };
1890
+ const handleRemove = (index) => {
1891
+ if (options.length <= 1) return;
1892
+ onChange(options.filter((_, i) => i !== index));
1893
+ };
1894
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1895
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
1896
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: "Score Options" }),
1897
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: handleAdd, className: "text-xs text-primary hover:underline", children: "+ Add" })
1898
+ ] }),
1899
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: options.map((opt, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 group", children: [
1900
+ /* @__PURE__ */ jsxRuntime.jsx(
1901
+ fieldcraftReact.Input,
1902
+ {
1903
+ value: opt.label,
1904
+ onChange: (e) => handleUpdate(index, "label", e.target.value),
1905
+ className: "h-7 text-xs flex-1",
1906
+ placeholder: "Label"
1907
+ }
1908
+ ),
1909
+ /* @__PURE__ */ jsxRuntime.jsx(
1910
+ fieldcraftReact.Input,
1911
+ {
1912
+ type: "number",
1913
+ value: opt.score,
1914
+ onChange: (e) => handleUpdate(index, "score", Number(e.target.value)),
1915
+ className: "h-7 text-xs w-16",
1916
+ placeholder: "Score"
1917
+ }
1918
+ ),
1919
+ options.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => handleRemove(index), className: "text-xs text-destructive opacity-0 group-hover:opacity-100 transition-opacity px-1", children: "x" })
1920
+ ] }, index)) })
1921
+ ] });
1922
+ }
1923
+ function ScoreRangesEditor({
1924
+ ranges,
1925
+ onChange
1926
+ }) {
1927
+ const handleUpdate = (index, updates) => {
1928
+ const updated = ranges.map((r, i) => i === index ? { ...r, ...updates } : r);
1929
+ onChange(updated);
1930
+ };
1931
+ const handleAdd = () => {
1932
+ const lastMax = ranges.length > 0 ? ranges[ranges.length - 1].max : 0;
1933
+ onChange([...ranges, { min: lastMax, max: lastMax + 10, label: `Range ${ranges.length + 1}` }]);
1934
+ };
1935
+ const handleRemove = (index) => {
1936
+ onChange(ranges.filter((_, i) => i !== index));
1937
+ };
1938
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1939
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
1940
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: "Score Ranges" }),
1941
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: handleAdd, className: "text-xs text-primary hover:underline", children: "+ Add" })
1942
+ ] }),
1943
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: ranges.map((range, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 rounded-md border border-border space-y-1.5 group", children: [
1944
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
1945
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Input, { type: "number", value: range.min, onChange: (e) => handleUpdate(index, { min: Number(e.target.value) }), className: "h-7 text-xs w-16", placeholder: "Min" }),
1946
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "to" }),
1947
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Input, { type: "number", value: range.max, onChange: (e) => handleUpdate(index, { max: Number(e.target.value) }), className: "h-7 text-xs w-16", placeholder: "Max" }),
1948
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Input, { value: range.label, onChange: (e) => handleUpdate(index, { label: e.target.value }), className: "h-7 text-xs flex-1", placeholder: "Label" }),
1949
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => handleRemove(index), className: "text-xs text-destructive opacity-0 group-hover:opacity-100 transition-opacity px-1", children: "x" })
1950
+ ] }),
1951
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Input, { value: range.description ?? "", onChange: (e) => handleUpdate(index, { description: e.target.value || void 0 }), className: "h-7 text-xs", placeholder: "Description (optional)" })
1952
+ ] }, index)) })
1953
+ ] });
1954
+ }
1955
+ var RULE_TYPES = [
1956
+ { value: "minLength", label: "Min Length", description: "Minimum character count" },
1957
+ { value: "maxLength", label: "Max Length", description: "Maximum character count" },
1958
+ { value: "min", label: "Min Value", description: "Minimum numeric value" },
1959
+ { value: "max", label: "Max Value", description: "Maximum numeric value" },
1960
+ { value: "pattern", label: "Pattern", description: "Regex pattern match" },
1961
+ { value: "email", label: "Email Format", description: "Valid email address" },
1962
+ { value: "phone", label: "Phone Format", description: "Valid phone number" },
1963
+ { value: "url", label: "URL Format", description: "Valid URL" },
1964
+ { value: "date", label: "Date Range", description: "Date within range" },
1965
+ { value: "fileSize", label: "File Size", description: "Max file size in MB" },
1966
+ { value: "fileType", label: "File Type", description: "Accepted file types" },
1967
+ { value: "custom", label: "Custom", description: "Named custom validator" }
1968
+ ];
1969
+ function ValidationRulesEditor({ question, onUpdate }) {
1970
+ const rules = question.validation ?? [];
1971
+ const updateRules = (next) => {
1972
+ onUpdate({ validation: next.length > 0 ? next : void 0 });
1973
+ };
1974
+ const addRule = (type) => {
1975
+ const newRule = createDefaultRule(type);
1976
+ if (newRule) updateRules([...rules, newRule]);
1977
+ };
1978
+ const updateRule = (index, updated) => {
1979
+ updateRules(rules.map((r, i) => i === index ? updated : r));
1980
+ };
1981
+ const removeRule = (index) => {
1982
+ updateRules(rules.filter((_, i) => i !== index));
1983
+ };
1984
+ const usedTypes = new Set(rules.map((r) => r.type));
1985
+ const availableTypes = RULE_TYPES.filter((t) => !usedTypes.has(t.value));
1986
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1987
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { className: "mb-4" }),
1988
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3 block", children: "Validation Rules" }),
1989
+ rules.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mb-3", children: "No validation rules configured." }),
1990
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: rules.map((rule, index) => /* @__PURE__ */ jsxRuntime.jsx(
1991
+ RuleRow,
1992
+ {
1993
+ rule,
1994
+ onChange: (updated) => updateRule(index, updated),
1995
+ onRemove: () => removeRule(index)
1996
+ },
1997
+ `${rule.type}-${index}`
1998
+ )) }),
1999
+ availableTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2000
+ /* @__PURE__ */ jsxRuntime.jsxs(
2001
+ "select",
2002
+ {
2003
+ value: "",
2004
+ onChange: (e) => {
2005
+ if (e.target.value) addRule(e.target.value);
2006
+ },
2007
+ className: "flex h-8 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-xs shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-muted-foreground cursor-pointer",
2008
+ children: [
2009
+ /* @__PURE__ */ jsxRuntime.jsxs("option", { value: "", children: [
2010
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 12 }),
2011
+ " Add validation rule..."
2012
+ ] }),
2013
+ availableTypes.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t.value, children: t.label }, t.value))
2014
+ ]
2015
+ }
2016
+ ),
2017
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) })
2018
+ ] }) })
2019
+ ] });
2020
+ }
2021
+ function RuleRow({
2022
+ rule,
2023
+ onChange,
2024
+ onRemove
2025
+ }) {
2026
+ const ruleInfo = RULE_TYPES.find((t) => t.value === rule.type);
2027
+ const label = ruleInfo?.label ?? rule.type;
2028
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 rounded-md border border-border space-y-1.5 group", children: [
2029
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2030
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-foreground", children: label }),
2031
+ /* @__PURE__ */ jsxRuntime.jsx(
2032
+ "button",
2033
+ {
2034
+ type: "button",
2035
+ onClick: onRemove,
2036
+ className: "opacity-0 group-hover:opacity-100 transition-opacity",
2037
+ title: "Remove rule",
2038
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, className: "text-destructive", strokeWidth: 1.75 })
2039
+ }
2040
+ )
2041
+ ] }),
2042
+ /* @__PURE__ */ jsxRuntime.jsx(RuleFields, { rule, onChange }),
2043
+ /* @__PURE__ */ jsxRuntime.jsx(
2044
+ fieldcraftReact.Input,
2045
+ {
2046
+ value: rule.message ?? "",
2047
+ onChange: (e) => onChange({ ...rule, message: e.target.value || void 0 }),
2048
+ className: "h-7 text-xs",
2049
+ placeholder: "Custom error message (optional)"
2050
+ }
2051
+ )
2052
+ ] });
2053
+ }
2054
+ function RuleFields({
2055
+ rule,
2056
+ onChange
2057
+ }) {
2058
+ switch (rule.type) {
2059
+ case "min":
2060
+ case "max":
2061
+ return /* @__PURE__ */ jsxRuntime.jsx(
2062
+ fieldcraftReact.Input,
2063
+ {
2064
+ type: "number",
2065
+ value: rule.value,
2066
+ onChange: (e) => onChange({ ...rule, value: Number(e.target.value) }),
2067
+ className: "h-7 text-xs",
2068
+ placeholder: rule.type === "min" ? "Minimum value" : "Maximum value"
2069
+ }
2070
+ );
2071
+ case "minLength":
2072
+ case "maxLength":
2073
+ return /* @__PURE__ */ jsxRuntime.jsx(
2074
+ fieldcraftReact.Input,
2075
+ {
2076
+ type: "number",
2077
+ value: rule.value,
2078
+ onChange: (e) => onChange({ ...rule, value: Number(e.target.value) }),
2079
+ className: "h-7 text-xs",
2080
+ placeholder: rule.type === "minLength" ? "Minimum characters" : "Maximum characters"
2081
+ }
2082
+ );
2083
+ case "pattern":
2084
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
2085
+ /* @__PURE__ */ jsxRuntime.jsx(
2086
+ fieldcraftReact.Input,
2087
+ {
2088
+ value: rule.regex,
2089
+ onChange: (e) => onChange({ ...rule, regex: e.target.value }),
2090
+ className: "h-7 text-xs font-mono",
2091
+ placeholder: "Regex pattern, e.g. ^[A-Z]+"
2092
+ }
2093
+ ),
2094
+ /* @__PURE__ */ jsxRuntime.jsx(
2095
+ fieldcraftReact.Input,
2096
+ {
2097
+ value: rule.flags ?? "",
2098
+ onChange: (e) => onChange({ ...rule, flags: e.target.value || void 0 }),
2099
+ className: "h-7 text-xs font-mono",
2100
+ placeholder: "Flags, e.g. gi"
2101
+ }
2102
+ )
2103
+ ] });
2104
+ case "date":
2105
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
2106
+ /* @__PURE__ */ jsxRuntime.jsx(
2107
+ fieldcraftReact.Input,
2108
+ {
2109
+ value: rule.min ?? "",
2110
+ onChange: (e) => onChange({ ...rule, min: e.target.value || void 0 }),
2111
+ className: "h-7 text-xs flex-1",
2112
+ placeholder: "Min date (YYYY-MM-DD)"
2113
+ }
2114
+ ),
2115
+ /* @__PURE__ */ jsxRuntime.jsx(
2116
+ fieldcraftReact.Input,
2117
+ {
2118
+ value: rule.max ?? "",
2119
+ onChange: (e) => onChange({ ...rule, max: e.target.value || void 0 }),
2120
+ className: "h-7 text-xs flex-1",
2121
+ placeholder: "Max date"
2122
+ }
2123
+ )
2124
+ ] });
2125
+ case "fileSize":
2126
+ return /* @__PURE__ */ jsxRuntime.jsx(
2127
+ fieldcraftReact.Input,
2128
+ {
2129
+ type: "number",
2130
+ value: rule.maxMb,
2131
+ onChange: (e) => onChange({ ...rule, maxMb: Number(e.target.value) }),
2132
+ className: "h-7 text-xs",
2133
+ placeholder: "Max size in MB"
2134
+ }
2135
+ );
2136
+ case "fileType":
2137
+ return /* @__PURE__ */ jsxRuntime.jsx(
2138
+ fieldcraftReact.Input,
2139
+ {
2140
+ value: rule.accept.join(", "),
2141
+ onChange: (e) => onChange({ ...rule, accept: e.target.value.split(",").map((s) => s.trim()).filter(Boolean) }),
2142
+ className: "h-7 text-xs",
2143
+ placeholder: ".pdf, .jpg, .png"
2144
+ }
2145
+ );
2146
+ case "custom":
2147
+ return /* @__PURE__ */ jsxRuntime.jsx(
2148
+ fieldcraftReact.Input,
2149
+ {
2150
+ value: rule.name,
2151
+ onChange: (e) => onChange({ ...rule, name: e.target.value }),
2152
+ className: "h-7 text-xs",
2153
+ placeholder: "Validator name"
2154
+ }
2155
+ );
2156
+ // email, phone, url — no extra fields needed
2157
+ case "email":
2158
+ case "phone":
2159
+ case "url":
2160
+ return null;
2161
+ default:
2162
+ return null;
2163
+ }
2164
+ }
2165
+ function createDefaultRule(type) {
2166
+ switch (type) {
2167
+ case "required":
2168
+ return { type: "required" };
2169
+ case "min":
2170
+ return { type: "min", value: 0 };
2171
+ case "max":
2172
+ return { type: "max", value: 100 };
2173
+ case "minLength":
2174
+ return { type: "minLength", value: 1 };
2175
+ case "maxLength":
2176
+ return { type: "maxLength", value: 255 };
2177
+ case "pattern":
2178
+ return { type: "pattern", regex: "" };
2179
+ case "email":
2180
+ return { type: "email" };
2181
+ case "phone":
2182
+ return { type: "phone" };
2183
+ case "url":
2184
+ return { type: "url" };
2185
+ case "date":
2186
+ return { type: "date" };
2187
+ case "fileSize":
2188
+ return { type: "fileSize", maxMb: 10 };
2189
+ case "fileType":
2190
+ return { type: "fileType", accept: [] };
2191
+ case "custom":
2192
+ return { type: "custom", name: "" };
2193
+ default:
2194
+ return null;
2195
+ }
2196
+ }
2197
+ var OPERATORS = [
2198
+ { value: "eq", label: "equals" },
2199
+ { value: "neq", label: "not equals" },
2200
+ { value: "gt", label: "greater than" },
2201
+ { value: "gte", label: "greater or equal" },
2202
+ { value: "lt", label: "less than" },
2203
+ { value: "lte", label: "less or equal" },
2204
+ { value: "contains", label: "contains" },
2205
+ { value: "notContains", label: "not contains" },
2206
+ { value: "startsWith", label: "starts with" },
2207
+ { value: "endsWith", label: "ends with" },
2208
+ { value: "in", label: "in list" },
2209
+ { value: "notIn", label: "not in list" },
2210
+ { value: "exists", label: "has value" },
2211
+ { value: "notExists", label: "is empty" },
2212
+ { value: "between", label: "between" },
2213
+ { value: "matches", label: "matches regex" }
2214
+ ];
2215
+ function getFieldOptions(schema, excludeId) {
2216
+ const fields = [];
2217
+ for (const section of schema.sections) {
2218
+ for (const q of section.questions) {
2219
+ if (q.id !== excludeId) {
2220
+ fields.push({ id: q.id, label: q.label || q.id });
2221
+ }
2222
+ }
2223
+ }
2224
+ return fields;
2225
+ }
2226
+ function ConditionEditor({ question, schema, onUpdate }) {
2227
+ const showIf = question.showIf;
2228
+ const fieldOptions = getFieldOptions(schema, question.id);
2229
+ const updateShowIf = (next) => {
2230
+ onUpdate({ showIf: next });
2231
+ };
2232
+ const hasConditions = showIf && showIf.conditions && showIf.conditions.length > 0;
2233
+ const addCondition = () => {
2234
+ const firstField = fieldOptions.length > 0 ? fieldOptions[0].id : "";
2235
+ const newCondition = {
2236
+ field: firstField,
2237
+ operator: "eq",
2238
+ value: ""
2239
+ };
2240
+ if (!showIf) {
2241
+ updateShowIf({ combine: "AND", conditions: [newCondition] });
2242
+ } else {
2243
+ updateShowIf({
2244
+ ...showIf,
2245
+ conditions: [...showIf.conditions ?? [], newCondition]
2246
+ });
2247
+ }
2248
+ };
2249
+ const updateCondition = (index, updates) => {
2250
+ if (!showIf?.conditions) return;
2251
+ const updated = showIf.conditions.map((c, i) => i === index ? { ...c, ...updates } : c);
2252
+ updateShowIf({ ...showIf, conditions: updated });
2253
+ };
2254
+ const removeCondition = (index) => {
2255
+ if (!showIf?.conditions) return;
2256
+ const updated = showIf.conditions.filter((_, i) => i !== index);
2257
+ if (updated.length === 0) {
2258
+ updateShowIf(void 0);
2259
+ } else {
2260
+ updateShowIf({ ...showIf, conditions: updated });
2261
+ }
2262
+ };
2263
+ const toggleCombine = () => {
2264
+ if (!showIf) return;
2265
+ updateShowIf({ ...showIf, combine: showIf.combine === "AND" ? "OR" : "AND" });
2266
+ };
2267
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2268
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { className: "mb-4" }),
2269
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3 block", children: "Visibility Rules" }),
2270
+ !hasConditions && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mb-3", children: "Always visible. Add a rule to show this field conditionally." }),
2271
+ hasConditions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 mb-3", children: showIf.conditions.map((cond, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2272
+ index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2273
+ "button",
2274
+ {
2275
+ type: "button",
2276
+ onClick: toggleCombine,
2277
+ className: "text-[10px] font-semibold uppercase tracking-wider text-primary mb-1.5 block cursor-pointer hover:underline",
2278
+ children: showIf.combine ?? "AND"
2279
+ }
2280
+ ),
2281
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 rounded-md border border-border space-y-1.5 group", children: [
2282
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2283
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground uppercase tracking-wider", children: "When" }),
2284
+ /* @__PURE__ */ jsxRuntime.jsx(
2285
+ "button",
2286
+ {
2287
+ type: "button",
2288
+ onClick: () => removeCondition(index),
2289
+ className: "opacity-0 group-hover:opacity-100 transition-opacity",
2290
+ title: "Remove condition",
2291
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, className: "text-destructive", strokeWidth: 1.75 })
2292
+ }
2293
+ )
2294
+ ] }),
2295
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2296
+ /* @__PURE__ */ jsxRuntime.jsxs(
2297
+ "select",
2298
+ {
2299
+ value: cond.field ?? "",
2300
+ onChange: (e) => updateCondition(index, { field: e.target.value }),
2301
+ className: "flex h-7 w-full appearance-none rounded-md border border-input bg-card px-2 py-1 pr-7 text-xs shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
2302
+ children: [
2303
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select field..." }),
2304
+ fieldOptions.map((f) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: f.id, children: f.label }, f.id))
2305
+ ]
2306
+ }
2307
+ ),
2308
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 size-3 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) })
2309
+ ] }),
2310
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2311
+ /* @__PURE__ */ jsxRuntime.jsx(
2312
+ "select",
2313
+ {
2314
+ value: cond.operator ?? "eq",
2315
+ onChange: (e) => updateCondition(index, { operator: e.target.value }),
2316
+ className: "flex h-7 w-full appearance-none rounded-md border border-input bg-card px-2 py-1 pr-7 text-xs shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
2317
+ children: OPERATORS.map((op) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: op.value, children: op.label }, op.value))
2318
+ }
2319
+ ),
2320
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 size-3 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) })
2321
+ ] }),
2322
+ cond.operator !== "exists" && cond.operator !== "notExists" && /* @__PURE__ */ jsxRuntime.jsx(
2323
+ fieldcraftReact.Input,
2324
+ {
2325
+ value: String(cond.value ?? ""),
2326
+ onChange: (e) => updateCondition(index, { value: e.target.value }),
2327
+ className: "h-7 text-xs",
2328
+ placeholder: "Value"
2329
+ }
2330
+ )
2331
+ ] })
2332
+ ] }, index)) }),
2333
+ fieldOptions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
2334
+ "button",
2335
+ {
2336
+ type: "button",
2337
+ onClick: addCondition,
2338
+ className: "flex items-center gap-1.5 text-xs text-primary hover:underline",
2339
+ children: [
2340
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 12, strokeWidth: 2 }),
2341
+ "Add condition"
2342
+ ]
2343
+ }
2344
+ ) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-muted-foreground", children: "Add other fields to create visibility rules." })
2345
+ ] });
2346
+ }
2347
+ function FormSettingsPanel({ schema, onUpdate }) {
2348
+ const settings = schema.settings ?? {};
2349
+ const updateSettings = (updates) => {
2350
+ onUpdate({ ...schema, settings: { ...settings, ...updates } });
2351
+ };
2352
+ const updateSubmitButton = (updates) => {
2353
+ onUpdate({
2354
+ ...schema,
2355
+ settings: {
2356
+ ...settings,
2357
+ submitButton: { ...settings.submitButton, ...updates }
2358
+ }
2359
+ });
2360
+ };
2361
+ const updateNavigation = (updates) => {
2362
+ onUpdate({
2363
+ ...schema,
2364
+ settings: {
2365
+ ...settings,
2366
+ navigation: { ...settings.navigation, ...updates }
2367
+ }
2368
+ });
2369
+ };
2370
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
2371
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingsSection, { title: "Display", children: [
2372
+ /* @__PURE__ */ jsxRuntime.jsx(
2373
+ SettingsSelect,
2374
+ {
2375
+ label: "Mode",
2376
+ value: settings.displayMode ?? "classic",
2377
+ options: [
2378
+ { label: "Classic", value: "classic" },
2379
+ { label: "Stepped", value: "stepped" },
2380
+ { label: "Conversational", value: "conversational" }
2381
+ ],
2382
+ onChange: (v) => updateSettings({ displayMode: v })
2383
+ }
2384
+ ),
2385
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsToggle, { label: "Show Progress", checked: settings.showProgress !== false, onChange: (v) => updateSettings({ showProgress: v }) }),
2386
+ settings.showProgress !== false && /* @__PURE__ */ jsxRuntime.jsx(
2387
+ SettingsSelect,
2388
+ {
2389
+ label: "Progress Style",
2390
+ value: settings.progressStyle ?? "bar",
2391
+ options: [
2392
+ { label: "Bar", value: "bar" },
2393
+ { label: "Steps", value: "steps" },
2394
+ { label: "Percentage", value: "percentage" }
2395
+ ],
2396
+ onChange: (v) => updateSettings({ progressStyle: v })
2397
+ }
2398
+ )
2399
+ ] }),
2400
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingsSection, { title: "Submit Button", children: [
2401
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsField, { label: "Label", value: settings.submitButton?.label ?? "", placeholder: "Submit", onChange: (v) => updateSubmitButton({ label: v || void 0 }) }),
2402
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsField, { label: "Loading Label", value: settings.submitButton?.loadingLabel ?? "", placeholder: "Submitting...", onChange: (v) => updateSubmitButton({ loadingLabel: v || void 0 }) }),
2403
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsField, { label: "Success Label", value: settings.submitButton?.successLabel ?? "", placeholder: "Submitted!", onChange: (v) => updateSubmitButton({ successLabel: v || void 0 }) })
2404
+ ] }),
2405
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingsSection, { title: "Navigation", children: [
2406
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsToggle, { label: "Show Back Button", checked: settings.navigation?.showBack !== false, onChange: (v) => updateNavigation({ showBack: v }) }),
2407
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsToggle, { label: "Show Section List", checked: !!settings.navigation?.showSectionList, onChange: (v) => updateNavigation({ showSectionList: v }) }),
2408
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsToggle, { label: "Allow Skip", checked: !!settings.navigation?.allowSkip, onChange: (v) => updateNavigation({ allowSkip: v }) }),
2409
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsField, { label: "Next Label", value: settings.navigation?.nextLabel ?? "", placeholder: "Next", onChange: (v) => updateNavigation({ nextLabel: v || void 0 }) }),
2410
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsField, { label: "Back Label", value: settings.navigation?.backLabel ?? "", placeholder: "Back", onChange: (v) => updateNavigation({ backLabel: v || void 0 }) })
2411
+ ] }),
2412
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingsSection, { title: "Drafts", children: [
2413
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsToggle, { label: "Allow Draft Save", checked: !!settings.allowDraftSave, onChange: (v) => updateSettings({ allowDraftSave: v }) }),
2414
+ settings.allowDraftSave && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2415
+ /* @__PURE__ */ jsxRuntime.jsx(
2416
+ SettingsSelect,
2417
+ {
2418
+ label: "Storage",
2419
+ value: settings.draftStorage ?? "local",
2420
+ options: [
2421
+ { label: "Local", value: "local" },
2422
+ { label: "Server", value: "server" },
2423
+ { label: "Both", value: "both" }
2424
+ ],
2425
+ onChange: (v) => updateSettings({ draftStorage: v })
2426
+ }
2427
+ ),
2428
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsNumber, { label: "Draft TTL (hours)", value: settings.draftTtlHours, placeholder: "24", onChange: (v) => updateSettings({ draftTtlHours: v }) })
2429
+ ] })
2430
+ ] }),
2431
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingsSection, { title: "Advanced", children: [
2432
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsField, { label: "Locale", value: settings.locale ?? "", placeholder: "en", onChange: (v) => updateSettings({ locale: v || void 0 }) }),
2433
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsField, { label: "Server URL", value: settings.serverUrl ?? "", placeholder: "https://api.example.com/submit", onChange: (v) => updateSettings({ serverUrl: v || void 0 }) }),
2434
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsToggle, { label: "No PII in Logs", checked: !!settings.noPiiInLogs, onChange: (v) => updateSettings({ noPiiInLogs: v }) })
2435
+ ] })
2436
+ ] });
2437
+ }
2438
+ function SettingsSection({ title, children }) {
2439
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2440
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { className: "mb-4" }),
2441
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3 block", children: title }),
2442
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children })
2443
+ ] });
2444
+ }
2445
+ function SettingsField({
2446
+ label,
2447
+ value,
2448
+ placeholder,
2449
+ onChange
2450
+ }) {
2451
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2452
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
2453
+ /* @__PURE__ */ jsxRuntime.jsx(
2454
+ fieldcraftReact.Input,
2455
+ {
2456
+ value,
2457
+ onChange: (e) => onChange(e.target.value),
2458
+ placeholder
2459
+ }
2460
+ )
2461
+ ] });
2462
+ }
2463
+ function SettingsNumber({
2464
+ label,
2465
+ value,
2466
+ placeholder,
2467
+ onChange
2468
+ }) {
2469
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2470
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
2471
+ /* @__PURE__ */ jsxRuntime.jsx(
2472
+ fieldcraftReact.Input,
2473
+ {
2474
+ type: "number",
2475
+ value: value ?? "",
2476
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : Number(e.target.value)),
2477
+ placeholder
2478
+ }
2479
+ )
2480
+ ] });
2481
+ }
2482
+ function SettingsToggle({
2483
+ label,
2484
+ checked,
2485
+ onChange
2486
+ }) {
2487
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2488
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: label }),
2489
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Switch, { checked, onCheckedChange: onChange })
2490
+ ] });
2491
+ }
2492
+ function SettingsSelect({
2493
+ label,
2494
+ value,
2495
+ options,
2496
+ onChange
2497
+ }) {
2498
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2499
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
2500
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2501
+ /* @__PURE__ */ jsxRuntime.jsx(
2502
+ "select",
2503
+ {
2504
+ value,
2505
+ onChange: (e) => onChange(e.target.value),
2506
+ className: "flex h-9 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
2507
+ children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
2508
+ }
2509
+ ),
2510
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) })
2511
+ ] })
2512
+ ] });
2513
+ }
2514
+ function PropertiesPanel({ builderState }) {
2515
+ const { schema, selectedItem } = builderState;
2516
+ const [showSettings, setShowSettings] = react.useState(false);
2517
+ if (showSettings) {
2518
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-80 h-full border-l border-border bg-card flex flex-col", children: [
2519
+ /* @__PURE__ */ jsxRuntime.jsx(PanelHeader, { title: "Form Settings", onClose: () => setShowSettings(false) }),
2520
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto p-4", children: /* @__PURE__ */ jsxRuntime.jsx(FormSettingsPanel, { schema, onUpdate: builderState.updateSchema }) })
2521
+ ] });
2522
+ }
2523
+ if (!selectedItem) {
2524
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-80 h-full border-l border-border bg-card flex flex-col", children: [
2525
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-4 py-3 border-b border-border flex items-center justify-between", children: [
2526
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Properties" }),
2527
+ /* @__PURE__ */ jsxRuntime.jsx(
2528
+ fieldcraftReact.Button,
2529
+ {
2530
+ type: "button",
2531
+ variant: "ghost",
2532
+ size: "icon-xs",
2533
+ onClick: () => setShowSettings(true),
2534
+ title: "Form settings",
2535
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { size: 14, strokeWidth: 1.75 })
2536
+ }
2537
+ )
2538
+ ] }),
2539
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Select a field or section to edit" }) })
2540
+ ] });
2541
+ }
2542
+ if (selectedItem.type === "section") {
2543
+ const found2 = findSection(schema, selectedItem.sectionId);
2544
+ if (!found2) return null;
2545
+ return /* @__PURE__ */ jsxRuntime.jsx(
2546
+ SectionProperties,
2547
+ {
2548
+ section: found2.section,
2549
+ onUpdate: (updates) => builderState.updateSection(selectedItem.sectionId, updates),
2550
+ onClose: builderState.clearSelection,
2551
+ onOpenSettings: () => setShowSettings(true)
2552
+ }
2553
+ );
2554
+ }
2555
+ const found = findQuestion(schema, selectedItem.sectionId, selectedItem.questionId);
2556
+ if (!found) return null;
2557
+ return /* @__PURE__ */ jsxRuntime.jsx(
2558
+ QuestionProperties,
2559
+ {
2560
+ question: found.question,
2561
+ sectionId: selectedItem.sectionId,
2562
+ builderState,
2563
+ onClose: builderState.clearSelection,
2564
+ onOpenSettings: () => setShowSettings(true)
2565
+ }
2566
+ );
2567
+ }
2568
+ function SectionProperties({ section, onUpdate, onClose, onOpenSettings }) {
2569
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-80 h-full border-l border-border bg-card flex flex-col", children: [
2570
+ /* @__PURE__ */ jsxRuntime.jsx(PanelHeader, { title: "Section Properties", onClose, children: /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: onOpenSettings, title: "Form settings", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { size: 13, strokeWidth: 1.75 }) }) }),
2571
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: [
2572
+ /* @__PURE__ */ jsxRuntime.jsx(FieldGroup, { label: "Title", children: /* @__PURE__ */ jsxRuntime.jsx(
2573
+ fieldcraftReact.Input,
2574
+ {
2575
+ value: section.title,
2576
+ onChange: (e) => onUpdate({ title: e.target.value })
2577
+ }
2578
+ ) }),
2579
+ /* @__PURE__ */ jsxRuntime.jsx(FieldGroup, { label: "Description", children: /* @__PURE__ */ jsxRuntime.jsx(
2580
+ fieldcraftReact.Textarea,
2581
+ {
2582
+ value: section.description ?? "",
2583
+ onChange: (e) => onUpdate({ description: e.target.value }),
2584
+ rows: 3
2585
+ }
2586
+ ) })
2587
+ ] })
2588
+ ] });
2589
+ }
2590
+ function QuestionProperties({ question, sectionId, builderState, onClose, onOpenSettings }) {
2591
+ const [activeTab, setActiveTab] = react.useState("basic");
2592
+ const typeInfo = QUESTION_TYPE_INFO[question.type];
2593
+ const IconComponent = typeInfo ? getIcon(typeInfo.icon) : null;
2594
+ const hasOptions = typeInfo?.requiresOptions || question.options && question.options.length > 0;
2595
+ const updateQuestion2 = react.useCallback(
2596
+ (updates) => {
2597
+ builderState.updateQuestion(sectionId, question.id, updates);
2598
+ },
2599
+ [builderState, sectionId, question.id]
2600
+ );
2601
+ const tabs = [
2602
+ { key: "basic", label: "Basic" },
2603
+ { key: "validation", label: "Rules" },
2604
+ { key: "logic", label: "Logic" }
2605
+ ];
2606
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-80 h-full border-l border-border bg-card flex flex-col", children: [
2607
+ /* @__PURE__ */ jsxRuntime.jsx(PanelHeader, { title: "Field Properties", onClose, children: /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: onOpenSettings, title: "Form settings", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { size: 13, strokeWidth: 1.75 }) }) }),
2608
+ IconComponent && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 pt-3 pb-0", children: /* @__PURE__ */ jsxRuntime.jsxs(fieldcraftReact.Badge, { variant: "secondary", className: "gap-1.5", children: [
2609
+ /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 11, strokeWidth: 1.75 }),
2610
+ typeInfo?.label
2611
+ ] }) }),
2612
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 px-4 pt-3 flex gap-0.5", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
2613
+ "button",
2614
+ {
2615
+ type: "button",
2616
+ onClick: () => setActiveTab(tab.key),
2617
+ className: cn(
2618
+ "px-3 py-1.5 text-xs font-medium rounded-md border-0 cursor-pointer transition-colors",
2619
+ activeTab === tab.key ? "bg-primary text-primary-foreground" : "bg-transparent text-muted-foreground hover:bg-accent hover:text-foreground"
2620
+ ),
2621
+ children: tab.label
2622
+ },
2623
+ tab.key
2624
+ )) }),
2625
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: [
2626
+ activeTab === "basic" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2627
+ /* @__PURE__ */ jsxRuntime.jsx(FieldGroup, { label: "Label", children: /* @__PURE__ */ jsxRuntime.jsx(
2628
+ fieldcraftReact.Input,
2629
+ {
2630
+ value: question.label,
2631
+ onChange: (e) => updateQuestion2({ label: e.target.value })
2632
+ }
2633
+ ) }),
2634
+ /* @__PURE__ */ jsxRuntime.jsx(FieldGroup, { label: "Help Text", children: /* @__PURE__ */ jsxRuntime.jsx(
2635
+ fieldcraftReact.Input,
2636
+ {
2637
+ value: question.helpText ?? "",
2638
+ onChange: (e) => updateQuestion2({ helpText: e.target.value || void 0 }),
2639
+ placeholder: "Optional help text below the field"
2640
+ }
2641
+ ) }),
2642
+ /* @__PURE__ */ jsxRuntime.jsx(FieldGroup, { label: "Placeholder", children: /* @__PURE__ */ jsxRuntime.jsx(
2643
+ fieldcraftReact.Input,
2644
+ {
2645
+ value: question.placeholder ?? "",
2646
+ onChange: (e) => updateQuestion2({ placeholder: e.target.value || void 0 }),
2647
+ placeholder: "Input placeholder text"
2648
+ }
2649
+ ) }),
2650
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2651
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { htmlFor: "required-switch", children: "Required" }),
2652
+ /* @__PURE__ */ jsxRuntime.jsx(
2653
+ fieldcraftReact.Switch,
2654
+ {
2655
+ id: "required-switch",
2656
+ checked: !!question.required,
2657
+ onCheckedChange: (checked) => updateQuestion2({ required: checked })
2658
+ }
2659
+ )
2660
+ ] }),
2661
+ /* @__PURE__ */ jsxRuntime.jsx(QuestionConfigEditor, { question, onUpdate: updateQuestion2 }),
2662
+ hasOptions && /* @__PURE__ */ jsxRuntime.jsx(
2663
+ OptionsEditor,
2664
+ {
2665
+ options: question.options ?? [],
2666
+ sectionId,
2667
+ questionId: question.id,
2668
+ builderState
2669
+ }
2670
+ )
2671
+ ] }),
2672
+ activeTab === "validation" && /* @__PURE__ */ jsxRuntime.jsx(ValidationRulesEditor, { question, onUpdate: updateQuestion2 }),
2673
+ activeTab === "logic" && /* @__PURE__ */ jsxRuntime.jsx(ConditionEditor, { question, schema: builderState.schema, onUpdate: updateQuestion2 })
2674
+ ] })
2675
+ ] });
2676
+ }
2677
+ function OptionsEditor({ options, sectionId, questionId, builderState }) {
2678
+ const handleAddOption = () => {
2679
+ const index = options.length;
2680
+ const newOption = {
2681
+ label: `Option ${index + 1}`,
2682
+ value: `option${index + 1}`
2683
+ };
2684
+ builderState.updateQuestion(sectionId, questionId, {
2685
+ options: [...options, newOption]
2686
+ });
2687
+ };
2688
+ const handleUpdateOption = (index, updates) => {
2689
+ const updated = options.map(
2690
+ (opt, i) => i === index ? { ...opt, ...updates } : opt
2691
+ );
2692
+ builderState.updateQuestion(sectionId, questionId, { options: updated });
2693
+ };
2694
+ const handleRemoveOption = (index) => {
2695
+ if (options.length <= 1) return;
2696
+ const updated = options.filter((_, i) => i !== index);
2697
+ builderState.updateQuestion(sectionId, questionId, { options: updated });
2698
+ };
2699
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2700
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
2701
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { children: "Options" }),
2702
+ /* @__PURE__ */ jsxRuntime.jsxs(fieldcraftReact.Button, { type: "button", variant: "secondary", size: "xs", onClick: handleAddOption, children: [
2703
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 12, strokeWidth: 2 }),
2704
+ "Add"
2705
+ ] })
2706
+ ] }),
2707
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: options.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 group", children: [
2708
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-muted-foreground cursor-grab shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { size: 12, strokeWidth: 1.5 }) }),
2709
+ /* @__PURE__ */ jsxRuntime.jsx(
2710
+ fieldcraftReact.Input,
2711
+ {
2712
+ value: option.label,
2713
+ onChange: (e) => {
2714
+ const label = e.target.value;
2715
+ handleUpdateOption(index, {
2716
+ label,
2717
+ value: label.toLowerCase().replace(/\s+/g, "_")
2718
+ });
2719
+ },
2720
+ className: "h-8 flex-1"
2721
+ }
2722
+ ),
2723
+ /* @__PURE__ */ jsxRuntime.jsx(
2724
+ fieldcraftReact.Button,
2725
+ {
2726
+ type: "button",
2727
+ variant: "ghost",
2728
+ size: "icon-xs",
2729
+ onClick: () => handleRemoveOption(index),
2730
+ disabled: options.length <= 1,
2731
+ className: "shrink-0 opacity-0 group-hover:opacity-100 transition-opacity disabled:opacity-0 hover:bg-destructive/10 hover:text-destructive",
2732
+ title: "Remove option",
2733
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, strokeWidth: 1.75 })
2734
+ }
2735
+ )
2736
+ ] }, index)) })
2737
+ ] });
2738
+ }
2739
+ function PanelHeader({
2740
+ title,
2741
+ onClose,
2742
+ children
2743
+ }) {
2744
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-4 py-3 border-b border-border flex items-center justify-between", children: [
2745
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-foreground", children: title }) }),
2746
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
2747
+ children,
2748
+ /* @__PURE__ */ jsxRuntime.jsx(
2749
+ fieldcraftReact.Button,
2750
+ {
2751
+ type: "button",
2752
+ variant: "ghost",
2753
+ size: "icon-xs",
2754
+ onClick: onClose,
2755
+ title: "Close panel",
2756
+ "aria-label": "Close properties panel",
2757
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 14, strokeWidth: 1.75 })
2758
+ }
2759
+ )
2760
+ ] })
2761
+ ] });
2762
+ }
2763
+ function FieldGroup({ label, children }) {
2764
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2765
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1.5", children: label }),
2766
+ children
2767
+ ] });
2768
+ }
2769
+ var ThemeCtx = react.createContext({});
2770
+ function useBuilderTheme() {
2771
+ return react.useContext(ThemeCtx);
2772
+ }
2773
+ function themeToCssVars(theme) {
2774
+ const vars = {};
2775
+ if (theme.background) vars["--background"] = theme.background;
2776
+ if (theme.foreground) vars["--foreground"] = theme.foreground;
2777
+ if (theme.card) {
2778
+ vars["--card"] = theme.card;
2779
+ vars["--card-foreground"] = theme.foreground ?? "";
2780
+ vars["--popover"] = theme.card;
2781
+ vars["--popover-foreground"] = theme.foreground ?? "";
2782
+ }
2783
+ if (theme.primary) vars["--primary"] = theme.primary;
2784
+ if (theme.primaryForeground) vars["--primary-foreground"] = theme.primaryForeground;
2785
+ if (theme.secondary) vars["--secondary"] = theme.secondary;
2786
+ if (theme.secondaryForeground) vars["--secondary-foreground"] = theme.secondaryForeground;
2787
+ if (theme.muted) vars["--muted"] = theme.muted;
2788
+ if (theme.mutedForeground) vars["--muted-foreground"] = theme.mutedForeground;
2789
+ if (theme.accent) vars["--accent"] = theme.accent;
2790
+ if (theme.accentForeground) vars["--accent-foreground"] = theme.accentForeground;
2791
+ if (theme.destructive) vars["--destructive"] = theme.destructive;
2792
+ if (theme.destructiveForeground) vars["--destructive-foreground"] = theme.destructiveForeground;
2793
+ if (theme.border) vars["--border"] = theme.border;
2794
+ if (theme.input) vars["--input"] = theme.input;
2795
+ if (theme.ring) vars["--ring"] = theme.ring;
2796
+ if (theme.radius) vars["--radius"] = theme.radius;
2797
+ if (theme.surface) vars["--fcb-surface"] = theme.surface;
2798
+ if (theme.surfaceHover) vars["--fcb-surface-hover"] = theme.surfaceHover;
2799
+ if (theme.canvas) vars["--fcb-canvas"] = theme.canvas;
2800
+ if (theme.panel) vars["--fcb-panel"] = theme.panel;
2801
+ if (theme.borderStrong) vars["--fcb-border-strong"] = theme.borderStrong;
2802
+ if (theme.textDim) vars["--fcb-text-dim"] = theme.textDim;
2803
+ return vars;
2804
+ }
2805
+ function FormBuilderThemeProvider({ theme, children }) {
2806
+ const resolved = theme ?? {};
2807
+ const cssVars = react.useMemo(() => themeToCssVars(resolved), [resolved]);
2808
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeCtx.Provider, { value: resolved, children: /* @__PURE__ */ jsxRuntime.jsx("div", { "data-fcb-root": "", style: cssVars, className: "w-full h-full", children }) });
2809
+ }
2810
+ var FormBuilderErrorBoundary = class extends react.Component {
2811
+ constructor(props) {
2812
+ super(props);
2813
+ this.state = { hasError: false, error: null };
2814
+ }
2815
+ static getDerivedStateFromError(error) {
2816
+ return { hasError: true, error };
2817
+ }
2818
+ componentDidCatch(error, info) {
2819
+ console.error("[FormBuilder] Render error:", error, info.componentStack);
2820
+ }
2821
+ render() {
2822
+ if (this.state.hasError) {
2823
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center justify-center h-full bg-background text-foreground p-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-md text-center space-y-4", children: [
2824
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold", children: "Something went wrong" }),
2825
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "The form builder encountered an unexpected error. Your schema data is preserved." }),
2826
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs text-destructive bg-destructive/10 rounded-md p-3 text-left overflow-auto max-h-32", children: this.state.error?.message }),
2827
+ /* @__PURE__ */ jsxRuntime.jsx(
2828
+ fieldcraftReact.Button,
2829
+ {
2830
+ onClick: () => this.setState({ hasError: false, error: null }),
2831
+ variant: "outline",
2832
+ children: "Try Again"
2833
+ }
2834
+ )
2835
+ ] }) });
2836
+ }
2837
+ return this.props.children;
2838
+ }
2839
+ };
2840
+ function FormBuilderCore(props) {
2841
+ const { initialSchema = DEFAULT_SCHEMA, onChange, onSave, height = "100vh", theme, className, toolbarExtra, questionTypes, palette } = props;
2842
+ const mergedQuestionTypes = questionTypes ? { ...QUESTION_TYPE_INFO, ...questionTypes } : QUESTION_TYPE_INFO;
2843
+ const builderState = useBuilderState(initialSchema);
2844
+ const dragDrop = useDragDrop(builderState);
2845
+ const fileInputRef = react.useRef(null);
2846
+ react.useEffect(() => {
2847
+ if (onChange && builderState.isDirty) {
2848
+ onChange(builderState.schema);
2849
+ }
2850
+ }, [builderState.schema, builderState.isDirty, onChange]);
2851
+ const handleSave = react.useCallback(() => {
2852
+ if (onSave) {
2853
+ onSave(builderState.schema);
2854
+ builderState.markClean();
2855
+ }
2856
+ }, [onSave, builderState]);
2857
+ const handleExport = react.useCallback(() => {
2858
+ const json = JSON.stringify(builderState.schema, null, 2);
2859
+ const blob = new Blob([json], { type: "application/json" });
2860
+ const url = URL.createObjectURL(blob);
2861
+ const a = document.createElement("a");
2862
+ a.href = url;
2863
+ a.download = `${builderState.schema.title?.replace(/\s+/g, "-").toLowerCase() || "form"}-schema.json`;
2864
+ a.click();
2865
+ URL.revokeObjectURL(url);
2866
+ }, [builderState.schema]);
2867
+ const handleImport = react.useCallback(() => {
2868
+ fileInputRef.current?.click();
2869
+ }, []);
2870
+ const handleFileChange = react.useCallback(
2871
+ (e) => {
2872
+ const file = e.target.files?.[0];
2873
+ if (!file) return;
2874
+ const reader = new FileReader();
2875
+ reader.onload = (event) => {
2876
+ try {
2877
+ const parsed = JSON.parse(event.target?.result);
2878
+ if (parsed && parsed.sections && Array.isArray(parsed.sections)) {
2879
+ builderState.resetSchema(parsed);
2880
+ } else {
2881
+ alert("Invalid schema: must contain a 'sections' array.");
2882
+ }
2883
+ } catch {
2884
+ alert("Failed to parse JSON file.");
2885
+ }
2886
+ };
2887
+ reader.readAsText(file);
2888
+ e.target.value = "";
2889
+ },
2890
+ [builderState]
2891
+ );
2892
+ const handleKeyDown = react.useCallback(
2893
+ (e) => {
2894
+ const mod = e.metaKey || e.ctrlKey;
2895
+ const tag = e.target.tagName;
2896
+ const isEditing = tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT";
2897
+ if (mod && e.key === "z" && !e.shiftKey) {
2898
+ e.preventDefault();
2899
+ builderState.undo();
2900
+ return;
2901
+ }
2902
+ if (mod && e.key === "z" && e.shiftKey) {
2903
+ e.preventDefault();
2904
+ builderState.redo();
2905
+ return;
2906
+ }
2907
+ if (mod && e.key === "s") {
2908
+ e.preventDefault();
2909
+ handleSave();
2910
+ return;
2911
+ }
2912
+ if (isEditing) return;
2913
+ if (e.key === "Delete" || e.key === "Backspace") {
2914
+ const sel = builderState.selectedItem;
2915
+ if (!sel) return;
2916
+ e.preventDefault();
2917
+ if (sel.type === "question") {
2918
+ builderState.removeQuestion(sel.sectionId, sel.questionId);
2919
+ } else if (sel.type === "section") {
2920
+ builderState.removeSection(sel.sectionId);
2921
+ }
2922
+ return;
2923
+ }
2924
+ if (e.key === "Escape") {
2925
+ if (builderState.selectedItem) {
2926
+ e.preventDefault();
2927
+ builderState.clearSelection();
2928
+ }
2929
+ return;
2930
+ }
2931
+ if (mod && e.key === "d") {
2932
+ const sel = builderState.selectedItem;
2933
+ if (!sel) return;
2934
+ e.preventDefault();
2935
+ if (sel.type === "question") {
2936
+ builderState.duplicateQuestion(sel.sectionId, sel.questionId);
2937
+ } else if (sel.type === "section") {
2938
+ builderState.duplicateSection(sel.sectionId);
2939
+ }
2940
+ }
2941
+ },
2942
+ [builderState, handleSave]
2943
+ );
2944
+ return /* @__PURE__ */ jsxRuntime.jsx(FormBuilderThemeProvider, { theme, children: /* @__PURE__ */ jsxRuntime.jsxs(
2945
+ core.DndContext,
2946
+ {
2947
+ sensors: dragDrop.sensors,
2948
+ onDragStart: dragDrop.handleDragStart,
2949
+ onDragEnd: dragDrop.handleDragEnd,
2950
+ onDragCancel: dragDrop.handleDragCancel,
2951
+ children: [
2952
+ /* @__PURE__ */ jsxRuntime.jsxs(
2953
+ "div",
2954
+ {
2955
+ className: cn("flex flex-col bg-background text-foreground", className),
2956
+ style: { height: typeof height === "number" ? `${height}px` : height },
2957
+ onKeyDown: handleKeyDown,
2958
+ tabIndex: -1,
2959
+ role: "application",
2960
+ "aria-label": "Form Builder",
2961
+ children: [
2962
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-12 shrink-0 grid px-4 bg-card border-b border-border", style: { gridTemplateColumns: "1fr auto 1fr" }, role: "toolbar", "aria-label": "Builder toolbar", children: [
2963
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
2964
+ /* @__PURE__ */ jsxRuntime.jsx(
2965
+ fieldcraftReact.Button,
2966
+ {
2967
+ variant: "ghost",
2968
+ size: "icon-sm",
2969
+ onClick: builderState.undo,
2970
+ disabled: !builderState.canUndo,
2971
+ className: "disabled:opacity-30",
2972
+ title: "Undo (Ctrl+Z)",
2973
+ "aria-label": "Undo",
2974
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Undo2, { size: 16, strokeWidth: 1.75 })
2975
+ }
2976
+ ),
2977
+ /* @__PURE__ */ jsxRuntime.jsx(
2978
+ fieldcraftReact.Button,
2979
+ {
2980
+ variant: "ghost",
2981
+ size: "icon-sm",
2982
+ onClick: builderState.redo,
2983
+ disabled: !builderState.canRedo,
2984
+ className: "disabled:opacity-30",
2985
+ title: "Redo (Ctrl+Shift+Z)",
2986
+ "aria-label": "Redo",
2987
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Redo2, { size: 16, strokeWidth: 1.75 })
2988
+ }
2989
+ ),
2990
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { orientation: "vertical", className: "mx-2 h-5" }),
2991
+ builderState.isDirty && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-primary", role: "status", children: "Unsaved changes" })
2992
+ ] }),
2993
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center", children: toolbarExtra }),
2994
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-1.5", children: [
2995
+ /* @__PURE__ */ jsxRuntime.jsx(
2996
+ fieldcraftReact.Button,
2997
+ {
2998
+ variant: "ghost",
2999
+ size: "icon-sm",
3000
+ onClick: handleImport,
3001
+ title: "Import schema JSON",
3002
+ "aria-label": "Import schema",
3003
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { size: 15, strokeWidth: 1.75 })
3004
+ }
3005
+ ),
3006
+ /* @__PURE__ */ jsxRuntime.jsx(
3007
+ fieldcraftReact.Button,
3008
+ {
3009
+ variant: "ghost",
3010
+ size: "icon-sm",
3011
+ onClick: handleExport,
3012
+ title: "Export schema JSON",
3013
+ "aria-label": "Export schema",
3014
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 15, strokeWidth: 1.75 })
3015
+ }
3016
+ ),
3017
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { orientation: "vertical", className: "mx-1 h-5" }),
3018
+ /* @__PURE__ */ jsxRuntime.jsxs(fieldcraftReact.Button, { onClick: handleSave, className: "border-0 shadow-sm fcb-glow", "aria-label": "Save form", children: [
3019
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Save, { size: 14, strokeWidth: 2 }),
3020
+ "Save"
3021
+ ] })
3022
+ ] }),
3023
+ /* @__PURE__ */ jsxRuntime.jsx(
3024
+ "input",
3025
+ {
3026
+ ref: fileInputRef,
3027
+ type: "file",
3028
+ accept: ".json,application/json",
3029
+ onChange: handleFileChange,
3030
+ className: "hidden",
3031
+ "aria-hidden": "true"
3032
+ }
3033
+ )
3034
+ ] }),
3035
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex overflow-hidden", children: [
3036
+ /* @__PURE__ */ jsxRuntime.jsx(QuestionPalette, { questionTypes: mergedQuestionTypes, palette }),
3037
+ /* @__PURE__ */ jsxRuntime.jsx(FormCanvas, { builderState }),
3038
+ /* @__PURE__ */ jsxRuntime.jsx(PropertiesPanel, { builderState })
3039
+ ] })
3040
+ ]
3041
+ }
3042
+ ),
3043
+ /* @__PURE__ */ jsxRuntime.jsx(core.DragOverlay, { dropAnimation: null, children: dragDrop.activeDragItem && /* @__PURE__ */ jsxRuntime.jsx(DragOverlayContent, { item: dragDrop.activeDragItem, schema: builderState.schema, questionTypes: mergedQuestionTypes }) })
3044
+ ]
3045
+ }
3046
+ ) });
3047
+ }
3048
+ function DragOverlayContent({
3049
+ item,
3050
+ schema,
3051
+ questionTypes: types
3052
+ }) {
3053
+ if (item.type === "palette-item") {
3054
+ const typeInfo = types[item.questionType];
3055
+ if (!typeInfo) return null;
3056
+ const IconComponent = getIcon(typeInfo.icon);
3057
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-3 py-2 rounded-md border border-primary bg-card text-foreground text-sm fcb-shadow-lg cursor-grabbing", children: [
3058
+ /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 14, className: "shrink-0 text-primary", strokeWidth: 1.75 }),
3059
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: typeInfo.label })
3060
+ ] });
3061
+ }
3062
+ if (item.type === "question") {
3063
+ const found = findQuestion(schema, item.sectionId, item.questionId);
3064
+ if (!found) return null;
3065
+ const typeInfo = types[found.question.type];
3066
+ const IconComponent = typeInfo ? getIcon(typeInfo.icon) : null;
3067
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 rounded-md border border-primary bg-card text-foreground fcb-shadow-lg cursor-grabbing max-w-sm", children: [
3068
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 mb-1", children: /* @__PURE__ */ jsxRuntime.jsxs(fieldcraftReact.Badge, { variant: "secondary", className: "gap-1.5 text-muted-foreground", children: [
3069
+ IconComponent && /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 11, strokeWidth: 1.75 }),
3070
+ typeInfo?.label ?? found.question.type
3071
+ ] }) }),
3072
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium", children: found.question.label })
3073
+ ] });
3074
+ }
3075
+ if (item.type === "section") {
3076
+ const section = schema.sections.find((s) => s.id === item.sectionId);
3077
+ if (!section) return null;
3078
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 rounded-md border border-primary bg-card text-foreground fcb-shadow-lg cursor-grabbing max-w-sm", children: [
3079
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold", children: section.title }),
3080
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground mt-0.5", children: [
3081
+ section.questions.length,
3082
+ " field",
3083
+ section.questions.length !== 1 ? "s" : ""
3084
+ ] })
3085
+ ] });
3086
+ }
3087
+ return null;
3088
+ }
3089
+ function FormBuilderInner(props) {
3090
+ return /* @__PURE__ */ jsxRuntime.jsx(FormBuilderErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(FormBuilderCore, { ...props }) });
3091
+ }
3092
+
3093
+ // src/form-builder/components/FormBuilderGated.tsx
3094
+ var FormBuilder = fieldcraftProLicense.requireLicense(FormBuilderInner, "FormBuilder");
3095
+
3096
+ // src/form-builder/theme/presets.ts
3097
+ var squaredrDarkPreset = {
3098
+ background: "#0a0a0b",
3099
+ // ink-950
3100
+ foreground: "#e8e8ea",
3101
+ // ink-100
3102
+ card: "#111113",
3103
+ // ink-900
3104
+ primary: "oklch(0.82 0.14 210)",
3105
+ // sr-accent-cyan
3106
+ primaryForeground: "#0a0a0b",
3107
+ // ink-950
3108
+ secondary: "#17171a",
3109
+ // ink-850
3110
+ secondaryForeground: "#e8e8ea",
3111
+ // ink-100
3112
+ muted: "#111113",
3113
+ // ink-900
3114
+ mutedForeground: "#8a8a95",
3115
+ // ink-400
3116
+ accent: "#17171a",
3117
+ // ink-850
3118
+ accentForeground: "#e8e8ea",
3119
+ // ink-100
3120
+ destructive: "oklch(0.68 0.22 25)",
3121
+ // sr-error
3122
+ destructiveForeground: "#e8e8ea",
3123
+ // ink-100
3124
+ border: "#1c1c20",
3125
+ // ink-800
3126
+ input: "#1c1c20",
3127
+ // ink-800
3128
+ ring: "oklch(0.82 0.14 210)",
3129
+ // sr-accent-cyan
3130
+ radius: "6px",
3131
+ surface: "#111113",
3132
+ // ink-900
3133
+ surfaceHover: "#17171a",
3134
+ // ink-850
3135
+ canvas: "#0a0a0b",
3136
+ // ink-950
3137
+ panel: "#111113",
3138
+ // ink-900
3139
+ borderStrong: "#26262c",
3140
+ // ink-700
3141
+ textDim: "#5a5a66"
3142
+ // ink-500
3143
+ };
3144
+ var cleanPreset = {
3145
+ background: "#ffffff",
3146
+ foreground: "#111113",
3147
+ card: "#f9fafb",
3148
+ primary: "#0d9488",
3149
+ primaryForeground: "#ffffff",
3150
+ secondary: "#f3f4f6",
3151
+ secondaryForeground: "#111113",
3152
+ muted: "#f3f4f6",
3153
+ mutedForeground: "#6b7280",
3154
+ accent: "#f3f4f6",
3155
+ accentForeground: "#111113",
3156
+ destructive: "#ef4444",
3157
+ destructiveForeground: "#ffffff",
3158
+ border: "#e5e7eb",
3159
+ input: "#e5e7eb",
3160
+ ring: "#0d9488",
3161
+ radius: "6px",
3162
+ surface: "#f9fafb",
3163
+ surfaceHover: "#f3f4f6",
3164
+ canvas: "#ffffff",
3165
+ panel: "#ffffff",
3166
+ borderStrong: "#d1d5db",
3167
+ textDim: "#9ca3af"
3168
+ };
3169
+ function ResponseTable({ schema, responses, onRowClick }) {
3170
+ const questions = getAllQuestions(schema);
3171
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflow: "auto", fontFamily: "inherit" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
3172
+ "table",
3173
+ {
3174
+ style: {
3175
+ width: "100%",
3176
+ borderCollapse: "collapse",
3177
+ fontSize: "13px"
3178
+ },
3179
+ children: [
3180
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { style: { backgroundColor: "var(--muted, #111113)" }, children: [
3181
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: thStyle, children: "Submitted" }),
3182
+ questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx("th", { style: thStyle, children: q.label }, q.id)),
3183
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: thStyle, children: "Score" })
3184
+ ] }) }),
3185
+ /* @__PURE__ */ jsxRuntime.jsxs("tbody", { children: [
3186
+ responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
3187
+ "tr",
3188
+ {
3189
+ onClick: () => onRowClick?.(response),
3190
+ style: {
3191
+ cursor: onRowClick ? "pointer" : "default",
3192
+ borderBottom: "1px solid var(--border, #1c1c20)"
3193
+ },
3194
+ onMouseEnter: (e) => {
3195
+ if (onRowClick) e.currentTarget.style.backgroundColor = "var(--accent, #17171a)";
3196
+ },
3197
+ onMouseLeave: (e) => {
3198
+ e.currentTarget.style.backgroundColor = "transparent";
3199
+ },
3200
+ children: [
3201
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: tdStyle, children: new Date(response.submittedAt).toLocaleString() }),
3202
+ questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: tdStyle, children: formatCellValue(response.values[q.id]) }, q.id)),
3203
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: tdStyle, children: response.totalScore ?? "\u2014" })
3204
+ ]
3205
+ },
3206
+ response.sessionToken || idx
3207
+ )),
3208
+ responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
3209
+ "td",
3210
+ {
3211
+ colSpan: questions.length + 2,
3212
+ style: { ...tdStyle, textAlign: "center", color: "var(--muted-foreground, #8a8a95)", padding: "32px" },
3213
+ children: "No responses yet"
3214
+ }
3215
+ ) })
3216
+ ] })
3217
+ ]
3218
+ }
3219
+ ) });
3220
+ }
3221
+ var thStyle = {
3222
+ padding: "8px 12px",
3223
+ textAlign: "left",
3224
+ fontWeight: 600,
3225
+ color: "var(--foreground, #e8e8ea)",
3226
+ borderBottom: "2px solid var(--border, #1c1c20)",
3227
+ whiteSpace: "nowrap"
3228
+ };
3229
+ var tdStyle = {
3230
+ padding: "8px 12px",
3231
+ color: "var(--foreground, #e8e8ea)",
3232
+ maxWidth: "200px",
3233
+ overflow: "hidden",
3234
+ textOverflow: "ellipsis",
3235
+ whiteSpace: "nowrap"
3236
+ };
3237
+ function getAllQuestions(schema) {
3238
+ const questions = [];
3239
+ for (const section of schema.sections) {
3240
+ for (const q of section.questions) {
3241
+ if (q.type === "info-block" || q.type === "section-header" || q.type === "page-break") {
3242
+ continue;
3243
+ }
3244
+ questions.push(q);
3245
+ }
3246
+ }
3247
+ return questions;
3248
+ }
3249
+ function formatCellValue(value) {
3250
+ if (value == null) return "\u2014";
3251
+ if (typeof value === "boolean") return value ? "Yes" : "No";
3252
+ if (Array.isArray(value)) return value.join(", ");
3253
+ if (typeof value === "object") return JSON.stringify(value);
3254
+ return String(value);
3255
+ }
3256
+ function ResponseCard({ response, fields, onClick }) {
3257
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3258
+ "div",
3259
+ {
3260
+ onClick,
3261
+ style: {
3262
+ border: "1px solid var(--border, #1c1c20)",
3263
+ borderRadius: "8px",
3264
+ padding: "16px",
3265
+ backgroundColor: "var(--card, #111113)",
3266
+ cursor: onClick ? "pointer" : "default",
3267
+ fontFamily: "inherit",
3268
+ transition: "box-shadow 0.15s"
3269
+ },
3270
+ onMouseEnter: (e) => {
3271
+ if (onClick) e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.3)";
3272
+ },
3273
+ onMouseLeave: (e) => {
3274
+ e.currentTarget.style.boxShadow = "none";
3275
+ },
3276
+ children: [
3277
+ /* @__PURE__ */ jsxRuntime.jsxs(
3278
+ "div",
3279
+ {
3280
+ style: {
3281
+ display: "flex",
3282
+ justifyContent: "space-between",
3283
+ marginBottom: "12px",
3284
+ fontSize: "12px",
3285
+ color: "var(--muted-foreground, #8a8a95)"
3286
+ },
3287
+ children: [
3288
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(response.submittedAt).toLocaleString() }),
3289
+ response.completionTimeMs != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3290
+ Math.round(response.completionTimeMs / 1e3),
3291
+ "s"
3292
+ ] })
3293
+ ]
3294
+ }
3295
+ ),
3296
+ fields.slice(0, 4).map((field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "8px" }, children: [
3297
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "11px", color: "var(--muted-foreground, #8a8a95)", marginBottom: "2px" }, children: field.label }),
3298
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "13px", color: "var(--foreground, #e8e8ea)" }, children: formatValue(field.value) })
3299
+ ] }, field.questionId)),
3300
+ fields.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "12px", color: "var(--muted-foreground, #8a8a95)", marginTop: "8px" }, children: [
3301
+ "+",
3302
+ fields.length - 4,
3303
+ " more fields"
3304
+ ] }),
3305
+ response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs(
3306
+ "div",
3307
+ {
3308
+ style: {
3309
+ marginTop: "12px",
3310
+ paddingTop: "8px",
3311
+ borderTop: "1px solid var(--border, #1c1c20)",
3312
+ fontSize: "13px",
3313
+ fontWeight: 600,
3314
+ color: "var(--foreground, #e8e8ea)"
3315
+ },
3316
+ children: [
3317
+ "Score: ",
3318
+ response.totalScore
3319
+ ]
3320
+ }
3321
+ )
3322
+ ]
3323
+ }
3324
+ );
3325
+ }
3326
+ function formatValue(value) {
3327
+ if (value == null) return "\u2014";
3328
+ if (typeof value === "boolean") return value ? "Yes" : "No";
3329
+ if (Array.isArray(value)) return value.map(formatValue).join(", ");
3330
+ if (typeof value === "object") return JSON.stringify(value);
3331
+ return String(value);
3332
+ }
3333
+ function ResponseDetail({ response, fields, onBack }) {
3334
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontFamily: "inherit" }, children: [
3335
+ /* @__PURE__ */ jsxRuntime.jsxs(
3336
+ "div",
3337
+ {
3338
+ style: {
3339
+ display: "flex",
3340
+ alignItems: "center",
3341
+ gap: "12px",
3342
+ marginBottom: "20px",
3343
+ paddingBottom: "12px",
3344
+ borderBottom: "1px solid var(--border, #1c1c20)"
3345
+ },
3346
+ children: [
3347
+ onBack && /* @__PURE__ */ jsxRuntime.jsx(
3348
+ "button",
3349
+ {
3350
+ type: "button",
3351
+ onClick: onBack,
3352
+ style: {
3353
+ padding: "4px 10px",
3354
+ fontSize: "13px",
3355
+ color: "var(--secondary-foreground, #e8e8ea)",
3356
+ backgroundColor: "var(--secondary, #17171a)",
3357
+ border: "1px solid var(--border, #1c1c20)",
3358
+ borderRadius: "6px",
3359
+ cursor: "pointer"
3360
+ },
3361
+ children: "Back"
3362
+ }
3363
+ ),
3364
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3365
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "14px", fontWeight: 600, color: "var(--foreground, #e8e8ea)" }, children: "Response Detail" }),
3366
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "12px", color: "var(--muted-foreground, #8a8a95)" }, children: [
3367
+ "Submitted ",
3368
+ new Date(response.submittedAt).toLocaleString(),
3369
+ response.completionTimeMs != null && ` \u2014 ${Math.round(response.completionTimeMs / 1e3)}s`
3370
+ ] })
3371
+ ] })
3372
+ ]
3373
+ }
3374
+ ),
3375
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs(
3376
+ "div",
3377
+ {
3378
+ style: {
3379
+ padding: "12px",
3380
+ backgroundColor: "var(--muted, #111113)",
3381
+ borderRadius: "6px"
3382
+ },
3383
+ children: [
3384
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "12px", color: "var(--muted-foreground, #8a8a95)", marginBottom: "4px" }, children: [
3385
+ field.label,
3386
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { marginLeft: "8px", fontSize: "11px", color: "var(--muted-foreground, #8a8a95)" }, children: [
3387
+ "(",
3388
+ field.type,
3389
+ ")"
3390
+ ] })
3391
+ ] }),
3392
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "14px", color: "var(--foreground, #e8e8ea)", whiteSpace: "pre-wrap" }, children: formatDetailValue(field.value) })
3393
+ ]
3394
+ },
3395
+ field.questionId
3396
+ )) }),
3397
+ response.scores && Object.keys(response.scores).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: "20px" }, children: [
3398
+ /* @__PURE__ */ jsxRuntime.jsx(
3399
+ "div",
3400
+ {
3401
+ style: {
3402
+ fontSize: "14px",
3403
+ fontWeight: 600,
3404
+ color: "var(--foreground, #e8e8ea)",
3405
+ marginBottom: "12px"
3406
+ },
3407
+ children: "Scores"
3408
+ }
3409
+ ),
3410
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "12px", flexWrap: "wrap" }, children: [
3411
+ Object.entries(response.scores).map(([key, value]) => /* @__PURE__ */ jsxRuntime.jsxs(
3412
+ "div",
3413
+ {
3414
+ style: {
3415
+ padding: "8px 16px",
3416
+ backgroundColor: "var(--accent, #17171a)",
3417
+ borderRadius: "6px",
3418
+ fontSize: "13px"
3419
+ },
3420
+ children: [
3421
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "var(--muted-foreground, #8a8a95)" }, children: [
3422
+ key,
3423
+ ": "
3424
+ ] }),
3425
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, color: "var(--primary, oklch(0.82 0.14 210))" }, children: value })
3426
+ ]
3427
+ },
3428
+ key
3429
+ )),
3430
+ response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs(
3431
+ "div",
3432
+ {
3433
+ style: {
3434
+ padding: "8px 16px",
3435
+ backgroundColor: "var(--accent, #17171a)",
3436
+ borderRadius: "6px",
3437
+ fontSize: "13px"
3438
+ },
3439
+ children: [
3440
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--muted-foreground, #8a8a95)" }, children: "Total: " }),
3441
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, color: "var(--primary, oklch(0.82 0.14 210))" }, children: response.totalScore })
3442
+ ]
3443
+ }
3444
+ )
3445
+ ] })
3446
+ ] }),
3447
+ /* @__PURE__ */ jsxRuntime.jsxs(
3448
+ "div",
3449
+ {
3450
+ style: {
3451
+ marginTop: "20px",
3452
+ paddingTop: "12px",
3453
+ borderTop: "1px solid var(--border, #1c1c20)",
3454
+ fontSize: "12px",
3455
+ color: "var(--muted-foreground, #8a8a95)"
3456
+ },
3457
+ children: [
3458
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3459
+ "Schema: ",
3460
+ response.schemaId,
3461
+ " (v",
3462
+ response.schemaVersion,
3463
+ ")"
3464
+ ] }),
3465
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3466
+ "Session: ",
3467
+ response.sessionToken
3468
+ ] })
3469
+ ]
3470
+ }
3471
+ )
3472
+ ] });
3473
+ }
3474
+ function formatDetailValue(value) {
3475
+ if (value == null) return "\u2014";
3476
+ if (typeof value === "boolean") return value ? "Yes" : "No";
3477
+ if (Array.isArray(value)) return value.map(formatDetailValue).join("\n");
3478
+ if (typeof value === "object") return JSON.stringify(value, null, 2);
3479
+ return String(value);
3480
+ }
3481
+ var viewButtonStyle = (active) => ({
3482
+ padding: "4px 10px",
3483
+ fontSize: "12px",
3484
+ fontWeight: active ? 600 : 400,
3485
+ color: active ? "var(--primary, oklch(0.82 0.14 210))" : "var(--muted-foreground, #8a8a95)",
3486
+ backgroundColor: active ? "var(--accent, #17171a)" : "transparent",
3487
+ border: "1px solid",
3488
+ borderColor: active ? "var(--ring, oklch(0.82 0.14 210))" : "var(--border, #1c1c20)",
3489
+ borderRadius: "6px",
3490
+ cursor: "pointer",
3491
+ fontFamily: "inherit"
3492
+ });
3493
+ function ResponseViewerInner({
3494
+ schema,
3495
+ responses,
3496
+ onResponseSelect,
3497
+ height = "500px",
3498
+ width = "100%"
3499
+ }) {
3500
+ const [viewMode, setViewMode] = react.useState("table");
3501
+ const [selectedResponse, setSelectedResponse] = react.useState(null);
3502
+ function handleSelect(response) {
3503
+ setSelectedResponse(response);
3504
+ onResponseSelect?.(response);
3505
+ }
3506
+ function handleBack() {
3507
+ setSelectedResponse(null);
3508
+ }
3509
+ const questions = getAllQuestions2(schema);
3510
+ function getFields(response) {
3511
+ return questions.map((q) => ({
3512
+ questionId: q.id,
3513
+ label: q.label,
3514
+ type: q.type,
3515
+ value: response.values[q.id]
3516
+ }));
3517
+ }
3518
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3519
+ "div",
3520
+ {
3521
+ style: {
3522
+ display: "flex",
3523
+ flexDirection: "column",
3524
+ height: typeof height === "number" ? `${height}px` : height,
3525
+ width: typeof width === "number" ? `${width}px` : width,
3526
+ border: "1px solid var(--border, #1c1c20)",
3527
+ borderRadius: "8px",
3528
+ overflow: "hidden",
3529
+ fontFamily: "inherit",
3530
+ backgroundColor: "var(--background, #0a0a0b)",
3531
+ color: "var(--foreground, #e8e8ea)"
3532
+ },
3533
+ children: [
3534
+ !selectedResponse && /* @__PURE__ */ jsxRuntime.jsxs(
3535
+ "div",
3536
+ {
3537
+ style: {
3538
+ display: "flex",
3539
+ justifyContent: "space-between",
3540
+ alignItems: "center",
3541
+ padding: "8px 12px",
3542
+ borderBottom: "1px solid var(--border, #1c1c20)"
3543
+ },
3544
+ children: [
3545
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "13px", color: "var(--muted-foreground, #8a8a95)" }, children: [
3546
+ responses.length,
3547
+ " response",
3548
+ responses.length !== 1 ? "s" : ""
3549
+ ] }),
3550
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "4px" }, children: [
3551
+ /* @__PURE__ */ jsxRuntime.jsx(
3552
+ "button",
3553
+ {
3554
+ type: "button",
3555
+ style: viewButtonStyle(viewMode === "table"),
3556
+ onClick: () => setViewMode("table"),
3557
+ children: "Table"
3558
+ }
3559
+ ),
3560
+ /* @__PURE__ */ jsxRuntime.jsx(
3561
+ "button",
3562
+ {
3563
+ type: "button",
3564
+ style: viewButtonStyle(viewMode === "card"),
3565
+ onClick: () => setViewMode("card"),
3566
+ children: "Cards"
3567
+ }
3568
+ )
3569
+ ] })
3570
+ ]
3571
+ }
3572
+ ),
3573
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "auto", padding: selectedResponse ? "16px" : void 0 }, children: selectedResponse ? /* @__PURE__ */ jsxRuntime.jsx(
3574
+ ResponseDetail,
3575
+ {
3576
+ response: selectedResponse,
3577
+ fields: getFields(selectedResponse),
3578
+ onBack: handleBack
3579
+ }
3580
+ ) : viewMode === "table" ? /* @__PURE__ */ jsxRuntime.jsx(
3581
+ ResponseTable,
3582
+ {
3583
+ schema,
3584
+ responses,
3585
+ onRowClick: handleSelect
3586
+ }
3587
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
3588
+ "div",
3589
+ {
3590
+ style: {
3591
+ display: "grid",
3592
+ gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
3593
+ gap: "12px",
3594
+ padding: "12px"
3595
+ },
3596
+ children: [
3597
+ responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsx(
3598
+ ResponseCard,
3599
+ {
3600
+ response,
3601
+ fields: getFields(response),
3602
+ onClick: () => handleSelect(response)
3603
+ },
3604
+ response.sessionToken || idx
3605
+ )),
3606
+ responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "32px", textAlign: "center", color: "var(--muted-foreground, #8a8a95)" }, children: "No responses yet" })
3607
+ ]
3608
+ }
3609
+ ) })
3610
+ ]
3611
+ }
3612
+ );
3613
+ }
3614
+ function getAllQuestions2(schema) {
3615
+ const questions = [];
3616
+ for (const section of schema.sections) {
3617
+ for (const q of section.questions) {
3618
+ if (q.type === "info-block" || q.type === "section-header" || q.type === "page-break") {
3619
+ continue;
3620
+ }
3621
+ questions.push(q);
3622
+ }
3623
+ }
3624
+ return questions;
3625
+ }
3626
+
3627
+ // src/response-viewer/ResponseViewer.tsx
3628
+ var ResponseViewer = fieldcraftProLicense.requireLicense(ResponseViewerInner, "ResponseViewer");
3629
+
3630
+ // src/theme-editor/preview-schema.ts
3631
+ var PREVIEW_SCHEMA = {
3632
+ id: "theme-preview",
3633
+ version: "1.0.0",
3634
+ title: "Theme Preview",
3635
+ description: "See how your theme looks on a real form.",
3636
+ sections: [
3637
+ {
3638
+ id: "s1",
3639
+ title: "Contact Information",
3640
+ questions: [
3641
+ {
3642
+ id: "name",
3643
+ type: "short_text",
3644
+ label: "Full Name",
3645
+ required: true,
3646
+ placeholder: "Jane Doe"
3647
+ },
3648
+ {
3649
+ id: "email",
3650
+ type: "email",
3651
+ label: "Email Address",
3652
+ required: true,
3653
+ placeholder: "jane@example.com"
3654
+ },
3655
+ {
3656
+ id: "department",
3657
+ type: "dropdown",
3658
+ label: "Department",
3659
+ options: [
3660
+ { label: "Engineering", value: "engineering" },
3661
+ { label: "Design", value: "design" },
3662
+ { label: "Marketing", value: "marketing" }
3663
+ ]
3664
+ },
3665
+ {
3666
+ id: "rating",
3667
+ type: "rating",
3668
+ label: "How would you rate this experience?",
3669
+ config: { type: "rating", max: 5 }
3670
+ },
3671
+ {
3672
+ id: "notes",
3673
+ type: "long_text",
3674
+ label: "Additional Notes",
3675
+ placeholder: "Any other feedback..."
3676
+ }
3677
+ ]
3678
+ }
3679
+ ],
3680
+ submitAction: { type: "adapter" }
3681
+ };
3682
+ var PRESETS = {
3683
+ clean: fieldcraftReact.cleanPreset,
3684
+ dark: fieldcraftReact.darkPreset,
3685
+ modern: fieldcraftReact.modernPreset,
3686
+ "high-contrast": fieldcraftReact.highContrastPreset,
3687
+ clinical: fieldcraftReact.clinicalPreset,
3688
+ playful: fieldcraftReact.playfulPreset
3689
+ };
3690
+ var SECTIONS = [
3691
+ {
3692
+ id: "colors",
3693
+ label: "Colors",
3694
+ themeKey: "colors",
3695
+ fields: [
3696
+ { kind: "color", key: "primary", label: "Primary" },
3697
+ { kind: "color", key: "primaryForeground", label: "Primary Foreground" },
3698
+ { kind: "color", key: "secondary", label: "Secondary" },
3699
+ { kind: "color", key: "secondaryForeground", label: "Secondary Foreground" },
3700
+ { kind: "color", key: "error", label: "Error" },
3701
+ { kind: "color", key: "errorForeground", label: "Error Foreground" },
3702
+ { kind: "color", key: "warning", label: "Warning" },
3703
+ { kind: "color", key: "success", label: "Success" },
3704
+ { kind: "color", key: "surface", label: "Surface" },
3705
+ { kind: "color", key: "background", label: "Background" },
3706
+ { kind: "color", key: "text", label: "Text" },
3707
+ { kind: "color", key: "textMuted", label: "Text Muted" },
3708
+ { kind: "color", key: "textDisabled", label: "Text Disabled" },
3709
+ { kind: "color", key: "border", label: "Border" },
3710
+ { kind: "color", key: "borderFocus", label: "Border Focus" },
3711
+ { kind: "color", key: "inputBackground", label: "Input Background" }
3712
+ ]
3713
+ },
3714
+ {
3715
+ id: "typography",
3716
+ label: "Typography",
3717
+ themeKey: "typography",
3718
+ fields: [
3719
+ { kind: "text", key: "fontFamily", label: "Font Family", placeholder: "Inter, system-ui, sans-serif" },
3720
+ {
3721
+ kind: "select",
3722
+ key: "scale",
3723
+ label: "Scale",
3724
+ options: [
3725
+ { label: "Compact", value: "compact" },
3726
+ { label: "Comfortable", value: "comfortable" },
3727
+ { label: "Spacious", value: "spacious" }
3728
+ ]
3729
+ },
3730
+ { kind: "text", key: "questionSize", label: "Question Size", placeholder: "1.125rem" },
3731
+ { kind: "text", key: "labelSize", label: "Label Size", placeholder: "0.875rem" },
3732
+ { kind: "text", key: "helpTextSize", label: "Help Text Size", placeholder: "0.8125rem" },
3733
+ { kind: "text", key: "bodySize", label: "Body Size", placeholder: "0.9375rem" }
3734
+ ]
3735
+ },
3736
+ {
3737
+ id: "shape",
3738
+ label: "Shape",
3739
+ themeKey: "shape",
3740
+ fields: [
3741
+ {
3742
+ kind: "select",
3743
+ key: "radius",
3744
+ label: "Radius",
3745
+ options: [
3746
+ { label: "None", value: "none" },
3747
+ { label: "Small", value: "sm" },
3748
+ { label: "Medium", value: "md" },
3749
+ { label: "Large", value: "lg" },
3750
+ { label: "Full", value: "full" }
3751
+ ]
3752
+ },
3753
+ { kind: "text", key: "inputRadius", label: "Input Radius", placeholder: "8px" },
3754
+ { kind: "text", key: "buttonRadius", label: "Button Radius", placeholder: "8px" },
3755
+ { kind: "text", key: "cardRadius", label: "Card Radius", placeholder: "12px" }
3756
+ ]
3757
+ },
3758
+ {
3759
+ id: "spacing",
3760
+ label: "Spacing",
3761
+ themeKey: "spacing",
3762
+ fields: [
3763
+ { kind: "number", key: "base", label: "Base", min: 4, max: 32, suffix: "px" },
3764
+ { kind: "number", key: "sectionGap", label: "Section Gap", min: 0, max: 64, suffix: "px" },
3765
+ { kind: "number", key: "fieldGap", label: "Field Gap", min: 0, max: 64, suffix: "px" },
3766
+ { kind: "number", key: "inputPaddingX", label: "Input Padding X", min: 0, max: 32, suffix: "px" },
3767
+ { kind: "number", key: "inputPaddingY", label: "Input Padding Y", min: 0, max: 32, suffix: "px" }
3768
+ ]
3769
+ },
3770
+ {
3771
+ id: "layout",
3772
+ label: "Layout",
3773
+ themeKey: "layout",
3774
+ fields: [
3775
+ { kind: "text", key: "maxWidth", label: "Max Width", placeholder: "640px" },
3776
+ {
3777
+ kind: "select",
3778
+ key: "alignment",
3779
+ label: "Alignment",
3780
+ options: [
3781
+ { label: "Left", value: "left" },
3782
+ { label: "Center", value: "center" }
3783
+ ]
3784
+ },
3785
+ {
3786
+ kind: "select",
3787
+ key: "progressPosition",
3788
+ label: "Progress Position",
3789
+ options: [
3790
+ { label: "Top", value: "top" },
3791
+ { label: "Bottom", value: "bottom" },
3792
+ { label: "None", value: "none" }
3793
+ ]
3794
+ },
3795
+ {
3796
+ kind: "select",
3797
+ key: "sectionLayout",
3798
+ label: "Section Layout",
3799
+ options: [
3800
+ { label: "Card", value: "card" },
3801
+ { label: "Flat", value: "flat" },
3802
+ { label: "Bordered", value: "bordered" }
3803
+ ]
3804
+ }
3805
+ ]
3806
+ }
3807
+ ];
3808
+ function ThemeEditorInner({
3809
+ initialTheme,
3810
+ onChange,
3811
+ onSave,
3812
+ height,
3813
+ width,
3814
+ className,
3815
+ toolbarExtra,
3816
+ showPreview = true
3817
+ }) {
3818
+ const [theme, setTheme] = react.useState(initialTheme ?? fieldcraftReact.cleanPreset);
3819
+ const [activeSection, setActiveSection] = react.useState("colors");
3820
+ const [presetKey, setPresetKey] = react.useState("custom");
3821
+ const themeRef = react.useRef(theme);
3822
+ themeRef.current = theme;
3823
+ react.useEffect(() => {
3824
+ function handleKeyDown(e) {
3825
+ if ((e.metaKey || e.ctrlKey) && e.key === "s") {
3826
+ e.preventDefault();
3827
+ onSave?.(themeRef.current);
3828
+ }
3829
+ }
3830
+ window.addEventListener("keydown", handleKeyDown);
3831
+ return () => window.removeEventListener("keydown", handleKeyDown);
3832
+ }, [onSave]);
3833
+ const updateField = react.useCallback(
3834
+ (section, key, value) => {
3835
+ setTheme((prev) => {
3836
+ const next = {
3837
+ ...prev,
3838
+ [section]: { ...prev[section], [key]: value }
3839
+ };
3840
+ onChange?.(next);
3841
+ return next;
3842
+ });
3843
+ setPresetKey("custom");
3844
+ },
3845
+ [onChange]
3846
+ );
3847
+ const loadPreset = react.useCallback(
3848
+ (key) => {
3849
+ const preset = PRESETS[key];
3850
+ if (preset) {
3851
+ setTheme(preset);
3852
+ setPresetKey(key);
3853
+ onChange?.(preset);
3854
+ }
3855
+ },
3856
+ [onChange]
3857
+ );
3858
+ const exportJson = react.useCallback(() => {
3859
+ const blob = new Blob([JSON.stringify(theme, null, 2)], { type: "application/json" });
3860
+ const url = URL.createObjectURL(blob);
3861
+ const a = document.createElement("a");
3862
+ a.href = url;
3863
+ a.download = "fieldcraft-theme.json";
3864
+ a.click();
3865
+ URL.revokeObjectURL(url);
3866
+ }, [theme]);
3867
+ const importJson = react.useCallback(() => {
3868
+ const input = document.createElement("input");
3869
+ input.type = "file";
3870
+ input.accept = ".json";
3871
+ input.onchange = () => {
3872
+ const file = input.files?.[0];
3873
+ if (!file) return;
3874
+ const reader = new FileReader();
3875
+ reader.onload = () => {
3876
+ try {
3877
+ const parsed = JSON.parse(reader.result);
3878
+ setTheme(parsed);
3879
+ setPresetKey("custom");
3880
+ onChange?.(parsed);
3881
+ } catch {
3882
+ }
3883
+ };
3884
+ reader.readAsText(file);
3885
+ };
3886
+ input.click();
3887
+ }, [onChange]);
3888
+ const currentSection = react.useMemo(
3889
+ () => SECTIONS.find((s) => s.id === activeSection),
3890
+ [activeSection]
3891
+ );
3892
+ const sectionValues = theme[currentSection.themeKey] ?? {};
3893
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3894
+ "div",
3895
+ {
3896
+ className: `fcte-root${className ? ` ${className}` : ""}`,
3897
+ style: {
3898
+ height: height ?? "100%",
3899
+ width: width ?? "100%"
3900
+ },
3901
+ children: [
3902
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-toolbar", children: [
3903
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-toolbar__left", children: [
3904
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fcte-toolbar__title", children: "Theme Editor" }),
3905
+ /* @__PURE__ */ jsxRuntime.jsxs(
3906
+ "select",
3907
+ {
3908
+ value: presetKey,
3909
+ onChange: (e) => loadPreset(e.target.value),
3910
+ className: "fcte-toolbar__preset",
3911
+ children: [
3912
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "custom", disabled: true, children: "Custom" }),
3913
+ Object.keys(PRESETS).map((k) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: k, children: k.charAt(0).toUpperCase() + k.slice(1).replace("-", " ") }, k))
3914
+ ]
3915
+ }
3916
+ )
3917
+ ] }),
3918
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-toolbar__right", children: [
3919
+ toolbarExtra,
3920
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: importJson, className: "fcte-btn fcte-btn--secondary", children: "Import" }),
3921
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: exportJson, className: "fcte-btn fcte-btn--secondary", children: "Export" }),
3922
+ onSave && /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => onSave(theme), className: "fcte-btn fcte-btn--primary", children: "Save" })
3923
+ ] })
3924
+ ] }),
3925
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-body", children: [
3926
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-editor", children: [
3927
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fcte-tabs", children: SECTIONS.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
3928
+ "button",
3929
+ {
3930
+ className: `fcte-tab${activeSection === s.id ? " fcte-tab--active" : ""}`,
3931
+ onClick: () => setActiveSection(s.id),
3932
+ children: s.label
3933
+ },
3934
+ s.id
3935
+ )) }),
3936
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fcte-fields", children: currentSection.fields.map((field) => {
3937
+ const val = sectionValues[field.key];
3938
+ if (field.kind === "color") {
3939
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-field", children: [
3940
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "fcte-field__label", children: field.label }),
3941
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-field__color-row", children: [
3942
+ /* @__PURE__ */ jsxRuntime.jsx(
3943
+ "input",
3944
+ {
3945
+ type: "color",
3946
+ value: typeof val === "string" ? val : "#000000",
3947
+ onChange: (e) => updateField(currentSection.themeKey, field.key, e.target.value),
3948
+ className: "fcte-field__swatch"
3949
+ }
3950
+ ),
3951
+ /* @__PURE__ */ jsxRuntime.jsx(
3952
+ "input",
3953
+ {
3954
+ type: "text",
3955
+ value: typeof val === "string" ? val : "",
3956
+ onChange: (e) => updateField(currentSection.themeKey, field.key, e.target.value),
3957
+ className: "fcte-field__text",
3958
+ spellCheck: false
3959
+ }
3960
+ )
3961
+ ] })
3962
+ ] }, field.key);
3963
+ }
3964
+ if (field.kind === "select") {
3965
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-field", children: [
3966
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "fcte-field__label", children: field.label }),
3967
+ /* @__PURE__ */ jsxRuntime.jsx(
3968
+ "select",
3969
+ {
3970
+ value: typeof val === "string" ? val : "",
3971
+ onChange: (e) => updateField(currentSection.themeKey, field.key, e.target.value),
3972
+ className: "fcte-field__select",
3973
+ children: field.options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
3974
+ }
3975
+ )
3976
+ ] }, field.key);
3977
+ }
3978
+ if (field.kind === "number") {
3979
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-field", children: [
3980
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "fcte-field__label", children: field.label }),
3981
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-field__number-row", children: [
3982
+ /* @__PURE__ */ jsxRuntime.jsx(
3983
+ "input",
3984
+ {
3985
+ type: "number",
3986
+ value: typeof val === "number" ? val : 0,
3987
+ onChange: (e) => updateField(currentSection.themeKey, field.key, Number(e.target.value)),
3988
+ min: field.min,
3989
+ max: field.max,
3990
+ className: "fcte-field__number"
3991
+ }
3992
+ ),
3993
+ field.suffix && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fcte-field__suffix", children: field.suffix })
3994
+ ] })
3995
+ ] }, field.key);
3996
+ }
3997
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fcte-field", children: [
3998
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "fcte-field__label", children: field.label }),
3999
+ /* @__PURE__ */ jsxRuntime.jsx(
4000
+ "input",
4001
+ {
4002
+ type: "text",
4003
+ value: typeof val === "string" ? val : "",
4004
+ onChange: (e) => updateField(currentSection.themeKey, field.key, e.target.value),
4005
+ placeholder: field.placeholder,
4006
+ className: "fcte-field__text",
4007
+ spellCheck: false
4008
+ }
4009
+ )
4010
+ ] }, field.key);
4011
+ }) })
4012
+ ] }),
4013
+ showPreview && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fcte-preview", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fcte-preview__inner", children: /* @__PURE__ */ jsxRuntime.jsx(
4014
+ fieldcraftReact.FormEngineRenderer,
4015
+ {
4016
+ schema: PREVIEW_SCHEMA,
4017
+ theme,
4018
+ onSubmit: () => {
4019
+ }
4020
+ }
4021
+ ) }) })
4022
+ ] })
4023
+ ]
4024
+ }
4025
+ );
4026
+ }
4027
+
4028
+ // src/theme-editor/ThemeEditor.tsx
4029
+ var ThemeEditor = fieldcraftProLicense.requireLicense(ThemeEditorInner, "ThemeEditor");
4030
+
4031
+ // src/index.ts
4032
+ if (typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" && globalThis.process.env?.NODE_ENV !== "production") {
4033
+ const _fc_banner = `
4034
+ %c FieldCraft Pro %c v0.0.0
4035
+
4036
+ %cForm Builder \xB7 Response Viewer \xB7 Theme Editor
4037
+
4038
+ Docs \u2192 https://squaredr.tech/products/fieldcraft/docs/pro
4039
+ Pro Tools \u2192 https://squaredr.tech/products/fieldcraft/admin-pro
4040
+ Discord \u2192 https://discord.gg/zMxdu5UVW
4041
+
4042
+ Need a license? \u2192 https://squaredr.tech/products/fieldcraft/admin-pro#pricing
4043
+ `;
4044
+ console.log(
4045
+ _fc_banner,
4046
+ "background:#f59e0b;color:#000;font-weight:bold;padding:2px 6px;border-radius:3px 0 0 3px",
4047
+ "background:#d97706;color:#000;padding:2px 6px;border-radius:0 3px 3px 0",
4048
+ "color:#6b7280"
4049
+ );
4050
+ }
4051
+
4052
+ Object.defineProperty(exports, "FieldCraftProProvider", {
4053
+ enumerable: true,
4054
+ get: function () { return fieldcraftProLicense.FieldCraftProProvider; }
4055
+ });
4056
+ Object.defineProperty(exports, "UnlicensedOverlay", {
4057
+ enumerable: true,
4058
+ get: function () { return fieldcraftProLicense.UnlicensedOverlay; }
4059
+ });
4060
+ Object.defineProperty(exports, "isProductionEnvironment", {
4061
+ enumerable: true,
4062
+ get: function () { return fieldcraftProLicense.isProductionEnvironment; }
4063
+ });
4064
+ Object.defineProperty(exports, "requireLicense", {
4065
+ enumerable: true,
4066
+ get: function () { return fieldcraftProLicense.requireLicense; }
4067
+ });
4068
+ Object.defineProperty(exports, "useLicense", {
4069
+ enumerable: true,
4070
+ get: function () { return fieldcraftProLicense.useLicense; }
4071
+ });
4072
+ Object.defineProperty(exports, "validateLicense", {
4073
+ enumerable: true,
4074
+ get: function () { return fieldcraftProLicense.validateLicense; }
4075
+ });
4076
+ exports.DEFAULT_PALETTE = DEFAULT_PALETTE;
4077
+ exports.DEFAULT_SCHEMA = DEFAULT_SCHEMA;
4078
+ exports.FormBuilder = FormBuilder;
4079
+ exports.FormBuilderThemeProvider = FormBuilderThemeProvider;
4080
+ exports.PREVIEW_SCHEMA = PREVIEW_SCHEMA;
4081
+ exports.QUESTION_TYPE_INFO = QUESTION_TYPE_INFO;
4082
+ exports.ResponseViewer = ResponseViewer;
4083
+ exports.ThemeEditor = ThemeEditor;
4084
+ exports.ThemeEditorInner = ThemeEditorInner;
4085
+ exports.addOption = addOption;
4086
+ exports.addQuestion = addQuestion;
4087
+ exports.addSection = addSection;
4088
+ exports.cleanPreset = cleanPreset;
4089
+ exports.cn = cn;
4090
+ exports.duplicateQuestion = duplicateQuestion;
4091
+ exports.duplicateSection = duplicateSection;
4092
+ exports.findQuestion = findQuestion;
4093
+ exports.findSection = findSection;
4094
+ exports.generateId = generateId;
4095
+ exports.generateOptionId = generateOptionId;
4096
+ exports.generateQuestionId = generateQuestionId;
4097
+ exports.generateSectionId = generateSectionId;
4098
+ exports.moveOption = moveOption;
4099
+ exports.moveQuestion = moveQuestion;
4100
+ exports.moveSection = moveSection;
4101
+ exports.removeOption = removeOption;
4102
+ exports.removeQuestion = removeQuestion;
4103
+ exports.removeSection = removeSection;
4104
+ exports.squaredrDarkPreset = squaredrDarkPreset;
4105
+ exports.updateOption = updateOption;
4106
+ exports.updateQuestion = updateQuestion;
4107
+ exports.updateSection = updateSection;
4108
+ exports.useBuilderState = useBuilderState;
4109
+ exports.useBuilderTheme = useBuilderTheme;
4110
+ exports.useDragDrop = useDragDrop;
4111
+ exports.useUndoRedo = useUndoRedo;