@squaredr/fieldcraft-pro 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-IW654Z3M.mjs → chunk-5QXKDYWY.mjs} +92 -5
- package/dist/{chunk-MLP5EDWP.mjs → chunk-7GPX3YIB.mjs} +35 -61
- package/dist/{chunk-GXKQCXIB.mjs → chunk-DWRLPGX5.mjs} +395 -86
- package/dist/{chunk-CYXJGOQH.mjs → chunk-KTIU7VCP.mjs} +68 -9
- package/dist/default-schema-BGxf9Jrg.d.ts +174 -0
- package/dist/default-schema-Bwq8FATF.d.mts +174 -0
- package/dist/form-builder/index.d.mts +14 -170
- package/dist/form-builder/index.d.ts +14 -170
- package/dist/form-builder/index.js +396 -85
- package/dist/form-builder/index.mjs +1 -1
- package/dist/index.d.mts +109 -8
- package/dist/index.d.ts +109 -8
- package/dist/index.js +1697 -217
- package/dist/index.mjs +1106 -11
- package/dist/response-viewer/index.js +36 -61
- package/dist/response-viewer/index.mjs +1 -1
- package/dist/styles.css +2 -2
- package/dist/templates/index.d.mts +1 -1
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.js +92 -5
- package/dist/templates/index.mjs +1 -1
- package/dist/theme-editor/index.d.mts +1 -2
- package/dist/theme-editor/index.d.ts +1 -2
- package/dist/theme-editor/index.js +68 -9
- package/dist/theme-editor/index.mjs +1 -1
- package/dist/{types-bFHsZT1n.d.mts → types-BQOeI3LX.d.mts} +1 -1
- package/dist/{types-bFHsZT1n.d.ts → types-BQOeI3LX.d.ts} +1 -1
- package/package.json +20 -20
- package/theme-editor/styles.css +29 -0
|
@@ -15,21 +15,20 @@ var MAX_HISTORY = 50;
|
|
|
15
15
|
function useUndoRedo(currentSchema, setSchema) {
|
|
16
16
|
const historyRef = useRef([currentSchema]);
|
|
17
17
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
18
|
-
const currentIndexRef = useRef(currentIndex);
|
|
19
|
-
currentIndexRef.current = currentIndex;
|
|
20
18
|
const canUndo = currentIndex > 0;
|
|
21
19
|
const canRedo = currentIndex < historyRef.current.length - 1;
|
|
22
20
|
const push = useCallback(
|
|
23
21
|
(schema) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
historyRef.current.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
+
});
|
|
33
32
|
},
|
|
34
33
|
[]
|
|
35
34
|
);
|
|
@@ -62,13 +61,9 @@ function useUndoRedo(currentSchema, setSchema) {
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
// src/form-builder/utils/id-generator.ts
|
|
65
|
-
var counter = 0;
|
|
66
64
|
function generateId(prefix) {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
counter = (counter + 1) % 1e4;
|
|
70
|
-
const count = counter.toString(36);
|
|
71
|
-
return `${prefix}_${timestamp}${count}${random}`;
|
|
65
|
+
const uuid = crypto.randomUUID().replace(/-/g, "").substring(0, 12);
|
|
66
|
+
return `${prefix}_${uuid}`;
|
|
72
67
|
}
|
|
73
68
|
function generateSectionId() {
|
|
74
69
|
return generateId("section");
|
|
@@ -155,18 +150,39 @@ function duplicateSection(schema, sectionId) {
|
|
|
155
150
|
const sectionIndex = newSchema.sections.findIndex((s) => s.id === sectionId);
|
|
156
151
|
if (sectionIndex === -1) return schema;
|
|
157
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
|
+
}
|
|
158
157
|
const duplicate = {
|
|
159
158
|
...original,
|
|
160
159
|
id: generateSectionId(),
|
|
161
160
|
title: `${original.title} (Copy)`,
|
|
161
|
+
showIf: original.showIf ? remapConditionFieldIds(original.showIf, idMap) : void 0,
|
|
162
162
|
questions: original.questions.map((q) => ({
|
|
163
163
|
...q,
|
|
164
|
-
id:
|
|
164
|
+
id: idMap.get(q.id),
|
|
165
|
+
showIf: q.showIf ? remapConditionFieldIds(q.showIf, idMap) : void 0
|
|
165
166
|
}))
|
|
166
167
|
};
|
|
167
168
|
newSchema.sections.splice(sectionIndex + 1, 0, duplicate);
|
|
168
169
|
return newSchema;
|
|
169
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
|
+
}
|
|
170
186
|
function addQuestion(schema, sectionId, question, index) {
|
|
171
187
|
const newSchema = deepClone(schema);
|
|
172
188
|
const section = newSchema.sections.find((s) => s.id === sectionId);
|
|
@@ -310,7 +326,7 @@ function useBuilderState(initialSchema) {
|
|
|
310
326
|
(sectionId) => {
|
|
311
327
|
applyMutation((s) => removeSection(s, sectionId));
|
|
312
328
|
setSelectedItem((prev) => {
|
|
313
|
-
if (prev?.
|
|
329
|
+
if (prev?.sectionId === sectionId) return null;
|
|
314
330
|
return prev;
|
|
315
331
|
});
|
|
316
332
|
},
|
|
@@ -1440,6 +1456,14 @@ function SectionBlock({ section, builderState }) {
|
|
|
1440
1456
|
}
|
|
1441
1457
|
function FormCanvas({ builderState }) {
|
|
1442
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]);
|
|
1443
1467
|
const handleAddSection = () => {
|
|
1444
1468
|
const newSection = {
|
|
1445
1469
|
id: generateSectionId(),
|
|
@@ -1456,9 +1480,12 @@ function FormCanvas({ builderState }) {
|
|
|
1456
1480
|
"input",
|
|
1457
1481
|
{
|
|
1458
1482
|
type: "text",
|
|
1459
|
-
value:
|
|
1460
|
-
onChange: (e) =>
|
|
1461
|
-
|
|
1483
|
+
value: localTitle,
|
|
1484
|
+
onChange: (e) => setLocalTitle(e.target.value),
|
|
1485
|
+
onBlur: () => {
|
|
1486
|
+
if (localTitle !== schema.title) {
|
|
1487
|
+
builderState.updateSchema({ ...schema, title: localTitle });
|
|
1488
|
+
}
|
|
1462
1489
|
},
|
|
1463
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",
|
|
1464
1491
|
placeholder: "Form Title"
|
|
@@ -1467,9 +1494,12 @@ function FormCanvas({ builderState }) {
|
|
|
1467
1494
|
/* @__PURE__ */ jsx(
|
|
1468
1495
|
"textarea",
|
|
1469
1496
|
{
|
|
1470
|
-
value:
|
|
1471
|
-
onChange: (e) =>
|
|
1472
|
-
|
|
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
|
+
}
|
|
1473
1503
|
},
|
|
1474
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",
|
|
1475
1505
|
placeholder: "Form description (optional)"
|
|
@@ -1572,12 +1602,26 @@ SelectItem.displayName = "SelectItem";
|
|
|
1572
1602
|
function useConfigUpdater(question, onUpdate) {
|
|
1573
1603
|
return (updates) => {
|
|
1574
1604
|
const current = question.config ?? {};
|
|
1575
|
-
|
|
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 });
|
|
1576
1610
|
};
|
|
1577
1611
|
}
|
|
1578
|
-
function QuestionConfigEditor({ question, onUpdate }) {
|
|
1612
|
+
function QuestionConfigEditor({ question, schema, onUpdate }) {
|
|
1579
1613
|
const updateConfig = useConfigUpdater(question, onUpdate);
|
|
1580
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]);
|
|
1581
1625
|
switch (question.type) {
|
|
1582
1626
|
// ── Text ──
|
|
1583
1627
|
case "short_text":
|
|
@@ -1601,32 +1645,32 @@ function QuestionConfigEditor({ question, onUpdate }) {
|
|
|
1601
1645
|
// ── Numeric ──
|
|
1602
1646
|
case "number":
|
|
1603
1647
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "Number Settings", children: [
|
|
1604
|
-
/* @__PURE__ */ jsx(NumberField, { label: "Min", value: config.min, onChange: (v) => updateConfig({ min: v }) }),
|
|
1605
|
-
/* @__PURE__ */ jsx(NumberField, { label: "Max", value: config.max, onChange: (v) => updateConfig({ max: 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 }) }),
|
|
1606
1650
|
/* @__PURE__ */ jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig({ step: v }) }),
|
|
1607
|
-
/* @__PURE__ */ jsx(NumberField, { label: "Decimal Places", value: config.decimalPlaces, placeholder: "0", onChange: (v) => updateConfig({ decimalPlaces: v }) }),
|
|
1608
1651
|
/* @__PURE__ */ jsx(TextField, { label: "Prefix", value: config.prefix, placeholder: "e.g. $", onChange: (v) => updateConfig({ prefix: v }) }),
|
|
1609
1652
|
/* @__PURE__ */ jsx(TextField, { label: "Suffix", value: config.suffix, placeholder: "e.g. kg", onChange: (v) => updateConfig({ suffix: v }) })
|
|
1610
1653
|
] });
|
|
1611
1654
|
case "slider":
|
|
1612
1655
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "Slider Settings", children: [
|
|
1613
|
-
/* @__PURE__ */ jsx(NumberField, { label: "Min", value: config.min, placeholder: "0", onChange: (v) => updateConfig({ min: v }) }),
|
|
1614
|
-
/* @__PURE__ */ jsx(NumberField, { label: "Max", value: config.max, placeholder: "100", onChange: (v) => updateConfig({ max: v }) }),
|
|
1615
|
-
/* @__PURE__ */ jsx(NumberField, { label: "Step", value: config.step, placeholder: "1", onChange: (v) => updateConfig({ step: v }) }),
|
|
1616
|
-
/* @__PURE__ */ jsx(
|
|
1617
|
-
/* @__PURE__ */ jsx(TextField, { label: "
|
|
1618
|
-
/* @__PURE__ */ jsx(
|
|
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 }) })
|
|
1619
1663
|
] });
|
|
1620
1664
|
case "rating":
|
|
1621
1665
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "Rating Settings", children: [
|
|
1622
|
-
/* @__PURE__ */ jsx(NumberField, { label: "Max
|
|
1623
|
-
/* @__PURE__ */ jsx(SelectField, { label: "Icon", value: config.icon ?? "star", options: [{ label: "Star", value: "star" }, { label: "Heart", value: "heart" }, { label: "
|
|
1624
|
-
/* @__PURE__ */ jsx(ToggleField, { label: "
|
|
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 }) })
|
|
1625
1669
|
] });
|
|
1626
1670
|
case "nps":
|
|
1627
1671
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "NPS Settings", children: [
|
|
1628
|
-
/* @__PURE__ */ jsx(TextField, { label: "
|
|
1629
|
-
/* @__PURE__ */ jsx(TextField, { label: "
|
|
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 }) })
|
|
1630
1674
|
] });
|
|
1631
1675
|
case "opinion_scale":
|
|
1632
1676
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "Scale Settings", children: [
|
|
@@ -1692,50 +1736,126 @@ function QuestionConfigEditor({ question, onUpdate }) {
|
|
|
1692
1736
|
/* @__PURE__ */ jsx(NumberField, { label: "Max Range (days)", value: config.maxRangeDays, onChange: (v) => updateConfig({ maxRangeDays: v }) })
|
|
1693
1737
|
] });
|
|
1694
1738
|
case "appointment": {
|
|
1695
|
-
const appointmentMode = typeof config.
|
|
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);
|
|
1696
1746
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "Appointment Settings", children: [
|
|
1697
1747
|
/* @__PURE__ */ jsx(
|
|
1698
1748
|
SelectField,
|
|
1699
1749
|
{
|
|
1700
|
-
label: "Mode",
|
|
1750
|
+
label: "Scheduling Mode",
|
|
1701
1751
|
value: appointmentMode,
|
|
1702
1752
|
options: [
|
|
1703
|
-
{ label: "Static Slots", value: "static" },
|
|
1704
|
-
{ label: "
|
|
1705
|
-
{ label: "Embed (Calendly / Cal.com)", value: "embed" }
|
|
1753
|
+
{ label: "Manual Static Slots", value: "static" },
|
|
1754
|
+
{ label: "Fetch from URL", value: "url" },
|
|
1755
|
+
{ label: "Third-Party Embed (Calendly / Cal.com)", value: "embed" }
|
|
1706
1756
|
],
|
|
1707
1757
|
onChange: (v) => {
|
|
1708
|
-
if (v === "
|
|
1709
|
-
updateConfig({
|
|
1710
|
-
|
|
1711
|
-
|
|
1758
|
+
if (v === "url") {
|
|
1759
|
+
updateConfig({
|
|
1760
|
+
slotsUrl: "",
|
|
1761
|
+
embedUrl: void 0,
|
|
1762
|
+
embedProvider: void 0,
|
|
1763
|
+
slots: void 0
|
|
1764
|
+
});
|
|
1712
1765
|
} else if (v === "embed") {
|
|
1713
|
-
updateConfig({
|
|
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
|
+
});
|
|
1714
1779
|
}
|
|
1715
1780
|
}
|
|
1716
1781
|
}
|
|
1717
1782
|
),
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
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
|
+
),
|
|
1723
1792
|
appointmentMode === "embed" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1724
|
-
/* @__PURE__ */ jsx(
|
|
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
|
+
),
|
|
1725
1802
|
/* @__PURE__ */ jsx(
|
|
1726
1803
|
SelectField,
|
|
1727
1804
|
{
|
|
1728
1805
|
label: "Embed Provider",
|
|
1729
|
-
value: config.embedProvider ?? "
|
|
1806
|
+
value: config.embedProvider ?? "cal_com",
|
|
1730
1807
|
options: [
|
|
1731
|
-
{ label: "Calendly", value: "calendly" },
|
|
1732
1808
|
{ label: "Cal.com", value: "cal_com" },
|
|
1809
|
+
{ label: "Calendly", value: "calendly" },
|
|
1733
1810
|
{ label: "Custom", value: "custom" }
|
|
1734
1811
|
],
|
|
1735
1812
|
onChange: (v) => updateConfig({ embedProvider: v })
|
|
1736
1813
|
}
|
|
1737
1814
|
)
|
|
1738
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
|
+
),
|
|
1739
1859
|
appointmentMode === "static" && /* @__PURE__ */ jsx(
|
|
1740
1860
|
AppointmentSlotsEditor,
|
|
1741
1861
|
{
|
|
@@ -1748,10 +1868,41 @@ function QuestionConfigEditor({ question, onUpdate }) {
|
|
|
1748
1868
|
// ── Media ──
|
|
1749
1869
|
case "file_upload":
|
|
1750
1870
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "Upload Settings", children: [
|
|
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
|
+
] }),
|
|
1751
1875
|
/* @__PURE__ */ jsx(NumberField, { label: "Max Files", value: config.maxFiles ?? 1, onChange: (v) => updateConfig({ maxFiles: v }) }),
|
|
1752
1876
|
/* @__PURE__ */ jsx(NumberField, { label: "Max Size (MB)", value: config.maxSizeMb ?? 10, onChange: (v) => updateConfig({ maxSizeMb: v }) }),
|
|
1753
|
-
/* @__PURE__ */ jsx(
|
|
1754
|
-
|
|
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
|
+
)
|
|
1755
1906
|
] });
|
|
1756
1907
|
case "signature":
|
|
1757
1908
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "Signature Settings", children: [
|
|
@@ -1868,32 +2019,120 @@ function QuestionConfigEditor({ question, onUpdate }) {
|
|
|
1868
2019
|
/* @__PURE__ */ jsx(TextField, { label: "Default Country", value: config.defaultCountry, placeholder: "e.g. US", onChange: (v) => updateConfig({ defaultCountry: v }) }),
|
|
1869
2020
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1870
2021
|
/* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground mb-2 block", children: "Fields" }),
|
|
1871
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: ADDRESS_FIELDS.map((f) =>
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
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
|
+
}) })
|
|
1883
2039
|
] })
|
|
1884
2040
|
] });
|
|
1885
2041
|
}
|
|
1886
|
-
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);
|
|
1887
2049
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "Payment Settings", children: [
|
|
1888
|
-
/* @__PURE__ */ jsx(
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
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
|
+
)
|
|
1896
2134
|
] });
|
|
2135
|
+
}
|
|
1897
2136
|
case "calculated":
|
|
1898
2137
|
return /* @__PURE__ */ jsxs(ConfigSection, { title: "Calculated Field", children: [
|
|
1899
2138
|
/* @__PURE__ */ jsx(TextField, { label: "Expression", value: config.expression ?? "", placeholder: "e.g. {q1} + {q2}", onChange: (v) => updateConfig({ expression: v }) }),
|
|
@@ -2002,11 +2241,12 @@ function NumberField({
|
|
|
2002
2241
|
function ToggleField({
|
|
2003
2242
|
label,
|
|
2004
2243
|
checked,
|
|
2005
|
-
onChange
|
|
2244
|
+
onChange,
|
|
2245
|
+
disabled
|
|
2006
2246
|
}) {
|
|
2007
2247
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2008
2248
|
/* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground", children: label }),
|
|
2009
|
-
/* @__PURE__ */ jsx(Switch, { checked, onCheckedChange: onChange })
|
|
2249
|
+
/* @__PURE__ */ jsx(Switch, { checked, onCheckedChange: onChange, disabled })
|
|
2010
2250
|
] });
|
|
2011
2251
|
}
|
|
2012
2252
|
function SelectField({
|
|
@@ -2978,7 +3218,14 @@ function QuestionProperties({ question, sectionId, builderState, onClose, onOpen
|
|
|
2978
3218
|
}
|
|
2979
3219
|
)
|
|
2980
3220
|
] }),
|
|
2981
|
-
/* @__PURE__ */ jsx(
|
|
3221
|
+
/* @__PURE__ */ jsx(
|
|
3222
|
+
QuestionConfigEditor,
|
|
3223
|
+
{
|
|
3224
|
+
question,
|
|
3225
|
+
schema: builderState.schema,
|
|
3226
|
+
onUpdate: updateQuestion2
|
|
3227
|
+
}
|
|
3228
|
+
),
|
|
2982
3229
|
hasOptions && /* @__PURE__ */ jsx(
|
|
2983
3230
|
OptionsEditor,
|
|
2984
3231
|
{
|
|
@@ -3155,6 +3402,11 @@ function MonacoWrapper({ value, onChange, errors }) {
|
|
|
3155
3402
|
editorRef.current = editor;
|
|
3156
3403
|
monacoRef.current = monaco;
|
|
3157
3404
|
};
|
|
3405
|
+
useEffect(() => {
|
|
3406
|
+
return () => {
|
|
3407
|
+
editorRef.current?.dispose();
|
|
3408
|
+
};
|
|
3409
|
+
}, []);
|
|
3158
3410
|
useEffect(() => {
|
|
3159
3411
|
const editor = editorRef.current;
|
|
3160
3412
|
const monaco = monacoRef.current;
|
|
@@ -3959,6 +4211,11 @@ function TemplateGallery({ templates, onSelect, onClose }) {
|
|
|
3959
4211
|
function useDebouncedValidation(delayMs = 500) {
|
|
3960
4212
|
const [errors, setErrors] = useState([]);
|
|
3961
4213
|
const timerRef = useRef(null);
|
|
4214
|
+
useEffect(() => {
|
|
4215
|
+
return () => {
|
|
4216
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
4217
|
+
};
|
|
4218
|
+
}, []);
|
|
3962
4219
|
const validate = useCallback(
|
|
3963
4220
|
(text) => {
|
|
3964
4221
|
if (timerRef.current) {
|
|
@@ -4709,4 +4966,56 @@ function FormBuilderInner(props) {
|
|
|
4709
4966
|
// src/form-builder/components/FormBuilderGated.tsx
|
|
4710
4967
|
var FormBuilder = requireLicense(FormBuilderInner, "FormBuilder");
|
|
4711
4968
|
|
|
4712
|
-
|
|
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 };
|