@squaredr/fieldcraft-pro 1.7.0 → 1.9.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.
Files changed (33) hide show
  1. package/LICENSE.txt +51 -51
  2. package/README.md +139 -139
  3. package/dist/{chunk-VEP3DOCQ.mjs → chunk-5QXKDYWY.mjs} +103 -19
  4. package/dist/{chunk-GZIV2XAD.mjs → chunk-7GPX3YIB.mjs} +146 -183
  5. package/dist/{chunk-J5V6OVZO.mjs → chunk-DWRLPGX5.mjs} +690 -211
  6. package/dist/{chunk-4TN6YJDF.mjs → chunk-KTIU7VCP.mjs} +971 -214
  7. package/dist/default-schema-BGxf9Jrg.d.ts +174 -0
  8. package/dist/default-schema-Bwq8FATF.d.mts +174 -0
  9. package/dist/form-builder/index.d.mts +14 -170
  10. package/dist/form-builder/index.d.ts +14 -170
  11. package/dist/form-builder/index.js +827 -326
  12. package/dist/form-builder/index.mjs +1 -1
  13. package/dist/index.d.mts +111 -10
  14. package/dist/index.d.ts +111 -10
  15. package/dist/index.js +3525 -971
  16. package/dist/index.mjs +1424 -124
  17. package/dist/response-viewer/index.d.mts +1 -1
  18. package/dist/response-viewer/index.d.ts +1 -1
  19. package/dist/response-viewer/index.js +146 -183
  20. package/dist/response-viewer/index.mjs +1 -1
  21. package/dist/styles.css +2 -2
  22. package/dist/templates/index.d.mts +1 -1
  23. package/dist/templates/index.d.ts +1 -1
  24. package/dist/templates/index.js +103 -19
  25. package/dist/templates/index.mjs +1 -1
  26. package/dist/theme-editor/index.d.mts +12 -7
  27. package/dist/theme-editor/index.d.ts +12 -7
  28. package/dist/theme-editor/index.js +971 -214
  29. package/dist/theme-editor/index.mjs +1 -1
  30. package/dist/{types-bFHsZT1n.d.mts → types-BQOeI3LX.d.mts} +1 -1
  31. package/dist/{types-bFHsZT1n.d.ts → types-BQOeI3LX.d.ts} +1 -1
  32. package/package.json +29 -18
  33. package/theme-editor/styles.css +497 -466
@@ -1,11 +1,13 @@
1
1
  import { cn } from './chunk-QQ4JZGTD.mjs';
2
2
  import { requireLicense } from './chunk-4MMKB2EW.mjs';
3
+ import * as React from 'react';
3
4
  import { forwardRef, lazy, memo, createContext, Component, useState, useRef, useEffect, useCallback, useMemo, Suspense, useContext } from 'react';
4
5
  import { DndContext, DragOverlay, useSensors, useSensor, MouseSensor, TouchSensor, useDraggable, useDroppable } from '@dnd-kit/core';
5
- import { ChevronDown, Undo2, Redo2, PencilRuler, Eye, Code, GitBranch, LayoutTemplate, Upload, Download, Save, X, PanelLeft, PanelRight, Search, ChevronRight, Plus, Settings, FileText, Layers, ZoomOut, ZoomIn, Maximize, Copy, Trash2, HelpCircle, MoveVertical, Minus, Video, Image, PartyPopper, Hand, ShieldCheck, SeparatorHorizontal, Info, Heading, Trophy, EyeOff, Calculator, CreditCard, MapPin, Repeat, Grid3X3, Camera, PenTool, Paperclip, CalendarCheck, CalendarRange, Clock, Calendar, ArrowUpDown, Globe, ToggleLeft, CheckSquare, CircleDot, ListOrdered, TrendingUp, BarChart3, Star, SlidersHorizontal, Hash, UserCheck, Link, PhoneCall, Phone, Mail, AlignLeft, Type, GripVertical } from 'lucide-react';
6
+ import { ChevronDown, ChevronUp, Check, Undo2, Redo2, PencilRuler, Eye, Code, GitBranch, LayoutTemplate, Upload, Download, Save, X, PanelLeft, PanelRight, Search, ChevronRight, Plus, Settings, FileText, Layers, ZoomOut, ZoomIn, Maximize, Copy, Trash2, HelpCircle, MoveVertical, Minus, Video, Image, PartyPopper, Hand, ShieldCheck, SeparatorHorizontal, Info, Heading, Trophy, EyeOff, Calculator, CreditCard, MapPin, Repeat, Grid3X3, Camera, PenTool, Paperclip, CalendarCheck, CalendarRange, Clock, Calendar, ArrowUpDown, Globe, ToggleLeft, CheckSquare, CircleDot, ListOrdered, TrendingUp, BarChart3, Star, SlidersHorizontal, Hash, UserCheck, Link, PhoneCall, Phone, Mail, AlignLeft, Type, GripVertical } from 'lucide-react';
6
7
  import { Button, Separator, FormEngineRenderer, Input, Badge, Textarea, Label, Switch } from '@squaredr/fieldcraft-react';
7
8
  import { validateSchema, FormEngineSchemaError } from '@squaredr/fieldcraft-core';
8
9
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
+ import * as SelectPrimitive from '@radix-ui/react-select';
9
11
  import { MarkerType, Handle, Position, getBezierPath, BaseEdge, EdgeLabelRenderer, useNodesState, useEdgesState, ReactFlowProvider, useReactFlow, ReactFlow, Background, MiniMap } from '@xyflow/react';
10
12
  import '@xyflow/react/dist/style.css';
11
13
 
