@ai-matrx/records-ui 0.105.1 → 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 +12 -0
- package/dist/index.cjs +1353 -878
- 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 +1349 -874
- 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 +4 -4
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,
|
|
@@ -14951,7 +14985,18 @@ function useMergedGridControls(args) {
|
|
|
14951
14985
|
label: "Duplicate property",
|
|
14952
14986
|
icon: "copy-plus",
|
|
14953
14987
|
disabledReason: mayStructure ? void 0 : editable ? rights.why("structure") : "Editing is off here.",
|
|
14954
|
-
|
|
14988
|
+
// DUPLICATE PROPERTY (Notion N5): one door declares the copy right after this column and copies
|
|
14989
|
+
// every record's value into it (`custom.field_duplicate`); nothing to fill in, as in Notion.
|
|
14990
|
+
onSelect: () => {
|
|
14991
|
+
void client.fieldDuplicate({ field_id: field2.id }).then((made) => {
|
|
14992
|
+
if (!made.ok) {
|
|
14993
|
+
notify?.error(`${fieldName(field2)} was not duplicated: ${refusalSentence(made.error)}`);
|
|
14994
|
+
return;
|
|
14995
|
+
}
|
|
14996
|
+
tableShapeChanged5(tableId);
|
|
14997
|
+
notify?.success(`${made.data.label} is added.`);
|
|
14998
|
+
});
|
|
14999
|
+
}
|
|
14955
15000
|
});
|
|
14956
15001
|
if (summaries) {
|
|
14957
15002
|
out.push(summaryMenuItem(field2, columnId2 === firstKey, summaries.chosen[columnId2], summaries.operations, (op2) => summaries.choose(columnId2, op2)));
|
|
@@ -15298,7 +15343,7 @@ function useMergedGridControls(args) {
|
|
|
15298
15343
|
},
|
|
15299
15344
|
children: /* @__PURE__ */ jsxs49(DialogContent8, { className: "max-h-[85vh] overflow-y-auto sm:max-w-2xl", children: [
|
|
15300
15345
|
/* @__PURE__ */ jsxs49(DialogHeader8, { children: [
|
|
15301
|
-
/* @__PURE__ */ jsx54(DialogTitle8, { children: columnDialog?.kind === "settings" ? `Column settings \xB7 ${fieldName(columnDialog.field)}` : columnDialog?.kind === "insert" && columnDialog.
|
|
15346
|
+
/* @__PURE__ */ jsx54(DialogTitle8, { children: columnDialog?.kind === "settings" ? `Column settings \xB7 ${fieldName(columnDialog.field)}` : columnDialog?.kind === "insert" && columnDialog.anchor ? `Add column ${columnDialog.side === "left" ? "left" : "right"} of ${fieldOf2(columnDialog.anchor) ? fieldName(fieldOf2(columnDialog.anchor)) : columnDialog.anchor}` : ADD_FIELD_OPEN_LABEL }),
|
|
15302
15347
|
/* @__PURE__ */ jsx54(DialogDescription6, { className: "sr-only", children: "Name, type and look" })
|
|
15303
15348
|
] }),
|
|
15304
15349
|
columnDialog?.kind === "settings" ? /* @__PURE__ */ jsx54(
|
|
@@ -15322,7 +15367,6 @@ function useMergedGridControls(args) {
|
|
|
15322
15367
|
FieldEditor,
|
|
15323
15368
|
{
|
|
15324
15369
|
tableId,
|
|
15325
|
-
...columnDialog.copyOf ? { copyOf: columnDialog.copyOf } : {},
|
|
15326
15370
|
onCancel: () => {
|
|
15327
15371
|
pendingInsert.current = null;
|
|
15328
15372
|
close();
|
|
@@ -37712,9 +37756,41 @@ function formPresentation(spec) {
|
|
|
37712
37756
|
welcome: welcomeDocument(spec.welcome ?? null),
|
|
37713
37757
|
endings: (spec.endings ?? []).map(endingDocument),
|
|
37714
37758
|
hidden_fields: spec.hiddenFields ?? [],
|
|
37715
|
-
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) }) } : {}
|
|
37716
37763
|
};
|
|
37717
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
|
+
}
|
|
37718
37794
|
var text = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : null;
|
|
37719
37795
|
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
37720
37796
|
var strip = (doc) => Object.fromEntries(Object.entries(doc).filter(([, v]) => v !== null && v !== void 0));
|
|
@@ -37889,6 +37965,9 @@ function formFromSummary(summary) {
|
|
|
37889
37965
|
thankYou: thankYouFromDocument(presentation["thank_you"]),
|
|
37890
37966
|
look: formLookFromDocument(presentation["look"]),
|
|
37891
37967
|
submitLabel: presentation["submit_label"] ?? null,
|
|
37968
|
+
showOwnerHeader: bool(presentation["show_owner_header"]),
|
|
37969
|
+
autoAdvance: bool(presentation["auto_advance"]),
|
|
37970
|
+
notify: notifyFromDocument(presentation["notify"]),
|
|
37892
37971
|
// A form is public when the store says it is OPEN. A screen never decides
|
|
37893
37972
|
// this from a flag of its own: the link answers or it does not.
|
|
37894
37973
|
isPublic: summary.state === "open",
|
|
@@ -38693,6 +38772,50 @@ function compactTick(value) {
|
|
|
38693
38772
|
if (Math.abs(value) < 1e3 && !Number.isInteger(value)) return String(Math.round(value * 10) / 10);
|
|
38694
38773
|
return formatCount5(value, { style: "compact" });
|
|
38695
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
|
+
}
|
|
38696
38819
|
function Drawing({
|
|
38697
38820
|
kind,
|
|
38698
38821
|
points,
|
|
@@ -38798,9 +38921,9 @@ function Drawing({
|
|
|
38798
38921
|
/* @__PURE__ */ jsx120(CartesianGrid, { vertical: horizontal, horizontal: !horizontal }),
|
|
38799
38922
|
horizontal ? /* @__PURE__ */ jsxs110(Fragment48, { children: [
|
|
38800
38923
|
/* @__PURE__ */ jsx120(XAxis, { type: "number", ...axis }),
|
|
38801
|
-
/* @__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 }) })
|
|
38802
38925
|
] }) : /* @__PURE__ */ jsxs110(Fragment48, { children: [
|
|
38803
|
-
/* @__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)) }) }),
|
|
38804
38927
|
/* @__PURE__ */ jsx120(YAxis, { width: 44, ...axis })
|
|
38805
38928
|
] }),
|
|
38806
38929
|
tooltip,
|
|
@@ -40449,7 +40572,7 @@ function FormLookLogo({ view, size = "md" }) {
|
|
|
40449
40572
|
}
|
|
40450
40573
|
);
|
|
40451
40574
|
}
|
|
40452
|
-
function FormLookFrame({ look, title, children, contained = false, className }) {
|
|
40575
|
+
function FormLookFrame({ look, title, children, contained = false, showOwnerHeader = true, className }) {
|
|
40453
40576
|
const view = formLookView(look, title);
|
|
40454
40577
|
return /* @__PURE__ */ jsxs117(
|
|
40455
40578
|
"div",
|
|
@@ -40466,10 +40589,10 @@ function FormLookFrame({ look, title, children, contained = false, className })
|
|
|
40466
40589
|
}
|
|
40467
40590
|
) : null,
|
|
40468
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,
|
|
40469
|
-
/* @__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: [
|
|
40470
40593
|
/* @__PURE__ */ jsx127(FormLookLogo, { view }),
|
|
40471
40594
|
/* @__PURE__ */ jsx127("p", { className: "min-w-0 truncate text-base font-semibold tracking-tight text-foreground", children: view.name })
|
|
40472
|
-
] }),
|
|
40595
|
+
] }) : null,
|
|
40473
40596
|
/* @__PURE__ */ jsx127("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
|
|
40474
40597
|
/* @__PURE__ */ jsx127("div", { className: "w-full", children }),
|
|
40475
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(
|
|
@@ -41292,17 +41415,20 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
|
41292
41415
|
// src/FormBuilder.tsx
|
|
41293
41416
|
import { copyText as copyText7 } from "@ai-matrx/kit/clipboard";
|
|
41294
41417
|
import { ErrorBox as ErrorBox7 } from "@ai-matrx/design-system";
|
|
41295
|
-
import { useCallback as
|
|
41296
|
-
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";
|
|
41297
41423
|
import { publicFormPath } from "@ai-matrx/records";
|
|
41298
41424
|
import {
|
|
41299
41425
|
BasicInput as BasicInput28,
|
|
41300
41426
|
BasicTextarea as BasicTextarea11,
|
|
41301
|
-
Button as
|
|
41427
|
+
Button as Button86,
|
|
41302
41428
|
Checkbox as Checkbox15,
|
|
41303
|
-
Label as
|
|
41304
|
-
Skeleton as
|
|
41305
|
-
cn as
|
|
41429
|
+
Label as Label23,
|
|
41430
|
+
Skeleton as Skeleton29,
|
|
41431
|
+
cn as cn78
|
|
41306
41432
|
} from "@ai-matrx/design-system";
|
|
41307
41433
|
|
|
41308
41434
|
// src/FormFlowEditor.tsx
|
|
@@ -41560,7 +41686,7 @@ function EmbedSnippets({ url, label }) {
|
|
|
41560
41686
|
// src/FormRunner.tsx
|
|
41561
41687
|
import { useCallback as useCallback53, useEffect as useEffect95, useMemo as useMemo67, useRef as useRef55, useState as useState109 } from "react";
|
|
41562
41688
|
import { useFields as useFields24, useOptionalRecordsClient as useOptionalRecordsClient14 } from "@ai-matrx/records/react";
|
|
41563
|
-
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";
|
|
41564
41690
|
import { humanizeIdentifier as humanizeIdentifier18 } from "@ai-matrx/kit/text-case";
|
|
41565
41691
|
import { Fragment as Fragment53, jsx as jsx132, jsxs as jsxs122 } from "react/jsx-runtime";
|
|
41566
41692
|
function authored(host, text2, where) {
|
|
@@ -41659,7 +41785,8 @@ function FormStage({
|
|
|
41659
41785
|
className,
|
|
41660
41786
|
connected = false,
|
|
41661
41787
|
onStart,
|
|
41662
|
-
onQuestionShown
|
|
41788
|
+
onQuestionShown,
|
|
41789
|
+
autoAdvance
|
|
41663
41790
|
}) {
|
|
41664
41791
|
const host = useRecordsUi();
|
|
41665
41792
|
const [answers, setAnswers] = useState109(() => ({ ...initialAnswers ?? {} }));
|
|
@@ -41672,6 +41799,11 @@ function FormStage({
|
|
|
41672
41799
|
const [ending, setEnding] = useState109(null);
|
|
41673
41800
|
const [started, setStarted] = useState109(() => !form.welcome || resumed);
|
|
41674
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;
|
|
41675
41807
|
const decoy = useRef55("");
|
|
41676
41808
|
const stage = useRef55(null);
|
|
41677
41809
|
const questions = useMemo67(() => {
|
|
@@ -41707,6 +41839,7 @@ function FormStage({
|
|
|
41707
41839
|
};
|
|
41708
41840
|
}
|
|
41709
41841
|
if (cancelled) return;
|
|
41842
|
+
setRouteKnown(true);
|
|
41710
41843
|
if (!told2.ok) {
|
|
41711
41844
|
setHidden({});
|
|
41712
41845
|
setError(told2.error ?? refusal("not_supported", told2.message, "Answer every question shown; none were skipped."));
|
|
@@ -41728,7 +41861,7 @@ function FormStage({
|
|
|
41728
41861
|
) : null
|
|
41729
41862
|
);
|
|
41730
41863
|
})();
|
|
41731
|
-
}, 250);
|
|
41864
|
+
}, routeKnown ? 250 : 0);
|
|
41732
41865
|
return () => {
|
|
41733
41866
|
cancelled = true;
|
|
41734
41867
|
clearTimeout(timer);
|
|
@@ -41763,11 +41896,24 @@ function FormStage({
|
|
|
41763
41896
|
const v = answers[q.key];
|
|
41764
41897
|
return v !== void 0 && v !== null && v !== "";
|
|
41765
41898
|
}).length;
|
|
41766
|
-
const
|
|
41767
|
-
|
|
41768
|
-
|
|
41769
|
-
|
|
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
|
+
};
|
|
41770
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
|
+
}
|
|
41771
41917
|
if (preview) {
|
|
41772
41918
|
setDone("preview");
|
|
41773
41919
|
return;
|
|
@@ -41799,20 +41945,28 @@ function FormStage({
|
|
|
41799
41945
|
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
41800
41946
|
async function advance() {
|
|
41801
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);
|
|
41802
41956
|
if (!routes || !whichAsked || !current) {
|
|
41803
41957
|
if (index < live.length - 1) setAt(index + 1);
|
|
41804
|
-
else
|
|
41958
|
+
else void submit();
|
|
41805
41959
|
return;
|
|
41806
41960
|
}
|
|
41807
41961
|
let told2;
|
|
41808
41962
|
try {
|
|
41809
|
-
told2 = await whichAsked(
|
|
41963
|
+
told2 = await whichAsked(answersRef.current);
|
|
41810
41964
|
} catch {
|
|
41811
41965
|
told2 = { ok: false, message: "This form could not work out which question comes next, so the next one is shown." };
|
|
41812
41966
|
}
|
|
41813
41967
|
if (!told2.ok) {
|
|
41814
41968
|
if (index < live.length - 1) setAt(index + 1);
|
|
41815
|
-
else
|
|
41969
|
+
else void submit();
|
|
41816
41970
|
return;
|
|
41817
41971
|
}
|
|
41818
41972
|
const next = {};
|
|
@@ -41825,13 +41979,18 @@ function FormStage({
|
|
|
41825
41979
|
setAt(here + 1);
|
|
41826
41980
|
return;
|
|
41827
41981
|
}
|
|
41828
|
-
const stillMissing = onRoute.
|
|
41829
|
-
|
|
41830
|
-
|
|
41831
|
-
});
|
|
41832
|
-
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();
|
|
41833
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]);
|
|
41834
41992
|
function retreat() {
|
|
41993
|
+
setStopped(null);
|
|
41835
41994
|
if (oneAtATime && index > 0) setAt(index - 1);
|
|
41836
41995
|
}
|
|
41837
41996
|
function onKeyDown(event) {
|
|
@@ -41847,12 +42006,11 @@ function FormStage({
|
|
|
41847
42006
|
else advance();
|
|
41848
42007
|
}
|
|
41849
42008
|
useEffect95(() => {
|
|
41850
|
-
const input = stage.current?.querySelector(
|
|
41851
|
-
// 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(
|
|
41852
42010
|
"input:not([tabindex='-1']), textarea, select, [role='combobox'], [role='radio'], [role='checkbox']"
|
|
41853
42011
|
);
|
|
41854
42012
|
input?.focus();
|
|
41855
|
-
}, [index, done]);
|
|
42013
|
+
}, [index, done, started]);
|
|
41856
42014
|
const startTold = useRef55(false);
|
|
41857
42015
|
useEffect95(() => {
|
|
41858
42016
|
if (startTold.current || !onStart) return;
|
|
@@ -41871,6 +42029,10 @@ function FormStage({
|
|
|
41871
42029
|
}
|
|
41872
42030
|
}, [current, live, oneAtATime, onQuestionShown, started, done]);
|
|
41873
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
|
+
});
|
|
41874
42036
|
if (done) {
|
|
41875
42037
|
return /* @__PURE__ */ jsx132(
|
|
41876
42038
|
ThankYou,
|
|
@@ -41879,6 +42041,7 @@ function FormStage({
|
|
|
41879
42041
|
done,
|
|
41880
42042
|
ending: (form.endings ?? []).find((e) => e.id === ending) ?? null,
|
|
41881
42043
|
buttonClass: theme.buttonClass,
|
|
42044
|
+
pipe,
|
|
41882
42045
|
className: cn77("mx-auto max-w-xl py-10", centred && "text-center", className)
|
|
41883
42046
|
}
|
|
41884
42047
|
);
|
|
@@ -41890,14 +42053,14 @@ function FormStage({
|
|
|
41890
42053
|
return /* @__PURE__ */ jsxs122(
|
|
41891
42054
|
"section",
|
|
41892
42055
|
{
|
|
41893
|
-
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),
|
|
41894
42057
|
style: theme.surfaceStyle,
|
|
41895
42058
|
"data-records-form-welcome": "",
|
|
41896
42059
|
children: [
|
|
41897
42060
|
w.pictureUrl ? /* @__PURE__ */ jsx132("img", { src: w.pictureUrl, alt: "", className: "max-h-56 w-full rounded-md object-cover" }) : null,
|
|
41898
|
-
w.title ? /* @__PURE__ */ jsx132("h2", { className: "text-
|
|
41899
|
-
w.body ? /* @__PURE__ */ jsx132("p", { className: "text-
|
|
41900
|
-
/* @__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" }) })
|
|
41901
42064
|
]
|
|
41902
42065
|
}
|
|
41903
42066
|
);
|
|
@@ -41906,7 +42069,15 @@ function FormStage({
|
|
|
41906
42069
|
return /* @__PURE__ */ jsxs122(
|
|
41907
42070
|
"section",
|
|
41908
42071
|
{
|
|
41909
|
-
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
|
+
),
|
|
41910
42081
|
style: theme.surfaceStyle,
|
|
41911
42082
|
"data-records-form-font": theme.font,
|
|
41912
42083
|
onKeyDown,
|
|
@@ -41916,20 +42087,41 @@ function FormStage({
|
|
|
41916
42087
|
Progress,
|
|
41917
42088
|
{
|
|
41918
42089
|
value: live.length === 0 ? 0 : (oneAtATime ? index + 1 : answered) / live.length * 100,
|
|
41919
|
-
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}` } : {}
|
|
41920
42093
|
}
|
|
41921
42094
|
),
|
|
41922
|
-
/* @__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}` })
|
|
41923
42096
|
] }),
|
|
41924
|
-
signedInPublic ? /* @__PURE__ */ jsx132(
|
|
41925
|
-
|
|
41926
|
-
|
|
41927
|
-
|
|
41928
|
-
|
|
41929
|
-
|
|
41930
|
-
|
|
41931
|
-
|
|
41932
|
-
|
|
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,
|
|
41933
42125
|
error ? /* @__PURE__ */ jsx132(RefusalNotice, { error, className: "text-left" }) : null,
|
|
41934
42126
|
refusal4 ? /* @__PURE__ */ jsx132(
|
|
41935
42127
|
RefusalNotice,
|
|
@@ -41951,14 +42143,20 @@ function FormStage({
|
|
|
41951
42143
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ jsx132(
|
|
41952
42144
|
Question,
|
|
41953
42145
|
{
|
|
41954
|
-
question: q,
|
|
42146
|
+
question: { ...q, ask: pipe(q.ask), help: q.help ? pipe(q.help) : null },
|
|
41955
42147
|
value: answers[q.key],
|
|
41956
|
-
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,
|
|
41957
42155
|
upload: host.upload
|
|
41958
42156
|
},
|
|
41959
42157
|
q.key
|
|
41960
42158
|
)),
|
|
41961
|
-
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
|
|
41962
42160
|
] }),
|
|
41963
42161
|
honeypotKey ? /* @__PURE__ */ jsxs122("div", { "aria-hidden": true, className: "pointer-events-none absolute left-[-9999px] h-px w-px overflow-hidden", children: [
|
|
41964
42162
|
/* @__PURE__ */ jsx132("label", { htmlFor: `hp-${honeypotKey}`, children: "Leave this field empty" }),
|
|
@@ -41977,16 +42175,14 @@ function FormStage({
|
|
|
41977
42175
|
}
|
|
41978
42176
|
)
|
|
41979
42177
|
] }) : null,
|
|
41980
|
-
/* @__PURE__ */ jsxs122("footer", { className: cn77("flex flex-wrap items-center gap-
|
|
41981
|
-
oneAtATime && index > 0 ? /* @__PURE__ */ jsx132(Button85, { size: "
|
|
41982
|
-
oneAtATime && index < live.length - 1 ? /* @__PURE__ */ jsx132(Button85, { size: "
|
|
41983
|
-
|
|
41984
|
-
|
|
41985
|
-
"
|
|
41986
|
-
missing.length === 1 ? "needs" : "need",
|
|
41987
|
-
" 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" })
|
|
41988
42184
|
] }) : null,
|
|
41989
|
-
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
|
|
41990
42186
|
] })
|
|
41991
42187
|
]
|
|
41992
42188
|
}
|
|
@@ -41998,12 +42194,15 @@ function ThankYou({
|
|
|
41998
42194
|
done,
|
|
41999
42195
|
className,
|
|
42000
42196
|
ending,
|
|
42001
|
-
buttonClass
|
|
42197
|
+
buttonClass,
|
|
42198
|
+
pipe = (t) => t
|
|
42002
42199
|
}) {
|
|
42003
42200
|
const host = useRecordsUi();
|
|
42004
42201
|
const redirect = (ending ? ending.redirectUrl?.trim() : form.thankYou?.redirectUrl?.trim()) || null;
|
|
42005
|
-
const
|
|
42006
|
-
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;
|
|
42007
42206
|
const going = redirect !== null && done !== "preview";
|
|
42008
42207
|
useEffect95(() => {
|
|
42009
42208
|
if (!going || !redirect) return;
|
|
@@ -42020,8 +42219,8 @@ function ThankYou({
|
|
|
42020
42219
|
}
|
|
42021
42220
|
),
|
|
42022
42221
|
ending?.pictureUrl ? /* @__PURE__ */ jsx132("img", { src: ending.pictureUrl, alt: "", className: "mb-3 max-h-48 w-full rounded-md object-cover" }) : null,
|
|
42023
|
-
/* @__PURE__ */ jsx132("h2", { className: "text-
|
|
42024
|
-
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,
|
|
42025
42224
|
going ? /* @__PURE__ */ jsxs122("p", { className: "mt-4 flex flex-wrap items-center gap-3 text-sm", children: [
|
|
42026
42225
|
/* @__PURE__ */ jsx132(
|
|
42027
42226
|
"a",
|
|
@@ -42034,11 +42233,11 @@ function ThankYou({
|
|
|
42034
42233
|
),
|
|
42035
42234
|
/* @__PURE__ */ jsx132("span", { className: "text-muted-foreground", children: "Taking you there\u2026" })
|
|
42036
42235
|
] }) : null,
|
|
42037
|
-
/* @__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" ? (
|
|
42038
42237
|
// SAID TO THE PERSON WHO ANSWERED (lane HANDOVER, 2026-09-29): "Saved to Form 1, with
|
|
42039
42238
|
// this form stamped on it" was the owner's bookkeeping read out to a front-desk member
|
|
42040
42239
|
// and to strangers. Where it landed is the owner's to see on the table.
|
|
42041
|
-
"
|
|
42240
|
+
"Answers received"
|
|
42042
42241
|
) : done })
|
|
42043
42242
|
] });
|
|
42044
42243
|
}
|
|
@@ -42046,6 +42245,9 @@ function Question({
|
|
|
42046
42245
|
question,
|
|
42047
42246
|
value,
|
|
42048
42247
|
onChange,
|
|
42248
|
+
onChosen,
|
|
42249
|
+
refused,
|
|
42250
|
+
large,
|
|
42049
42251
|
upload
|
|
42050
42252
|
}) {
|
|
42051
42253
|
const host = useRecordsUi();
|
|
@@ -42053,297 +42255,62 @@ function Question({
|
|
|
42053
42255
|
const field2 = question.field;
|
|
42054
42256
|
if (!field2) return null;
|
|
42055
42257
|
const id = `form-${field2.key}`;
|
|
42258
|
+
const sayId = `${id}-refused`;
|
|
42056
42259
|
const isAttachment = editorKindFor(field2) === "attachment";
|
|
42057
|
-
return /* @__PURE__ */ jsxs122(
|
|
42058
|
-
|
|
42059
|
-
|
|
42060
|
-
|
|
42061
|
-
|
|
42062
|
-
"
|
|
42063
|
-
{
|
|
42064
|
-
id,
|
|
42065
|
-
type: "file",
|
|
42066
|
-
className: "text-xs",
|
|
42067
|
-
onChange: (e) => {
|
|
42068
|
-
const file = e.target.files?.[0];
|
|
42069
|
-
if (!file) return;
|
|
42070
|
-
setUploadError(null);
|
|
42071
|
-
void upload?.(file).then((result) => {
|
|
42072
|
-
if (!result) return;
|
|
42073
|
-
if (result.ok) onChange(result.fileId);
|
|
42074
|
-
else
|
|
42075
|
-
setUploadError(
|
|
42076
|
-
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
42077
|
-
);
|
|
42078
|
-
});
|
|
42079
|
-
}
|
|
42080
|
-
}
|
|
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"
|
|
42081
42266
|
),
|
|
42082
|
-
|
|
42083
|
-
|
|
42084
|
-
|
|
42085
|
-
|
|
42086
|
-
|
|
42087
|
-
|
|
42088
|
-
}
|
|
42089
|
-
|
|
42090
|
-
|
|
42091
|
-
|
|
42092
|
-
|
|
42093
|
-
import { hasQuietHours as hasQuietHours3 } from "@ai-matrx/records";
|
|
42094
|
-
import { Button as Button86, Skeleton as Skeleton29, cn as cn78 } from "@ai-matrx/design-system";
|
|
42095
|
-
import { jsx as jsx133, jsxs as jsxs123 } from "react/jsx-runtime";
|
|
42096
|
-
var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
|
|
42097
|
-
var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
42098
|
-
function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className }) {
|
|
42099
|
-
const client = useRecordsClient79();
|
|
42100
|
-
const host = useRecordsUi();
|
|
42101
|
-
const table = useTable26(tableId);
|
|
42102
|
-
const rights = useTableRights(table.data);
|
|
42103
|
-
const [subscriptions, setSubscriptions] = useState110(null);
|
|
42104
|
-
const [cadences, setCadences] = useState110([]);
|
|
42105
|
-
const [views, setViews] = useState110(null);
|
|
42106
|
-
const [error, setError] = useState110(null);
|
|
42107
|
-
const [busy, setBusy] = useState110(false);
|
|
42108
|
-
const [stopping, setStopping] = useState110(null);
|
|
42109
|
-
const load = useCallback54(async () => {
|
|
42110
|
-
const [held, offered] = await Promise.all([
|
|
42111
|
-
// THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
|
|
42112
|
-
// to them plus — only where they hold admin on this Table — anyone's over
|
|
42113
|
-
// it, and it is narrowed to Tables they can already open.
|
|
42114
|
-
client.subscriptions({ table_id: tableId }),
|
|
42115
|
-
// The cadences are ASKED of the store, never a list typed into a picker
|
|
42116
|
-
// here that could drift from what the digest runner understands.
|
|
42117
|
-
client.subscriptionCadences()
|
|
42118
|
-
]);
|
|
42119
|
-
if (!held.ok) {
|
|
42120
|
-
setError(held.error);
|
|
42121
|
-
return;
|
|
42122
|
-
}
|
|
42123
|
-
setError(null);
|
|
42124
|
-
setSubscriptions(held.data);
|
|
42125
|
-
if (offered.ok) setCadences(offered.data);
|
|
42126
|
-
if (host.savedViews) {
|
|
42127
|
-
setViews(await host.savedViews());
|
|
42128
|
-
return;
|
|
42129
|
-
}
|
|
42130
|
-
const listed = await client.views({ table_id: tableId });
|
|
42131
|
-
if (!listed.ok) {
|
|
42132
|
-
setError(listed.error);
|
|
42133
|
-
return;
|
|
42134
|
-
}
|
|
42135
|
-
setViews(listed.data.map((v) => ({ id: v.view_id, name: v.name })));
|
|
42136
|
-
}, [client, host, tableId]);
|
|
42137
|
-
useEffect96(() => {
|
|
42138
|
-
void load();
|
|
42139
|
-
}, [load]);
|
|
42140
|
-
const write = useCallback54(
|
|
42141
|
-
async (spec) => {
|
|
42142
|
-
const declared2 = await client.subscriptionDeclare({
|
|
42143
|
-
table_id: tableId,
|
|
42144
|
-
spec: {
|
|
42145
|
-
name: spec.name,
|
|
42146
|
-
saved_view_id: spec.savedViewId,
|
|
42147
|
-
cadence: spec.cadence ?? "instant",
|
|
42148
|
-
schedule: spec.schedule ?? null,
|
|
42149
|
-
channel: spec.channel ?? "in_app",
|
|
42150
|
-
quiet_hours: spec.quietHours ?? null,
|
|
42151
|
-
// "Tell me" is the default, and the store refuses a subscription with
|
|
42152
|
-
// nobody to tell rather than writing one that fires at nobody.
|
|
42153
|
-
recipient_user_id: spec.recipientUserId ?? client.config.actor.user_id ?? null,
|
|
42154
|
-
event_key: spec.eventKey ?? "records.changed"
|
|
42155
|
-
}
|
|
42156
|
-
});
|
|
42157
|
-
if (!declared2.ok) {
|
|
42158
|
-
setError(declared2.error);
|
|
42159
|
-
return null;
|
|
42160
|
-
}
|
|
42161
|
-
return declared2.data;
|
|
42162
|
-
},
|
|
42163
|
-
[client, tableId]
|
|
42164
|
-
);
|
|
42165
|
-
useEffect96(() => {
|
|
42166
|
-
if (!subscriptions || !seed || seed.length === 0) return;
|
|
42167
|
-
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
42168
|
-
if (missing.length === 0) return;
|
|
42169
|
-
void (async () => {
|
|
42170
|
-
for (const spec of missing) {
|
|
42171
|
-
if (!await write(spec)) return;
|
|
42172
|
-
}
|
|
42173
|
-
await load();
|
|
42174
|
-
})();
|
|
42175
|
-
}, [subscriptions, seed, write, load]);
|
|
42176
|
-
async function subscribe(viewId, viewName, cadence, channel, schedule, quiet2) {
|
|
42177
|
-
setBusy(true);
|
|
42178
|
-
const ok = await write({
|
|
42179
|
-
name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
|
|
42180
|
-
savedViewId: viewId,
|
|
42181
|
-
cadence,
|
|
42182
|
-
channel,
|
|
42183
|
-
schedule: schedule.trim() === "" ? null : schedule.trim(),
|
|
42184
|
-
quietHours: quiet2
|
|
42185
|
-
});
|
|
42186
|
-
setBusy(false);
|
|
42187
|
-
if (!ok) return;
|
|
42188
|
-
onChoose?.(ok);
|
|
42189
|
-
await load();
|
|
42190
|
-
}
|
|
42191
|
-
async function makeEveryRecordView() {
|
|
42192
|
-
setBusy(true);
|
|
42193
|
-
const made = await client.viewDeclare({
|
|
42194
|
-
table_id: tableId,
|
|
42195
|
-
spec: { name: `Every ${table.data?.name ?? "record"}`, filters: null }
|
|
42196
|
-
});
|
|
42197
|
-
setBusy(false);
|
|
42198
|
-
if (!made.ok) {
|
|
42199
|
-
setError(made.error);
|
|
42200
|
-
return;
|
|
42201
|
-
}
|
|
42202
|
-
await load();
|
|
42203
|
-
}
|
|
42204
|
-
async function unsubscribe(ruleId) {
|
|
42205
|
-
setStopping(null);
|
|
42206
|
-
setBusy(true);
|
|
42207
|
-
const deleted = await client.recordDelete({ record_id: ruleId });
|
|
42208
|
-
setBusy(false);
|
|
42209
|
-
if (!deleted.ok) {
|
|
42210
|
-
setError(deleted.error);
|
|
42211
|
-
return;
|
|
42212
|
-
}
|
|
42213
|
-
await load();
|
|
42214
|
-
}
|
|
42215
|
-
if (error) {
|
|
42216
|
-
return /* @__PURE__ */ jsx133(
|
|
42217
|
-
RefusalNotice,
|
|
42218
|
-
{
|
|
42219
|
-
error,
|
|
42220
|
-
className,
|
|
42221
|
-
actions: /* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
42222
|
-
}
|
|
42223
|
-
);
|
|
42224
|
-
}
|
|
42225
|
-
if (subscriptions === null) return /* @__PURE__ */ jsx133(Skeleton29, { className: cn78("h-40 w-full", className) });
|
|
42226
|
-
return /* @__PURE__ */ jsxs123("div", { className: cn78("flex flex-col gap-2", className), children: [
|
|
42227
|
-
/* @__PURE__ */ jsxs123("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
42228
|
-
/* @__PURE__ */ jsx133("span", { className: "font-medium text-foreground", children: "Tell me when" }),
|
|
42229
|
-
/* @__PURE__ */ jsxs123("span", { children: [
|
|
42230
|
-
subscriptions.length,
|
|
42231
|
-
" subscription",
|
|
42232
|
-
subscriptions.length === 1 ? "" : "s"
|
|
42233
|
-
] })
|
|
42234
|
-
] }),
|
|
42235
|
-
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: [
|
|
42236
|
-
/* @__PURE__ */ jsx133("span", { className: "truncate font-medium", children: held.name }),
|
|
42237
|
-
/* @__PURE__ */ jsx133("span", { className: "text-muted-foreground", children: CADENCE_WORDS2[held.cadence] ?? held.cadence }),
|
|
42238
|
-
/* @__PURE__ */ jsx133("span", { className: "text-muted-foreground", children: CHANNEL_WORDS3[held.channel] ?? held.channel }),
|
|
42239
|
-
held.schedule ? /* @__PURE__ */ jsx133("span", { className: "text-muted-foreground", children: scheduleWords(held.schedule, held.quiet_hours?.tz) }) : null,
|
|
42240
|
-
held.next_digest_at ? /* @__PURE__ */ jsxs123("span", { className: "text-muted-foreground", children: [
|
|
42241
|
-
"next ",
|
|
42242
|
-
new Date(held.next_digest_at).toLocaleString()
|
|
42243
|
-
] }) : null,
|
|
42244
|
-
hasQuietHours3(held.quiet_hours) ? /* @__PURE__ */ jsxs123("span", { className: "text-muted-foreground", children: [
|
|
42245
|
-
"quiet ",
|
|
42246
|
-
held.quiet_hours.start,
|
|
42247
|
-
"\u2013",
|
|
42248
|
-
held.quiet_hours.end
|
|
42249
|
-
] }) : null,
|
|
42250
|
-
held.recipient_user_id === null ? /* @__PURE__ */ jsx133("span", { className: "text-destructive", children: "This one has nobody to tell, so it fires at nobody." }) : null,
|
|
42251
|
-
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,
|
|
42252
|
-
held.muted ? /* @__PURE__ */ jsx133("span", { className: "text-muted-foreground", children: "Switched off \u2014 it tells nobody until somebody switches it back on." }) : null,
|
|
42253
|
-
onChoose ? held.rule_id === chosenRuleId ? /* @__PURE__ */ jsxs123("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
42254
|
-
/* @__PURE__ */ jsx133("span", { className: "font-medium text-foreground", children: "Used for this form" }),
|
|
42255
|
-
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "ghost", disabled: busy, onClick: () => onChoose(null), children: "Stop using" })
|
|
42256
|
-
] }) : /* @__PURE__ */ jsx133(
|
|
42257
|
-
Button86,
|
|
42258
|
-
{
|
|
42259
|
-
size: "sm",
|
|
42260
|
-
variant: "outline",
|
|
42261
|
-
className: "ml-auto",
|
|
42262
|
-
disabled: busy,
|
|
42263
|
-
onClick: () => onChoose(held.rule_id),
|
|
42264
|
-
children: "Use for this form"
|
|
42265
|
-
}
|
|
42266
|
-
) : null,
|
|
42267
|
-
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: [
|
|
42268
|
-
/* @__PURE__ */ jsx133("span", { className: "text-xs text-destructive", children: archiveConfirmSentence("this subscription") }),
|
|
42269
|
-
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "destructive", disabled: busy, onClick: () => void unsubscribe(held.rule_id), children: "Stop telling me" }),
|
|
42270
|
-
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "ghost", onClick: () => setStopping(null), children: "Keep it" })
|
|
42271
|
-
] }) : rights.write && !(onChoose && held.rule_id === chosenRuleId) ? /* @__PURE__ */ jsx133(
|
|
42272
|
-
Button86,
|
|
42273
|
-
{
|
|
42274
|
-
size: "sm",
|
|
42275
|
-
variant: "ghost",
|
|
42276
|
-
className: onChoose ? void 0 : "ml-auto",
|
|
42277
|
-
disabled: busy,
|
|
42278
|
-
onClick: () => setStopping(held.rule_id),
|
|
42279
|
-
children: "Stop telling me"
|
|
42280
|
-
}
|
|
42281
|
-
) : null
|
|
42282
|
-
] }, held.rule_id)) }),
|
|
42283
|
-
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,
|
|
42284
|
-
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: [
|
|
42285
|
-
/* @__PURE__ */ jsx133("span", { children: "A subscription watches a saved view, and this table has none yet." }),
|
|
42286
|
-
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "outline", disabled: busy, onClick: () => void makeEveryRecordView(), children: "Watch every record" })
|
|
42287
|
-
] }) : /* @__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(
|
|
42288
|
-
"form",
|
|
42289
|
-
{
|
|
42290
|
-
className: "flex flex-wrap items-center gap-1",
|
|
42291
|
-
onSubmit: (event) => {
|
|
42292
|
-
event.preventDefault();
|
|
42293
|
-
const form = event.currentTarget;
|
|
42294
|
-
const view = form.elements.namedItem("view").value;
|
|
42295
|
-
const cadence = form.elements.namedItem("cadence").value;
|
|
42296
|
-
const channel = form.elements.namedItem("channel").value;
|
|
42297
|
-
const schedule = form.elements.namedItem("schedule").value;
|
|
42298
|
-
const start = form.elements.namedItem("quiet_start").value;
|
|
42299
|
-
const end = form.elements.namedItem("quiet_end").value;
|
|
42300
|
-
const name = views.find((v) => v.id === view)?.name ?? "View";
|
|
42301
|
-
const quiet2 = start && end ? { start, end, tz: Intl.DateTimeFormat().resolvedOptions().timeZone } : null;
|
|
42302
|
-
if (view) void subscribe(view, name, cadence, channel, schedule, quiet2);
|
|
42303
|
-
},
|
|
42304
|
-
children: [
|
|
42305
|
-
/* @__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)) }),
|
|
42306
|
-
/* @__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)) }),
|
|
42307
|
-
/* @__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)) }),
|
|
42308
|
-
/* @__PURE__ */ jsx133(
|
|
42309
|
-
"input",
|
|
42310
|
-
{
|
|
42311
|
-
name: "schedule",
|
|
42312
|
-
"aria-label": "When the summary goes out",
|
|
42313
|
-
placeholder: "monday 08:00",
|
|
42314
|
-
className: "h-7 w-28 rounded border bg-background px-1 text-xs"
|
|
42315
|
-
}
|
|
42316
|
-
),
|
|
42317
|
-
/* @__PURE__ */ jsx133("span", { className: "text-xs text-muted-foreground", children: "not between" }),
|
|
42318
|
-
/* @__PURE__ */ jsx133(
|
|
42319
|
-
"input",
|
|
42320
|
-
{
|
|
42321
|
-
name: "quiet_start",
|
|
42322
|
-
type: "time",
|
|
42323
|
-
"aria-label": "Quiet hours start",
|
|
42324
|
-
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
42325
|
-
}
|
|
42326
|
-
),
|
|
42327
|
-
/* @__PURE__ */ jsx133("span", { className: "text-xs text-muted-foreground", children: "and" }),
|
|
42328
|
-
/* @__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(
|
|
42329
42278
|
"input",
|
|
42330
42279
|
{
|
|
42331
|
-
|
|
42332
|
-
type: "
|
|
42333
|
-
|
|
42334
|
-
|
|
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
|
+
}
|
|
42335
42296
|
}
|
|
42336
42297
|
),
|
|
42337
|
-
/* @__PURE__ */
|
|
42338
|
-
|
|
42339
|
-
|
|
42340
|
-
|
|
42341
|
-
|
|
42342
|
-
|
|
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
|
+
);
|
|
42343
42310
|
}
|
|
42344
42311
|
|
|
42345
42312
|
// src/FormBuilder.tsx
|
|
42346
|
-
import { Fragment as Fragment54, jsx as
|
|
42313
|
+
import { Fragment as Fragment54, jsx as jsx133, jsxs as jsxs123 } from "react/jsx-runtime";
|
|
42347
42314
|
function freshFormName(base, taken) {
|
|
42348
42315
|
const names = new Set(taken.map((n) => n.trim().toLowerCase()));
|
|
42349
42316
|
if (!names.has(base.toLowerCase())) return base;
|
|
@@ -42360,7 +42327,21 @@ function isLookRefusal(error) {
|
|
|
42360
42327
|
return Boolean(error && LOOK_REFUSAL.test(refusalSentence(error)));
|
|
42361
42328
|
}
|
|
42362
42329
|
var KEPT_DRAFT_PREFIX = "records-ui:form-draft:";
|
|
42363
|
-
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
|
+
];
|
|
42364
42345
|
function readKeptDraft(formId) {
|
|
42365
42346
|
try {
|
|
42366
42347
|
const raw = window.localStorage.getItem(KEPT_DRAFT_PREFIX + formId);
|
|
@@ -42391,31 +42372,31 @@ function withKept(stored, spec) {
|
|
|
42391
42372
|
return out;
|
|
42392
42373
|
}
|
|
42393
42374
|
function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne, createOnMount, className }) {
|
|
42394
|
-
const client =
|
|
42375
|
+
const client = useRecordsClient79();
|
|
42395
42376
|
const host = useRecordsUi();
|
|
42396
42377
|
const claimSeed = useSeedGuard();
|
|
42397
|
-
const table =
|
|
42378
|
+
const table = useTable26(tableId);
|
|
42398
42379
|
const rights = useTableRights(table.data);
|
|
42399
42380
|
const fields = useFields25(tableId);
|
|
42400
|
-
const [forms, setForms] =
|
|
42401
|
-
const [error, setError] =
|
|
42402
|
-
const [refused, setRefused] =
|
|
42403
|
-
const [activeId, setActiveId] =
|
|
42404
|
-
const [draft, setDraft] =
|
|
42405
|
-
const [saving, setSaving] =
|
|
42406
|
-
const [saved, setSaved] =
|
|
42407
|
-
const [publishing, setPublishing] =
|
|
42408
|
-
const [copied, setCopied] =
|
|
42409
|
-
const [shownUrl, setShownUrl] =
|
|
42410
|
-
const [lookOptions, setLookOptions] =
|
|
42411
|
-
const [lookUnread, setLookUnread] =
|
|
42412
|
-
const [restoredAt, setRestoredAt] =
|
|
42413
|
-
const [showResults, setShowResults] =
|
|
42414
|
-
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);
|
|
42415
42396
|
const keysBeforeAdd = useRef56(null);
|
|
42416
|
-
const [makeOnMount, setMakeOnMount] =
|
|
42397
|
+
const [makeOnMount, setMakeOnMount] = useState110("waiting");
|
|
42417
42398
|
const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
42418
|
-
const load =
|
|
42399
|
+
const load = useCallback54(async () => {
|
|
42419
42400
|
const answered = await client.forms({ table_id: tableId });
|
|
42420
42401
|
if (!answered.ok) {
|
|
42421
42402
|
setError(answered.error);
|
|
@@ -42442,10 +42423,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42442
42423
|
setError(null);
|
|
42443
42424
|
setForms(mine);
|
|
42444
42425
|
}, [client, tableId, seed, claimSeed]);
|
|
42445
|
-
|
|
42426
|
+
useEffect96(() => {
|
|
42446
42427
|
void load();
|
|
42447
42428
|
}, [load]);
|
|
42448
|
-
|
|
42429
|
+
useEffect96(() => {
|
|
42449
42430
|
if (!forms || forms.length === 0) return;
|
|
42450
42431
|
if (createOnMount && makeOnMount !== "made") return;
|
|
42451
42432
|
const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
|
|
@@ -42469,7 +42450,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42469
42450
|
);
|
|
42470
42451
|
const stored = useMemo68(() => draft ? (forms ?? []).find((f) => f.id === draft.id) ?? null : null, [draft, forms]);
|
|
42471
42452
|
const dirty = Boolean(draft && stored && !sameForm(draft, stored));
|
|
42472
|
-
|
|
42453
|
+
useEffect96(() => {
|
|
42473
42454
|
if (!draft || !stored) return;
|
|
42474
42455
|
const timer = window.setTimeout(() => {
|
|
42475
42456
|
if (sameForm(draft, stored)) dropKeptDraft(draft.id);
|
|
@@ -42478,7 +42459,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42478
42459
|
return () => window.clearTimeout(timer);
|
|
42479
42460
|
}, [draft, stored]);
|
|
42480
42461
|
const lookKey = draft ? JSON.stringify(formLookDocument(draft.look ?? null)) : null;
|
|
42481
|
-
|
|
42462
|
+
useEffect96(() => {
|
|
42482
42463
|
if (lookKey === null) return;
|
|
42483
42464
|
let cancelled = false;
|
|
42484
42465
|
const timer = window.setTimeout(() => {
|
|
@@ -42495,7 +42476,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42495
42476
|
window.clearTimeout(timer);
|
|
42496
42477
|
};
|
|
42497
42478
|
}, [client, lookKey]);
|
|
42498
|
-
|
|
42479
|
+
useEffect96(() => {
|
|
42499
42480
|
const before = keysBeforeAdd.current;
|
|
42500
42481
|
if (!before || !fields.data) return;
|
|
42501
42482
|
const made = askableFields(fields.data).filter((f) => !before.has(f.key));
|
|
@@ -42534,7 +42515,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42534
42515
|
}
|
|
42535
42516
|
dropKeptDraft(draft.id);
|
|
42536
42517
|
setRestoredAt(null);
|
|
42537
|
-
setSaved("Saved
|
|
42518
|
+
setSaved("Saved");
|
|
42538
42519
|
await load();
|
|
42539
42520
|
return true;
|
|
42540
42521
|
}
|
|
@@ -42573,7 +42554,22 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42573
42554
|
setRefused(written.error);
|
|
42574
42555
|
return;
|
|
42575
42556
|
}
|
|
42576
|
-
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");
|
|
42577
42573
|
await load();
|
|
42578
42574
|
}
|
|
42579
42575
|
async function create(name) {
|
|
@@ -42590,15 +42586,15 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42590
42586
|
await load();
|
|
42591
42587
|
return true;
|
|
42592
42588
|
}
|
|
42593
|
-
const [started, setStarted] =
|
|
42594
|
-
|
|
42589
|
+
const [started, setStarted] = useState110(false);
|
|
42590
|
+
useEffect96(() => {
|
|
42595
42591
|
if (!startWithOne || started || forms === null || forms.length > 0) return;
|
|
42596
42592
|
if (askableFields(fields.data ?? []).length === 0) return;
|
|
42597
42593
|
if (!table.data && !table.error) return;
|
|
42598
42594
|
setStarted(true);
|
|
42599
42595
|
void create(table.data?.name?.trim() ? `${table.data.name.trim()} form` : "Form 1");
|
|
42600
42596
|
}, [startWithOne, started, forms, fields.data, create, table.data, table.error]);
|
|
42601
|
-
|
|
42597
|
+
useEffect96(() => {
|
|
42602
42598
|
if (!createOnMount || makeOnMount !== "waiting" || forms === null) return;
|
|
42603
42599
|
if (!table.data && !table.error) return;
|
|
42604
42600
|
if (askableFields(fields.data ?? []).length === 0) {
|
|
@@ -42611,396 +42607,320 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42611
42607
|
(made) => setMakeOnMount(made ? "made" : "not-made")
|
|
42612
42608
|
);
|
|
42613
42609
|
}, [createOnMount, makeOnMount, forms, fields.data, create, table.data, table.error]);
|
|
42614
|
-
if (fields.error) return /* @__PURE__ */
|
|
42610
|
+
if (fields.error) return /* @__PURE__ */ jsx133(RefusalNotice, { error: fields.error, className });
|
|
42615
42611
|
if (error) {
|
|
42616
|
-
return /* @__PURE__ */
|
|
42612
|
+
return /* @__PURE__ */ jsx133(
|
|
42617
42613
|
RefusalNotice,
|
|
42618
42614
|
{
|
|
42619
42615
|
error,
|
|
42620
42616
|
className,
|
|
42621
|
-
actions: /* @__PURE__ */
|
|
42617
|
+
actions: /* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
42622
42618
|
}
|
|
42623
42619
|
);
|
|
42624
42620
|
}
|
|
42625
|
-
if (fields.loading || forms === null) return /* @__PURE__ */
|
|
42621
|
+
if (fields.loading || forms === null) return /* @__PURE__ */ jsx133(Skeleton29, { className: cn78("h-64 w-full", className) });
|
|
42626
42622
|
if (createOnMount && (makeOnMount === "waiting" || makeOnMount === "making") && !refused) {
|
|
42627
|
-
return /* @__PURE__ */
|
|
42623
|
+
return /* @__PURE__ */ jsx133(Skeleton29, { className: cn78("h-64 w-full", className) });
|
|
42628
42624
|
}
|
|
42629
42625
|
if (!rights.admin) {
|
|
42630
|
-
return /* @__PURE__ */
|
|
42626
|
+
return /* @__PURE__ */ jsx133("p", { className: cn78("text-xs text-muted-foreground", className), children: rights.why("structure") });
|
|
42631
42627
|
}
|
|
42632
|
-
return
|
|
42633
|
-
|
|
42634
|
-
|
|
42635
|
-
|
|
42636
|
-
|
|
42637
|
-
|
|
42638
|
-
|
|
42639
|
-
|
|
42640
|
-
|
|
42641
|
-
|
|
42642
|
-
|
|
42643
|
-
|
|
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
|
|
42644
42645
|
},
|
|
42645
|
-
|
|
42646
|
-
|
|
42647
|
-
|
|
42648
|
-
|
|
42649
|
-
|
|
42650
|
-
|
|
42651
|
-
|
|
42652
|
-
|
|
42653
|
-
|
|
42654
|
-
|
|
42655
|
-
|
|
42656
|
-
|
|
42657
|
-
|
|
42658
|
-
|
|
42659
|
-
|
|
42660
|
-
|
|
42661
|
-
|
|
42662
|
-
|
|
42663
|
-
|
|
42664
|
-
|
|
42665
|
-
|
|
42666
|
-
/* @__PURE__ */ jsx134(Button87, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
42667
|
-
] })
|
|
42668
|
-
] }),
|
|
42669
|
-
refused && !isLookRefusal(refused) ? /* @__PURE__ */ jsx134(
|
|
42670
|
-
RefusalNotice,
|
|
42671
|
-
{
|
|
42672
|
-
error: refused,
|
|
42673
|
-
onKeepEditing: () => setRefused(null),
|
|
42674
|
-
onDiscard: () => {
|
|
42675
|
-
const kept = forms.find((f) => f.id === activeId) ?? null;
|
|
42676
|
-
if (kept) dropKeptDraft(kept.id);
|
|
42677
|
-
setDraft(kept);
|
|
42678
|
-
setRestoredAt(null);
|
|
42679
|
-
setSaved(null);
|
|
42680
|
-
setRefused(null);
|
|
42681
|
-
}
|
|
42682
|
-
}
|
|
42683
|
-
) : null,
|
|
42684
|
-
restoredAt && dirty && stored ? /* @__PURE__ */ jsxs124("p", { className: "flex items-center gap-2 text-xs text-muted-foreground", "data-records-form-restored": "", children: [
|
|
42685
|
-
/* @__PURE__ */ jsxs124("span", { children: [
|
|
42686
|
-
"Unsaved changes from",
|
|
42687
|
-
" ",
|
|
42688
|
-
new Date(restoredAt).toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" }),
|
|
42689
|
-
" 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
|
+
] })
|
|
42690
42667
|
] }),
|
|
42691
|
-
/* @__PURE__ */
|
|
42692
|
-
|
|
42668
|
+
refused && !isLookRefusal(refused) ? /* @__PURE__ */ jsx133(
|
|
42669
|
+
RefusalNotice,
|
|
42693
42670
|
{
|
|
42694
|
-
|
|
42695
|
-
|
|
42696
|
-
|
|
42697
|
-
|
|
42698
|
-
|
|
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);
|
|
42699
42677
|
setRestoredAt(null);
|
|
42700
|
-
|
|
42701
|
-
|
|
42702
|
-
|
|
42703
|
-
)
|
|
42704
|
-
] }) : null,
|
|
42705
|
-
!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: [
|
|
42706
|
-
/* @__PURE__ */ jsx134(
|
|
42707
|
-
PublishRow,
|
|
42708
|
-
{
|
|
42709
|
-
url: `${publicOrigin}${publicFormPath(draft.id)}`,
|
|
42710
|
-
state: draft.state,
|
|
42711
|
-
mayPublish: rights.structure,
|
|
42712
|
-
why: rights.why("structure"),
|
|
42713
|
-
busy: publishing,
|
|
42714
|
-
copied,
|
|
42715
|
-
shownUrl,
|
|
42716
|
-
onPublish: (wanted) => void publish(wanted),
|
|
42717
|
-
onCopy: (url) => void copyLink(url),
|
|
42718
|
-
unsaved: dirty
|
|
42678
|
+
setSaved(null);
|
|
42679
|
+
setRefused(null);
|
|
42680
|
+
}
|
|
42719
42681
|
}
|
|
42720
|
-
),
|
|
42721
|
-
/* @__PURE__ */
|
|
42722
|
-
|
|
42723
|
-
|
|
42724
|
-
|
|
42725
|
-
|
|
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,
|
|
42726
42692
|
{
|
|
42727
|
-
|
|
42728
|
-
|
|
42729
|
-
|
|
42693
|
+
size: "sm",
|
|
42694
|
+
variant: "ghost",
|
|
42695
|
+
onClick: () => {
|
|
42696
|
+
dropKeptDraft(stored.id);
|
|
42697
|
+
setDraft(stored);
|
|
42698
|
+
setRestoredAt(null);
|
|
42699
|
+
},
|
|
42700
|
+
children: "Discard"
|
|
42730
42701
|
}
|
|
42731
42702
|
)
|
|
42732
|
-
] }),
|
|
42733
|
-
/* @__PURE__ */
|
|
42734
|
-
/* @__PURE__ */
|
|
42735
|
-
|
|
42736
|
-
|
|
42737
|
-
|
|
42738
|
-
|
|
42739
|
-
|
|
42740
|
-
|
|
42741
|
-
|
|
42742
|
-
|
|
42743
|
-
|
|
42744
|
-
|
|
42745
|
-
)
|
|
42746
|
-
|
|
42747
|
-
|
|
42748
|
-
|
|
42749
|
-
|
|
42750
|
-
|
|
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,
|
|
42751
42737
|
{
|
|
42752
|
-
|
|
42753
|
-
|
|
42754
|
-
|
|
42755
|
-
placeholder: "https://",
|
|
42756
|
-
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"
|
|
42757
42741
|
}
|
|
42758
|
-
)
|
|
42742
|
+
),
|
|
42743
|
+
"Show our logo and name"
|
|
42759
42744
|
] }),
|
|
42760
|
-
/* @__PURE__ */
|
|
42761
|
-
/* @__PURE__ */
|
|
42762
|
-
|
|
42763
|
-
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,
|
|
42764
42748
|
{
|
|
42765
|
-
|
|
42766
|
-
|
|
42767
|
-
|
|
42768
|
-
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"
|
|
42769
42752
|
}
|
|
42770
|
-
)
|
|
42753
|
+
),
|
|
42754
|
+
"Next after a choice"
|
|
42771
42755
|
] })
|
|
42772
|
-
] })
|
|
42773
|
-
] }),
|
|
42774
|
-
/* @__PURE__ */ jsx134(WelcomeEditor, { value: draft.welcome, pictures: lookOptions?.pictures ?? null, onChange: (welcome) => patch({ welcome }) }),
|
|
42775
|
-
/* @__PURE__ */ jsx134(
|
|
42776
|
-
EndingsEditor,
|
|
42777
|
-
{
|
|
42778
|
-
endings: draft.endings ?? [],
|
|
42779
|
-
pictures: lookOptions?.pictures ?? null,
|
|
42780
|
-
fieldKeys: conditionFields,
|
|
42781
|
-
scored: draft.questions.some((q) => q.points),
|
|
42782
|
-
onChange: (endings) => patch({ endings })
|
|
42783
|
-
}
|
|
42784
|
-
),
|
|
42785
|
-
/* @__PURE__ */ jsxs124("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
42786
|
-
/* @__PURE__ */ jsxs124("label", { className: "flex flex-col gap-1", children: [
|
|
42787
|
-
/* @__PURE__ */ jsx134(Label22, { className: "text-xs font-medium", children: "Name" }),
|
|
42788
|
-
/* @__PURE__ */ jsx134(BasicInput28, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
|
|
42789
|
-
] }),
|
|
42790
|
-
/* @__PURE__ */ jsxs124("label", { className: "flex flex-col gap-1", children: [
|
|
42791
|
-
/* @__PURE__ */ jsx134(Label22, { className: "text-xs font-medium", children: "Flow" }),
|
|
42792
|
-
/* @__PURE__ */ jsx134(
|
|
42793
|
-
"select",
|
|
42794
|
-
{
|
|
42795
|
-
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
42796
|
-
value: draft.flow ?? "one-at-a-time",
|
|
42797
|
-
onChange: (e) => patch({ flow: e.target.value }),
|
|
42798
|
-
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))
|
|
42799
|
-
}
|
|
42800
|
-
)
|
|
42801
42756
|
] }),
|
|
42802
|
-
/* @__PURE__ */
|
|
42803
|
-
/* @__PURE__ */
|
|
42804
|
-
/* @__PURE__ */
|
|
42805
|
-
|
|
42806
|
-
|
|
42807
|
-
|
|
42808
|
-
|
|
42809
|
-
|
|
42810
|
-
|
|
42811
|
-
|
|
42812
|
-
|
|
42813
|
-
|
|
42814
|
-
|
|
42815
|
-
|
|
42816
|
-
|
|
42817
|
-
|
|
42818
|
-
|
|
42819
|
-
|
|
42820
|
-
|
|
42821
|
-
|
|
42822
|
-
|
|
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
|
+
] })
|
|
42823
42797
|
] }),
|
|
42824
|
-
/* @__PURE__ */
|
|
42825
|
-
|
|
42826
|
-
|
|
42827
|
-
/* @__PURE__ */ jsx134(Label22, { className: "text-xs font-medium", children: "Look" }),
|
|
42828
|
-
/* @__PURE__ */ jsx134(
|
|
42829
|
-
AccentSwatches,
|
|
42798
|
+
/* @__PURE__ */ jsx133(WelcomeEditor, { value: draft.welcome, pictures: lookOptions?.pictures ?? null, onChange: (welcome) => patch({ welcome }) }),
|
|
42799
|
+
/* @__PURE__ */ jsx133(
|
|
42800
|
+
EndingsEditor,
|
|
42830
42801
|
{
|
|
42831
|
-
|
|
42832
|
-
|
|
42833
|
-
|
|
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 })
|
|
42834
42807
|
}
|
|
42835
42808
|
),
|
|
42836
|
-
/* @__PURE__ */
|
|
42837
|
-
|
|
42838
|
-
|
|
42839
|
-
/* @__PURE__ */
|
|
42840
|
-
|
|
42841
|
-
|
|
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",
|
|
42842
42818
|
{
|
|
42843
|
-
|
|
42844
|
-
|
|
42845
|
-
|
|
42846
|
-
|
|
42847
|
-
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))
|
|
42848
42823
|
}
|
|
42849
42824
|
)
|
|
42850
42825
|
] }),
|
|
42851
|
-
/* @__PURE__ */
|
|
42852
|
-
/* @__PURE__ */
|
|
42853
|
-
/* @__PURE__ */
|
|
42854
|
-
|
|
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,
|
|
42855
42830
|
{
|
|
42856
|
-
|
|
42857
|
-
|
|
42858
|
-
|
|
42859
|
-
noneLabel: "No cover",
|
|
42860
|
-
onChange: (id) => patchLook({ coverFileId: id || null })
|
|
42831
|
+
rows: 2,
|
|
42832
|
+
value: draft.intro ?? "",
|
|
42833
|
+
onChange: (e) => patch({ intro: e.target.value || null })
|
|
42861
42834
|
}
|
|
42862
42835
|
)
|
|
42863
|
-
] })
|
|
42864
|
-
|
|
42865
|
-
|
|
42866
|
-
|
|
42867
|
-
|
|
42868
|
-
|
|
42869
|
-
|
|
42870
|
-
|
|
42871
|
-
|
|
42872
|
-
|
|
42873
|
-
|
|
42874
|
-
|
|
42875
|
-
|
|
42876
|
-
] })
|
|
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 }) })
|
|
42877
42849
|
] }),
|
|
42878
|
-
|
|
42879
|
-
|
|
42880
|
-
|
|
42881
|
-
|
|
42882
|
-
|
|
42883
|
-
|
|
42884
|
-
|
|
42885
|
-
|
|
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,
|
|
42886
42866
|
{
|
|
42887
|
-
|
|
42888
|
-
|
|
42889
|
-
|
|
42890
|
-
|
|
42891
|
-
})
|
|
42867
|
+
label: "Logo",
|
|
42868
|
+
pictures: lookOptions.pictures,
|
|
42869
|
+
value: draft.look?.logoFileId ?? "",
|
|
42870
|
+
noneLabel: "Organization logo",
|
|
42871
|
+
onChange: (id) => patchLook({ logoFileId: id || null })
|
|
42892
42872
|
}
|
|
42893
|
-
)
|
|
42894
|
-
/* @__PURE__ */ jsx134("span", { className: "text-xs font-medium", children: fieldName(field2) }),
|
|
42895
|
-
field2.required ? /* @__PURE__ */ jsx134("span", { className: "text-xs text-destructive", children: "required" }) : null
|
|
42873
|
+
)
|
|
42896
42874
|
] }),
|
|
42897
|
-
|
|
42898
|
-
/* @__PURE__ */
|
|
42899
|
-
|
|
42900
|
-
|
|
42901
|
-
className: "h-8 text-xs",
|
|
42902
|
-
placeholder: fieldName(field2),
|
|
42903
|
-
"aria-label": `How to ask for ${fieldName(field2)}`,
|
|
42904
|
-
value: question.ask ?? "",
|
|
42905
|
-
onChange: (e) => patchQuestion(index, { ask: e.target.value || null })
|
|
42906
|
-
}
|
|
42907
|
-
),
|
|
42908
|
-
/* @__PURE__ */ jsx134(
|
|
42909
|
-
BasicInput28,
|
|
42910
|
-
{
|
|
42911
|
-
className: "h-8 text-xs",
|
|
42912
|
-
placeholder: "Help text",
|
|
42913
|
-
"aria-label": `Help text for ${fieldName(field2)}`,
|
|
42914
|
-
value: question.help ?? "",
|
|
42915
|
-
onChange: (e) => patchQuestion(index, { help: e.target.value || null })
|
|
42916
|
-
}
|
|
42917
|
-
),
|
|
42918
|
-
/* @__PURE__ */ jsxs124("label", { className: "col-span-2 flex items-center gap-2 text-xs", children: [
|
|
42919
|
-
/* @__PURE__ */ jsx134(
|
|
42920
|
-
Checkbox15,
|
|
42921
|
-
{
|
|
42922
|
-
checked: field2.required ? true : question.required === true,
|
|
42923
|
-
disabled: field2.required === true,
|
|
42924
|
-
"aria-label": `${fieldName(field2)} is required`,
|
|
42925
|
-
onCheckedChange: (checked) => patchQuestion(index, { required: checked === true ? true : null })
|
|
42926
|
-
}
|
|
42927
|
-
),
|
|
42928
|
-
"Required"
|
|
42929
|
-
] }),
|
|
42930
|
-
/* @__PURE__ */ jsx134(
|
|
42931
|
-
Condition,
|
|
42932
|
-
{
|
|
42933
|
-
question,
|
|
42934
|
-
fieldKeys: (fields.data ?? []).map((f) => ({
|
|
42935
|
-
id: String(f.id),
|
|
42936
|
-
key: f.key,
|
|
42937
|
-
label: f.label || f.key,
|
|
42938
|
-
field: f
|
|
42939
|
-
})),
|
|
42940
|
-
onChange: (showIf) => patchQuestion(index, { showIf })
|
|
42941
|
-
}
|
|
42942
|
-
),
|
|
42943
|
-
/* @__PURE__ */ jsx134(
|
|
42944
|
-
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,
|
|
42945
42879
|
{
|
|
42946
|
-
|
|
42947
|
-
|
|
42948
|
-
|
|
42949
|
-
|
|
42950
|
-
|
|
42951
|
-
fieldKeys: conditionFields,
|
|
42952
|
-
label: (key) => {
|
|
42953
|
-
const f = byKey.get(key);
|
|
42954
|
-
return draft.questions.find((q) => q.field === key)?.ask || (f ? fieldName(f) : key);
|
|
42955
|
-
},
|
|
42956
|
-
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 })
|
|
42957
42885
|
}
|
|
42958
42886
|
)
|
|
42959
|
-
] })
|
|
42960
|
-
] },
|
|
42961
|
-
|
|
42962
|
-
|
|
42963
|
-
|
|
42964
|
-
|
|
42965
|
-
|
|
42966
|
-
className: "rounded border border-destructive/40 bg-destructive/5 px-2 py-1.5 text-xs text-destructive",
|
|
42967
|
-
message: requiredLeftUnasked(fields.data ?? [], draft.questions.map((q) => q.field)) ?? "",
|
|
42968
|
-
operation: "Build a form",
|
|
42969
|
-
origin: "records-ui/FormBuilder"
|
|
42970
|
-
}
|
|
42971
|
-
) : null,
|
|
42972
|
-
draft.questions.filter((q) => !byKey.has(q.field)).map((q) => /* @__PURE__ */ jsxs124("p", { className: "text-xs text-destructive", children: [
|
|
42973
|
-
'This form asks for "',
|
|
42974
|
-
q.field,
|
|
42975
|
-
'" and this table has no such Field. It is shown here rather than quietly dropped.'
|
|
42976
|
-
] }, q.field)),
|
|
42977
|
-
adding ? /* @__PURE__ */ jsx134("div", { className: "rounded border p-2", "data-records-form-add-question": "", children: /* @__PURE__ */ jsx134(
|
|
42978
|
-
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,
|
|
42979
42894
|
{
|
|
42980
42895
|
tableId,
|
|
42981
|
-
|
|
42982
|
-
|
|
42983
|
-
|
|
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);
|
|
42984
42903
|
},
|
|
42985
|
-
|
|
42904
|
+
onAdded: () => setAdding(false),
|
|
42905
|
+
onQuestions: (questions) => patch({ questions }),
|
|
42906
|
+
onQuestion: patchQuestion
|
|
42986
42907
|
}
|
|
42987
|
-
)
|
|
42988
|
-
Button87,
|
|
42989
|
-
{
|
|
42990
|
-
size: "sm",
|
|
42991
|
-
variant: "outline",
|
|
42992
|
-
onClick: () => {
|
|
42993
|
-
keysBeforeAdd.current = new Set((fields.data ?? []).map((f) => f.key));
|
|
42994
|
-
setAdding(true);
|
|
42995
|
-
},
|
|
42996
|
-
children: "Add a question"
|
|
42997
|
-
}
|
|
42998
|
-
) })
|
|
42908
|
+
)
|
|
42999
42909
|
] })
|
|
43000
|
-
] })
|
|
43001
|
-
|
|
43002
|
-
|
|
43003
|
-
|
|
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
|
+
);
|
|
43004
42924
|
}
|
|
43005
42925
|
function PublishRow({
|
|
43006
42926
|
url,
|
|
@@ -43015,10 +42935,10 @@ function PublishRow({
|
|
|
43015
42935
|
unsaved = false
|
|
43016
42936
|
}) {
|
|
43017
42937
|
const open = state === "open";
|
|
43018
|
-
return /* @__PURE__ */
|
|
43019
|
-
/* @__PURE__ */
|
|
43020
|
-
mayPublish ? /* @__PURE__ */
|
|
43021
|
-
|
|
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,
|
|
43022
42942
|
{
|
|
43023
42943
|
size: "sm",
|
|
43024
42944
|
variant: open ? "ghost" : "default",
|
|
@@ -43027,8 +42947,8 @@ function PublishRow({
|
|
|
43027
42947
|
children: busy ? "\u2026" : open ? "Unpublish" : unsaved ? "Save and publish" : "Publish"
|
|
43028
42948
|
}
|
|
43029
42949
|
) : null,
|
|
43030
|
-
state !== "draft" ? /* @__PURE__ */
|
|
43031
|
-
/* @__PURE__ */
|
|
42950
|
+
state !== "draft" ? /* @__PURE__ */ jsxs123(Fragment54, { children: [
|
|
42951
|
+
/* @__PURE__ */ jsx133(
|
|
43032
42952
|
"a",
|
|
43033
42953
|
{
|
|
43034
42954
|
href: url,
|
|
@@ -43038,14 +42958,14 @@ function PublishRow({
|
|
|
43038
42958
|
children: url
|
|
43039
42959
|
}
|
|
43040
42960
|
),
|
|
43041
|
-
/* @__PURE__ */
|
|
42961
|
+
/* @__PURE__ */ jsx133(Button86, { size: "sm", variant: "outline", onClick: () => onCopy(url), children: copied ? "Copied" : "Copy link" })
|
|
43042
42962
|
] }) : null
|
|
43043
42963
|
] }),
|
|
43044
|
-
/* @__PURE__ */
|
|
43045
|
-
open && unsaved ? /* @__PURE__ */
|
|
43046
|
-
!mayPublish ? /* @__PURE__ */
|
|
43047
|
-
shownUrl ? /* @__PURE__ */
|
|
43048
|
-
"
|
|
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: ",
|
|
43049
42969
|
shownUrl
|
|
43050
42970
|
] }) : null
|
|
43051
42971
|
] });
|
|
@@ -43055,7 +42975,7 @@ function Condition({
|
|
|
43055
42975
|
fieldKeys,
|
|
43056
42976
|
onChange
|
|
43057
42977
|
}) {
|
|
43058
|
-
return /* @__PURE__ */
|
|
42978
|
+
return /* @__PURE__ */ jsx133(
|
|
43059
42979
|
ConditionGroup,
|
|
43060
42980
|
{
|
|
43061
42981
|
lead: "Ask only when",
|
|
@@ -43066,26 +42986,326 @@ function Condition({
|
|
|
43066
42986
|
}
|
|
43067
42987
|
);
|
|
43068
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
|
+
}
|
|
43069
43289
|
|
|
43070
43290
|
// src/DocTemplate.tsx
|
|
43071
|
-
import { useCallback as
|
|
43072
|
-
import { useFields as useFields26, useRecordsClient as
|
|
43073
|
-
import { BasicInput as BasicInput29, BasicTextarea as BasicTextarea12, Button as
|
|
43074
|
-
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";
|
|
43075
43295
|
function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, className }) {
|
|
43076
|
-
const client =
|
|
43296
|
+
const client = useRecordsClient80();
|
|
43077
43297
|
const claimSeed = useSeedGuard();
|
|
43078
|
-
const table =
|
|
43298
|
+
const table = useTable27(tableId);
|
|
43079
43299
|
const rights = useTableRights(table.data);
|
|
43080
43300
|
const fields = useFields26(tableId);
|
|
43081
|
-
const [templates, setTemplates] =
|
|
43082
|
-
const [error, setError] =
|
|
43083
|
-
const [activeId, setActiveId] =
|
|
43084
|
-
const [draftName, setDraftName] =
|
|
43085
|
-
const [draftBody, setDraftBody] =
|
|
43086
|
-
const [unresolved, setUnresolved] =
|
|
43087
|
-
const [saving, setSaving] =
|
|
43088
|
-
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 () => {
|
|
43089
43309
|
const held = await client.docTemplates({ table_id: tableId });
|
|
43090
43310
|
if (!held.ok) {
|
|
43091
43311
|
setError(held.error);
|
|
@@ -43116,10 +43336,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43116
43336
|
setError(null);
|
|
43117
43337
|
setTemplates(rows);
|
|
43118
43338
|
}, [client, tableId, seed, fields.data]);
|
|
43119
|
-
|
|
43339
|
+
useEffect97(() => {
|
|
43120
43340
|
void load();
|
|
43121
43341
|
}, [load]);
|
|
43122
|
-
|
|
43342
|
+
useEffect97(() => {
|
|
43123
43343
|
if (!templates || templates.length === 0) return;
|
|
43124
43344
|
const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
|
|
43125
43345
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -43127,7 +43347,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43127
43347
|
setDraftBody(chosen.body);
|
|
43128
43348
|
onActiveTemplate?.(chosen);
|
|
43129
43349
|
}, [templates, activeTemplateId]);
|
|
43130
|
-
|
|
43350
|
+
useEffect97(() => {
|
|
43131
43351
|
let cancelled = false;
|
|
43132
43352
|
if (draftBody.trim() === "") {
|
|
43133
43353
|
setUnresolved([]);
|
|
@@ -43157,25 +43377,25 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43157
43377
|
setActiveId(saved.data);
|
|
43158
43378
|
await load();
|
|
43159
43379
|
}
|
|
43160
|
-
if (fields.error) return /* @__PURE__ */
|
|
43380
|
+
if (fields.error) return /* @__PURE__ */ jsx134(RefusalNotice, { error: fields.error, className });
|
|
43161
43381
|
if (error) {
|
|
43162
|
-
return /* @__PURE__ */
|
|
43382
|
+
return /* @__PURE__ */ jsx134(
|
|
43163
43383
|
RefusalNotice,
|
|
43164
43384
|
{
|
|
43165
43385
|
error,
|
|
43166
43386
|
className,
|
|
43167
|
-
actions: /* @__PURE__ */
|
|
43387
|
+
actions: /* @__PURE__ */ jsx134(Button87, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43168
43388
|
}
|
|
43169
43389
|
);
|
|
43170
43390
|
}
|
|
43171
|
-
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) });
|
|
43172
43392
|
if (!rights.admin) {
|
|
43173
|
-
return /* @__PURE__ */
|
|
43393
|
+
return /* @__PURE__ */ jsx134("p", { className: cn79("text-xs text-muted-foreground", className), children: rights.why("structure") });
|
|
43174
43394
|
}
|
|
43175
|
-
return /* @__PURE__ */
|
|
43176
|
-
/* @__PURE__ */
|
|
43177
|
-
templates.map((t) => /* @__PURE__ */
|
|
43178
|
-
|
|
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,
|
|
43179
43399
|
{
|
|
43180
43400
|
size: "sm",
|
|
43181
43401
|
variant: t.id === activeId ? "secondary" : "ghost",
|
|
@@ -43187,7 +43407,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43187
43407
|
},
|
|
43188
43408
|
children: [
|
|
43189
43409
|
t.name,
|
|
43190
|
-
/* @__PURE__ */
|
|
43410
|
+
/* @__PURE__ */ jsxs124("span", { className: "ml-1 text-[10px] opacity-70", children: [
|
|
43191
43411
|
"v",
|
|
43192
43412
|
t.template_version
|
|
43193
43413
|
] })
|
|
@@ -43195,8 +43415,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43195
43415
|
},
|
|
43196
43416
|
t.id
|
|
43197
43417
|
)),
|
|
43198
|
-
/* @__PURE__ */
|
|
43199
|
-
|
|
43418
|
+
/* @__PURE__ */ jsx134(
|
|
43419
|
+
Button87,
|
|
43200
43420
|
{
|
|
43201
43421
|
size: "sm",
|
|
43202
43422
|
variant: "ghost",
|
|
@@ -43208,16 +43428,16 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43208
43428
|
children: "New template"
|
|
43209
43429
|
}
|
|
43210
43430
|
),
|
|
43211
|
-
/* @__PURE__ */
|
|
43431
|
+
/* @__PURE__ */ jsx134(Button87, { size: "sm", className: "ml-auto", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
43212
43432
|
] }),
|
|
43213
|
-
/* @__PURE__ */
|
|
43214
|
-
/* @__PURE__ */
|
|
43215
|
-
/* @__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" })
|
|
43216
43436
|
] }),
|
|
43217
|
-
/* @__PURE__ */
|
|
43218
|
-
/* @__PURE__ */
|
|
43219
|
-
(fields.data ?? []).map((field2) => /* @__PURE__ */
|
|
43220
|
-
|
|
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,
|
|
43221
43441
|
{
|
|
43222
43442
|
size: "sm",
|
|
43223
43443
|
variant: "ghost",
|
|
@@ -43228,7 +43448,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43228
43448
|
field2.id
|
|
43229
43449
|
))
|
|
43230
43450
|
] }),
|
|
43231
|
-
/* @__PURE__ */
|
|
43451
|
+
/* @__PURE__ */ jsx134(
|
|
43232
43452
|
BasicTextarea12,
|
|
43233
43453
|
{
|
|
43234
43454
|
rows: 10,
|
|
@@ -43238,33 +43458,33 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43238
43458
|
onChange: (e) => setDraftBody(e.target.value)
|
|
43239
43459
|
}
|
|
43240
43460
|
),
|
|
43241
|
-
unresolved.length > 0 ? /* @__PURE__ */
|
|
43242
|
-
/* @__PURE__ */
|
|
43243
|
-
/* @__PURE__ */
|
|
43244
|
-
/* @__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(
|
|
43245
43465
|
RefusalLine,
|
|
43246
43466
|
{
|
|
43247
43467
|
error: refusal("invalid_argument", token.why, "Change it to a Field this table has, or take it out.")
|
|
43248
43468
|
}
|
|
43249
43469
|
)
|
|
43250
|
-
] }, 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
|
|
43251
43471
|
] });
|
|
43252
43472
|
}
|
|
43253
43473
|
|
|
43254
43474
|
// src/DocRender.tsx
|
|
43255
|
-
import { useCallback as
|
|
43256
|
-
import { useRecordsClient as
|
|
43257
|
-
import { Button as
|
|
43258
|
-
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";
|
|
43259
43479
|
function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
43260
|
-
const client =
|
|
43261
|
-
const [preview, setPreview] =
|
|
43262
|
-
const [renders, setRenders] =
|
|
43263
|
-
const [showing, setShowing] =
|
|
43264
|
-
const [error, setError] =
|
|
43265
|
-
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);
|
|
43266
43486
|
const paper = useRef57(null);
|
|
43267
|
-
const load =
|
|
43487
|
+
const load = useCallback56(async () => {
|
|
43268
43488
|
const [body, held] = await Promise.all([
|
|
43269
43489
|
client.docRenderBody({ template_id: templateId, record_id: recordId }),
|
|
43270
43490
|
client.docRenders({ record_id: recordId })
|
|
@@ -43281,7 +43501,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43281
43501
|
setPreview(body.data);
|
|
43282
43502
|
setRenders(held.data.filter((r) => r.template_id === templateId));
|
|
43283
43503
|
}, [client, templateId, recordId]);
|
|
43284
|
-
|
|
43504
|
+
useEffect98(() => {
|
|
43285
43505
|
void load();
|
|
43286
43506
|
}, [load]);
|
|
43287
43507
|
async function freeze() {
|
|
@@ -43321,22 +43541,22 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43321
43541
|
}
|
|
43322
43542
|
}
|
|
43323
43543
|
if (error) {
|
|
43324
|
-
return /* @__PURE__ */
|
|
43544
|
+
return /* @__PURE__ */ jsx135(
|
|
43325
43545
|
RefusalNotice,
|
|
43326
43546
|
{
|
|
43327
43547
|
error,
|
|
43328
43548
|
className,
|
|
43329
|
-
actions: /* @__PURE__ */
|
|
43549
|
+
actions: /* @__PURE__ */ jsx135(Button88, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43330
43550
|
}
|
|
43331
43551
|
);
|
|
43332
43552
|
}
|
|
43333
|
-
if (preview === null || renders === null) return /* @__PURE__ */
|
|
43553
|
+
if (preview === null || renders === null) return /* @__PURE__ */ jsx135(Skeleton31, { className: cn80("h-64 w-full", className) });
|
|
43334
43554
|
const frozen = showing ? renders.find((r) => r.id === showing) ?? null : null;
|
|
43335
43555
|
const text2 = frozen ? frozen.body : preview;
|
|
43336
|
-
return /* @__PURE__ */
|
|
43337
|
-
/* @__PURE__ */
|
|
43338
|
-
/* @__PURE__ */
|
|
43339
|
-
|
|
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,
|
|
43340
43560
|
{
|
|
43341
43561
|
size: "sm",
|
|
43342
43562
|
variant: frozen ? "ghost" : "secondary",
|
|
@@ -43345,8 +43565,8 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43345
43565
|
children: "As it reads now"
|
|
43346
43566
|
}
|
|
43347
43567
|
),
|
|
43348
|
-
renders.map((render) => /* @__PURE__ */
|
|
43349
|
-
|
|
43568
|
+
renders.map((render) => /* @__PURE__ */ jsx135(
|
|
43569
|
+
Button88,
|
|
43350
43570
|
{
|
|
43351
43571
|
size: "sm",
|
|
43352
43572
|
variant: render.id === showing ? "secondary" : "ghost",
|
|
@@ -43357,33 +43577,33 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43357
43577
|
},
|
|
43358
43578
|
render.id
|
|
43359
43579
|
)),
|
|
43360
|
-
/* @__PURE__ */
|
|
43361
|
-
/* @__PURE__ */
|
|
43362
|
-
/* @__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" })
|
|
43363
43583
|
] })
|
|
43364
43584
|
] }),
|
|
43365
|
-
/* @__PURE__ */
|
|
43366
|
-
/* @__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." })
|
|
43367
43587
|
] });
|
|
43368
43588
|
}
|
|
43369
43589
|
|
|
43370
43590
|
// src/SignBlock.tsx
|
|
43371
|
-
import { useCallback as
|
|
43372
|
-
import { useFields as useFields27, useRecordsClient as
|
|
43373
|
-
import { BasicInput as BasicInput30, Button as
|
|
43374
|
-
import { useTable as
|
|
43375
|
-
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";
|
|
43376
43596
|
function SignBlock({ tableId, recordId, render, className }) {
|
|
43377
|
-
const client =
|
|
43378
|
-
const table =
|
|
43597
|
+
const client = useRecordsClient82();
|
|
43598
|
+
const table = useTable28(tableId);
|
|
43379
43599
|
const rights = useTableRights(table.data);
|
|
43380
43600
|
const fields = useFields27(tableId);
|
|
43381
|
-
const [signatures, setSignatures] =
|
|
43382
|
-
const [verdicts, setVerdicts] =
|
|
43383
|
-
const [error, setError] =
|
|
43384
|
-
const [name, setName] =
|
|
43385
|
-
const [busy, setBusy] =
|
|
43386
|
-
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 () => {
|
|
43387
43607
|
const held = await client.docSignatures({ record_id: recordId });
|
|
43388
43608
|
if (!held.ok) {
|
|
43389
43609
|
setError(held.error);
|
|
@@ -43398,7 +43618,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43398
43618
|
}
|
|
43399
43619
|
setVerdicts(answers);
|
|
43400
43620
|
}, [client, recordId]);
|
|
43401
|
-
|
|
43621
|
+
useEffect99(() => {
|
|
43402
43622
|
void load();
|
|
43403
43623
|
}, [load]);
|
|
43404
43624
|
async function sign(field2) {
|
|
@@ -43418,36 +43638,36 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43418
43638
|
setName("");
|
|
43419
43639
|
await load();
|
|
43420
43640
|
}
|
|
43421
|
-
if (fields.error) return /* @__PURE__ */
|
|
43641
|
+
if (fields.error) return /* @__PURE__ */ jsx136(RefusalNotice, { error: fields.error, className });
|
|
43422
43642
|
if (error) {
|
|
43423
|
-
return /* @__PURE__ */
|
|
43643
|
+
return /* @__PURE__ */ jsx136(
|
|
43424
43644
|
RefusalNotice,
|
|
43425
43645
|
{
|
|
43426
43646
|
error,
|
|
43427
43647
|
className,
|
|
43428
|
-
actions: /* @__PURE__ */
|
|
43648
|
+
actions: /* @__PURE__ */ jsx136(Button89, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43429
43649
|
}
|
|
43430
43650
|
);
|
|
43431
43651
|
}
|
|
43432
|
-
if (fields.loading || signatures === null) return /* @__PURE__ */
|
|
43652
|
+
if (fields.loading || signatures === null) return /* @__PURE__ */ jsx136(Skeleton32, { className: cn81("h-32 w-full", className) });
|
|
43433
43653
|
const signable = (fields.data ?? []).filter(isSignatureField);
|
|
43434
|
-
return /* @__PURE__ */
|
|
43435
|
-
/* @__PURE__ */
|
|
43436
|
-
/* @__PURE__ */
|
|
43437
|
-
/* @__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: [
|
|
43438
43658
|
signatures.length,
|
|
43439
43659
|
" on this record"
|
|
43440
43660
|
] })
|
|
43441
43661
|
] }),
|
|
43442
|
-
signatures.length > 0 ? /* @__PURE__ */
|
|
43662
|
+
signatures.length > 0 ? /* @__PURE__ */ jsx136("ul", { className: "flex flex-col gap-1", children: signatures.map((signature) => {
|
|
43443
43663
|
const verdict = verdicts[signature.id];
|
|
43444
43664
|
const intact = typeof verdict === "object" && verdict !== null ? verdict.intact : void 0;
|
|
43445
|
-
return /* @__PURE__ */
|
|
43446
|
-
/* @__PURE__ */
|
|
43447
|
-
/* @__PURE__ */
|
|
43448
|
-
/* @__PURE__ */
|
|
43449
|
-
/* @__PURE__ */
|
|
43450
|
-
/* @__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: [
|
|
43451
43671
|
"v",
|
|
43452
43672
|
signature.document_version,
|
|
43453
43673
|
" \xB7 ",
|
|
@@ -43457,7 +43677,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43457
43677
|
intact === false ? (
|
|
43458
43678
|
// THE STORE REFUSED TO CALL IT INTACT: its reason goes through the one
|
|
43459
43679
|
// formatter, with what to do next, never raw in red (lane REFUSAL-SWEEP).
|
|
43460
|
-
/* @__PURE__ */
|
|
43680
|
+
/* @__PURE__ */ jsx136(
|
|
43461
43681
|
RefusalLine,
|
|
43462
43682
|
{
|
|
43463
43683
|
className: "mt-0.5",
|
|
@@ -43468,22 +43688,22 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43468
43688
|
)
|
|
43469
43689
|
}
|
|
43470
43690
|
)
|
|
43471
|
-
) : /* @__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 ?? "") })
|
|
43472
43692
|
] }, signature.id);
|
|
43473
43693
|
}) }) : null,
|
|
43474
|
-
!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: [
|
|
43475
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 ",
|
|
43476
|
-
/* @__PURE__ */
|
|
43696
|
+
/* @__PURE__ */ jsx136("code", { children: "signature" }),
|
|
43477
43697
|
" \u2014 add one and the signing appears."
|
|
43478
|
-
] }) : !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) => {
|
|
43479
43699
|
const already = signatures.find((s) => s.field_key === field2.key);
|
|
43480
43700
|
if (already) {
|
|
43481
|
-
return /* @__PURE__ */
|
|
43701
|
+
return /* @__PURE__ */ jsxs126("p", { className: "text-xs text-muted-foreground", children: [
|
|
43482
43702
|
fieldName(field2),
|
|
43483
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."
|
|
43484
43704
|
] }, field2.id);
|
|
43485
43705
|
}
|
|
43486
|
-
return /* @__PURE__ */
|
|
43706
|
+
return /* @__PURE__ */ jsxs126(
|
|
43487
43707
|
"form",
|
|
43488
43708
|
{
|
|
43489
43709
|
className: "flex items-center gap-1",
|
|
@@ -43492,7 +43712,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43492
43712
|
if (name.trim() !== "") void sign(field2);
|
|
43493
43713
|
},
|
|
43494
43714
|
children: [
|
|
43495
|
-
/* @__PURE__ */
|
|
43715
|
+
/* @__PURE__ */ jsx136(
|
|
43496
43716
|
BasicInput30,
|
|
43497
43717
|
{
|
|
43498
43718
|
className: "h-8 w-56 text-xs",
|
|
@@ -43502,8 +43722,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43502
43722
|
onChange: (e) => setName(e.target.value)
|
|
43503
43723
|
}
|
|
43504
43724
|
),
|
|
43505
|
-
/* @__PURE__ */
|
|
43506
|
-
/* @__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: [
|
|
43507
43727
|
"seals version ",
|
|
43508
43728
|
render.template_version,
|
|
43509
43729
|
" \xB7 ",
|
|
@@ -43518,6 +43738,261 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43518
43738
|
] });
|
|
43519
43739
|
}
|
|
43520
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
|
+
|
|
43521
43996
|
// src/DashboardCanvas.tsx
|
|
43522
43997
|
import { useCallback as useCallback59, useEffect as useEffect102, useMemo as useMemo71, useState as useState117 } from "react";
|
|
43523
43998
|
import { useFields as useFields30, useRecordsClient as useRecordsClient85, useTable as useTable31 } from "@ai-matrx/records/react";
|
|
@@ -43538,20 +44013,20 @@ import {
|
|
|
43538
44013
|
|
|
43539
44014
|
// src/DashboardGrid.tsx
|
|
43540
44015
|
import {
|
|
43541
|
-
DndContext,
|
|
43542
|
-
KeyboardSensor,
|
|
43543
|
-
PointerSensor,
|
|
44016
|
+
DndContext as DndContext2,
|
|
44017
|
+
KeyboardSensor as KeyboardSensor2,
|
|
44018
|
+
PointerSensor as PointerSensor2,
|
|
43544
44019
|
pointerWithin,
|
|
43545
|
-
useSensor,
|
|
43546
|
-
useSensors
|
|
44020
|
+
useSensor as useSensor2,
|
|
44021
|
+
useSensors as useSensors2
|
|
43547
44022
|
} from "@dnd-kit/core";
|
|
43548
44023
|
import {
|
|
43549
|
-
SortableContext,
|
|
44024
|
+
SortableContext as SortableContext2,
|
|
43550
44025
|
rectSortingStrategy,
|
|
43551
|
-
sortableKeyboardCoordinates,
|
|
43552
|
-
useSortable
|
|
44026
|
+
sortableKeyboardCoordinates as sortableKeyboardCoordinates2,
|
|
44027
|
+
useSortable as useSortable2
|
|
43553
44028
|
} from "@dnd-kit/sortable";
|
|
43554
|
-
import { CSS } from "@dnd-kit/utilities";
|
|
44029
|
+
import { CSS as CSS3 } from "@dnd-kit/utilities";
|
|
43555
44030
|
import { cn as cn83 } from "@ai-matrx/design-system";
|
|
43556
44031
|
|
|
43557
44032
|
// src/dashboardLayout.ts
|
|
@@ -43615,9 +44090,9 @@ var dashboardCollision = (args) => {
|
|
|
43615
44090
|
}).sort((a, b) => a.data.value - b.data.value);
|
|
43616
44091
|
};
|
|
43617
44092
|
function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
43618
|
-
const sensors =
|
|
43619
|
-
|
|
43620
|
-
|
|
44093
|
+
const sensors = useSensors2(
|
|
44094
|
+
useSensor2(PointerSensor2, { activationConstraint: { distance: 4 } }),
|
|
44095
|
+
useSensor2(KeyboardSensor2, { coordinateGetter: sortableKeyboardCoordinates2 })
|
|
43621
44096
|
);
|
|
43622
44097
|
const ids = items.map((item) => item.id);
|
|
43623
44098
|
function dragEnd(event) {
|
|
@@ -43627,7 +44102,7 @@ function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
|
43627
44102
|
}
|
|
43628
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)) });
|
|
43629
44104
|
if (!arranging) return grid;
|
|
43630
|
-
return /* @__PURE__ */ jsx138(
|
|
44105
|
+
return /* @__PURE__ */ jsx138(DndContext2, { sensors, collisionDetection: dashboardCollision, onDragEnd: dragEnd, children: /* @__PURE__ */ jsx138(SortableContext2, { items: ids, strategy: rectSortingStrategy, children: grid }) });
|
|
43631
44106
|
}
|
|
43632
44107
|
function GridCell2({
|
|
43633
44108
|
item,
|
|
@@ -43635,8 +44110,8 @@ function GridCell2({
|
|
|
43635
44110
|
arranging,
|
|
43636
44111
|
onSpan
|
|
43637
44112
|
}) {
|
|
43638
|
-
const sortable =
|
|
43639
|
-
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;
|
|
43640
44115
|
return /* @__PURE__ */ jsxs128(
|
|
43641
44116
|
"div",
|
|
43642
44117
|
{
|
|
@@ -44842,7 +45317,7 @@ function TargetControl({
|
|
|
44842
45317
|
// src/QuestionPanel.tsx
|
|
44843
45318
|
import { copyText as copyText8 } from "@ai-matrx/kit/clipboard";
|
|
44844
45319
|
import { useEffect as useEffect103, useMemo as useMemo72, useRef as useRef59, useState as useState118 } from "react";
|
|
44845
|
-
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";
|
|
44846
45321
|
import { useFields as useFields31, useRecordsClient as useRecordsClient86 } from "@ai-matrx/records/react";
|
|
44847
45322
|
|
|
44848
45323
|
// src/question.ts
|
|
@@ -45424,9 +45899,9 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
|
|
|
45424
45899
|
linkMissed ? /* @__PURE__ */ jsx143("p", { className: "text-muted-foreground", children: "That question is not here or not shared with you." }) : null,
|
|
45425
45900
|
saveError ? /* @__PURE__ */ jsx143(RefusalNotice, { error: saveError }) : null,
|
|
45426
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: [
|
|
45427
|
-
/* @__PURE__ */ jsx143(
|
|
45902
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Measure" }),
|
|
45428
45903
|
/* @__PURE__ */ jsx143(Measures, { measures: spec.measures, fields: offered, onChange: (measures) => patch({ measures }) }),
|
|
45429
|
-
/* @__PURE__ */ jsx143(
|
|
45904
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Filter" }),
|
|
45430
45905
|
/* @__PURE__ */ jsxs132("div", { className: "flex flex-col gap-1.5", children: [
|
|
45431
45906
|
/* @__PURE__ */ jsx143(
|
|
45432
45907
|
ConditionGroup,
|
|
@@ -45440,9 +45915,9 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
|
|
|
45440
45915
|
),
|
|
45441
45916
|
/* @__PURE__ */ jsx143(Through, { conditions: spec.through, related, onChange: (through) => patch({ through }) })
|
|
45442
45917
|
] }),
|
|
45443
|
-
/* @__PURE__ */ jsx143(
|
|
45918
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Summarize by" }),
|
|
45444
45919
|
/* @__PURE__ */ jsx143(GroupPicker, { spec, fields: offered, related, onChange: (group) => patch({ group }) }),
|
|
45445
|
-
/* @__PURE__ */ jsx143(
|
|
45920
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "As of" }),
|
|
45446
45921
|
/* @__PURE__ */ jsxs132("div", { className: "flex items-center gap-1.5", children: [
|
|
45447
45922
|
/* @__PURE__ */ jsx143(
|
|
45448
45923
|
Checkbox16,
|
|
@@ -45466,7 +45941,7 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
|
|
|
45466
45941
|
) : /* @__PURE__ */ jsx143("span", { className: "text-muted-foreground", children: "Now" })
|
|
45467
45942
|
] }),
|
|
45468
45943
|
spec.group ? /* @__PURE__ */ jsxs132(Fragment57, { children: [
|
|
45469
|
-
/* @__PURE__ */ jsx143(
|
|
45944
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Sort" }),
|
|
45470
45945
|
/* @__PURE__ */ jsxs132("div", { className: "flex flex-wrap items-center gap-1.5", children: [
|
|
45471
45946
|
/* @__PURE__ */ jsx143(
|
|
45472
45947
|
PickOne,
|
|
@@ -45497,7 +45972,7 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
|
|
|
45497
45972
|
}
|
|
45498
45973
|
)
|
|
45499
45974
|
] }),
|
|
45500
|
-
/* @__PURE__ */ jsx143(
|
|
45975
|
+
/* @__PURE__ */ jsx143(Label25, { className: "pt-1.5 text-xs text-muted-foreground", children: "Show as" }),
|
|
45501
45976
|
/* @__PURE__ */ jsx143("div", { className: "flex flex-wrap gap-1", role: "radiogroup", "aria-label": "Show as", children: SHOWS.map((s) => /* @__PURE__ */ jsx143(
|
|
45502
45977
|
Button94,
|
|
45503
45978
|
{
|