@ai-matrx/records-ui 0.106.0 → 0.107.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.cjs +1340 -875
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -2
- package/dist/index.d.ts +31 -2
- package/dist/index.js +1336 -871
- package/dist/index.js.map +1 -1
- package/dist/pickers.cjs +34 -9
- package/dist/pickers.cjs.map +1 -1
- package/dist/pickers.js +34 -9
- package/dist/pickers.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -5637,7 +5637,8 @@ function formatControl(args) {
|
|
|
5637
5637
|
{
|
|
5638
5638
|
id,
|
|
5639
5639
|
type: def.editor,
|
|
5640
|
-
|
|
5640
|
+
inputMode: def.editor,
|
|
5641
|
+
...ownDetails ? { autoComplete: def.editor } : {},
|
|
5641
5642
|
value: text2,
|
|
5642
5643
|
disabled,
|
|
5643
5644
|
onChange: (e) => commit(e.target.value)
|
|
@@ -6128,7 +6129,7 @@ function isPersonsName(field2) {
|
|
|
6128
6129
|
const words2 = `${field2.key} ${field2.label ?? ""}`.toLowerCase();
|
|
6129
6130
|
return /(^|[\s_])(full[\s_]?)?name(\s|$)/.test(words2) && !/(therapist|doctor|physician|company|business|pet|room)/.test(words2);
|
|
6130
6131
|
}
|
|
6131
|
-
function FieldControl({ field: field2, value, onChange, disabled, id, autoOpen, onClose, onCommit, onCancel, addsChoices, initialSearch, asForm }) {
|
|
6132
|
+
function FieldControl({ field: field2, value, onChange, disabled, id, autoOpen, onClose, onCommit, onCancel, addsChoices, initialSearch, asForm, onChosen }) {
|
|
6132
6133
|
const host = useRecordsUi();
|
|
6133
6134
|
const kind = editorKindFor(field2);
|
|
6134
6135
|
const inputId = id ?? `field-${field2.key}`;
|
|
@@ -6192,7 +6193,8 @@ function FieldControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
6192
6193
|
{
|
|
6193
6194
|
id: inputId,
|
|
6194
6195
|
type: kind === "email" ? "email" : kind === "phone" ? "tel" : "url",
|
|
6195
|
-
|
|
6196
|
+
inputMode: kind === "email" ? "email" : kind === "phone" ? "tel" : "url",
|
|
6197
|
+
...ownDetails ? { autoComplete: kind === "email" ? "email" : kind === "phone" ? "tel" : "url" } : {},
|
|
6196
6198
|
value: value === null || value === void 0 ? "" : String(value),
|
|
6197
6199
|
disabled,
|
|
6198
6200
|
onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
|
|
@@ -6202,7 +6204,7 @@ function FieldControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
6202
6204
|
return /* @__PURE__ */ jsx23(CheckboxControl, { field: field2, value, onChange, disabled, id: inputId });
|
|
6203
6205
|
case "select":
|
|
6204
6206
|
case "multi_select":
|
|
6205
|
-
return /* @__PURE__ */ jsx23(OptionControl, { field: field2, value, onChange, disabled, id: inputId, autoOpen, onClose, onCancel, addsChoices, initialSearch, asForm });
|
|
6207
|
+
return /* @__PURE__ */ jsx23(OptionControl, { field: field2, value, onChange, disabled, id: inputId, autoOpen, onClose, onCancel, addsChoices, initialSearch, asForm, onChosen });
|
|
6206
6208
|
case "member":
|
|
6207
6209
|
return /* @__PURE__ */ jsx23(PersonPicker, { field: field2, value, onChange, disabled, id: inputId, autoOpen, onClose, onCancel, initialSearch });
|
|
6208
6210
|
case "entity_reference":
|
|
@@ -6384,7 +6386,7 @@ function inTheStoresOrder(options) {
|
|
|
6384
6386
|
const made = (o) => String(o.created_at ?? "");
|
|
6385
6387
|
return [...options].sort((a, b) => position(a) - position(b) || made(a).localeCompare(made(b)));
|
|
6386
6388
|
}
|
|
6387
|
-
function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen, onClose, onCancel, addsChoices, initialSearch, asForm }) {
|
|
6389
|
+
function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen, onClose, onCancel, addsChoices, initialSearch, asForm, onChosen }) {
|
|
6388
6390
|
const client = useOptionalRecordsClient7();
|
|
6389
6391
|
const [options, setOptions] = useState20(null);
|
|
6390
6392
|
const [refusal4, setRefusal] = useState20(null);
|
|
@@ -6403,7 +6405,7 @@ function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
6403
6405
|
}, [client, field2.id, reread]);
|
|
6404
6406
|
const handed = publicChoicesOf(field2);
|
|
6405
6407
|
if (!client && handed.length > 0) {
|
|
6406
|
-
return /* @__PURE__ */ jsx23(ChoiceAnswer, { field: field2, choices: handed, value, onChange, disabled, id });
|
|
6408
|
+
return /* @__PURE__ */ jsx23(ChoiceAnswer, { field: field2, choices: handed, value, onChange, disabled, id, onChosen });
|
|
6407
6409
|
}
|
|
6408
6410
|
if (!client) {
|
|
6409
6411
|
return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-1", children: [
|
|
@@ -6433,6 +6435,7 @@ function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
6433
6435
|
onChange,
|
|
6434
6436
|
disabled,
|
|
6435
6437
|
id,
|
|
6438
|
+
onChosen,
|
|
6436
6439
|
isChosen: (choice, v) => {
|
|
6437
6440
|
const option = options.find((o) => optionKey2(o) === choice.key);
|
|
6438
6441
|
return option ? isTheChosen(option, v) : false;
|
|
@@ -6703,20 +6706,24 @@ function ChoiceAnswer({
|
|
|
6703
6706
|
onChange,
|
|
6704
6707
|
disabled,
|
|
6705
6708
|
id,
|
|
6706
|
-
isChosen
|
|
6709
|
+
isChosen,
|
|
6710
|
+
onChosen
|
|
6707
6711
|
}) {
|
|
6708
6712
|
const [search, setSearch] = useState20("");
|
|
6709
6713
|
const held = field2.multi ? choiceValuesOf(value) : value === null || value === void 0 || value === "" ? [] : [value];
|
|
6710
6714
|
const on = (choice) => held.some((v) => isChosen ? isChosen(choice, v) : String(v) === choice.key || String(v) === choice.label);
|
|
6711
6715
|
const press = (choice) => {
|
|
6712
6716
|
if (!field2.multi) {
|
|
6713
|
-
|
|
6717
|
+
const picking = !on(choice);
|
|
6718
|
+
onChange(picking ? choice.key : null);
|
|
6719
|
+
if (picking) onChosen?.();
|
|
6714
6720
|
return;
|
|
6715
6721
|
}
|
|
6716
6722
|
const keys = choices.filter((c) => c.key === choice.key ? !on(c) : on(c)).map((c) => c.key);
|
|
6717
6723
|
onChange(keys.length === 0 ? null : keys);
|
|
6718
6724
|
};
|
|
6719
6725
|
const lettered = choices.length <= CHOICE_LETTERS.length;
|
|
6726
|
+
const tabStop = choices.find((c) => on(c))?.key ?? choices[0]?.key;
|
|
6720
6727
|
const option = (choice) => {
|
|
6721
6728
|
const chosen = on(choice);
|
|
6722
6729
|
const letter = lettered ? CHOICE_LETTERS[choices.indexOf(choice)] : null;
|
|
@@ -6728,6 +6735,7 @@ function ChoiceAnswer({
|
|
|
6728
6735
|
"aria-checked": chosen,
|
|
6729
6736
|
"aria-label": choice.label,
|
|
6730
6737
|
disabled,
|
|
6738
|
+
tabIndex: field2.multi || choice.key === tabStop ? 0 : -1,
|
|
6731
6739
|
onClick: () => press(choice),
|
|
6732
6740
|
"data-records-choice-answer": choice.key,
|
|
6733
6741
|
className: cn16(
|
|
@@ -6754,8 +6762,25 @@ function ChoiceAnswer({
|
|
|
6754
6762
|
);
|
|
6755
6763
|
};
|
|
6756
6764
|
const onKeyDown = (event) => {
|
|
6757
|
-
if (
|
|
6765
|
+
if (disabled || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
6758
6766
|
if (event.target.tagName === "INPUT") return;
|
|
6767
|
+
const arrow = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[event.key];
|
|
6768
|
+
if (arrow) {
|
|
6769
|
+
const buttons = Array.from(
|
|
6770
|
+
event.currentTarget.querySelectorAll("[data-records-choice-answer]")
|
|
6771
|
+
);
|
|
6772
|
+
const here = buttons.indexOf(event.target);
|
|
6773
|
+
if (buttons.length === 0) return;
|
|
6774
|
+
event.preventDefault();
|
|
6775
|
+
const next = buttons[(Math.max(here, 0) + arrow + buttons.length) % buttons.length];
|
|
6776
|
+
next.focus();
|
|
6777
|
+
if (!field2.multi) {
|
|
6778
|
+
const choice = choices.find((c) => c.key === next.dataset["recordsChoiceAnswer"]);
|
|
6779
|
+
if (choice && !on(choice)) onChange(choice.key);
|
|
6780
|
+
}
|
|
6781
|
+
return;
|
|
6782
|
+
}
|
|
6783
|
+
if (!lettered) return;
|
|
6759
6784
|
const at = CHOICE_LETTERS.indexOf(event.key.toUpperCase());
|
|
6760
6785
|
if (event.key.length !== 1 || at < 0 || at >= choices.length) return;
|
|
6761
6786
|
event.preventDefault();
|
|
@@ -9833,7 +9858,7 @@ function ChoiceValue({ field: field2, value, onChange }) {
|
|
|
9833
9858
|
{
|
|
9834
9859
|
triggerSize: "compact",
|
|
9835
9860
|
triggerLabel: `Choice for ${field2.label || field2.key}`,
|
|
9836
|
-
triggerText: value || "Choose",
|
|
9861
|
+
triggerText: choices.find((c) => c.key === value || c.label === value)?.label ?? (value || "Choose"),
|
|
9837
9862
|
open,
|
|
9838
9863
|
onOpenChange: (next) => {
|
|
9839
9864
|
setOpen(next);
|
|
@@ -9845,7 +9870,7 @@ function ChoiceValue({ field: field2, value, onChange }) {
|
|
|
9845
9870
|
id: c.key,
|
|
9846
9871
|
label: c.label,
|
|
9847
9872
|
chip: choiceChipLookOf(field2, c.key, c.label).className,
|
|
9848
|
-
picked: c.label === value,
|
|
9873
|
+
picked: c.label === value || c.key === value,
|
|
9849
9874
|
attrs: { "data-pick-value": c.label }
|
|
9850
9875
|
})),
|
|
9851
9876
|
onPick: (key) => {
|
|
@@ -9954,6 +9979,15 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
9954
9979
|
attrs: { "data-condition": "field" }
|
|
9955
9980
|
}
|
|
9956
9981
|
),
|
|
9982
|
+
onField && !fields.some((f) => f.id === onField) ? /* @__PURE__ */ jsx37(
|
|
9983
|
+
"span",
|
|
9984
|
+
{
|
|
9985
|
+
className: "rounded bg-destructive px-1.5 py-0.5 text-[11px] font-medium text-destructive-foreground",
|
|
9986
|
+
title: "This condition names a question that is no longer on the form.",
|
|
9987
|
+
"data-records-condition-missing": "",
|
|
9988
|
+
children: "Missing question"
|
|
9989
|
+
}
|
|
9990
|
+
) : null,
|
|
9957
9991
|
onField ? /* @__PURE__ */ jsxs32(Fragment11, { children: [
|
|
9958
9992
|
/* @__PURE__ */ jsx37(
|
|
9959
9993
|
PickOne,
|
|
@@ -37722,9 +37756,41 @@ function formPresentation(spec) {
|
|
|
37722
37756
|
welcome: welcomeDocument(spec.welcome ?? null),
|
|
37723
37757
|
endings: (spec.endings ?? []).map(endingDocument),
|
|
37724
37758
|
hidden_fields: spec.hiddenFields ?? [],
|
|
37725
|
-
theme: themeDocument(spec.theme ?? null)
|
|
37759
|
+
theme: themeDocument(spec.theme ?? null),
|
|
37760
|
+
...typeof spec.showOwnerHeader === "boolean" ? { show_owner_header: spec.showOwnerHeader } : {},
|
|
37761
|
+
...typeof spec.autoAdvance === "boolean" ? { auto_advance: spec.autoAdvance } : {},
|
|
37762
|
+
...spec.notify && (spec.notify.webhookUrl || spec.notify.webhookAutomationId) ? { notify: strip({ webhook_url: text(spec.notify.webhookUrl), webhook_automation_id: text(spec.notify.webhookAutomationId) }) } : {}
|
|
37726
37763
|
};
|
|
37727
37764
|
}
|
|
37765
|
+
function notifyFromDocument(raw) {
|
|
37766
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
37767
|
+
const d = raw;
|
|
37768
|
+
const n = { webhookUrl: text(d["webhook_url"]), webhookAutomationId: text(d["webhook_automation_id"]) };
|
|
37769
|
+
return n.webhookUrl || n.webhookAutomationId ? n : null;
|
|
37770
|
+
}
|
|
37771
|
+
var bool = (v) => typeof v === "boolean" ? v : null;
|
|
37772
|
+
function answerRefusal(kind, required, value) {
|
|
37773
|
+
const empty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
|
|
37774
|
+
if (empty) return required ? "This field is required" : null;
|
|
37775
|
+
if (typeof value !== "string") return null;
|
|
37776
|
+
const v = value.trim();
|
|
37777
|
+
if (kind === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v)) return "Enter a valid email";
|
|
37778
|
+
if (kind === "phone" && (v.replace(/\D/g, "").length < 7 || /[^\d\s()+.\-x]/i.test(v))) return "Enter a valid phone number";
|
|
37779
|
+
if (kind === "url" && !/^(https?:\/\/)?[^\s.\/]+(\.[^\s.\/]+)+(\/\S*)?$/i.test(v)) return "Enter a valid link";
|
|
37780
|
+
return null;
|
|
37781
|
+
}
|
|
37782
|
+
function pipeAnswers(text2, answers, questions, labelOf) {
|
|
37783
|
+
if (!text2.includes("{{")) return text2;
|
|
37784
|
+
return text2.replace(/\{\{\s*([^{}]+?)\s*\}\}/g, (_all, name) => {
|
|
37785
|
+
const wanted = name.trim().toLowerCase();
|
|
37786
|
+
const q = questions.find((x) => x.key.toLowerCase() === wanted) ?? questions.find((x) => (x.ask ?? "").trim().toLowerCase() === wanted);
|
|
37787
|
+
if (!q) return "";
|
|
37788
|
+
const value = answers[q.key];
|
|
37789
|
+
if (value === void 0 || value === null) return "";
|
|
37790
|
+
const one = (v) => labelOf?.(q.key, v) ?? String(v);
|
|
37791
|
+
return Array.isArray(value) ? value.map(one).join(", ") : one(value);
|
|
37792
|
+
});
|
|
37793
|
+
}
|
|
37728
37794
|
var text = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : null;
|
|
37729
37795
|
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
37730
37796
|
var strip = (doc) => Object.fromEntries(Object.entries(doc).filter(([, v]) => v !== null && v !== void 0));
|
|
@@ -37899,6 +37965,9 @@ function formFromSummary(summary) {
|
|
|
37899
37965
|
thankYou: thankYouFromDocument(presentation["thank_you"]),
|
|
37900
37966
|
look: formLookFromDocument(presentation["look"]),
|
|
37901
37967
|
submitLabel: presentation["submit_label"] ?? null,
|
|
37968
|
+
showOwnerHeader: bool(presentation["show_owner_header"]),
|
|
37969
|
+
autoAdvance: bool(presentation["auto_advance"]),
|
|
37970
|
+
notify: notifyFromDocument(presentation["notify"]),
|
|
37902
37971
|
// A form is public when the store says it is OPEN. A screen never decides
|
|
37903
37972
|
// this from a flag of its own: the link answers or it does not.
|
|
37904
37973
|
isPublic: summary.state === "open",
|
|
@@ -38703,6 +38772,50 @@ function compactTick(value) {
|
|
|
38703
38772
|
if (Math.abs(value) < 1e3 && !Number.isInteger(value)) return String(Math.round(value * 10) / 10);
|
|
38704
38773
|
return formatCount5(value, { style: "compact" });
|
|
38705
38774
|
}
|
|
38775
|
+
function wrapTickWords(label, perLine, maxLines = 3) {
|
|
38776
|
+
const words2 = label.split(/\s+/).filter(Boolean).map((w) => w.length > perLine ? `${w.slice(0, perLine - 1)}\u2026` : w);
|
|
38777
|
+
const lines = [];
|
|
38778
|
+
let line = "";
|
|
38779
|
+
for (const word of words2) {
|
|
38780
|
+
const next = line ? `${line} ${word}` : word;
|
|
38781
|
+
if (next.length <= perLine) line = next;
|
|
38782
|
+
else {
|
|
38783
|
+
lines.push(line);
|
|
38784
|
+
line = word;
|
|
38785
|
+
}
|
|
38786
|
+
}
|
|
38787
|
+
if (line) lines.push(line);
|
|
38788
|
+
if (lines.length <= maxLines) return lines;
|
|
38789
|
+
const kept = lines.slice(0, maxLines);
|
|
38790
|
+
const last = kept[maxLines - 1].replace(/…$/, "");
|
|
38791
|
+
kept[maxLines - 1] = `${last.length >= perLine ? last.slice(0, perLine - 1) : last}\u2026`;
|
|
38792
|
+
return kept;
|
|
38793
|
+
}
|
|
38794
|
+
function WrappedTick(props) {
|
|
38795
|
+
const { x = 0, y = 0, payload, across, perLine } = props;
|
|
38796
|
+
const lines = wrapTickWords(String(payload?.value ?? ""), perLine);
|
|
38797
|
+
const lineHeight = 11;
|
|
38798
|
+
return /* @__PURE__ */ jsx120(
|
|
38799
|
+
"text",
|
|
38800
|
+
{
|
|
38801
|
+
x,
|
|
38802
|
+
y,
|
|
38803
|
+
fontSize: 10,
|
|
38804
|
+
fill: "currentColor",
|
|
38805
|
+
textAnchor: across ? "end" : "middle",
|
|
38806
|
+
"data-records-chart-tick": "",
|
|
38807
|
+
children: lines.map((line, i) => /* @__PURE__ */ jsx120(
|
|
38808
|
+
"tspan",
|
|
38809
|
+
{
|
|
38810
|
+
x: across ? x - 4 : x,
|
|
38811
|
+
dy: i === 0 ? across ? -((lines.length - 1) * lineHeight) / 2 + 3 : 10 : lineHeight,
|
|
38812
|
+
children: line
|
|
38813
|
+
},
|
|
38814
|
+
i
|
|
38815
|
+
))
|
|
38816
|
+
}
|
|
38817
|
+
);
|
|
38818
|
+
}
|
|
38706
38819
|
function Drawing({
|
|
38707
38820
|
kind,
|
|
38708
38821
|
points,
|
|
@@ -38808,9 +38921,9 @@ function Drawing({
|
|
|
38808
38921
|
/* @__PURE__ */ jsx120(CartesianGrid, { vertical: horizontal, horizontal: !horizontal }),
|
|
38809
38922
|
horizontal ? /* @__PURE__ */ jsxs110(Fragment48, { children: [
|
|
38810
38923
|
/* @__PURE__ */ jsx120(XAxis, { type: "number", ...axis }),
|
|
38811
|
-
/* @__PURE__ */ jsx120(YAxis, { type: "category", dataKey: "label", width:
|
|
38924
|
+
/* @__PURE__ */ jsx120(YAxis, { type: "category", dataKey: "label", width: 96, ...axis, tick: /* @__PURE__ */ jsx120(WrappedTick, { across: true, perLine: 15 }) })
|
|
38812
38925
|
] }) : /* @__PURE__ */ jsxs110(Fragment48, { children: [
|
|
38813
|
-
/* @__PURE__ */ jsx120(XAxis, { dataKey: "label", ...axis }),
|
|
38926
|
+
/* @__PURE__ */ jsx120(XAxis, { dataKey: "label", ...axis, interval: 0, height: 40, tick: /* @__PURE__ */ jsx120(WrappedTick, { perLine: Math.max(6, Math.floor(width / Math.max(points.length, 1) / 6)) }) }),
|
|
38814
38927
|
/* @__PURE__ */ jsx120(YAxis, { width: 44, ...axis })
|
|
38815
38928
|
] }),
|
|
38816
38929
|
tooltip,
|
|
@@ -40459,7 +40572,7 @@ function FormLookLogo({ view, size = "md" }) {
|
|
|
40459
40572
|
}
|
|
40460
40573
|
);
|
|
40461
40574
|
}
|
|
40462
|
-
function FormLookFrame({ look, title, children, contained = false, className }) {
|
|
40575
|
+
function FormLookFrame({ look, title, children, contained = false, showOwnerHeader = true, className }) {
|
|
40463
40576
|
const view = formLookView(look, title);
|
|
40464
40577
|
return /* @__PURE__ */ jsxs117(
|
|
40465
40578
|
"div",
|
|
@@ -40476,10 +40589,10 @@ function FormLookFrame({ look, title, children, contained = false, className })
|
|
|
40476
40589
|
}
|
|
40477
40590
|
) : null,
|
|
40478
40591
|
view.coverUrl ? /* @__PURE__ */ jsx127("div", { "data-records-form-cover": "", className: "aspect-[3/1] w-full overflow-hidden rounded-xl border border-border bg-muted", children: /* @__PURE__ */ jsx127("img", { src: view.coverUrl, alt: "", className: "h-full w-full object-cover" }) }) : null,
|
|
40479
|
-
/* @__PURE__ */ jsxs117("div", { className: "flex items-center gap-3", children: [
|
|
40592
|
+
showOwnerHeader ? /* @__PURE__ */ jsxs117("div", { className: "flex items-center gap-3", "data-records-form-owner": "", children: [
|
|
40480
40593
|
/* @__PURE__ */ jsx127(FormLookLogo, { view }),
|
|
40481
40594
|
/* @__PURE__ */ jsx127("p", { className: "min-w-0 truncate text-base font-semibold tracking-tight text-foreground", children: view.name })
|
|
40482
|
-
] }),
|
|
40595
|
+
] }) : null,
|
|
40483
40596
|
/* @__PURE__ */ jsx127("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
|
|
40484
40597
|
/* @__PURE__ */ jsx127("div", { className: "w-full", children }),
|
|
40485
40598
|
view.footerLinks.length > 0 ? /* @__PURE__ */ jsx127("footer", { className: "mt-10 flex flex-wrap items-center justify-center gap-x-5 gap-y-2 pb-6 text-sm text-muted-foreground", children: view.footerLinks.map((link) => /* @__PURE__ */ jsx127(
|
|
@@ -41302,17 +41415,20 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
|
41302
41415
|
// src/FormBuilder.tsx
|
|
41303
41416
|
import { copyText as copyText7 } from "@ai-matrx/kit/clipboard";
|
|
41304
41417
|
import { ErrorBox as ErrorBox7 } from "@ai-matrx/design-system";
|
|
41305
|
-
import { useCallback as
|
|
41306
|
-
import {
|
|
41418
|
+
import { useCallback as useCallback54, useEffect as useEffect96, useMemo as useMemo68, useRef as useRef56, useState as useState110 } from "react";
|
|
41419
|
+
import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
|
|
41420
|
+
import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
41421
|
+
import { CSS as CSS2 } from "@dnd-kit/utilities";
|
|
41422
|
+
import { useFields as useFields25, useRecordsClient as useRecordsClient79, useTable as useTable26 } from "@ai-matrx/records/react";
|
|
41307
41423
|
import { publicFormPath } from "@ai-matrx/records";
|
|
41308
41424
|
import {
|
|
41309
41425
|
BasicInput as BasicInput28,
|
|
41310
41426
|
BasicTextarea as BasicTextarea11,
|
|
41311
|
-
Button as
|
|
41427
|
+
Button as Button86,
|
|
41312
41428
|
Checkbox as Checkbox15,
|
|
41313
|
-
Label as
|
|
41314
|
-
Skeleton as
|
|
41315
|
-
cn as
|
|
41429
|
+
Label as Label23,
|
|
41430
|
+
Skeleton as Skeleton29,
|
|
41431
|
+
cn as cn78
|
|
41316
41432
|
} from "@ai-matrx/design-system";
|
|
41317
41433
|
|
|
41318
41434
|
// src/FormFlowEditor.tsx
|
|
@@ -41570,7 +41686,7 @@ function EmbedSnippets({ url, label }) {
|
|
|
41570
41686
|
// src/FormRunner.tsx
|
|
41571
41687
|
import { useCallback as useCallback53, useEffect as useEffect95, useMemo as useMemo67, useRef as useRef55, useState as useState109 } from "react";
|
|
41572
41688
|
import { useFields as useFields24, useOptionalRecordsClient as useOptionalRecordsClient14 } from "@ai-matrx/records/react";
|
|
41573
|
-
import { Button as Button85, Progress, Skeleton as Skeleton28, cn as cn77, formThemeClasses } from "@ai-matrx/design-system";
|
|
41689
|
+
import { Button as Button85, Label as Label22, Progress, Skeleton as Skeleton28, cn as cn77, formThemeClasses } from "@ai-matrx/design-system";
|
|
41574
41690
|
import { humanizeIdentifier as humanizeIdentifier18 } from "@ai-matrx/kit/text-case";
|
|
41575
41691
|
import { Fragment as Fragment53, jsx as jsx132, jsxs as jsxs122 } from "react/jsx-runtime";
|
|
41576
41692
|
function authored(host, text2, where) {
|
|
@@ -41669,7 +41785,8 @@ function FormStage({
|
|
|
41669
41785
|
className,
|
|
41670
41786
|
connected = false,
|
|
41671
41787
|
onStart,
|
|
41672
|
-
onQuestionShown
|
|
41788
|
+
onQuestionShown,
|
|
41789
|
+
autoAdvance
|
|
41673
41790
|
}) {
|
|
41674
41791
|
const host = useRecordsUi();
|
|
41675
41792
|
const [answers, setAnswers] = useState109(() => ({ ...initialAnswers ?? {} }));
|
|
@@ -41682,6 +41799,11 @@ function FormStage({
|
|
|
41682
41799
|
const [ending, setEnding] = useState109(null);
|
|
41683
41800
|
const [started, setStarted] = useState109(() => !form.welcome || resumed);
|
|
41684
41801
|
const routes = formRoutes(form);
|
|
41802
|
+
const [routeKnown, setRouteKnown] = useState109(() => !routes || !whichAsked);
|
|
41803
|
+
const [stopped, setStopped] = useState109(null);
|
|
41804
|
+
const answersRef = useRef55(answers);
|
|
41805
|
+
answersRef.current = answers;
|
|
41806
|
+
const choose = autoAdvance ?? form.autoAdvance ?? true;
|
|
41685
41807
|
const decoy = useRef55("");
|
|
41686
41808
|
const stage = useRef55(null);
|
|
41687
41809
|
const questions = useMemo67(() => {
|
|
@@ -41717,6 +41839,7 @@ function FormStage({
|
|
|
41717
41839
|
};
|
|
41718
41840
|
}
|
|
41719
41841
|
if (cancelled) return;
|
|
41842
|
+
setRouteKnown(true);
|
|
41720
41843
|
if (!told2.ok) {
|
|
41721
41844
|
setHidden({});
|
|
41722
41845
|
setError(told2.error ?? refusal("not_supported", told2.message, "Answer every question shown; none were skipped."));
|
|
@@ -41738,7 +41861,7 @@ function FormStage({
|
|
|
41738
41861
|
) : null
|
|
41739
41862
|
);
|
|
41740
41863
|
})();
|
|
41741
|
-
}, 250);
|
|
41864
|
+
}, routeKnown ? 250 : 0);
|
|
41742
41865
|
return () => {
|
|
41743
41866
|
cancelled = true;
|
|
41744
41867
|
clearTimeout(timer);
|
|
@@ -41773,11 +41896,24 @@ function FormStage({
|
|
|
41773
41896
|
const v = answers[q.key];
|
|
41774
41897
|
return v !== void 0 && v !== null && v !== "";
|
|
41775
41898
|
}).length;
|
|
41776
|
-
const
|
|
41777
|
-
|
|
41778
|
-
|
|
41779
|
-
|
|
41899
|
+
const refusalFor = (q, values = answersRef.current) => answerRefusal(q.field ? String(editorKindFor(q.field)) : "text", q.required, values[q.key]);
|
|
41900
|
+
const refuse = (q, say2) => {
|
|
41901
|
+
setStopped({ key: q.key, say: say2 });
|
|
41902
|
+
const i = live.findIndex((x) => x.key === q.key);
|
|
41903
|
+
if (oneAtATime && i >= 0 && i !== index) setAt(i);
|
|
41904
|
+
window.setTimeout(() => {
|
|
41905
|
+
const box = stage.current?.querySelector(`[data-records-form-question="${CSS.escape(q.key)}"]`);
|
|
41906
|
+
box?.querySelector("[role='radio'][aria-checked='true'], input:not([tabindex='-1']), textarea, select, [role='combobox'], [role='radio'], [role='checkbox']")?.focus();
|
|
41907
|
+
}, 0);
|
|
41908
|
+
};
|
|
41780
41909
|
const submit = useCallback53(async () => {
|
|
41910
|
+
for (const q of live) {
|
|
41911
|
+
const say2 = refusalFor(q);
|
|
41912
|
+
if (say2) {
|
|
41913
|
+
refuse(q, say2);
|
|
41914
|
+
return;
|
|
41915
|
+
}
|
|
41916
|
+
}
|
|
41781
41917
|
if (preview) {
|
|
41782
41918
|
setDone("preview");
|
|
41783
41919
|
return;
|
|
@@ -41809,20 +41945,28 @@ function FormStage({
|
|
|
41809
41945
|
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
41810
41946
|
async function advance() {
|
|
41811
41947
|
if (!oneAtATime) return;
|
|
41948
|
+
if (current) {
|
|
41949
|
+
const say2 = refusalFor(current);
|
|
41950
|
+
if (say2) {
|
|
41951
|
+
refuse(current, say2);
|
|
41952
|
+
return;
|
|
41953
|
+
}
|
|
41954
|
+
}
|
|
41955
|
+
setStopped(null);
|
|
41812
41956
|
if (!routes || !whichAsked || !current) {
|
|
41813
41957
|
if (index < live.length - 1) setAt(index + 1);
|
|
41814
|
-
else
|
|
41958
|
+
else void submit();
|
|
41815
41959
|
return;
|
|
41816
41960
|
}
|
|
41817
41961
|
let told2;
|
|
41818
41962
|
try {
|
|
41819
|
-
told2 = await whichAsked(
|
|
41963
|
+
told2 = await whichAsked(answersRef.current);
|
|
41820
41964
|
} catch {
|
|
41821
41965
|
told2 = { ok: false, message: "This form could not work out which question comes next, so the next one is shown." };
|
|
41822
41966
|
}
|
|
41823
41967
|
if (!told2.ok) {
|
|
41824
41968
|
if (index < live.length - 1) setAt(index + 1);
|
|
41825
|
-
else
|
|
41969
|
+
else void submit();
|
|
41826
41970
|
return;
|
|
41827
41971
|
}
|
|
41828
41972
|
const next = {};
|
|
@@ -41835,13 +41979,18 @@ function FormStage({
|
|
|
41835
41979
|
setAt(here + 1);
|
|
41836
41980
|
return;
|
|
41837
41981
|
}
|
|
41838
|
-
const stillMissing = onRoute.
|
|
41839
|
-
|
|
41840
|
-
|
|
41841
|
-
});
|
|
41842
|
-
if (stillMissing.length === 0) void submit();
|
|
41982
|
+
const stillMissing = onRoute.find((q) => refusalFor(q) !== null);
|
|
41983
|
+
if (stillMissing) refuse(stillMissing, refusalFor(stillMissing));
|
|
41984
|
+
else void submit();
|
|
41843
41985
|
}
|
|
41986
|
+
const advanceRef = useRef55(advance);
|
|
41987
|
+
advanceRef.current = advance;
|
|
41988
|
+
const chosen = useCallback53(() => {
|
|
41989
|
+
if (!oneAtATime || !choose) return;
|
|
41990
|
+
window.setTimeout(() => void advanceRef.current(), 450);
|
|
41991
|
+
}, [oneAtATime, choose]);
|
|
41844
41992
|
function retreat() {
|
|
41993
|
+
setStopped(null);
|
|
41845
41994
|
if (oneAtATime && index > 0) setAt(index - 1);
|
|
41846
41995
|
}
|
|
41847
41996
|
function onKeyDown(event) {
|
|
@@ -41857,12 +42006,11 @@ function FormStage({
|
|
|
41857
42006
|
else advance();
|
|
41858
42007
|
}
|
|
41859
42008
|
useEffect95(() => {
|
|
41860
|
-
const input = stage.current?.querySelector(
|
|
41861
|
-
// A choice question has no box: its first option takes the caret, so A / B / C answer it.
|
|
42009
|
+
const input = stage.current?.querySelector("[role='radio'][aria-checked='true']") ?? stage.current?.querySelector(
|
|
41862
42010
|
"input:not([tabindex='-1']), textarea, select, [role='combobox'], [role='radio'], [role='checkbox']"
|
|
41863
42011
|
);
|
|
41864
42012
|
input?.focus();
|
|
41865
|
-
}, [index, done]);
|
|
42013
|
+
}, [index, done, started]);
|
|
41866
42014
|
const startTold = useRef55(false);
|
|
41867
42015
|
useEffect95(() => {
|
|
41868
42016
|
if (startTold.current || !onStart) return;
|
|
@@ -41881,6 +42029,10 @@ function FormStage({
|
|
|
41881
42029
|
}
|
|
41882
42030
|
}, [current, live, oneAtATime, onQuestionShown, started, done]);
|
|
41883
42031
|
const theme = formThemeClasses(form.theme ?? null);
|
|
42032
|
+
const pipe = (text2) => pipeAnswers(text2, answers, questions, (key, v) => {
|
|
42033
|
+
const f = questions.find((q) => q.key === key)?.field;
|
|
42034
|
+
return f ? publicChoicesOf(f).find((c) => c.key === String(v))?.label ?? null : null;
|
|
42035
|
+
});
|
|
41884
42036
|
if (done) {
|
|
41885
42037
|
return /* @__PURE__ */ jsx132(
|
|
41886
42038
|
ThankYou,
|
|
@@ -41889,6 +42041,7 @@ function FormStage({
|
|
|
41889
42041
|
done,
|
|
41890
42042
|
ending: (form.endings ?? []).find((e) => e.id === ending) ?? null,
|
|
41891
42043
|
buttonClass: theme.buttonClass,
|
|
42044
|
+
pipe,
|
|
41892
42045
|
className: cn77("mx-auto max-w-xl py-10", centred && "text-center", className)
|
|
41893
42046
|
}
|
|
41894
42047
|
);
|
|
@@ -41900,14 +42053,14 @@ function FormStage({
|
|
|
41900
42053
|
return /* @__PURE__ */ jsxs122(
|
|
41901
42054
|
"section",
|
|
41902
42055
|
{
|
|
41903
|
-
className: cn77("mx-auto flex w-full max-w-
|
|
42056
|
+
className: cn77("mx-auto flex min-h-[min(70dvh,640px)] w-full max-w-2xl flex-col justify-center gap-4 py-8", theme.surface, theme.background !== "plain" && "rounded-lg px-5", centred && "items-center text-center", className),
|
|
41904
42057
|
style: theme.surfaceStyle,
|
|
41905
42058
|
"data-records-form-welcome": "",
|
|
41906
42059
|
children: [
|
|
41907
42060
|
w.pictureUrl ? /* @__PURE__ */ jsx132("img", { src: w.pictureUrl, alt: "", className: "max-h-56 w-full rounded-md object-cover" }) : null,
|
|
41908
|
-
w.title ? /* @__PURE__ */ jsx132("h2", { className: "text-
|
|
41909
|
-
w.body ? /* @__PURE__ */ jsx132("p", { className: "text-
|
|
41910
|
-
/* @__PURE__ */ jsx132("div", { children: /* @__PURE__ */ jsx132(Button85, { size: "
|
|
42061
|
+
w.title ? /* @__PURE__ */ jsx132("h2", { className: "text-2xl font-semibold sm:text-3xl", children: w.title }) : null,
|
|
42062
|
+
w.body ? /* @__PURE__ */ jsx132("p", { className: "text-base text-muted-foreground", children: authored(host, w.body, "form-intro") }) : null,
|
|
42063
|
+
/* @__PURE__ */ jsx132("div", { children: /* @__PURE__ */ jsx132(Button85, { size: "lg", className: theme.buttonClass, onClick: () => setStarted(true), "data-records-form-start": "", children: w.buttonLabel || "Start" }) })
|
|
41911
42064
|
]
|
|
41912
42065
|
}
|
|
41913
42066
|
);
|
|
@@ -41916,7 +42069,15 @@ function FormStage({
|
|
|
41916
42069
|
return /* @__PURE__ */ jsxs122(
|
|
41917
42070
|
"section",
|
|
41918
42071
|
{
|
|
41919
|
-
className: cn77(
|
|
42072
|
+
className: cn77(
|
|
42073
|
+
"mx-auto flex w-full max-w-2xl flex-col gap-4 py-4",
|
|
42074
|
+
// TYPEFORM'S SIZE (TYPEFORM-2): one question at a time fills the screen, centred in it.
|
|
42075
|
+
oneAtATime && "min-h-[min(70dvh,640px)]",
|
|
42076
|
+
theme.surface,
|
|
42077
|
+
theme.background !== "plain" && "rounded-lg px-5 sm:px-8",
|
|
42078
|
+
centred && "text-center",
|
|
42079
|
+
className
|
|
42080
|
+
),
|
|
41920
42081
|
style: theme.surfaceStyle,
|
|
41921
42082
|
"data-records-form-font": theme.font,
|
|
41922
42083
|
onKeyDown,
|
|
@@ -41926,20 +42087,41 @@ function FormStage({
|
|
|
41926
42087
|
Progress,
|
|
41927
42088
|
{
|
|
41928
42089
|
value: live.length === 0 ? 0 : (oneAtATime ? index + 1 : answered) / live.length * 100,
|
|
41929
|
-
className: "h-1 flex-1"
|
|
42090
|
+
className: "h-1 flex-1",
|
|
42091
|
+
"aria-label": "Form progress",
|
|
42092
|
+
...routeKnown ? { "aria-valuetext": oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` } : {}
|
|
41930
42093
|
}
|
|
41931
42094
|
),
|
|
41932
|
-
/* @__PURE__ */ jsx132("span", { className: "tabular-nums", children: oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` })
|
|
42095
|
+
/* @__PURE__ */ jsx132("span", { className: cn77("min-w-[3.5rem] text-right tabular-nums", !routeKnown && "invisible"), "data-records-form-count": "", children: oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` })
|
|
41933
42096
|
] }),
|
|
41934
|
-
signedInPublic ? /* @__PURE__ */ jsx132(
|
|
41935
|
-
|
|
41936
|
-
|
|
41937
|
-
|
|
41938
|
-
|
|
41939
|
-
|
|
41940
|
-
|
|
41941
|
-
|
|
41942
|
-
|
|
42097
|
+
signedInPublic ? /* @__PURE__ */ jsx132(
|
|
42098
|
+
"p",
|
|
42099
|
+
{
|
|
42100
|
+
className: "w-fit rounded border border-dashed px-2 py-0.5 text-left text-xs text-muted-foreground",
|
|
42101
|
+
title: "The record will carry your name. Strangers answer at the public link.",
|
|
42102
|
+
children: "Signed in \xB7 saved as you"
|
|
42103
|
+
}
|
|
42104
|
+
) : null,
|
|
42105
|
+
unanswerable ? /* @__PURE__ */ jsx132(
|
|
42106
|
+
"p",
|
|
42107
|
+
{
|
|
42108
|
+
className: "w-fit rounded border border-dashed px-2 py-0.5 text-left text-xs text-muted-foreground",
|
|
42109
|
+
title: "Nothing here can work out which conditional questions apply, so all of them are shown.",
|
|
42110
|
+
children: "Showing every question"
|
|
42111
|
+
}
|
|
42112
|
+
) : null,
|
|
42113
|
+
unresolved.length > 0 ? /* @__PURE__ */ jsxs122(
|
|
42114
|
+
"p",
|
|
42115
|
+
{
|
|
42116
|
+
className: "w-fit rounded border border-destructive/40 px-2 py-0.5 text-left text-xs text-destructive",
|
|
42117
|
+
title: "The table has no such field. Add the field, or take the question out.",
|
|
42118
|
+
children: [
|
|
42119
|
+
"Missing field: ",
|
|
42120
|
+
unresolved.map((q) => q.ask).join(", ")
|
|
42121
|
+
]
|
|
42122
|
+
}
|
|
42123
|
+
) : null,
|
|
42124
|
+
form.intro && index === 0 ? /* @__PURE__ */ jsx132("p", { className: "text-base text-muted-foreground", children: authored(host, form.intro, "form-intro") }) : null,
|
|
41943
42125
|
error ? /* @__PURE__ */ jsx132(RefusalNotice, { error, className: "text-left" }) : null,
|
|
41944
42126
|
refusal4 ? /* @__PURE__ */ jsx132(
|
|
41945
42127
|
RefusalNotice,
|
|
@@ -41961,14 +42143,20 @@ function FormStage({
|
|
|
41961
42143
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ jsx132(
|
|
41962
42144
|
Question,
|
|
41963
42145
|
{
|
|
41964
|
-
question: q,
|
|
42146
|
+
question: { ...q, ask: pipe(q.ask), help: q.help ? pipe(q.help) : null },
|
|
41965
42147
|
value: answers[q.key],
|
|
41966
|
-
onChange: (v) =>
|
|
42148
|
+
onChange: (v) => {
|
|
42149
|
+
if (stopped?.key === q.key) setStopped(null);
|
|
42150
|
+
setAnswers((prev) => ({ ...prev, [q.key]: v }));
|
|
42151
|
+
},
|
|
42152
|
+
onChosen: chosen,
|
|
42153
|
+
refused: stopped?.key === q.key ? stopped.say : null,
|
|
42154
|
+
large: oneAtATime,
|
|
41967
42155
|
upload: host.upload
|
|
41968
42156
|
},
|
|
41969
42157
|
q.key
|
|
41970
42158
|
)),
|
|
41971
|
-
live.length === 0 ? /* @__PURE__ */ jsx132("p", { className: "text-xs text-muted-foreground",
|
|
42159
|
+
live.length === 0 ? /* @__PURE__ */ jsx132("p", { className: "text-xs text-muted-foreground", title: "Every question is hidden by its own condition right now.", children: "Nothing to answer yet" }) : null
|
|
41972
42160
|
] }),
|
|
41973
42161
|
honeypotKey ? /* @__PURE__ */ jsxs122("div", { "aria-hidden": true, className: "pointer-events-none absolute left-[-9999px] h-px w-px overflow-hidden", children: [
|
|
41974
42162
|
/* @__PURE__ */ jsx132("label", { htmlFor: `hp-${honeypotKey}`, children: "Leave this field empty" }),
|
|
@@ -41987,16 +42175,14 @@ function FormStage({
|
|
|
41987
42175
|
}
|
|
41988
42176
|
)
|
|
41989
42177
|
] }) : null,
|
|
41990
|
-
/* @__PURE__ */ jsxs122("footer", { className: cn77("flex flex-wrap items-center gap-
|
|
41991
|
-
oneAtATime && index > 0 ? /* @__PURE__ */ jsx132(Button85, { size: "
|
|
41992
|
-
oneAtATime && index < live.length - 1 ? /* @__PURE__ */ jsx132(Button85, { size: "
|
|
41993
|
-
|
|
41994
|
-
|
|
41995
|
-
"
|
|
41996
|
-
missing.length === 1 ? "needs" : "need",
|
|
41997
|
-
" an answer."
|
|
42178
|
+
/* @__PURE__ */ jsxs122("footer", { className: cn77("flex flex-wrap items-center gap-3", centred && "justify-center"), children: [
|
|
42179
|
+
oneAtATime && index > 0 ? /* @__PURE__ */ jsx132(Button85, { size: "lg", variant: "ghost", className: theme.buttonClass, onClick: retreat, children: "Back" }) : null,
|
|
42180
|
+
oneAtATime && index < live.length - 1 ? /* @__PURE__ */ jsx132(Button85, { size: "lg", className: theme.buttonClass, onClick: () => void advance(), children: "Next" }) : /* @__PURE__ */ jsx132(Button85, { size: "lg", className: theme.buttonClass, disabled: writing, onClick: () => void (oneAtATime ? advance() : submit()), children: writing ? "Sending\u2026" : form.submitLabel ?? "Submit" }),
|
|
42181
|
+
oneAtATime ? /* @__PURE__ */ jsxs122("span", { className: "hidden text-xs text-muted-foreground sm:inline", "data-records-form-enter-hint": "", children: [
|
|
42182
|
+
"press ",
|
|
42183
|
+
/* @__PURE__ */ jsx132("strong", { className: "font-semibold text-foreground", children: "Enter \u21B5" })
|
|
41998
42184
|
] }) : null,
|
|
41999
|
-
preview ? /* @__PURE__ */ jsx132("span", { className: "text-xs text-muted-foreground",
|
|
42185
|
+
preview ? /* @__PURE__ */ jsx132("span", { className: "text-xs text-muted-foreground", title: "Nothing is written from a preview.", children: "Preview" }) : null
|
|
42000
42186
|
] })
|
|
42001
42187
|
]
|
|
42002
42188
|
}
|
|
@@ -42008,12 +42194,15 @@ function ThankYou({
|
|
|
42008
42194
|
done,
|
|
42009
42195
|
className,
|
|
42010
42196
|
ending,
|
|
42011
|
-
buttonClass
|
|
42197
|
+
buttonClass,
|
|
42198
|
+
pipe = (t) => t
|
|
42012
42199
|
}) {
|
|
42013
42200
|
const host = useRecordsUi();
|
|
42014
42201
|
const redirect = (ending ? ending.redirectUrl?.trim() : form.thankYou?.redirectUrl?.trim()) || null;
|
|
42015
|
-
const
|
|
42016
|
-
const
|
|
42202
|
+
const rawTitle = ending ? ending.title : form.thankYou?.title;
|
|
42203
|
+
const rawBody = ending ? ending.body : form.thankYou?.body;
|
|
42204
|
+
const title = rawTitle ? pipe(rawTitle) : rawTitle;
|
|
42205
|
+
const body = rawBody ? pipe(rawBody) : rawBody;
|
|
42017
42206
|
const going = redirect !== null && done !== "preview";
|
|
42018
42207
|
useEffect95(() => {
|
|
42019
42208
|
if (!going || !redirect) return;
|
|
@@ -42030,8 +42219,8 @@ function ThankYou({
|
|
|
42030
42219
|
}
|
|
42031
42220
|
),
|
|
42032
42221
|
ending?.pictureUrl ? /* @__PURE__ */ jsx132("img", { src: ending.pictureUrl, alt: "", className: "mb-3 max-h-48 w-full rounded-md object-cover" }) : null,
|
|
42033
|
-
/* @__PURE__ */ jsx132("h2", { className: "text-
|
|
42034
|
-
body ? /* @__PURE__ */ jsx132("p", { className: "mt-
|
|
42222
|
+
/* @__PURE__ */ jsx132("h2", { className: "text-2xl font-semibold", "data-records-form-ending": ending?.id ?? "", children: title || "Thank you" }),
|
|
42223
|
+
body ? /* @__PURE__ */ jsx132("p", { className: "mt-2 text-base text-muted-foreground", children: authored(host, body, "form-thank-you") }) : null,
|
|
42035
42224
|
going ? /* @__PURE__ */ jsxs122("p", { className: "mt-4 flex flex-wrap items-center gap-3 text-sm", children: [
|
|
42036
42225
|
/* @__PURE__ */ jsx132(
|
|
42037
42226
|
"a",
|
|
@@ -42044,11 +42233,11 @@ function ThankYou({
|
|
|
42044
42233
|
),
|
|
42045
42234
|
/* @__PURE__ */ jsx132("span", { className: "text-muted-foreground", children: "Taking you there\u2026" })
|
|
42046
42235
|
] }) : null,
|
|
42047
|
-
/* @__PURE__ */ jsx132("p", { className: "mt-3 text-xs text-muted-foreground", children: done === "preview" ? redirect ? `
|
|
42236
|
+
/* @__PURE__ */ jsx132("p", { className: "mt-3 text-xs text-muted-foreground", children: done === "preview" ? redirect ? `Preview \xB7 then ${redirect}` : "Preview \xB7 nothing saved" : done === "sent" ? (
|
|
42048
42237
|
// SAID TO THE PERSON WHO ANSWERED (lane HANDOVER, 2026-09-29): "Saved to Form 1, with
|
|
42049
42238
|
// this form stamped on it" was the owner's bookkeeping read out to a front-desk member
|
|
42050
42239
|
// and to strangers. Where it landed is the owner's to see on the table.
|
|
42051
|
-
"
|
|
42240
|
+
"Answers received"
|
|
42052
42241
|
) : done })
|
|
42053
42242
|
] });
|
|
42054
42243
|
}
|
|
@@ -42056,6 +42245,9 @@ function Question({
|
|
|
42056
42245
|
question,
|
|
42057
42246
|
value,
|
|
42058
42247
|
onChange,
|
|
42248
|
+
onChosen,
|
|
42249
|
+
refused,
|
|
42250
|
+
large,
|
|
42059
42251
|
upload
|
|
42060
42252
|
}) {
|
|
42061
42253
|
const host = useRecordsUi();
|
|
@@ -42063,297 +42255,62 @@ function Question({
|
|
|
42063
42255
|
const field2 = question.field;
|
|
42064
42256
|
if (!field2) return null;
|
|
42065
42257
|
const id = `form-${field2.key}`;
|
|
42258
|
+
const sayId = `${id}-refused`;
|
|
42066
42259
|
const isAttachment = editorKindFor(field2) === "attachment";
|
|
42067
|
-
return /* @__PURE__ */ jsxs122(
|
|
42068
|
-
|
|
42069
|
-
|
|
42070
|
-
|
|
42071
|
-
|
|
42072
|
-
"
|
|
42073
|
-
{
|
|
42074
|
-
id,
|
|
42075
|
-
type: "file",
|
|
42076
|
-
className: "text-xs",
|
|
42077
|
-
onChange: (e) => {
|
|
42078
|
-
const file = e.target.files?.[0];
|
|
42079
|
-
if (!file) return;
|
|
42080
|
-
setUploadError(null);
|
|
42081
|
-
void upload?.(file).then((result) => {
|
|
42082
|
-
if (!result) return;
|
|
42083
|
-
if (result.ok) onChange(result.fileId);
|
|
42084
|
-
else
|
|
42085
|
-
setUploadError(
|
|
42086
|
-
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
42087
|
-
);
|
|
42088
|
-
});
|
|
42089
|
-
}
|
|
42090
|
-
}
|
|
42260
|
+
return /* @__PURE__ */ jsxs122(
|
|
42261
|
+
"div",
|
|
42262
|
+
{
|
|
42263
|
+
className: cn77(
|
|
42264
|
+
"flex flex-col",
|
|
42265
|
+
large ? "gap-3 [&_input]:text-base [&_textarea]:text-base [&_input]:min-h-12" : "gap-1.5 [&_input]:text-base [&_textarea]:text-base"
|
|
42091
42266
|
),
|
|
42092
|
-
|
|
42093
|
-
|
|
42094
|
-
|
|
42095
|
-
|
|
42096
|
-
|
|
42097
|
-
|
|
42098
|
-
}
|
|
42099
|
-
|
|
42100
|
-
|
|
42101
|
-
|
|
42102
|
-
|
|
42103
|
-
import { hasQuietHours as hasQuietHours3 } from "@ai-matrx/records";
|
|
42104
|
-
import { Button as Button86, Skeleton as Skeleton29, cn as cn78 } from "@ai-matrx/design-system";
|
|
42105
|
-
import { jsx as jsx133, jsxs as jsxs123 } from "react/jsx-runtime";
|
|
42106
|
-
var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
|
|
42107
|
-
var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
42108
|
-
function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className }) {
|
|
42109
|
-
const client = useRecordsClient79();
|
|
42110
|
-
const host = useRecordsUi();
|
|
42111
|
-
const table = useTable26(tableId);
|
|
42112
|
-
const rights = useTableRights(table.data);
|
|
42113
|
-
const [subscriptions, setSubscriptions] = useState110(null);
|
|
42114
|
-
const [cadences, setCadences] = useState110([]);
|
|
42115
|
-
const [views, setViews] = useState110(null);
|
|
42116
|
-
const [error, setError] = useState110(null);
|
|
42117
|
-
const [busy, setBusy] = useState110(false);
|
|
42118
|
-
const [stopping, setStopping] = useState110(null);
|
|
42119
|
-
const load = useCallback54(async () => {
|
|
42120
|
-
const [held, offered] = await Promise.all([
|
|
42121
|
-
// THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
|
|
42122
|
-
// to them plus — only where they hold admin on this Table — anyone's over
|
|
42123
|
-
// it, and it is narrowed to Tables they can already open.
|
|
42124
|
-
client.subscriptions({ table_id: tableId }),
|
|
42125
|
-
// The cadences are ASKED of the store, never a list typed into a picker
|
|
42126
|
-
// here that could drift from what the digest runner understands.
|
|
42127
|
-
client.subscriptionCadences()
|
|
42128
|
-
]);
|
|
42129
|
-
if (!held.ok) {
|
|
42130
|
-
setError(held.error);
|
|
42131
|
-
return;
|
|
42132
|
-
}
|
|
42133
|
-
setError(null);
|
|
42134
|
-
setSubscriptions(held.data);
|
|
42135
|
-
if (offered.ok) setCadences(offered.data);
|
|
42136
|
-
if (host.savedViews) {
|
|
42137
|
-
setViews(await host.savedViews());
|
|
42138
|
-
return;
|
|
42139
|
-
}
|
|
42140
|
-
const listed = await client.views({ table_id: tableId });
|
|
42141
|
-
if (!listed.ok) {
|
|
42142
|
-
setError(listed.error);
|
|
42143
|
-
return;
|
|
42144
|
-
}
|
|
42145
|
-
setViews(listed.data.map((v) => ({ id: v.view_id, name: v.name })));
|
|
42146
|
-
}, [client, host, tableId]);
|
|
42147
|
-
useEffect96(() => {
|
|
42148
|
-
void load();
|
|
42149
|
-
}, [load]);
|
|
42150
|
-
const write = useCallback54(
|
|
42151
|
-
async (spec) => {
|
|
42152
|
-
const declared2 = await client.subscriptionDeclare({
|
|
42153
|
-
table_id: tableId,
|
|
42154
|
-
spec: {
|
|
42155
|
-
name: spec.name,
|
|
42156
|
-
saved_view_id: spec.savedViewId,
|
|
42157
|
-
cadence: spec.cadence ?? "instant",
|
|
42158
|
-
schedule: spec.schedule ?? null,
|
|
42159
|
-
channel: spec.channel ?? "in_app",
|
|
42160
|
-
quiet_hours: spec.quietHours ?? null,
|
|
42161
|
-
// "Tell me" is the default, and the store refuses a subscription with
|
|
42162
|
-
// nobody to tell rather than writing one that fires at nobody.
|
|
42163
|
-
recipient_user_id: spec.recipientUserId ?? client.config.actor.user_id ?? null,
|
|
42164
|
-
event_key: spec.eventKey ?? "records.changed"
|
|
42165
|
-
}
|
|
42166
|
-
});
|
|
42167
|
-
if (!declared2.ok) {
|
|
42168
|
-
setError(declared2.error);
|
|
42169
|
-
return null;
|
|
42170
|
-
}
|
|
42171
|
-
return declared2.data;
|
|
42172
|
-
},
|
|
42173
|
-
[client, tableId]
|
|
42174
|
-
);
|
|
42175
|
-
useEffect96(() => {
|
|
42176
|
-
if (!subscriptions || !seed || seed.length === 0) return;
|
|
42177
|
-
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
42178
|
-
if (missing.length === 0) return;
|
|
42179
|
-
void (async () => {
|
|
42180
|
-
for (const spec of missing) {
|
|
42181
|
-
if (!await write(spec)) return;
|
|
42182
|
-
}
|
|
42183
|
-
await load();
|
|
42184
|
-
})();
|
|
42185
|
-
}, [subscriptions, seed, write, load]);
|
|
42186
|
-
async function subscribe(viewId, viewName, cadence, channel, schedule, quiet2) {
|
|
42187
|
-
setBusy(true);
|
|
42188
|
-
const ok = await write({
|
|
42189
|
-
name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
|
|
42190
|
-
savedViewId: viewId,
|
|
42191
|
-
cadence,
|
|
42192
|
-
channel,
|
|
42193
|
-
schedule: schedule.trim() === "" ? null : schedule.trim(),
|
|
42194
|
-
quietHours: quiet2
|
|
42195
|
-
});
|
|
42196
|
-
setBusy(false);
|
|
42197
|
-
if (!ok) return;
|
|
42198
|
-
onChoose?.(ok);
|
|
42199
|
-
await load();
|
|
42200
|
-
}
|
|
42201
|
-
async function makeEveryRecordView() {
|
|
42202
|
-
setBusy(true);
|
|
42203
|
-
const made = await client.viewDeclare({
|
|
42204
|
-
table_id: tableId,
|
|
42205
|
-
spec: { name: `Every ${table.data?.name ?? "record"}`, filters: null }
|
|
42206
|
-
});
|
|
42207
|
-
setBusy(false);
|
|
42208
|
-
if (!made.ok) {
|
|
42209
|
-
setError(made.error);
|
|
42210
|
-
return;
|
|
42211
|
-
}
|
|
42212
|
-
await load();
|
|
42213
|
-
}
|
|
42214
|
-
async function unsubscribe(ruleId) {
|
|
42215
|
-
setStopping(null);
|
|
42216
|
-
setBusy(true);
|
|
42217
|
-
const deleted = await client.recordDelete({ record_id: ruleId });
|
|
42218
|
-
setBusy(false);
|
|
42219
|
-
if (!deleted.ok) {
|
|
42220
|
-
setError(deleted.error);
|
|
42221
|
-
return;
|
|
42222
|
-
}
|
|
42223
|
-
await load();
|
|
42224
|
-
}
|
|
42225
|
-
if (error) {
|
|
42226
|
-
return /* @__PURE__ */ jsx133(
|
|
42227
|
-
RefusalNotice,
|
|
42228
|
-
{
|
|
42229
|
-
error,
|
|
42230
|
-
className,
|
|
42231
|
-
actions: /* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
42232
|
-
}
|
|
42233
|
-
);
|
|
42234
|
-
}
|
|
42235
|
-
if (subscriptions === null) return /* @__PURE__ */ jsx133(Skeleton29, { className: cn78("h-40 w-full", className) });
|
|
42236
|
-
return /* @__PURE__ */ jsxs123("div", { className: cn78("flex flex-col gap-2", className), children: [
|
|
42237
|
-
/* @__PURE__ */ jsxs123("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
42238
|
-
/* @__PURE__ */ jsx133("span", { className: "font-medium text-foreground", children: "Tell me when" }),
|
|
42239
|
-
/* @__PURE__ */ jsxs123("span", { children: [
|
|
42240
|
-
subscriptions.length,
|
|
42241
|
-
" subscription",
|
|
42242
|
-
subscriptions.length === 1 ? "" : "s"
|
|
42243
|
-
] })
|
|
42244
|
-
] }),
|
|
42245
|
-
subscriptions.length === 0 ? /* @__PURE__ */ jsx133("p", { className: "text-xs text-muted-foreground", children: "Nothing is telling anyone about this yet. A subscription is a Rule over a saved view, so what you are told about is exactly what that view holds." }) : /* @__PURE__ */ jsx133("ul", { className: "flex flex-col gap-1", children: subscriptions.map((held) => /* @__PURE__ */ jsxs123("li", { className: "flex items-center gap-2 rounded border px-2 py-1.5 text-xs", children: [
|
|
42246
|
-
/* @__PURE__ */ jsx133("span", { className: "truncate font-medium", children: held.name }),
|
|
42247
|
-
/* @__PURE__ */ jsx133("span", { className: "text-muted-foreground", children: CADENCE_WORDS2[held.cadence] ?? held.cadence }),
|
|
42248
|
-
/* @__PURE__ */ jsx133("span", { className: "text-muted-foreground", children: CHANNEL_WORDS3[held.channel] ?? held.channel }),
|
|
42249
|
-
held.schedule ? /* @__PURE__ */ jsx133("span", { className: "text-muted-foreground", children: scheduleWords(held.schedule, held.quiet_hours?.tz) }) : null,
|
|
42250
|
-
held.next_digest_at ? /* @__PURE__ */ jsxs123("span", { className: "text-muted-foreground", children: [
|
|
42251
|
-
"next ",
|
|
42252
|
-
new Date(held.next_digest_at).toLocaleString()
|
|
42253
|
-
] }) : null,
|
|
42254
|
-
hasQuietHours3(held.quiet_hours) ? /* @__PURE__ */ jsxs123("span", { className: "text-muted-foreground", children: [
|
|
42255
|
-
"quiet ",
|
|
42256
|
-
held.quiet_hours.start,
|
|
42257
|
-
"\u2013",
|
|
42258
|
-
held.quiet_hours.end
|
|
42259
|
-
] }) : null,
|
|
42260
|
-
held.recipient_user_id === null ? /* @__PURE__ */ jsx133("span", { className: "text-destructive", children: "This one has nobody to tell, so it fires at nobody." }) : null,
|
|
42261
|
-
held.saved_view_id === null ? /* @__PURE__ */ jsx133("span", { className: "text-destructive", children: "This one names no view, so the store never admits a record to it." }) : null,
|
|
42262
|
-
held.muted ? /* @__PURE__ */ jsx133("span", { className: "text-muted-foreground", children: "Switched off \u2014 it tells nobody until somebody switches it back on." }) : null,
|
|
42263
|
-
onChoose ? held.rule_id === chosenRuleId ? /* @__PURE__ */ jsxs123("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
42264
|
-
/* @__PURE__ */ jsx133("span", { className: "font-medium text-foreground", children: "Used for this form" }),
|
|
42265
|
-
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "ghost", disabled: busy, onClick: () => onChoose(null), children: "Stop using" })
|
|
42266
|
-
] }) : /* @__PURE__ */ jsx133(
|
|
42267
|
-
Button86,
|
|
42268
|
-
{
|
|
42269
|
-
size: "sm",
|
|
42270
|
-
variant: "outline",
|
|
42271
|
-
className: "ml-auto",
|
|
42272
|
-
disabled: busy,
|
|
42273
|
-
onClick: () => onChoose(held.rule_id),
|
|
42274
|
-
children: "Use for this form"
|
|
42275
|
-
}
|
|
42276
|
-
) : null,
|
|
42277
|
-
rights.write && !(onChoose && held.rule_id === chosenRuleId) && stopping === held.rule_id ? /* @__PURE__ */ jsxs123("span", { className: cn78("flex flex-wrap items-center gap-2", onChoose ? void 0 : "ml-auto"), children: [
|
|
42278
|
-
/* @__PURE__ */ jsx133("span", { className: "text-xs text-destructive", children: archiveConfirmSentence("this subscription") }),
|
|
42279
|
-
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "destructive", disabled: busy, onClick: () => void unsubscribe(held.rule_id), children: "Stop telling me" }),
|
|
42280
|
-
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "ghost", onClick: () => setStopping(null), children: "Keep it" })
|
|
42281
|
-
] }) : rights.write && !(onChoose && held.rule_id === chosenRuleId) ? /* @__PURE__ */ jsx133(
|
|
42282
|
-
Button86,
|
|
42283
|
-
{
|
|
42284
|
-
size: "sm",
|
|
42285
|
-
variant: "ghost",
|
|
42286
|
-
className: onChoose ? void 0 : "ml-auto",
|
|
42287
|
-
disabled: busy,
|
|
42288
|
-
onClick: () => setStopping(held.rule_id),
|
|
42289
|
-
children: "Stop telling me"
|
|
42290
|
-
}
|
|
42291
|
-
) : null
|
|
42292
|
-
] }, held.rule_id)) }),
|
|
42293
|
-
onChoose && chosenRuleId && !subscriptions.some((held) => held.rule_id === chosenRuleId) ? /* @__PURE__ */ jsx133("p", { className: "text-xs text-destructive", children: "This form points at a subscription that is not in this list any more, so nobody is told when it is answered. Choose one below, or make one." }) : null,
|
|
42294
|
-
views === null ? /* @__PURE__ */ jsx133("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_SAVED_VIEWS_REASON }) : views.length === 0 ? onChoose && rights.write ? /* @__PURE__ */ jsxs123("div", { className: "flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [
|
|
42295
|
-
/* @__PURE__ */ jsx133("span", { children: "A subscription watches a saved view, and this table has none yet." }),
|
|
42296
|
-
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "outline", disabled: busy, onClick: () => void makeEveryRecordView(), children: "Watch every record" })
|
|
42297
|
-
] }) : /* @__PURE__ */ jsx133("p", { className: "text-xs text-muted-foreground", children: "This organization has no saved views yet, so there is nothing to subscribe to. Save a view first." }) : rights.write ? /* @__PURE__ */ jsxs123(
|
|
42298
|
-
"form",
|
|
42299
|
-
{
|
|
42300
|
-
className: "flex flex-wrap items-center gap-1",
|
|
42301
|
-
onSubmit: (event) => {
|
|
42302
|
-
event.preventDefault();
|
|
42303
|
-
const form = event.currentTarget;
|
|
42304
|
-
const view = form.elements.namedItem("view").value;
|
|
42305
|
-
const cadence = form.elements.namedItem("cadence").value;
|
|
42306
|
-
const channel = form.elements.namedItem("channel").value;
|
|
42307
|
-
const schedule = form.elements.namedItem("schedule").value;
|
|
42308
|
-
const start = form.elements.namedItem("quiet_start").value;
|
|
42309
|
-
const end = form.elements.namedItem("quiet_end").value;
|
|
42310
|
-
const name = views.find((v) => v.id === view)?.name ?? "View";
|
|
42311
|
-
const quiet2 = start && end ? { start, end, tz: Intl.DateTimeFormat().resolvedOptions().timeZone } : null;
|
|
42312
|
-
if (view) void subscribe(view, name, cadence, channel, schedule, quiet2);
|
|
42313
|
-
},
|
|
42314
|
-
children: [
|
|
42315
|
-
/* @__PURE__ */ jsx133("select", { name: "view", "aria-label": "View to be told about", className: "h-7 rounded border bg-background px-1 text-xs", children: views.map((view) => /* @__PURE__ */ jsx133("option", { value: view.id, children: view.name }, view.id)) }),
|
|
42316
|
-
/* @__PURE__ */ jsx133("select", { name: "cadence", "aria-label": "How often", className: "h-7 rounded border bg-background px-1 text-xs", children: (cadences.length > 0 ? cadences : ["instant"]).map((cadence) => /* @__PURE__ */ jsx133("option", { value: cadence, children: CADENCE_WORDS2[cadence] ?? cadence }, cadence)) }),
|
|
42317
|
-
/* @__PURE__ */ jsx133("select", { name: "channel", "aria-label": "Where it arrives", className: "h-7 rounded border bg-background px-1 text-xs", children: ["in_app", "email", "sms"].map((channel) => /* @__PURE__ */ jsx133("option", { value: channel, children: CHANNEL_WORDS3[channel] }, channel)) }),
|
|
42318
|
-
/* @__PURE__ */ jsx133(
|
|
42319
|
-
"input",
|
|
42320
|
-
{
|
|
42321
|
-
name: "schedule",
|
|
42322
|
-
"aria-label": "When the summary goes out",
|
|
42323
|
-
placeholder: "monday 08:00",
|
|
42324
|
-
className: "h-7 w-28 rounded border bg-background px-1 text-xs"
|
|
42325
|
-
}
|
|
42326
|
-
),
|
|
42327
|
-
/* @__PURE__ */ jsx133("span", { className: "text-xs text-muted-foreground", children: "not between" }),
|
|
42328
|
-
/* @__PURE__ */ jsx133(
|
|
42329
|
-
"input",
|
|
42330
|
-
{
|
|
42331
|
-
name: "quiet_start",
|
|
42332
|
-
type: "time",
|
|
42333
|
-
"aria-label": "Quiet hours start",
|
|
42334
|
-
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
42335
|
-
}
|
|
42336
|
-
),
|
|
42337
|
-
/* @__PURE__ */ jsx133("span", { className: "text-xs text-muted-foreground", children: "and" }),
|
|
42338
|
-
/* @__PURE__ */ jsx133(
|
|
42267
|
+
"data-records-form-question": field2.key,
|
|
42268
|
+
"aria-describedby": refused ? sayId : void 0,
|
|
42269
|
+
children: [
|
|
42270
|
+
/* @__PURE__ */ jsxs122("div", { className: "flex flex-wrap items-baseline gap-x-2", children: [
|
|
42271
|
+
/* @__PURE__ */ jsx132(Label22, { htmlFor: id, className: cn77("font-medium leading-snug text-foreground", large ? "text-xl sm:text-2xl" : "text-base"), children: question.ask }),
|
|
42272
|
+
question.required ? /* @__PURE__ */ jsx132("span", { className: "text-sm text-foreground/80", children: "required" }) : null,
|
|
42273
|
+
field2.unit ? /* @__PURE__ */ jsx132("span", { className: "text-sm text-foreground/70", children: field2.unit }) : null
|
|
42274
|
+
] }),
|
|
42275
|
+
question.help ? /* @__PURE__ */ jsx132("p", { className: cn77("text-muted-foreground", large ? "text-base" : "text-sm"), children: authored(host, question.help, "form-help") }) : null,
|
|
42276
|
+
isAttachment && !upload ? /* @__PURE__ */ jsx132("p", { className: "w-fit rounded border border-dashed px-2 py-0.5 text-xs text-muted-foreground", title: NO_UPLOAD_REASON, children: "File upload unavailable here" }) : isAttachment ? /* @__PURE__ */ jsxs122(Fragment53, { children: [
|
|
42277
|
+
/* @__PURE__ */ jsx132(
|
|
42339
42278
|
"input",
|
|
42340
42279
|
{
|
|
42341
|
-
|
|
42342
|
-
type: "
|
|
42343
|
-
|
|
42344
|
-
|
|
42280
|
+
id,
|
|
42281
|
+
type: "file",
|
|
42282
|
+
className: "text-sm",
|
|
42283
|
+
onChange: (e) => {
|
|
42284
|
+
const file = e.target.files?.[0];
|
|
42285
|
+
if (!file) return;
|
|
42286
|
+
setUploadError(null);
|
|
42287
|
+
void upload?.(file).then((result) => {
|
|
42288
|
+
if (!result) return;
|
|
42289
|
+
if (result.ok) onChange(result.fileId);
|
|
42290
|
+
else
|
|
42291
|
+
setUploadError(
|
|
42292
|
+
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
42293
|
+
);
|
|
42294
|
+
});
|
|
42295
|
+
}
|
|
42345
42296
|
}
|
|
42346
42297
|
),
|
|
42347
|
-
/* @__PURE__ */
|
|
42348
|
-
|
|
42349
|
-
|
|
42350
|
-
|
|
42351
|
-
|
|
42352
|
-
|
|
42298
|
+
uploadError ? /* @__PURE__ */ jsx132(RefusalLine, { error: uploadError }) : null
|
|
42299
|
+
] }) : (
|
|
42300
|
+
// A choice is laid out as lettered rows in the form's colour (`ChoiceAnswer`).
|
|
42301
|
+
/* @__PURE__ */ jsx132(FieldControl, { field: field2, value, onChange, id, asForm: true, onChosen })
|
|
42302
|
+
),
|
|
42303
|
+
/* @__PURE__ */ jsx132("p", { id: sayId, role: "alert", "aria-live": "assertive", className: "min-h-0 empty:hidden", "data-records-form-refused": "", children: refused ? /* @__PURE__ */ jsxs122("span", { className: "inline-flex items-center gap-1.5 rounded bg-destructive px-2 py-1 text-sm font-medium text-destructive-foreground", children: [
|
|
42304
|
+
/* @__PURE__ */ jsx132("span", { "aria-hidden": "true", children: "!" }),
|
|
42305
|
+
refused
|
|
42306
|
+
] }) : null })
|
|
42307
|
+
]
|
|
42308
|
+
}
|
|
42309
|
+
);
|
|
42353
42310
|
}
|
|
42354
42311
|
|
|
42355
42312
|
// src/FormBuilder.tsx
|
|
42356
|
-
import { Fragment as Fragment54, jsx as
|
|
42313
|
+
import { Fragment as Fragment54, jsx as jsx133, jsxs as jsxs123 } from "react/jsx-runtime";
|
|
42357
42314
|
function freshFormName(base, taken) {
|
|
42358
42315
|
const names = new Set(taken.map((n) => n.trim().toLowerCase()));
|
|
42359
42316
|
if (!names.has(base.toLowerCase())) return base;
|
|
@@ -42370,7 +42327,21 @@ function isLookRefusal(error) {
|
|
|
42370
42327
|
return Boolean(error && LOOK_REFUSAL.test(refusalSentence(error)));
|
|
42371
42328
|
}
|
|
42372
42329
|
var KEPT_DRAFT_PREFIX = "records-ui:form-draft:";
|
|
42373
|
-
var SPEC_KEYS = [
|
|
42330
|
+
var SPEC_KEYS = [
|
|
42331
|
+
"name",
|
|
42332
|
+
"intro",
|
|
42333
|
+
"questions",
|
|
42334
|
+
"flow",
|
|
42335
|
+
"theme",
|
|
42336
|
+
"look",
|
|
42337
|
+
"thankYou",
|
|
42338
|
+
"submitLabel",
|
|
42339
|
+
"welcome",
|
|
42340
|
+
"endings",
|
|
42341
|
+
"hiddenFields",
|
|
42342
|
+
"showOwnerHeader",
|
|
42343
|
+
"autoAdvance"
|
|
42344
|
+
];
|
|
42374
42345
|
function readKeptDraft(formId) {
|
|
42375
42346
|
try {
|
|
42376
42347
|
const raw = window.localStorage.getItem(KEPT_DRAFT_PREFIX + formId);
|
|
@@ -42401,31 +42372,31 @@ function withKept(stored, spec) {
|
|
|
42401
42372
|
return out;
|
|
42402
42373
|
}
|
|
42403
42374
|
function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne, createOnMount, className }) {
|
|
42404
|
-
const client =
|
|
42375
|
+
const client = useRecordsClient79();
|
|
42405
42376
|
const host = useRecordsUi();
|
|
42406
42377
|
const claimSeed = useSeedGuard();
|
|
42407
|
-
const table =
|
|
42378
|
+
const table = useTable26(tableId);
|
|
42408
42379
|
const rights = useTableRights(table.data);
|
|
42409
42380
|
const fields = useFields25(tableId);
|
|
42410
|
-
const [forms, setForms] =
|
|
42411
|
-
const [error, setError] =
|
|
42412
|
-
const [refused, setRefused] =
|
|
42413
|
-
const [activeId, setActiveId] =
|
|
42414
|
-
const [draft, setDraft] =
|
|
42415
|
-
const [saving, setSaving] =
|
|
42416
|
-
const [saved, setSaved] =
|
|
42417
|
-
const [publishing, setPublishing] =
|
|
42418
|
-
const [copied, setCopied] =
|
|
42419
|
-
const [shownUrl, setShownUrl] =
|
|
42420
|
-
const [lookOptions, setLookOptions] =
|
|
42421
|
-
const [lookUnread, setLookUnread] =
|
|
42422
|
-
const [restoredAt, setRestoredAt] =
|
|
42423
|
-
const [showResults, setShowResults] =
|
|
42424
|
-
const [adding, setAdding] =
|
|
42381
|
+
const [forms, setForms] = useState110(null);
|
|
42382
|
+
const [error, setError] = useState110(null);
|
|
42383
|
+
const [refused, setRefused] = useState110(null);
|
|
42384
|
+
const [activeId, setActiveId] = useState110(activeFormId ?? null);
|
|
42385
|
+
const [draft, setDraft] = useState110(null);
|
|
42386
|
+
const [saving, setSaving] = useState110(false);
|
|
42387
|
+
const [saved, setSaved] = useState110(null);
|
|
42388
|
+
const [publishing, setPublishing] = useState110(false);
|
|
42389
|
+
const [copied, setCopied] = useState110(false);
|
|
42390
|
+
const [shownUrl, setShownUrl] = useState110(null);
|
|
42391
|
+
const [lookOptions, setLookOptions] = useState110(null);
|
|
42392
|
+
const [lookUnread, setLookUnread] = useState110(null);
|
|
42393
|
+
const [restoredAt, setRestoredAt] = useState110(null);
|
|
42394
|
+
const [showResults, setShowResults] = useState110(false);
|
|
42395
|
+
const [adding, setAdding] = useState110(false);
|
|
42425
42396
|
const keysBeforeAdd = useRef56(null);
|
|
42426
|
-
const [makeOnMount, setMakeOnMount] =
|
|
42397
|
+
const [makeOnMount, setMakeOnMount] = useState110("waiting");
|
|
42427
42398
|
const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
42428
|
-
const load =
|
|
42399
|
+
const load = useCallback54(async () => {
|
|
42429
42400
|
const answered = await client.forms({ table_id: tableId });
|
|
42430
42401
|
if (!answered.ok) {
|
|
42431
42402
|
setError(answered.error);
|
|
@@ -42452,10 +42423,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42452
42423
|
setError(null);
|
|
42453
42424
|
setForms(mine);
|
|
42454
42425
|
}, [client, tableId, seed, claimSeed]);
|
|
42455
|
-
|
|
42426
|
+
useEffect96(() => {
|
|
42456
42427
|
void load();
|
|
42457
42428
|
}, [load]);
|
|
42458
|
-
|
|
42429
|
+
useEffect96(() => {
|
|
42459
42430
|
if (!forms || forms.length === 0) return;
|
|
42460
42431
|
if (createOnMount && makeOnMount !== "made") return;
|
|
42461
42432
|
const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
|
|
@@ -42479,7 +42450,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42479
42450
|
);
|
|
42480
42451
|
const stored = useMemo68(() => draft ? (forms ?? []).find((f) => f.id === draft.id) ?? null : null, [draft, forms]);
|
|
42481
42452
|
const dirty = Boolean(draft && stored && !sameForm(draft, stored));
|
|
42482
|
-
|
|
42453
|
+
useEffect96(() => {
|
|
42483
42454
|
if (!draft || !stored) return;
|
|
42484
42455
|
const timer = window.setTimeout(() => {
|
|
42485
42456
|
if (sameForm(draft, stored)) dropKeptDraft(draft.id);
|
|
@@ -42488,7 +42459,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42488
42459
|
return () => window.clearTimeout(timer);
|
|
42489
42460
|
}, [draft, stored]);
|
|
42490
42461
|
const lookKey = draft ? JSON.stringify(formLookDocument(draft.look ?? null)) : null;
|
|
42491
|
-
|
|
42462
|
+
useEffect96(() => {
|
|
42492
42463
|
if (lookKey === null) return;
|
|
42493
42464
|
let cancelled = false;
|
|
42494
42465
|
const timer = window.setTimeout(() => {
|
|
@@ -42505,7 +42476,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42505
42476
|
window.clearTimeout(timer);
|
|
42506
42477
|
};
|
|
42507
42478
|
}, [client, lookKey]);
|
|
42508
|
-
|
|
42479
|
+
useEffect96(() => {
|
|
42509
42480
|
const before = keysBeforeAdd.current;
|
|
42510
42481
|
if (!before || !fields.data) return;
|
|
42511
42482
|
const made = askableFields(fields.data).filter((f) => !before.has(f.key));
|
|
@@ -42544,7 +42515,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42544
42515
|
}
|
|
42545
42516
|
dropKeptDraft(draft.id);
|
|
42546
42517
|
setRestoredAt(null);
|
|
42547
|
-
setSaved("Saved
|
|
42518
|
+
setSaved("Saved");
|
|
42548
42519
|
await load();
|
|
42549
42520
|
return true;
|
|
42550
42521
|
}
|
|
@@ -42583,7 +42554,22 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42583
42554
|
setRefused(written.error);
|
|
42584
42555
|
return;
|
|
42585
42556
|
}
|
|
42586
|
-
setSaved(
|
|
42557
|
+
setSaved("Saved");
|
|
42558
|
+
await load();
|
|
42559
|
+
}
|
|
42560
|
+
async function chooseWebhook(notify) {
|
|
42561
|
+
if (!draft) return;
|
|
42562
|
+
const next = { ...draft, notify };
|
|
42563
|
+
setDraft(next);
|
|
42564
|
+
setSaving(true);
|
|
42565
|
+
setRefused(null);
|
|
42566
|
+
const written = await client.formDeclare(formDeclareArgs(next));
|
|
42567
|
+
setSaving(false);
|
|
42568
|
+
if (!written.ok) {
|
|
42569
|
+
setRefused(written.error);
|
|
42570
|
+
return;
|
|
42571
|
+
}
|
|
42572
|
+
setSaved("Saved");
|
|
42587
42573
|
await load();
|
|
42588
42574
|
}
|
|
42589
42575
|
async function create(name) {
|
|
@@ -42600,15 +42586,15 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42600
42586
|
await load();
|
|
42601
42587
|
return true;
|
|
42602
42588
|
}
|
|
42603
|
-
const [started, setStarted] =
|
|
42604
|
-
|
|
42589
|
+
const [started, setStarted] = useState110(false);
|
|
42590
|
+
useEffect96(() => {
|
|
42605
42591
|
if (!startWithOne || started || forms === null || forms.length > 0) return;
|
|
42606
42592
|
if (askableFields(fields.data ?? []).length === 0) return;
|
|
42607
42593
|
if (!table.data && !table.error) return;
|
|
42608
42594
|
setStarted(true);
|
|
42609
42595
|
void create(table.data?.name?.trim() ? `${table.data.name.trim()} form` : "Form 1");
|
|
42610
42596
|
}, [startWithOne, started, forms, fields.data, create, table.data, table.error]);
|
|
42611
|
-
|
|
42597
|
+
useEffect96(() => {
|
|
42612
42598
|
if (!createOnMount || makeOnMount !== "waiting" || forms === null) return;
|
|
42613
42599
|
if (!table.data && !table.error) return;
|
|
42614
42600
|
if (askableFields(fields.data ?? []).length === 0) {
|
|
@@ -42621,396 +42607,320 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42621
42607
|
(made) => setMakeOnMount(made ? "made" : "not-made")
|
|
42622
42608
|
);
|
|
42623
42609
|
}, [createOnMount, makeOnMount, forms, fields.data, create, table.data, table.error]);
|
|
42624
|
-
if (fields.error) return /* @__PURE__ */
|
|
42610
|
+
if (fields.error) return /* @__PURE__ */ jsx133(RefusalNotice, { error: fields.error, className });
|
|
42625
42611
|
if (error) {
|
|
42626
|
-
return /* @__PURE__ */
|
|
42612
|
+
return /* @__PURE__ */ jsx133(
|
|
42627
42613
|
RefusalNotice,
|
|
42628
42614
|
{
|
|
42629
42615
|
error,
|
|
42630
42616
|
className,
|
|
42631
|
-
actions: /* @__PURE__ */
|
|
42617
|
+
actions: /* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
42632
42618
|
}
|
|
42633
42619
|
);
|
|
42634
42620
|
}
|
|
42635
|
-
if (fields.loading || forms === null) return /* @__PURE__ */
|
|
42621
|
+
if (fields.loading || forms === null) return /* @__PURE__ */ jsx133(Skeleton29, { className: cn78("h-64 w-full", className) });
|
|
42636
42622
|
if (createOnMount && (makeOnMount === "waiting" || makeOnMount === "making") && !refused) {
|
|
42637
|
-
return /* @__PURE__ */
|
|
42623
|
+
return /* @__PURE__ */ jsx133(Skeleton29, { className: cn78("h-64 w-full", className) });
|
|
42638
42624
|
}
|
|
42639
42625
|
if (!rights.admin) {
|
|
42640
|
-
return /* @__PURE__ */
|
|
42626
|
+
return /* @__PURE__ */ jsx133("p", { className: cn78("text-xs text-muted-foreground", className), children: rights.why("structure") });
|
|
42641
42627
|
}
|
|
42642
|
-
return
|
|
42643
|
-
|
|
42644
|
-
|
|
42645
|
-
|
|
42646
|
-
|
|
42647
|
-
|
|
42648
|
-
|
|
42649
|
-
|
|
42650
|
-
|
|
42651
|
-
|
|
42652
|
-
|
|
42653
|
-
|
|
42628
|
+
return (
|
|
42629
|
+
// THE CONTAINER IS THE PARENT (TYPEFORM-2): a container query asks an ANCESTOR, so `@3xl:` on the
|
|
42630
|
+
// element that is itself `@container` never fired and the preview sat 3,000 px below the editor.
|
|
42631
|
+
/* @__PURE__ */ jsx133("div", { className: cn78("@container min-h-0", className), children: /* @__PURE__ */ jsxs123("div", { className: "flex min-h-0 flex-col gap-3 @3xl:flex-row @3xl:items-start", children: [
|
|
42632
|
+
/* @__PURE__ */ jsxs123("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
|
|
42633
|
+
/* @__PURE__ */ jsxs123("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Forms", children: [
|
|
42634
|
+
forms.map((form) => /* @__PURE__ */ jsx133(
|
|
42635
|
+
Button86,
|
|
42636
|
+
{
|
|
42637
|
+
size: "sm",
|
|
42638
|
+
variant: form.id === activeId ? "secondary" : "ghost",
|
|
42639
|
+
"aria-pressed": form.id === activeId,
|
|
42640
|
+
onClick: () => {
|
|
42641
|
+
setActiveId(form.id);
|
|
42642
|
+
setDraft(form);
|
|
42643
|
+
},
|
|
42644
|
+
children: form.name
|
|
42654
42645
|
},
|
|
42655
|
-
|
|
42656
|
-
|
|
42657
|
-
|
|
42658
|
-
|
|
42659
|
-
|
|
42660
|
-
|
|
42661
|
-
|
|
42662
|
-
|
|
42663
|
-
|
|
42664
|
-
|
|
42665
|
-
|
|
42666
|
-
|
|
42667
|
-
|
|
42668
|
-
|
|
42669
|
-
|
|
42670
|
-
|
|
42671
|
-
|
|
42672
|
-
|
|
42673
|
-
|
|
42674
|
-
|
|
42675
|
-
|
|
42676
|
-
/* @__PURE__ */ jsx134(Button87, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
42677
|
-
] })
|
|
42678
|
-
] }),
|
|
42679
|
-
refused && !isLookRefusal(refused) ? /* @__PURE__ */ jsx134(
|
|
42680
|
-
RefusalNotice,
|
|
42681
|
-
{
|
|
42682
|
-
error: refused,
|
|
42683
|
-
onKeepEditing: () => setRefused(null),
|
|
42684
|
-
onDiscard: () => {
|
|
42685
|
-
const kept = forms.find((f) => f.id === activeId) ?? null;
|
|
42686
|
-
if (kept) dropKeptDraft(kept.id);
|
|
42687
|
-
setDraft(kept);
|
|
42688
|
-
setRestoredAt(null);
|
|
42689
|
-
setSaved(null);
|
|
42690
|
-
setRefused(null);
|
|
42691
|
-
}
|
|
42692
|
-
}
|
|
42693
|
-
) : null,
|
|
42694
|
-
restoredAt && dirty && stored ? /* @__PURE__ */ jsxs124("p", { className: "flex items-center gap-2 text-xs text-muted-foreground", "data-records-form-restored": "", children: [
|
|
42695
|
-
/* @__PURE__ */ jsxs124("span", { children: [
|
|
42696
|
-
"Unsaved changes from",
|
|
42697
|
-
" ",
|
|
42698
|
-
new Date(restoredAt).toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" }),
|
|
42699
|
-
" restored"
|
|
42646
|
+
form.id
|
|
42647
|
+
)),
|
|
42648
|
+
(fields.data ?? []).length > 0 ? /* @__PURE__ */ jsx133(
|
|
42649
|
+
Button86,
|
|
42650
|
+
{
|
|
42651
|
+
size: "sm",
|
|
42652
|
+
variant: "ghost",
|
|
42653
|
+
onClick: () => void create(
|
|
42654
|
+
freshFormName(
|
|
42655
|
+
table.data?.name?.trim() ? `${table.data.name.trim()} form` : "Form",
|
|
42656
|
+
forms.map((f) => f.name)
|
|
42657
|
+
)
|
|
42658
|
+
),
|
|
42659
|
+
children: "New form"
|
|
42660
|
+
}
|
|
42661
|
+
) : null,
|
|
42662
|
+
/* @__PURE__ */ jsxs123("span", { className: "ml-auto flex items-center gap-2", children: [
|
|
42663
|
+
saved ? /* @__PURE__ */ jsx133("span", { className: "text-xs text-muted-foreground", children: saved }) : null,
|
|
42664
|
+
stored ? /* @__PURE__ */ jsx133(Button86, { size: "sm", variant: showResults ? "default" : "ghost", onClick: () => setShowResults((v) => !v), "data-records-form-results-toggle": "", children: "Results" }) : null,
|
|
42665
|
+
/* @__PURE__ */ jsx133(Button86, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
42666
|
+
] })
|
|
42700
42667
|
] }),
|
|
42701
|
-
/* @__PURE__ */
|
|
42702
|
-
|
|
42668
|
+
refused && !isLookRefusal(refused) ? /* @__PURE__ */ jsx133(
|
|
42669
|
+
RefusalNotice,
|
|
42703
42670
|
{
|
|
42704
|
-
|
|
42705
|
-
|
|
42706
|
-
|
|
42707
|
-
|
|
42708
|
-
|
|
42671
|
+
error: refused,
|
|
42672
|
+
onKeepEditing: () => setRefused(null),
|
|
42673
|
+
onDiscard: () => {
|
|
42674
|
+
const kept = forms.find((f) => f.id === activeId) ?? null;
|
|
42675
|
+
if (kept) dropKeptDraft(kept.id);
|
|
42676
|
+
setDraft(kept);
|
|
42709
42677
|
setRestoredAt(null);
|
|
42710
|
-
|
|
42711
|
-
|
|
42712
|
-
|
|
42713
|
-
)
|
|
42714
|
-
] }) : null,
|
|
42715
|
-
!draft ? /* @__PURE__ */ jsx134("p", { className: "text-xs text-muted-foreground", children: (fields.data ?? []).length === 0 ? "This table has no fields yet, so there is nothing a form could ask for. Add a field first \u2014 every question is one of this table's own fields." : 'No form collects into this table yet. "New form" makes one, or you ask an agent for the whole thing in a sentence.' }) : /* @__PURE__ */ jsxs124(Fragment54, { children: [
|
|
42716
|
-
/* @__PURE__ */ jsx134(
|
|
42717
|
-
PublishRow,
|
|
42718
|
-
{
|
|
42719
|
-
url: `${publicOrigin}${publicFormPath(draft.id)}`,
|
|
42720
|
-
state: draft.state,
|
|
42721
|
-
mayPublish: rights.structure,
|
|
42722
|
-
why: rights.why("structure"),
|
|
42723
|
-
busy: publishing,
|
|
42724
|
-
copied,
|
|
42725
|
-
shownUrl,
|
|
42726
|
-
onPublish: (wanted) => void publish(wanted),
|
|
42727
|
-
onCopy: (url) => void copyLink(url),
|
|
42728
|
-
unsaved: dirty
|
|
42678
|
+
setSaved(null);
|
|
42679
|
+
setRefused(null);
|
|
42680
|
+
}
|
|
42729
42681
|
}
|
|
42730
|
-
),
|
|
42731
|
-
/* @__PURE__ */
|
|
42732
|
-
|
|
42733
|
-
|
|
42734
|
-
|
|
42735
|
-
|
|
42682
|
+
) : null,
|
|
42683
|
+
restoredAt && dirty && stored ? /* @__PURE__ */ jsxs123("p", { className: "flex items-center gap-2 text-xs text-muted-foreground", "data-records-form-restored": "", children: [
|
|
42684
|
+
/* @__PURE__ */ jsxs123("span", { children: [
|
|
42685
|
+
"Unsaved changes from",
|
|
42686
|
+
" ",
|
|
42687
|
+
new Date(restoredAt).toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" }),
|
|
42688
|
+
" restored"
|
|
42689
|
+
] }),
|
|
42690
|
+
/* @__PURE__ */ jsx133(
|
|
42691
|
+
Button86,
|
|
42736
42692
|
{
|
|
42737
|
-
|
|
42738
|
-
|
|
42739
|
-
|
|
42693
|
+
size: "sm",
|
|
42694
|
+
variant: "ghost",
|
|
42695
|
+
onClick: () => {
|
|
42696
|
+
dropKeptDraft(stored.id);
|
|
42697
|
+
setDraft(stored);
|
|
42698
|
+
setRestoredAt(null);
|
|
42699
|
+
},
|
|
42700
|
+
children: "Discard"
|
|
42740
42701
|
}
|
|
42741
42702
|
)
|
|
42742
|
-
] }),
|
|
42743
|
-
/* @__PURE__ */
|
|
42744
|
-
/* @__PURE__ */
|
|
42745
|
-
|
|
42746
|
-
|
|
42747
|
-
|
|
42748
|
-
|
|
42749
|
-
|
|
42750
|
-
|
|
42751
|
-
|
|
42752
|
-
|
|
42753
|
-
|
|
42754
|
-
|
|
42755
|
-
)
|
|
42756
|
-
|
|
42757
|
-
|
|
42758
|
-
|
|
42759
|
-
|
|
42760
|
-
|
|
42703
|
+
] }) : null,
|
|
42704
|
+
!draft ? /* @__PURE__ */ jsx133("p", { className: "text-xs text-muted-foreground", children: (fields.data ?? []).length === 0 ? "No fields yet" : "No form yet" }) : /* @__PURE__ */ jsxs123(Fragment54, { children: [
|
|
42705
|
+
/* @__PURE__ */ jsx133(
|
|
42706
|
+
PublishRow,
|
|
42707
|
+
{
|
|
42708
|
+
url: `${publicOrigin}${publicFormPath(draft.id)}`,
|
|
42709
|
+
state: draft.state,
|
|
42710
|
+
mayPublish: rights.structure,
|
|
42711
|
+
why: rights.why("structure"),
|
|
42712
|
+
busy: publishing,
|
|
42713
|
+
copied,
|
|
42714
|
+
shownUrl,
|
|
42715
|
+
onPublish: (wanted) => void publish(wanted),
|
|
42716
|
+
onCopy: (url) => void copyLink(url),
|
|
42717
|
+
unsaved: dirty
|
|
42718
|
+
}
|
|
42719
|
+
),
|
|
42720
|
+
/* @__PURE__ */ jsx133(EmbedSnippets, { url: `${publicOrigin}${publicFormPath(draft.id)}`, label: draft.name }),
|
|
42721
|
+
/* @__PURE__ */ jsx133(
|
|
42722
|
+
NotifyOnAnswer,
|
|
42723
|
+
{
|
|
42724
|
+
tableId,
|
|
42725
|
+
tableName: table.data?.name ?? "record",
|
|
42726
|
+
formName: draft.name,
|
|
42727
|
+
ruleId: draft.notifyRuleId,
|
|
42728
|
+
notify: draft.notify ?? null,
|
|
42729
|
+
onRule: (ruleId) => void chooseNotify(ruleId),
|
|
42730
|
+
onNotify: (notify) => void chooseWebhook(notify)
|
|
42731
|
+
}
|
|
42732
|
+
),
|
|
42733
|
+
/* @__PURE__ */ jsxs123("section", { className: "flex flex-wrap items-center gap-x-4 gap-y-1 rounded border px-2.5 py-2 text-xs", "aria-label": "Options", children: [
|
|
42734
|
+
/* @__PURE__ */ jsxs123("label", { className: "flex items-center gap-2", title: "Off hides your logo and name \u2014 for a client's form.", children: [
|
|
42735
|
+
/* @__PURE__ */ jsx133(
|
|
42736
|
+
Checkbox15,
|
|
42761
42737
|
{
|
|
42762
|
-
|
|
42763
|
-
|
|
42764
|
-
|
|
42765
|
-
placeholder: "https://",
|
|
42766
|
-
onChange: (e) => patch({ thankYou: { ...draft.thankYou, redirectUrl: e.target.value || null } })
|
|
42738
|
+
checked: draft.showOwnerHeader ?? true,
|
|
42739
|
+
onCheckedChange: (checked) => patch({ showOwnerHeader: checked === true }),
|
|
42740
|
+
"aria-label": "Show our logo and name"
|
|
42767
42741
|
}
|
|
42768
|
-
)
|
|
42742
|
+
),
|
|
42743
|
+
"Show our logo and name"
|
|
42769
42744
|
] }),
|
|
42770
|
-
/* @__PURE__ */
|
|
42771
|
-
/* @__PURE__ */
|
|
42772
|
-
|
|
42773
|
-
BasicTextarea11,
|
|
42745
|
+
/* @__PURE__ */ jsxs123("label", { className: "flex items-center gap-2", title: "A single choice goes on to the next question when picked.", children: [
|
|
42746
|
+
/* @__PURE__ */ jsx133(
|
|
42747
|
+
Checkbox15,
|
|
42774
42748
|
{
|
|
42775
|
-
|
|
42776
|
-
|
|
42777
|
-
|
|
42778
|
-
onChange: (e) => patch({ thankYou: { ...draft.thankYou, body: e.target.value || null } })
|
|
42749
|
+
checked: draft.autoAdvance ?? true,
|
|
42750
|
+
onCheckedChange: (checked) => patch({ autoAdvance: checked === true }),
|
|
42751
|
+
"aria-label": "Next after a choice"
|
|
42779
42752
|
}
|
|
42780
|
-
)
|
|
42753
|
+
),
|
|
42754
|
+
"Next after a choice"
|
|
42781
42755
|
] })
|
|
42782
|
-
] })
|
|
42783
|
-
] }),
|
|
42784
|
-
/* @__PURE__ */ jsx134(WelcomeEditor, { value: draft.welcome, pictures: lookOptions?.pictures ?? null, onChange: (welcome) => patch({ welcome }) }),
|
|
42785
|
-
/* @__PURE__ */ jsx134(
|
|
42786
|
-
EndingsEditor,
|
|
42787
|
-
{
|
|
42788
|
-
endings: draft.endings ?? [],
|
|
42789
|
-
pictures: lookOptions?.pictures ?? null,
|
|
42790
|
-
fieldKeys: conditionFields,
|
|
42791
|
-
scored: draft.questions.some((q) => q.points),
|
|
42792
|
-
onChange: (endings) => patch({ endings })
|
|
42793
|
-
}
|
|
42794
|
-
),
|
|
42795
|
-
/* @__PURE__ */ jsxs124("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
42796
|
-
/* @__PURE__ */ jsxs124("label", { className: "flex flex-col gap-1", children: [
|
|
42797
|
-
/* @__PURE__ */ jsx134(Label22, { className: "text-xs font-medium", children: "Name" }),
|
|
42798
|
-
/* @__PURE__ */ jsx134(BasicInput28, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
|
|
42799
|
-
] }),
|
|
42800
|
-
/* @__PURE__ */ jsxs124("label", { className: "flex flex-col gap-1", children: [
|
|
42801
|
-
/* @__PURE__ */ jsx134(Label22, { className: "text-xs font-medium", children: "Flow" }),
|
|
42802
|
-
/* @__PURE__ */ jsx134(
|
|
42803
|
-
"select",
|
|
42804
|
-
{
|
|
42805
|
-
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
42806
|
-
value: draft.flow ?? "one-at-a-time",
|
|
42807
|
-
onChange: (e) => patch({ flow: e.target.value }),
|
|
42808
|
-
children: FORM_FLOWS.map((flow) => /* @__PURE__ */ jsx134("option", { value: flow, children: flow === "one-at-a-time" ? "One question at a time" : "All on one page" }, flow))
|
|
42809
|
-
}
|
|
42810
|
-
)
|
|
42811
42756
|
] }),
|
|
42812
|
-
/* @__PURE__ */
|
|
42813
|
-
/* @__PURE__ */
|
|
42814
|
-
/* @__PURE__ */
|
|
42815
|
-
|
|
42816
|
-
|
|
42817
|
-
|
|
42818
|
-
|
|
42819
|
-
|
|
42820
|
-
|
|
42821
|
-
|
|
42822
|
-
|
|
42823
|
-
|
|
42824
|
-
|
|
42825
|
-
|
|
42826
|
-
|
|
42827
|
-
|
|
42828
|
-
|
|
42829
|
-
|
|
42830
|
-
|
|
42831
|
-
|
|
42832
|
-
|
|
42757
|
+
/* @__PURE__ */ jsxs123("section", { className: "flex flex-col gap-2 rounded border px-2.5 py-2", "aria-label": "After somebody sends it", children: [
|
|
42758
|
+
/* @__PURE__ */ jsx133("span", { className: "text-xs font-medium", title: "What people see after sending, and an optional page they go on to.", children: "After sending" }),
|
|
42759
|
+
/* @__PURE__ */ jsxs123("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
42760
|
+
/* @__PURE__ */ jsxs123("label", { className: "flex flex-col gap-1", children: [
|
|
42761
|
+
/* @__PURE__ */ jsx133(Label23, { className: "text-xs font-medium", children: "Thank-you title" }),
|
|
42762
|
+
/* @__PURE__ */ jsx133(
|
|
42763
|
+
BasicInput28,
|
|
42764
|
+
{
|
|
42765
|
+
value: draft.thankYou?.title ?? "",
|
|
42766
|
+
placeholder: "Thank you",
|
|
42767
|
+
onChange: (e) => patch({ thankYou: { ...draft.thankYou, title: e.target.value || null } })
|
|
42768
|
+
}
|
|
42769
|
+
)
|
|
42770
|
+
] }),
|
|
42771
|
+
/* @__PURE__ */ jsxs123("label", { className: "flex flex-col gap-1", children: [
|
|
42772
|
+
/* @__PURE__ */ jsx133(Label23, { className: "text-xs font-medium", children: "Then go to (optional)" }),
|
|
42773
|
+
/* @__PURE__ */ jsx133(
|
|
42774
|
+
BasicInput28,
|
|
42775
|
+
{
|
|
42776
|
+
type: "url",
|
|
42777
|
+
inputMode: "url",
|
|
42778
|
+
value: draft.thankYou?.redirectUrl ?? "",
|
|
42779
|
+
placeholder: "https://",
|
|
42780
|
+
onChange: (e) => patch({ thankYou: { ...draft.thankYou, redirectUrl: e.target.value || null } })
|
|
42781
|
+
}
|
|
42782
|
+
)
|
|
42783
|
+
] }),
|
|
42784
|
+
/* @__PURE__ */ jsxs123("label", { className: "col-span-2 flex flex-col gap-1", children: [
|
|
42785
|
+
/* @__PURE__ */ jsx133(Label23, { className: "text-xs font-medium", children: "Thank-you message" }),
|
|
42786
|
+
/* @__PURE__ */ jsx133(
|
|
42787
|
+
BasicTextarea11,
|
|
42788
|
+
{
|
|
42789
|
+
rows: 2,
|
|
42790
|
+
value: draft.thankYou?.body ?? "",
|
|
42791
|
+
placeholder: "What happens next, in your words.",
|
|
42792
|
+
onChange: (e) => patch({ thankYou: { ...draft.thankYou, body: e.target.value || null } })
|
|
42793
|
+
}
|
|
42794
|
+
)
|
|
42795
|
+
] })
|
|
42796
|
+
] })
|
|
42833
42797
|
] }),
|
|
42834
|
-
/* @__PURE__ */
|
|
42835
|
-
|
|
42836
|
-
|
|
42837
|
-
/* @__PURE__ */ jsx134(Label22, { className: "text-xs font-medium", children: "Look" }),
|
|
42838
|
-
/* @__PURE__ */ jsx134(
|
|
42839
|
-
AccentSwatches,
|
|
42798
|
+
/* @__PURE__ */ jsx133(WelcomeEditor, { value: draft.welcome, pictures: lookOptions?.pictures ?? null, onChange: (welcome) => patch({ welcome }) }),
|
|
42799
|
+
/* @__PURE__ */ jsx133(
|
|
42800
|
+
EndingsEditor,
|
|
42840
42801
|
{
|
|
42841
|
-
|
|
42842
|
-
|
|
42843
|
-
|
|
42802
|
+
endings: draft.endings ?? [],
|
|
42803
|
+
pictures: lookOptions?.pictures ?? null,
|
|
42804
|
+
fieldKeys: conditionFields,
|
|
42805
|
+
scored: draft.questions.some((q) => q.points),
|
|
42806
|
+
onChange: (endings) => patch({ endings })
|
|
42844
42807
|
}
|
|
42845
42808
|
),
|
|
42846
|
-
/* @__PURE__ */
|
|
42847
|
-
|
|
42848
|
-
|
|
42849
|
-
/* @__PURE__ */
|
|
42850
|
-
|
|
42851
|
-
|
|
42809
|
+
/* @__PURE__ */ jsxs123("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
42810
|
+
/* @__PURE__ */ jsxs123("label", { className: "flex flex-col gap-1", children: [
|
|
42811
|
+
/* @__PURE__ */ jsx133(Label23, { className: "text-xs font-medium", children: "Name" }),
|
|
42812
|
+
/* @__PURE__ */ jsx133(BasicInput28, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
|
|
42813
|
+
] }),
|
|
42814
|
+
/* @__PURE__ */ jsxs123("label", { className: "flex flex-col gap-1", children: [
|
|
42815
|
+
/* @__PURE__ */ jsx133(Label23, { className: "text-xs font-medium", children: "Flow" }),
|
|
42816
|
+
/* @__PURE__ */ jsx133(
|
|
42817
|
+
"select",
|
|
42852
42818
|
{
|
|
42853
|
-
|
|
42854
|
-
|
|
42855
|
-
|
|
42856
|
-
|
|
42857
|
-
onChange: (id) => patchLook({ logoFileId: id || null })
|
|
42819
|
+
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
42820
|
+
value: draft.flow ?? "one-at-a-time",
|
|
42821
|
+
onChange: (e) => patch({ flow: e.target.value }),
|
|
42822
|
+
children: FORM_FLOWS.map((flow) => /* @__PURE__ */ jsx133("option", { value: flow, children: flow === "one-at-a-time" ? "One question at a time" : "All on one page" }, flow))
|
|
42858
42823
|
}
|
|
42859
42824
|
)
|
|
42860
42825
|
] }),
|
|
42861
|
-
/* @__PURE__ */
|
|
42862
|
-
/* @__PURE__ */
|
|
42863
|
-
/* @__PURE__ */
|
|
42864
|
-
|
|
42826
|
+
/* @__PURE__ */ jsxs123("label", { className: "col-span-2 flex flex-col gap-1", children: [
|
|
42827
|
+
/* @__PURE__ */ jsx133(Label23, { className: "text-xs font-medium", children: "Intro" }),
|
|
42828
|
+
/* @__PURE__ */ jsx133(
|
|
42829
|
+
BasicTextarea11,
|
|
42865
42830
|
{
|
|
42866
|
-
|
|
42867
|
-
|
|
42868
|
-
|
|
42869
|
-
noneLabel: "No cover",
|
|
42870
|
-
onChange: (id) => patchLook({ coverFileId: id || null })
|
|
42831
|
+
rows: 2,
|
|
42832
|
+
value: draft.intro ?? "",
|
|
42833
|
+
onChange: (e) => patch({ intro: e.target.value || null })
|
|
42871
42834
|
}
|
|
42872
42835
|
)
|
|
42873
|
-
] })
|
|
42874
|
-
|
|
42875
|
-
|
|
42876
|
-
|
|
42877
|
-
|
|
42878
|
-
|
|
42879
|
-
|
|
42880
|
-
|
|
42881
|
-
|
|
42882
|
-
|
|
42883
|
-
|
|
42884
|
-
|
|
42885
|
-
|
|
42886
|
-
] })
|
|
42836
|
+
] }),
|
|
42837
|
+
/* @__PURE__ */ jsxs123("label", { className: "flex flex-col gap-1", children: [
|
|
42838
|
+
/* @__PURE__ */ jsx133(Label23, { className: "text-xs font-medium", children: "Submit button" }),
|
|
42839
|
+
/* @__PURE__ */ jsx133(
|
|
42840
|
+
BasicInput28,
|
|
42841
|
+
{
|
|
42842
|
+
value: draft.submitLabel ?? "",
|
|
42843
|
+
placeholder: "Submit",
|
|
42844
|
+
onChange: (e) => patch({ submitLabel: e.target.value || null })
|
|
42845
|
+
}
|
|
42846
|
+
)
|
|
42847
|
+
] }),
|
|
42848
|
+
/* @__PURE__ */ jsx133(HiddenFieldsEditor, { value: draft.hiddenFields ?? [], onChange: (hiddenFields) => patch({ hiddenFields }) })
|
|
42887
42849
|
] }),
|
|
42888
|
-
|
|
42889
|
-
|
|
42890
|
-
|
|
42891
|
-
|
|
42892
|
-
|
|
42893
|
-
|
|
42894
|
-
|
|
42895
|
-
|
|
42850
|
+
/* @__PURE__ */ jsxs123("div", { className: "flex flex-col gap-1.5", "data-records-form-look-editor": "", children: [
|
|
42851
|
+
/* @__PURE__ */ jsx133(Label23, { className: "text-xs font-medium", children: "Look" }),
|
|
42852
|
+
/* @__PURE__ */ jsx133(
|
|
42853
|
+
AccentSwatches,
|
|
42854
|
+
{
|
|
42855
|
+
value: draft.look?.accent ?? "",
|
|
42856
|
+
noneLabel: "Organization colour",
|
|
42857
|
+
onChange: (accent) => patchLook({ accent: accent || null })
|
|
42858
|
+
}
|
|
42859
|
+
),
|
|
42860
|
+
/* @__PURE__ */ jsx133(ThemeEditor, { theme: draft.theme, pictures: lookOptions?.pictures ?? null, onChange: (theme) => patch({ theme }) }),
|
|
42861
|
+
lookOptions ? /* @__PURE__ */ jsxs123("div", { className: "flex flex-col gap-2", children: [
|
|
42862
|
+
/* @__PURE__ */ jsxs123("div", { className: "flex flex-col gap-1", children: [
|
|
42863
|
+
/* @__PURE__ */ jsx133("span", { className: "text-xs", children: "Logo" }),
|
|
42864
|
+
/* @__PURE__ */ jsx133(
|
|
42865
|
+
PublicPicturePicker,
|
|
42896
42866
|
{
|
|
42897
|
-
|
|
42898
|
-
|
|
42899
|
-
|
|
42900
|
-
|
|
42901
|
-
})
|
|
42867
|
+
label: "Logo",
|
|
42868
|
+
pictures: lookOptions.pictures,
|
|
42869
|
+
value: draft.look?.logoFileId ?? "",
|
|
42870
|
+
noneLabel: "Organization logo",
|
|
42871
|
+
onChange: (id) => patchLook({ logoFileId: id || null })
|
|
42902
42872
|
}
|
|
42903
|
-
)
|
|
42904
|
-
/* @__PURE__ */ jsx134("span", { className: "text-xs font-medium", children: fieldName(field2) }),
|
|
42905
|
-
field2.required ? /* @__PURE__ */ jsx134("span", { className: "text-xs text-destructive", children: "required" }) : null
|
|
42873
|
+
)
|
|
42906
42874
|
] }),
|
|
42907
|
-
|
|
42908
|
-
/* @__PURE__ */
|
|
42909
|
-
|
|
42910
|
-
|
|
42911
|
-
className: "h-8 text-xs",
|
|
42912
|
-
placeholder: fieldName(field2),
|
|
42913
|
-
"aria-label": `How to ask for ${fieldName(field2)}`,
|
|
42914
|
-
value: question.ask ?? "",
|
|
42915
|
-
onChange: (e) => patchQuestion(index, { ask: e.target.value || null })
|
|
42916
|
-
}
|
|
42917
|
-
),
|
|
42918
|
-
/* @__PURE__ */ jsx134(
|
|
42919
|
-
BasicInput28,
|
|
42920
|
-
{
|
|
42921
|
-
className: "h-8 text-xs",
|
|
42922
|
-
placeholder: "Help text",
|
|
42923
|
-
"aria-label": `Help text for ${fieldName(field2)}`,
|
|
42924
|
-
value: question.help ?? "",
|
|
42925
|
-
onChange: (e) => patchQuestion(index, { help: e.target.value || null })
|
|
42926
|
-
}
|
|
42927
|
-
),
|
|
42928
|
-
/* @__PURE__ */ jsxs124("label", { className: "col-span-2 flex items-center gap-2 text-xs", children: [
|
|
42929
|
-
/* @__PURE__ */ jsx134(
|
|
42930
|
-
Checkbox15,
|
|
42931
|
-
{
|
|
42932
|
-
checked: field2.required ? true : question.required === true,
|
|
42933
|
-
disabled: field2.required === true,
|
|
42934
|
-
"aria-label": `${fieldName(field2)} is required`,
|
|
42935
|
-
onCheckedChange: (checked) => patchQuestion(index, { required: checked === true ? true : null })
|
|
42936
|
-
}
|
|
42937
|
-
),
|
|
42938
|
-
"Required"
|
|
42939
|
-
] }),
|
|
42940
|
-
/* @__PURE__ */ jsx134(
|
|
42941
|
-
Condition,
|
|
42942
|
-
{
|
|
42943
|
-
question,
|
|
42944
|
-
fieldKeys: (fields.data ?? []).map((f) => ({
|
|
42945
|
-
id: String(f.id),
|
|
42946
|
-
key: f.key,
|
|
42947
|
-
label: f.label || f.key,
|
|
42948
|
-
field: f
|
|
42949
|
-
})),
|
|
42950
|
-
onChange: (showIf) => patchQuestion(index, { showIf })
|
|
42951
|
-
}
|
|
42952
|
-
),
|
|
42953
|
-
/* @__PURE__ */ jsx134(
|
|
42954
|
-
QuestionLogic,
|
|
42875
|
+
/* @__PURE__ */ jsxs123("div", { className: "flex flex-col gap-1", children: [
|
|
42876
|
+
/* @__PURE__ */ jsx133("span", { className: "text-xs", children: "Cover" }),
|
|
42877
|
+
/* @__PURE__ */ jsx133(
|
|
42878
|
+
PublicPicturePicker,
|
|
42955
42879
|
{
|
|
42956
|
-
|
|
42957
|
-
|
|
42958
|
-
|
|
42959
|
-
|
|
42960
|
-
|
|
42961
|
-
fieldKeys: conditionFields,
|
|
42962
|
-
label: (key) => {
|
|
42963
|
-
const f = byKey.get(key);
|
|
42964
|
-
return draft.questions.find((q) => q.field === key)?.ask || (f ? fieldName(f) : key);
|
|
42965
|
-
},
|
|
42966
|
-
onChange: (part) => patchQuestion(index, part)
|
|
42880
|
+
label: "Cover",
|
|
42881
|
+
pictures: lookOptions.pictures,
|
|
42882
|
+
value: draft.look?.coverFileId ?? "",
|
|
42883
|
+
noneLabel: "No cover",
|
|
42884
|
+
onChange: (id) => patchLook({ coverFileId: id || null })
|
|
42967
42885
|
}
|
|
42968
42886
|
)
|
|
42969
|
-
] })
|
|
42970
|
-
] },
|
|
42971
|
-
|
|
42972
|
-
|
|
42973
|
-
|
|
42974
|
-
|
|
42975
|
-
|
|
42976
|
-
className: "rounded border border-destructive/40 bg-destructive/5 px-2 py-1.5 text-xs text-destructive",
|
|
42977
|
-
message: requiredLeftUnasked(fields.data ?? [], draft.questions.map((q) => q.field)) ?? "",
|
|
42978
|
-
operation: "Build a form",
|
|
42979
|
-
origin: "records-ui/FormBuilder"
|
|
42980
|
-
}
|
|
42981
|
-
) : null,
|
|
42982
|
-
draft.questions.filter((q) => !byKey.has(q.field)).map((q) => /* @__PURE__ */ jsxs124("p", { className: "text-xs text-destructive", children: [
|
|
42983
|
-
'This form asks for "',
|
|
42984
|
-
q.field,
|
|
42985
|
-
'" and this table has no such Field. It is shown here rather than quietly dropped.'
|
|
42986
|
-
] }, q.field)),
|
|
42987
|
-
adding ? /* @__PURE__ */ jsx134("div", { className: "rounded border p-2", "data-records-form-add-question": "", children: /* @__PURE__ */ jsx134(
|
|
42988
|
-
FieldEditor,
|
|
42887
|
+
] })
|
|
42888
|
+
] }) : lookUnread ? null : /* @__PURE__ */ jsx133(Skeleton29, { className: "h-12 w-full" }),
|
|
42889
|
+
lookUnread ? /* @__PURE__ */ jsx133(RefusalLine, { error: lookUnread }) : null,
|
|
42890
|
+
refused && isLookRefusal(refused) ? /* @__PURE__ */ jsx133(RefusalLine, { error: refused }) : null
|
|
42891
|
+
] }),
|
|
42892
|
+
/* @__PURE__ */ jsx133(
|
|
42893
|
+
QuestionList,
|
|
42989
42894
|
{
|
|
42990
42895
|
tableId,
|
|
42991
|
-
|
|
42992
|
-
|
|
42993
|
-
|
|
42896
|
+
draft,
|
|
42897
|
+
fields: fields.data ?? [],
|
|
42898
|
+
byKey,
|
|
42899
|
+
adding,
|
|
42900
|
+
onAdding: (on) => {
|
|
42901
|
+
keysBeforeAdd.current = on ? new Set((fields.data ?? []).map((f) => f.key)) : null;
|
|
42902
|
+
setAdding(on);
|
|
42994
42903
|
},
|
|
42995
|
-
|
|
42904
|
+
onAdded: () => setAdding(false),
|
|
42905
|
+
onQuestions: (questions) => patch({ questions }),
|
|
42906
|
+
onQuestion: patchQuestion
|
|
42996
42907
|
}
|
|
42997
|
-
)
|
|
42998
|
-
Button87,
|
|
42999
|
-
{
|
|
43000
|
-
size: "sm",
|
|
43001
|
-
variant: "outline",
|
|
43002
|
-
onClick: () => {
|
|
43003
|
-
keysBeforeAdd.current = new Set((fields.data ?? []).map((f) => f.key));
|
|
43004
|
-
setAdding(true);
|
|
43005
|
-
},
|
|
43006
|
-
children: "Add a question"
|
|
43007
|
-
}
|
|
43008
|
-
) })
|
|
42908
|
+
)
|
|
43009
42909
|
] })
|
|
43010
|
-
] })
|
|
43011
|
-
|
|
43012
|
-
|
|
43013
|
-
|
|
42910
|
+
] }),
|
|
42911
|
+
draft ? (
|
|
42912
|
+
// BESIDE THE EDITOR, ALWAYS IN VIEW (TYPEFORM-2): it follows the scroll and updates as you type.
|
|
42913
|
+
/* @__PURE__ */ jsx133(
|
|
42914
|
+
"aside",
|
|
42915
|
+
{
|
|
42916
|
+
className: "w-full shrink-0 overflow-auto rounded border p-2 @3xl:sticky @3xl:top-2 @3xl:max-h-[calc(100dvh-6rem)] @3xl:w-[min(45%,520px)]",
|
|
42917
|
+
"data-records-form-preview": "",
|
|
42918
|
+
children: showResults && stored ? /* @__PURE__ */ jsx133(FormResultsPanel, { form: stored }) : /* @__PURE__ */ jsx133(FormLookFrame, { look: lookOptions?.look ?? null, title: draft.name, contained: true, showOwnerHeader: draft.showOwnerHeader ?? true, children: /* @__PURE__ */ jsx133(FormRunner, { form: draft, preview: true }) })
|
|
42919
|
+
}
|
|
42920
|
+
)
|
|
42921
|
+
) : null
|
|
42922
|
+
] }) })
|
|
42923
|
+
);
|
|
43014
42924
|
}
|
|
43015
42925
|
function PublishRow({
|
|
43016
42926
|
url,
|
|
@@ -43025,10 +42935,10 @@ function PublishRow({
|
|
|
43025
42935
|
unsaved = false
|
|
43026
42936
|
}) {
|
|
43027
42937
|
const open = state === "open";
|
|
43028
|
-
return /* @__PURE__ */
|
|
43029
|
-
/* @__PURE__ */
|
|
43030
|
-
mayPublish ? /* @__PURE__ */
|
|
43031
|
-
|
|
42938
|
+
return /* @__PURE__ */ jsxs123("div", { className: "flex flex-col gap-1.5 rounded border px-2.5 py-2", children: [
|
|
42939
|
+
/* @__PURE__ */ jsxs123("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
42940
|
+
mayPublish ? /* @__PURE__ */ jsx133(
|
|
42941
|
+
Button86,
|
|
43032
42942
|
{
|
|
43033
42943
|
size: "sm",
|
|
43034
42944
|
variant: open ? "ghost" : "default",
|
|
@@ -43037,8 +42947,8 @@ function PublishRow({
|
|
|
43037
42947
|
children: busy ? "\u2026" : open ? "Unpublish" : unsaved ? "Save and publish" : "Publish"
|
|
43038
42948
|
}
|
|
43039
42949
|
) : null,
|
|
43040
|
-
state !== "draft" ? /* @__PURE__ */
|
|
43041
|
-
/* @__PURE__ */
|
|
42950
|
+
state !== "draft" ? /* @__PURE__ */ jsxs123(Fragment54, { children: [
|
|
42951
|
+
/* @__PURE__ */ jsx133(
|
|
43042
42952
|
"a",
|
|
43043
42953
|
{
|
|
43044
42954
|
href: url,
|
|
@@ -43048,14 +42958,14 @@ function PublishRow({
|
|
|
43048
42958
|
children: url
|
|
43049
42959
|
}
|
|
43050
42960
|
),
|
|
43051
|
-
/* @__PURE__ */
|
|
42961
|
+
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "outline", onClick: () => onCopy(url), children: copied ? "Copied" : "Copy link" })
|
|
43052
42962
|
] }) : null
|
|
43053
42963
|
] }),
|
|
43054
|
-
/* @__PURE__ */
|
|
43055
|
-
open && unsaved ? /* @__PURE__ */
|
|
43056
|
-
!mayPublish ? /* @__PURE__ */
|
|
43057
|
-
shownUrl ? /* @__PURE__ */
|
|
43058
|
-
"
|
|
42964
|
+
/* @__PURE__ */ jsx133("p", { className: "w-fit text-xs text-muted-foreground", title: FORM_STATE_WORDS[state], "data-records-form-state": state, children: FORM_STATE_LABELS[state] }),
|
|
42965
|
+
open && unsaved ? /* @__PURE__ */ jsx133("p", { className: "text-xs text-muted-foreground", "data-records-form-unsaved": "", children: "Unsaved changes \u2014 the live form shows your last save" }) : null,
|
|
42966
|
+
!mayPublish ? /* @__PURE__ */ jsx133("p", { className: "w-fit text-xs text-muted-foreground", title: why, children: "Publishing needs table admin" }) : null,
|
|
42967
|
+
shownUrl ? /* @__PURE__ */ jsxs123("p", { className: "break-all rounded border border-dashed px-2 py-1 text-xs", title: "This browser would not copy for you.", children: [
|
|
42968
|
+
"Copy by hand: ",
|
|
43059
42969
|
shownUrl
|
|
43060
42970
|
] }) : null
|
|
43061
42971
|
] });
|
|
@@ -43065,7 +42975,7 @@ function Condition({
|
|
|
43065
42975
|
fieldKeys,
|
|
43066
42976
|
onChange
|
|
43067
42977
|
}) {
|
|
43068
|
-
return /* @__PURE__ */
|
|
42978
|
+
return /* @__PURE__ */ jsx133(
|
|
43069
42979
|
ConditionGroup,
|
|
43070
42980
|
{
|
|
43071
42981
|
lead: "Ask only when",
|
|
@@ -43076,26 +42986,326 @@ function Condition({
|
|
|
43076
42986
|
}
|
|
43077
42987
|
);
|
|
43078
42988
|
}
|
|
42989
|
+
var FORM_STATE_LABELS = {
|
|
42990
|
+
draft: "Not published",
|
|
42991
|
+
open: "Open",
|
|
42992
|
+
closed: "Closed",
|
|
42993
|
+
full: "Full"
|
|
42994
|
+
};
|
|
42995
|
+
function QuestionList({
|
|
42996
|
+
tableId,
|
|
42997
|
+
draft,
|
|
42998
|
+
fields,
|
|
42999
|
+
byKey,
|
|
43000
|
+
adding,
|
|
43001
|
+
onAdding,
|
|
43002
|
+
onAdded,
|
|
43003
|
+
onQuestions,
|
|
43004
|
+
onQuestion
|
|
43005
|
+
}) {
|
|
43006
|
+
const sensors = useSensors(
|
|
43007
|
+
useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
43008
|
+
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
|
43009
|
+
);
|
|
43010
|
+
const askable = askableFields([...fields]);
|
|
43011
|
+
const unasked = askable.filter((f) => !draft.questions.some((q) => q.field === f.key));
|
|
43012
|
+
const asked3 = draft.questions.map((q) => byKey.get(q.field)).filter((f) => Boolean(f)).map((f) => ({ id: String(f.id), key: f.key, label: draft.questions.find((q) => q.field === f.key)?.ask || fieldName(f), field: f }));
|
|
43013
|
+
const label = (key) => {
|
|
43014
|
+
const f = byKey.get(key);
|
|
43015
|
+
return draft.questions.find((q) => q.field === key)?.ask || (f ? fieldName(f) : key);
|
|
43016
|
+
};
|
|
43017
|
+
function dragEnd(event) {
|
|
43018
|
+
const from = draft.questions.findIndex((q) => q.field === event.active.id);
|
|
43019
|
+
const to = event.over ? draft.questions.findIndex((q) => q.field === event.over.id) : -1;
|
|
43020
|
+
if (from < 0 || to < 0 || from === to) return;
|
|
43021
|
+
onQuestions(arrayMove(draft.questions, from, to));
|
|
43022
|
+
}
|
|
43023
|
+
const notAskable = whyNotAskable([...fields]);
|
|
43024
|
+
const leftUnasked = requiredLeftUnasked([...fields], draft.questions.map((q) => q.field));
|
|
43025
|
+
return /* @__PURE__ */ jsxs123("div", { className: "flex flex-col gap-1.5", "data-records-form-questions": "", children: [
|
|
43026
|
+
/* @__PURE__ */ jsxs123("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
43027
|
+
/* @__PURE__ */ jsx133("span", { className: "font-medium text-foreground", children: "Questions" }),
|
|
43028
|
+
/* @__PURE__ */ jsx133("span", { className: "tabular-nums", children: draft.questions.length })
|
|
43029
|
+
] }),
|
|
43030
|
+
/* @__PURE__ */ jsx133(DndContext, { sensors, collisionDetection: closestCenter, onDragEnd: dragEnd, children: /* @__PURE__ */ jsx133(SortableContext, { items: draft.questions.map((q) => q.field), strategy: verticalListSortingStrategy, children: draft.questions.map((question, index) => /* @__PURE__ */ jsx133(
|
|
43031
|
+
QuestionRow,
|
|
43032
|
+
{
|
|
43033
|
+
index,
|
|
43034
|
+
question,
|
|
43035
|
+
field: byKey.get(question.field) ?? null,
|
|
43036
|
+
draft,
|
|
43037
|
+
conditionFields: asked3.filter((f) => f.key !== question.field),
|
|
43038
|
+
label,
|
|
43039
|
+
onChange: (change) => onQuestion(index, change),
|
|
43040
|
+
onRemove: () => onQuestions(draft.questions.filter((q) => q.field !== question.field))
|
|
43041
|
+
},
|
|
43042
|
+
question.field
|
|
43043
|
+
)) }) }),
|
|
43044
|
+
unasked.length > 0 ? /* @__PURE__ */ jsx133("div", { className: "flex flex-wrap items-center gap-1", "data-records-form-unasked": "", children: unasked.map((f) => /* @__PURE__ */ jsxs123(
|
|
43045
|
+
Button86,
|
|
43046
|
+
{
|
|
43047
|
+
size: "sm",
|
|
43048
|
+
variant: "outline",
|
|
43049
|
+
"aria-label": `Ask for ${fieldName(f)}`,
|
|
43050
|
+
onClick: () => onQuestions([...draft.questions, { field: f.key }]),
|
|
43051
|
+
children: [
|
|
43052
|
+
"+ ",
|
|
43053
|
+
fieldName(f)
|
|
43054
|
+
]
|
|
43055
|
+
},
|
|
43056
|
+
f.id
|
|
43057
|
+
)) }) : null,
|
|
43058
|
+
notAskable ? /* @__PURE__ */ jsx133("p", { className: "w-fit text-xs text-muted-foreground", title: notAskable, children: "Some fields cannot be asked publicly" }) : null,
|
|
43059
|
+
leftUnasked ? /* @__PURE__ */ jsx133(
|
|
43060
|
+
ErrorBox7,
|
|
43061
|
+
{
|
|
43062
|
+
className: "rounded border border-destructive/40 bg-destructive/5 px-2 py-1.5 text-xs text-destructive",
|
|
43063
|
+
message: leftUnasked,
|
|
43064
|
+
operation: "Build a form",
|
|
43065
|
+
origin: "records-ui/FormBuilder"
|
|
43066
|
+
}
|
|
43067
|
+
) : null,
|
|
43068
|
+
adding ? /* @__PURE__ */ jsx133("div", { className: "rounded border p-2", "data-records-form-add-question": "", children: /* @__PURE__ */ jsx133(FieldEditor, { tableId, onCancel: () => onAdding(false), onSaved: onAdded }) }) : /* @__PURE__ */ jsx133("div", { children: /* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "outline", onClick: () => onAdding(true), children: "Add a question" }) })
|
|
43069
|
+
] });
|
|
43070
|
+
}
|
|
43071
|
+
function QuestionRow({
|
|
43072
|
+
index,
|
|
43073
|
+
question,
|
|
43074
|
+
field: field2,
|
|
43075
|
+
draft,
|
|
43076
|
+
conditionFields,
|
|
43077
|
+
label,
|
|
43078
|
+
onChange,
|
|
43079
|
+
onRemove
|
|
43080
|
+
}) {
|
|
43081
|
+
const sortable = useSortable({ id: question.field });
|
|
43082
|
+
const [open, setOpen] = useState110(true);
|
|
43083
|
+
const name = field2 ? fieldName(field2) : question.field;
|
|
43084
|
+
return /* @__PURE__ */ jsxs123(
|
|
43085
|
+
"div",
|
|
43086
|
+
{
|
|
43087
|
+
ref: sortable.setNodeRef,
|
|
43088
|
+
style: { transform: CSS2.Transform.toString(sortable.transform), transition: sortable.transition },
|
|
43089
|
+
className: cn78("rounded border bg-background px-2 py-1.5", sortable.isDragging && "z-10 shadow-md"),
|
|
43090
|
+
"data-records-form-question-row": question.field,
|
|
43091
|
+
children: [
|
|
43092
|
+
/* @__PURE__ */ jsxs123("div", { className: "flex items-center gap-2", children: [
|
|
43093
|
+
/* @__PURE__ */ jsx133(
|
|
43094
|
+
"button",
|
|
43095
|
+
{
|
|
43096
|
+
type: "button",
|
|
43097
|
+
className: "cursor-grab touch-none rounded px-1 text-muted-foreground hover:bg-muted active:cursor-grabbing",
|
|
43098
|
+
"aria-label": `Move ${name}`,
|
|
43099
|
+
title: "Drag to reorder",
|
|
43100
|
+
"data-records-form-drag": "",
|
|
43101
|
+
...sortable.attributes,
|
|
43102
|
+
...sortable.listeners,
|
|
43103
|
+
children: "\u22EE\u22EE"
|
|
43104
|
+
}
|
|
43105
|
+
),
|
|
43106
|
+
/* @__PURE__ */ jsx133("span", { className: "w-5 text-right text-xs tabular-nums text-muted-foreground", children: index + 1 }),
|
|
43107
|
+
/* @__PURE__ */ jsx133("button", { type: "button", className: "min-w-0 flex-1 truncate text-left text-xs font-medium", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: question.ask?.trim() || name }),
|
|
43108
|
+
field2?.required || question.required ? /* @__PURE__ */ jsx133("span", { className: "text-xs text-foreground/80", children: "required" }) : null,
|
|
43109
|
+
!field2 ? /* @__PURE__ */ jsx133("span", { className: "rounded bg-destructive px-1.5 text-[11px] text-destructive-foreground", title: "The table has no such field.", children: "Missing field" }) : null,
|
|
43110
|
+
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "ghost", onClick: onRemove, "aria-label": `Stop asking ${name}`, children: "Remove" })
|
|
43111
|
+
] }),
|
|
43112
|
+
open && field2 ? /* @__PURE__ */ jsxs123("div", { className: "mt-1.5 grid grid-cols-2 gap-2 pl-7", children: [
|
|
43113
|
+
/* @__PURE__ */ jsx133(
|
|
43114
|
+
BasicInput28,
|
|
43115
|
+
{
|
|
43116
|
+
className: "h-8 text-xs",
|
|
43117
|
+
placeholder: name,
|
|
43118
|
+
"aria-label": `How to ask for ${name}`,
|
|
43119
|
+
title: "Use {{question}} to recall an earlier answer",
|
|
43120
|
+
value: question.ask ?? "",
|
|
43121
|
+
onChange: (e) => onChange({ ask: e.target.value || null })
|
|
43122
|
+
}
|
|
43123
|
+
),
|
|
43124
|
+
/* @__PURE__ */ jsx133(
|
|
43125
|
+
BasicInput28,
|
|
43126
|
+
{
|
|
43127
|
+
className: "h-8 text-xs",
|
|
43128
|
+
placeholder: "Help text",
|
|
43129
|
+
"aria-label": `Help text for ${name}`,
|
|
43130
|
+
value: question.help ?? "",
|
|
43131
|
+
onChange: (e) => onChange({ help: e.target.value || null })
|
|
43132
|
+
}
|
|
43133
|
+
),
|
|
43134
|
+
/* @__PURE__ */ jsxs123("label", { className: "col-span-2 flex items-center gap-2 text-xs", children: [
|
|
43135
|
+
/* @__PURE__ */ jsx133(
|
|
43136
|
+
Checkbox15,
|
|
43137
|
+
{
|
|
43138
|
+
checked: field2.required ? true : question.required === true,
|
|
43139
|
+
disabled: field2.required === true,
|
|
43140
|
+
"aria-label": `${name} is required`,
|
|
43141
|
+
onCheckedChange: (checked) => onChange({ required: checked === true ? true : null })
|
|
43142
|
+
}
|
|
43143
|
+
),
|
|
43144
|
+
"Required"
|
|
43145
|
+
] }),
|
|
43146
|
+
/* @__PURE__ */ jsx133(Condition, { question, fieldKeys: conditionFields, onChange: (showIf) => onChange({ showIf }) }),
|
|
43147
|
+
/* @__PURE__ */ jsx133(
|
|
43148
|
+
QuestionLogic,
|
|
43149
|
+
{
|
|
43150
|
+
question,
|
|
43151
|
+
index,
|
|
43152
|
+
questions: draft.questions,
|
|
43153
|
+
endings: draft.endings ?? [],
|
|
43154
|
+
field: field2,
|
|
43155
|
+
fieldKeys: conditionFields,
|
|
43156
|
+
label,
|
|
43157
|
+
onChange
|
|
43158
|
+
}
|
|
43159
|
+
)
|
|
43160
|
+
] }) : null
|
|
43161
|
+
]
|
|
43162
|
+
}
|
|
43163
|
+
);
|
|
43164
|
+
}
|
|
43165
|
+
function NotifyOnAnswer({
|
|
43166
|
+
tableId,
|
|
43167
|
+
tableName: tableName2,
|
|
43168
|
+
formName,
|
|
43169
|
+
ruleId,
|
|
43170
|
+
notify,
|
|
43171
|
+
onRule,
|
|
43172
|
+
onNotify
|
|
43173
|
+
}) {
|
|
43174
|
+
const client = useRecordsClient79();
|
|
43175
|
+
const [subs, setSubs] = useState110(null);
|
|
43176
|
+
const [busy, setBusy] = useState110(false);
|
|
43177
|
+
const [error, setError] = useState110(null);
|
|
43178
|
+
const [said2, setSaid] = useState110(null);
|
|
43179
|
+
const [url, setUrl] = useState110(notify?.webhookUrl ?? "");
|
|
43180
|
+
useEffect96(() => setUrl(notify?.webhookUrl ?? ""), [notify?.webhookUrl]);
|
|
43181
|
+
useEffect96(() => {
|
|
43182
|
+
let cancelled = false;
|
|
43183
|
+
void client.subscriptions({ table_id: tableId }).then((r) => {
|
|
43184
|
+
if (!cancelled && r.ok) setSubs(r.data);
|
|
43185
|
+
});
|
|
43186
|
+
return () => {
|
|
43187
|
+
cancelled = true;
|
|
43188
|
+
};
|
|
43189
|
+
}, [client, tableId, ruleId]);
|
|
43190
|
+
const chosen = subs?.find((s) => s.rule_id === ruleId) ?? null;
|
|
43191
|
+
async function emailMe(on) {
|
|
43192
|
+
setBusy(true);
|
|
43193
|
+
setError(null);
|
|
43194
|
+
try {
|
|
43195
|
+
if (!on) {
|
|
43196
|
+
onRule(null);
|
|
43197
|
+
if (ruleId) await client.recordDelete({ record_id: ruleId });
|
|
43198
|
+
return;
|
|
43199
|
+
}
|
|
43200
|
+
const listed = await client.views({ table_id: tableId });
|
|
43201
|
+
if (!listed.ok) return setError(listed.error);
|
|
43202
|
+
let viewId = listed.data.find((v) => !v.filters || Object.keys(v.filters).length === 0)?.view_id ?? null;
|
|
43203
|
+
if (!viewId) {
|
|
43204
|
+
const made = await client.viewDeclare({ table_id: tableId, spec: { name: `Every ${tableName2}`, filters: null } });
|
|
43205
|
+
if (!made.ok) return setError(made.error);
|
|
43206
|
+
viewId = made.data;
|
|
43207
|
+
}
|
|
43208
|
+
const declared2 = await client.subscriptionDeclare({
|
|
43209
|
+
table_id: tableId,
|
|
43210
|
+
spec: {
|
|
43211
|
+
name: `${formName} \u2014 new answers`,
|
|
43212
|
+
saved_view_id: viewId,
|
|
43213
|
+
cadence: "instant",
|
|
43214
|
+
schedule: null,
|
|
43215
|
+
channel: "email",
|
|
43216
|
+
quiet_hours: null,
|
|
43217
|
+
recipient_user_id: client.config.actor.user_id ?? null,
|
|
43218
|
+
event_key: "records.changed"
|
|
43219
|
+
}
|
|
43220
|
+
});
|
|
43221
|
+
if (!declared2.ok) return setError(declared2.error);
|
|
43222
|
+
onRule(declared2.data);
|
|
43223
|
+
} finally {
|
|
43224
|
+
setBusy(false);
|
|
43225
|
+
}
|
|
43226
|
+
}
|
|
43227
|
+
async function saveWebhook() {
|
|
43228
|
+
const next = url.trim();
|
|
43229
|
+
if (next === (notify?.webhookUrl ?? "")) return;
|
|
43230
|
+
setBusy(true);
|
|
43231
|
+
setError(null);
|
|
43232
|
+
setSaid(null);
|
|
43233
|
+
try {
|
|
43234
|
+
if (next === "") {
|
|
43235
|
+
if (notify?.webhookAutomationId) await client.automationArchive({ automation_id: notify.webhookAutomationId });
|
|
43236
|
+
onNotify(null);
|
|
43237
|
+
return;
|
|
43238
|
+
}
|
|
43239
|
+
const declared2 = await client.automationDeclare({
|
|
43240
|
+
table_id: tableId,
|
|
43241
|
+
automation_id: notify?.webhookAutomationId ?? null,
|
|
43242
|
+
spec: { name: `${formName} \u2014 webhook`, trigger: { on: "form_answered" }, actions: [{ do: "webhook", url: next }] }
|
|
43243
|
+
});
|
|
43244
|
+
if (!declared2.ok) return setError(declared2.error);
|
|
43245
|
+
if (!declared2.data.ok) {
|
|
43246
|
+
setSaid(Object.values(declared2.data._errors)[0] ?? "Not saved");
|
|
43247
|
+
return;
|
|
43248
|
+
}
|
|
43249
|
+
onNotify({ webhookUrl: next, webhookAutomationId: declared2.data.automation_id });
|
|
43250
|
+
} finally {
|
|
43251
|
+
setBusy(false);
|
|
43252
|
+
}
|
|
43253
|
+
}
|
|
43254
|
+
return /* @__PURE__ */ jsxs123("section", { className: "flex flex-col gap-2 rounded border px-2.5 py-2 text-xs", "aria-label": "Notify on answer", "data-records-form-notify": "", children: [
|
|
43255
|
+
/* @__PURE__ */ jsx133("span", { className: "font-medium", children: "Notify on answer" }),
|
|
43256
|
+
/* @__PURE__ */ jsx133("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1", children: /* @__PURE__ */ jsxs123("label", { className: "flex items-center gap-2", title: "An email to you each time somebody answers.", children: [
|
|
43257
|
+
/* @__PURE__ */ jsx133(Checkbox15, { checked: Boolean(ruleId), disabled: busy, onCheckedChange: (c) => void emailMe(c === true), "aria-label": "Email me" }),
|
|
43258
|
+
"Email me",
|
|
43259
|
+
chosen && chosen.channel !== "email" ? /* @__PURE__ */ jsxs123("span", { className: "text-muted-foreground", children: [
|
|
43260
|
+
"(",
|
|
43261
|
+
chosen.channel === "sms" ? "text" : "in app",
|
|
43262
|
+
")"
|
|
43263
|
+
] }) : null
|
|
43264
|
+
] }) }),
|
|
43265
|
+
/* @__PURE__ */ jsxs123("label", { className: "flex items-center gap-2", children: [
|
|
43266
|
+
/* @__PURE__ */ jsx133("span", { className: "shrink-0", children: "Webhook" }),
|
|
43267
|
+
/* @__PURE__ */ jsx133(
|
|
43268
|
+
BasicInput28,
|
|
43269
|
+
{
|
|
43270
|
+
type: "url",
|
|
43271
|
+
inputMode: "url",
|
|
43272
|
+
className: "h-8 text-xs",
|
|
43273
|
+
placeholder: "https://",
|
|
43274
|
+
"aria-label": "Webhook URL",
|
|
43275
|
+
value: url,
|
|
43276
|
+
disabled: busy,
|
|
43277
|
+
onChange: (e) => setUrl(e.target.value),
|
|
43278
|
+
onBlur: () => void saveWebhook(),
|
|
43279
|
+
onKeyDown: (e) => {
|
|
43280
|
+
if (e.key === "Enter") void saveWebhook();
|
|
43281
|
+
}
|
|
43282
|
+
}
|
|
43283
|
+
)
|
|
43284
|
+
] }),
|
|
43285
|
+
said2 ? /* @__PURE__ */ jsx133("p", { className: "text-destructive", children: said2 }) : null,
|
|
43286
|
+
error ? /* @__PURE__ */ jsx133(RefusalLine, { error }) : null
|
|
43287
|
+
] });
|
|
43288
|
+
}
|
|
43079
43289
|
|
|
43080
43290
|
// src/DocTemplate.tsx
|
|
43081
|
-
import { useCallback as
|
|
43082
|
-
import { useFields as useFields26, useRecordsClient as
|
|
43083
|
-
import { BasicInput as BasicInput29, BasicTextarea as BasicTextarea12, Button as
|
|
43084
|
-
import { jsx as
|
|
43291
|
+
import { useCallback as useCallback55, useEffect as useEffect97, useState as useState111 } from "react";
|
|
43292
|
+
import { useFields as useFields26, useRecordsClient as useRecordsClient80, useTable as useTable27 } from "@ai-matrx/records/react";
|
|
43293
|
+
import { BasicInput as BasicInput29, BasicTextarea as BasicTextarea12, Button as Button87, Label as Label24, Skeleton as Skeleton30, cn as cn79 } from "@ai-matrx/design-system";
|
|
43294
|
+
import { jsx as jsx134, jsxs as jsxs124 } from "react/jsx-runtime";
|
|
43085
43295
|
function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, className }) {
|
|
43086
|
-
const client =
|
|
43296
|
+
const client = useRecordsClient80();
|
|
43087
43297
|
const claimSeed = useSeedGuard();
|
|
43088
|
-
const table =
|
|
43298
|
+
const table = useTable27(tableId);
|
|
43089
43299
|
const rights = useTableRights(table.data);
|
|
43090
43300
|
const fields = useFields26(tableId);
|
|
43091
|
-
const [templates, setTemplates] =
|
|
43092
|
-
const [error, setError] =
|
|
43093
|
-
const [activeId, setActiveId] =
|
|
43094
|
-
const [draftName, setDraftName] =
|
|
43095
|
-
const [draftBody, setDraftBody] =
|
|
43096
|
-
const [unresolved, setUnresolved] =
|
|
43097
|
-
const [saving, setSaving] =
|
|
43098
|
-
const load =
|
|
43301
|
+
const [templates, setTemplates] = useState111(null);
|
|
43302
|
+
const [error, setError] = useState111(null);
|
|
43303
|
+
const [activeId, setActiveId] = useState111(activeTemplateId ?? null);
|
|
43304
|
+
const [draftName, setDraftName] = useState111("");
|
|
43305
|
+
const [draftBody, setDraftBody] = useState111("");
|
|
43306
|
+
const [unresolved, setUnresolved] = useState111([]);
|
|
43307
|
+
const [saving, setSaving] = useState111(false);
|
|
43308
|
+
const load = useCallback55(async () => {
|
|
43099
43309
|
const held = await client.docTemplates({ table_id: tableId });
|
|
43100
43310
|
if (!held.ok) {
|
|
43101
43311
|
setError(held.error);
|
|
@@ -43126,10 +43336,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43126
43336
|
setError(null);
|
|
43127
43337
|
setTemplates(rows);
|
|
43128
43338
|
}, [client, tableId, seed, fields.data]);
|
|
43129
|
-
|
|
43339
|
+
useEffect97(() => {
|
|
43130
43340
|
void load();
|
|
43131
43341
|
}, [load]);
|
|
43132
|
-
|
|
43342
|
+
useEffect97(() => {
|
|
43133
43343
|
if (!templates || templates.length === 0) return;
|
|
43134
43344
|
const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
|
|
43135
43345
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -43137,7 +43347,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43137
43347
|
setDraftBody(chosen.body);
|
|
43138
43348
|
onActiveTemplate?.(chosen);
|
|
43139
43349
|
}, [templates, activeTemplateId]);
|
|
43140
|
-
|
|
43350
|
+
useEffect97(() => {
|
|
43141
43351
|
let cancelled = false;
|
|
43142
43352
|
if (draftBody.trim() === "") {
|
|
43143
43353
|
setUnresolved([]);
|
|
@@ -43167,25 +43377,25 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43167
43377
|
setActiveId(saved.data);
|
|
43168
43378
|
await load();
|
|
43169
43379
|
}
|
|
43170
|
-
if (fields.error) return /* @__PURE__ */
|
|
43380
|
+
if (fields.error) return /* @__PURE__ */ jsx134(RefusalNotice, { error: fields.error, className });
|
|
43171
43381
|
if (error) {
|
|
43172
|
-
return /* @__PURE__ */
|
|
43382
|
+
return /* @__PURE__ */ jsx134(
|
|
43173
43383
|
RefusalNotice,
|
|
43174
43384
|
{
|
|
43175
43385
|
error,
|
|
43176
43386
|
className,
|
|
43177
|
-
actions: /* @__PURE__ */
|
|
43387
|
+
actions: /* @__PURE__ */ jsx134(Button87, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43178
43388
|
}
|
|
43179
43389
|
);
|
|
43180
43390
|
}
|
|
43181
|
-
if (templates === null || fields.loading && !fields.data) return /* @__PURE__ */
|
|
43391
|
+
if (templates === null || fields.loading && !fields.data) return /* @__PURE__ */ jsx134(Skeleton30, { className: cn79("h-64 w-full", className) });
|
|
43182
43392
|
if (!rights.admin) {
|
|
43183
|
-
return /* @__PURE__ */
|
|
43393
|
+
return /* @__PURE__ */ jsx134("p", { className: cn79("text-xs text-muted-foreground", className), children: rights.why("structure") });
|
|
43184
43394
|
}
|
|
43185
|
-
return /* @__PURE__ */
|
|
43186
|
-
/* @__PURE__ */
|
|
43187
|
-
templates.map((t) => /* @__PURE__ */
|
|
43188
|
-
|
|
43395
|
+
return /* @__PURE__ */ jsxs124("div", { className: cn79("flex flex-col gap-2", className), children: [
|
|
43396
|
+
/* @__PURE__ */ jsxs124("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Templates", children: [
|
|
43397
|
+
templates.map((t) => /* @__PURE__ */ jsxs124(
|
|
43398
|
+
Button87,
|
|
43189
43399
|
{
|
|
43190
43400
|
size: "sm",
|
|
43191
43401
|
variant: t.id === activeId ? "secondary" : "ghost",
|
|
@@ -43197,7 +43407,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43197
43407
|
},
|
|
43198
43408
|
children: [
|
|
43199
43409
|
t.name,
|
|
43200
|
-
/* @__PURE__ */
|
|
43410
|
+
/* @__PURE__ */ jsxs124("span", { className: "ml-1 text-[10px] opacity-70", children: [
|
|
43201
43411
|
"v",
|
|
43202
43412
|
t.template_version
|
|
43203
43413
|
] })
|
|
@@ -43205,8 +43415,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43205
43415
|
},
|
|
43206
43416
|
t.id
|
|
43207
43417
|
)),
|
|
43208
|
-
/* @__PURE__ */
|
|
43209
|
-
|
|
43418
|
+
/* @__PURE__ */ jsx134(
|
|
43419
|
+
Button87,
|
|
43210
43420
|
{
|
|
43211
43421
|
size: "sm",
|
|
43212
43422
|
variant: "ghost",
|
|
@@ -43218,16 +43428,16 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43218
43428
|
children: "New template"
|
|
43219
43429
|
}
|
|
43220
43430
|
),
|
|
43221
|
-
/* @__PURE__ */
|
|
43431
|
+
/* @__PURE__ */ jsx134(Button87, { size: "sm", className: "ml-auto", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
43222
43432
|
] }),
|
|
43223
|
-
/* @__PURE__ */
|
|
43224
|
-
/* @__PURE__ */
|
|
43225
|
-
/* @__PURE__ */
|
|
43433
|
+
/* @__PURE__ */ jsxs124("label", { className: "flex flex-col gap-1", children: [
|
|
43434
|
+
/* @__PURE__ */ jsx134(Label24, { className: "text-xs font-medium", children: "Name" }),
|
|
43435
|
+
/* @__PURE__ */ jsx134(BasicInput29, { value: draftName, onChange: (e) => setDraftName(e.target.value), placeholder: "Agreement" })
|
|
43226
43436
|
] }),
|
|
43227
|
-
/* @__PURE__ */
|
|
43228
|
-
/* @__PURE__ */
|
|
43229
|
-
(fields.data ?? []).map((field2) => /* @__PURE__ */
|
|
43230
|
-
|
|
43437
|
+
/* @__PURE__ */ jsxs124("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
43438
|
+
/* @__PURE__ */ jsx134("span", { className: "text-xs text-muted-foreground", children: "Insert" }),
|
|
43439
|
+
(fields.data ?? []).map((field2) => /* @__PURE__ */ jsx134(
|
|
43440
|
+
Button87,
|
|
43231
43441
|
{
|
|
43232
43442
|
size: "sm",
|
|
43233
43443
|
variant: "ghost",
|
|
@@ -43238,7 +43448,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43238
43448
|
field2.id
|
|
43239
43449
|
))
|
|
43240
43450
|
] }),
|
|
43241
|
-
/* @__PURE__ */
|
|
43451
|
+
/* @__PURE__ */ jsx134(
|
|
43242
43452
|
BasicTextarea12,
|
|
43243
43453
|
{
|
|
43244
43454
|
rows: 10,
|
|
@@ -43248,33 +43458,33 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43248
43458
|
onChange: (e) => setDraftBody(e.target.value)
|
|
43249
43459
|
}
|
|
43250
43460
|
),
|
|
43251
|
-
unresolved.length > 0 ? /* @__PURE__ */
|
|
43252
|
-
/* @__PURE__ */
|
|
43253
|
-
/* @__PURE__ */
|
|
43254
|
-
/* @__PURE__ */
|
|
43461
|
+
unresolved.length > 0 ? /* @__PURE__ */ jsx134("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ jsxs124("li", { className: "flex items-baseline gap-1", children: [
|
|
43462
|
+
/* @__PURE__ */ jsx134("code", { className: "text-destructive", children: token.raw }),
|
|
43463
|
+
/* @__PURE__ */ jsx134("span", { "aria-hidden": "true", children: "\u2014" }),
|
|
43464
|
+
/* @__PURE__ */ jsx134(
|
|
43255
43465
|
RefusalLine,
|
|
43256
43466
|
{
|
|
43257
43467
|
error: refusal("invalid_argument", token.why, "Change it to a Field this table has, or take it out.")
|
|
43258
43468
|
}
|
|
43259
43469
|
)
|
|
43260
|
-
] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */
|
|
43470
|
+
] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */ jsx134("p", { className: "text-xs text-muted-foreground", children: "Every token in this body points at a Field this table can answer." }) : null
|
|
43261
43471
|
] });
|
|
43262
43472
|
}
|
|
43263
43473
|
|
|
43264
43474
|
// src/DocRender.tsx
|
|
43265
|
-
import { useCallback as
|
|
43266
|
-
import { useRecordsClient as
|
|
43267
|
-
import { Button as
|
|
43268
|
-
import { jsx as
|
|
43475
|
+
import { useCallback as useCallback56, useEffect as useEffect98, useRef as useRef57, useState as useState112 } from "react";
|
|
43476
|
+
import { useRecordsClient as useRecordsClient81 } from "@ai-matrx/records/react";
|
|
43477
|
+
import { Button as Button88, Skeleton as Skeleton31, cn as cn80 } from "@ai-matrx/design-system";
|
|
43478
|
+
import { jsx as jsx135, jsxs as jsxs125 } from "react/jsx-runtime";
|
|
43269
43479
|
function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
43270
|
-
const client =
|
|
43271
|
-
const [preview, setPreview] =
|
|
43272
|
-
const [renders, setRenders] =
|
|
43273
|
-
const [showing, setShowing] =
|
|
43274
|
-
const [error, setError] =
|
|
43275
|
-
const [busy, setBusy] =
|
|
43480
|
+
const client = useRecordsClient81();
|
|
43481
|
+
const [preview, setPreview] = useState112(null);
|
|
43482
|
+
const [renders, setRenders] = useState112(null);
|
|
43483
|
+
const [showing, setShowing] = useState112(null);
|
|
43484
|
+
const [error, setError] = useState112(null);
|
|
43485
|
+
const [busy, setBusy] = useState112(null);
|
|
43276
43486
|
const paper = useRef57(null);
|
|
43277
|
-
const load =
|
|
43487
|
+
const load = useCallback56(async () => {
|
|
43278
43488
|
const [body, held] = await Promise.all([
|
|
43279
43489
|
client.docRenderBody({ template_id: templateId, record_id: recordId }),
|
|
43280
43490
|
client.docRenders({ record_id: recordId })
|
|
@@ -43291,7 +43501,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43291
43501
|
setPreview(body.data);
|
|
43292
43502
|
setRenders(held.data.filter((r) => r.template_id === templateId));
|
|
43293
43503
|
}, [client, templateId, recordId]);
|
|
43294
|
-
|
|
43504
|
+
useEffect98(() => {
|
|
43295
43505
|
void load();
|
|
43296
43506
|
}, [load]);
|
|
43297
43507
|
async function freeze() {
|
|
@@ -43331,22 +43541,22 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43331
43541
|
}
|
|
43332
43542
|
}
|
|
43333
43543
|
if (error) {
|
|
43334
|
-
return /* @__PURE__ */
|
|
43544
|
+
return /* @__PURE__ */ jsx135(
|
|
43335
43545
|
RefusalNotice,
|
|
43336
43546
|
{
|
|
43337
43547
|
error,
|
|
43338
43548
|
className,
|
|
43339
|
-
actions: /* @__PURE__ */
|
|
43549
|
+
actions: /* @__PURE__ */ jsx135(Button88, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43340
43550
|
}
|
|
43341
43551
|
);
|
|
43342
43552
|
}
|
|
43343
|
-
if (preview === null || renders === null) return /* @__PURE__ */
|
|
43553
|
+
if (preview === null || renders === null) return /* @__PURE__ */ jsx135(Skeleton31, { className: cn80("h-64 w-full", className) });
|
|
43344
43554
|
const frozen = showing ? renders.find((r) => r.id === showing) ?? null : null;
|
|
43345
43555
|
const text2 = frozen ? frozen.body : preview;
|
|
43346
|
-
return /* @__PURE__ */
|
|
43347
|
-
/* @__PURE__ */
|
|
43348
|
-
/* @__PURE__ */
|
|
43349
|
-
|
|
43556
|
+
return /* @__PURE__ */ jsxs125("div", { className: cn80("flex flex-col gap-2", className), children: [
|
|
43557
|
+
/* @__PURE__ */ jsxs125("div", { className: "flex items-center gap-1 text-xs", children: [
|
|
43558
|
+
/* @__PURE__ */ jsx135(
|
|
43559
|
+
Button88,
|
|
43350
43560
|
{
|
|
43351
43561
|
size: "sm",
|
|
43352
43562
|
variant: frozen ? "ghost" : "secondary",
|
|
@@ -43355,8 +43565,8 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43355
43565
|
children: "As it reads now"
|
|
43356
43566
|
}
|
|
43357
43567
|
),
|
|
43358
|
-
renders.map((render) => /* @__PURE__ */
|
|
43359
|
-
|
|
43568
|
+
renders.map((render) => /* @__PURE__ */ jsx135(
|
|
43569
|
+
Button88,
|
|
43360
43570
|
{
|
|
43361
43571
|
size: "sm",
|
|
43362
43572
|
variant: render.id === showing ? "secondary" : "ghost",
|
|
@@ -43367,33 +43577,33 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43367
43577
|
},
|
|
43368
43578
|
render.id
|
|
43369
43579
|
)),
|
|
43370
|
-
/* @__PURE__ */
|
|
43371
|
-
/* @__PURE__ */
|
|
43372
|
-
/* @__PURE__ */
|
|
43580
|
+
/* @__PURE__ */ jsxs125("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
43581
|
+
/* @__PURE__ */ jsx135(Button88, { size: "sm", disabled: busy !== null, onClick: () => void freeze(), children: busy === "freeze" ? "Freezing\u2026" : "Freeze this version" }),
|
|
43582
|
+
/* @__PURE__ */ jsx135(Button88, { size: "sm", variant: "ghost", disabled: busy !== null, onClick: () => void toPdf(), children: busy === "pdf" ? "Making the PDF\u2026" : "PDF" })
|
|
43373
43583
|
] })
|
|
43374
43584
|
] }),
|
|
43375
|
-
/* @__PURE__ */
|
|
43376
|
-
/* @__PURE__ */
|
|
43585
|
+
/* @__PURE__ */ jsx135("div", { ref: paper, className: "whitespace-pre-wrap rounded border bg-background p-4 text-sm text-foreground", children: text2 }),
|
|
43586
|
+
/* @__PURE__ */ jsx135("p", { className: "text-xs text-muted-foreground", children: frozen ? `Frozen ${new Date(frozen.rendered_at).toLocaleString()} from template v${frozen.template_version}. Its text and its SHA-256 (${frozen.content_hash.slice(0, 12)}\u2026) are what any signature seals.` : "This is how the template reads against this record right now. Nothing has been frozen, so there is no version for anyone to sign yet." })
|
|
43377
43587
|
] });
|
|
43378
43588
|
}
|
|
43379
43589
|
|
|
43380
43590
|
// src/SignBlock.tsx
|
|
43381
|
-
import { useCallback as
|
|
43382
|
-
import { useFields as useFields27, useRecordsClient as
|
|
43383
|
-
import { BasicInput as BasicInput30, Button as
|
|
43384
|
-
import { useTable as
|
|
43385
|
-
import { jsx as
|
|
43591
|
+
import { useCallback as useCallback57, useEffect as useEffect99, useState as useState113 } from "react";
|
|
43592
|
+
import { useFields as useFields27, useRecordsClient as useRecordsClient82 } from "@ai-matrx/records/react";
|
|
43593
|
+
import { BasicInput as BasicInput30, Button as Button89, Skeleton as Skeleton32, cn as cn81 } from "@ai-matrx/design-system";
|
|
43594
|
+
import { useTable as useTable28 } from "@ai-matrx/records/react";
|
|
43595
|
+
import { jsx as jsx136, jsxs as jsxs126 } from "react/jsx-runtime";
|
|
43386
43596
|
function SignBlock({ tableId, recordId, render, className }) {
|
|
43387
|
-
const client =
|
|
43388
|
-
const table =
|
|
43597
|
+
const client = useRecordsClient82();
|
|
43598
|
+
const table = useTable28(tableId);
|
|
43389
43599
|
const rights = useTableRights(table.data);
|
|
43390
43600
|
const fields = useFields27(tableId);
|
|
43391
|
-
const [signatures, setSignatures] =
|
|
43392
|
-
const [verdicts, setVerdicts] =
|
|
43393
|
-
const [error, setError] =
|
|
43394
|
-
const [name, setName] =
|
|
43395
|
-
const [busy, setBusy] =
|
|
43396
|
-
const load =
|
|
43601
|
+
const [signatures, setSignatures] = useState113(null);
|
|
43602
|
+
const [verdicts, setVerdicts] = useState113({});
|
|
43603
|
+
const [error, setError] = useState113(null);
|
|
43604
|
+
const [name, setName] = useState113("");
|
|
43605
|
+
const [busy, setBusy] = useState113(false);
|
|
43606
|
+
const load = useCallback57(async () => {
|
|
43397
43607
|
const held = await client.docSignatures({ record_id: recordId });
|
|
43398
43608
|
if (!held.ok) {
|
|
43399
43609
|
setError(held.error);
|
|
@@ -43408,7 +43618,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43408
43618
|
}
|
|
43409
43619
|
setVerdicts(answers);
|
|
43410
43620
|
}, [client, recordId]);
|
|
43411
|
-
|
|
43621
|
+
useEffect99(() => {
|
|
43412
43622
|
void load();
|
|
43413
43623
|
}, [load]);
|
|
43414
43624
|
async function sign(field2) {
|
|
@@ -43428,36 +43638,36 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43428
43638
|
setName("");
|
|
43429
43639
|
await load();
|
|
43430
43640
|
}
|
|
43431
|
-
if (fields.error) return /* @__PURE__ */
|
|
43641
|
+
if (fields.error) return /* @__PURE__ */ jsx136(RefusalNotice, { error: fields.error, className });
|
|
43432
43642
|
if (error) {
|
|
43433
|
-
return /* @__PURE__ */
|
|
43643
|
+
return /* @__PURE__ */ jsx136(
|
|
43434
43644
|
RefusalNotice,
|
|
43435
43645
|
{
|
|
43436
43646
|
error,
|
|
43437
43647
|
className,
|
|
43438
|
-
actions: /* @__PURE__ */
|
|
43648
|
+
actions: /* @__PURE__ */ jsx136(Button89, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43439
43649
|
}
|
|
43440
43650
|
);
|
|
43441
43651
|
}
|
|
43442
|
-
if (fields.loading || signatures === null) return /* @__PURE__ */
|
|
43652
|
+
if (fields.loading || signatures === null) return /* @__PURE__ */ jsx136(Skeleton32, { className: cn81("h-32 w-full", className) });
|
|
43443
43653
|
const signable = (fields.data ?? []).filter(isSignatureField);
|
|
43444
|
-
return /* @__PURE__ */
|
|
43445
|
-
/* @__PURE__ */
|
|
43446
|
-
/* @__PURE__ */
|
|
43447
|
-
/* @__PURE__ */
|
|
43654
|
+
return /* @__PURE__ */ jsxs126("div", { className: cn81("flex flex-col gap-2", className), children: [
|
|
43655
|
+
/* @__PURE__ */ jsxs126("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
43656
|
+
/* @__PURE__ */ jsx136("span", { className: "font-medium text-foreground", children: "Signatures" }),
|
|
43657
|
+
/* @__PURE__ */ jsxs126("span", { children: [
|
|
43448
43658
|
signatures.length,
|
|
43449
43659
|
" on this record"
|
|
43450
43660
|
] })
|
|
43451
43661
|
] }),
|
|
43452
|
-
signatures.length > 0 ? /* @__PURE__ */
|
|
43662
|
+
signatures.length > 0 ? /* @__PURE__ */ jsx136("ul", { className: "flex flex-col gap-1", children: signatures.map((signature) => {
|
|
43453
43663
|
const verdict = verdicts[signature.id];
|
|
43454
43664
|
const intact = typeof verdict === "object" && verdict !== null ? verdict.intact : void 0;
|
|
43455
|
-
return /* @__PURE__ */
|
|
43456
|
-
/* @__PURE__ */
|
|
43457
|
-
/* @__PURE__ */
|
|
43458
|
-
/* @__PURE__ */
|
|
43459
|
-
/* @__PURE__ */
|
|
43460
|
-
/* @__PURE__ */
|
|
43665
|
+
return /* @__PURE__ */ jsxs126("li", { className: "rounded border px-2 py-1.5 text-xs", children: [
|
|
43666
|
+
/* @__PURE__ */ jsxs126("div", { className: "flex items-center gap-2", children: [
|
|
43667
|
+
/* @__PURE__ */ jsx136("span", { className: "font-medium", children: signature.signer_name }),
|
|
43668
|
+
/* @__PURE__ */ jsx136("span", { className: "text-muted-foreground", children: signature.field_key }),
|
|
43669
|
+
/* @__PURE__ */ jsx136("span", { className: "text-muted-foreground", children: new Date(signature.signed_at).toLocaleString() }),
|
|
43670
|
+
/* @__PURE__ */ jsxs126("span", { className: "ml-auto font-mono text-[10px] text-muted-foreground", children: [
|
|
43461
43671
|
"v",
|
|
43462
43672
|
signature.document_version,
|
|
43463
43673
|
" \xB7 ",
|
|
@@ -43467,7 +43677,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43467
43677
|
intact === false ? (
|
|
43468
43678
|
// THE STORE REFUSED TO CALL IT INTACT: its reason goes through the one
|
|
43469
43679
|
// formatter, with what to do next, never raw in red (lane REFUSAL-SWEEP).
|
|
43470
|
-
/* @__PURE__ */
|
|
43680
|
+
/* @__PURE__ */ jsx136(
|
|
43471
43681
|
RefusalLine,
|
|
43472
43682
|
{
|
|
43473
43683
|
className: "mt-0.5",
|
|
@@ -43478,22 +43688,22 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43478
43688
|
)
|
|
43479
43689
|
}
|
|
43480
43690
|
)
|
|
43481
|
-
) : /* @__PURE__ */
|
|
43691
|
+
) : /* @__PURE__ */ jsx136("p", { className: "mt-0.5 text-muted-foreground", children: intact === true ? "The store says what was signed is still exactly what is there." : String(verdict ?? "") })
|
|
43482
43692
|
] }, signature.id);
|
|
43483
43693
|
}) }) : null,
|
|
43484
|
-
!render ? /* @__PURE__ */
|
|
43694
|
+
!render ? /* @__PURE__ */ jsx136("p", { className: "text-xs text-muted-foreground", children: 'Nothing has been frozen yet, so there is no version to sign. A signature seals one exact document version and its hash \u2014 "the document as it reads today" is not something anyone can agree to.' }) : signable.length === 0 ? /* @__PURE__ */ jsxs126("p", { className: "text-xs text-muted-foreground", children: [
|
|
43485
43695
|
"This table has no signature Field, so there is nowhere to write a signature. A signature is a Value on a text Field whose format is ",
|
|
43486
|
-
/* @__PURE__ */
|
|
43696
|
+
/* @__PURE__ */ jsx136("code", { children: "signature" }),
|
|
43487
43697
|
" \u2014 add one and the signing appears."
|
|
43488
|
-
] }) : !rights.write ? /* @__PURE__ */
|
|
43698
|
+
] }) : !rights.write ? /* @__PURE__ */ jsx136("p", { className: "text-xs text-muted-foreground", children: rights.why("write") }) : /* @__PURE__ */ jsx136("div", { className: "flex flex-col gap-1", children: signable.map((field2) => {
|
|
43489
43699
|
const already = signatures.find((s) => s.field_key === field2.key);
|
|
43490
43700
|
if (already) {
|
|
43491
|
-
return /* @__PURE__ */
|
|
43701
|
+
return /* @__PURE__ */ jsxs126("p", { className: "text-xs text-muted-foreground", children: [
|
|
43492
43702
|
fieldName(field2),
|
|
43493
43703
|
" is signed, and a signature is immutable once made. A further agreement is a further Field with its own signature, or a further version with its own seal."
|
|
43494
43704
|
] }, field2.id);
|
|
43495
43705
|
}
|
|
43496
|
-
return /* @__PURE__ */
|
|
43706
|
+
return /* @__PURE__ */ jsxs126(
|
|
43497
43707
|
"form",
|
|
43498
43708
|
{
|
|
43499
43709
|
className: "flex items-center gap-1",
|
|
@@ -43502,7 +43712,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43502
43712
|
if (name.trim() !== "") void sign(field2);
|
|
43503
43713
|
},
|
|
43504
43714
|
children: [
|
|
43505
|
-
/* @__PURE__ */
|
|
43715
|
+
/* @__PURE__ */ jsx136(
|
|
43506
43716
|
BasicInput30,
|
|
43507
43717
|
{
|
|
43508
43718
|
className: "h-8 w-56 text-xs",
|
|
@@ -43512,8 +43722,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43512
43722
|
onChange: (e) => setName(e.target.value)
|
|
43513
43723
|
}
|
|
43514
43724
|
),
|
|
43515
|
-
/* @__PURE__ */
|
|
43516
|
-
/* @__PURE__ */
|
|
43725
|
+
/* @__PURE__ */ jsx136(Button89, { size: "sm", type: "submit", disabled: busy || name.trim() === "", children: "Sign" }),
|
|
43726
|
+
/* @__PURE__ */ jsxs126("span", { className: "text-xs text-muted-foreground", children: [
|
|
43517
43727
|
"seals version ",
|
|
43518
43728
|
render.template_version,
|
|
43519
43729
|
" \xB7 ",
|
|
@@ -43528,6 +43738,261 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43528
43738
|
] });
|
|
43529
43739
|
}
|
|
43530
43740
|
|
|
43741
|
+
// src/NotifyRuleEditor.tsx
|
|
43742
|
+
import { useCallback as useCallback58, useEffect as useEffect100, useState as useState114 } from "react";
|
|
43743
|
+
import { useRecordsClient as useRecordsClient83, useTable as useTable29 } from "@ai-matrx/records/react";
|
|
43744
|
+
import { hasQuietHours as hasQuietHours3 } from "@ai-matrx/records";
|
|
43745
|
+
import { Button as Button90, Skeleton as Skeleton33, cn as cn82 } from "@ai-matrx/design-system";
|
|
43746
|
+
import { jsx as jsx137, jsxs as jsxs127 } from "react/jsx-runtime";
|
|
43747
|
+
var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
|
|
43748
|
+
var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
43749
|
+
function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className }) {
|
|
43750
|
+
const client = useRecordsClient83();
|
|
43751
|
+
const host = useRecordsUi();
|
|
43752
|
+
const table = useTable29(tableId);
|
|
43753
|
+
const rights = useTableRights(table.data);
|
|
43754
|
+
const [subscriptions, setSubscriptions] = useState114(null);
|
|
43755
|
+
const [cadences, setCadences] = useState114([]);
|
|
43756
|
+
const [views, setViews] = useState114(null);
|
|
43757
|
+
const [error, setError] = useState114(null);
|
|
43758
|
+
const [busy, setBusy] = useState114(false);
|
|
43759
|
+
const [stopping, setStopping] = useState114(null);
|
|
43760
|
+
const load = useCallback58(async () => {
|
|
43761
|
+
const [held, offered] = await Promise.all([
|
|
43762
|
+
// THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
|
|
43763
|
+
// to them plus — only where they hold admin on this Table — anyone's over
|
|
43764
|
+
// it, and it is narrowed to Tables they can already open.
|
|
43765
|
+
client.subscriptions({ table_id: tableId }),
|
|
43766
|
+
// The cadences are ASKED of the store, never a list typed into a picker
|
|
43767
|
+
// here that could drift from what the digest runner understands.
|
|
43768
|
+
client.subscriptionCadences()
|
|
43769
|
+
]);
|
|
43770
|
+
if (!held.ok) {
|
|
43771
|
+
setError(held.error);
|
|
43772
|
+
return;
|
|
43773
|
+
}
|
|
43774
|
+
setError(null);
|
|
43775
|
+
setSubscriptions(held.data);
|
|
43776
|
+
if (offered.ok) setCadences(offered.data);
|
|
43777
|
+
if (host.savedViews) {
|
|
43778
|
+
setViews(await host.savedViews());
|
|
43779
|
+
return;
|
|
43780
|
+
}
|
|
43781
|
+
const listed = await client.views({ table_id: tableId });
|
|
43782
|
+
if (!listed.ok) {
|
|
43783
|
+
setError(listed.error);
|
|
43784
|
+
return;
|
|
43785
|
+
}
|
|
43786
|
+
setViews(listed.data.map((v) => ({ id: v.view_id, name: v.name })));
|
|
43787
|
+
}, [client, host, tableId]);
|
|
43788
|
+
useEffect100(() => {
|
|
43789
|
+
void load();
|
|
43790
|
+
}, [load]);
|
|
43791
|
+
const write = useCallback58(
|
|
43792
|
+
async (spec) => {
|
|
43793
|
+
const declared2 = await client.subscriptionDeclare({
|
|
43794
|
+
table_id: tableId,
|
|
43795
|
+
spec: {
|
|
43796
|
+
name: spec.name,
|
|
43797
|
+
saved_view_id: spec.savedViewId,
|
|
43798
|
+
cadence: spec.cadence ?? "instant",
|
|
43799
|
+
schedule: spec.schedule ?? null,
|
|
43800
|
+
channel: spec.channel ?? "in_app",
|
|
43801
|
+
quiet_hours: spec.quietHours ?? null,
|
|
43802
|
+
// "Tell me" is the default, and the store refuses a subscription with
|
|
43803
|
+
// nobody to tell rather than writing one that fires at nobody.
|
|
43804
|
+
recipient_user_id: spec.recipientUserId ?? client.config.actor.user_id ?? null,
|
|
43805
|
+
event_key: spec.eventKey ?? "records.changed"
|
|
43806
|
+
}
|
|
43807
|
+
});
|
|
43808
|
+
if (!declared2.ok) {
|
|
43809
|
+
setError(declared2.error);
|
|
43810
|
+
return null;
|
|
43811
|
+
}
|
|
43812
|
+
return declared2.data;
|
|
43813
|
+
},
|
|
43814
|
+
[client, tableId]
|
|
43815
|
+
);
|
|
43816
|
+
useEffect100(() => {
|
|
43817
|
+
if (!subscriptions || !seed || seed.length === 0) return;
|
|
43818
|
+
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
43819
|
+
if (missing.length === 0) return;
|
|
43820
|
+
void (async () => {
|
|
43821
|
+
for (const spec of missing) {
|
|
43822
|
+
if (!await write(spec)) return;
|
|
43823
|
+
}
|
|
43824
|
+
await load();
|
|
43825
|
+
})();
|
|
43826
|
+
}, [subscriptions, seed, write, load]);
|
|
43827
|
+
async function subscribe(viewId, viewName, cadence, channel, schedule, quiet2) {
|
|
43828
|
+
setBusy(true);
|
|
43829
|
+
const ok = await write({
|
|
43830
|
+
name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
|
|
43831
|
+
savedViewId: viewId,
|
|
43832
|
+
cadence,
|
|
43833
|
+
channel,
|
|
43834
|
+
schedule: schedule.trim() === "" ? null : schedule.trim(),
|
|
43835
|
+
quietHours: quiet2
|
|
43836
|
+
});
|
|
43837
|
+
setBusy(false);
|
|
43838
|
+
if (!ok) return;
|
|
43839
|
+
onChoose?.(ok);
|
|
43840
|
+
await load();
|
|
43841
|
+
}
|
|
43842
|
+
async function makeEveryRecordView() {
|
|
43843
|
+
setBusy(true);
|
|
43844
|
+
const made = await client.viewDeclare({
|
|
43845
|
+
table_id: tableId,
|
|
43846
|
+
spec: { name: `Every ${table.data?.name ?? "record"}`, filters: null }
|
|
43847
|
+
});
|
|
43848
|
+
setBusy(false);
|
|
43849
|
+
if (!made.ok) {
|
|
43850
|
+
setError(made.error);
|
|
43851
|
+
return;
|
|
43852
|
+
}
|
|
43853
|
+
await load();
|
|
43854
|
+
}
|
|
43855
|
+
async function unsubscribe(ruleId) {
|
|
43856
|
+
setStopping(null);
|
|
43857
|
+
setBusy(true);
|
|
43858
|
+
const deleted = await client.recordDelete({ record_id: ruleId });
|
|
43859
|
+
setBusy(false);
|
|
43860
|
+
if (!deleted.ok) {
|
|
43861
|
+
setError(deleted.error);
|
|
43862
|
+
return;
|
|
43863
|
+
}
|
|
43864
|
+
await load();
|
|
43865
|
+
}
|
|
43866
|
+
if (error) {
|
|
43867
|
+
return /* @__PURE__ */ jsx137(
|
|
43868
|
+
RefusalNotice,
|
|
43869
|
+
{
|
|
43870
|
+
error,
|
|
43871
|
+
className,
|
|
43872
|
+
actions: /* @__PURE__ */ jsx137(Button90, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43873
|
+
}
|
|
43874
|
+
);
|
|
43875
|
+
}
|
|
43876
|
+
if (subscriptions === null) return /* @__PURE__ */ jsx137(Skeleton33, { className: cn82("h-40 w-full", className) });
|
|
43877
|
+
return /* @__PURE__ */ jsxs127("div", { className: cn82("flex flex-col gap-2", className), children: [
|
|
43878
|
+
/* @__PURE__ */ jsxs127("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
43879
|
+
/* @__PURE__ */ jsx137("span", { className: "font-medium text-foreground", children: "Tell me when" }),
|
|
43880
|
+
/* @__PURE__ */ jsxs127("span", { children: [
|
|
43881
|
+
subscriptions.length,
|
|
43882
|
+
" subscription",
|
|
43883
|
+
subscriptions.length === 1 ? "" : "s"
|
|
43884
|
+
] })
|
|
43885
|
+
] }),
|
|
43886
|
+
subscriptions.length === 0 ? /* @__PURE__ */ jsx137("p", { className: "text-xs text-muted-foreground", children: "Nothing is telling anyone about this yet. A subscription is a Rule over a saved view, so what you are told about is exactly what that view holds." }) : /* @__PURE__ */ jsx137("ul", { className: "flex flex-col gap-1", children: subscriptions.map((held) => /* @__PURE__ */ jsxs127("li", { className: "flex items-center gap-2 rounded border px-2 py-1.5 text-xs", children: [
|
|
43887
|
+
/* @__PURE__ */ jsx137("span", { className: "truncate font-medium", children: held.name }),
|
|
43888
|
+
/* @__PURE__ */ jsx137("span", { className: "text-muted-foreground", children: CADENCE_WORDS2[held.cadence] ?? held.cadence }),
|
|
43889
|
+
/* @__PURE__ */ jsx137("span", { className: "text-muted-foreground", children: CHANNEL_WORDS3[held.channel] ?? held.channel }),
|
|
43890
|
+
held.schedule ? /* @__PURE__ */ jsx137("span", { className: "text-muted-foreground", children: scheduleWords(held.schedule, held.quiet_hours?.tz) }) : null,
|
|
43891
|
+
held.next_digest_at ? /* @__PURE__ */ jsxs127("span", { className: "text-muted-foreground", children: [
|
|
43892
|
+
"next ",
|
|
43893
|
+
new Date(held.next_digest_at).toLocaleString()
|
|
43894
|
+
] }) : null,
|
|
43895
|
+
hasQuietHours3(held.quiet_hours) ? /* @__PURE__ */ jsxs127("span", { className: "text-muted-foreground", children: [
|
|
43896
|
+
"quiet ",
|
|
43897
|
+
held.quiet_hours.start,
|
|
43898
|
+
"\u2013",
|
|
43899
|
+
held.quiet_hours.end
|
|
43900
|
+
] }) : null,
|
|
43901
|
+
held.recipient_user_id === null ? /* @__PURE__ */ jsx137("span", { className: "text-destructive", children: "This one has nobody to tell, so it fires at nobody." }) : null,
|
|
43902
|
+
held.saved_view_id === null ? /* @__PURE__ */ jsx137("span", { className: "text-destructive", children: "This one names no view, so the store never admits a record to it." }) : null,
|
|
43903
|
+
held.muted ? /* @__PURE__ */ jsx137("span", { className: "text-muted-foreground", children: "Switched off \u2014 it tells nobody until somebody switches it back on." }) : null,
|
|
43904
|
+
onChoose ? held.rule_id === chosenRuleId ? /* @__PURE__ */ jsxs127("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
43905
|
+
/* @__PURE__ */ jsx137("span", { className: "font-medium text-foreground", children: "Used for this form" }),
|
|
43906
|
+
/* @__PURE__ */ jsx137(Button90, { size: "sm", variant: "ghost", disabled: busy, onClick: () => onChoose(null), children: "Stop using" })
|
|
43907
|
+
] }) : /* @__PURE__ */ jsx137(
|
|
43908
|
+
Button90,
|
|
43909
|
+
{
|
|
43910
|
+
size: "sm",
|
|
43911
|
+
variant: "outline",
|
|
43912
|
+
className: "ml-auto",
|
|
43913
|
+
disabled: busy,
|
|
43914
|
+
onClick: () => onChoose(held.rule_id),
|
|
43915
|
+
children: "Use for this form"
|
|
43916
|
+
}
|
|
43917
|
+
) : null,
|
|
43918
|
+
rights.write && !(onChoose && held.rule_id === chosenRuleId) && stopping === held.rule_id ? /* @__PURE__ */ jsxs127("span", { className: cn82("flex flex-wrap items-center gap-2", onChoose ? void 0 : "ml-auto"), children: [
|
|
43919
|
+
/* @__PURE__ */ jsx137("span", { className: "text-xs text-destructive", children: archiveConfirmSentence("this subscription") }),
|
|
43920
|
+
/* @__PURE__ */ jsx137(Button90, { size: "sm", variant: "destructive", disabled: busy, onClick: () => void unsubscribe(held.rule_id), children: "Stop telling me" }),
|
|
43921
|
+
/* @__PURE__ */ jsx137(Button90, { size: "sm", variant: "ghost", onClick: () => setStopping(null), children: "Keep it" })
|
|
43922
|
+
] }) : rights.write && !(onChoose && held.rule_id === chosenRuleId) ? /* @__PURE__ */ jsx137(
|
|
43923
|
+
Button90,
|
|
43924
|
+
{
|
|
43925
|
+
size: "sm",
|
|
43926
|
+
variant: "ghost",
|
|
43927
|
+
className: onChoose ? void 0 : "ml-auto",
|
|
43928
|
+
disabled: busy,
|
|
43929
|
+
onClick: () => setStopping(held.rule_id),
|
|
43930
|
+
children: "Stop telling me"
|
|
43931
|
+
}
|
|
43932
|
+
) : null
|
|
43933
|
+
] }, held.rule_id)) }),
|
|
43934
|
+
onChoose && chosenRuleId && !subscriptions.some((held) => held.rule_id === chosenRuleId) ? /* @__PURE__ */ jsx137("p", { className: "text-xs text-destructive", children: "This form points at a subscription that is not in this list any more, so nobody is told when it is answered. Choose one below, or make one." }) : null,
|
|
43935
|
+
views === null ? /* @__PURE__ */ jsx137("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_SAVED_VIEWS_REASON }) : views.length === 0 ? onChoose && rights.write ? /* @__PURE__ */ jsxs127("div", { className: "flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [
|
|
43936
|
+
/* @__PURE__ */ jsx137("span", { children: "A subscription watches a saved view, and this table has none yet." }),
|
|
43937
|
+
/* @__PURE__ */ jsx137(Button90, { size: "sm", variant: "outline", disabled: busy, onClick: () => void makeEveryRecordView(), children: "Watch every record" })
|
|
43938
|
+
] }) : /* @__PURE__ */ jsx137("p", { className: "text-xs text-muted-foreground", children: "This organization has no saved views yet, so there is nothing to subscribe to. Save a view first." }) : rights.write ? /* @__PURE__ */ jsxs127(
|
|
43939
|
+
"form",
|
|
43940
|
+
{
|
|
43941
|
+
className: "flex flex-wrap items-center gap-1",
|
|
43942
|
+
onSubmit: (event) => {
|
|
43943
|
+
event.preventDefault();
|
|
43944
|
+
const form = event.currentTarget;
|
|
43945
|
+
const view = form.elements.namedItem("view").value;
|
|
43946
|
+
const cadence = form.elements.namedItem("cadence").value;
|
|
43947
|
+
const channel = form.elements.namedItem("channel").value;
|
|
43948
|
+
const schedule = form.elements.namedItem("schedule").value;
|
|
43949
|
+
const start = form.elements.namedItem("quiet_start").value;
|
|
43950
|
+
const end = form.elements.namedItem("quiet_end").value;
|
|
43951
|
+
const name = views.find((v) => v.id === view)?.name ?? "View";
|
|
43952
|
+
const quiet2 = start && end ? { start, end, tz: Intl.DateTimeFormat().resolvedOptions().timeZone } : null;
|
|
43953
|
+
if (view) void subscribe(view, name, cadence, channel, schedule, quiet2);
|
|
43954
|
+
},
|
|
43955
|
+
children: [
|
|
43956
|
+
/* @__PURE__ */ jsx137("select", { name: "view", "aria-label": "View to be told about", className: "h-7 rounded border bg-background px-1 text-xs", children: views.map((view) => /* @__PURE__ */ jsx137("option", { value: view.id, children: view.name }, view.id)) }),
|
|
43957
|
+
/* @__PURE__ */ jsx137("select", { name: "cadence", "aria-label": "How often", className: "h-7 rounded border bg-background px-1 text-xs", children: (cadences.length > 0 ? cadences : ["instant"]).map((cadence) => /* @__PURE__ */ jsx137("option", { value: cadence, children: CADENCE_WORDS2[cadence] ?? cadence }, cadence)) }),
|
|
43958
|
+
/* @__PURE__ */ jsx137("select", { name: "channel", "aria-label": "Where it arrives", className: "h-7 rounded border bg-background px-1 text-xs", children: ["in_app", "email", "sms"].map((channel) => /* @__PURE__ */ jsx137("option", { value: channel, children: CHANNEL_WORDS3[channel] }, channel)) }),
|
|
43959
|
+
/* @__PURE__ */ jsx137(
|
|
43960
|
+
"input",
|
|
43961
|
+
{
|
|
43962
|
+
name: "schedule",
|
|
43963
|
+
"aria-label": "When the summary goes out",
|
|
43964
|
+
placeholder: "monday 08:00",
|
|
43965
|
+
className: "h-7 w-28 rounded border bg-background px-1 text-xs"
|
|
43966
|
+
}
|
|
43967
|
+
),
|
|
43968
|
+
/* @__PURE__ */ jsx137("span", { className: "text-xs text-muted-foreground", children: "not between" }),
|
|
43969
|
+
/* @__PURE__ */ jsx137(
|
|
43970
|
+
"input",
|
|
43971
|
+
{
|
|
43972
|
+
name: "quiet_start",
|
|
43973
|
+
type: "time",
|
|
43974
|
+
"aria-label": "Quiet hours start",
|
|
43975
|
+
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
43976
|
+
}
|
|
43977
|
+
),
|
|
43978
|
+
/* @__PURE__ */ jsx137("span", { className: "text-xs text-muted-foreground", children: "and" }),
|
|
43979
|
+
/* @__PURE__ */ jsx137(
|
|
43980
|
+
"input",
|
|
43981
|
+
{
|
|
43982
|
+
name: "quiet_end",
|
|
43983
|
+
type: "time",
|
|
43984
|
+
"aria-label": "Quiet hours end",
|
|
43985
|
+
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
43986
|
+
}
|
|
43987
|
+
),
|
|
43988
|
+
/* @__PURE__ */ jsx137(Button90, { size: "sm", type: "submit", disabled: busy, children: "Tell me" }),
|
|
43989
|
+
/* @__PURE__ */ jsx137("p", { className: "w-full text-xs text-muted-foreground", children: "A send inside your quiet hours is held until they end \u2014 it is never dropped. Leave the schedule blank for the store's own default of 08:00." })
|
|
43990
|
+
]
|
|
43991
|
+
}
|
|
43992
|
+
) : /* @__PURE__ */ jsx137("p", { className: "text-xs text-muted-foreground", children: rights.why("write") })
|
|
43993
|
+
] });
|
|
43994
|
+
}
|
|
43995
|
+
|
|
43531
43996
|
// src/DashboardCanvas.tsx
|
|
43532
43997
|
import { useCallback as useCallback59, useEffect as useEffect102, useMemo as useMemo71, useState as useState117 } from "react";
|
|
43533
43998
|
import { useFields as useFields30, useRecordsClient as useRecordsClient85, useTable as useTable31 } from "@ai-matrx/records/react";
|
|
@@ -43548,20 +44013,20 @@ import {
|
|
|
43548
44013
|
|
|
43549
44014
|
// src/DashboardGrid.tsx
|
|
43550
44015
|
import {
|
|
43551
|
-
DndContext,
|
|
43552
|
-
KeyboardSensor,
|
|
43553
|
-
PointerSensor,
|
|
44016
|
+
DndContext as DndContext2,
|
|
44017
|
+
KeyboardSensor as KeyboardSensor2,
|
|
44018
|
+
PointerSensor as PointerSensor2,
|
|
43554
44019
|
pointerWithin,
|
|
43555
|
-
useSensor,
|
|
43556
|
-
useSensors
|
|
44020
|
+
useSensor as useSensor2,
|
|
44021
|
+
useSensors as useSensors2
|
|
43557
44022
|
} from "@dnd-kit/core";
|
|
43558
44023
|
import {
|
|
43559
|
-
SortableContext,
|
|
44024
|
+
SortableContext as SortableContext2,
|
|
43560
44025
|
rectSortingStrategy,
|
|
43561
|
-
sortableKeyboardCoordinates,
|
|
43562
|
-
useSortable
|
|
44026
|
+
sortableKeyboardCoordinates as sortableKeyboardCoordinates2,
|
|
44027
|
+
useSortable as useSortable2
|
|
43563
44028
|
} from "@dnd-kit/sortable";
|
|
43564
|
-
import { CSS } from "@dnd-kit/utilities";
|
|
44029
|
+
import { CSS as CSS3 } from "@dnd-kit/utilities";
|
|
43565
44030
|
import { cn as cn83 } from "@ai-matrx/design-system";
|
|
43566
44031
|
|
|
43567
44032
|
// src/dashboardLayout.ts
|
|
@@ -43625,9 +44090,9 @@ var dashboardCollision = (args) => {
|
|
|
43625
44090
|
}).sort((a, b) => a.data.value - b.data.value);
|
|
43626
44091
|
};
|
|
43627
44092
|
function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
43628
|
-
const sensors =
|
|
43629
|
-
|
|
43630
|
-
|
|
44093
|
+
const sensors = useSensors2(
|
|
44094
|
+
useSensor2(PointerSensor2, { activationConstraint: { distance: 4 } }),
|
|
44095
|
+
useSensor2(KeyboardSensor2, { coordinateGetter: sortableKeyboardCoordinates2 })
|
|
43631
44096
|
);
|
|
43632
44097
|
const ids = items.map((item) => item.id);
|
|
43633
44098
|
function dragEnd(event) {
|
|
@@ -43637,7 +44102,7 @@ function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
|
43637
44102
|
}
|
|
43638
44103
|
const grid = /* @__PURE__ */ jsx138("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-12", children: items.map((item, index) => /* @__PURE__ */ jsx138(GridCell2, { item, index, arranging, onSpan }, item.id)) });
|
|
43639
44104
|
if (!arranging) return grid;
|
|
43640
|
-
return /* @__PURE__ */ jsx138(
|
|
44105
|
+
return /* @__PURE__ */ jsx138(DndContext2, { sensors, collisionDetection: dashboardCollision, onDragEnd: dragEnd, children: /* @__PURE__ */ jsx138(SortableContext2, { items: ids, strategy: rectSortingStrategy, children: grid }) });
|
|
43641
44106
|
}
|
|
43642
44107
|
function GridCell2({
|
|
43643
44108
|
item,
|
|
@@ -43645,8 +44110,8 @@ function GridCell2({
|
|
|
43645
44110
|
arranging,
|
|
43646
44111
|
onSpan
|
|
43647
44112
|
}) {
|
|
43648
|
-
const sortable =
|
|
43649
|
-
const style = arranging ? { transform:
|
|
44113
|
+
const sortable = useSortable2({ id: item.id, disabled: !arranging });
|
|
44114
|
+
const style = arranging ? { transform: CSS3.Transform.toString(sortable.transform), transition: sortable.transition } : void 0;
|
|
43650
44115
|
return /* @__PURE__ */ jsxs128(
|
|
43651
44116
|
"div",
|
|
43652
44117
|
{
|
|
@@ -44852,7 +45317,7 @@ function TargetControl({
|
|
|
44852
45317
|
// src/QuestionPanel.tsx
|
|
44853
45318
|
import { copyText as copyText8 } from "@ai-matrx/kit/clipboard";
|
|
44854
45319
|
import { useEffect as useEffect103, useMemo as useMemo72, useRef as useRef59, useState as useState118 } from "react";
|
|
44855
|
-
import { Badge as Badge23, BasicInput as BasicInput32, Button as Button94, Checkbox as Checkbox16, Label as
|
|
45320
|
+
import { Badge as Badge23, BasicInput as BasicInput32, Button as Button94, Checkbox as Checkbox16, Label as Label25, Skeleton as Skeleton35, cn as cn85 } from "@ai-matrx/design-system";
|
|
44856
45321
|
import { useFields as useFields31, useRecordsClient as useRecordsClient86 } from "@ai-matrx/records/react";
|
|
44857
45322
|
|
|
44858
45323
|
// src/question.ts
|
|
@@ -45434,9 +45899,9 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
|
|
|
45434
45899
|
linkMissed ? /* @__PURE__ */ jsx143("p", { className: "text-muted-foreground", children: "That question is not here or not shared with you." }) : null,
|
|
45435
45900
|
saveError ? /* @__PURE__ */ jsx143(RefusalNotice, { error: saveError }) : null,
|
|
45436
45901
|
/* @__PURE__ */ jsxs132("div", { className: "grid grid-cols-[6.5rem_minmax(0,1fr)] items-start gap-x-3 gap-y-2", "data-testid": "question-builder", children: [
|
|
45437
|
-
/* @__PURE__ */ jsx143(
|
|
45902
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Measure" }),
|
|
45438
45903
|
/* @__PURE__ */ jsx143(Measures, { measures: spec.measures, fields: offered, onChange: (measures) => patch({ measures }) }),
|
|
45439
|
-
/* @__PURE__ */ jsx143(
|
|
45904
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Filter" }),
|
|
45440
45905
|
/* @__PURE__ */ jsxs132("div", { className: "flex flex-col gap-1.5", children: [
|
|
45441
45906
|
/* @__PURE__ */ jsx143(
|
|
45442
45907
|
ConditionGroup,
|
|
@@ -45450,9 +45915,9 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
|
|
|
45450
45915
|
),
|
|
45451
45916
|
/* @__PURE__ */ jsx143(Through, { conditions: spec.through, related, onChange: (through) => patch({ through }) })
|
|
45452
45917
|
] }),
|
|
45453
|
-
/* @__PURE__ */ jsx143(
|
|
45918
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Summarize by" }),
|
|
45454
45919
|
/* @__PURE__ */ jsx143(GroupPicker, { spec, fields: offered, related, onChange: (group) => patch({ group }) }),
|
|
45455
|
-
/* @__PURE__ */ jsx143(
|
|
45920
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "As of" }),
|
|
45456
45921
|
/* @__PURE__ */ jsxs132("div", { className: "flex items-center gap-1.5", children: [
|
|
45457
45922
|
/* @__PURE__ */ jsx143(
|
|
45458
45923
|
Checkbox16,
|
|
@@ -45476,7 +45941,7 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
|
|
|
45476
45941
|
) : /* @__PURE__ */ jsx143("span", { className: "text-muted-foreground", children: "Now" })
|
|
45477
45942
|
] }),
|
|
45478
45943
|
spec.group ? /* @__PURE__ */ jsxs132(Fragment57, { children: [
|
|
45479
|
-
/* @__PURE__ */ jsx143(
|
|
45944
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Sort" }),
|
|
45480
45945
|
/* @__PURE__ */ jsxs132("div", { className: "flex flex-wrap items-center gap-1.5", children: [
|
|
45481
45946
|
/* @__PURE__ */ jsx143(
|
|
45482
45947
|
PickOne,
|
|
@@ -45507,7 +45972,7 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
|
|
|
45507
45972
|
}
|
|
45508
45973
|
)
|
|
45509
45974
|
] }),
|
|
45510
|
-
/* @__PURE__ */ jsx143(
|
|
45975
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Show as" }),
|
|
45511
45976
|
/* @__PURE__ */ jsx143("div", { className: "flex flex-wrap gap-1", role: "radiogroup", "aria-label": "Show as", children: SHOWS.map((s) => /* @__PURE__ */ jsx143(
|
|
45512
45977
|
Button94,
|
|
45513
45978
|
{
|