@@ -17,16 +19,18 @@ function useUndoRedo(currentSchema, setSchema) {
17
19
  const canRedo = currentIndex < historyRef.current.length - 1;
18
20
  const push = useCallback(
19
21
  (schema) => {
20
- historyRef.current = historyRef.current.slice(0, currentIndex + 1);
21
- historyRef.current.push(schema);
22
- if (historyRef.current.length > MAX_HISTORY) {
23
- historyRef.current.shift();
24
- setCurrentIndex(historyRef.current.length - 1);
25
- } else {
26
- setCurrentIndex((prev) => prev + 1);
27
- }
22
+ setCurrentIndex((prevIndex) => {
23
+ historyRef.current = historyRef.current.slice(0, prevIndex + 1);
24
+ historyRef.current.push(schema);
25
+ let newIndex = prevIndex + 1;
26
+ if (historyRef.current.length > MAX_HISTORY) {
27
+ historyRef.current.shift();
28
+ newIndex = Math.max(0, newIndex - 1);
29
+ }
30
+ return newIndex;
31
+ });
28
32
  },
29
- [currentIndex]
33
+ []
30
34
  );
31
35
  const undo = useCallback(() => {
32
36
  if (currentIndex > 0) {
@@ -57,13 +61,9 @@ function useUndoRedo(currentSchema, setSchema) {
57
61
  }
58
62
 
59
63
  // src/form-builder/utils/id-generator.ts
60
- var counter = 0;
61
64
  function generateId(prefix) {
62
- const timestamp = Date.now().toString(36);
63
- const random = Math.random().toString(36).substring(2, 7);
64
- counter = (counter + 1) % 1e4;
65
- const count = counter.toString(36);
66
- return `${prefix}_${timestamp}${count}${random}`;
65
+ const uuid = crypto.randomUUID().replace(/-/g, "").substring(0, 12);
66
+ return `${prefix}_${uuid}`;
67
67
  }
68
68
  function generateSectionId() {
69
69
  return generateId("section");
@@ -150,18 +150,39 @@ function duplicateSection(schema, sectionId) {
150
150
  const sectionIndex = newSchema.sections.findIndex((s) => s.id === sectionId);
151
151
  if (sectionIndex === -1) return schema;
152
152
  const original = newSchema.sections[sectionIndex];
153
+ const idMap = /* @__PURE__ */ new Map();
154
+ for (const q of original.questions) {
155
+ idMap.set(q.id, generateQuestionId());
156
+ }
153
157
  const duplicate = {
154
158
  ...original,
155
159
  id: generateSectionId(),
156
160
  title: `${original.title} (Copy)`,
161
+ showIf: original.showIf ? remapConditionFieldIds(original.showIf, idMap) : void 0,
157
162
  questions: original.questions.map((q) => ({
158
163
  ...q,
159
- id: generateQuestionId()
164
+ id: idMap.get(q.id),
165
+ showIf: q.showIf ? remapConditionFieldIds(q.showIf, idMap) : void 0
160
166
  }))
161
167
  };
162
168
  newSchema.sections.splice(sectionIndex + 1, 0, duplicate);
163
169
  return newSchema;
164
170
  }
171
+ function remapConditionFieldIds(expr, idMap) {
172
+ if (expr.field) {
173
+ return {
174
+ ...expr,
175
+ field: idMap.get(expr.field) ?? expr.field
176
+ };
177
+ }
178
+ if (expr.conditions) {
179
+ return {
180
+ ...expr,
181
+ conditions: expr.conditions.map((c) => remapConditionFieldIds(c, idMap))
182
+ };
183
+ }
184
+ return expr;
185
+ }
165
186
  function addQuestion(schema, sectionId, question, index) {
166
187
  const newSchema = deepClone(schema);
167
188
  const section = newSchema.sections.find((s) => s.id === sectionId);
@@ -305,7 +326,7 @@ function useBuilderState(initialSchema) {
305
326
  (sectionId) => {
306
327
  applyMutation((s) => removeSection(s, sectionId));
307
328
  setSelectedItem((prev) => {
308
- if (prev?.type === "section" && prev.sectionId === sectionId) return null;
329
+ if (prev?.sectionId === sectionId) return null;
309
330
  return prev;
310
331
  });
311
332
  },
@@ -644,7 +665,7 @@ var QUESTION_TYPE_INFO = {
644
665
  category: "advanced",
645
666
  icon: "ListPlus",
646
667
  description: "Repeatable group of fields",
647
- defaultConfig: { type: "repeater", fields: [], minItems: 1, maxItems: 10 }
668
+ defaultConfig: { type: "repeater", fields: [], minEntries: 1, maxEntries: 10 }
648
669
  },
649
670
  likert: {
650
671
  type: "likert",
@@ -655,13 +676,7 @@ var QUESTION_TYPE_INFO = {
655
676
  requiresOptions: true,
656
677
  defaultConfig: {
657
678
  type: "likert",
658
- scale: [
659
- { label: "Strongly Disagree", value: "1" },
660
- { label: "Disagree", value: "2" },
661
- { label: "Neutral", value: "3" },
662
- { label: "Agree", value: "4" },
663
- { label: "Strongly Agree", value: "5" }
664
- ]
679
+ labels: ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"]
665
680
  }
666
681
  },
667
682
  scoring: {
@@ -714,7 +729,7 @@ var QUESTION_TYPE_INFO = {
714
729
  category: "advanced",
715
730
  icon: "CreditCard",
716
731
  description: "Collect payment via Stripe",
717
- defaultConfig: { type: "payment", provider: "stripe", publicKey: "", currency: "USD" }
732
+ defaultConfig: { type: "payment", provider: "stripe", publicKey: "", currency: "USD", serverUrl: "" }
718
733
  },
719
734
  // ── Structural ──
720
735
  section_header: {
@@ -1441,6 +1456,14 @@ function SectionBlock({ section, builderState }) {
1441
1456
  }
1442
1457
  function FormCanvas({ builderState }) {
1443
1458
  const { schema } = builderState;
1459
+ const [localTitle, setLocalTitle] = useState(schema.title);
1460
+ const [localDesc, setLocalDesc] = useState(schema.description ?? "");
1461
+ useEffect(() => {
1462
+ setLocalTitle(schema.title);
1463
+ }, [schema.title]);
1464
+ useEffect(() => {
1465
+ setLocalDesc(schema.description ?? "");
1466
+ }, [schema.description]);
1444
1467
  const handleAddSection = () => {
1445
1468
  const newSection = {
1446
1469
  id: generateSectionId(),
@@ -1457,9 +1480,12 @@ function FormCanvas({ builderState }) {
1457
1480
  "input",
1458
1481
  {
1459
1482
  type: "text",
1460
- value: schema.title,
1461
- onChange: (e) => {
1462
- builderState.updateSchema({ ...schema, title: e.target.value });
1483
+ value: localTitle,
1484
+ onChange: (e) => setLocalTitle(e.target.value),
1485
+ onBlur: () => {
1486
+ if (localTitle !== schema.title) {
1487
+ builderState.updateSchema({ ...schema, title: localTitle });
1488
+ }
1463
1489
  },
1464
1490
  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",
1465
1491
  placeholder: "Form Title"
@@ -1468,9 +1494,12 @@ function FormCanvas({ builderState }) {
1468
1494
  /* @__PURE__ */ jsx(
1469
1495
  "textarea",
1470
1496
  {
1471
- value: schema.description ?? "",
1472
- onChange: (e) => {
1473
- builderState.updateSchema({ ...schema, description: e.target.value });
1497
+ value: localDesc,
1498
+ onChange: (e) => setLocalDesc(e.target.value),
1499
+ onBlur: () => {
1500
+ if (localDesc !== (schema.description ?? "")) {
1501
+ builderState.updateSchema({ ...schema, description: localDesc || void 0 });
1502
+ }
1474
1503
  },
1475
1504
  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",
1476
1505
  placeholder: "Form description (optional)"
@@ -1509,259 +1538,469 @@ function FormCanvas({ builderState }) {
1509
1538
  ] })
1510
1539
  ] }) });
1511
1540
  }
1512
- var NativeSelect = forwardRef(
1513
- ({ className, wrapperClassName, children, ...props }, ref) => {
1514
- return /* @__PURE__ */ jsxs("div", { className: cn("relative", wrapperClassName), children: [
1541
+ var Select = SelectPrimitive.Root;
1542
+ var SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1543
+ SelectPrimitive.Trigger,
1544
+ {
1545
+ ref,
1546
+ className: cn(
1547
+ "flex h-9 w-full items-center justify-between rounded-md border border-input bg-card px-3 py-1 text-sm shadow-xs transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
1548
+ className
1549
+ ),
1550
+ ...props,
1551
+ children: [
1552
+ children,
1553
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-4 text-muted-foreground" }) })
1554
+ ]
1555
+ }
1556
+ ));
1557
+ SelectTrigger.displayName = "SelectTrigger";
1558
+ var SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
1559
+ SelectPrimitive.Content,
1560
+ {
1561
+ ref,
1562
+ className: cn(
1563
+ "relative z-50 max-h-64 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
1564
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
1565
+ className
1566
+ ),
1567
+ position,
1568
+ ...props,
1569
+ children: [
1570
+ /* @__PURE__ */ jsx(SelectPrimitive.ScrollUpButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ jsx(ChevronUp, { className: "size-4" }) }),
1515
1571
  /* @__PURE__ */ jsx(
1516
- "select",
1572
+ SelectPrimitive.Viewport,
1517
1573
  {
1518
- ref,
1519
1574
  className: cn(
1520
- "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",
1521
- className
1575
+ "p-1",
1576
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
1522
1577
  ),
1523
- ...props,
1524
1578
  children
1525
1579
  }
1526
1580
  ),
1527
- /* @__PURE__ */ jsx(ChevronDown, { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" })
1528
- ] });
1581
+ /* @__PURE__ */ jsx(SelectPrimitive.ScrollDownButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-4" }) })
1582
+ ]
1529
1583
  }
1530
- );
1531
- NativeSelect.displayName = "NativeSelect";
1584
+ ) }));
1585
+ SelectContent.displayName = "SelectContent";
1586
+ var SelectItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1587
+ SelectPrimitive.Item,
1588
+ {
1589
+ ref,
1590
+ className: cn(
1591
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
1592
+ className
1593
+ ),
1594
+ ...props,
1595
+ children: [
1596
+ /* @__PURE__ */ jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "size-4" }) }) }),
1597
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
1598
+ ]
1599
+ }
1600
+ ));
1601
+ SelectItem.displayName = "SelectItem";
1532
1602
  function useConfigUpdater(question, onUpdate) {
1533
- return (field, value) => {
1603
+ return (updates) => {
1534
1604
  const current = question.config ?? {};
1535
- onUpdate({ config: { ...current, type: question.type, [field]: value } });
1605
+ const merged = { ...current, type: question.type, ...updates };
1606
+ for (const key of Object.keys(merged)) {
1607
+ if (merged[key] === void 0) delete merged[key];
1608
+ }
1609
+ onUpdate({ config: merged });
1536
1610
  };
1537
1611
  }
1538
- function QuestionConfigEditor({ question, onUpdate }) {
1612
+ function QuestionConfigEditor({ question, schema, onUpdate }) {
1539
1613
  const updateConfig = useConfigUpdater(question, onUpdate);
1540
1614
  const config = question.config ?? {};
1615
+ const allQuestions = useMemo(() => {
1616
+ if (!schema?.sections) return [];
1617
+ return schema.sections.flatMap((s) => s.questions || []);
1618
+ }, [schema]);
1619
+ const candidateAmountFields = useMemo(() => {
1620
+ return allQuestions.filter((q) => q.id !== question.id && ["calculated", "number", "slider", "rating", "single_select"].includes(q.type)).map((q) => ({ label: `${q.label || q.id} (${q.id})`, value: q.id }));
1621
+ }, [allQuestions, question.id]);
1622
+ const candidateTimezoneFields = useMemo(() => {
1623
+ return allQuestions.filter((q) => q.id !== question.id && ["dropdown", "single_select", "short_text"].includes(q.type)).map((q) => ({ label: `${q.label || q.id} (${q.id})`, value: q.id }));
1624
+ }, [allQuestions, question.id]);
1541
1625
  switch (question.type) {
1542
1626
  // ── Text ──
1543
1627
  case "short_text":
1544
1628
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Text Settings", children: [
1545
- /* @__PURE__ */ jsx(NumberField, { label: "Max Length", value: config.maxLength, placeholder: "255", onChange: (v) => updateConfig("maxLength", v) }),
1546
- /* @__PURE__ */ jsx(SelectField, { label: "Input Type", value: config.inputType ?? "text", options: [{ label: "Text", value: "text" }, { label: "Password", value: "password" }], onChange: (v) => updateConfig("inputType", v) }),
1547
- /* @__PURE__ */ jsx(TextField, { label: "Prefix", value: config.prefix, placeholder: "e.g. $", onChange: (v) => updateConfig("prefix", v) }),
1548
- /* @__PURE__ */ jsx(TextField, { label: "Suffix", value: config.suffix, placeholder: "e.g. USD", onChange: (v) => updateConfig("suffix", v) })
1629
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Length", value: config.maxLength, placeholder: "255", onChange: (v) => updateConfig({ maxLength: v }) }),
1630
+ /* @__PURE__ */ jsx(SelectField, { label: "Input Type", value: config.inputType ?? "text", options: [{ label: "Text", value: "text" }, { label: "Password", value: "password" }], onChange: (v) => updateConfig({ inputType: v }) }),
1631
+ /* @__PURE__ */ jsx(TextField, { label: "Prefix", value: config.prefix, placeholder: "e.g. $", onChange: (v) => updateConfig({ prefix: v }) }),
1632
+ /* @__PURE__ */ jsx(TextField, { label: "Suffix", value: config.suffix, placeholder: "e.g. USD", onChange: (v) => updateConfig({ suffix: v }) })
1549
1633
  ] });
1550
1634
  case "long_text":
1551
1635
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Text Settings", children: [
1552
- /* @__PURE__ */ jsx(NumberField, { label: "Max Length", value: config.maxLength, placeholder: "No limit", onChange: (v) => updateConfig("maxLength", v) }),
1553
- /* @__PURE__ */ jsx(NumberField, { label: "Rows", value: config.rows, placeholder: "4", onChange: (v) => updateConfig("rows", v) }),
1554
- /* @__PURE__ */ jsx(ToggleField, { label: "Show Character Count", checked: !!config.showCharCount, onChange: (v) => updateConfig("showCharCount", v) })
1636
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Length", value: config.maxLength, placeholder: "No limit", onChange: (v) => updateConfig({ maxLength: v }) }),
1637
+ /* @__PURE__ */ jsx(NumberField, { label: "Rows", value: config.rows, placeholder: "4", onChange: (v) => updateConfig({ rows: v }) }),
1638
+ /* @__PURE__ */ jsx(ToggleField, { label: "Show Character Count", checked: !!config.showCharCount, onChange: (v) => updateConfig({ showCharCount: v }) })
1555
1639
  ] });
1556
1640
  case "legal_name":
1557
1641
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Name Fields", children: [
1558
- /* @__PURE__ */ jsx(ToggleField, { label: "Show Middle Name", checked: !!config.showMiddleName, onChange: (v) => updateConfig("showMiddleName", v) }),
1559
- /* @__PURE__ */ jsx(ToggleField, { label: "Show Suffix", checked: !!config.showSuffix, onChange: (v) => updateConfig("showSuffix", v) })
1642
+ /* @__PURE__ */ jsx(ToggleField, { label: "Show Middle Name", checked: !!config.showMiddleName, onChange: (v) => updateConfig({ showMiddleName: v }) }),
1643
+ /* @__PURE__ */ jsx(ToggleField, { label: "Show Suffix", checked: !!config.showSuffix, onChange: (v) => updateConfig({ showSuffix: v }) })
1560
1644
  ] });
1561
1645
  // ── Numeric ──
1562
1646
  case "number":
1563
1647
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Number Settings", children: [
1564
- /* @__PURE__ */ jsx(NumberField, { label: "Min", value: config.min, onChange: (v) => updateConfig("min", v) }),
1565
- /* @__PURE__ */ jsx(NumberField, { label: "Max", value: config.max, onChange: (v) => updateConfig("max", v) }),
1566
- /* @__PURE__ */ jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig("step", v) }),
1567
- /* @__PURE__ */ jsx(NumberField, { label: "Decimal Places", value: config.decimalPlaces, placeholder: "0", onChange: (v) => updateConfig("decimalPlaces", v) }),
1568
- /* @__PURE__ */ jsx(TextField, { label: "Prefix", value: config.prefix, placeholder: "e.g. $", onChange: (v) => updateConfig("prefix", v) }),
1569
- /* @__PURE__ */ jsx(TextField, { label: "Suffix", value: config.suffix, placeholder: "e.g. kg", onChange: (v) => updateConfig("suffix", v) })
1648
+ /* @__PURE__ */ jsx(NumberField, { label: "Min Value", value: config.min, onChange: (v) => updateConfig({ min: v }) }),
1649
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Value", value: config.max, onChange: (v) => updateConfig({ max: v }) }),
1650
+ /* @__PURE__ */ jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig({ step: v }) }),
1651
+ /* @__PURE__ */ jsx(TextField, { label: "Prefix", value: config.prefix, placeholder: "e.g. $", onChange: (v) => updateConfig({ prefix: v }) }),
1652
+ /* @__PURE__ */ jsx(TextField, { label: "Suffix", value: config.suffix, placeholder: "e.g. kg", onChange: (v) => updateConfig({ suffix: v }) })
1570
1653
  ] });
