@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.cjs
CHANGED
|
@@ -6186,7 +6186,8 @@ function formatControl(args) {
|
|
|
6186
6186
|
{
|
|
6187
6187
|
id,
|
|
6188
6188
|
type: def.editor,
|
|
6189
|
-
|
|
6189
|
+
inputMode: def.editor,
|
|
6190
|
+
...ownDetails ? { autoComplete: def.editor } : {},
|
|
6190
6191
|
value: text2,
|
|
6191
6192
|
disabled,
|
|
6192
6193
|
onChange: (e) => commit(e.target.value)
|
|
@@ -6677,7 +6678,7 @@ function isPersonsName(field2) {
|
|
|
6677
6678
|
const words2 = `${field2.key} ${field2.label ?? ""}`.toLowerCase();
|
|
6678
6679
|
return /(^|[\s_])(full[\s_]?)?name(\s|$)/.test(words2) && !/(therapist|doctor|physician|company|business|pet|room)/.test(words2);
|
|
6679
6680
|
}
|
|
6680
|
-
function FieldControl({ field: field2, value, onChange, disabled, id, autoOpen, onClose, onCommit, onCancel, addsChoices, initialSearch, asForm }) {
|
|
6681
|
+
function FieldControl({ field: field2, value, onChange, disabled, id, autoOpen, onClose, onCommit, onCancel, addsChoices, initialSearch, asForm, onChosen }) {
|
|
6681
6682
|
const host = useRecordsUi();
|
|
6682
6683
|
const kind = editorKindFor(field2);
|
|
6683
6684
|
const inputId = id ?? `field-${field2.key}`;
|
|
@@ -6741,7 +6742,8 @@ function FieldControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
6741
6742
|
{
|
|
6742
6743
|
id: inputId,
|
|
6743
6744
|
type: kind === "email" ? "email" : kind === "phone" ? "tel" : "url",
|
|
6744
|
-
|
|
6745
|
+
inputMode: kind === "email" ? "email" : kind === "phone" ? "tel" : "url",
|
|
6746
|
+
...ownDetails ? { autoComplete: kind === "email" ? "email" : kind === "phone" ? "tel" : "url" } : {},
|
|
6745
6747
|
value: value === null || value === void 0 ? "" : String(value),
|
|
6746
6748
|
disabled,
|
|
6747
6749
|
onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
|
|
@@ -6751,7 +6753,7 @@ function FieldControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
6751
6753
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CheckboxControl, { field: field2, value, onChange, disabled, id: inputId });
|
|
6752
6754
|
case "select":
|
|
6753
6755
|
case "multi_select":
|
|
6754
|
-
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(OptionControl, { field: field2, value, onChange, disabled, id: inputId, autoOpen, onClose, onCancel, addsChoices, initialSearch, asForm });
|
|
6756
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(OptionControl, { field: field2, value, onChange, disabled, id: inputId, autoOpen, onClose, onCancel, addsChoices, initialSearch, asForm, onChosen });
|
|
6755
6757
|
case "member":
|
|
6756
6758
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(PersonPicker, { field: field2, value, onChange, disabled, id: inputId, autoOpen, onClose, onCancel, initialSearch });
|
|
6757
6759
|
case "entity_reference":
|
|
@@ -6933,7 +6935,7 @@ function inTheStoresOrder(options) {
|
|
|
6933
6935
|
const made = (o) => String(o.created_at ?? "");
|
|
6934
6936
|
return [...options].sort((a, b) => position(a) - position(b) || made(a).localeCompare(made(b)));
|
|
6935
6937
|
}
|
|
6936
|
-
function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen, onClose, onCancel, addsChoices, initialSearch, asForm }) {
|
|
6938
|
+
function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen, onClose, onCancel, addsChoices, initialSearch, asForm, onChosen }) {
|
|
6937
6939
|
const client = (0, import_react38.useOptionalRecordsClient)();
|
|
6938
6940
|
const [options, setOptions] = (0, import_react37.useState)(null);
|
|
6939
6941
|
const [refusal4, setRefusal] = (0, import_react37.useState)(null);
|
|
@@ -6952,7 +6954,7 @@ function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
6952
6954
|
}, [client, field2.id, reread]);
|
|
6953
6955
|
const handed = publicChoicesOf(field2);
|
|
6954
6956
|
if (!client && handed.length > 0) {
|
|
6955
|
-
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ChoiceAnswer, { field: field2, choices: handed, value, onChange, disabled, id });
|
|
6957
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ChoiceAnswer, { field: field2, choices: handed, value, onChange, disabled, id, onChosen });
|
|
6956
6958
|
}
|
|
6957
6959
|
if (!client) {
|
|
6958
6960
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
@@ -6982,6 +6984,7 @@ function OptionControl({ field: field2, value, onChange, disabled, id, autoOpen,
|
|
|
6982
6984
|
onChange,
|
|
6983
6985
|
disabled,
|
|
6984
6986
|
id,
|
|
6987
|
+
onChosen,
|
|
6985
6988
|
isChosen: (choice, v) => {
|
|
6986
6989
|
const option = options.find((o) => (0, import_records15.optionKey)(o) === choice.key);
|
|
6987
6990
|
return option ? (0, import_records15.isTheChosen)(option, v) : false;
|
|
@@ -7252,20 +7255,24 @@ function ChoiceAnswer({
|
|
|
7252
7255
|
onChange,
|
|
7253
7256
|
disabled,
|
|
7254
7257
|
id,
|
|
7255
|
-
isChosen
|
|
7258
|
+
isChosen,
|
|
7259
|
+
onChosen
|
|
7256
7260
|
}) {
|
|
7257
7261
|
const [search, setSearch] = (0, import_react37.useState)("");
|
|
7258
7262
|
const held = field2.multi ? (0, import_records15.choiceValuesOf)(value) : value === null || value === void 0 || value === "" ? [] : [value];
|
|
7259
7263
|
const on = (choice) => held.some((v) => isChosen ? isChosen(choice, v) : String(v) === choice.key || String(v) === choice.label);
|
|
7260
7264
|
const press = (choice) => {
|
|
7261
7265
|
if (!field2.multi) {
|
|
7262
|
-
|
|
7266
|
+
const picking = !on(choice);
|
|
7267
|
+
onChange(picking ? choice.key : null);
|
|
7268
|
+
if (picking) onChosen?.();
|
|
7263
7269
|
return;
|
|
7264
7270
|
}
|
|
7265
7271
|
const keys = choices.filter((c) => c.key === choice.key ? !on(c) : on(c)).map((c) => c.key);
|
|
7266
7272
|
onChange(keys.length === 0 ? null : keys);
|
|
7267
7273
|
};
|
|
7268
7274
|
const lettered = choices.length <= CHOICE_LETTERS.length;
|
|
7275
|
+
const tabStop = choices.find((c) => on(c))?.key ?? choices[0]?.key;
|
|
7269
7276
|
const option = (choice) => {
|
|
7270
7277
|
const chosen = on(choice);
|
|
7271
7278
|
const letter = lettered ? CHOICE_LETTERS[choices.indexOf(choice)] : null;
|
|
@@ -7277,6 +7284,7 @@ function ChoiceAnswer({
|
|
|
7277
7284
|
"aria-checked": chosen,
|
|
7278
7285
|
"aria-label": choice.label,
|
|
7279
7286
|
disabled,
|
|
7287
|
+
tabIndex: field2.multi || choice.key === tabStop ? 0 : -1,
|
|
7280
7288
|
onClick: () => press(choice),
|
|
7281
7289
|
"data-records-choice-answer": choice.key,
|
|
7282
7290
|
className: (0, import_design_system20.cn)(
|
|
@@ -7303,8 +7311,25 @@ function ChoiceAnswer({
|
|
|
7303
7311
|
);
|
|
7304
7312
|
};
|
|
7305
7313
|
const onKeyDown = (event) => {
|
|
7306
|
-
if (
|
|
7314
|
+
if (disabled || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
7307
7315
|
if (event.target.tagName === "INPUT") return;
|
|
7316
|
+
const arrow = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[event.key];
|
|
7317
|
+
if (arrow) {
|
|
7318
|
+
const buttons = Array.from(
|
|
7319
|
+
event.currentTarget.querySelectorAll("[data-records-choice-answer]")
|
|
7320
|
+
);
|
|
7321
|
+
const here = buttons.indexOf(event.target);
|
|
7322
|
+
if (buttons.length === 0) return;
|
|
7323
|
+
event.preventDefault();
|
|
7324
|
+
const next = buttons[(Math.max(here, 0) + arrow + buttons.length) % buttons.length];
|
|
7325
|
+
next.focus();
|
|
7326
|
+
if (!field2.multi) {
|
|
7327
|
+
const choice = choices.find((c) => c.key === next.dataset["recordsChoiceAnswer"]);
|
|
7328
|
+
if (choice && !on(choice)) onChange(choice.key);
|
|
7329
|
+
}
|
|
7330
|
+
return;
|
|
7331
|
+
}
|
|
7332
|
+
if (!lettered) return;
|
|
7308
7333
|
const at = CHOICE_LETTERS.indexOf(event.key.toUpperCase());
|
|
7309
7334
|
if (event.key.length !== 1 || at < 0 || at >= choices.length) return;
|
|
7310
7335
|
event.preventDefault();
|
|
@@ -10340,7 +10365,7 @@ function ChoiceValue({ field: field2, value, onChange }) {
|
|
|
10340
10365
|
{
|
|
10341
10366
|
triggerSize: "compact",
|
|
10342
10367
|
triggerLabel: `Choice for ${field2.label || field2.key}`,
|
|
10343
|
-
triggerText: value || "Choose",
|
|
10368
|
+
triggerText: choices.find((c) => c.key === value || c.label === value)?.label ?? (value || "Choose"),
|
|
10344
10369
|
open,
|
|
10345
10370
|
onOpenChange: (next) => {
|
|
10346
10371
|
setOpen(next);
|
|
@@ -10352,7 +10377,7 @@ function ChoiceValue({ field: field2, value, onChange }) {
|
|
|
10352
10377
|
id: c.key,
|
|
10353
10378
|
label: c.label,
|
|
10354
10379
|
chip: choiceChipLookOf(field2, c.key, c.label).className,
|
|
10355
|
-
picked: c.label === value,
|
|
10380
|
+
picked: c.label === value || c.key === value,
|
|
10356
10381
|
attrs: { "data-pick-value": c.label }
|
|
10357
10382
|
})),
|
|
10358
10383
|
onPick: (key) => {
|
|
@@ -10461,6 +10486,15 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
10461
10486
|
attrs: { "data-condition": "field" }
|
|
10462
10487
|
}
|
|
10463
10488
|
),
|
|
10489
|
+
onField && !fields.some((f) => f.id === onField) ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
10490
|
+
"span",
|
|
10491
|
+
{
|
|
10492
|
+
className: "rounded bg-destructive px-1.5 py-0.5 text-[11px] font-medium text-destructive-foreground",
|
|
10493
|
+
title: "This condition names a question that is no longer on the form.",
|
|
10494
|
+
"data-records-condition-missing": "",
|
|
10495
|
+
children: "Missing question"
|
|
10496
|
+
}
|
|
10497
|
+
) : null,
|
|
10464
10498
|
onField ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
|
|
10465
10499
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
10466
10500
|
PickOne,
|
|
@@ -15352,7 +15386,18 @@ function useMergedGridControls(args) {
|
|
|
15352
15386
|
label: "Duplicate property",
|
|
15353
15387
|
icon: "copy-plus",
|
|
15354
15388
|
disabledReason: mayStructure ? void 0 : editable ? rights.why("structure") : "Editing is off here.",
|
|
15355
|
-
|
|
15389
|
+
// DUPLICATE PROPERTY (Notion N5): one door declares the copy right after this column and copies
|
|
15390
|
+
// every record's value into it (`custom.field_duplicate`); nothing to fill in, as in Notion.
|
|
15391
|
+
onSelect: () => {
|
|
15392
|
+
void client.fieldDuplicate({ field_id: field2.id }).then((made) => {
|
|
15393
|
+
if (!made.ok) {
|
|
15394
|
+
notify?.error(`${fieldName(field2)} was not duplicated: ${refusalSentence(made.error)}`);
|
|
15395
|
+
return;
|
|
15396
|
+
}
|
|
15397
|
+
(0, import_react86.tableShapeChanged)(tableId);
|
|
15398
|
+
notify?.success(`${made.data.label} is added.`);
|
|
15399
|
+
});
|
|
15400
|
+
}
|
|
15356
15401
|
});
|
|
15357
15402
|
if (summaries) {
|
|
15358
15403
|
out.push(summaryMenuItem(field2, columnId2 === firstKey, summaries.chosen[columnId2], summaries.operations, (op2) => summaries.choose(columnId2, op2)));
|
|
@@ -15699,7 +15744,7 @@ function useMergedGridControls(args) {
|
|
|
15699
15744
|
},
|
|
15700
15745
|
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_design_system47.DialogContent, { className: "max-h-[85vh] overflow-y-auto sm:max-w-2xl", children: [
|
|
15701
15746
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_design_system47.DialogHeader, { children: [
|
|
15702
|
-
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_design_system47.DialogTitle, { children: columnDialog?.kind === "settings" ? `Column settings \xB7 ${fieldName(columnDialog.field)}` : columnDialog?.kind === "insert" && columnDialog.
|
|
15747
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_design_system47.DialogTitle, { 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 }),
|
|
15703
15748
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_design_system47.DialogDescription, { className: "sr-only", children: "Name, type and look" })
|
|
15704
15749
|
] }),
|
|
15705
15750
|
columnDialog?.kind === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
@@ -15723,7 +15768,6 @@ function useMergedGridControls(args) {
|
|
|
15723
15768
|
FieldEditor,
|
|
15724
15769
|
{
|
|
15725
15770
|
tableId,
|
|
15726
|
-
...columnDialog.copyOf ? { copyOf: columnDialog.copyOf } : {},
|
|
15727
15771
|
onCancel: () => {
|
|
15728
15772
|
pendingInsert.current = null;
|
|
15729
15773
|
close();
|
|
@@ -37910,9 +37954,41 @@ function formPresentation(spec) {
|
|
|
37910
37954
|
welcome: welcomeDocument(spec.welcome ?? null),
|
|
37911
37955
|
endings: (spec.endings ?? []).map(endingDocument),
|
|
37912
37956
|
hidden_fields: spec.hiddenFields ?? [],
|
|
37913
|
-
theme: themeDocument(spec.theme ?? null)
|
|
37957
|
+
theme: themeDocument(spec.theme ?? null),
|
|
37958
|
+
...typeof spec.showOwnerHeader === "boolean" ? { show_owner_header: spec.showOwnerHeader } : {},
|
|
37959
|
+
...typeof spec.autoAdvance === "boolean" ? { auto_advance: spec.autoAdvance } : {},
|
|
37960
|
+
...spec.notify && (spec.notify.webhookUrl || spec.notify.webhookAutomationId) ? { notify: strip({ webhook_url: text(spec.notify.webhookUrl), webhook_automation_id: text(spec.notify.webhookAutomationId) }) } : {}
|
|
37914
37961
|
};
|
|
37915
37962
|
}
|
|
37963
|
+
function notifyFromDocument(raw) {
|
|
37964
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
37965
|
+
const d = raw;
|
|
37966
|
+
const n = { webhookUrl: text(d["webhook_url"]), webhookAutomationId: text(d["webhook_automation_id"]) };
|
|
37967
|
+
return n.webhookUrl || n.webhookAutomationId ? n : null;
|
|
37968
|
+
}
|
|
37969
|
+
var bool = (v) => typeof v === "boolean" ? v : null;
|
|
37970
|
+
function answerRefusal(kind, required, value) {
|
|
37971
|
+
const empty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
|
|
37972
|
+
if (empty) return required ? "This field is required" : null;
|
|
37973
|
+
if (typeof value !== "string") return null;
|
|
37974
|
+
const v = value.trim();
|
|
37975
|
+
if (kind === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v)) return "Enter a valid email";
|
|
37976
|
+
if (kind === "phone" && (v.replace(/\D/g, "").length < 7 || /[^\d\s()+.\-x]/i.test(v))) return "Enter a valid phone number";
|
|
37977
|
+
if (kind === "url" && !/^(https?:\/\/)?[^\s.\/]+(\.[^\s.\/]+)+(\/\S*)?$/i.test(v)) return "Enter a valid link";
|
|
37978
|
+
return null;
|
|
37979
|
+
}
|
|
37980
|
+
function pipeAnswers(text2, answers, questions, labelOf) {
|
|
37981
|
+
if (!text2.includes("{{")) return text2;
|
|
37982
|
+
return text2.replace(/\{\{\s*([^{}]+?)\s*\}\}/g, (_all, name) => {
|
|
37983
|
+
const wanted = name.trim().toLowerCase();
|
|
37984
|
+
const q = questions.find((x) => x.key.toLowerCase() === wanted) ?? questions.find((x) => (x.ask ?? "").trim().toLowerCase() === wanted);
|
|
37985
|
+
if (!q) return "";
|
|
37986
|
+
const value = answers[q.key];
|
|
37987
|
+
if (value === void 0 || value === null) return "";
|
|
37988
|
+
const one = (v) => labelOf?.(q.key, v) ?? String(v);
|
|
37989
|
+
return Array.isArray(value) ? value.map(one).join(", ") : one(value);
|
|
37990
|
+
});
|
|
37991
|
+
}
|
|
37916
37992
|
var text = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : null;
|
|
37917
37993
|
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
37918
37994
|
var strip = (doc) => Object.fromEntries(Object.entries(doc).filter(([, v]) => v !== null && v !== void 0));
|
|
@@ -38087,6 +38163,9 @@ function formFromSummary(summary) {
|
|
|
38087
38163
|
thankYou: thankYouFromDocument(presentation["thank_you"]),
|
|
38088
38164
|
look: formLookFromDocument(presentation["look"]),
|
|
38089
38165
|
submitLabel: presentation["submit_label"] ?? null,
|
|
38166
|
+
showOwnerHeader: bool(presentation["show_owner_header"]),
|
|
38167
|
+
autoAdvance: bool(presentation["auto_advance"]),
|
|
38168
|
+
notify: notifyFromDocument(presentation["notify"]),
|
|
38090
38169
|
// A form is public when the store says it is OPEN. A screen never decides
|
|
38091
38170
|
// this from a flag of its own: the link answers or it does not.
|
|
38092
38171
|
isPublic: summary.state === "open",
|
|
@@ -38870,6 +38949,50 @@ function compactTick(value) {
|
|
|
38870
38949
|
if (Math.abs(value) < 1e3 && !Number.isInteger(value)) return String(Math.round(value * 10) / 10);
|
|
38871
38950
|
return (0, import_format10.formatCount)(value, { style: "compact" });
|
|
38872
38951
|
}
|
|
38952
|
+
function wrapTickWords(label, perLine, maxLines = 3) {
|
|
38953
|
+
const words2 = label.split(/\s+/).filter(Boolean).map((w) => w.length > perLine ? `${w.slice(0, perLine - 1)}\u2026` : w);
|
|
38954
|
+
const lines = [];
|
|
38955
|
+
let line = "";
|
|
38956
|
+
for (const word of words2) {
|
|
38957
|
+
const next = line ? `${line} ${word}` : word;
|
|
38958
|
+
if (next.length <= perLine) line = next;
|
|
38959
|
+
else {
|
|
38960
|
+
lines.push(line);
|
|
38961
|
+
line = word;
|
|
38962
|
+
}
|
|
38963
|
+
}
|
|
38964
|
+
if (line) lines.push(line);
|
|
38965
|
+
if (lines.length <= maxLines) return lines;
|
|
38966
|
+
const kept = lines.slice(0, maxLines);
|
|
38967
|
+
const last = kept[maxLines - 1].replace(/…$/, "");
|
|
38968
|
+
kept[maxLines - 1] = `${last.length >= perLine ? last.slice(0, perLine - 1) : last}\u2026`;
|
|
38969
|
+
return kept;
|
|
38970
|
+
}
|
|
38971
|
+
function WrappedTick(props) {
|
|
38972
|
+
const { x = 0, y = 0, payload, across, perLine } = props;
|
|
38973
|
+
const lines = wrapTickWords(String(payload?.value ?? ""), perLine);
|
|
38974
|
+
const lineHeight = 11;
|
|
38975
|
+
return /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(
|
|
38976
|
+
"text",
|
|
38977
|
+
{
|
|
38978
|
+
x,
|
|
38979
|
+
y,
|
|
38980
|
+
fontSize: 10,
|
|
38981
|
+
fill: "currentColor",
|
|
38982
|
+
textAnchor: across ? "end" : "middle",
|
|
38983
|
+
"data-records-chart-tick": "",
|
|
38984
|
+
children: lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(
|
|
38985
|
+
"tspan",
|
|
38986
|
+
{
|
|
38987
|
+
x: across ? x - 4 : x,
|
|
38988
|
+
dy: i === 0 ? across ? -((lines.length - 1) * lineHeight) / 2 + 3 : 10 : lineHeight,
|
|
38989
|
+
children: line
|
|
38990
|
+
},
|
|
38991
|
+
i
|
|
38992
|
+
))
|
|
38993
|
+
}
|
|
38994
|
+
);
|
|
38995
|
+
}
|
|
38873
38996
|
function Drawing({
|
|
38874
38997
|
kind,
|
|
38875
38998
|
points,
|
|
@@ -38975,9 +39098,9 @@ function Drawing({
|
|
|
38975
39098
|
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.CartesianGrid, { vertical: horizontal, horizontal: !horizontal }),
|
|
38976
39099
|
horizontal ? /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(import_jsx_runtime120.Fragment, { children: [
|
|
38977
39100
|
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.XAxis, { type: "number", ...axis }),
|
|
38978
|
-
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.YAxis, { type: "category", dataKey: "label", width:
|
|
39101
|
+
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.YAxis, { type: "category", dataKey: "label", width: 96, ...axis, tick: /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(WrappedTick, { across: true, perLine: 15 }) })
|
|
38979
39102
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(import_jsx_runtime120.Fragment, { children: [
|
|
38980
|
-
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.XAxis, { dataKey: "label", ...axis }),
|
|
39103
|
+
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.XAxis, { dataKey: "label", ...axis, interval: 0, height: 40, tick: /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(WrappedTick, { perLine: Math.max(6, Math.floor(width / Math.max(points.length, 1) / 6)) }) }),
|
|
38981
39104
|
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.YAxis, { width: 44, ...axis })
|
|
38982
39105
|
] }),
|
|
38983
39106
|
tooltip,
|
|
@@ -40621,7 +40744,7 @@ function FormLookLogo({ view, size = "md" }) {
|
|
|
40621
40744
|
}
|
|
40622
40745
|
);
|
|
40623
40746
|
}
|
|
40624
|
-
function FormLookFrame({ look, title, children, contained = false, className }) {
|
|
40747
|
+
function FormLookFrame({ look, title, children, contained = false, showOwnerHeader = true, className }) {
|
|
40625
40748
|
const view = formLookView(look, title);
|
|
40626
40749
|
return /* @__PURE__ */ (0, import_jsx_runtime127.jsxs)(
|
|
40627
40750
|
"div",
|
|
@@ -40638,10 +40761,10 @@ function FormLookFrame({ look, title, children, contained = false, className })
|
|
|
40638
40761
|
}
|
|
40639
40762
|
) : null,
|
|
40640
40763
|
view.coverUrl ? /* @__PURE__ */ (0, import_jsx_runtime127.jsx)("div", { "data-records-form-cover": "", className: "aspect-[3/1] w-full overflow-hidden rounded-xl border border-border bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime127.jsx)("img", { src: view.coverUrl, alt: "", className: "h-full w-full object-cover" }) }) : null,
|
|
40641
|
-
/* @__PURE__ */ (0, import_jsx_runtime127.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
40764
|
+
showOwnerHeader ? /* @__PURE__ */ (0, import_jsx_runtime127.jsxs)("div", { className: "flex items-center gap-3", "data-records-form-owner": "", children: [
|
|
40642
40765
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)(FormLookLogo, { view }),
|
|
40643
40766
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)("p", { className: "min-w-0 truncate text-base font-semibold tracking-tight text-foreground", children: view.name })
|
|
40644
|
-
] }),
|
|
40767
|
+
] }) : null,
|
|
40645
40768
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
|
|
40646
40769
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)("div", { className: "w-full", children }),
|
|
40647
40770
|
view.footerLinks.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime127.jsx)("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__ */ (0, import_jsx_runtime127.jsx)(
|
|
@@ -41463,11 +41586,14 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
|
41463
41586
|
|
|
41464
41587
|
// src/FormBuilder.tsx
|
|
41465
41588
|
var import_clipboard9 = require("@ai-matrx/kit/clipboard");
|
|
41589
|
+
var import_design_system117 = require("@ai-matrx/design-system");
|
|
41590
|
+
var import_react232 = require("react");
|
|
41591
|
+
var import_core22 = require("@dnd-kit/core");
|
|
41592
|
+
var import_sortable = require("@dnd-kit/sortable");
|
|
41593
|
+
var import_utilities = require("@dnd-kit/utilities");
|
|
41594
|
+
var import_react233 = require("@ai-matrx/records/react");
|
|
41595
|
+
var import_records56 = require("@ai-matrx/records");
|
|
41466
41596
|
var import_design_system118 = require("@ai-matrx/design-system");
|
|
41467
|
-
var import_react234 = require("react");
|
|
41468
|
-
var import_react235 = require("@ai-matrx/records/react");
|
|
41469
|
-
var import_records57 = require("@ai-matrx/records");
|
|
41470
|
-
var import_design_system119 = require("@ai-matrx/design-system");
|
|
41471
41597
|
|
|
41472
41598
|
// src/FormFlowEditor.tsx
|
|
41473
41599
|
var import_react229 = require("react");
|
|
@@ -41814,7 +41940,8 @@ function FormStage({
|
|
|
41814
41940
|
className,
|
|
41815
41941
|
connected = false,
|
|
41816
41942
|
onStart,
|
|
41817
|
-
onQuestionShown
|
|
41943
|
+
onQuestionShown,
|
|
41944
|
+
autoAdvance
|
|
41818
41945
|
}) {
|
|
41819
41946
|
const host = useRecordsUi();
|
|
41820
41947
|
const [answers, setAnswers] = (0, import_react230.useState)(() => ({ ...initialAnswers ?? {} }));
|
|
@@ -41827,6 +41954,11 @@ function FormStage({
|
|
|
41827
41954
|
const [ending, setEnding] = (0, import_react230.useState)(null);
|
|
41828
41955
|
const [started, setStarted] = (0, import_react230.useState)(() => !form.welcome || resumed);
|
|
41829
41956
|
const routes = formRoutes(form);
|
|
41957
|
+
const [routeKnown, setRouteKnown] = (0, import_react230.useState)(() => !routes || !whichAsked);
|
|
41958
|
+
const [stopped, setStopped] = (0, import_react230.useState)(null);
|
|
41959
|
+
const answersRef = (0, import_react230.useRef)(answers);
|
|
41960
|
+
answersRef.current = answers;
|
|
41961
|
+
const choose = autoAdvance ?? form.autoAdvance ?? true;
|
|
41830
41962
|
const decoy = (0, import_react230.useRef)("");
|
|
41831
41963
|
const stage = (0, import_react230.useRef)(null);
|
|
41832
41964
|
const questions = (0, import_react230.useMemo)(() => {
|
|
@@ -41862,6 +41994,7 @@ function FormStage({
|
|
|
41862
41994
|
};
|
|
41863
41995
|
}
|
|
41864
41996
|
if (cancelled) return;
|
|
41997
|
+
setRouteKnown(true);
|
|
41865
41998
|
if (!told2.ok) {
|
|
41866
41999
|
setHidden({});
|
|
41867
42000
|
setError(told2.error ?? refusal("not_supported", told2.message, "Answer every question shown; none were skipped."));
|
|
@@ -41883,7 +42016,7 @@ function FormStage({
|
|
|
41883
42016
|
) : null
|
|
41884
42017
|
);
|
|
41885
42018
|
})();
|
|
41886
|
-
}, 250);
|
|
42019
|
+
}, routeKnown ? 250 : 0);
|
|
41887
42020
|
return () => {
|
|
41888
42021
|
cancelled = true;
|
|
41889
42022
|
clearTimeout(timer);
|
|
@@ -41918,11 +42051,24 @@ function FormStage({
|
|
|
41918
42051
|
const v = answers[q.key];
|
|
41919
42052
|
return v !== void 0 && v !== null && v !== "";
|
|
41920
42053
|
}).length;
|
|
41921
|
-
const
|
|
41922
|
-
|
|
41923
|
-
|
|
41924
|
-
|
|
42054
|
+
const refusalFor = (q, values = answersRef.current) => answerRefusal(q.field ? String(editorKindFor(q.field)) : "text", q.required, values[q.key]);
|
|
42055
|
+
const refuse = (q, say2) => {
|
|
42056
|
+
setStopped({ key: q.key, say: say2 });
|
|
42057
|
+
const i = live.findIndex((x) => x.key === q.key);
|
|
42058
|
+
if (oneAtATime && i >= 0 && i !== index) setAt(i);
|
|
42059
|
+
window.setTimeout(() => {
|
|
42060
|
+
const box = stage.current?.querySelector(`[data-records-form-question="${CSS.escape(q.key)}"]`);
|
|
42061
|
+
box?.querySelector("[role='radio'][aria-checked='true'], input:not([tabindex='-1']), textarea, select, [role='combobox'], [role='radio'], [role='checkbox']")?.focus();
|
|
42062
|
+
}, 0);
|
|
42063
|
+
};
|
|
41925
42064
|
const submit = (0, import_react230.useCallback)(async () => {
|
|
42065
|
+
for (const q of live) {
|
|
42066
|
+
const say2 = refusalFor(q);
|
|
42067
|
+
if (say2) {
|
|
42068
|
+
refuse(q, say2);
|
|
42069
|
+
return;
|
|
42070
|
+
}
|
|
42071
|
+
}
|
|
41926
42072
|
if (preview) {
|
|
41927
42073
|
setDone("preview");
|
|
41928
42074
|
return;
|
|
@@ -41954,20 +42100,28 @@ function FormStage({
|
|
|
41954
42100
|
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
41955
42101
|
async function advance() {
|
|
41956
42102
|
if (!oneAtATime) return;
|
|
42103
|
+
if (current) {
|
|
42104
|
+
const say2 = refusalFor(current);
|
|
42105
|
+
if (say2) {
|
|
42106
|
+
refuse(current, say2);
|
|
42107
|
+
return;
|
|
42108
|
+
}
|
|
42109
|
+
}
|
|
42110
|
+
setStopped(null);
|
|
41957
42111
|
if (!routes || !whichAsked || !current) {
|
|
41958
42112
|
if (index < live.length - 1) setAt(index + 1);
|
|
41959
|
-
else
|
|
42113
|
+
else void submit();
|
|
41960
42114
|
return;
|
|
41961
42115
|
}
|
|
41962
42116
|
let told2;
|
|
41963
42117
|
try {
|
|
41964
|
-
told2 = await whichAsked(
|
|
42118
|
+
told2 = await whichAsked(answersRef.current);
|
|
41965
42119
|
} catch {
|
|
41966
42120
|
told2 = { ok: false, message: "This form could not work out which question comes next, so the next one is shown." };
|
|
41967
42121
|
}
|
|
41968
42122
|
if (!told2.ok) {
|
|
41969
42123
|
if (index < live.length - 1) setAt(index + 1);
|
|
41970
|
-
else
|
|
42124
|
+
else void submit();
|
|
41971
42125
|
return;
|
|
41972
42126
|
}
|
|
41973
42127
|
const next = {};
|
|
@@ -41980,13 +42134,18 @@ function FormStage({
|
|
|
41980
42134
|
setAt(here + 1);
|
|
41981
42135
|
return;
|
|
41982
42136
|
}
|
|
41983
|
-
const stillMissing = onRoute.
|
|
41984
|
-
|
|
41985
|
-
|
|
41986
|
-
});
|
|
41987
|
-
if (stillMissing.length === 0) void submit();
|
|
42137
|
+
const stillMissing = onRoute.find((q) => refusalFor(q) !== null);
|
|
42138
|
+
if (stillMissing) refuse(stillMissing, refusalFor(stillMissing));
|
|
42139
|
+
else void submit();
|
|
41988
42140
|
}
|
|
42141
|
+
const advanceRef = (0, import_react230.useRef)(advance);
|
|
42142
|
+
advanceRef.current = advance;
|
|
42143
|
+
const chosen = (0, import_react230.useCallback)(() => {
|
|
42144
|
+
if (!oneAtATime || !choose) return;
|
|
42145
|
+
window.setTimeout(() => void advanceRef.current(), 450);
|
|
42146
|
+
}, [oneAtATime, choose]);
|
|
41989
42147
|
function retreat() {
|
|
42148
|
+
setStopped(null);
|
|
41990
42149
|
if (oneAtATime && index > 0) setAt(index - 1);
|
|
41991
42150
|
}
|
|
41992
42151
|
function onKeyDown(event) {
|
|
@@ -42002,12 +42161,11 @@ function FormStage({
|
|
|
42002
42161
|
else advance();
|
|
42003
42162
|
}
|
|
42004
42163
|
(0, import_react230.useEffect)(() => {
|
|
42005
|
-
const input = stage.current?.querySelector(
|
|
42006
|
-
// A choice question has no box: its first option takes the caret, so A / B / C answer it.
|
|
42164
|
+
const input = stage.current?.querySelector("[role='radio'][aria-checked='true']") ?? stage.current?.querySelector(
|
|
42007
42165
|
"input:not([tabindex='-1']), textarea, select, [role='combobox'], [role='radio'], [role='checkbox']"
|
|
42008
42166
|
);
|
|
42009
42167
|
input?.focus();
|
|
42010
|
-
}, [index, done]);
|
|
42168
|
+
}, [index, done, started]);
|
|
42011
42169
|
const startTold = (0, import_react230.useRef)(false);
|
|
42012
42170
|
(0, import_react230.useEffect)(() => {
|
|
42013
42171
|
if (startTold.current || !onStart) return;
|
|
@@ -42026,6 +42184,10 @@ function FormStage({
|
|
|
42026
42184
|
}
|
|
42027
42185
|
}, [current, live, oneAtATime, onQuestionShown, started, done]);
|
|
42028
42186
|
const theme = (0, import_design_system116.formThemeClasses)(form.theme ?? null);
|
|
42187
|
+
const pipe = (text2) => pipeAnswers(text2, answers, questions, (key, v) => {
|
|
42188
|
+
const f = questions.find((q) => q.key === key)?.field;
|
|
42189
|
+
return f ? publicChoicesOf(f).find((c) => c.key === String(v))?.label ?? null : null;
|
|
42190
|
+
});
|
|
42029
42191
|
if (done) {
|
|
42030
42192
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42031
42193
|
ThankYou,
|
|
@@ -42034,6 +42196,7 @@ function FormStage({
|
|
|
42034
42196
|
done,
|
|
42035
42197
|
ending: (form.endings ?? []).find((e) => e.id === ending) ?? null,
|
|
42036
42198
|
buttonClass: theme.buttonClass,
|
|
42199
|
+
pipe,
|
|
42037
42200
|
className: (0, import_design_system116.cn)("mx-auto max-w-xl py-10", centred && "text-center", className)
|
|
42038
42201
|
}
|
|
42039
42202
|
);
|
|
@@ -42045,14 +42208,14 @@ function FormStage({
|
|
|
42045
42208
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42046
42209
|
"section",
|
|
42047
42210
|
{
|
|
42048
|
-
className: (0, import_design_system116.cn)("mx-auto flex w-full max-w-
|
|
42211
|
+
className: (0, import_design_system116.cn)("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),
|
|
42049
42212
|
style: theme.surfaceStyle,
|
|
42050
42213
|
"data-records-form-welcome": "",
|
|
42051
42214
|
children: [
|
|
42052
42215
|
w.pictureUrl ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("img", { src: w.pictureUrl, alt: "", className: "max-h-56 w-full rounded-md object-cover" }) : null,
|
|
42053
|
-
w.title ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("h2", { className: "text-
|
|
42054
|
-
w.body ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "text-
|
|
42055
|
-
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Button, { size: "
|
|
42216
|
+
w.title ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("h2", { className: "text-2xl font-semibold sm:text-3xl", children: w.title }) : null,
|
|
42217
|
+
w.body ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "text-base text-muted-foreground", children: authored(host, w.body, "form-intro") }) : null,
|
|
42218
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Button, { size: "lg", className: theme.buttonClass, onClick: () => setStarted(true), "data-records-form-start": "", children: w.buttonLabel || "Start" }) })
|
|
42056
42219
|
]
|
|
42057
42220
|
}
|
|
42058
42221
|
);
|
|
@@ -42061,7 +42224,15 @@ function FormStage({
|
|
|
42061
42224
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42062
42225
|
"section",
|
|
42063
42226
|
{
|
|
42064
|
-
className: (0, import_design_system116.cn)(
|
|
42227
|
+
className: (0, import_design_system116.cn)(
|
|
42228
|
+
"mx-auto flex w-full max-w-2xl flex-col gap-4 py-4",
|
|
42229
|
+
// TYPEFORM'S SIZE (TYPEFORM-2): one question at a time fills the screen, centred in it.
|
|
42230
|
+
oneAtATime && "min-h-[min(70dvh,640px)]",
|
|
42231
|
+
theme.surface,
|
|
42232
|
+
theme.background !== "plain" && "rounded-lg px-5 sm:px-8",
|
|
42233
|
+
centred && "text-center",
|
|
42234
|
+
className
|
|
42235
|
+
),
|
|
42065
42236
|
style: theme.surfaceStyle,
|
|
42066
42237
|
"data-records-form-font": theme.font,
|
|
42067
42238
|
onKeyDown,
|
|
@@ -42071,20 +42242,41 @@ function FormStage({
|
|
|
42071
42242
|
import_design_system116.Progress,
|
|
42072
42243
|
{
|
|
42073
42244
|
value: live.length === 0 ? 0 : (oneAtATime ? index + 1 : answered) / live.length * 100,
|
|
42074
|
-
className: "h-1 flex-1"
|
|
42245
|
+
className: "h-1 flex-1",
|
|
42246
|
+
"aria-label": "Form progress",
|
|
42247
|
+
...routeKnown ? { "aria-valuetext": oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` } : {}
|
|
42075
42248
|
}
|
|
42076
42249
|
),
|
|
42077
|
-
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "tabular-nums", children: oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` })
|
|
42250
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: (0, import_design_system116.cn)("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}` })
|
|
42078
42251
|
] }),
|
|
42079
|
-
signedInPublic ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42080
|
-
|
|
42081
|
-
|
|
42082
|
-
|
|
42083
|
-
|
|
42084
|
-
|
|
42085
|
-
|
|
42086
|
-
|
|
42087
|
-
|
|
42252
|
+
signedInPublic ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42253
|
+
"p",
|
|
42254
|
+
{
|
|
42255
|
+
className: "w-fit rounded border border-dashed px-2 py-0.5 text-left text-xs text-muted-foreground",
|
|
42256
|
+
title: "The record will carry your name. Strangers answer at the public link.",
|
|
42257
|
+
children: "Signed in \xB7 saved as you"
|
|
42258
|
+
}
|
|
42259
|
+
) : null,
|
|
42260
|
+
unanswerable ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42261
|
+
"p",
|
|
42262
|
+
{
|
|
42263
|
+
className: "w-fit rounded border border-dashed px-2 py-0.5 text-left text-xs text-muted-foreground",
|
|
42264
|
+
title: "Nothing here can work out which conditional questions apply, so all of them are shown.",
|
|
42265
|
+
children: "Showing every question"
|
|
42266
|
+
}
|
|
42267
|
+
) : null,
|
|
42268
|
+
unresolved.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42269
|
+
"p",
|
|
42270
|
+
{
|
|
42271
|
+
className: "w-fit rounded border border-destructive/40 px-2 py-0.5 text-left text-xs text-destructive",
|
|
42272
|
+
title: "The table has no such field. Add the field, or take the question out.",
|
|
42273
|
+
children: [
|
|
42274
|
+
"Missing field: ",
|
|
42275
|
+
unresolved.map((q) => q.ask).join(", ")
|
|
42276
|
+
]
|
|
42277
|
+
}
|
|
42278
|
+
) : null,
|
|
42279
|
+
form.intro && index === 0 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "text-base text-muted-foreground", children: authored(host, form.intro, "form-intro") }) : null,
|
|
42088
42280
|
error ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(RefusalNotice, { error, className: "text-left" }) : null,
|
|
42089
42281
|
refusal4 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42090
42282
|
RefusalNotice,
|
|
@@ -42106,14 +42298,20 @@ function FormStage({
|
|
|
42106
42298
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42107
42299
|
Question,
|
|
42108
42300
|
{
|
|
42109
|
-
question: q,
|
|
42301
|
+
question: { ...q, ask: pipe(q.ask), help: q.help ? pipe(q.help) : null },
|
|
42110
42302
|
value: answers[q.key],
|
|
42111
|
-
onChange: (v) =>
|
|
42303
|
+
onChange: (v) => {
|
|
42304
|
+
if (stopped?.key === q.key) setStopped(null);
|
|
42305
|
+
setAnswers((prev) => ({ ...prev, [q.key]: v }));
|
|
42306
|
+
},
|
|
42307
|
+
onChosen: chosen,
|
|
42308
|
+
refused: stopped?.key === q.key ? stopped.say : null,
|
|
42309
|
+
large: oneAtATime,
|
|
42112
42310
|
upload: host.upload
|
|
42113
42311
|
},
|
|
42114
42312
|
q.key
|
|
42115
42313
|
)),
|
|
42116
|
-
live.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "text-xs text-muted-foreground",
|
|
42314
|
+
live.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "text-xs text-muted-foreground", title: "Every question is hidden by its own condition right now.", children: "Nothing to answer yet" }) : null
|
|
42117
42315
|
] }),
|
|
42118
42316
|
honeypotKey ? /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("div", { "aria-hidden": true, className: "pointer-events-none absolute left-[-9999px] h-px w-px overflow-hidden", children: [
|
|
42119
42317
|
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("label", { htmlFor: `hp-${honeypotKey}`, children: "Leave this field empty" }),
|
|
@@ -42132,16 +42330,14 @@ function FormStage({
|
|
|
42132
42330
|
}
|
|
42133
42331
|
)
|
|
42134
42332
|
] }) : null,
|
|
42135
|
-
/* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("footer", { className: (0, import_design_system116.cn)("flex flex-wrap items-center gap-
|
|
42136
|
-
oneAtATime && index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Button, { size: "
|
|
42137
|
-
oneAtATime && index < live.length - 1 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Button, { size: "
|
|
42138
|
-
|
|
42139
|
-
|
|
42140
|
-
"
|
|
42141
|
-
missing.length === 1 ? "needs" : "need",
|
|
42142
|
-
" an answer."
|
|
42333
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("footer", { className: (0, import_design_system116.cn)("flex flex-wrap items-center gap-3", centred && "justify-center"), children: [
|
|
42334
|
+
oneAtATime && index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Button, { size: "lg", variant: "ghost", className: theme.buttonClass, onClick: retreat, children: "Back" }) : null,
|
|
42335
|
+
oneAtATime && index < live.length - 1 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Button, { size: "lg", className: theme.buttonClass, onClick: () => void advance(), children: "Next" }) : /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Button, { size: "lg", className: theme.buttonClass, disabled: writing, onClick: () => void (oneAtATime ? advance() : submit()), children: writing ? "Sending\u2026" : form.submitLabel ?? "Submit" }),
|
|
42336
|
+
oneAtATime ? /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("span", { className: "hidden text-xs text-muted-foreground sm:inline", "data-records-form-enter-hint": "", children: [
|
|
42337
|
+
"press ",
|
|
42338
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("strong", { className: "font-semibold text-foreground", children: "Enter \u21B5" })
|
|
42143
42339
|
] }) : null,
|
|
42144
|
-
preview ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-xs text-muted-foreground",
|
|
42340
|
+
preview ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-xs text-muted-foreground", title: "Nothing is written from a preview.", children: "Preview" }) : null
|
|
42145
42341
|
] })
|
|
42146
42342
|
]
|
|
42147
42343
|
}
|
|
@@ -42153,12 +42349,15 @@ function ThankYou({
|
|
|
42153
42349
|
done,
|
|
42154
42350
|
className,
|
|
42155
42351
|
ending,
|
|
42156
|
-
buttonClass
|
|
42352
|
+
buttonClass,
|
|
42353
|
+
pipe = (t) => t
|
|
42157
42354
|
}) {
|
|
42158
42355
|
const host = useRecordsUi();
|
|
42159
42356
|
const redirect = (ending ? ending.redirectUrl?.trim() : form.thankYou?.redirectUrl?.trim()) || null;
|
|
42160
|
-
const
|
|
42161
|
-
const
|
|
42357
|
+
const rawTitle = ending ? ending.title : form.thankYou?.title;
|
|
42358
|
+
const rawBody = ending ? ending.body : form.thankYou?.body;
|
|
42359
|
+
const title = rawTitle ? pipe(rawTitle) : rawTitle;
|
|
42360
|
+
const body = rawBody ? pipe(rawBody) : rawBody;
|
|
42162
42361
|
const going = redirect !== null && done !== "preview";
|
|
42163
42362
|
(0, import_react230.useEffect)(() => {
|
|
42164
42363
|
if (!going || !redirect) return;
|
|
@@ -42175,8 +42374,8 @@ function ThankYou({
|
|
|
42175
42374
|
}
|
|
42176
42375
|
),
|
|
42177
42376
|
ending?.pictureUrl ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("img", { src: ending.pictureUrl, alt: "", className: "mb-3 max-h-48 w-full rounded-md object-cover" }) : null,
|
|
42178
|
-
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("h2", { className: "text-
|
|
42179
|
-
body ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "mt-
|
|
42377
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("h2", { className: "text-2xl font-semibold", "data-records-form-ending": ending?.id ?? "", children: title || "Thank you" }),
|
|
42378
|
+
body ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "mt-2 text-base text-muted-foreground", children: authored(host, body, "form-thank-you") }) : null,
|
|
42180
42379
|
going ? /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("p", { className: "mt-4 flex flex-wrap items-center gap-3 text-sm", children: [
|
|
42181
42380
|
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42182
42381
|
"a",
|
|
@@ -42189,11 +42388,11 @@ function ThankYou({
|
|
|
42189
42388
|
),
|
|
42190
42389
|
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-muted-foreground", children: "Taking you there\u2026" })
|
|
42191
42390
|
] }) : null,
|
|
42192
|
-
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "mt-3 text-xs text-muted-foreground", children: done === "preview" ? redirect ? `
|
|
42391
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "mt-3 text-xs text-muted-foreground", children: done === "preview" ? redirect ? `Preview \xB7 then ${redirect}` : "Preview \xB7 nothing saved" : done === "sent" ? (
|
|
42193
42392
|
// SAID TO THE PERSON WHO ANSWERED (lane HANDOVER, 2026-09-29): "Saved to Form 1, with
|
|
42194
42393
|
// this form stamped on it" was the owner's bookkeeping read out to a front-desk member
|
|
42195
42394
|
// and to strangers. Where it landed is the owner's to see on the table.
|
|
42196
|
-
"
|
|
42395
|
+
"Answers received"
|
|
42197
42396
|
) : done })
|
|
42198
42397
|
] });
|
|
42199
42398
|
}
|
|
@@ -42201,6 +42400,9 @@ function Question({
|
|
|
42201
42400
|
question,
|
|
42202
42401
|
value,
|
|
42203
42402
|
onChange,
|
|
42403
|
+
onChosen,
|
|
42404
|
+
refused,
|
|
42405
|
+
large,
|
|
42204
42406
|
upload
|
|
42205
42407
|
}) {
|
|
42206
42408
|
const host = useRecordsUi();
|
|
@@ -42208,297 +42410,62 @@ function Question({
|
|
|
42208
42410
|
const field2 = question.field;
|
|
42209
42411
|
if (!field2) return null;
|
|
42210
42412
|
const id = `form-${field2.key}`;
|
|
42413
|
+
const sayId = `${id}-refused`;
|
|
42211
42414
|
const isAttachment = editorKindFor(field2) === "attachment";
|
|
42212
|
-
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42213
|
-
|
|
42214
|
-
|
|
42215
|
-
|
|
42216
|
-
|
|
42217
|
-
"
|
|
42218
|
-
{
|
|
42219
|
-
id,
|
|
42220
|
-
type: "file",
|
|
42221
|
-
className: "text-xs",
|
|
42222
|
-
onChange: (e) => {
|
|
42223
|
-
const file = e.target.files?.[0];
|
|
42224
|
-
if (!file) return;
|
|
42225
|
-
setUploadError(null);
|
|
42226
|
-
void upload?.(file).then((result) => {
|
|
42227
|
-
if (!result) return;
|
|
42228
|
-
if (result.ok) onChange(result.fileId);
|
|
42229
|
-
else
|
|
42230
|
-
setUploadError(
|
|
42231
|
-
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
42232
|
-
);
|
|
42233
|
-
});
|
|
42234
|
-
}
|
|
42235
|
-
}
|
|
42415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42416
|
+
"div",
|
|
42417
|
+
{
|
|
42418
|
+
className: (0, import_design_system116.cn)(
|
|
42419
|
+
"flex flex-col",
|
|
42420
|
+
large ? "gap-3 [&_input]:text-base [&_textarea]:text-base [&_input]:min-h-12" : "gap-1.5 [&_input]:text-base [&_textarea]:text-base"
|
|
42236
42421
|
),
|
|
42237
|
-
|
|
42238
|
-
|
|
42239
|
-
|
|
42240
|
-
|
|
42241
|
-
|
|
42242
|
-
|
|
42243
|
-
}
|
|
42244
|
-
|
|
42245
|
-
|
|
42246
|
-
|
|
42247
|
-
|
|
42248
|
-
var import_records56 = require("@ai-matrx/records");
|
|
42249
|
-
var import_design_system117 = require("@ai-matrx/design-system");
|
|
42250
|
-
var import_jsx_runtime133 = require("react/jsx-runtime");
|
|
42251
|
-
var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
|
|
42252
|
-
var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
42253
|
-
function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className }) {
|
|
42254
|
-
const client = (0, import_react233.useRecordsClient)();
|
|
42255
|
-
const host = useRecordsUi();
|
|
42256
|
-
const table = (0, import_react233.useTable)(tableId);
|
|
42257
|
-
const rights = useTableRights(table.data);
|
|
42258
|
-
const [subscriptions, setSubscriptions] = (0, import_react232.useState)(null);
|
|
42259
|
-
const [cadences, setCadences] = (0, import_react232.useState)([]);
|
|
42260
|
-
const [views, setViews] = (0, import_react232.useState)(null);
|
|
42261
|
-
const [error, setError] = (0, import_react232.useState)(null);
|
|
42262
|
-
const [busy, setBusy] = (0, import_react232.useState)(false);
|
|
42263
|
-
const [stopping, setStopping] = (0, import_react232.useState)(null);
|
|
42264
|
-
const load = (0, import_react232.useCallback)(async () => {
|
|
42265
|
-
const [held, offered] = await Promise.all([
|
|
42266
|
-
// THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
|
|
42267
|
-
// to them plus — only where they hold admin on this Table — anyone's over
|
|
42268
|
-
// it, and it is narrowed to Tables they can already open.
|
|
42269
|
-
client.subscriptions({ table_id: tableId }),
|
|
42270
|
-
// The cadences are ASKED of the store, never a list typed into a picker
|
|
42271
|
-
// here that could drift from what the digest runner understands.
|
|
42272
|
-
client.subscriptionCadences()
|
|
42273
|
-
]);
|
|
42274
|
-
if (!held.ok) {
|
|
42275
|
-
setError(held.error);
|
|
42276
|
-
return;
|
|
42277
|
-
}
|
|
42278
|
-
setError(null);
|
|
42279
|
-
setSubscriptions(held.data);
|
|
42280
|
-
if (offered.ok) setCadences(offered.data);
|
|
42281
|
-
if (host.savedViews) {
|
|
42282
|
-
setViews(await host.savedViews());
|
|
42283
|
-
return;
|
|
42284
|
-
}
|
|
42285
|
-
const listed = await client.views({ table_id: tableId });
|
|
42286
|
-
if (!listed.ok) {
|
|
42287
|
-
setError(listed.error);
|
|
42288
|
-
return;
|
|
42289
|
-
}
|
|
42290
|
-
setViews(listed.data.map((v) => ({ id: v.view_id, name: v.name })));
|
|
42291
|
-
}, [client, host, tableId]);
|
|
42292
|
-
(0, import_react232.useEffect)(() => {
|
|
42293
|
-
void load();
|
|
42294
|
-
}, [load]);
|
|
42295
|
-
const write = (0, import_react232.useCallback)(
|
|
42296
|
-
async (spec) => {
|
|
42297
|
-
const declared2 = await client.subscriptionDeclare({
|
|
42298
|
-
table_id: tableId,
|
|
42299
|
-
spec: {
|
|
42300
|
-
name: spec.name,
|
|
42301
|
-
saved_view_id: spec.savedViewId,
|
|
42302
|
-
cadence: spec.cadence ?? "instant",
|
|
42303
|
-
schedule: spec.schedule ?? null,
|
|
42304
|
-
channel: spec.channel ?? "in_app",
|
|
42305
|
-
quiet_hours: spec.quietHours ?? null,
|
|
42306
|
-
// "Tell me" is the default, and the store refuses a subscription with
|
|
42307
|
-
// nobody to tell rather than writing one that fires at nobody.
|
|
42308
|
-
recipient_user_id: spec.recipientUserId ?? client.config.actor.user_id ?? null,
|
|
42309
|
-
event_key: spec.eventKey ?? "records.changed"
|
|
42310
|
-
}
|
|
42311
|
-
});
|
|
42312
|
-
if (!declared2.ok) {
|
|
42313
|
-
setError(declared2.error);
|
|
42314
|
-
return null;
|
|
42315
|
-
}
|
|
42316
|
-
return declared2.data;
|
|
42317
|
-
},
|
|
42318
|
-
[client, tableId]
|
|
42319
|
-
);
|
|
42320
|
-
(0, import_react232.useEffect)(() => {
|
|
42321
|
-
if (!subscriptions || !seed || seed.length === 0) return;
|
|
42322
|
-
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
42323
|
-
if (missing.length === 0) return;
|
|
42324
|
-
void (async () => {
|
|
42325
|
-
for (const spec of missing) {
|
|
42326
|
-
if (!await write(spec)) return;
|
|
42327
|
-
}
|
|
42328
|
-
await load();
|
|
42329
|
-
})();
|
|
42330
|
-
}, [subscriptions, seed, write, load]);
|
|
42331
|
-
async function subscribe(viewId, viewName, cadence, channel, schedule, quiet2) {
|
|
42332
|
-
setBusy(true);
|
|
42333
|
-
const ok = await write({
|
|
42334
|
-
name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
|
|
42335
|
-
savedViewId: viewId,
|
|
42336
|
-
cadence,
|
|
42337
|
-
channel,
|
|
42338
|
-
schedule: schedule.trim() === "" ? null : schedule.trim(),
|
|
42339
|
-
quietHours: quiet2
|
|
42340
|
-
});
|
|
42341
|
-
setBusy(false);
|
|
42342
|
-
if (!ok) return;
|
|
42343
|
-
onChoose?.(ok);
|
|
42344
|
-
await load();
|
|
42345
|
-
}
|
|
42346
|
-
async function makeEveryRecordView() {
|
|
42347
|
-
setBusy(true);
|
|
42348
|
-
const made = await client.viewDeclare({
|
|
42349
|
-
table_id: tableId,
|
|
42350
|
-
spec: { name: `Every ${table.data?.name ?? "record"}`, filters: null }
|
|
42351
|
-
});
|
|
42352
|
-
setBusy(false);
|
|
42353
|
-
if (!made.ok) {
|
|
42354
|
-
setError(made.error);
|
|
42355
|
-
return;
|
|
42356
|
-
}
|
|
42357
|
-
await load();
|
|
42358
|
-
}
|
|
42359
|
-
async function unsubscribe(ruleId) {
|
|
42360
|
-
setStopping(null);
|
|
42361
|
-
setBusy(true);
|
|
42362
|
-
const deleted = await client.recordDelete({ record_id: ruleId });
|
|
42363
|
-
setBusy(false);
|
|
42364
|
-
if (!deleted.ok) {
|
|
42365
|
-
setError(deleted.error);
|
|
42366
|
-
return;
|
|
42367
|
-
}
|
|
42368
|
-
await load();
|
|
42369
|
-
}
|
|
42370
|
-
if (error) {
|
|
42371
|
-
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42372
|
-
RefusalNotice,
|
|
42373
|
-
{
|
|
42374
|
-
error,
|
|
42375
|
-
className,
|
|
42376
|
-
actions: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
42377
|
-
}
|
|
42378
|
-
);
|
|
42379
|
-
}
|
|
42380
|
-
if (subscriptions === null) return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Skeleton, { className: (0, import_design_system117.cn)("h-40 w-full", className) });
|
|
42381
|
-
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: (0, import_design_system117.cn)("flex flex-col gap-2", className), children: [
|
|
42382
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
42383
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "font-medium text-foreground", children: "Tell me when" }),
|
|
42384
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { children: [
|
|
42385
|
-
subscriptions.length,
|
|
42386
|
-
" subscription",
|
|
42387
|
-
subscriptions.length === 1 ? "" : "s"
|
|
42388
|
-
] })
|
|
42389
|
-
] }),
|
|
42390
|
-
subscriptions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("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__ */ (0, import_jsx_runtime133.jsx)("ul", { className: "flex flex-col gap-1", children: subscriptions.map((held) => /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("li", { className: "flex items-center gap-2 rounded border px-2 py-1.5 text-xs", children: [
|
|
42391
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "truncate font-medium", children: held.name }),
|
|
42392
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-muted-foreground", children: CADENCE_WORDS2[held.cadence] ?? held.cadence }),
|
|
42393
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-muted-foreground", children: CHANNEL_WORDS3[held.channel] ?? held.channel }),
|
|
42394
|
-
held.schedule ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-muted-foreground", children: scheduleWords(held.schedule, held.quiet_hours?.tz) }) : null,
|
|
42395
|
-
held.next_digest_at ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
42396
|
-
"next ",
|
|
42397
|
-
new Date(held.next_digest_at).toLocaleString()
|
|
42398
|
-
] }) : null,
|
|
42399
|
-
(0, import_records56.hasQuietHours)(held.quiet_hours) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
42400
|
-
"quiet ",
|
|
42401
|
-
held.quiet_hours.start,
|
|
42402
|
-
"\u2013",
|
|
42403
|
-
held.quiet_hours.end
|
|
42404
|
-
] }) : null,
|
|
42405
|
-
held.recipient_user_id === null ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-destructive", children: "This one has nobody to tell, so it fires at nobody." }) : null,
|
|
42406
|
-
held.saved_view_id === null ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-destructive", children: "This one names no view, so the store never admits a record to it." }) : null,
|
|
42407
|
-
held.muted ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-muted-foreground", children: "Switched off \u2014 it tells nobody until somebody switches it back on." }) : null,
|
|
42408
|
-
onChoose ? held.rule_id === chosenRuleId ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
42409
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "font-medium text-foreground", children: "Used for this form" }),
|
|
42410
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Button, { size: "sm", variant: "ghost", disabled: busy, onClick: () => onChoose(null), children: "Stop using" })
|
|
42411
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42412
|
-
import_design_system117.Button,
|
|
42413
|
-
{
|
|
42414
|
-
size: "sm",
|
|
42415
|
-
variant: "outline",
|
|
42416
|
-
className: "ml-auto",
|
|
42417
|
-
disabled: busy,
|
|
42418
|
-
onClick: () => onChoose(held.rule_id),
|
|
42419
|
-
children: "Use for this form"
|
|
42420
|
-
}
|
|
42421
|
-
) : null,
|
|
42422
|
-
rights.write && !(onChoose && held.rule_id === chosenRuleId) && stopping === held.rule_id ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: (0, import_design_system117.cn)("flex flex-wrap items-center gap-2", onChoose ? void 0 : "ml-auto"), children: [
|
|
42423
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs text-destructive", children: archiveConfirmSentence("this subscription") }),
|
|
42424
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Button, { size: "sm", variant: "destructive", disabled: busy, onClick: () => void unsubscribe(held.rule_id), children: "Stop telling me" }),
|
|
42425
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Button, { size: "sm", variant: "ghost", onClick: () => setStopping(null), children: "Keep it" })
|
|
42426
|
-
] }) : rights.write && !(onChoose && held.rule_id === chosenRuleId) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42427
|
-
import_design_system117.Button,
|
|
42428
|
-
{
|
|
42429
|
-
size: "sm",
|
|
42430
|
-
variant: "ghost",
|
|
42431
|
-
className: onChoose ? void 0 : "ml-auto",
|
|
42432
|
-
disabled: busy,
|
|
42433
|
-
onClick: () => setStopping(held.rule_id),
|
|
42434
|
-
children: "Stop telling me"
|
|
42435
|
-
}
|
|
42436
|
-
) : null
|
|
42437
|
-
] }, held.rule_id)) }),
|
|
42438
|
-
onChoose && chosenRuleId && !subscriptions.some((held) => held.rule_id === chosenRuleId) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("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,
|
|
42439
|
-
views === null ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("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__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [
|
|
42440
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { children: "A subscription watches a saved view, and this table has none yet." }),
|
|
42441
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Button, { size: "sm", variant: "outline", disabled: busy, onClick: () => void makeEveryRecordView(), children: "Watch every record" })
|
|
42442
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("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__ */ (0, import_jsx_runtime133.jsxs)(
|
|
42443
|
-
"form",
|
|
42444
|
-
{
|
|
42445
|
-
className: "flex flex-wrap items-center gap-1",
|
|
42446
|
-
onSubmit: (event) => {
|
|
42447
|
-
event.preventDefault();
|
|
42448
|
-
const form = event.currentTarget;
|
|
42449
|
-
const view = form.elements.namedItem("view").value;
|
|
42450
|
-
const cadence = form.elements.namedItem("cadence").value;
|
|
42451
|
-
const channel = form.elements.namedItem("channel").value;
|
|
42452
|
-
const schedule = form.elements.namedItem("schedule").value;
|
|
42453
|
-
const start = form.elements.namedItem("quiet_start").value;
|
|
42454
|
-
const end = form.elements.namedItem("quiet_end").value;
|
|
42455
|
-
const name = views.find((v) => v.id === view)?.name ?? "View";
|
|
42456
|
-
const quiet2 = start && end ? { start, end, tz: Intl.DateTimeFormat().resolvedOptions().timeZone } : null;
|
|
42457
|
-
if (view) void subscribe(view, name, cadence, channel, schedule, quiet2);
|
|
42458
|
-
},
|
|
42459
|
-
children: [
|
|
42460
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("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__ */ (0, import_jsx_runtime133.jsx)("option", { value: view.id, children: view.name }, view.id)) }),
|
|
42461
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("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__ */ (0, import_jsx_runtime133.jsx)("option", { value: cadence, children: CADENCE_WORDS2[cadence] ?? cadence }, cadence)) }),
|
|
42462
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("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__ */ (0, import_jsx_runtime133.jsx)("option", { value: channel, children: CHANNEL_WORDS3[channel] }, channel)) }),
|
|
42463
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42464
|
-
"input",
|
|
42465
|
-
{
|
|
42466
|
-
name: "schedule",
|
|
42467
|
-
"aria-label": "When the summary goes out",
|
|
42468
|
-
placeholder: "monday 08:00",
|
|
42469
|
-
className: "h-7 w-28 rounded border bg-background px-1 text-xs"
|
|
42470
|
-
}
|
|
42471
|
-
),
|
|
42472
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs text-muted-foreground", children: "not between" }),
|
|
42473
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42474
|
-
"input",
|
|
42475
|
-
{
|
|
42476
|
-
name: "quiet_start",
|
|
42477
|
-
type: "time",
|
|
42478
|
-
"aria-label": "Quiet hours start",
|
|
42479
|
-
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
42480
|
-
}
|
|
42481
|
-
),
|
|
42482
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs text-muted-foreground", children: "and" }),
|
|
42483
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42422
|
+
"data-records-form-question": field2.key,
|
|
42423
|
+
"aria-describedby": refused ? sayId : void 0,
|
|
42424
|
+
children: [
|
|
42425
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("div", { className: "flex flex-wrap items-baseline gap-x-2", children: [
|
|
42426
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Label, { htmlFor: id, className: (0, import_design_system116.cn)("font-medium leading-snug text-foreground", large ? "text-xl sm:text-2xl" : "text-base"), children: question.ask }),
|
|
42427
|
+
question.required ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-sm text-foreground/80", children: "required" }) : null,
|
|
42428
|
+
field2.unit ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-sm text-foreground/70", children: field2.unit }) : null
|
|
42429
|
+
] }),
|
|
42430
|
+
question.help ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: (0, import_design_system116.cn)("text-muted-foreground", large ? "text-base" : "text-sm"), children: authored(host, question.help, "form-help") }) : null,
|
|
42431
|
+
isAttachment && !upload ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("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__ */ (0, import_jsx_runtime132.jsxs)(import_jsx_runtime132.Fragment, { children: [
|
|
42432
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42484
42433
|
"input",
|
|
42485
42434
|
{
|
|
42486
|
-
|
|
42487
|
-
type: "
|
|
42488
|
-
|
|
42489
|
-
|
|
42435
|
+
id,
|
|
42436
|
+
type: "file",
|
|
42437
|
+
className: "text-sm",
|
|
42438
|
+
onChange: (e) => {
|
|
42439
|
+
const file = e.target.files?.[0];
|
|
42440
|
+
if (!file) return;
|
|
42441
|
+
setUploadError(null);
|
|
42442
|
+
void upload?.(file).then((result) => {
|
|
42443
|
+
if (!result) return;
|
|
42444
|
+
if (result.ok) onChange(result.fileId);
|
|
42445
|
+
else
|
|
42446
|
+
setUploadError(
|
|
42447
|
+
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
42448
|
+
);
|
|
42449
|
+
});
|
|
42450
|
+
}
|
|
42490
42451
|
}
|
|
42491
42452
|
),
|
|
42492
|
-
/* @__PURE__ */ (0,
|
|
42493
|
-
|
|
42494
|
-
|
|
42495
|
-
|
|
42496
|
-
|
|
42497
|
-
|
|
42453
|
+
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(RefusalLine, { error: uploadError }) : null
|
|
42454
|
+
] }) : (
|
|
42455
|
+
// A choice is laid out as lettered rows in the form's colour (`ChoiceAnswer`).
|
|
42456
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(FieldControl, { field: field2, value, onChange, id, asForm: true, onChosen })
|
|
42457
|
+
),
|
|
42458
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { id: sayId, role: "alert", "aria-live": "assertive", className: "min-h-0 empty:hidden", "data-records-form-refused": "", children: refused ? /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("span", { className: "inline-flex items-center gap-1.5 rounded bg-destructive px-2 py-1 text-sm font-medium text-destructive-foreground", children: [
|
|
42459
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { "aria-hidden": "true", children: "!" }),
|
|
42460
|
+
refused
|
|
42461
|
+
] }) : null })
|
|
42462
|
+
]
|
|
42463
|
+
}
|
|
42464
|
+
);
|
|
42498
42465
|
}
|
|
42499
42466
|
|
|
42500
42467
|
// src/FormBuilder.tsx
|
|
42501
|
-
var
|
|
42468
|
+
var import_jsx_runtime133 = require("react/jsx-runtime");
|
|
42502
42469
|
function freshFormName(base, taken) {
|
|
42503
42470
|
const names = new Set(taken.map((n) => n.trim().toLowerCase()));
|
|
42504
42471
|
if (!names.has(base.toLowerCase())) return base;
|
|
@@ -42515,7 +42482,21 @@ function isLookRefusal(error) {
|
|
|
42515
42482
|
return Boolean(error && LOOK_REFUSAL.test(refusalSentence(error)));
|
|
42516
42483
|
}
|
|
42517
42484
|
var KEPT_DRAFT_PREFIX = "records-ui:form-draft:";
|
|
42518
|
-
var SPEC_KEYS = [
|
|
42485
|
+
var SPEC_KEYS = [
|
|
42486
|
+
"name",
|
|
42487
|
+
"intro",
|
|
42488
|
+
"questions",
|
|
42489
|
+
"flow",
|
|
42490
|
+
"theme",
|
|
42491
|
+
"look",
|
|
42492
|
+
"thankYou",
|
|
42493
|
+
"submitLabel",
|
|
42494
|
+
"welcome",
|
|
42495
|
+
"endings",
|
|
42496
|
+
"hiddenFields",
|
|
42497
|
+
"showOwnerHeader",
|
|
42498
|
+
"autoAdvance"
|
|
42499
|
+
];
|
|
42519
42500
|
function readKeptDraft(formId) {
|
|
42520
42501
|
try {
|
|
42521
42502
|
const raw = window.localStorage.getItem(KEPT_DRAFT_PREFIX + formId);
|
|
@@ -42546,31 +42527,31 @@ function withKept(stored, spec) {
|
|
|
42546
42527
|
return out;
|
|
42547
42528
|
}
|
|
42548
42529
|
function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne, createOnMount, className }) {
|
|
42549
|
-
const client = (0,
|
|
42530
|
+
const client = (0, import_react233.useRecordsClient)();
|
|
42550
42531
|
const host = useRecordsUi();
|
|
42551
42532
|
const claimSeed = useSeedGuard();
|
|
42552
|
-
const table = (0,
|
|
42533
|
+
const table = (0, import_react233.useTable)(tableId);
|
|
42553
42534
|
const rights = useTableRights(table.data);
|
|
42554
|
-
const fields = (0,
|
|
42555
|
-
const [forms, setForms] = (0,
|
|
42556
|
-
const [error, setError] = (0,
|
|
42557
|
-
const [refused, setRefused] = (0,
|
|
42558
|
-
const [activeId, setActiveId] = (0,
|
|
42559
|
-
const [draft, setDraft] = (0,
|
|
42560
|
-
const [saving, setSaving] = (0,
|
|
42561
|
-
const [saved, setSaved] = (0,
|
|
42562
|
-
const [publishing, setPublishing] = (0,
|
|
42563
|
-
const [copied, setCopied] = (0,
|
|
42564
|
-
const [shownUrl, setShownUrl] = (0,
|
|
42565
|
-
const [lookOptions, setLookOptions] = (0,
|
|
42566
|
-
const [lookUnread, setLookUnread] = (0,
|
|
42567
|
-
const [restoredAt, setRestoredAt] = (0,
|
|
42568
|
-
const [showResults, setShowResults] = (0,
|
|
42569
|
-
const [adding, setAdding] = (0,
|
|
42570
|
-
const keysBeforeAdd = (0,
|
|
42571
|
-
const [makeOnMount, setMakeOnMount] = (0,
|
|
42535
|
+
const fields = (0, import_react233.useFields)(tableId);
|
|
42536
|
+
const [forms, setForms] = (0, import_react232.useState)(null);
|
|
42537
|
+
const [error, setError] = (0, import_react232.useState)(null);
|
|
42538
|
+
const [refused, setRefused] = (0, import_react232.useState)(null);
|
|
42539
|
+
const [activeId, setActiveId] = (0, import_react232.useState)(activeFormId ?? null);
|
|
42540
|
+
const [draft, setDraft] = (0, import_react232.useState)(null);
|
|
42541
|
+
const [saving, setSaving] = (0, import_react232.useState)(false);
|
|
42542
|
+
const [saved, setSaved] = (0, import_react232.useState)(null);
|
|
42543
|
+
const [publishing, setPublishing] = (0, import_react232.useState)(false);
|
|
42544
|
+
const [copied, setCopied] = (0, import_react232.useState)(false);
|
|
42545
|
+
const [shownUrl, setShownUrl] = (0, import_react232.useState)(null);
|
|
42546
|
+
const [lookOptions, setLookOptions] = (0, import_react232.useState)(null);
|
|
42547
|
+
const [lookUnread, setLookUnread] = (0, import_react232.useState)(null);
|
|
42548
|
+
const [restoredAt, setRestoredAt] = (0, import_react232.useState)(null);
|
|
42549
|
+
const [showResults, setShowResults] = (0, import_react232.useState)(false);
|
|
42550
|
+
const [adding, setAdding] = (0, import_react232.useState)(false);
|
|
42551
|
+
const keysBeforeAdd = (0, import_react232.useRef)(null);
|
|
42552
|
+
const [makeOnMount, setMakeOnMount] = (0, import_react232.useState)("waiting");
|
|
42572
42553
|
const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
42573
|
-
const load = (0,
|
|
42554
|
+
const load = (0, import_react232.useCallback)(async () => {
|
|
42574
42555
|
const answered = await client.forms({ table_id: tableId });
|
|
42575
42556
|
if (!answered.ok) {
|
|
42576
42557
|
setError(answered.error);
|
|
@@ -42597,10 +42578,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42597
42578
|
setError(null);
|
|
42598
42579
|
setForms(mine);
|
|
42599
42580
|
}, [client, tableId, seed, claimSeed]);
|
|
42600
|
-
(0,
|
|
42581
|
+
(0, import_react232.useEffect)(() => {
|
|
42601
42582
|
void load();
|
|
42602
42583
|
}, [load]);
|
|
42603
|
-
(0,
|
|
42584
|
+
(0, import_react232.useEffect)(() => {
|
|
42604
42585
|
if (!forms || forms.length === 0) return;
|
|
42605
42586
|
if (createOnMount && makeOnMount !== "made") return;
|
|
42606
42587
|
const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
|
|
@@ -42617,14 +42598,14 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42617
42598
|
}
|
|
42618
42599
|
onActiveForm?.(chosen);
|
|
42619
42600
|
}, [forms, activeFormId, makeOnMount]);
|
|
42620
|
-
const byKey = (0,
|
|
42621
|
-
const conditionFields = (0,
|
|
42601
|
+
const byKey = (0, import_react232.useMemo)(() => new Map((fields.data ?? []).map((f) => [f.key, f])), [fields.data]);
|
|
42602
|
+
const conditionFields = (0, import_react232.useMemo)(
|
|
42622
42603
|
() => (fields.data ?? []).map((f) => ({ id: String(f.id), key: f.key, label: f.label || f.key, field: f })),
|
|
42623
42604
|
[fields.data]
|
|
42624
42605
|
);
|
|
42625
|
-
const stored = (0,
|
|
42606
|
+
const stored = (0, import_react232.useMemo)(() => draft ? (forms ?? []).find((f) => f.id === draft.id) ?? null : null, [draft, forms]);
|
|
42626
42607
|
const dirty = Boolean(draft && stored && !sameForm(draft, stored));
|
|
42627
|
-
(0,
|
|
42608
|
+
(0, import_react232.useEffect)(() => {
|
|
42628
42609
|
if (!draft || !stored) return;
|
|
42629
42610
|
const timer = window.setTimeout(() => {
|
|
42630
42611
|
if (sameForm(draft, stored)) dropKeptDraft(draft.id);
|
|
@@ -42633,7 +42614,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42633
42614
|
return () => window.clearTimeout(timer);
|
|
42634
42615
|
}, [draft, stored]);
|
|
42635
42616
|
const lookKey = draft ? JSON.stringify(formLookDocument(draft.look ?? null)) : null;
|
|
42636
|
-
(0,
|
|
42617
|
+
(0, import_react232.useEffect)(() => {
|
|
42637
42618
|
if (lookKey === null) return;
|
|
42638
42619
|
let cancelled = false;
|
|
42639
42620
|
const timer = window.setTimeout(() => {
|
|
@@ -42650,7 +42631,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42650
42631
|
window.clearTimeout(timer);
|
|
42651
42632
|
};
|
|
42652
42633
|
}, [client, lookKey]);
|
|
42653
|
-
(0,
|
|
42634
|
+
(0, import_react232.useEffect)(() => {
|
|
42654
42635
|
const before = keysBeforeAdd.current;
|
|
42655
42636
|
if (!before || !fields.data) return;
|
|
42656
42637
|
const made = askableFields(fields.data).filter((f) => !before.has(f.key));
|
|
@@ -42689,7 +42670,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42689
42670
|
}
|
|
42690
42671
|
dropKeptDraft(draft.id);
|
|
42691
42672
|
setRestoredAt(null);
|
|
42692
|
-
setSaved("Saved
|
|
42673
|
+
setSaved("Saved");
|
|
42693
42674
|
await load();
|
|
42694
42675
|
return true;
|
|
42695
42676
|
}
|
|
@@ -42728,7 +42709,22 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42728
42709
|
setRefused(written.error);
|
|
42729
42710
|
return;
|
|
42730
42711
|
}
|
|
42731
|
-
setSaved(
|
|
42712
|
+
setSaved("Saved");
|
|
42713
|
+
await load();
|
|
42714
|
+
}
|
|
42715
|
+
async function chooseWebhook(notify) {
|
|
42716
|
+
if (!draft) return;
|
|
42717
|
+
const next = { ...draft, notify };
|
|
42718
|
+
setDraft(next);
|
|
42719
|
+
setSaving(true);
|
|
42720
|
+
setRefused(null);
|
|
42721
|
+
const written = await client.formDeclare(formDeclareArgs(next));
|
|
42722
|
+
setSaving(false);
|
|
42723
|
+
if (!written.ok) {
|
|
42724
|
+
setRefused(written.error);
|
|
42725
|
+
return;
|
|
42726
|
+
}
|
|
42727
|
+
setSaved("Saved");
|
|
42732
42728
|
await load();
|
|
42733
42729
|
}
|
|
42734
42730
|
async function create(name) {
|
|
@@ -42745,15 +42741,15 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42745
42741
|
await load();
|
|
42746
42742
|
return true;
|
|
42747
42743
|
}
|
|
42748
|
-
const [started, setStarted] = (0,
|
|
42749
|
-
(0,
|
|
42744
|
+
const [started, setStarted] = (0, import_react232.useState)(false);
|
|
42745
|
+
(0, import_react232.useEffect)(() => {
|
|
42750
42746
|
if (!startWithOne || started || forms === null || forms.length > 0) return;
|
|
42751
42747
|
if (askableFields(fields.data ?? []).length === 0) return;
|
|
42752
42748
|
if (!table.data && !table.error) return;
|
|
42753
42749
|
setStarted(true);
|
|
42754
42750
|
void create(table.data?.name?.trim() ? `${table.data.name.trim()} form` : "Form 1");
|
|
42755
42751
|
}, [startWithOne, started, forms, fields.data, create, table.data, table.error]);
|
|
42756
|
-
(0,
|
|
42752
|
+
(0, import_react232.useEffect)(() => {
|
|
42757
42753
|
if (!createOnMount || makeOnMount !== "waiting" || forms === null) return;
|
|
42758
42754
|
if (!table.data && !table.error) return;
|
|
42759
42755
|
if (askableFields(fields.data ?? []).length === 0) {
|
|
@@ -42766,396 +42762,320 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42766
42762
|
(made) => setMakeOnMount(made ? "made" : "not-made")
|
|
42767
42763
|
);
|
|
42768
42764
|
}, [createOnMount, makeOnMount, forms, fields.data, create, table.data, table.error]);
|
|
42769
|
-
if (fields.error) return /* @__PURE__ */ (0,
|
|
42765
|
+
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(RefusalNotice, { error: fields.error, className });
|
|
42770
42766
|
if (error) {
|
|
42771
|
-
return /* @__PURE__ */ (0,
|
|
42767
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42772
42768
|
RefusalNotice,
|
|
42773
42769
|
{
|
|
42774
42770
|
error,
|
|
42775
42771
|
className,
|
|
42776
|
-
actions: /* @__PURE__ */ (0,
|
|
42772
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
42777
42773
|
}
|
|
42778
42774
|
);
|
|
42779
42775
|
}
|
|
42780
|
-
if (fields.loading || forms === null) return /* @__PURE__ */ (0,
|
|
42776
|
+
if (fields.loading || forms === null) return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Skeleton, { className: (0, import_design_system118.cn)("h-64 w-full", className) });
|
|
42781
42777
|
if (createOnMount && (makeOnMount === "waiting" || makeOnMount === "making") && !refused) {
|
|
42782
|
-
return /* @__PURE__ */ (0,
|
|
42778
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Skeleton, { className: (0, import_design_system118.cn)("h-64 w-full", className) });
|
|
42783
42779
|
}
|
|
42784
42780
|
if (!rights.admin) {
|
|
42785
|
-
return /* @__PURE__ */ (0,
|
|
42781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("p", { className: (0, import_design_system118.cn)("text-xs text-muted-foreground", className), children: rights.why("structure") });
|
|
42786
42782
|
}
|
|
42787
|
-
return
|
|
42788
|
-
|
|
42789
|
-
|
|
42790
|
-
|
|
42791
|
-
|
|
42792
|
-
|
|
42793
|
-
|
|
42794
|
-
|
|
42795
|
-
|
|
42796
|
-
|
|
42797
|
-
|
|
42798
|
-
|
|
42783
|
+
return (
|
|
42784
|
+
// THE CONTAINER IS THE PARENT (TYPEFORM-2): a container query asks an ANCESTOR, so `@3xl:` on the
|
|
42785
|
+
// element that is itself `@container` never fired and the preview sat 3,000 px below the editor.
|
|
42786
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("div", { className: (0, import_design_system118.cn)("@container min-h-0", className), children: /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex min-h-0 flex-col gap-3 @3xl:flex-row @3xl:items-start", children: [
|
|
42787
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
|
|
42788
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Forms", children: [
|
|
42789
|
+
forms.map((form) => /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42790
|
+
import_design_system118.Button,
|
|
42791
|
+
{
|
|
42792
|
+
size: "sm",
|
|
42793
|
+
variant: form.id === activeId ? "secondary" : "ghost",
|
|
42794
|
+
"aria-pressed": form.id === activeId,
|
|
42795
|
+
onClick: () => {
|
|
42796
|
+
setActiveId(form.id);
|
|
42797
|
+
setDraft(form);
|
|
42798
|
+
},
|
|
42799
|
+
children: form.name
|
|
42799
42800
|
},
|
|
42800
|
-
|
|
42801
|
-
|
|
42802
|
-
|
|
42803
|
-
|
|
42804
|
-
|
|
42805
|
-
|
|
42806
|
-
|
|
42807
|
-
|
|
42808
|
-
|
|
42809
|
-
|
|
42810
|
-
|
|
42811
|
-
|
|
42812
|
-
|
|
42813
|
-
|
|
42814
|
-
|
|
42815
|
-
|
|
42816
|
-
|
|
42817
|
-
|
|
42818
|
-
|
|
42819
|
-
|
|
42820
|
-
|
|
42821
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Button, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
42822
|
-
] })
|
|
42823
|
-
] }),
|
|
42824
|
-
refused && !isLookRefusal(refused) ? /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42825
|
-
RefusalNotice,
|
|
42826
|
-
{
|
|
42827
|
-
error: refused,
|
|
42828
|
-
onKeepEditing: () => setRefused(null),
|
|
42829
|
-
onDiscard: () => {
|
|
42830
|
-
const kept = forms.find((f) => f.id === activeId) ?? null;
|
|
42831
|
-
if (kept) dropKeptDraft(kept.id);
|
|
42832
|
-
setDraft(kept);
|
|
42833
|
-
setRestoredAt(null);
|
|
42834
|
-
setSaved(null);
|
|
42835
|
-
setRefused(null);
|
|
42836
|
-
}
|
|
42837
|
-
}
|
|
42838
|
-
) : null,
|
|
42839
|
-
restoredAt && dirty && stored ? /* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("p", { className: "flex items-center gap-2 text-xs text-muted-foreground", "data-records-form-restored": "", children: [
|
|
42840
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("span", { children: [
|
|
42841
|
-
"Unsaved changes from",
|
|
42842
|
-
" ",
|
|
42843
|
-
new Date(restoredAt).toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" }),
|
|
42844
|
-
" restored"
|
|
42801
|
+
form.id
|
|
42802
|
+
)),
|
|
42803
|
+
(fields.data ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42804
|
+
import_design_system118.Button,
|
|
42805
|
+
{
|
|
42806
|
+
size: "sm",
|
|
42807
|
+
variant: "ghost",
|
|
42808
|
+
onClick: () => void create(
|
|
42809
|
+
freshFormName(
|
|
42810
|
+
table.data?.name?.trim() ? `${table.data.name.trim()} form` : "Form",
|
|
42811
|
+
forms.map((f) => f.name)
|
|
42812
|
+
)
|
|
42813
|
+
),
|
|
42814
|
+
children: "New form"
|
|
42815
|
+
}
|
|
42816
|
+
) : null,
|
|
42817
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "ml-auto flex items-center gap-2", children: [
|
|
42818
|
+
saved ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs text-muted-foreground", children: saved }) : null,
|
|
42819
|
+
stored ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", variant: showResults ? "default" : "ghost", onClick: () => setShowResults((v) => !v), "data-records-form-results-toggle": "", children: "Results" }) : null,
|
|
42820
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
42821
|
+
] })
|
|
42845
42822
|
] }),
|
|
42846
|
-
/* @__PURE__ */ (0,
|
|
42847
|
-
|
|
42823
|
+
refused && !isLookRefusal(refused) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42824
|
+
RefusalNotice,
|
|
42848
42825
|
{
|
|
42849
|
-
|
|
42850
|
-
|
|
42851
|
-
|
|
42852
|
-
|
|
42853
|
-
|
|
42826
|
+
error: refused,
|
|
42827
|
+
onKeepEditing: () => setRefused(null),
|
|
42828
|
+
onDiscard: () => {
|
|
42829
|
+
const kept = forms.find((f) => f.id === activeId) ?? null;
|
|
42830
|
+
if (kept) dropKeptDraft(kept.id);
|
|
42831
|
+
setDraft(kept);
|
|
42854
42832
|
setRestoredAt(null);
|
|
42855
|
-
|
|
42856
|
-
|
|
42857
|
-
|
|
42858
|
-
)
|
|
42859
|
-
] }) : null,
|
|
42860
|
-
!draft ? /* @__PURE__ */ (0, import_jsx_runtime134.jsx)("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__ */ (0, import_jsx_runtime134.jsxs)(import_jsx_runtime134.Fragment, { children: [
|
|
42861
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42862
|
-
PublishRow,
|
|
42863
|
-
{
|
|
42864
|
-
url: `${publicOrigin}${(0, import_records57.publicFormPath)(draft.id)}`,
|
|
42865
|
-
state: draft.state,
|
|
42866
|
-
mayPublish: rights.structure,
|
|
42867
|
-
why: rights.why("structure"),
|
|
42868
|
-
busy: publishing,
|
|
42869
|
-
copied,
|
|
42870
|
-
shownUrl,
|
|
42871
|
-
onPublish: (wanted) => void publish(wanted),
|
|
42872
|
-
onCopy: (url) => void copyLink(url),
|
|
42873
|
-
unsaved: dirty
|
|
42833
|
+
setSaved(null);
|
|
42834
|
+
setRefused(null);
|
|
42835
|
+
}
|
|
42874
42836
|
}
|
|
42875
|
-
),
|
|
42876
|
-
/* @__PURE__ */ (0,
|
|
42877
|
-
|
|
42878
|
-
|
|
42879
|
-
|
|
42880
|
-
|
|
42837
|
+
) : null,
|
|
42838
|
+
restoredAt && dirty && stored ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("p", { className: "flex items-center gap-2 text-xs text-muted-foreground", "data-records-form-restored": "", children: [
|
|
42839
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { children: [
|
|
42840
|
+
"Unsaved changes from",
|
|
42841
|
+
" ",
|
|
42842
|
+
new Date(restoredAt).toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" }),
|
|
42843
|
+
" restored"
|
|
42844
|
+
] }),
|
|
42845
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42846
|
+
import_design_system118.Button,
|
|
42881
42847
|
{
|
|
42882
|
-
|
|
42883
|
-
|
|
42884
|
-
|
|
42848
|
+
size: "sm",
|
|
42849
|
+
variant: "ghost",
|
|
42850
|
+
onClick: () => {
|
|
42851
|
+
dropKeptDraft(stored.id);
|
|
42852
|
+
setDraft(stored);
|
|
42853
|
+
setRestoredAt(null);
|
|
42854
|
+
},
|
|
42855
|
+
children: "Discard"
|
|
42885
42856
|
}
|
|
42886
42857
|
)
|
|
42887
|
-
] }),
|
|
42888
|
-
/* @__PURE__ */ (0,
|
|
42889
|
-
/* @__PURE__ */ (0,
|
|
42890
|
-
|
|
42891
|
-
|
|
42892
|
-
|
|
42893
|
-
|
|
42894
|
-
|
|
42895
|
-
|
|
42896
|
-
|
|
42897
|
-
|
|
42898
|
-
|
|
42899
|
-
|
|
42900
|
-
)
|
|
42901
|
-
|
|
42902
|
-
|
|
42903
|
-
|
|
42904
|
-
|
|
42905
|
-
|
|
42858
|
+
] }) : null,
|
|
42859
|
+
!draft ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("p", { className: "text-xs text-muted-foreground", children: (fields.data ?? []).length === 0 ? "No fields yet" : "No form yet" }) : /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(import_jsx_runtime133.Fragment, { children: [
|
|
42860
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42861
|
+
PublishRow,
|
|
42862
|
+
{
|
|
42863
|
+
url: `${publicOrigin}${(0, import_records56.publicFormPath)(draft.id)}`,
|
|
42864
|
+
state: draft.state,
|
|
42865
|
+
mayPublish: rights.structure,
|
|
42866
|
+
why: rights.why("structure"),
|
|
42867
|
+
busy: publishing,
|
|
42868
|
+
copied,
|
|
42869
|
+
shownUrl,
|
|
42870
|
+
onPublish: (wanted) => void publish(wanted),
|
|
42871
|
+
onCopy: (url) => void copyLink(url),
|
|
42872
|
+
unsaved: dirty
|
|
42873
|
+
}
|
|
42874
|
+
),
|
|
42875
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(EmbedSnippets, { url: `${publicOrigin}${(0, import_records56.publicFormPath)(draft.id)}`, label: draft.name }),
|
|
42876
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42877
|
+
NotifyOnAnswer,
|
|
42878
|
+
{
|
|
42879
|
+
tableId,
|
|
42880
|
+
tableName: table.data?.name ?? "record",
|
|
42881
|
+
formName: draft.name,
|
|
42882
|
+
ruleId: draft.notifyRuleId,
|
|
42883
|
+
notify: draft.notify ?? null,
|
|
42884
|
+
onRule: (ruleId) => void chooseNotify(ruleId),
|
|
42885
|
+
onNotify: (notify) => void chooseWebhook(notify)
|
|
42886
|
+
}
|
|
42887
|
+
),
|
|
42888
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("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: [
|
|
42889
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex items-center gap-2", title: "Off hides your logo and name \u2014 for a client's form.", children: [
|
|
42890
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42891
|
+
import_design_system118.Checkbox,
|
|
42906
42892
|
{
|
|
42907
|
-
|
|
42908
|
-
|
|
42909
|
-
|
|
42910
|
-
placeholder: "https://",
|
|
42911
|
-
onChange: (e) => patch({ thankYou: { ...draft.thankYou, redirectUrl: e.target.value || null } })
|
|
42893
|
+
checked: draft.showOwnerHeader ?? true,
|
|
42894
|
+
onCheckedChange: (checked) => patch({ showOwnerHeader: checked === true }),
|
|
42895
|
+
"aria-label": "Show our logo and name"
|
|
42912
42896
|
}
|
|
42913
|
-
)
|
|
42897
|
+
),
|
|
42898
|
+
"Show our logo and name"
|
|
42914
42899
|
] }),
|
|
42915
|
-
/* @__PURE__ */ (0,
|
|
42916
|
-
/* @__PURE__ */ (0,
|
|
42917
|
-
|
|
42918
|
-
import_design_system119.BasicTextarea,
|
|
42900
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex items-center gap-2", title: "A single choice goes on to the next question when picked.", children: [
|
|
42901
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42902
|
+
import_design_system118.Checkbox,
|
|
42919
42903
|
{
|
|
42920
|
-
|
|
42921
|
-
|
|
42922
|
-
|
|
42923
|
-
onChange: (e) => patch({ thankYou: { ...draft.thankYou, body: e.target.value || null } })
|
|
42904
|
+
checked: draft.autoAdvance ?? true,
|
|
42905
|
+
onCheckedChange: (checked) => patch({ autoAdvance: checked === true }),
|
|
42906
|
+
"aria-label": "Next after a choice"
|
|
42924
42907
|
}
|
|
42925
|
-
)
|
|
42908
|
+
),
|
|
42909
|
+
"Next after a choice"
|
|
42926
42910
|
] })
|
|
42927
|
-
] })
|
|
42928
|
-
] }),
|
|
42929
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(WelcomeEditor, { value: draft.welcome, pictures: lookOptions?.pictures ?? null, onChange: (welcome) => patch({ welcome }) }),
|
|
42930
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42931
|
-
EndingsEditor,
|
|
42932
|
-
{
|
|
42933
|
-
endings: draft.endings ?? [],
|
|
42934
|
-
pictures: lookOptions?.pictures ?? null,
|
|
42935
|
-
fieldKeys: conditionFields,
|
|
42936
|
-
scored: draft.questions.some((q) => q.points),
|
|
42937
|
-
onChange: (endings) => patch({ endings })
|
|
42938
|
-
}
|
|
42939
|
-
),
|
|
42940
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
42941
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42942
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Label, { className: "text-xs font-medium", children: "Name" }),
|
|
42943
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.BasicInput, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
|
|
42944
|
-
] }),
|
|
42945
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42946
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Label, { className: "text-xs font-medium", children: "Flow" }),
|
|
42947
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42948
|
-
"select",
|
|
42949
|
-
{
|
|
42950
|
-
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
42951
|
-
value: draft.flow ?? "one-at-a-time",
|
|
42952
|
-
onChange: (e) => patch({ flow: e.target.value }),
|
|
42953
|
-
children: FORM_FLOWS.map((flow) => /* @__PURE__ */ (0, import_jsx_runtime134.jsx)("option", { value: flow, children: flow === "one-at-a-time" ? "One question at a time" : "All on one page" }, flow))
|
|
42954
|
-
}
|
|
42955
|
-
)
|
|
42956
42911
|
] }),
|
|
42957
|
-
/* @__PURE__ */ (0,
|
|
42958
|
-
/* @__PURE__ */ (0,
|
|
42959
|
-
/* @__PURE__ */ (0,
|
|
42960
|
-
|
|
42961
|
-
|
|
42962
|
-
|
|
42963
|
-
|
|
42964
|
-
|
|
42965
|
-
|
|
42966
|
-
|
|
42967
|
-
|
|
42968
|
-
|
|
42969
|
-
|
|
42970
|
-
|
|
42971
|
-
|
|
42972
|
-
|
|
42973
|
-
|
|
42974
|
-
|
|
42975
|
-
|
|
42976
|
-
|
|
42977
|
-
|
|
42912
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("section", { className: "flex flex-col gap-2 rounded border px-2.5 py-2", "aria-label": "After somebody sends it", children: [
|
|
42913
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs font-medium", title: "What people see after sending, and an optional page they go on to.", children: "After sending" }),
|
|
42914
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
42915
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42916
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Thank-you title" }),
|
|
42917
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42918
|
+
import_design_system118.BasicInput,
|
|
42919
|
+
{
|
|
42920
|
+
value: draft.thankYou?.title ?? "",
|
|
42921
|
+
placeholder: "Thank you",
|
|
42922
|
+
onChange: (e) => patch({ thankYou: { ...draft.thankYou, title: e.target.value || null } })
|
|
42923
|
+
}
|
|
42924
|
+
)
|
|
42925
|
+
] }),
|
|
42926
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42927
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Then go to (optional)" }),
|
|
42928
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42929
|
+
import_design_system118.BasicInput,
|
|
42930
|
+
{
|
|
42931
|
+
type: "url",
|
|
42932
|
+
inputMode: "url",
|
|
42933
|
+
value: draft.thankYou?.redirectUrl ?? "",
|
|
42934
|
+
placeholder: "https://",
|
|
42935
|
+
onChange: (e) => patch({ thankYou: { ...draft.thankYou, redirectUrl: e.target.value || null } })
|
|
42936
|
+
}
|
|
42937
|
+
)
|
|
42938
|
+
] }),
|
|
42939
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "col-span-2 flex flex-col gap-1", children: [
|
|
42940
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Thank-you message" }),
|
|
42941
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42942
|
+
import_design_system118.BasicTextarea,
|
|
42943
|
+
{
|
|
42944
|
+
rows: 2,
|
|
42945
|
+
value: draft.thankYou?.body ?? "",
|
|
42946
|
+
placeholder: "What happens next, in your words.",
|
|
42947
|
+
onChange: (e) => patch({ thankYou: { ...draft.thankYou, body: e.target.value || null } })
|
|
42948
|
+
}
|
|
42949
|
+
)
|
|
42950
|
+
] })
|
|
42951
|
+
] })
|
|
42978
42952
|
] }),
|
|
42979
|
-
/* @__PURE__ */ (0,
|
|
42980
|
-
|
|
42981
|
-
|
|
42982
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Label, { className: "text-xs font-medium", children: "Look" }),
|
|
42983
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42984
|
-
AccentSwatches,
|
|
42953
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(WelcomeEditor, { value: draft.welcome, pictures: lookOptions?.pictures ?? null, onChange: (welcome) => patch({ welcome }) }),
|
|
42954
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42955
|
+
EndingsEditor,
|
|
42985
42956
|
{
|
|
42986
|
-
|
|
42987
|
-
|
|
42988
|
-
|
|
42957
|
+
endings: draft.endings ?? [],
|
|
42958
|
+
pictures: lookOptions?.pictures ?? null,
|
|
42959
|
+
fieldKeys: conditionFields,
|
|
42960
|
+
scored: draft.questions.some((q) => q.points),
|
|
42961
|
+
onChange: (endings) => patch({ endings })
|
|
42989
42962
|
}
|
|
42990
42963
|
),
|
|
42991
|
-
/* @__PURE__ */ (0,
|
|
42992
|
-
|
|
42993
|
-
|
|
42994
|
-
/* @__PURE__ */ (0,
|
|
42995
|
-
|
|
42996
|
-
|
|
42964
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
42965
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42966
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Name" }),
|
|
42967
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.BasicInput, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
|
|
42968
|
+
] }),
|
|
42969
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42970
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Flow" }),
|
|
42971
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42972
|
+
"select",
|
|
42997
42973
|
{
|
|
42998
|
-
|
|
42999
|
-
|
|
43000
|
-
|
|
43001
|
-
|
|
43002
|
-
onChange: (id) => patchLook({ logoFileId: id || null })
|
|
42974
|
+
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
42975
|
+
value: draft.flow ?? "one-at-a-time",
|
|
42976
|
+
onChange: (e) => patch({ flow: e.target.value }),
|
|
42977
|
+
children: FORM_FLOWS.map((flow) => /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("option", { value: flow, children: flow === "one-at-a-time" ? "One question at a time" : "All on one page" }, flow))
|
|
43003
42978
|
}
|
|
43004
42979
|
)
|
|
43005
42980
|
] }),
|
|
43006
|
-
/* @__PURE__ */ (0,
|
|
43007
|
-
/* @__PURE__ */ (0,
|
|
43008
|
-
/* @__PURE__ */ (0,
|
|
43009
|
-
|
|
42981
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "col-span-2 flex flex-col gap-1", children: [
|
|
42982
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Intro" }),
|
|
42983
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42984
|
+
import_design_system118.BasicTextarea,
|
|
43010
42985
|
{
|
|
43011
|
-
|
|
43012
|
-
|
|
43013
|
-
|
|
43014
|
-
noneLabel: "No cover",
|
|
43015
|
-
onChange: (id) => patchLook({ coverFileId: id || null })
|
|
42986
|
+
rows: 2,
|
|
42987
|
+
value: draft.intro ?? "",
|
|
42988
|
+
onChange: (e) => patch({ intro: e.target.value || null })
|
|
43016
42989
|
}
|
|
43017
42990
|
)
|
|
43018
|
-
] })
|
|
43019
|
-
|
|
43020
|
-
|
|
43021
|
-
|
|
43022
|
-
|
|
43023
|
-
|
|
43024
|
-
|
|
43025
|
-
|
|
43026
|
-
|
|
43027
|
-
|
|
43028
|
-
|
|
43029
|
-
|
|
43030
|
-
|
|
43031
|
-
] })
|
|
42991
|
+
] }),
|
|
42992
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42993
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Submit button" }),
|
|
42994
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42995
|
+
import_design_system118.BasicInput,
|
|
42996
|
+
{
|
|
42997
|
+
value: draft.submitLabel ?? "",
|
|
42998
|
+
placeholder: "Submit",
|
|
42999
|
+
onChange: (e) => patch({ submitLabel: e.target.value || null })
|
|
43000
|
+
}
|
|
43001
|
+
)
|
|
43002
|
+
] }),
|
|
43003
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(HiddenFieldsEditor, { value: draft.hiddenFields ?? [], onChange: (hiddenFields) => patch({ hiddenFields }) })
|
|
43032
43004
|
] }),
|
|
43033
|
-
|
|
43034
|
-
|
|
43035
|
-
|
|
43036
|
-
|
|
43037
|
-
|
|
43038
|
-
|
|
43039
|
-
|
|
43040
|
-
|
|
43005
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1.5", "data-records-form-look-editor": "", children: [
|
|
43006
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Look" }),
|
|
43007
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43008
|
+
AccentSwatches,
|
|
43009
|
+
{
|
|
43010
|
+
value: draft.look?.accent ?? "",
|
|
43011
|
+
noneLabel: "Organization colour",
|
|
43012
|
+
onChange: (accent) => patchLook({ accent: accent || null })
|
|
43013
|
+
}
|
|
43014
|
+
),
|
|
43015
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(ThemeEditor, { theme: draft.theme, pictures: lookOptions?.pictures ?? null, onChange: (theme) => patch({ theme }) }),
|
|
43016
|
+
lookOptions ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
43017
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
43018
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs", children: "Logo" }),
|
|
43019
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43020
|
+
PublicPicturePicker,
|
|
43041
43021
|
{
|
|
43042
|
-
|
|
43043
|
-
|
|
43044
|
-
|
|
43045
|
-
|
|
43046
|
-
})
|
|
43022
|
+
label: "Logo",
|
|
43023
|
+
pictures: lookOptions.pictures,
|
|
43024
|
+
value: draft.look?.logoFileId ?? "",
|
|
43025
|
+
noneLabel: "Organization logo",
|
|
43026
|
+
onChange: (id) => patchLook({ logoFileId: id || null })
|
|
43047
43027
|
}
|
|
43048
|
-
)
|
|
43049
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)("span", { className: "text-xs font-medium", children: fieldName(field2) }),
|
|
43050
|
-
field2.required ? /* @__PURE__ */ (0, import_jsx_runtime134.jsx)("span", { className: "text-xs text-destructive", children: "required" }) : null
|
|
43028
|
+
)
|
|
43051
43029
|
] }),
|
|
43052
|
-
|
|
43053
|
-
/* @__PURE__ */ (0,
|
|
43054
|
-
|
|
43055
|
-
|
|
43056
|
-
className: "h-8 text-xs",
|
|
43057
|
-
placeholder: fieldName(field2),
|
|
43058
|
-
"aria-label": `How to ask for ${fieldName(field2)}`,
|
|
43059
|
-
value: question.ask ?? "",
|
|
43060
|
-
onChange: (e) => patchQuestion(index, { ask: e.target.value || null })
|
|
43061
|
-
}
|
|
43062
|
-
),
|
|
43063
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43064
|
-
import_design_system119.BasicInput,
|
|
43065
|
-
{
|
|
43066
|
-
className: "h-8 text-xs",
|
|
43067
|
-
placeholder: "Help text",
|
|
43068
|
-
"aria-label": `Help text for ${fieldName(field2)}`,
|
|
43069
|
-
value: question.help ?? "",
|
|
43070
|
-
onChange: (e) => patchQuestion(index, { help: e.target.value || null })
|
|
43071
|
-
}
|
|
43072
|
-
),
|
|
43073
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("label", { className: "col-span-2 flex items-center gap-2 text-xs", children: [
|
|
43074
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43075
|
-
import_design_system119.Checkbox,
|
|
43076
|
-
{
|
|
43077
|
-
checked: field2.required ? true : question.required === true,
|
|
43078
|
-
disabled: field2.required === true,
|
|
43079
|
-
"aria-label": `${fieldName(field2)} is required`,
|
|
43080
|
-
onCheckedChange: (checked) => patchQuestion(index, { required: checked === true ? true : null })
|
|
43081
|
-
}
|
|
43082
|
-
),
|
|
43083
|
-
"Required"
|
|
43084
|
-
] }),
|
|
43085
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43086
|
-
Condition,
|
|
43030
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
43031
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs", children: "Cover" }),
|
|
43032
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43033
|
+
PublicPicturePicker,
|
|
43087
43034
|
{
|
|
43088
|
-
|
|
43089
|
-
|
|
43090
|
-
|
|
43091
|
-
|
|
43092
|
-
|
|
43093
|
-
field: f
|
|
43094
|
-
})),
|
|
43095
|
-
onChange: (showIf) => patchQuestion(index, { showIf })
|
|
43096
|
-
}
|
|
43097
|
-
),
|
|
43098
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43099
|
-
QuestionLogic,
|
|
43100
|
-
{
|
|
43101
|
-
question,
|
|
43102
|
-
index,
|
|
43103
|
-
questions: draft.questions,
|
|
43104
|
-
endings: draft.endings ?? [],
|
|
43105
|
-
field: field2,
|
|
43106
|
-
fieldKeys: conditionFields,
|
|
43107
|
-
label: (key) => {
|
|
43108
|
-
const f = byKey.get(key);
|
|
43109
|
-
return draft.questions.find((q) => q.field === key)?.ask || (f ? fieldName(f) : key);
|
|
43110
|
-
},
|
|
43111
|
-
onChange: (part) => patchQuestion(index, part)
|
|
43035
|
+
label: "Cover",
|
|
43036
|
+
pictures: lookOptions.pictures,
|
|
43037
|
+
value: draft.look?.coverFileId ?? "",
|
|
43038
|
+
noneLabel: "No cover",
|
|
43039
|
+
onChange: (id) => patchLook({ coverFileId: id || null })
|
|
43112
43040
|
}
|
|
43113
43041
|
)
|
|
43114
|
-
] })
|
|
43115
|
-
] },
|
|
43116
|
-
|
|
43117
|
-
|
|
43118
|
-
|
|
43119
|
-
|
|
43120
|
-
|
|
43121
|
-
className: "rounded border border-destructive/40 bg-destructive/5 px-2 py-1.5 text-xs text-destructive",
|
|
43122
|
-
message: requiredLeftUnasked(fields.data ?? [], draft.questions.map((q) => q.field)) ?? "",
|
|
43123
|
-
operation: "Build a form",
|
|
43124
|
-
origin: "records-ui/FormBuilder"
|
|
43125
|
-
}
|
|
43126
|
-
) : null,
|
|
43127
|
-
draft.questions.filter((q) => !byKey.has(q.field)).map((q) => /* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("p", { className: "text-xs text-destructive", children: [
|
|
43128
|
-
'This form asks for "',
|
|
43129
|
-
q.field,
|
|
43130
|
-
'" and this table has no such Field. It is shown here rather than quietly dropped.'
|
|
43131
|
-
] }, q.field)),
|
|
43132
|
-
adding ? /* @__PURE__ */ (0, import_jsx_runtime134.jsx)("div", { className: "rounded border p-2", "data-records-form-add-question": "", children: /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43133
|
-
FieldEditor,
|
|
43042
|
+
] })
|
|
43043
|
+
] }) : lookUnread ? null : /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Skeleton, { className: "h-12 w-full" }),
|
|
43044
|
+
lookUnread ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(RefusalLine, { error: lookUnread }) : null,
|
|
43045
|
+
refused && isLookRefusal(refused) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(RefusalLine, { error: refused }) : null
|
|
43046
|
+
] }),
|
|
43047
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43048
|
+
QuestionList,
|
|
43134
43049
|
{
|
|
43135
43050
|
tableId,
|
|
43136
|
-
|
|
43137
|
-
|
|
43138
|
-
|
|
43051
|
+
draft,
|
|
43052
|
+
fields: fields.data ?? [],
|
|
43053
|
+
byKey,
|
|
43054
|
+
adding,
|
|
43055
|
+
onAdding: (on) => {
|
|
43056
|
+
keysBeforeAdd.current = on ? new Set((fields.data ?? []).map((f) => f.key)) : null;
|
|
43057
|
+
setAdding(on);
|
|
43139
43058
|
},
|
|
43140
|
-
|
|
43059
|
+
onAdded: () => setAdding(false),
|
|
43060
|
+
onQuestions: (questions) => patch({ questions }),
|
|
43061
|
+
onQuestion: patchQuestion
|
|
43141
43062
|
}
|
|
43142
|
-
)
|
|
43143
|
-
import_design_system119.Button,
|
|
43144
|
-
{
|
|
43145
|
-
size: "sm",
|
|
43146
|
-
variant: "outline",
|
|
43147
|
-
onClick: () => {
|
|
43148
|
-
keysBeforeAdd.current = new Set((fields.data ?? []).map((f) => f.key));
|
|
43149
|
-
setAdding(true);
|
|
43150
|
-
},
|
|
43151
|
-
children: "Add a question"
|
|
43152
|
-
}
|
|
43153
|
-
) })
|
|
43063
|
+
)
|
|
43154
43064
|
] })
|
|
43155
|
-
] })
|
|
43156
|
-
|
|
43157
|
-
|
|
43158
|
-
|
|
43065
|
+
] }),
|
|
43066
|
+
draft ? (
|
|
43067
|
+
// BESIDE THE EDITOR, ALWAYS IN VIEW (TYPEFORM-2): it follows the scroll and updates as you type.
|
|
43068
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43069
|
+
"aside",
|
|
43070
|
+
{
|
|
43071
|
+
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)]",
|
|
43072
|
+
"data-records-form-preview": "",
|
|
43073
|
+
children: showResults && stored ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormResultsPanel, { form: stored }) : /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormLookFrame, { look: lookOptions?.look ?? null, title: draft.name, contained: true, showOwnerHeader: draft.showOwnerHeader ?? true, children: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormRunner, { form: draft, preview: true }) })
|
|
43074
|
+
}
|
|
43075
|
+
)
|
|
43076
|
+
) : null
|
|
43077
|
+
] }) })
|
|
43078
|
+
);
|
|
43159
43079
|
}
|
|
43160
43080
|
function PublishRow({
|
|
43161
43081
|
url,
|
|
@@ -43170,10 +43090,10 @@ function PublishRow({
|
|
|
43170
43090
|
unsaved = false
|
|
43171
43091
|
}) {
|
|
43172
43092
|
const open = state === "open";
|
|
43173
|
-
return /* @__PURE__ */ (0,
|
|
43174
|
-
/* @__PURE__ */ (0,
|
|
43175
|
-
mayPublish ? /* @__PURE__ */ (0,
|
|
43176
|
-
|
|
43093
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1.5 rounded border px-2.5 py-2", children: [
|
|
43094
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
43095
|
+
mayPublish ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43096
|
+
import_design_system118.Button,
|
|
43177
43097
|
{
|
|
43178
43098
|
size: "sm",
|
|
43179
43099
|
variant: open ? "ghost" : "default",
|
|
@@ -43182,8 +43102,8 @@ function PublishRow({
|
|
|
43182
43102
|
children: busy ? "\u2026" : open ? "Unpublish" : unsaved ? "Save and publish" : "Publish"
|
|
43183
43103
|
}
|
|
43184
43104
|
) : null,
|
|
43185
|
-
state !== "draft" ? /* @__PURE__ */ (0,
|
|
43186
|
-
/* @__PURE__ */ (0,
|
|
43105
|
+
state !== "draft" ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(import_jsx_runtime133.Fragment, { children: [
|
|
43106
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43187
43107
|
"a",
|
|
43188
43108
|
{
|
|
43189
43109
|
href: url,
|
|
@@ -43193,14 +43113,14 @@ function PublishRow({
|
|
|
43193
43113
|
children: url
|
|
43194
43114
|
}
|
|
43195
43115
|
),
|
|
43196
|
-
/* @__PURE__ */ (0,
|
|
43116
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", variant: "outline", onClick: () => onCopy(url), children: copied ? "Copied" : "Copy link" })
|
|
43197
43117
|
] }) : null
|
|
43198
43118
|
] }),
|
|
43199
|
-
/* @__PURE__ */ (0,
|
|
43200
|
-
open && unsaved ? /* @__PURE__ */ (0,
|
|
43201
|
-
!mayPublish ? /* @__PURE__ */ (0,
|
|
43202
|
-
shownUrl ? /* @__PURE__ */ (0,
|
|
43203
|
-
"
|
|
43119
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("p", { className: "w-fit text-xs text-muted-foreground", title: FORM_STATE_WORDS[state], "data-records-form-state": state, children: FORM_STATE_LABELS[state] }),
|
|
43120
|
+
open && unsaved ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("p", { className: "text-xs text-muted-foreground", "data-records-form-unsaved": "", children: "Unsaved changes \u2014 the live form shows your last save" }) : null,
|
|
43121
|
+
!mayPublish ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("p", { className: "w-fit text-xs text-muted-foreground", title: why, children: "Publishing needs table admin" }) : null,
|
|
43122
|
+
shownUrl ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("p", { className: "break-all rounded border border-dashed px-2 py-1 text-xs", title: "This browser would not copy for you.", children: [
|
|
43123
|
+
"Copy by hand: ",
|
|
43204
43124
|
shownUrl
|
|
43205
43125
|
] }) : null
|
|
43206
43126
|
] });
|
|
@@ -43210,7 +43130,7 @@ function Condition({
|
|
|
43210
43130
|
fieldKeys,
|
|
43211
43131
|
onChange
|
|
43212
43132
|
}) {
|
|
43213
|
-
return /* @__PURE__ */ (0,
|
|
43133
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43214
43134
|
ConditionGroup,
|
|
43215
43135
|
{
|
|
43216
43136
|
lead: "Ask only when",
|
|
@@ -43221,26 +43141,326 @@ function Condition({
|
|
|
43221
43141
|
}
|
|
43222
43142
|
);
|
|
43223
43143
|
}
|
|
43144
|
+
var FORM_STATE_LABELS = {
|
|
43145
|
+
draft: "Not published",
|
|
43146
|
+
open: "Open",
|
|
43147
|
+
closed: "Closed",
|
|
43148
|
+
full: "Full"
|
|
43149
|
+
};
|
|
43150
|
+
function QuestionList({
|
|
43151
|
+
tableId,
|
|
43152
|
+
draft,
|
|
43153
|
+
fields,
|
|
43154
|
+
byKey,
|
|
43155
|
+
adding,
|
|
43156
|
+
onAdding,
|
|
43157
|
+
onAdded,
|
|
43158
|
+
onQuestions,
|
|
43159
|
+
onQuestion
|
|
43160
|
+
}) {
|
|
43161
|
+
const sensors = (0, import_core22.useSensors)(
|
|
43162
|
+
(0, import_core22.useSensor)(import_core22.PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
43163
|
+
(0, import_core22.useSensor)(import_core22.KeyboardSensor, { coordinateGetter: import_sortable.sortableKeyboardCoordinates })
|
|
43164
|
+
);
|
|
43165
|
+
const askable = askableFields([...fields]);
|
|
43166
|
+
const unasked = askable.filter((f) => !draft.questions.some((q) => q.field === f.key));
|
|
43167
|
+
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 }));
|
|
43168
|
+
const label = (key) => {
|
|
43169
|
+
const f = byKey.get(key);
|
|
43170
|
+
return draft.questions.find((q) => q.field === key)?.ask || (f ? fieldName(f) : key);
|
|
43171
|
+
};
|
|
43172
|
+
function dragEnd(event) {
|
|
43173
|
+
const from = draft.questions.findIndex((q) => q.field === event.active.id);
|
|
43174
|
+
const to = event.over ? draft.questions.findIndex((q) => q.field === event.over.id) : -1;
|
|
43175
|
+
if (from < 0 || to < 0 || from === to) return;
|
|
43176
|
+
onQuestions((0, import_sortable.arrayMove)(draft.questions, from, to));
|
|
43177
|
+
}
|
|
43178
|
+
const notAskable = whyNotAskable([...fields]);
|
|
43179
|
+
const leftUnasked = requiredLeftUnasked([...fields], draft.questions.map((q) => q.field));
|
|
43180
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1.5", "data-records-form-questions": "", children: [
|
|
43181
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
43182
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "font-medium text-foreground", children: "Questions" }),
|
|
43183
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "tabular-nums", children: draft.questions.length })
|
|
43184
|
+
] }),
|
|
43185
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_core22.DndContext, { sensors, collisionDetection: import_core22.closestCenter, onDragEnd: dragEnd, children: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_sortable.SortableContext, { items: draft.questions.map((q) => q.field), strategy: import_sortable.verticalListSortingStrategy, children: draft.questions.map((question, index) => /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43186
|
+
QuestionRow,
|
|
43187
|
+
{
|
|
43188
|
+
index,
|
|
43189
|
+
question,
|
|
43190
|
+
field: byKey.get(question.field) ?? null,
|
|
43191
|
+
draft,
|
|
43192
|
+
conditionFields: asked3.filter((f) => f.key !== question.field),
|
|
43193
|
+
label,
|
|
43194
|
+
onChange: (change) => onQuestion(index, change),
|
|
43195
|
+
onRemove: () => onQuestions(draft.questions.filter((q) => q.field !== question.field))
|
|
43196
|
+
},
|
|
43197
|
+
question.field
|
|
43198
|
+
)) }) }),
|
|
43199
|
+
unasked.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("div", { className: "flex flex-wrap items-center gap-1", "data-records-form-unasked": "", children: unasked.map((f) => /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(
|
|
43200
|
+
import_design_system118.Button,
|
|
43201
|
+
{
|
|
43202
|
+
size: "sm",
|
|
43203
|
+
variant: "outline",
|
|
43204
|
+
"aria-label": `Ask for ${fieldName(f)}`,
|
|
43205
|
+
onClick: () => onQuestions([...draft.questions, { field: f.key }]),
|
|
43206
|
+
children: [
|
|
43207
|
+
"+ ",
|
|
43208
|
+
fieldName(f)
|
|
43209
|
+
]
|
|
43210
|
+
},
|
|
43211
|
+
f.id
|
|
43212
|
+
)) }) : null,
|
|
43213
|
+
notAskable ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("p", { className: "w-fit text-xs text-muted-foreground", title: notAskable, children: "Some fields cannot be asked publicly" }) : null,
|
|
43214
|
+
leftUnasked ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43215
|
+
import_design_system117.ErrorBox,
|
|
43216
|
+
{
|
|
43217
|
+
className: "rounded border border-destructive/40 bg-destructive/5 px-2 py-1.5 text-xs text-destructive",
|
|
43218
|
+
message: leftUnasked,
|
|
43219
|
+
operation: "Build a form",
|
|
43220
|
+
origin: "records-ui/FormBuilder"
|
|
43221
|
+
}
|
|
43222
|
+
) : null,
|
|
43223
|
+
adding ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("div", { className: "rounded border p-2", "data-records-form-add-question": "", children: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FieldEditor, { tableId, onCancel: () => onAdding(false), onSaved: onAdded }) }) : /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", variant: "outline", onClick: () => onAdding(true), children: "Add a question" }) })
|
|
43224
|
+
] });
|
|
43225
|
+
}
|
|
43226
|
+
function QuestionRow({
|
|
43227
|
+
index,
|
|
43228
|
+
question,
|
|
43229
|
+
field: field2,
|
|
43230
|
+
draft,
|
|
43231
|
+
conditionFields,
|
|
43232
|
+
label,
|
|
43233
|
+
onChange,
|
|
43234
|
+
onRemove
|
|
43235
|
+
}) {
|
|
43236
|
+
const sortable = (0, import_sortable.useSortable)({ id: question.field });
|
|
43237
|
+
const [open, setOpen] = (0, import_react232.useState)(true);
|
|
43238
|
+
const name = field2 ? fieldName(field2) : question.field;
|
|
43239
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(
|
|
43240
|
+
"div",
|
|
43241
|
+
{
|
|
43242
|
+
ref: sortable.setNodeRef,
|
|
43243
|
+
style: { transform: import_utilities.CSS.Transform.toString(sortable.transform), transition: sortable.transition },
|
|
43244
|
+
className: (0, import_design_system118.cn)("rounded border bg-background px-2 py-1.5", sortable.isDragging && "z-10 shadow-md"),
|
|
43245
|
+
"data-records-form-question-row": question.field,
|
|
43246
|
+
children: [
|
|
43247
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
43248
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43249
|
+
"button",
|
|
43250
|
+
{
|
|
43251
|
+
type: "button",
|
|
43252
|
+
className: "cursor-grab touch-none rounded px-1 text-muted-foreground hover:bg-muted active:cursor-grabbing",
|
|
43253
|
+
"aria-label": `Move ${name}`,
|
|
43254
|
+
title: "Drag to reorder",
|
|
43255
|
+
"data-records-form-drag": "",
|
|
43256
|
+
...sortable.attributes,
|
|
43257
|
+
...sortable.listeners,
|
|
43258
|
+
children: "\u22EE\u22EE"
|
|
43259
|
+
}
|
|
43260
|
+
),
|
|
43261
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "w-5 text-right text-xs tabular-nums text-muted-foreground", children: index + 1 }),
|
|
43262
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("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 }),
|
|
43263
|
+
field2?.required || question.required ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs text-foreground/80", children: "required" }) : null,
|
|
43264
|
+
!field2 ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "rounded bg-destructive px-1.5 text-[11px] text-destructive-foreground", title: "The table has no such field.", children: "Missing field" }) : null,
|
|
43265
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", variant: "ghost", onClick: onRemove, "aria-label": `Stop asking ${name}`, children: "Remove" })
|
|
43266
|
+
] }),
|
|
43267
|
+
open && field2 ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "mt-1.5 grid grid-cols-2 gap-2 pl-7", children: [
|
|
43268
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43269
|
+
import_design_system118.BasicInput,
|
|
43270
|
+
{
|
|
43271
|
+
className: "h-8 text-xs",
|
|
43272
|
+
placeholder: name,
|
|
43273
|
+
"aria-label": `How to ask for ${name}`,
|
|
43274
|
+
title: "Use {{question}} to recall an earlier answer",
|
|
43275
|
+
value: question.ask ?? "",
|
|
43276
|
+
onChange: (e) => onChange({ ask: e.target.value || null })
|
|
43277
|
+
}
|
|
43278
|
+
),
|
|
43279
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43280
|
+
import_design_system118.BasicInput,
|
|
43281
|
+
{
|
|
43282
|
+
className: "h-8 text-xs",
|
|
43283
|
+
placeholder: "Help text",
|
|
43284
|
+
"aria-label": `Help text for ${name}`,
|
|
43285
|
+
value: question.help ?? "",
|
|
43286
|
+
onChange: (e) => onChange({ help: e.target.value || null })
|
|
43287
|
+
}
|
|
43288
|
+
),
|
|
43289
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "col-span-2 flex items-center gap-2 text-xs", children: [
|
|
43290
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43291
|
+
import_design_system118.Checkbox,
|
|
43292
|
+
{
|
|
43293
|
+
checked: field2.required ? true : question.required === true,
|
|
43294
|
+
disabled: field2.required === true,
|
|
43295
|
+
"aria-label": `${name} is required`,
|
|
43296
|
+
onCheckedChange: (checked) => onChange({ required: checked === true ? true : null })
|
|
43297
|
+
}
|
|
43298
|
+
),
|
|
43299
|
+
"Required"
|
|
43300
|
+
] }),
|
|
43301
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(Condition, { question, fieldKeys: conditionFields, onChange: (showIf) => onChange({ showIf }) }),
|
|
43302
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43303
|
+
QuestionLogic,
|
|
43304
|
+
{
|
|
43305
|
+
question,
|
|
43306
|
+
index,
|
|
43307
|
+
questions: draft.questions,
|
|
43308
|
+
endings: draft.endings ?? [],
|
|
43309
|
+
field: field2,
|
|
43310
|
+
fieldKeys: conditionFields,
|
|
43311
|
+
label,
|
|
43312
|
+
onChange
|
|
43313
|
+
}
|
|
43314
|
+
)
|
|
43315
|
+
] }) : null
|
|
43316
|
+
]
|
|
43317
|
+
}
|
|
43318
|
+
);
|
|
43319
|
+
}
|
|
43320
|
+
function NotifyOnAnswer({
|
|
43321
|
+
tableId,
|
|
43322
|
+
tableName: tableName2,
|
|
43323
|
+
formName,
|
|
43324
|
+
ruleId,
|
|
43325
|
+
notify,
|
|
43326
|
+
onRule,
|
|
43327
|
+
onNotify
|
|
43328
|
+
}) {
|
|
43329
|
+
const client = (0, import_react233.useRecordsClient)();
|
|
43330
|
+
const [subs, setSubs] = (0, import_react232.useState)(null);
|
|
43331
|
+
const [busy, setBusy] = (0, import_react232.useState)(false);
|
|
43332
|
+
const [error, setError] = (0, import_react232.useState)(null);
|
|
43333
|
+
const [said2, setSaid] = (0, import_react232.useState)(null);
|
|
43334
|
+
const [url, setUrl] = (0, import_react232.useState)(notify?.webhookUrl ?? "");
|
|
43335
|
+
(0, import_react232.useEffect)(() => setUrl(notify?.webhookUrl ?? ""), [notify?.webhookUrl]);
|
|
43336
|
+
(0, import_react232.useEffect)(() => {
|
|
43337
|
+
let cancelled = false;
|
|
43338
|
+
void client.subscriptions({ table_id: tableId }).then((r) => {
|
|
43339
|
+
if (!cancelled && r.ok) setSubs(r.data);
|
|
43340
|
+
});
|
|
43341
|
+
return () => {
|
|
43342
|
+
cancelled = true;
|
|
43343
|
+
};
|
|
43344
|
+
}, [client, tableId, ruleId]);
|
|
43345
|
+
const chosen = subs?.find((s) => s.rule_id === ruleId) ?? null;
|
|
43346
|
+
async function emailMe(on) {
|
|
43347
|
+
setBusy(true);
|
|
43348
|
+
setError(null);
|
|
43349
|
+
try {
|
|
43350
|
+
if (!on) {
|
|
43351
|
+
onRule(null);
|
|
43352
|
+
if (ruleId) await client.recordDelete({ record_id: ruleId });
|
|
43353
|
+
return;
|
|
43354
|
+
}
|
|
43355
|
+
const listed = await client.views({ table_id: tableId });
|
|
43356
|
+
if (!listed.ok) return setError(listed.error);
|
|
43357
|
+
let viewId = listed.data.find((v) => !v.filters || Object.keys(v.filters).length === 0)?.view_id ?? null;
|
|
43358
|
+
if (!viewId) {
|
|
43359
|
+
const made = await client.viewDeclare({ table_id: tableId, spec: { name: `Every ${tableName2}`, filters: null } });
|
|
43360
|
+
if (!made.ok) return setError(made.error);
|
|
43361
|
+
viewId = made.data;
|
|
43362
|
+
}
|
|
43363
|
+
const declared2 = await client.subscriptionDeclare({
|
|
43364
|
+
table_id: tableId,
|
|
43365
|
+
spec: {
|
|
43366
|
+
name: `${formName} \u2014 new answers`,
|
|
43367
|
+
saved_view_id: viewId,
|
|
43368
|
+
cadence: "instant",
|
|
43369
|
+
schedule: null,
|
|
43370
|
+
channel: "email",
|
|
43371
|
+
quiet_hours: null,
|
|
43372
|
+
recipient_user_id: client.config.actor.user_id ?? null,
|
|
43373
|
+
event_key: "records.changed"
|
|
43374
|
+
}
|
|
43375
|
+
});
|
|
43376
|
+
if (!declared2.ok) return setError(declared2.error);
|
|
43377
|
+
onRule(declared2.data);
|
|
43378
|
+
} finally {
|
|
43379
|
+
setBusy(false);
|
|
43380
|
+
}
|
|
43381
|
+
}
|
|
43382
|
+
async function saveWebhook() {
|
|
43383
|
+
const next = url.trim();
|
|
43384
|
+
if (next === (notify?.webhookUrl ?? "")) return;
|
|
43385
|
+
setBusy(true);
|
|
43386
|
+
setError(null);
|
|
43387
|
+
setSaid(null);
|
|
43388
|
+
try {
|
|
43389
|
+
if (next === "") {
|
|
43390
|
+
if (notify?.webhookAutomationId) await client.automationArchive({ automation_id: notify.webhookAutomationId });
|
|
43391
|
+
onNotify(null);
|
|
43392
|
+
return;
|
|
43393
|
+
}
|
|
43394
|
+
const declared2 = await client.automationDeclare({
|
|
43395
|
+
table_id: tableId,
|
|
43396
|
+
automation_id: notify?.webhookAutomationId ?? null,
|
|
43397
|
+
spec: { name: `${formName} \u2014 webhook`, trigger: { on: "form_answered" }, actions: [{ do: "webhook", url: next }] }
|
|
43398
|
+
});
|
|
43399
|
+
if (!declared2.ok) return setError(declared2.error);
|
|
43400
|
+
if (!declared2.data.ok) {
|
|
43401
|
+
setSaid(Object.values(declared2.data._errors)[0] ?? "Not saved");
|
|
43402
|
+
return;
|
|
43403
|
+
}
|
|
43404
|
+
onNotify({ webhookUrl: next, webhookAutomationId: declared2.data.automation_id });
|
|
43405
|
+
} finally {
|
|
43406
|
+
setBusy(false);
|
|
43407
|
+
}
|
|
43408
|
+
}
|
|
43409
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("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: [
|
|
43410
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "font-medium", children: "Notify on answer" }),
|
|
43411
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1", children: /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex items-center gap-2", title: "An email to you each time somebody answers.", children: [
|
|
43412
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Checkbox, { checked: Boolean(ruleId), disabled: busy, onCheckedChange: (c) => void emailMe(c === true), "aria-label": "Email me" }),
|
|
43413
|
+
"Email me",
|
|
43414
|
+
chosen && chosen.channel !== "email" ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
43415
|
+
"(",
|
|
43416
|
+
chosen.channel === "sms" ? "text" : "in app",
|
|
43417
|
+
")"
|
|
43418
|
+
] }) : null
|
|
43419
|
+
] }) }),
|
|
43420
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex items-center gap-2", children: [
|
|
43421
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "shrink-0", children: "Webhook" }),
|
|
43422
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43423
|
+
import_design_system118.BasicInput,
|
|
43424
|
+
{
|
|
43425
|
+
type: "url",
|
|
43426
|
+
inputMode: "url",
|
|
43427
|
+
className: "h-8 text-xs",
|
|
43428
|
+
placeholder: "https://",
|
|
43429
|
+
"aria-label": "Webhook URL",
|
|
43430
|
+
value: url,
|
|
43431
|
+
disabled: busy,
|
|
43432
|
+
onChange: (e) => setUrl(e.target.value),
|
|
43433
|
+
onBlur: () => void saveWebhook(),
|
|
43434
|
+
onKeyDown: (e) => {
|
|
43435
|
+
if (e.key === "Enter") void saveWebhook();
|
|
43436
|
+
}
|
|
43437
|
+
}
|
|
43438
|
+
)
|
|
43439
|
+
] }),
|
|
43440
|
+
said2 ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("p", { className: "text-destructive", children: said2 }) : null,
|
|
43441
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(RefusalLine, { error }) : null
|
|
43442
|
+
] });
|
|
43443
|
+
}
|
|
43224
43444
|
|
|
43225
43445
|
// src/DocTemplate.tsx
|
|
43226
|
-
var
|
|
43227
|
-
var
|
|
43228
|
-
var
|
|
43229
|
-
var
|
|
43446
|
+
var import_react234 = require("react");
|
|
43447
|
+
var import_react235 = require("@ai-matrx/records/react");
|
|
43448
|
+
var import_design_system119 = require("@ai-matrx/design-system");
|
|
43449
|
+
var import_jsx_runtime134 = require("react/jsx-runtime");
|
|
43230
43450
|
function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, className }) {
|
|
43231
|
-
const client = (0,
|
|
43451
|
+
const client = (0, import_react235.useRecordsClient)();
|
|
43232
43452
|
const claimSeed = useSeedGuard();
|
|
43233
|
-
const table = (0,
|
|
43453
|
+
const table = (0, import_react235.useTable)(tableId);
|
|
43234
43454
|
const rights = useTableRights(table.data);
|
|
43235
|
-
const fields = (0,
|
|
43236
|
-
const [templates, setTemplates] = (0,
|
|
43237
|
-
const [error, setError] = (0,
|
|
43238
|
-
const [activeId, setActiveId] = (0,
|
|
43239
|
-
const [draftName, setDraftName] = (0,
|
|
43240
|
-
const [draftBody, setDraftBody] = (0,
|
|
43241
|
-
const [unresolved, setUnresolved] = (0,
|
|
43242
|
-
const [saving, setSaving] = (0,
|
|
43243
|
-
const load = (0,
|
|
43455
|
+
const fields = (0, import_react235.useFields)(tableId);
|
|
43456
|
+
const [templates, setTemplates] = (0, import_react234.useState)(null);
|
|
43457
|
+
const [error, setError] = (0, import_react234.useState)(null);
|
|
43458
|
+
const [activeId, setActiveId] = (0, import_react234.useState)(activeTemplateId ?? null);
|
|
43459
|
+
const [draftName, setDraftName] = (0, import_react234.useState)("");
|
|
43460
|
+
const [draftBody, setDraftBody] = (0, import_react234.useState)("");
|
|
43461
|
+
const [unresolved, setUnresolved] = (0, import_react234.useState)([]);
|
|
43462
|
+
const [saving, setSaving] = (0, import_react234.useState)(false);
|
|
43463
|
+
const load = (0, import_react234.useCallback)(async () => {
|
|
43244
43464
|
const held = await client.docTemplates({ table_id: tableId });
|
|
43245
43465
|
if (!held.ok) {
|
|
43246
43466
|
setError(held.error);
|
|
@@ -43271,10 +43491,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43271
43491
|
setError(null);
|
|
43272
43492
|
setTemplates(rows);
|
|
43273
43493
|
}, [client, tableId, seed, fields.data]);
|
|
43274
|
-
(0,
|
|
43494
|
+
(0, import_react234.useEffect)(() => {
|
|
43275
43495
|
void load();
|
|
43276
43496
|
}, [load]);
|
|
43277
|
-
(0,
|
|
43497
|
+
(0, import_react234.useEffect)(() => {
|
|
43278
43498
|
if (!templates || templates.length === 0) return;
|
|
43279
43499
|
const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
|
|
43280
43500
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -43282,7 +43502,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43282
43502
|
setDraftBody(chosen.body);
|
|
43283
43503
|
onActiveTemplate?.(chosen);
|
|
43284
43504
|
}, [templates, activeTemplateId]);
|
|
43285
|
-
(0,
|
|
43505
|
+
(0, import_react234.useEffect)(() => {
|
|
43286
43506
|
let cancelled = false;
|
|
43287
43507
|
if (draftBody.trim() === "") {
|
|
43288
43508
|
setUnresolved([]);
|
|
@@ -43312,25 +43532,25 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43312
43532
|
setActiveId(saved.data);
|
|
43313
43533
|
await load();
|
|
43314
43534
|
}
|
|
43315
|
-
if (fields.error) return /* @__PURE__ */ (0,
|
|
43535
|
+
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(RefusalNotice, { error: fields.error, className });
|
|
43316
43536
|
if (error) {
|
|
43317
|
-
return /* @__PURE__ */ (0,
|
|
43537
|
+
return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43318
43538
|
RefusalNotice,
|
|
43319
43539
|
{
|
|
43320
43540
|
error,
|
|
43321
43541
|
className,
|
|
43322
|
-
actions: /* @__PURE__ */ (0,
|
|
43542
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43323
43543
|
}
|
|
43324
43544
|
);
|
|
43325
43545
|
}
|
|
43326
|
-
if (templates === null || fields.loading && !fields.data) return /* @__PURE__ */ (0,
|
|
43546
|
+
if (templates === null || fields.loading && !fields.data) return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Skeleton, { className: (0, import_design_system119.cn)("h-64 w-full", className) });
|
|
43327
43547
|
if (!rights.admin) {
|
|
43328
|
-
return /* @__PURE__ */ (0,
|
|
43548
|
+
return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)("p", { className: (0, import_design_system119.cn)("text-xs text-muted-foreground", className), children: rights.why("structure") });
|
|
43329
43549
|
}
|
|
43330
|
-
return /* @__PURE__ */ (0,
|
|
43331
|
-
/* @__PURE__ */ (0,
|
|
43332
|
-
templates.map((t) => /* @__PURE__ */ (0,
|
|
43333
|
-
|
|
43550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("div", { className: (0, import_design_system119.cn)("flex flex-col gap-2", className), children: [
|
|
43551
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Templates", children: [
|
|
43552
|
+
templates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime134.jsxs)(
|
|
43553
|
+
import_design_system119.Button,
|
|
43334
43554
|
{
|
|
43335
43555
|
size: "sm",
|
|
43336
43556
|
variant: t.id === activeId ? "secondary" : "ghost",
|
|
@@ -43342,7 +43562,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43342
43562
|
},
|
|
43343
43563
|
children: [
|
|
43344
43564
|
t.name,
|
|
43345
|
-
/* @__PURE__ */ (0,
|
|
43565
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("span", { className: "ml-1 text-[10px] opacity-70", children: [
|
|
43346
43566
|
"v",
|
|
43347
43567
|
t.template_version
|
|
43348
43568
|
] })
|
|
@@ -43350,8 +43570,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43350
43570
|
},
|
|
43351
43571
|
t.id
|
|
43352
43572
|
)),
|
|
43353
|
-
/* @__PURE__ */ (0,
|
|
43354
|
-
|
|
43573
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43574
|
+
import_design_system119.Button,
|
|
43355
43575
|
{
|
|
43356
43576
|
size: "sm",
|
|
43357
43577
|
variant: "ghost",
|
|
@@ -43363,16 +43583,16 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43363
43583
|
children: "New template"
|
|
43364
43584
|
}
|
|
43365
43585
|
),
|
|
43366
|
-
/* @__PURE__ */ (0,
|
|
43586
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Button, { size: "sm", className: "ml-auto", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
43367
43587
|
] }),
|
|
43368
|
-
/* @__PURE__ */ (0,
|
|
43369
|
-
/* @__PURE__ */ (0,
|
|
43370
|
-
/* @__PURE__ */ (0,
|
|
43588
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
43589
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Label, { className: "text-xs font-medium", children: "Name" }),
|
|
43590
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.BasicInput, { value: draftName, onChange: (e) => setDraftName(e.target.value), placeholder: "Agreement" })
|
|
43371
43591
|
] }),
|
|
43372
|
-
/* @__PURE__ */ (0,
|
|
43373
|
-
/* @__PURE__ */ (0,
|
|
43374
|
-
(fields.data ?? []).map((field2) => /* @__PURE__ */ (0,
|
|
43375
|
-
|
|
43592
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
43593
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)("span", { className: "text-xs text-muted-foreground", children: "Insert" }),
|
|
43594
|
+
(fields.data ?? []).map((field2) => /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43595
|
+
import_design_system119.Button,
|
|
43376
43596
|
{
|
|
43377
43597
|
size: "sm",
|
|
43378
43598
|
variant: "ghost",
|
|
@@ -43383,8 +43603,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43383
43603
|
field2.id
|
|
43384
43604
|
))
|
|
43385
43605
|
] }),
|
|
43386
|
-
/* @__PURE__ */ (0,
|
|
43387
|
-
|
|
43606
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43607
|
+
import_design_system119.BasicTextarea,
|
|
43388
43608
|
{
|
|
43389
43609
|
rows: 10,
|
|
43390
43610
|
"aria-label": "Template body",
|
|
@@ -43393,33 +43613,33 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43393
43613
|
onChange: (e) => setDraftBody(e.target.value)
|
|
43394
43614
|
}
|
|
43395
43615
|
),
|
|
43396
|
-
unresolved.length > 0 ? /* @__PURE__ */ (0,
|
|
43397
|
-
/* @__PURE__ */ (0,
|
|
43398
|
-
/* @__PURE__ */ (0,
|
|
43399
|
-
/* @__PURE__ */ (0,
|
|
43616
|
+
unresolved.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime134.jsx)("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("li", { className: "flex items-baseline gap-1", children: [
|
|
43617
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)("code", { className: "text-destructive", children: token.raw }),
|
|
43618
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)("span", { "aria-hidden": "true", children: "\u2014" }),
|
|
43619
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43400
43620
|
RefusalLine,
|
|
43401
43621
|
{
|
|
43402
43622
|
error: refusal("invalid_argument", token.why, "Change it to a Field this table has, or take it out.")
|
|
43403
43623
|
}
|
|
43404
43624
|
)
|
|
43405
|
-
] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */ (0,
|
|
43625
|
+
] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */ (0, import_jsx_runtime134.jsx)("p", { className: "text-xs text-muted-foreground", children: "Every token in this body points at a Field this table can answer." }) : null
|
|
43406
43626
|
] });
|
|
43407
43627
|
}
|
|
43408
43628
|
|
|
43409
43629
|
// src/DocRender.tsx
|
|
43410
|
-
var
|
|
43411
|
-
var
|
|
43412
|
-
var
|
|
43413
|
-
var
|
|
43630
|
+
var import_react236 = require("react");
|
|
43631
|
+
var import_react237 = require("@ai-matrx/records/react");
|
|
43632
|
+
var import_design_system120 = require("@ai-matrx/design-system");
|
|
43633
|
+
var import_jsx_runtime135 = require("react/jsx-runtime");
|
|
43414
43634
|
function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
43415
|
-
const client = (0,
|
|
43416
|
-
const [preview, setPreview] = (0,
|
|
43417
|
-
const [renders, setRenders] = (0,
|
|
43418
|
-
const [showing, setShowing] = (0,
|
|
43419
|
-
const [error, setError] = (0,
|
|
43420
|
-
const [busy, setBusy] = (0,
|
|
43421
|
-
const paper = (0,
|
|
43422
|
-
const load = (0,
|
|
43635
|
+
const client = (0, import_react237.useRecordsClient)();
|
|
43636
|
+
const [preview, setPreview] = (0, import_react236.useState)(null);
|
|
43637
|
+
const [renders, setRenders] = (0, import_react236.useState)(null);
|
|
43638
|
+
const [showing, setShowing] = (0, import_react236.useState)(null);
|
|
43639
|
+
const [error, setError] = (0, import_react236.useState)(null);
|
|
43640
|
+
const [busy, setBusy] = (0, import_react236.useState)(null);
|
|
43641
|
+
const paper = (0, import_react236.useRef)(null);
|
|
43642
|
+
const load = (0, import_react236.useCallback)(async () => {
|
|
43423
43643
|
const [body, held] = await Promise.all([
|
|
43424
43644
|
client.docRenderBody({ template_id: templateId, record_id: recordId }),
|
|
43425
43645
|
client.docRenders({ record_id: recordId })
|
|
@@ -43436,7 +43656,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43436
43656
|
setPreview(body.data);
|
|
43437
43657
|
setRenders(held.data.filter((r) => r.template_id === templateId));
|
|
43438
43658
|
}, [client, templateId, recordId]);
|
|
43439
|
-
(0,
|
|
43659
|
+
(0, import_react236.useEffect)(() => {
|
|
43440
43660
|
void load();
|
|
43441
43661
|
}, [load]);
|
|
43442
43662
|
async function freeze() {
|
|
@@ -43476,22 +43696,22 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43476
43696
|
}
|
|
43477
43697
|
}
|
|
43478
43698
|
if (error) {
|
|
43479
|
-
return /* @__PURE__ */ (0,
|
|
43699
|
+
return /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(
|
|
43480
43700
|
RefusalNotice,
|
|
43481
43701
|
{
|
|
43482
43702
|
error,
|
|
43483
43703
|
className,
|
|
43484
|
-
actions: /* @__PURE__ */ (0,
|
|
43704
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(import_design_system120.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43485
43705
|
}
|
|
43486
43706
|
);
|
|
43487
43707
|
}
|
|
43488
|
-
if (preview === null || renders === null) return /* @__PURE__ */ (0,
|
|
43708
|
+
if (preview === null || renders === null) return /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(import_design_system120.Skeleton, { className: (0, import_design_system120.cn)("h-64 w-full", className) });
|
|
43489
43709
|
const frozen = showing ? renders.find((r) => r.id === showing) ?? null : null;
|
|
43490
43710
|
const text2 = frozen ? frozen.body : preview;
|
|
43491
|
-
return /* @__PURE__ */ (0,
|
|
43492
|
-
/* @__PURE__ */ (0,
|
|
43493
|
-
/* @__PURE__ */ (0,
|
|
43494
|
-
|
|
43711
|
+
return /* @__PURE__ */ (0, import_jsx_runtime135.jsxs)("div", { className: (0, import_design_system120.cn)("flex flex-col gap-2", className), children: [
|
|
43712
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsxs)("div", { className: "flex items-center gap-1 text-xs", children: [
|
|
43713
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsx)(
|
|
43714
|
+
import_design_system120.Button,
|
|
43495
43715
|
{
|
|
43496
43716
|
size: "sm",
|
|
43497
43717
|
variant: frozen ? "ghost" : "secondary",
|
|
@@ -43500,8 +43720,8 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43500
43720
|
children: "As it reads now"
|
|
43501
43721
|
}
|
|
43502
43722
|
),
|
|
43503
|
-
renders.map((render) => /* @__PURE__ */ (0,
|
|
43504
|
-
|
|
43723
|
+
renders.map((render) => /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(
|
|
43724
|
+
import_design_system120.Button,
|
|
43505
43725
|
{
|
|
43506
43726
|
size: "sm",
|
|
43507
43727
|
variant: render.id === showing ? "secondary" : "ghost",
|
|
@@ -43512,33 +43732,33 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43512
43732
|
},
|
|
43513
43733
|
render.id
|
|
43514
43734
|
)),
|
|
43515
|
-
/* @__PURE__ */ (0,
|
|
43516
|
-
/* @__PURE__ */ (0,
|
|
43517
|
-
/* @__PURE__ */ (0,
|
|
43735
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsxs)("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
43736
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsx)(import_design_system120.Button, { size: "sm", disabled: busy !== null, onClick: () => void freeze(), children: busy === "freeze" ? "Freezing\u2026" : "Freeze this version" }),
|
|
43737
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsx)(import_design_system120.Button, { size: "sm", variant: "ghost", disabled: busy !== null, onClick: () => void toPdf(), children: busy === "pdf" ? "Making the PDF\u2026" : "PDF" })
|
|
43518
43738
|
] })
|
|
43519
43739
|
] }),
|
|
43520
|
-
/* @__PURE__ */ (0,
|
|
43521
|
-
/* @__PURE__ */ (0,
|
|
43740
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsx)("div", { ref: paper, className: "whitespace-pre-wrap rounded border bg-background p-4 text-sm text-foreground", children: text2 }),
|
|
43741
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsx)("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." })
|
|
43522
43742
|
] });
|
|
43523
43743
|
}
|
|
43524
43744
|
|
|
43525
43745
|
// src/SignBlock.tsx
|
|
43526
|
-
var
|
|
43527
|
-
var
|
|
43528
|
-
var
|
|
43529
|
-
var
|
|
43530
|
-
var
|
|
43746
|
+
var import_react238 = require("react");
|
|
43747
|
+
var import_react239 = require("@ai-matrx/records/react");
|
|
43748
|
+
var import_design_system121 = require("@ai-matrx/design-system");
|
|
43749
|
+
var import_react240 = require("@ai-matrx/records/react");
|
|
43750
|
+
var import_jsx_runtime136 = require("react/jsx-runtime");
|
|
43531
43751
|
function SignBlock({ tableId, recordId, render, className }) {
|
|
43532
|
-
const client = (0,
|
|
43533
|
-
const table = (0,
|
|
43752
|
+
const client = (0, import_react239.useRecordsClient)();
|
|
43753
|
+
const table = (0, import_react240.useTable)(tableId);
|
|
43534
43754
|
const rights = useTableRights(table.data);
|
|
43535
|
-
const fields = (0,
|
|
43536
|
-
const [signatures, setSignatures] = (0,
|
|
43537
|
-
const [verdicts, setVerdicts] = (0,
|
|
43538
|
-
const [error, setError] = (0,
|
|
43539
|
-
const [name, setName] = (0,
|
|
43540
|
-
const [busy, setBusy] = (0,
|
|
43541
|
-
const load = (0,
|
|
43755
|
+
const fields = (0, import_react239.useFields)(tableId);
|
|
43756
|
+
const [signatures, setSignatures] = (0, import_react238.useState)(null);
|
|
43757
|
+
const [verdicts, setVerdicts] = (0, import_react238.useState)({});
|
|
43758
|
+
const [error, setError] = (0, import_react238.useState)(null);
|
|
43759
|
+
const [name, setName] = (0, import_react238.useState)("");
|
|
43760
|
+
const [busy, setBusy] = (0, import_react238.useState)(false);
|
|
43761
|
+
const load = (0, import_react238.useCallback)(async () => {
|
|
43542
43762
|
const held = await client.docSignatures({ record_id: recordId });
|
|
43543
43763
|
if (!held.ok) {
|
|
43544
43764
|
setError(held.error);
|
|
@@ -43553,7 +43773,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43553
43773
|
}
|
|
43554
43774
|
setVerdicts(answers);
|
|
43555
43775
|
}, [client, recordId]);
|
|
43556
|
-
(0,
|
|
43776
|
+
(0, import_react238.useEffect)(() => {
|
|
43557
43777
|
void load();
|
|
43558
43778
|
}, [load]);
|
|
43559
43779
|
async function sign(field2) {
|
|
@@ -43573,36 +43793,36 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43573
43793
|
setName("");
|
|
43574
43794
|
await load();
|
|
43575
43795
|
}
|
|
43576
|
-
if (fields.error) return /* @__PURE__ */ (0,
|
|
43796
|
+
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(RefusalNotice, { error: fields.error, className });
|
|
43577
43797
|
if (error) {
|
|
43578
|
-
return /* @__PURE__ */ (0,
|
|
43798
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(
|
|
43579
43799
|
RefusalNotice,
|
|
43580
43800
|
{
|
|
43581
43801
|
error,
|
|
43582
43802
|
className,
|
|
43583
|
-
actions: /* @__PURE__ */ (0,
|
|
43803
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(import_design_system121.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43584
43804
|
}
|
|
43585
43805
|
);
|
|
43586
43806
|
}
|
|
43587
|
-
if (fields.loading || signatures === null) return /* @__PURE__ */ (0,
|
|
43807
|
+
if (fields.loading || signatures === null) return /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(import_design_system121.Skeleton, { className: (0, import_design_system121.cn)("h-32 w-full", className) });
|
|
43588
43808
|
const signable = (fields.data ?? []).filter(isSignatureField);
|
|
43589
|
-
return /* @__PURE__ */ (0,
|
|
43590
|
-
/* @__PURE__ */ (0,
|
|
43591
|
-
/* @__PURE__ */ (0,
|
|
43592
|
-
/* @__PURE__ */ (0,
|
|
43809
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("div", { className: (0, import_design_system121.cn)("flex flex-col gap-2", className), children: [
|
|
43810
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
43811
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("span", { className: "font-medium text-foreground", children: "Signatures" }),
|
|
43812
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("span", { children: [
|
|
43593
43813
|
signatures.length,
|
|
43594
43814
|
" on this record"
|
|
43595
43815
|
] })
|
|
43596
43816
|
] }),
|
|
43597
|
-
signatures.length > 0 ? /* @__PURE__ */ (0,
|
|
43817
|
+
signatures.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime136.jsx)("ul", { className: "flex flex-col gap-1", children: signatures.map((signature) => {
|
|
43598
43818
|
const verdict = verdicts[signature.id];
|
|
43599
43819
|
const intact = typeof verdict === "object" && verdict !== null ? verdict.intact : void 0;
|
|
43600
|
-
return /* @__PURE__ */ (0,
|
|
43601
|
-
/* @__PURE__ */ (0,
|
|
43602
|
-
/* @__PURE__ */ (0,
|
|
43603
|
-
/* @__PURE__ */ (0,
|
|
43604
|
-
/* @__PURE__ */ (0,
|
|
43605
|
-
/* @__PURE__ */ (0,
|
|
43820
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("li", { className: "rounded border px-2 py-1.5 text-xs", children: [
|
|
43821
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
43822
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("span", { className: "font-medium", children: signature.signer_name }),
|
|
43823
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("span", { className: "text-muted-foreground", children: signature.field_key }),
|
|
43824
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("span", { className: "text-muted-foreground", children: new Date(signature.signed_at).toLocaleString() }),
|
|
43825
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("span", { className: "ml-auto font-mono text-[10px] text-muted-foreground", children: [
|
|
43606
43826
|
"v",
|
|
43607
43827
|
signature.document_version,
|
|
43608
43828
|
" \xB7 ",
|
|
@@ -43612,7 +43832,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43612
43832
|
intact === false ? (
|
|
43613
43833
|
// THE STORE REFUSED TO CALL IT INTACT: its reason goes through the one
|
|
43614
43834
|
// formatter, with what to do next, never raw in red (lane REFUSAL-SWEEP).
|
|
43615
|
-
/* @__PURE__ */ (0,
|
|
43835
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(
|
|
43616
43836
|
RefusalLine,
|
|
43617
43837
|
{
|
|
43618
43838
|
className: "mt-0.5",
|
|
@@ -43623,22 +43843,22 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43623
43843
|
)
|
|
43624
43844
|
}
|
|
43625
43845
|
)
|
|
43626
|
-
) : /* @__PURE__ */ (0,
|
|
43846
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime136.jsx)("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 ?? "") })
|
|
43627
43847
|
] }, signature.id);
|
|
43628
43848
|
}) }) : null,
|
|
43629
|
-
!render ? /* @__PURE__ */ (0,
|
|
43849
|
+
!render ? /* @__PURE__ */ (0, import_jsx_runtime136.jsx)("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__ */ (0, import_jsx_runtime136.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
43630
43850
|
"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 ",
|
|
43631
|
-
/* @__PURE__ */ (0,
|
|
43851
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("code", { children: "signature" }),
|
|
43632
43852
|
" \u2014 add one and the signing appears."
|
|
43633
|
-
] }) : !rights.write ? /* @__PURE__ */ (0,
|
|
43853
|
+
] }) : !rights.write ? /* @__PURE__ */ (0, import_jsx_runtime136.jsx)("p", { className: "text-xs text-muted-foreground", children: rights.why("write") }) : /* @__PURE__ */ (0, import_jsx_runtime136.jsx)("div", { className: "flex flex-col gap-1", children: signable.map((field2) => {
|
|
43634
43854
|
const already = signatures.find((s) => s.field_key === field2.key);
|
|
43635
43855
|
if (already) {
|
|
43636
|
-
return /* @__PURE__ */ (0,
|
|
43856
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
43637
43857
|
fieldName(field2),
|
|
43638
43858
|
" 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."
|
|
43639
43859
|
] }, field2.id);
|
|
43640
43860
|
}
|
|
43641
|
-
return /* @__PURE__ */ (0,
|
|
43861
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)(
|
|
43642
43862
|
"form",
|
|
43643
43863
|
{
|
|
43644
43864
|
className: "flex items-center gap-1",
|
|
@@ -43647,8 +43867,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43647
43867
|
if (name.trim() !== "") void sign(field2);
|
|
43648
43868
|
},
|
|
43649
43869
|
children: [
|
|
43650
|
-
/* @__PURE__ */ (0,
|
|
43651
|
-
|
|
43870
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(
|
|
43871
|
+
import_design_system121.BasicInput,
|
|
43652
43872
|
{
|
|
43653
43873
|
className: "h-8 w-56 text-xs",
|
|
43654
43874
|
"aria-label": `Sign ${fieldName(field2)}`,
|
|
@@ -43657,8 +43877,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43657
43877
|
onChange: (e) => setName(e.target.value)
|
|
43658
43878
|
}
|
|
43659
43879
|
),
|
|
43660
|
-
/* @__PURE__ */ (0,
|
|
43661
|
-
/* @__PURE__ */ (0,
|
|
43880
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(import_design_system121.Button, { size: "sm", type: "submit", disabled: busy || name.trim() === "", children: "Sign" }),
|
|
43881
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
43662
43882
|
"seals version ",
|
|
43663
43883
|
render.template_version,
|
|
43664
43884
|
" \xB7 ",
|
|
@@ -43673,15 +43893,270 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43673
43893
|
] });
|
|
43674
43894
|
}
|
|
43675
43895
|
|
|
43896
|
+
// src/NotifyRuleEditor.tsx
|
|
43897
|
+
var import_react241 = require("react");
|
|
43898
|
+
var import_react242 = require("@ai-matrx/records/react");
|
|
43899
|
+
var import_records57 = require("@ai-matrx/records");
|
|
43900
|
+
var import_design_system122 = require("@ai-matrx/design-system");
|
|
43901
|
+
var import_jsx_runtime137 = require("react/jsx-runtime");
|
|
43902
|
+
var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
|
|
43903
|
+
var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
43904
|
+
function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className }) {
|
|
43905
|
+
const client = (0, import_react242.useRecordsClient)();
|
|
43906
|
+
const host = useRecordsUi();
|
|
43907
|
+
const table = (0, import_react242.useTable)(tableId);
|
|
43908
|
+
const rights = useTableRights(table.data);
|
|
43909
|
+
const [subscriptions, setSubscriptions] = (0, import_react241.useState)(null);
|
|
43910
|
+
const [cadences, setCadences] = (0, import_react241.useState)([]);
|
|
43911
|
+
const [views, setViews] = (0, import_react241.useState)(null);
|
|
43912
|
+
const [error, setError] = (0, import_react241.useState)(null);
|
|
43913
|
+
const [busy, setBusy] = (0, import_react241.useState)(false);
|
|
43914
|
+
const [stopping, setStopping] = (0, import_react241.useState)(null);
|
|
43915
|
+
const load = (0, import_react241.useCallback)(async () => {
|
|
43916
|
+
const [held, offered] = await Promise.all([
|
|
43917
|
+
// THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
|
|
43918
|
+
// to them plus — only where they hold admin on this Table — anyone's over
|
|
43919
|
+
// it, and it is narrowed to Tables they can already open.
|
|
43920
|
+
client.subscriptions({ table_id: tableId }),
|
|
43921
|
+
// The cadences are ASKED of the store, never a list typed into a picker
|
|
43922
|
+
// here that could drift from what the digest runner understands.
|
|
43923
|
+
client.subscriptionCadences()
|
|
43924
|
+
]);
|
|
43925
|
+
if (!held.ok) {
|
|
43926
|
+
setError(held.error);
|
|
43927
|
+
return;
|
|
43928
|
+
}
|
|
43929
|
+
setError(null);
|
|
43930
|
+
setSubscriptions(held.data);
|
|
43931
|
+
if (offered.ok) setCadences(offered.data);
|
|
43932
|
+
if (host.savedViews) {
|
|
43933
|
+
setViews(await host.savedViews());
|
|
43934
|
+
return;
|
|
43935
|
+
}
|
|
43936
|
+
const listed = await client.views({ table_id: tableId });
|
|
43937
|
+
if (!listed.ok) {
|
|
43938
|
+
setError(listed.error);
|
|
43939
|
+
return;
|
|
43940
|
+
}
|
|
43941
|
+
setViews(listed.data.map((v) => ({ id: v.view_id, name: v.name })));
|
|
43942
|
+
}, [client, host, tableId]);
|
|
43943
|
+
(0, import_react241.useEffect)(() => {
|
|
43944
|
+
void load();
|
|
43945
|
+
}, [load]);
|
|
43946
|
+
const write = (0, import_react241.useCallback)(
|
|
43947
|
+
async (spec) => {
|
|
43948
|
+
const declared2 = await client.subscriptionDeclare({
|
|
43949
|
+
table_id: tableId,
|
|
43950
|
+
spec: {
|
|
43951
|
+
name: spec.name,
|
|
43952
|
+
saved_view_id: spec.savedViewId,
|
|
43953
|
+
cadence: spec.cadence ?? "instant",
|
|
43954
|
+
schedule: spec.schedule ?? null,
|
|
43955
|
+
channel: spec.channel ?? "in_app",
|
|
43956
|
+
quiet_hours: spec.quietHours ?? null,
|
|
43957
|
+
// "Tell me" is the default, and the store refuses a subscription with
|
|
43958
|
+
// nobody to tell rather than writing one that fires at nobody.
|
|
43959
|
+
recipient_user_id: spec.recipientUserId ?? client.config.actor.user_id ?? null,
|
|
43960
|
+
event_key: spec.eventKey ?? "records.changed"
|
|
43961
|
+
}
|
|
43962
|
+
});
|
|
43963
|
+
if (!declared2.ok) {
|
|
43964
|
+
setError(declared2.error);
|
|
43965
|
+
return null;
|
|
43966
|
+
}
|
|
43967
|
+
return declared2.data;
|
|
43968
|
+
},
|
|
43969
|
+
[client, tableId]
|
|
43970
|
+
);
|
|
43971
|
+
(0, import_react241.useEffect)(() => {
|
|
43972
|
+
if (!subscriptions || !seed || seed.length === 0) return;
|
|
43973
|
+
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
43974
|
+
if (missing.length === 0) return;
|
|
43975
|
+
void (async () => {
|
|
43976
|
+
for (const spec of missing) {
|
|
43977
|
+
if (!await write(spec)) return;
|
|
43978
|
+
}
|
|
43979
|
+
await load();
|
|
43980
|
+
})();
|
|
43981
|
+
}, [subscriptions, seed, write, load]);
|
|
43982
|
+
async function subscribe(viewId, viewName, cadence, channel, schedule, quiet2) {
|
|
43983
|
+
setBusy(true);
|
|
43984
|
+
const ok = await write({
|
|
43985
|
+
name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
|
|
43986
|
+
savedViewId: viewId,
|
|
43987
|
+
cadence,
|
|
43988
|
+
channel,
|
|
43989
|
+
schedule: schedule.trim() === "" ? null : schedule.trim(),
|
|
43990
|
+
quietHours: quiet2
|
|
43991
|
+
});
|
|
43992
|
+
setBusy(false);
|
|
43993
|
+
if (!ok) return;
|
|
43994
|
+
onChoose?.(ok);
|
|
43995
|
+
await load();
|
|
43996
|
+
}
|
|
43997
|
+
async function makeEveryRecordView() {
|
|
43998
|
+
setBusy(true);
|
|
43999
|
+
const made = await client.viewDeclare({
|
|
44000
|
+
table_id: tableId,
|
|
44001
|
+
spec: { name: `Every ${table.data?.name ?? "record"}`, filters: null }
|
|
44002
|
+
});
|
|
44003
|
+
setBusy(false);
|
|
44004
|
+
if (!made.ok) {
|
|
44005
|
+
setError(made.error);
|
|
44006
|
+
return;
|
|
44007
|
+
}
|
|
44008
|
+
await load();
|
|
44009
|
+
}
|
|
44010
|
+
async function unsubscribe(ruleId) {
|
|
44011
|
+
setStopping(null);
|
|
44012
|
+
setBusy(true);
|
|
44013
|
+
const deleted = await client.recordDelete({ record_id: ruleId });
|
|
44014
|
+
setBusy(false);
|
|
44015
|
+
if (!deleted.ok) {
|
|
44016
|
+
setError(deleted.error);
|
|
44017
|
+
return;
|
|
44018
|
+
}
|
|
44019
|
+
await load();
|
|
44020
|
+
}
|
|
44021
|
+
if (error) {
|
|
44022
|
+
return /* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44023
|
+
RefusalNotice,
|
|
44024
|
+
{
|
|
44025
|
+
error,
|
|
44026
|
+
className,
|
|
44027
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
44028
|
+
}
|
|
44029
|
+
);
|
|
44030
|
+
}
|
|
44031
|
+
if (subscriptions === null) return /* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Skeleton, { className: (0, import_design_system122.cn)("h-40 w-full", className) });
|
|
44032
|
+
return /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("div", { className: (0, import_design_system122.cn)("flex flex-col gap-2", className), children: [
|
|
44033
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
44034
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "font-medium text-foreground", children: "Tell me when" }),
|
|
44035
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("span", { children: [
|
|
44036
|
+
subscriptions.length,
|
|
44037
|
+
" subscription",
|
|
44038
|
+
subscriptions.length === 1 ? "" : "s"
|
|
44039
|
+
] })
|
|
44040
|
+
] }),
|
|
44041
|
+
subscriptions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("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__ */ (0, import_jsx_runtime137.jsx)("ul", { className: "flex flex-col gap-1", children: subscriptions.map((held) => /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("li", { className: "flex items-center gap-2 rounded border px-2 py-1.5 text-xs", children: [
|
|
44042
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "truncate font-medium", children: held.name }),
|
|
44043
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-muted-foreground", children: CADENCE_WORDS2[held.cadence] ?? held.cadence }),
|
|
44044
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-muted-foreground", children: CHANNEL_WORDS3[held.channel] ?? held.channel }),
|
|
44045
|
+
held.schedule ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-muted-foreground", children: scheduleWords(held.schedule, held.quiet_hours?.tz) }) : null,
|
|
44046
|
+
held.next_digest_at ? /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
44047
|
+
"next ",
|
|
44048
|
+
new Date(held.next_digest_at).toLocaleString()
|
|
44049
|
+
] }) : null,
|
|
44050
|
+
(0, import_records57.hasQuietHours)(held.quiet_hours) ? /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
44051
|
+
"quiet ",
|
|
44052
|
+
held.quiet_hours.start,
|
|
44053
|
+
"\u2013",
|
|
44054
|
+
held.quiet_hours.end
|
|
44055
|
+
] }) : null,
|
|
44056
|
+
held.recipient_user_id === null ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-destructive", children: "This one has nobody to tell, so it fires at nobody." }) : null,
|
|
44057
|
+
held.saved_view_id === null ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-destructive", children: "This one names no view, so the store never admits a record to it." }) : null,
|
|
44058
|
+
held.muted ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-muted-foreground", children: "Switched off \u2014 it tells nobody until somebody switches it back on." }) : null,
|
|
44059
|
+
onChoose ? held.rule_id === chosenRuleId ? /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
44060
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "font-medium text-foreground", children: "Used for this form" }),
|
|
44061
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", variant: "ghost", disabled: busy, onClick: () => onChoose(null), children: "Stop using" })
|
|
44062
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44063
|
+
import_design_system122.Button,
|
|
44064
|
+
{
|
|
44065
|
+
size: "sm",
|
|
44066
|
+
variant: "outline",
|
|
44067
|
+
className: "ml-auto",
|
|
44068
|
+
disabled: busy,
|
|
44069
|
+
onClick: () => onChoose(held.rule_id),
|
|
44070
|
+
children: "Use for this form"
|
|
44071
|
+
}
|
|
44072
|
+
) : null,
|
|
44073
|
+
rights.write && !(onChoose && held.rule_id === chosenRuleId) && stopping === held.rule_id ? /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("span", { className: (0, import_design_system122.cn)("flex flex-wrap items-center gap-2", onChoose ? void 0 : "ml-auto"), children: [
|
|
44074
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-xs text-destructive", children: archiveConfirmSentence("this subscription") }),
|
|
44075
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", variant: "destructive", disabled: busy, onClick: () => void unsubscribe(held.rule_id), children: "Stop telling me" }),
|
|
44076
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", variant: "ghost", onClick: () => setStopping(null), children: "Keep it" })
|
|
44077
|
+
] }) : rights.write && !(onChoose && held.rule_id === chosenRuleId) ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44078
|
+
import_design_system122.Button,
|
|
44079
|
+
{
|
|
44080
|
+
size: "sm",
|
|
44081
|
+
variant: "ghost",
|
|
44082
|
+
className: onChoose ? void 0 : "ml-auto",
|
|
44083
|
+
disabled: busy,
|
|
44084
|
+
onClick: () => setStopping(held.rule_id),
|
|
44085
|
+
children: "Stop telling me"
|
|
44086
|
+
}
|
|
44087
|
+
) : null
|
|
44088
|
+
] }, held.rule_id)) }),
|
|
44089
|
+
onChoose && chosenRuleId && !subscriptions.some((held) => held.rule_id === chosenRuleId) ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("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,
|
|
44090
|
+
views === null ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("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__ */ (0, import_jsx_runtime137.jsxs)("div", { className: "flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [
|
|
44091
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { children: "A subscription watches a saved view, and this table has none yet." }),
|
|
44092
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", variant: "outline", disabled: busy, onClick: () => void makeEveryRecordView(), children: "Watch every record" })
|
|
44093
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("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__ */ (0, import_jsx_runtime137.jsxs)(
|
|
44094
|
+
"form",
|
|
44095
|
+
{
|
|
44096
|
+
className: "flex flex-wrap items-center gap-1",
|
|
44097
|
+
onSubmit: (event) => {
|
|
44098
|
+
event.preventDefault();
|
|
44099
|
+
const form = event.currentTarget;
|
|
44100
|
+
const view = form.elements.namedItem("view").value;
|
|
44101
|
+
const cadence = form.elements.namedItem("cadence").value;
|
|
44102
|
+
const channel = form.elements.namedItem("channel").value;
|
|
44103
|
+
const schedule = form.elements.namedItem("schedule").value;
|
|
44104
|
+
const start = form.elements.namedItem("quiet_start").value;
|
|
44105
|
+
const end = form.elements.namedItem("quiet_end").value;
|
|
44106
|
+
const name = views.find((v) => v.id === view)?.name ?? "View";
|
|
44107
|
+
const quiet2 = start && end ? { start, end, tz: Intl.DateTimeFormat().resolvedOptions().timeZone } : null;
|
|
44108
|
+
if (view) void subscribe(view, name, cadence, channel, schedule, quiet2);
|
|
44109
|
+
},
|
|
44110
|
+
children: [
|
|
44111
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("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__ */ (0, import_jsx_runtime137.jsx)("option", { value: view.id, children: view.name }, view.id)) }),
|
|
44112
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("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__ */ (0, import_jsx_runtime137.jsx)("option", { value: cadence, children: CADENCE_WORDS2[cadence] ?? cadence }, cadence)) }),
|
|
44113
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("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__ */ (0, import_jsx_runtime137.jsx)("option", { value: channel, children: CHANNEL_WORDS3[channel] }, channel)) }),
|
|
44114
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44115
|
+
"input",
|
|
44116
|
+
{
|
|
44117
|
+
name: "schedule",
|
|
44118
|
+
"aria-label": "When the summary goes out",
|
|
44119
|
+
placeholder: "monday 08:00",
|
|
44120
|
+
className: "h-7 w-28 rounded border bg-background px-1 text-xs"
|
|
44121
|
+
}
|
|
44122
|
+
),
|
|
44123
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-xs text-muted-foreground", children: "not between" }),
|
|
44124
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44125
|
+
"input",
|
|
44126
|
+
{
|
|
44127
|
+
name: "quiet_start",
|
|
44128
|
+
type: "time",
|
|
44129
|
+
"aria-label": "Quiet hours start",
|
|
44130
|
+
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
44131
|
+
}
|
|
44132
|
+
),
|
|
44133
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-xs text-muted-foreground", children: "and" }),
|
|
44134
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44135
|
+
"input",
|
|
44136
|
+
{
|
|
44137
|
+
name: "quiet_end",
|
|
44138
|
+
type: "time",
|
|
44139
|
+
"aria-label": "Quiet hours end",
|
|
44140
|
+
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
44141
|
+
}
|
|
44142
|
+
),
|
|
44143
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", type: "submit", disabled: busy, children: "Tell me" }),
|
|
44144
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("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." })
|
|
44145
|
+
]
|
|
44146
|
+
}
|
|
44147
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("p", { className: "text-xs text-muted-foreground", children: rights.why("write") })
|
|
44148
|
+
] });
|
|
44149
|
+
}
|
|
44150
|
+
|
|
43676
44151
|
// src/DashboardCanvas.tsx
|
|
43677
44152
|
var import_react249 = require("react");
|
|
43678
44153
|
var import_react250 = require("@ai-matrx/records/react");
|
|
43679
44154
|
var import_design_system126 = require("@ai-matrx/design-system");
|
|
43680
44155
|
|
|
43681
44156
|
// src/DashboardGrid.tsx
|
|
43682
|
-
var
|
|
43683
|
-
var
|
|
43684
|
-
var
|
|
44157
|
+
var import_core23 = require("@dnd-kit/core");
|
|
44158
|
+
var import_sortable2 = require("@dnd-kit/sortable");
|
|
44159
|
+
var import_utilities2 = require("@dnd-kit/utilities");
|
|
43685
44160
|
var import_design_system123 = require("@ai-matrx/design-system");
|
|
43686
44161
|
|
|
43687
44162
|
// src/dashboardLayout.ts
|
|
@@ -43735,7 +44210,7 @@ function withBlockIds(blocks) {
|
|
|
43735
44210
|
// src/DashboardGrid.tsx
|
|
43736
44211
|
var import_jsx_runtime138 = require("react/jsx-runtime");
|
|
43737
44212
|
var dashboardCollision = (args) => {
|
|
43738
|
-
const under = (0,
|
|
44213
|
+
const under = (0, import_core23.pointerWithin)(args);
|
|
43739
44214
|
if (under.length > 0) return under;
|
|
43740
44215
|
const at = args.collisionRect;
|
|
43741
44216
|
return args.droppableContainers.flatMap((container) => {
|
|
@@ -43745,9 +44220,9 @@ var dashboardCollision = (args) => {
|
|
|
43745
44220
|
}).sort((a, b) => a.data.value - b.data.value);
|
|
43746
44221
|
};
|
|
43747
44222
|
function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
43748
|
-
const sensors = (0,
|
|
43749
|
-
(0,
|
|
43750
|
-
(0,
|
|
44223
|
+
const sensors = (0, import_core23.useSensors)(
|
|
44224
|
+
(0, import_core23.useSensor)(import_core23.PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
44225
|
+
(0, import_core23.useSensor)(import_core23.KeyboardSensor, { coordinateGetter: import_sortable2.sortableKeyboardCoordinates })
|
|
43751
44226
|
);
|
|
43752
44227
|
const ids = items.map((item) => item.id);
|
|
43753
44228
|
function dragEnd(event) {
|
|
@@ -43757,7 +44232,7 @@ function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
|
43757
44232
|
}
|
|
43758
44233
|
const grid = /* @__PURE__ */ (0, import_jsx_runtime138.jsx)("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-12", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(GridCell2, { item, index, arranging, onSpan }, item.id)) });
|
|
43759
44234
|
if (!arranging) return grid;
|
|
43760
|
-
return /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(
|
|
44235
|
+
return /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(import_core23.DndContext, { sensors, collisionDetection: dashboardCollision, onDragEnd: dragEnd, children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(import_sortable2.SortableContext, { items: ids, strategy: import_sortable2.rectSortingStrategy, children: grid }) });
|
|
43761
44236
|
}
|
|
43762
44237
|
function GridCell2({
|
|
43763
44238
|
item,
|
|
@@ -43765,8 +44240,8 @@ function GridCell2({
|
|
|
43765
44240
|
arranging,
|
|
43766
44241
|
onSpan
|
|
43767
44242
|
}) {
|
|
43768
|
-
const sortable = (0,
|
|
43769
|
-
const style = arranging ? { transform:
|
|
44243
|
+
const sortable = (0, import_sortable2.useSortable)({ id: item.id, disabled: !arranging });
|
|
44244
|
+
const style = arranging ? { transform: import_utilities2.CSS.Transform.toString(sortable.transform), transition: sortable.transition } : void 0;
|
|
43770
44245
|
return /* @__PURE__ */ (0, import_jsx_runtime138.jsxs)(
|
|
43771
44246
|
"div",
|
|
43772
44247
|
{
|
|
@@ -44970,7 +45445,7 @@ var import_react252 = require("@ai-matrx/records/react");
|
|
|
44970
45445
|
|
|
44971
45446
|
// src/question.ts
|
|
44972
45447
|
var import_records59 = require("@ai-matrx/records");
|
|
44973
|
-
var
|
|
45448
|
+
var import_core24 = require("@ai-matrx/records/core");
|
|
44974
45449
|
var QUESTION_OPS = ["count", "sum", "avg", "min", "max"];
|
|
44975
45450
|
var QUESTION_LIMIT_DEFAULT = 25;
|
|
44976
45451
|
var QUESTION_GROUP_CEILING = 200;
|
|
@@ -45222,7 +45697,7 @@ async function askAsOf(client, tableId, spec, fields, measures, keys) {
|
|
|
45222
45697
|
},
|
|
45223
45698
|
{ schema: config.schema ?? "custom" }
|
|
45224
45699
|
);
|
|
45225
|
-
if (response.error) return { ok: false, error: (0,
|
|
45700
|
+
if (response.error) return { ok: false, error: (0, import_core24.mapPgError)(response.error, "askQuestion.asOf") };
|
|
45226
45701
|
const rows2 = Array.isArray(response.data) ? response.data : [];
|
|
45227
45702
|
const bad = rows2.find((r) => Number(r.not_numbers ?? 0) > 0);
|
|
45228
45703
|
if (bad && m.op !== "count") {
|
|
@@ -45293,7 +45768,7 @@ function savedOf(row) {
|
|
|
45293
45768
|
}
|
|
45294
45769
|
async function questionsOf(client, tableId) {
|
|
45295
45770
|
const response = await client.config.dataSource.schema("platform").from("saved_view").select("id, name, definition, visibility, version, created_by").eq("surface_key", QUESTION_SURFACE).eq("subject_id", tableId).is("deleted_at", null).order("name", { ascending: true });
|
|
45296
|
-
if (response.error) return { ok: false, error: (0,
|
|
45771
|
+
if (response.error) return { ok: false, error: (0, import_core24.mapPgError)(response.error, "questionsOf") };
|
|
45297
45772
|
const me = client.config.actor?.user_id ?? null;
|
|
45298
45773
|
return {
|
|
45299
45774
|
ok: true,
|
|
@@ -45322,7 +45797,7 @@ async function questionSave(client, args) {
|
|
|
45322
45797
|
if (response.error.code === "23505") {
|
|
45323
45798
|
return { ok: false, error: { code: "already_exists", message: `You already have a question called \u201C${name}\u201D.` } };
|
|
45324
45799
|
}
|
|
45325
|
-
return { ok: false, error: (0,
|
|
45800
|
+
return { ok: false, error: (0, import_core24.mapPgError)(response.error, "questionSave") };
|
|
45326
45801
|
}
|
|
45327
45802
|
return { ok: true, data: response.data ? savedOf(response.data) : null };
|
|
45328
45803
|
}
|
|
@@ -45332,7 +45807,7 @@ async function questionArchive(client, id, version) {
|
|
|
45332
45807
|
{ p_surface_key: QUESTION_SURFACE, p_id: id, ...version !== void 0 ? { p_expected_version: version } : {} },
|
|
45333
45808
|
{ schema: "public" }
|
|
45334
45809
|
);
|
|
45335
|
-
if (response.error) return { ok: false, error: (0,
|
|
45810
|
+
if (response.error) return { ok: false, error: (0, import_core24.mapPgError)(response.error, "questionArchive") };
|
|
45336
45811
|
return { ok: true, data: response.data !== null };
|
|
45337
45812
|
}
|
|
45338
45813
|
|
|
@@ -46191,7 +46666,7 @@ function TableAsk({ tableId, onOpenInQuestions }) {
|
|
|
46191
46666
|
// src/PageScreen.tsx
|
|
46192
46667
|
var import_react255 = require("react");
|
|
46193
46668
|
var import_react256 = require("@ai-matrx/records/react");
|
|
46194
|
-
var
|
|
46669
|
+
var import_core25 = require("@ai-matrx/records/core");
|
|
46195
46670
|
var import_design_system129 = require("@ai-matrx/design-system");
|
|
46196
46671
|
var import_jsx_runtime145 = require("react/jsx-runtime");
|
|
46197
46672
|
var EDIT_LEVELS = /* @__PURE__ */ new Set(["edit_content", "editor", "admin", "owner"]);
|
|
@@ -46607,7 +47082,7 @@ function ListBlockView({
|
|
|
46607
47082
|
...sort.length > 0 ? { sort } : {}
|
|
46608
47083
|
});
|
|
46609
47084
|
const reload = page.reload;
|
|
46610
|
-
(0, import_react255.useEffect)(() => (0,
|
|
47085
|
+
(0, import_react255.useEffect)(() => (0, import_core25.onRecordChanged)(() => reload()), [reload]);
|
|
46611
47086
|
const rows = nobodyIsMe ? [] : page.data?.rows ?? [];
|
|
46612
47087
|
const columns = block.columns && block.columns.length > 0 ? block.columns.map((k) => all.find((f) => f.key === k)).filter((f) => !!f) : [...all].sort((a, b) => (a.sort ?? 100) - (b.sort ?? 100)).slice(0, DEFAULT_COLUMNS);
|
|
46613
47088
|
const firstId = rows[0]?.id ?? null;
|
|
@@ -48142,7 +48617,7 @@ var import_design_system142 = require("@ai-matrx/design-system");
|
|
|
48142
48617
|
var import_text_case19 = require("@ai-matrx/kit/text-case");
|
|
48143
48618
|
|
|
48144
48619
|
// src/viewShare.ts
|
|
48145
|
-
var
|
|
48620
|
+
var import_core26 = require("@ai-matrx/records/core");
|
|
48146
48621
|
var NO_VIEW_REASON = "No saved view yet";
|
|
48147
48622
|
function viewLink(tableId, viewId, origin) {
|
|
48148
48623
|
return `${tableLink(tableId, origin)}?view=${encodeURIComponent(viewId)}`;
|
|
@@ -48178,7 +48653,7 @@ async function downloadViewCsv(args) {
|
|
|
48178
48653
|
const columns = viewColumns(fields, view.presentation);
|
|
48179
48654
|
const rows = await rowsForFile({ client, host, fields: columns, records: read.rows, labels });
|
|
48180
48655
|
const name = fileNameOf(args.tableName) || "records";
|
|
48181
|
-
download(`${name}.csv`, "text/csv;charset=utf-8", (0,
|
|
48656
|
+
download(`${name}.csv`, "text/csv;charset=utf-8", (0, import_core26.exportCsv)(columns, rows));
|
|
48182
48657
|
host.notify?.success(read.cut ? cutWords(read.rows.length) : "CSV downloaded");
|
|
48183
48658
|
}
|
|
48184
48659
|
|