1571
1654
  case "slider":
1572
1655
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Slider Settings", children: [
1573
- /* @__PURE__ */ jsx(NumberField, { label: "Min", value: config.min, placeholder: "0", onChange: (v) => updateConfig("min", v) }),
1574
- /* @__PURE__ */ jsx(NumberField, { label: "Max", value: config.max, placeholder: "100", onChange: (v) => updateConfig("max", v) }),
1575
- /* @__PURE__ */ jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig("step", v) }),
1576
- /* @__PURE__ */ jsx(ToggleField, { label: "Show Value", checked: !!config.showValue, onChange: (v) => updateConfig("showValue", v) }),
1577
- /* @__PURE__ */ jsx(TextField, { label: "Min Label", value: config.minLabel, onChange: (v) => updateConfig("minLabel", v) }),
1578
- /* @__PURE__ */ jsx(TextField, { label: "Max Label", value: config.maxLabel, onChange: (v) => updateConfig("maxLabel", v) })
1656
+ /* @__PURE__ */ jsx(NumberField, { label: "Min Value", value: config.min, placeholder: "0", onChange: (v) => updateConfig({ min: v ?? 0 }) }),
1657
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Value", value: config.max, placeholder: "100", onChange: (v) => updateConfig({ max: v ?? 100 }) }),
1658
+ /* @__PURE__ */ jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig({ step: v ?? 1 }) }),
1659
+ /* @__PURE__ */ jsx(TextField, { label: "Min Label", value: config.minLabel, placeholder: "Low", onChange: (v) => updateConfig({ minLabel: v }) }),
1660
+ /* @__PURE__ */ jsx(TextField, { label: "Max Label", value: config.maxLabel, placeholder: "High", onChange: (v) => updateConfig({ maxLabel: v }) }),
1661
+ /* @__PURE__ */ jsx(ToggleField, { label: "Show Value", checked: config.showValue !== false, onChange: (v) => updateConfig({ showValue: v }) }),
1662
+ /* @__PURE__ */ jsx(TextField, { label: "Unit", value: config.unit, placeholder: "e.g. %", onChange: (v) => updateConfig({ unit: v }) })
1579
1663
  ] });
1580
1664
  case "rating":
1581
1665
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Rating Settings", children: [
1582
- /* @__PURE__ */ jsx(NumberField, { label: "Max Stars", value: config.max, placeholder: "5", onChange: (v) => updateConfig("max", v) }),
1583
- /* @__PURE__ */ 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) }),
1584
- /* @__PURE__ */ jsx(ToggleField, { label: "Show Labels", checked: !!config.showLabels, onChange: (v) => updateConfig("showLabels", v) })
1666
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Rating", value: config.maxRating, placeholder: "5", onChange: (v) => updateConfig({ maxRating: v ?? 5 }) }),
1667
+ /* @__PURE__ */ jsx(SelectField, { label: "Icon", value: config.icon ?? "star", options: [{ label: "Star", value: "star" }, { label: "Heart", value: "heart" }, { label: "Thumb", value: "thumb" }], onChange: (v) => updateConfig({ icon: v }) }),
1668
+ /* @__PURE__ */ jsx(ToggleField, { label: "Allow Half Ratings", checked: !!config.allowHalf, onChange: (v) => updateConfig({ allowHalf: v }) })
1585
1669
  ] });
1586
1670
  case "nps":
1587
1671
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "NPS Settings", children: [
1588
- /* @__PURE__ */ jsx(TextField, { label: "Low Label", value: config.lowLabel ?? "Not likely", onChange: (v) => updateConfig("lowLabel", v) }),
1589
- /* @__PURE__ */ jsx(TextField, { label: "High Label", value: config.highLabel ?? "Very likely", onChange: (v) => updateConfig("highLabel", v) })
1672
+ /* @__PURE__ */ jsx(TextField, { label: "Left Label", value: config.leftLabel ?? "Not likely", onChange: (v) => updateConfig({ leftLabel: v }) }),
1673
+ /* @__PURE__ */ jsx(TextField, { label: "Right Label", value: config.rightLabel ?? "Extremely likely", onChange: (v) => updateConfig({ rightLabel: v }) })
1590
1674
  ] });
1591
1675
  case "opinion_scale":
1592
1676
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Scale Settings", children: [
1593
- /* @__PURE__ */ jsx(NumberField, { label: "Min", value: config.min, placeholder: "1", onChange: (v) => updateConfig("min", v) }),
1594
- /* @__PURE__ */ jsx(NumberField, { label: "Max", value: config.max, placeholder: "5", onChange: (v) => updateConfig("max", v) }),
1595
- /* @__PURE__ */ jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig("step", v) }),
1596
- /* @__PURE__ */ jsx(TextField, { label: "Min Label", value: config.minLabel, onChange: (v) => updateConfig("minLabel", v) }),
1597
- /* @__PURE__ */ jsx(TextField, { label: "Max Label", value: config.maxLabel, onChange: (v) => updateConfig("maxLabel", v) })
1677
+ /* @__PURE__ */ jsx(NumberField, { label: "Min", value: config.min, placeholder: "1", onChange: (v) => updateConfig({ min: v }) }),
1678
+ /* @__PURE__ */ jsx(NumberField, { label: "Max", value: config.max, placeholder: "5", onChange: (v) => updateConfig({ max: v }) }),
1679
+ /* @__PURE__ */ jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig({ step: v }) }),
1680
+ /* @__PURE__ */ jsx(TextField, { label: "Min Label", value: config.minLabel, onChange: (v) => updateConfig({ minLabel: v }) }),
1681
+ /* @__PURE__ */ jsx(TextField, { label: "Max Label", value: config.maxLabel, onChange: (v) => updateConfig({ maxLabel: v }) })
1598
1682
  ] });
1599
1683
  case "likert":
1600
- return /* @__PURE__ */ jsx(ConfigSection, { title: "Likert Settings", children: /* @__PURE__ */ jsx(LikertLabelsEditor, { labels: config.labels ?? ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"], onChange: (v) => updateConfig("labels", v) }) });
1684
+ return /* @__PURE__ */ jsx(ConfigSection, { title: "Likert Settings", children: /* @__PURE__ */ jsx(LikertLabelsEditor, { labels: config.labels ?? ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"], onChange: (v) => updateConfig({ labels: v }) }) });
1601
1685
  // ── Selection ──
1602
1686
  case "single_select":
1603
1687
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Select Settings", children: [
1604
- /* @__PURE__ */ 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) }),
1605
- /* @__PURE__ */ jsx(ToggleField, { label: "Allow Other", checked: !!config.allowOther, onChange: (v) => updateConfig("allowOther", v) }),
1606
- !!config.allowOther && /* @__PURE__ */ jsx(TextField, { label: "Other Label", value: config.otherLabel ?? "Other", onChange: (v) => updateConfig("otherLabel", v) })
1688
+ /* @__PURE__ */ 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 }) }),
1689
+ /* @__PURE__ */ jsx(ToggleField, { label: "Allow Other", checked: !!config.allowOther, onChange: (v) => v ? updateConfig({ allowOther: true }) : updateConfig({ allowOther: false, otherLabel: void 0 }) }),
1690
+ !!config.allowOther && /* @__PURE__ */ jsx(TextField, { label: "Other Label", value: config.otherLabel ?? "Other", onChange: (v) => updateConfig({ otherLabel: v }) })
1607
1691
  ] });
1608
1692
  case "multi_select":
1609
1693
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Multi-Select Settings", children: [
1610
- /* @__PURE__ */ 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) }),
1611
- /* @__PURE__ */ jsx(NumberField, { label: "Min Selections", value: config.minSelections, onChange: (v) => updateConfig("minSelections", v) }),
1612
- /* @__PURE__ */ jsx(NumberField, { label: "Max Selections", value: config.maxSelections, onChange: (v) => updateConfig("maxSelections", v) }),
1613
- /* @__PURE__ */ jsx(ToggleField, { label: "Allow Other", checked: !!config.allowOther, onChange: (v) => updateConfig("allowOther", v) }),
1614
- !!config.allowOther && /* @__PURE__ */ jsx(TextField, { label: "Other Label", value: config.otherLabel ?? "Other", onChange: (v) => updateConfig("otherLabel", v) })
1694
+ /* @__PURE__ */ 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 }) }),
1695
+ /* @__PURE__ */ jsx(NumberField, { label: "Min Selections", value: config.minSelections, onChange: (v) => updateConfig({ minSelections: v }) }),
1696
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Selections", value: config.maxSelections, onChange: (v) => updateConfig({ maxSelections: v }) }),
1697
+ /* @__PURE__ */ jsx(ToggleField, { label: "Allow Other", checked: !!config.allowOther, onChange: (v) => v ? updateConfig({ allowOther: true }) : updateConfig({ allowOther: false, otherLabel: void 0 }) }),
1698
+ !!config.allowOther && /* @__PURE__ */ jsx(TextField, { label: "Other Label", value: config.otherLabel ?? "Other", onChange: (v) => updateConfig({ otherLabel: v }) })
1615
1699
  ] });
1616
1700
  case "dropdown":
1617
1701
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Dropdown Settings", children: [
1618
- /* @__PURE__ */ jsx(ToggleField, { label: "Searchable", checked: !!config.searchable, onChange: (v) => updateConfig("searchable", v) }),
1619
- /* @__PURE__ */ jsx(ToggleField, { label: "Allow Other", checked: !!config.allowOther, onChange: (v) => updateConfig("allowOther", v) }),
1620
- /* @__PURE__ */ jsx(ToggleField, { label: "Multiple", checked: !!config.multiple, onChange: (v) => updateConfig("multiple", v) })
1702
+ /* @__PURE__ */ jsx(ToggleField, { label: "Searchable", checked: !!config.searchable, onChange: (v) => updateConfig({ searchable: v }) }),
1703
+ /* @__PURE__ */ jsx(ToggleField, { label: "Allow Other", checked: !!config.allowOther, onChange: (v) => updateConfig({ allowOther: v }) }),
1704
+ /* @__PURE__ */ jsx(ToggleField, { label: "Multiple", checked: !!config.multiple, onChange: (v) => updateConfig({ multiple: v }) })
1621
1705
  ] });
1622
1706
  case "boolean":
1623
1707
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Yes/No Settings", children: [
1624
- /* @__PURE__ */ 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) }),
1625
- /* @__PURE__ */ jsx(TextField, { label: "True Label", value: config.trueLabel ?? "Yes", onChange: (v) => updateConfig("trueLabel", v) }),
1626
- /* @__PURE__ */ jsx(TextField, { label: "False Label", value: config.falseLabel ?? "No", onChange: (v) => updateConfig("falseLabel", v) })
1708
+ /* @__PURE__ */ 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 }) }),
1709
+ /* @__PURE__ */ jsx(TextField, { label: "True Label", value: config.trueLabel ?? "Yes", onChange: (v) => updateConfig({ trueLabel: v }) }),
1710
+ /* @__PURE__ */ jsx(TextField, { label: "False Label", value: config.falseLabel ?? "No", onChange: (v) => updateConfig({ falseLabel: v }) })
1627
1711
  ] });
1628
1712
  case "country_select":
1629
1713
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Country Settings", children: [
1630
- /* @__PURE__ */ jsx(ToggleField, { label: "Show Flags", checked: config.showFlags !== false, onChange: (v) => updateConfig("showFlags", v) }),
1631
- /* @__PURE__ */ 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) }),
1632
- /* @__PURE__ */ 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) })
1714
+ /* @__PURE__ */ jsx(ToggleField, { label: "Show Flags", checked: config.showFlags !== false, onChange: (v) => updateConfig({ showFlags: v }) }),
1715
+ /* @__PURE__ */ 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 }) }),
1716
+ /* @__PURE__ */ 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 }) })
1633
1717
  ] });
1634
1718
  // ── Date/Time ──
1635
1719
  case "date":
1636
1720
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Date Settings", children: [
1637
- /* @__PURE__ */ jsx(TextField, { label: "Min Date", value: config.minDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig("minDate", v) }),
1638
- /* @__PURE__ */ jsx(TextField, { label: "Max Date", value: config.maxDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig("maxDate", v) }),
1639
- /* @__PURE__ */ jsx(ToggleField, { label: "Disable Past Dates", checked: !!config.disablePast, onChange: (v) => updateConfig("disablePast", v) }),
1640
- /* @__PURE__ */ jsx(ToggleField, { label: "Disable Future Dates", checked: !!config.disableFuture, onChange: (v) => updateConfig("disableFuture", v) }),
1641
- /* @__PURE__ */ jsx(TextField, { label: "Format", value: config.format, placeholder: "e.g. MM/DD/YYYY", onChange: (v) => updateConfig("format", v) })
1721
+ /* @__PURE__ */ jsx(TextField, { label: "Min Date", value: config.minDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig({ minDate: v }) }),
1722
+ /* @__PURE__ */ jsx(TextField, { label: "Max Date", value: config.maxDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig({ maxDate: v }) }),
1723
+ /* @__PURE__ */ jsx(ToggleField, { label: "Disable Past Dates", checked: !!config.disablePast, onChange: (v) => updateConfig({ disablePast: v }) }),
1724
+ /* @__PURE__ */ jsx(ToggleField, { label: "Disable Future Dates", checked: !!config.disableFuture, onChange: (v) => updateConfig({ disableFuture: v }) }),
1725
+ /* @__PURE__ */ jsx(TextField, { label: "Format", value: config.format, placeholder: "e.g. MM/DD/YYYY", onChange: (v) => updateConfig({ format: v }) })
1642
1726
  ] });
1643
1727
  case "time":
1644
1728
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Time Settings", children: [
1645
- /* @__PURE__ */ jsx(SelectField, { label: "Format", value: config.format ?? "12h", options: [{ label: "12 Hour", value: "12h" }, { label: "24 Hour", value: "24h" }], onChange: (v) => updateConfig("format", v) }),
1646
- /* @__PURE__ */ jsx(NumberField, { label: "Minute Step", value: config.minuteStep, placeholder: "1", onChange: (v) => updateConfig("minuteStep", v) })
1729
+ /* @__PURE__ */ jsx(SelectField, { label: "Format", value: config.format ?? "12h", options: [{ label: "12 Hour", value: "12h" }, { label: "24 Hour", value: "24h" }], onChange: (v) => updateConfig({ format: v }) }),
1730
+ /* @__PURE__ */ jsx(NumberField, { label: "Minute Step", value: config.minuteStep, placeholder: "1", onChange: (v) => updateConfig({ minuteStep: v }) })
1647
1731
  ] });
1648
1732
  case "date_range":
1649
1733
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Date Range Settings", children: [
1650
- /* @__PURE__ */ jsx(TextField, { label: "Min Date", value: config.minDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig("minDate", v) }),
1651
- /* @__PURE__ */ jsx(TextField, { label: "Max Date", value: config.maxDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig("maxDate", v) }),
1652
- /* @__PURE__ */ jsx(NumberField, { label: "Max Range (days)", value: config.maxRangeDays, onChange: (v) => updateConfig("maxRangeDays", v) })
1734
+ /* @__PURE__ */ jsx(TextField, { label: "Min Date", value: config.minDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig({ minDate: v }) }),
1735
+ /* @__PURE__ */ jsx(TextField, { label: "Max Date", value: config.maxDate, placeholder: "YYYY-MM-DD", onChange: (v) => updateConfig({ maxDate: v }) }),
1736
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Range (days)", value: config.maxRangeDays, onChange: (v) => updateConfig({ maxRangeDays: v }) })
1653
1737
  ] });
1654
- case "appointment":
1738
+ case "appointment": {
1739
+ const appointmentMode = typeof config.slotsUrl === "string" ? "url" : typeof config.embedUrl === "string" ? "embed" : "static";
1740
+ const timezoneFieldOptions = [
1741
+ { label: "None (Static Timezone)", value: "" },
1742
+ ...candidateTimezoneFields,
1743
+ { label: "Custom Field ID...", value: "__custom__" }
1744
+ ];
1745
+ const isCustomTzField = Boolean(config.timezoneField) && !candidateTimezoneFields.some((opt) => opt.value === config.timezoneField);
1655
1746
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Appointment Settings", children: [
1656
- /* @__PURE__ */ jsx(NumberField, { label: "Duration (min)", value: config.duration ?? 30, onChange: (v) => updateConfig("duration", v) }),
1657
- /* @__PURE__ */ jsx(TextField, { label: "Timezone", value: config.timezone, placeholder: "e.g. America/New_York", onChange: (v) => updateConfig("timezone", v) }),
1658
- /* @__PURE__ */ jsx(TextField, { label: "Slots URL", value: config.slotsUrl, placeholder: "API endpoint for available slots", onChange: (v) => updateConfig("slotsUrl", v) })
1747
+ /* @__PURE__ */ jsx(
1748
+ SelectField,
1749
+ {
1750
+ label: "Scheduling Mode",
1751
+ value: appointmentMode,
1752
+ options: [
1753
+ { label: "Manual Static Slots", value: "static" },
1754
+ { label: "Fetch from URL", value: "url" },
1755
+ { label: "Third-Party Embed (Calendly / Cal.com)", value: "embed" }
1756
+ ],
1757
+ onChange: (v) => {
1758
+ if (v === "url") {
1759
+ updateConfig({
1760
+ slotsUrl: "",
1761
+ embedUrl: void 0,
1762
+ embedProvider: void 0,
1763
+ slots: void 0
1764
+ });
1765
+ } else if (v === "embed") {
1766
+ updateConfig({
1767
+ embedUrl: "",
1768
+ embedProvider: config.embedProvider || "cal_com",
1769
+ slotsUrl: void 0,
1770
+ slots: void 0
1771
+ });
1772
+ } else {
1773
+ updateConfig({
1774
+ slots: config.slots ?? [],
1775
+ slotsUrl: void 0,
1776
+ embedUrl: void 0,
1777
+ embedProvider: void 0
1778
+ });
1779
+ }
1780
+ }
1781
+ }
1782
+ ),
1783
+ appointmentMode === "url" && /* @__PURE__ */ jsx(
1784
+ TextField,
1785
+ {
1786
+ label: "Slots URL",
1787
+ value: config.slotsUrl,
1788
+ placeholder: "https://example.com/slots",
1789
+ onChange: (v) => updateConfig({ slotsUrl: v ?? "" })
1790
+ }
1791
+ ),
1792
+ appointmentMode === "embed" && /* @__PURE__ */ jsxs(Fragment, { children: [
1793
+ /* @__PURE__ */ jsx(
1794
+ TextField,
1795
+ {
1796
+ label: "Embed URL",
1797
+ value: config.embedUrl,
1798
+ placeholder: "https://calendly.com/... or https://cal.com/...",
1799
+ onChange: (v) => updateConfig({ embedUrl: v ?? "" })
1800
+ }
1801
+ ),
1802
+ /* @__PURE__ */ jsx(
1803
+ SelectField,
1804
+ {
1805
+ label: "Embed Provider",
1806
+ value: config.embedProvider ?? "cal_com",
1807
+ options: [
1808
+ { label: "Cal.com", value: "cal_com" },
1809
+ { label: "Calendly", value: "calendly" },
1810
+ { label: "Custom", value: "custom" }
1811
+ ],
1812
+ onChange: (v) => updateConfig({ embedProvider: v })
1813
+ }
1814
+ )
1815
+ ] }),
1816
+ /* @__PURE__ */ jsx(NumberField, { label: "Duration (min)", value: config.duration ?? 30, onChange: (v) => updateConfig({ duration: v }) }),
1817
+ /* @__PURE__ */ jsx(TextField, { label: "Timezone", value: config.timezone, placeholder: "e.g. America/New_York", onChange: (v) => updateConfig({ timezone: v }) }),
1818
+ candidateTimezoneFields.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
1819
+ /* @__PURE__ */ jsx(
1820
+ SelectField,
1821
+ {
1822
+ label: "Dynamic Timezone Field",
1823
+ value: isCustomTzField ? "__custom__" : config.timezoneField ?? "",
1824
+ options: timezoneFieldOptions,
1825
+ onChange: (v) => {
1826
+ if (v !== "__custom__") {
1827
+ updateConfig({ timezoneField: v || void 0 });
1828
+ }
1829
+ }
1830
+ }
1831
+ ),
1832
+ isCustomTzField && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
1833
+ TextField,
1834
+ {
1835
+ label: "Custom Timezone Field ID",
1836
+ value: config.timezoneField,
1837
+ placeholder: "e.g. timezone",
1838
+ onChange: (v) => updateConfig({ timezoneField: v || void 0 })
1839
+ }
1840
+ ) })
1841
+ ] }) : /* @__PURE__ */ jsx(
1842
+ TextField,
1843
+ {
1844
+ label: "Timezone Field",
1845
+ value: config.timezoneField,
1846
+ placeholder: "Field ID for dynamic timezone",
1847
+ onChange: (v) => updateConfig({ timezoneField: v || void 0 })
1848
+ }
1849
+ ),
1850
+ /* @__PURE__ */ jsx(TextField, { label: "Date Format", value: config.dateFormat, placeholder: "Locale-aware (default)", onChange: (v) => updateConfig({ dateFormat: v }) }),
1851
+ /* @__PURE__ */ jsx(
1852
+ ToggleField,
1853
+ {
1854
+ label: "Auto-Advance on Booking Complete",
1855
+ checked: config.autoAdvance !== false,
1856
+ onChange: (v) => updateConfig({ autoAdvance: v })
1857
+ }
1858
+ ),
1859
+ appointmentMode === "static" && /* @__PURE__ */ jsx(
1860
+ AppointmentSlotsEditor,
1861
+ {
1862
+ slots: config.slots ?? [],
1863
+ onChange: (v) => updateConfig({ slots: v })
1864
+ }
1865
+ )
1659
1866
  ] });
1867
+ }
1660
1868
  // ── Media ──
1661
1869
  case "file_upload":
1662
1870
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Upload Settings", children: [
1663
- /* @__PURE__ */ jsx(NumberField, { label: "Max Files", value: config.maxFiles ?? 1, onChange: (v) => updateConfig("maxFiles", v) }),
1664
- /* @__PURE__ */ jsx(NumberField, { label: "Max Size (MB)", value: config.maxSizeMb ?? 10, onChange: (v) => updateConfig("maxSizeMb", v) }),
1665
- /* @__PURE__ */ 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) }),
1666
- /* @__PURE__ */ jsx(TextField, { label: "Upload URL", value: config.uploadUrl, placeholder: "Server upload endpoint", onChange: (v) => updateConfig("uploadUrl", v) })
1871
+ /* @__PURE__ */ jsxs("div", { className: "p-2.5 bg-muted/40 border border-border rounded-md text-xs text-muted-foreground flex items-center gap-1.5", children: [
1872
+ /* @__PURE__ */ jsx("span", { children: "\u{1F4E6}" }),
1873
+ /* @__PURE__ */ jsx("span", { children: "Uploads stream to your cloud bucket using Form / Account Storage settings." })
1874
+ ] }),
1875
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Files", value: config.maxFiles ?? 1, onChange: (v) => updateConfig({ maxFiles: v }) }),
1876
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Size (MB)", value: config.maxSizeMb ?? 10, onChange: (v) => updateConfig({ maxSizeMb: v }) }),
1877
+ /* @__PURE__ */ jsx(
1878
+ TextField,
1879
+ {
1880
+ label: "Accepted Types (comma-separated)",
1881
+ value: config.accept?.join(", "),
1882
+ placeholder: "e.g. image/*, .pdf, .docx, .zip",
1883
+ onChange: (v) => updateConfig({
1884
+ accept: v ? v.split(",").map((s) => s.trim()).filter(Boolean) : void 0
1885
+ })
1886
+ }
1887
+ ),
1888
+ /* @__PURE__ */ jsx(
1889
+ TextField,
1890
+ {
1891
+ label: "Upload URL",
1892
+ value: config.uploadUrl,
1893
+ placeholder: "https://api.example.com/s3/presign",
1894
+ onChange: (v) => updateConfig({ uploadUrl: v || void 0 })
1895
+ }
1896
+ ),
1897
+ /* @__PURE__ */ jsx(
1898
+ TextField,
1899
+ {
1900
+ label: "Public Key",
1901
+ value: config.publicKey,
1902
+ placeholder: "pk_test_...",
1903
+ onChange: (v) => updateConfig({ publicKey: v || void 0 })
1904
+ }
1905
+ )
1667
1906
  ] });
1668
1907
  case "signature":
1669
1908
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Signature Settings", children: [
1670
- /* @__PURE__ */ jsx(TextField, { label: "Pen Color", value: config.penColor, placeholder: "#000000", onChange: (v) => updateConfig("penColor", v) }),
1671
- /* @__PURE__ */ jsx(TextField, { label: "Background", value: config.backgroundColor, placeholder: "#ffffff", onChange: (v) => updateConfig("backgroundColor", v) }),
1672
- /* @__PURE__ */ jsx(NumberField, { label: "Width (px)", value: config.width, onChange: (v) => updateConfig("width", v) }),
1673
- /* @__PURE__ */ jsx(NumberField, { label: "Height (px)", value: config.height, onChange: (v) => updateConfig("height", v) })
1909
+ /* @__PURE__ */ jsx(TextField, { label: "Pen Color", value: config.penColor, placeholder: "#000000", onChange: (v) => updateConfig({ penColor: v }) }),
1910
+ /* @__PURE__ */ jsx(TextField, { label: "Background", value: config.backgroundColor, placeholder: "#ffffff", onChange: (v) => updateConfig({ backgroundColor: v }) }),
1911
+ /* @__PURE__ */ jsx(NumberField, { label: "Width (px)", value: config.width, onChange: (v) => updateConfig({ width: v }) }),
1912
+ /* @__PURE__ */ jsx(NumberField, { label: "Height (px)", value: config.height, onChange: (v) => updateConfig({ height: v }) })
1674
1913
  ] });
1675
1914
  case "image_capture":
1676
1915
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Camera Settings", children: [
1677
- /* @__PURE__ */ 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) }),
1678
- /* @__PURE__ */ jsx(NumberField, { label: "Max Size (MB)", value: config.maxSizeMb, onChange: (v) => updateConfig("maxSizeMb", v) }),
1679
- /* @__PURE__ */ jsx(ToggleField, { label: "Allow Gallery", checked: config.allowGallery !== false, onChange: (v) => updateConfig("allowGallery", v) })
1916
+ /* @__PURE__ */ 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 }) }),
1917
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Size (MB)", value: config.maxSizeMb, onChange: (v) => updateConfig({ maxSizeMb: v }) }),
1918
+ /* @__PURE__ */ jsx(ToggleField, { label: "Allow Gallery", checked: config.allowGallery !== false, onChange: (v) => updateConfig({ allowGallery: v }) })
1680
1919
  ] });
1681
1920
  // ── Content & Visual ──
1682
1921
  case "welcome-screen":
1683
1922
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Welcome Screen", children: [
1684
- /* @__PURE__ */ jsx(TextField, { label: "Heading", value: config.heading ?? "Welcome", onChange: (v) => updateConfig("heading", v) }),
1685
- /* @__PURE__ */ jsx(TextareaField, { label: "Description", value: config.description, onChange: (v) => updateConfig("description", v) }),
1686
- /* @__PURE__ */ jsx(TextField, { label: "Button Text", value: config.buttonText ?? "Start", onChange: (v) => updateConfig("buttonText", v) }),
1687
- /* @__PURE__ */ jsx(TextField, { label: "Image URL", value: config.imageUrl, placeholder: "https://...", onChange: (v) => updateConfig("imageUrl", v) }),
1688
- /* @__PURE__ */ 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) })
1923
+ /* @__PURE__ */ jsx(TextField, { label: "Heading", value: config.heading ?? "Welcome", onChange: (v) => updateConfig({ heading: v }) }),
1924
+ /* @__PURE__ */ jsx(TextareaField, { label: "Description", value: config.description, onChange: (v) => updateConfig({ description: v }) }),
1925
+ /* @__PURE__ */ jsx(TextField, { label: "Button Text", value: config.buttonText ?? "Start", onChange: (v) => updateConfig({ buttonText: v }) }),
1926
+ /* @__PURE__ */ jsx(TextField, { label: "Image URL", value: config.imageUrl, placeholder: "https://...", onChange: (v) => updateConfig({ imageUrl: v }) }),
1927
+ /* @__PURE__ */ 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 }) })
1689
1928
  ] });
1690
1929
  case "thank-you-screen":
1691
1930
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Thank You Screen", children: [
1692
- /* @__PURE__ */ jsx(TextField, { label: "Heading", value: config.heading ?? "Thank You!", onChange: (v) => updateConfig("heading", v) }),
1693
- /* @__PURE__ */ jsx(TextareaField, { label: "Description", value: config.description ?? "", onChange: (v) => updateConfig("description", v) }),
1694
- /* @__PURE__ */ jsx(TextField, { label: "Image URL", value: config.imageUrl, placeholder: "https://...", onChange: (v) => updateConfig("imageUrl", v) }),
1695
- /* @__PURE__ */ jsx(TextField, { label: "Redirect URL", value: config.redirectUrl, placeholder: "https://...", onChange: (v) => updateConfig("redirectUrl", v) }),
1696
- /* @__PURE__ */ jsx(NumberField, { label: "Redirect Delay (s)", value: config.redirectDelay, placeholder: "0", onChange: (v) => updateConfig("redirectDelay", v) }),
1697
- /* @__PURE__ */ jsx(ToggleField, { label: "Show Response Summary", checked: !!config.showSummary, onChange: (v) => updateConfig("showSummary", v) })
1931
+ /* @__PURE__ */ jsx(TextField, { label: "Heading", value: config.heading ?? "Thank You!", onChange: (v) => updateConfig({ heading: v }) }),
1932
+ /* @__PURE__ */ jsx(TextareaField, { label: "Description", value: config.description ?? "", onChange: (v) => updateConfig({ description: v }) }),
1933
+ /* @__PURE__ */ jsx(TextField, { label: "Image URL", value: config.imageUrl, placeholder: "https://...", onChange: (v) => updateConfig({ imageUrl: v }) }),
1934
+ /* @__PURE__ */ jsx(TextField, { label: "Redirect URL", value: config.redirectUrl, placeholder: "https://...", onChange: (v) => updateConfig({ redirectUrl: v }) }),
1935
+ /* @__PURE__ */ jsx(NumberField, { label: "Redirect Delay (s)", value: config.redirectDelay, placeholder: "0", onChange: (v) => updateConfig({ redirectDelay: v }) }),
1936
+ /* @__PURE__ */ jsx(ToggleField, { label: "Show Response Summary", checked: !!config.showSummary, onChange: (v) => updateConfig({ showSummary: v }) })
1698
1937
  ] });
1699
1938
  case "rich-text":
1700
1939
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Rich Text", children: [
1701
- /* @__PURE__ */ jsx(TextareaField, { label: "Content", value: config.content ?? "", rows: 6, onChange: (v) => updateConfig("content", v) }),
1702
- /* @__PURE__ */ jsx(SelectField, { label: "Format", value: config.format ?? "html", options: [{ label: "HTML", value: "html" }, { label: "Markdown", value: "markdown" }], onChange: (v) => updateConfig("format", v) })
1940
+ /* @__PURE__ */ jsx(TextareaField, { label: "Content", value: config.content ?? "", rows: 6, onChange: (v) => updateConfig({ content: v }) }),
1941
+ /* @__PURE__ */ jsx(SelectField, { label: "Format", value: config.format ?? "html", options: [{ label: "HTML", value: "html" }, { label: "Markdown", value: "markdown" }], onChange: (v) => updateConfig({ format: v }) })
1703
1942
  ] });
1704
1943
  case "image":
1705
1944
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Image Settings", children: [
1706
- /* @__PURE__ */ jsx(TextField, { label: "Source URL", value: config.src ?? "", placeholder: "https://...", onChange: (v) => updateConfig("src", v) }),
1707
- /* @__PURE__ */ jsx(TextField, { label: "Alt Text", value: config.alt ?? "", onChange: (v) => updateConfig("alt", v) }),
1708
- /* @__PURE__ */ jsx(TextField, { label: "Caption", value: config.caption, onChange: (v) => updateConfig("caption", v) }),
1709
- /* @__PURE__ */ 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) }),
1710
- /* @__PURE__ */ jsx(TextField, { label: "Width", value: config.width, placeholder: "e.g. 100% or 300px", onChange: (v) => updateConfig("width", v) }),
1711
- /* @__PURE__ */ jsx(TextField, { label: "Link URL", value: config.link, placeholder: "Click opens this URL", onChange: (v) => updateConfig("link", v) })
1945
+ /* @__PURE__ */ jsx(TextField, { label: "Source URL", value: config.src ?? "", placeholder: "https://...", onChange: (v) => updateConfig({ src: v }) }),
1946
+ /* @__PURE__ */ jsx(TextField, { label: "Alt Text", value: config.alt ?? "", onChange: (v) => updateConfig({ alt: v }) }),
1947
+ /* @__PURE__ */ jsx(TextField, { label: "Caption", value: config.caption, onChange: (v) => updateConfig({ caption: v }) }),
1948
+ /* @__PURE__ */ 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 }) }),
1949
+ /* @__PURE__ */ jsx(TextField, { label: "Width", value: config.width, placeholder: "e.g. 100% or 300px", onChange: (v) => updateConfig({ width: v }) }),
1950
+ /* @__PURE__ */ jsx(TextField, { label: "Link URL", value: config.link, placeholder: "Click opens this URL", onChange: (v) => updateConfig({ link: v }) })
1712
1951
  ] });
1713
1952
  case "video":
1714
1953
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Video Settings", children: [
1715
- /* @__PURE__ */ jsx(TextField, { label: "Source URL", value: config.src ?? "", placeholder: "YouTube/Vimeo URL", onChange: (v) => updateConfig("src", v) }),
1716
- /* @__PURE__ */ 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) }),
1717
- /* @__PURE__ */ jsx(ToggleField, { label: "Autoplay", checked: !!config.autoplay, onChange: (v) => updateConfig("autoplay", v) }),
1718
- /* @__PURE__ */ jsx(ToggleField, { label: "Muted", checked: !!config.muted, onChange: (v) => updateConfig("muted", v) }),
1719
- /* @__PURE__ */ jsx(TextField, { label: "Width", value: config.width, placeholder: "e.g. 100% or 640px", onChange: (v) => updateConfig("width", v) }),
1720
- /* @__PURE__ */ jsx(TextField, { label: "Height", value: config.height, placeholder: "e.g. 360px", onChange: (v) => updateConfig("height", v) })
1954
+ /* @__PURE__ */ jsx(TextField, { label: "Source URL", value: config.src ?? "", placeholder: "YouTube/Vimeo URL", onChange: (v) => updateConfig({ src: v }) }),
1955
+ /* @__PURE__ */ 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 }) }),
1956
+ /* @__PURE__ */ jsx(ToggleField, { label: "Autoplay", checked: !!config.autoplay, onChange: (v) => updateConfig({ autoplay: v }) }),
1957
+ /* @__PURE__ */ jsx(ToggleField, { label: "Muted", checked: !!config.muted, onChange: (v) => updateConfig({ muted: v }) }),
1958
+ /* @__PURE__ */ jsx(TextField, { label: "Width", value: config.width, placeholder: "e.g. 100% or 640px", onChange: (v) => updateConfig({ width: v }) }),
1959
+ /* @__PURE__ */ jsx(TextField, { label: "Height", value: config.height, placeholder: "e.g. 360px", onChange: (v) => updateConfig({ height: v }) })
1721
1960
  ] });
1722
1961
  case "divider":
1723
1962
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Divider Settings", children: [
1724
- /* @__PURE__ */ 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) }),
1725
- /* @__PURE__ */ jsx(TextField, { label: "Color", value: config.color, placeholder: "#e5e7eb", onChange: (v) => updateConfig("color", v) }),
1726
- /* @__PURE__ */ jsx(NumberField, { label: "Thickness (px)", value: config.thickness, placeholder: "1", onChange: (v) => updateConfig("thickness", v) }),
1727
- /* @__PURE__ */ jsx(NumberField, { label: "Spacing (px)", value: config.spacing, placeholder: "16", onChange: (v) => updateConfig("spacing", v) })
1963
+ /* @__PURE__ */ 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 }) }),
1964
+ /* @__PURE__ */ jsx(TextField, { label: "Color", value: config.color, placeholder: "#e5e7eb", onChange: (v) => updateConfig({ color: v }) }),
1965
+ /* @__PURE__ */ jsx(NumberField, { label: "Thickness (px)", value: config.thickness, placeholder: "1", onChange: (v) => updateConfig({ thickness: v }) }),
1966
+ /* @__PURE__ */ jsx(NumberField, { label: "Spacing (px)", value: config.spacing, placeholder: "16", onChange: (v) => updateConfig({ spacing: v }) })
1728
1967
  ] });
1729
1968
  case "spacer":
1730
- return /* @__PURE__ */ jsx(ConfigSection, { title: "Spacer Settings", children: /* @__PURE__ */ jsx(NumberField, { label: "Height (px)", value: config.height ?? 32, onChange: (v) => updateConfig("height", v) }) });
1969
+ return /* @__PURE__ */ jsx(ConfigSection, { title: "Spacer Settings", children: /* @__PURE__ */ jsx(NumberField, { label: "Height (px)", value: config.height ?? 32, onChange: (v) => updateConfig({ height: v }) }) });
1731
1970
  // ── Structural ──
1732
1971
  case "section_header":
1733
1972
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Header Settings", children: [
1734
- /* @__PURE__ */ 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) }),
1735
- /* @__PURE__ */ jsx(ToggleField, { label: "Show Divider", checked: !!config.showDivider, onChange: (v) => updateConfig("showDivider", v) })
1973
+ /* @__PURE__ */ 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 }) }),
1974
+ /* @__PURE__ */ jsx(ToggleField, { label: "Show Divider", checked: !!config.showDivider, onChange: (v) => updateConfig({ showDivider: v }) })
1736
1975
  ] });
1737
1976
  case "info_block":
1738
1977
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Info Block", children: [
1739
- /* @__PURE__ */ jsx(TextareaField, { label: "Content", value: config.content ?? "", rows: 4, onChange: (v) => updateConfig("content", v) }),
1740
- /* @__PURE__ */ 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) })
1978
+ /* @__PURE__ */ jsx(TextareaField, { label: "Content", value: config.content ?? "", rows: 4, onChange: (v) => updateConfig({ content: v }) }),
1979
+ /* @__PURE__ */ 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 }) })
1741
1980
  ] });
1742
1981
  case "page_break":
1743
- return /* @__PURE__ */ jsx(ConfigSection, { title: "Page Break", children: /* @__PURE__ */ jsx(TextField, { label: "Label", value: config.label, placeholder: "Next page label", onChange: (v) => updateConfig("label", v) }) });
1982
+ return /* @__PURE__ */ jsx(ConfigSection, { title: "Page Break", children: /* @__PURE__ */ jsx(TextField, { label: "Label", value: config.label, placeholder: "Next page label", onChange: (v) => updateConfig({ label: v }) }) });
1744
1983
  case "consent":
1745
1984
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Consent Settings", children: [
1746
- /* @__PURE__ */ jsx(TextareaField, { label: "Consent Text", value: config.text ?? "", rows: 4, onChange: (v) => updateConfig("text", v) }),
1747
- /* @__PURE__ */ jsx(TextField, { label: "Checkbox Label", value: config.checkboxLabel ?? "I agree", onChange: (v) => updateConfig("checkboxLabel", v) }),
1748
- /* @__PURE__ */ jsx(TextareaField, { label: "Expandable Text", value: config.expandableText, rows: 3, placeholder: "Additional text shown on expand", onChange: (v) => updateConfig("expandableText", v) })
1985
+ /* @__PURE__ */ jsx(TextareaField, { label: "Consent Text", value: config.text ?? "", rows: 4, onChange: (v) => updateConfig({ text: v }) }),
1986
+ /* @__PURE__ */ jsx(TextField, { label: "Checkbox Label", value: config.checkboxLabel ?? "I agree", onChange: (v) => updateConfig({ checkboxLabel: v }) }),
1987
+ /* @__PURE__ */ jsx(TextareaField, { label: "Expandable Text", value: config.expandableText, rows: 3, placeholder: "Additional text shown on expand", onChange: (v) => updateConfig({ expandableText: v }) })
1749
1988
  ] });
1750
1989
  // ── Advanced ──
1751
1990
  case "matrix":
1752
1991
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Matrix Settings", children: [
1753
- /* @__PURE__ */ 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) }),
1754
- /* @__PURE__ */ 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) }),
1755
- /* @__PURE__ */ jsx(MatrixItemsEditor, { label: "Rows", items: config.rows ?? [], onChange: (v) => updateConfig("rows", v) }),
1756
- /* @__PURE__ */ jsx(MatrixItemsEditor, { label: "Columns", items: config.columns ?? [], onChange: (v) => updateConfig("columns", v) })
1992
+ /* @__PURE__ */ 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 }) }),
1993
+ /* @__PURE__ */ 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 }) }),
1994
+ /* @__PURE__ */ jsx(MatrixItemsEditor, { label: "Rows", items: config.rows ?? [], onChange: (v) => updateConfig({ rows: v }) }),
1995
+ /* @__PURE__ */ jsx(MatrixItemsEditor, { label: "Columns", items: config.columns ?? [], onChange: (v) => updateConfig({ columns: v }) })
1757
1996
  ] });
1758
1997
  case "repeater":
1759
1998
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Repeater Settings", children: [
1760
- /* @__PURE__ */ jsx(NumberField, { label: "Min Entries", value: config.minEntries, placeholder: "1", onChange: (v) => updateConfig("minEntries", v) }),
1761
- /* @__PURE__ */ jsx(NumberField, { label: "Max Entries", value: config.maxEntries, onChange: (v) => updateConfig("maxEntries", v) }),
1762
- /* @__PURE__ */ jsx(NumberField, { label: "Default Entries", value: config.defaultEntries, placeholder: "1", onChange: (v) => updateConfig("defaultEntries", v) }),
1763
- /* @__PURE__ */ jsx(TextField, { label: "Add Button Label", value: config.addLabel, placeholder: "Add Entry", onChange: (v) => updateConfig("addLabel", v) }),
1764
- /* @__PURE__ */ jsx(TextField, { label: "Remove Button Label", value: config.removeLabel, placeholder: "Remove", onChange: (v) => updateConfig("removeLabel", v) }),
1999
+ /* @__PURE__ */ jsx(NumberField, { label: "Min Entries", value: config.minEntries, placeholder: "1", onChange: (v) => updateConfig({ minEntries: v }) }),
2000
+ /* @__PURE__ */ jsx(NumberField, { label: "Max Entries", value: config.maxEntries, onChange: (v) => updateConfig({ maxEntries: v }) }),
2001
+ /* @__PURE__ */ jsx(NumberField, { label: "Default Entries", value: config.defaultEntries, placeholder: "1", onChange: (v) => updateConfig({ defaultEntries: v }) }),
2002
+ /* @__PURE__ */ jsx(TextField, { label: "Add Button Label", value: config.addLabel, placeholder: "Add Entry", onChange: (v) => updateConfig({ addLabel: v }) }),
2003
+ /* @__PURE__ */ jsx(TextField, { label: "Remove Button Label", value: config.removeLabel, placeholder: "Remove", onChange: (v) => updateConfig({ removeLabel: v }) }),
1765
2004
  /* @__PURE__ */ 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." })
1766
2005
  ] });
1767
2006
  case "address": {
@@ -1775,63 +2014,157 @@ function QuestionConfigEditor({ question, onUpdate }) {
1775
2014
  ];
1776
2015
  const activeFields = config.fields ?? ["street", "city", "state", "zip", "country"];
1777
2016
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Address Settings", children: [
1778
- /* @__PURE__ */ 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) }),
1779
- config.provider !== "none" && /* @__PURE__ */ jsx(TextField, { label: "API Key", value: config.apiKey, placeholder: "Provider API key", onChange: (v) => updateConfig("apiKey", v) }),
1780
- /* @__PURE__ */ jsx(TextField, { label: "Default Country", value: config.defaultCountry, placeholder: "e.g. US", onChange: (v) => updateConfig("defaultCountry", v) }),
2017
+ /* @__PURE__ */ 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 }) }),
2018
+ config.provider !== "none" && /* @__PURE__ */ jsx(TextField, { label: "API Key", value: config.apiKey, placeholder: "Provider API key", onChange: (v) => updateConfig({ apiKey: v }) }),
2019
+ /* @__PURE__ */ jsx(TextField, { label: "Default Country", value: config.defaultCountry, placeholder: "e.g. US", onChange: (v) => updateConfig({ defaultCountry: v }) }),
1781
2020
  /* @__PURE__ */ jsxs("div", { children: [
1782
2021
  /* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground mb-2 block", children: "Fields" }),
1783
- /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: ADDRESS_FIELDS.map((f) => /* @__PURE__ */ jsx(
1784
- ToggleField,
1785
- {
1786
- label: f.label,
1787
- checked: activeFields.includes(f.value),
1788
- onChange: (checked) => {
1789
- const next = checked ? [...activeFields, f.value] : activeFields.filter((v) => v !== f.value);
1790
- updateConfig("fields", next.length > 0 ? next : void 0);
1791
- }
1792
- },
1793
- f.value
1794
- )) })
2022
+ /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: ADDRESS_FIELDS.map((f) => {
2023
+ const isChecked = activeFields.includes(f.value);
2024
+ const isLastChecked = isChecked && activeFields.length === 1;
2025
+ return /* @__PURE__ */ jsx(
2026
+ ToggleField,
2027
+ {
2028
+ label: f.label,
2029
+ checked: isChecked,
2030
+ disabled: isLastChecked,
2031
+ onChange: (checked) => {
2032
+ const next = checked ? [...activeFields, f.value] : activeFields.filter((v) => v !== f.value);
2033
+ updateConfig({ fields: next });
2034
+ }
2035
+ },
2036
+ f.value
2037
+ );
2038
+ }) })
1795
2039
  ] })
1796
2040
  ] });
1797
2041
  }
1798
- case "payment":
2042
+ case "payment": {
2043
+ const amountFieldOptions = [
2044
+ { label: "None (Fixed Amount)", value: "" },
2045
+ ...candidateAmountFields,
2046
+ { label: "Custom Field ID...", value: "__custom__" }
2047
+ ];
2048
+ const isCustomAmountField = Boolean(config.amountField) && !candidateAmountFields.some((opt) => opt.value === config.amountField);
1799
2049
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Payment Settings", children: [
1800
- /* @__PURE__ */ jsx(SelectField, { label: "Provider", value: config.provider ?? "stripe", options: [{ label: "Stripe", value: "stripe" }, { label: "PayPal", value: "paypal" }], onChange: (v) => updateConfig("provider", v) }),
1801
- /* @__PURE__ */ jsx(TextField, { label: "Public Key", value: config.publicKey ?? "", placeholder: "Provider public/client key", onChange: (v) => updateConfig("publicKey", v) }),
1802
- /* @__PURE__ */ jsx(NumberField, { label: "Amount", value: config.amount, onChange: (v) => updateConfig("amount", v) }),
1803
- /* @__PURE__ */ jsx(TextField, { label: "Amount Field", value: config.amountField, placeholder: "Field ID for dynamic amount", onChange: (v) => updateConfig("amountField", v) }),
1804
- /* @__PURE__ */ jsx(TextField, { label: "Currency", value: config.currency, placeholder: "USD", onChange: (v) => updateConfig("currency", v) }),
1805
- /* @__PURE__ */ jsx(TextField, { label: "Description", value: config.description, onChange: (v) => updateConfig("description", v) })
2050
+ /* @__PURE__ */ jsx(
2051
+ SelectField,
2052
+ {
2053
+ label: "Provider",
2054
+ value: config.provider ?? "stripe",
2055
+ options: [
2056
+ { label: "Stripe", value: "stripe" },
2057
+ { label: "PayPal", value: "paypal" },
2058
+ { label: "Razorpay", value: "razorpay" }
2059
+ ],
2060
+ onChange: (v) => updateConfig({ provider: v })
2061
+ }
2062
+ ),
2063
+ /* @__PURE__ */ jsx(
2064
+ TextField,
2065
+ {
2066
+ label: "Public Key",
2067
+ value: config.publicKey,
2068
+ placeholder: "pk_test_...",
2069
+ onChange: (v) => updateConfig({ publicKey: v || void 0 })
2070
+ }
2071
+ ),
2072
+ /* @__PURE__ */ jsx(
2073
+ TextField,
2074
+ {
2075
+ label: "Server URL",
2076
+ value: config.serverUrl,
2077
+ placeholder: "https://api.example.com/create-intent",
2078
+ onChange: (v) => updateConfig({ serverUrl: v || void 0 })
2079
+ }
2080
+ ),
2081
+ candidateAmountFields.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
2082
+ /* @__PURE__ */ jsx(
2083
+ SelectField,
2084
+ {
2085
+ label: "Dynamic Amount Field",
2086
+ value: isCustomAmountField ? "__custom__" : config.amountField ?? "",
2087
+ options: amountFieldOptions,
2088
+ onChange: (v) => {
2089
+ if (v !== "__custom__") {
2090
+ updateConfig({ amountField: v || void 0 });
2091
+ }
2092
+ }
2093
+ }
2094
+ ),
2095
+ isCustomAmountField && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
2096
+ TextField,
2097
+ {
2098
+ label: "Custom Field ID",
2099
+ value: config.amountField,
2100
+ placeholder: "Field ID for dynamic amount",
2101
+ onChange: (v) => updateConfig({ amountField: v || void 0 })
2102
+ }
2103
+ ) })
2104
+ ] }) : /* @__PURE__ */ jsx(
2105
+ TextField,
2106
+ {
2107
+ label: "Amount Field",
2108
+ value: config.amountField,
2109
+ placeholder: "Field ID for dynamic amount (e.g. total_cost)",
2110
+ onChange: (v) => updateConfig({ amountField: v || void 0 })
2111
+ }
2112
+ ),
2113
+ !config.amountField && /* @__PURE__ */ jsx(
2114
+ NumberField,
2115
+ {
2116
+ label: "Fixed Amount",
2117
+ value: config.amount,
2118
+ onChange: (v) => updateConfig({ amount: v })
2119
+ }
2120
+ ),
2121
+ /* @__PURE__ */ jsx(TextField, { label: "Currency", value: config.currency ?? "USD", placeholder: "USD", onChange: (v) => updateConfig({ currency: v || "USD" }) }),
2122
+ /* @__PURE__ */ jsx(TextField, { label: "Description", value: config.description, onChange: (v) => updateConfig({ description: v }) }),
2123
+ /* @__PURE__ */ jsx(
2124
+ TextField,
2125
+ {
2126
+ label: "Client Secret Response Path",
2127
+ value: typeof config.responseMapping?.clientSecretPath === "string" ? config.responseMapping.clientSecretPath : config.responseMapping ?? "",
2128
+ placeholder: "clientSecret",
2129
+ onChange: (v) => updateConfig({
2130
+ responseMapping: v ? { clientSecretPath: v } : void 0
2131
+ })
2132
+ }
2133
+ )
1806
2134
  ] });
2135
+ }
1807
2136
  case "calculated":
1808
2137
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Calculated Field", children: [
1809
- /* @__PURE__ */ jsx(TextField, { label: "Expression", value: config.expression ?? "", placeholder: "e.g. {q1} + {q2}", onChange: (v) => updateConfig("expression", v) }),
1810
- /* @__PURE__ */ 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) }),
1811
- /* @__PURE__ */ jsx(NumberField, { label: "Decimal Places", value: config.decimalPlaces, placeholder: "2", onChange: (v) => updateConfig("decimalPlaces", v) }),
1812
- /* @__PURE__ */ jsx(TextField, { label: "Prefix", value: config.prefix, onChange: (v) => updateConfig("prefix", v) }),
1813
- /* @__PURE__ */ jsx(TextField, { label: "Suffix", value: config.suffix, onChange: (v) => updateConfig("suffix", v) }),
1814
- /* @__PURE__ */ jsx(ToggleField, { label: "Visible to User", checked: config.visible !== false, onChange: (v) => updateConfig("visible", v) })
2138
+ /* @__PURE__ */ jsx(TextField, { label: "Expression", value: config.expression ?? "", placeholder: "e.g. {q1} + {q2}", onChange: (v) => updateConfig({ expression: v }) }),
2139
+ /* @__PURE__ */ 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 }) }),
2140
+ /* @__PURE__ */ jsx(NumberField, { label: "Decimal Places", value: config.decimalPlaces, placeholder: "2", onChange: (v) => updateConfig({ decimalPlaces: v }) }),
2141
+ /* @__PURE__ */ jsx(TextField, { label: "Prefix", value: config.prefix, onChange: (v) => updateConfig({ prefix: v }) }),
2142
+ /* @__PURE__ */ jsx(TextField, { label: "Suffix", value: config.suffix, onChange: (v) => updateConfig({ suffix: v }) }),
2143
+ /* @__PURE__ */ jsx(ToggleField, { label: "Visible to User", checked: config.visible !== false, onChange: (v) => updateConfig({ visible: v }) })
1815
2144
  ] });
1816
2145
  case "hidden":
1817
2146
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Hidden Field", children: [
1818
- /* @__PURE__ */ 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) }),
1819
- config.source === "static" && /* @__PURE__ */ jsx(TextField, { label: "Default Value", value: config.defaultValue, onChange: (v) => updateConfig("defaultValue", v) }),
1820
- config.source === "url_param" && /* @__PURE__ */ jsx(TextField, { label: "Parameter Name", value: config.paramName, placeholder: "e.g. utm_source", onChange: (v) => updateConfig("paramName", v) }),
1821
- config.source === "cookie" && /* @__PURE__ */ jsx(TextField, { label: "Cookie Name", value: config.cookieName, onChange: (v) => updateConfig("cookieName", v) })
2147
+ /* @__PURE__ */ 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 }) }),
2148
+ config.source === "static" && /* @__PURE__ */ jsx(TextField, { label: "Default Value", value: config.defaultValue, onChange: (v) => updateConfig({ defaultValue: v }) }),
2149
+ config.source === "url_param" && /* @__PURE__ */ jsx(TextField, { label: "Parameter Name", value: config.paramName, placeholder: "e.g. utm_source", onChange: (v) => updateConfig({ paramName: v }) }),
2150
+ config.source === "cookie" && /* @__PURE__ */ jsx(TextField, { label: "Cookie Name", value: config.cookieName, onChange: (v) => updateConfig({ cookieName: v }) })
1822
2151
  ] });
1823
2152
  case "scoring":
1824
2153
  return /* @__PURE__ */ jsxs(ConfigSection, { title: "Scoring Settings", children: [
1825
- /* @__PURE__ */ jsx(ToggleField, { label: "Show Score", checked: !!config.showScore, onChange: (v) => updateConfig("showScore", v) }),
1826
- /* @__PURE__ */ jsx(ScoringOptionsEditor, { options: config.options ?? [], onChange: (v) => updateConfig("options", v) }),
1827
- /* @__PURE__ */ jsx(ScoreRangesEditor, { ranges: config.scoreRanges ?? [], onChange: (v) => updateConfig("scoreRanges", v) })
2154
+ /* @__PURE__ */ jsx(ToggleField, { label: "Show Score", checked: !!config.showScore, onChange: (v) => updateConfig({ showScore: v }) }),
2155
+ /* @__PURE__ */ jsx(ScoringOptionsEditor, { options: config.options ?? [], onChange: (v) => updateConfig({ options: v }) }),
2156
+ /* @__PURE__ */ jsx(ScoreRangesEditor, { ranges: config.scoreRanges ?? [], onChange: (v) => updateConfig({ scoreRanges: v }) })
1828
2157
  ] });
1829
2158
  case "ranking":
1830
- return /* @__PURE__ */ jsx(ConfigSection, { title: "Ranking Settings", children: /* @__PURE__ */ jsx(MatrixItemsEditor, { label: "Items", items: config.items ?? [], onChange: (v) => updateConfig("items", v) }) });
2159
+ return /* @__PURE__ */ jsx(ConfigSection, { title: "Ranking Settings", children: /* @__PURE__ */ jsx(MatrixItemsEditor, { label: "Items", items: config.items ?? [], onChange: (v) => updateConfig({ items: v }) }) });
2160
+ case "phone_international":
2161
+ return /* @__PURE__ */ jsxs(ConfigSection, { title: "International Phone Settings", children: [
2162
+ /* @__PURE__ */ jsx(TextField, { label: "Default Country", value: config.defaultCountry ?? "US", placeholder: "e.g. US", onChange: (v) => updateConfig({ defaultCountry: v }) }),
2163
+ /* @__PURE__ */ 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 }) })
2164
+ ] });
1831
2165
  // Types with no additional config
1832
2166
  case "email":
1833
2167
  case "phone":
1834
- case "phone_international":
1835
2168
  case "url":
1836
2169
  return null;
1837
2170
  default:
@@ -1908,11 +2241,12 @@ function NumberField({
1908
2241
  function ToggleField({
1909
2242
  label,
1910
2243
  checked,
1911
- onChange
2244
+ onChange,
2245
+ disabled
1912
2246
  }) {
1913
2247
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1914
2248
  /* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground", children: label }),
1915
- /* @__PURE__ */ jsx(Switch, { checked, onCheckedChange: onChange })
2249
+ /* @__PURE__ */ jsx(Switch, { checked, onCheckedChange: onChange, disabled })
1916
2250
  ] });
1917
2251
  }
1918
2252
  function SelectField({
@@ -1923,7 +2257,10 @@ function SelectField({
1923
2257
  }) {
1924
2258
  return /* @__PURE__ */ jsxs("div", { children: [
1925
2259
  /* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
1926
- /* @__PURE__ */ jsx(NativeSelect, { value, onChange: (e) => onChange(e.target.value), children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value)) })
2260
+ /* @__PURE__ */ jsxs(Select, { value, onValueChange: onChange, children: [
2261
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx("span", { children: options.find((o) => o.value === value)?.label ?? value }) }),
2262
+ /* @__PURE__ */ jsx(SelectContent, { children: options.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
2263
+ ] })
1927
2264
  ] });
1928
2265
  }
1929
2266
  function LikertLabelsEditor({
@@ -2069,6 +2406,59 @@ function ScoringOptionsEditor({
2069
2406
  ] }, index)) })
2070
2407
  ] });
2071
2408
  }
2409
+ function AppointmentSlotsEditor({
2410
+ slots,
2411
+ onChange
2412
+ }) {
2413
+ const handleDateChange = (index, date) => {
2414
+ const updated = slots.map((s, i) => i === index ? { ...s, date } : s);
2415
+ onChange(updated);
2416
+ };
2417
+ const handleTimesChange = (index, timesStr) => {
2418
+ const times = timesStr.split(",").map((t) => t.trim()).filter(Boolean);
2419
+ const updated = slots.map((s, i) => i === index ? { ...s, times } : s);
2420
+ onChange(updated);
2421
+ };
2422
+ const handleAdd = () => {
2423
+ const tomorrow = /* @__PURE__ */ new Date();
2424
+ tomorrow.setDate(tomorrow.getDate() + 1);
2425
+ const dateStr = tomorrow.toISOString().split("T")[0];
2426
+ onChange([...slots, { date: dateStr, times: ["09:00", "10:00", "11:00", "14:00", "15:00"] }]);
2427
+ };
2428
+ const handleRemove = (index) => {
2429
+ onChange(slots.filter((_, i) => i !== index));
2430
+ };
2431
+ return /* @__PURE__ */ jsxs("div", { children: [
2432
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
2433
+ /* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground", children: "Slots" }),
2434
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add Date" })
2435
+ ] }),
2436
+ slots.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: "No slots configured. Add a date to get started." }),
2437
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: slots.map((slot, index) => /* @__PURE__ */ jsxs("div", { className: "p-2 rounded-md border border-border space-y-1.5 group", children: [
2438
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
2439
+ /* @__PURE__ */ jsx(
2440
+ Input,
2441
+ {
2442
+ type: "date",
2443
+ value: slot.date,
2444
+ onChange: (e) => handleDateChange(index, e.target.value),
2445
+ className: "h-7 text-xs flex-1"
2446
+ }
2447
+ ),
2448
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: () => handleRemove(index), className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity", children: "x" })
2449
+ ] }),
2450
+ /* @__PURE__ */ jsx(
2451
+ Input,
2452
+ {
2453
+ value: slot.times.join(", "),
2454
+ onChange: (e) => handleTimesChange(index, e.target.value),
2455
+ className: "h-7 text-xs",
2456
+ placeholder: "09:00, 10:00, 14:00, 15:00"
2457
+ }
2458
+ )
2459
+ ] }, index)) })
2460
+ ] });
2461
+ }
2072
2462
  function ScoreRangesEditor({
2073
2463
  ranges,
2074
2464
  onChange
@@ -2101,6 +2491,26 @@ function ScoreRangesEditor({
2101
2491
  ] }, index)) })
2102
2492
  ] });
2103
2493
  }
2494
+ var NativeSelect = forwardRef(
2495
+ ({ className, wrapperClassName, children, ...props }, ref) => {
2496
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative", wrapperClassName), children: [
2497
+ /* @__PURE__ */ jsx(
2498
+ "select",
2499
+ {
2500
+ ref,
2501
+ className: cn(
2502
+ "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",
2503
+ className
2504
+ ),
2505
+ ...props,
2506
+ children
2507
+ }
2508
+ ),
2509
+ /* @__PURE__ */ jsx(ChevronDown, { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" })
2510
+ ] });
2511
+ }
2512
+ );
2513
+ NativeSelect.displayName = "NativeSelect";
2104
2514
  var RULE_TYPES = [
2105
2515
  { value: "minLength", label: "Min Length", description: "Minimum character count" },
2106
2516
  { value: "maxLength", label: "Max Length", description: "Maximum character count" },
@@ -2808,7 +3218,14 @@ function QuestionProperties({ question, sectionId, builderState, onClose, onOpen
2808
3218
  }
2809
3219
  )
2810
3220
  ] }),
2811
- /* @__PURE__ */ jsx(QuestionConfigEditor, { question, onUpdate: updateQuestion2 }),
3221
+ /* @__PURE__ */ jsx(
3222
+ QuestionConfigEditor,
3223
+ {
3224
+ question,
3225
+ schema: builderState.schema,
3226
+ onUpdate: updateQuestion2
3227
+ }
3228
+ ),
2812
3229
  hasOptions && /* @__PURE__ */ jsx(
2813
3230
  OptionsEditor,
2814
3231
  {
@@ -2985,6 +3402,11 @@ function MonacoWrapper({ value, onChange, errors }) {
2985
3402
  editorRef.current = editor;
2986
3403
  monacoRef.current = monaco;
2987
3404
  };
3405
+ useEffect(() => {
3406
+ return () => {
3407
+ editorRef.current?.dispose();
3408
+ };
3409
+ }, []);
2988
3410
  useEffect(() => {
2989
3411
  const editor = editorRef.current;
2990
3412
  const monaco = monacoRef.current;
@@ -3789,6 +4211,11 @@ function TemplateGallery({ templates, onSelect, onClose }) {
3789
4211
  function useDebouncedValidation(delayMs = 500) {
3790
4212
  const [errors, setErrors] = useState([]);
3791
4213
  const timerRef = useRef(null);
4214
+ useEffect(() => {
4215
+ return () => {
4216
+ if (timerRef.current) clearTimeout(timerRef.current);
4217
+ };
4218
+ }, []);
3792
4219
  const validate = useCallback(
3793
4220
  (text) => {
3794
4221
  if (timerRef.current) {
@@ -4539,4 +4966,56 @@ function FormBuilderInner(props) {
4539
4966
  // src/form-builder/components/FormBuilderGated.tsx
4540
4967
  var FormBuilder = requireLicense(FormBuilderInner, "FormBuilder");
4541
4968
 
4542
- export { DEFAULT_PALETTE, DEFAULT_SCHEMA, FormBuilder, FormBuilderThemeProvider, QUESTION_TYPE_INFO, addOption, addQuestion, addSection, duplicateQuestion, duplicateSection, findQuestion, findSection, generateId, generateOptionId, generateQuestionId, generateSectionId, moveOption, moveQuestion, moveSection, removeOption, removeQuestion, removeSection, updateOption, updateQuestion, updateSection, useBuilderState, useBuilderTheme, useDragDrop, useUndoRedo };
4969
+ // src/form-builder/theme/presets.ts
4970
+ var squaredrDarkPreset = {
4971
+ background: "#0F1A1F",
4972
+ foreground: "#E8EFF1",
4973
+ card: "#16242A",
4974
+ primary: "#63BDB4",
4975
+ primaryForeground: "#0F1A1F",
4976
+ secondary: "#182F31",
4977
+ secondaryForeground: "#E8EFF1",
4978
+ muted: "#182F31",
4979
+ mutedForeground: "#8CA1A9",
4980
+ accent: "#182F31",
4981
+ accentForeground: "#E8EFF1",
4982
+ destructive: "#E08072",
4983
+ destructiveForeground: "#0F1A1F",
4984
+ border: "#2A3B42",
4985
+ input: "#2A3B42",
4986
+ ring: "#63BDB4",
4987
+ radius: "0px",
4988
+ surface: "#16242A",
4989
+ surfaceHover: "#182F31",
4990
+ canvas: "#0F1A1F",
4991
+ panel: "#16242A",
4992
+ borderStrong: "#2F4F4C",
4993
+ textDim: "#5E7680"
4994
+ };
4995
+ var cleanPreset = {
4996
+ background: "#F4F7F8",
4997
+ foreground: "#12222A",
4998
+ card: "#FFFFFF",
4999
+ primary: "#1F6B6E",
5000
+ primaryForeground: "#FFFFFF",
5001
+ secondary: "#EDF3F2",
5002
+ secondaryForeground: "#12222A",
5003
+ muted: "#EDF3F2",
5004
+ mutedForeground: "#6A7B85",
5005
+ accent: "#EDF3F2",
5006
+ accentForeground: "#12222A",
5007
+ destructive: "#B04A3C",
5008
+ destructiveForeground: "#FFFFFF",
5009
+ border: "#DCE4E8",
5010
+ input: "#DCE4E8",
5011
+ ring: "#1F6B6E",
5012
+ radius: "0px",
5013
+ surface: "#FAFCFC",
5014
+ surfaceHover: "#EDF3F2",
5015
+ canvas: "#F4F7F8",
5016
+ panel: "#FFFFFF",
5017
+ borderStrong: "#B9D1CF",
5018
+ textDim: "#6A7B85"
5019
+ };
5020
+
5021
+ export { DEFAULT_PALETTE, DEFAULT_SCHEMA, FormBuilder, FormBuilderThemeProvider, QUESTION_TYPE_INFO, addOption, addQuestion, addSection, cleanPreset, duplicateQuestion, duplicateSection, findQuestion, findSection, generateId, generateOptionId, generateQuestionId, generateSectionId, moveOption, moveQuestion, moveSection, removeOption, removeQuestion, removeSection, squaredrDarkPreset, updateOption, updateQuestion, updateSection, useBuilderState, useBuilderTheme, useDragDrop, useUndoRedo };