@ai-matrx/records-ui 0.106.0 → 0.107.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.cjs +1340 -875
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -2
- package/dist/index.d.ts +31 -2
- package/dist/index.js +1336 -871
- package/dist/index.js.map +1 -1
- package/dist/pickers.cjs +34 -9
- package/dist/pickers.cjs.map +1 -1
- package/dist/pickers.js +34 -9
- package/dist/pickers.js.map +1 -1
- package/package.json +5 -5
package/dist/index.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,
|
|
@@ -37920,9 +37954,41 @@ function formPresentation(spec) {
|
|
|
37920
37954
|
welcome: welcomeDocument(spec.welcome ?? null),
|
|
37921
37955
|
endings: (spec.endings ?? []).map(endingDocument),
|
|
37922
37956
|
hidden_fields: spec.hiddenFields ?? [],
|
|
37923
|
-
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) }) } : {}
|
|
37924
37961
|
};
|
|
37925
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
|
+
}
|
|
37926
37992
|
var text = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : null;
|
|
37927
37993
|
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
37928
37994
|
var strip = (doc) => Object.fromEntries(Object.entries(doc).filter(([, v]) => v !== null && v !== void 0));
|
|
@@ -38097,6 +38163,9 @@ function formFromSummary(summary) {
|
|
|
38097
38163
|
thankYou: thankYouFromDocument(presentation["thank_you"]),
|
|
38098
38164
|
look: formLookFromDocument(presentation["look"]),
|
|
38099
38165
|
submitLabel: presentation["submit_label"] ?? null,
|
|
38166
|
+
showOwnerHeader: bool(presentation["show_owner_header"]),
|
|
38167
|
+
autoAdvance: bool(presentation["auto_advance"]),
|
|
38168
|
+
notify: notifyFromDocument(presentation["notify"]),
|
|
38100
38169
|
// A form is public when the store says it is OPEN. A screen never decides
|
|
38101
38170
|
// this from a flag of its own: the link answers or it does not.
|
|
38102
38171
|
isPublic: summary.state === "open",
|
|
@@ -38880,6 +38949,50 @@ function compactTick(value) {
|
|
|
38880
38949
|
if (Math.abs(value) < 1e3 && !Number.isInteger(value)) return String(Math.round(value * 10) / 10);
|
|
38881
38950
|
return (0, import_format10.formatCount)(value, { style: "compact" });
|
|
38882
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
|
+
}
|
|
38883
38996
|
function Drawing({
|
|
38884
38997
|
kind,
|
|
38885
38998
|
points,
|
|
@@ -38985,9 +39098,9 @@ function Drawing({
|
|
|
38985
39098
|
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.CartesianGrid, { vertical: horizontal, horizontal: !horizontal }),
|
|
38986
39099
|
horizontal ? /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(import_jsx_runtime120.Fragment, { children: [
|
|
38987
39100
|
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.XAxis, { type: "number", ...axis }),
|
|
38988
|
-
/* @__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 }) })
|
|
38989
39102
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(import_jsx_runtime120.Fragment, { children: [
|
|
38990
|
-
/* @__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)) }) }),
|
|
38991
39104
|
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.YAxis, { width: 44, ...axis })
|
|
38992
39105
|
] }),
|
|
38993
39106
|
tooltip,
|
|
@@ -40631,7 +40744,7 @@ function FormLookLogo({ view, size = "md" }) {
|
|
|
40631
40744
|
}
|
|
40632
40745
|
);
|
|
40633
40746
|
}
|
|
40634
|
-
function FormLookFrame({ look, title, children, contained = false, className }) {
|
|
40747
|
+
function FormLookFrame({ look, title, children, contained = false, showOwnerHeader = true, className }) {
|
|
40635
40748
|
const view = formLookView(look, title);
|
|
40636
40749
|
return /* @__PURE__ */ (0, import_jsx_runtime127.jsxs)(
|
|
40637
40750
|
"div",
|
|
@@ -40648,10 +40761,10 @@ function FormLookFrame({ look, title, children, contained = false, className })
|
|
|
40648
40761
|
}
|
|
40649
40762
|
) : null,
|
|
40650
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,
|
|
40651
|
-
/* @__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: [
|
|
40652
40765
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)(FormLookLogo, { view }),
|
|
40653
40766
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)("p", { className: "min-w-0 truncate text-base font-semibold tracking-tight text-foreground", children: view.name })
|
|
40654
|
-
] }),
|
|
40767
|
+
] }) : null,
|
|
40655
40768
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
|
|
40656
40769
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)("div", { className: "w-full", children }),
|
|
40657
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)(
|
|
@@ -41473,11 +41586,14 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
|
41473
41586
|
|
|
41474
41587
|
// src/FormBuilder.tsx
|
|
41475
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");
|
|
41476
41596
|
var import_design_system118 = require("@ai-matrx/design-system");
|
|
41477
|
-
var import_react234 = require("react");
|
|
41478
|
-
var import_react235 = require("@ai-matrx/records/react");
|
|
41479
|
-
var import_records57 = require("@ai-matrx/records");
|
|
41480
|
-
var import_design_system119 = require("@ai-matrx/design-system");
|
|
41481
41597
|
|
|
41482
41598
|
// src/FormFlowEditor.tsx
|
|
41483
41599
|
var import_react229 = require("react");
|
|
@@ -41824,7 +41940,8 @@ function FormStage({
|
|
|
41824
41940
|
className,
|
|
41825
41941
|
connected = false,
|
|
41826
41942
|
onStart,
|
|
41827
|
-
onQuestionShown
|
|
41943
|
+
onQuestionShown,
|
|
41944
|
+
autoAdvance
|
|
41828
41945
|
}) {
|
|
41829
41946
|
const host = useRecordsUi();
|
|
41830
41947
|
const [answers, setAnswers] = (0, import_react230.useState)(() => ({ ...initialAnswers ?? {} }));
|
|
@@ -41837,6 +41954,11 @@ function FormStage({
|
|
|
41837
41954
|
const [ending, setEnding] = (0, import_react230.useState)(null);
|
|
41838
41955
|
const [started, setStarted] = (0, import_react230.useState)(() => !form.welcome || resumed);
|
|
41839
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;
|
|
41840
41962
|
const decoy = (0, import_react230.useRef)("");
|
|
41841
41963
|
const stage = (0, import_react230.useRef)(null);
|
|
41842
41964
|
const questions = (0, import_react230.useMemo)(() => {
|
|
@@ -41872,6 +41994,7 @@ function FormStage({
|
|
|
41872
41994
|
};
|
|
41873
41995
|
}
|
|
41874
41996
|
if (cancelled) return;
|
|
41997
|
+
setRouteKnown(true);
|
|
41875
41998
|
if (!told2.ok) {
|
|
41876
41999
|
setHidden({});
|
|
41877
42000
|
setError(told2.error ?? refusal("not_supported", told2.message, "Answer every question shown; none were skipped."));
|
|
@@ -41893,7 +42016,7 @@ function FormStage({
|
|
|
41893
42016
|
) : null
|
|
41894
42017
|
);
|
|
41895
42018
|
})();
|
|
41896
|
-
}, 250);
|
|
42019
|
+
}, routeKnown ? 250 : 0);
|
|
41897
42020
|
return () => {
|
|
41898
42021
|
cancelled = true;
|
|
41899
42022
|
clearTimeout(timer);
|
|
@@ -41928,11 +42051,24 @@ function FormStage({
|
|
|
41928
42051
|
const v = answers[q.key];
|
|
41929
42052
|
return v !== void 0 && v !== null && v !== "";
|
|
41930
42053
|
}).length;
|
|
41931
|
-
const
|
|
41932
|
-
|
|
41933
|
-
|
|
41934
|
-
|
|
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
|
+
};
|
|
41935
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
|
+
}
|
|
41936
42072
|
if (preview) {
|
|
41937
42073
|
setDone("preview");
|
|
41938
42074
|
return;
|
|
@@ -41964,20 +42100,28 @@ function FormStage({
|
|
|
41964
42100
|
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
41965
42101
|
async function advance() {
|
|
41966
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);
|
|
41967
42111
|
if (!routes || !whichAsked || !current) {
|
|
41968
42112
|
if (index < live.length - 1) setAt(index + 1);
|
|
41969
|
-
else
|
|
42113
|
+
else void submit();
|
|
41970
42114
|
return;
|
|
41971
42115
|
}
|
|
41972
42116
|
let told2;
|
|
41973
42117
|
try {
|
|
41974
|
-
told2 = await whichAsked(
|
|
42118
|
+
told2 = await whichAsked(answersRef.current);
|
|
41975
42119
|
} catch {
|
|
41976
42120
|
told2 = { ok: false, message: "This form could not work out which question comes next, so the next one is shown." };
|
|
41977
42121
|
}
|
|
41978
42122
|
if (!told2.ok) {
|
|
41979
42123
|
if (index < live.length - 1) setAt(index + 1);
|
|
41980
|
-
else
|
|
42124
|
+
else void submit();
|
|
41981
42125
|
return;
|
|
41982
42126
|
}
|
|
41983
42127
|
const next = {};
|
|
@@ -41990,13 +42134,18 @@ function FormStage({
|
|
|
41990
42134
|
setAt(here + 1);
|
|
41991
42135
|
return;
|
|
41992
42136
|
}
|
|
41993
|
-
const stillMissing = onRoute.
|
|
41994
|
-
|
|
41995
|
-
|
|
41996
|
-
});
|
|
41997
|
-
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();
|
|
41998
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]);
|
|
41999
42147
|
function retreat() {
|
|
42148
|
+
setStopped(null);
|
|
42000
42149
|
if (oneAtATime && index > 0) setAt(index - 1);
|
|
42001
42150
|
}
|
|
42002
42151
|
function onKeyDown(event) {
|
|
@@ -42012,12 +42161,11 @@ function FormStage({
|
|
|
42012
42161
|
else advance();
|
|
42013
42162
|
}
|
|
42014
42163
|
(0, import_react230.useEffect)(() => {
|
|
42015
|
-
const input = stage.current?.querySelector(
|
|
42016
|
-
// 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(
|
|
42017
42165
|
"input:not([tabindex='-1']), textarea, select, [role='combobox'], [role='radio'], [role='checkbox']"
|
|
42018
42166
|
);
|
|
42019
42167
|
input?.focus();
|
|
42020
|
-
}, [index, done]);
|
|
42168
|
+
}, [index, done, started]);
|
|
42021
42169
|
const startTold = (0, import_react230.useRef)(false);
|
|
42022
42170
|
(0, import_react230.useEffect)(() => {
|
|
42023
42171
|
if (startTold.current || !onStart) return;
|
|
@@ -42036,6 +42184,10 @@ function FormStage({
|
|
|
42036
42184
|
}
|
|
42037
42185
|
}, [current, live, oneAtATime, onQuestionShown, started, done]);
|
|
42038
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
|
+
});
|
|
42039
42191
|
if (done) {
|
|
42040
42192
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42041
42193
|
ThankYou,
|
|
@@ -42044,6 +42196,7 @@ function FormStage({
|
|
|
42044
42196
|
done,
|
|
42045
42197
|
ending: (form.endings ?? []).find((e) => e.id === ending) ?? null,
|
|
42046
42198
|
buttonClass: theme.buttonClass,
|
|
42199
|
+
pipe,
|
|
42047
42200
|
className: (0, import_design_system116.cn)("mx-auto max-w-xl py-10", centred && "text-center", className)
|
|
42048
42201
|
}
|
|
42049
42202
|
);
|
|
@@ -42055,14 +42208,14 @@ function FormStage({
|
|
|
42055
42208
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42056
42209
|
"section",
|
|
42057
42210
|
{
|
|
42058
|
-
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),
|
|
42059
42212
|
style: theme.surfaceStyle,
|
|
42060
42213
|
"data-records-form-welcome": "",
|
|
42061
42214
|
children: [
|
|
42062
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,
|
|
42063
|
-
w.title ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("h2", { className: "text-
|
|
42064
|
-
w.body ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "text-
|
|
42065
|
-
/* @__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" }) })
|
|
42066
42219
|
]
|
|
42067
42220
|
}
|
|
42068
42221
|
);
|
|
@@ -42071,7 +42224,15 @@ function FormStage({
|
|
|
42071
42224
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42072
42225
|
"section",
|
|
42073
42226
|
{
|
|
42074
|
-
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
|
+
),
|
|
42075
42236
|
style: theme.surfaceStyle,
|
|
42076
42237
|
"data-records-form-font": theme.font,
|
|
42077
42238
|
onKeyDown,
|
|
@@ -42081,20 +42242,41 @@ function FormStage({
|
|
|
42081
42242
|
import_design_system116.Progress,
|
|
42082
42243
|
{
|
|
42083
42244
|
value: live.length === 0 ? 0 : (oneAtATime ? index + 1 : answered) / live.length * 100,
|
|
42084
|
-
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}` } : {}
|
|
42085
42248
|
}
|
|
42086
42249
|
),
|
|
42087
|
-
/* @__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}` })
|
|
42088
42251
|
] }),
|
|
42089
|
-
signedInPublic ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42090
|
-
|
|
42091
|
-
|
|
42092
|
-
|
|
42093
|
-
|
|
42094
|
-
|
|
42095
|
-
|
|
42096
|
-
|
|
42097
|
-
|
|
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,
|
|
42098
42280
|
error ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(RefusalNotice, { error, className: "text-left" }) : null,
|
|
42099
42281
|
refusal4 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42100
42282
|
RefusalNotice,
|
|
@@ -42116,14 +42298,20 @@ function FormStage({
|
|
|
42116
42298
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42117
42299
|
Question,
|
|
42118
42300
|
{
|
|
42119
|
-
question: q,
|
|
42301
|
+
question: { ...q, ask: pipe(q.ask), help: q.help ? pipe(q.help) : null },
|
|
42120
42302
|
value: answers[q.key],
|
|
42121
|
-
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,
|
|
42122
42310
|
upload: host.upload
|
|
42123
42311
|
},
|
|
42124
42312
|
q.key
|
|
42125
42313
|
)),
|
|
42126
|
-
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
|
|
42127
42315
|
] }),
|
|
42128
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: [
|
|
42129
42317
|
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("label", { htmlFor: `hp-${honeypotKey}`, children: "Leave this field empty" }),
|
|
@@ -42142,16 +42330,14 @@ function FormStage({
|
|
|
42142
42330
|
}
|
|
42143
42331
|
)
|
|
42144
42332
|
] }) : null,
|
|
42145
|
-
/* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("footer", { className: (0, import_design_system116.cn)("flex flex-wrap items-center gap-
|
|
42146
|
-
oneAtATime && index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Button, { size: "
|
|
42147
|
-
oneAtATime && index < live.length - 1 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_design_system116.Button, { size: "
|
|
42148
|
-
|
|
42149
|
-
|
|
42150
|
-
"
|
|
42151
|
-
missing.length === 1 ? "needs" : "need",
|
|
42152
|
-
" 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" })
|
|
42153
42339
|
] }) : null,
|
|
42154
|
-
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
|
|
42155
42341
|
] })
|
|
42156
42342
|
]
|
|
42157
42343
|
}
|
|
@@ -42163,12 +42349,15 @@ function ThankYou({
|
|
|
42163
42349
|
done,
|
|
42164
42350
|
className,
|
|
42165
42351
|
ending,
|
|
42166
|
-
buttonClass
|
|
42352
|
+
buttonClass,
|
|
42353
|
+
pipe = (t) => t
|
|
42167
42354
|
}) {
|
|
42168
42355
|
const host = useRecordsUi();
|
|
42169
42356
|
const redirect = (ending ? ending.redirectUrl?.trim() : form.thankYou?.redirectUrl?.trim()) || null;
|
|
42170
|
-
const
|
|
42171
|
-
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;
|
|
42172
42361
|
const going = redirect !== null && done !== "preview";
|
|
42173
42362
|
(0, import_react230.useEffect)(() => {
|
|
42174
42363
|
if (!going || !redirect) return;
|
|
@@ -42185,8 +42374,8 @@ function ThankYou({
|
|
|
42185
42374
|
}
|
|
42186
42375
|
),
|
|
42187
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,
|
|
42188
|
-
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("h2", { className: "text-
|
|
42189
|
-
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,
|
|
42190
42379
|
going ? /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("p", { className: "mt-4 flex flex-wrap items-center gap-3 text-sm", children: [
|
|
42191
42380
|
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42192
42381
|
"a",
|
|
@@ -42199,11 +42388,11 @@ function ThankYou({
|
|
|
42199
42388
|
),
|
|
42200
42389
|
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-muted-foreground", children: "Taking you there\u2026" })
|
|
42201
42390
|
] }) : null,
|
|
42202
|
-
/* @__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" ? (
|
|
42203
42392
|
// SAID TO THE PERSON WHO ANSWERED (lane HANDOVER, 2026-09-29): "Saved to Form 1, with
|
|
42204
42393
|
// this form stamped on it" was the owner's bookkeeping read out to a front-desk member
|
|
42205
42394
|
// and to strangers. Where it landed is the owner's to see on the table.
|
|
42206
|
-
"
|
|
42395
|
+
"Answers received"
|
|
42207
42396
|
) : done })
|
|
42208
42397
|
] });
|
|
42209
42398
|
}
|
|
@@ -42211,6 +42400,9 @@ function Question({
|
|
|
42211
42400
|
question,
|
|
42212
42401
|
value,
|
|
42213
42402
|
onChange,
|
|
42403
|
+
onChosen,
|
|
42404
|
+
refused,
|
|
42405
|
+
large,
|
|
42214
42406
|
upload
|
|
42215
42407
|
}) {
|
|
42216
42408
|
const host = useRecordsUi();
|
|
@@ -42218,297 +42410,62 @@ function Question({
|
|
|
42218
42410
|
const field2 = question.field;
|
|
42219
42411
|
if (!field2) return null;
|
|
42220
42412
|
const id = `form-${field2.key}`;
|
|
42413
|
+
const sayId = `${id}-refused`;
|
|
42221
42414
|
const isAttachment = editorKindFor(field2) === "attachment";
|
|
42222
|
-
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42223
|
-
|
|
42224
|
-
|
|
42225
|
-
|
|
42226
|
-
|
|
42227
|
-
"
|
|
42228
|
-
{
|
|
42229
|
-
id,
|
|
42230
|
-
type: "file",
|
|
42231
|
-
className: "text-xs",
|
|
42232
|
-
onChange: (e) => {
|
|
42233
|
-
const file = e.target.files?.[0];
|
|
42234
|
-
if (!file) return;
|
|
42235
|
-
setUploadError(null);
|
|
42236
|
-
void upload?.(file).then((result) => {
|
|
42237
|
-
if (!result) return;
|
|
42238
|
-
if (result.ok) onChange(result.fileId);
|
|
42239
|
-
else
|
|
42240
|
-
setUploadError(
|
|
42241
|
-
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
42242
|
-
);
|
|
42243
|
-
});
|
|
42244
|
-
}
|
|
42245
|
-
}
|
|
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"
|
|
42246
42421
|
),
|
|
42247
|
-
|
|
42248
|
-
|
|
42249
|
-
|
|
42250
|
-
|
|
42251
|
-
|
|
42252
|
-
|
|
42253
|
-
}
|
|
42254
|
-
|
|
42255
|
-
|
|
42256
|
-
|
|
42257
|
-
|
|
42258
|
-
var import_records56 = require("@ai-matrx/records");
|
|
42259
|
-
var import_design_system117 = require("@ai-matrx/design-system");
|
|
42260
|
-
var import_jsx_runtime133 = require("react/jsx-runtime");
|
|
42261
|
-
var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
|
|
42262
|
-
var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
42263
|
-
function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className }) {
|
|
42264
|
-
const client = (0, import_react233.useRecordsClient)();
|
|
42265
|
-
const host = useRecordsUi();
|
|
42266
|
-
const table = (0, import_react233.useTable)(tableId);
|
|
42267
|
-
const rights = useTableRights(table.data);
|
|
42268
|
-
const [subscriptions, setSubscriptions] = (0, import_react232.useState)(null);
|
|
42269
|
-
const [cadences, setCadences] = (0, import_react232.useState)([]);
|
|
42270
|
-
const [views, setViews] = (0, import_react232.useState)(null);
|
|
42271
|
-
const [error, setError] = (0, import_react232.useState)(null);
|
|
42272
|
-
const [busy, setBusy] = (0, import_react232.useState)(false);
|
|
42273
|
-
const [stopping, setStopping] = (0, import_react232.useState)(null);
|
|
42274
|
-
const load = (0, import_react232.useCallback)(async () => {
|
|
42275
|
-
const [held, offered] = await Promise.all([
|
|
42276
|
-
// THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
|
|
42277
|
-
// to them plus — only where they hold admin on this Table — anyone's over
|
|
42278
|
-
// it, and it is narrowed to Tables they can already open.
|
|
42279
|
-
client.subscriptions({ table_id: tableId }),
|
|
42280
|
-
// The cadences are ASKED of the store, never a list typed into a picker
|
|
42281
|
-
// here that could drift from what the digest runner understands.
|
|
42282
|
-
client.subscriptionCadences()
|
|
42283
|
-
]);
|
|
42284
|
-
if (!held.ok) {
|
|
42285
|
-
setError(held.error);
|
|
42286
|
-
return;
|
|
42287
|
-
}
|
|
42288
|
-
setError(null);
|
|
42289
|
-
setSubscriptions(held.data);
|
|
42290
|
-
if (offered.ok) setCadences(offered.data);
|
|
42291
|
-
if (host.savedViews) {
|
|
42292
|
-
setViews(await host.savedViews());
|
|
42293
|
-
return;
|
|
42294
|
-
}
|
|
42295
|
-
const listed = await client.views({ table_id: tableId });
|
|
42296
|
-
if (!listed.ok) {
|
|
42297
|
-
setError(listed.error);
|
|
42298
|
-
return;
|
|
42299
|
-
}
|
|
42300
|
-
setViews(listed.data.map((v) => ({ id: v.view_id, name: v.name })));
|
|
42301
|
-
}, [client, host, tableId]);
|
|
42302
|
-
(0, import_react232.useEffect)(() => {
|
|
42303
|
-
void load();
|
|
42304
|
-
}, [load]);
|
|
42305
|
-
const write = (0, import_react232.useCallback)(
|
|
42306
|
-
async (spec) => {
|
|
42307
|
-
const declared2 = await client.subscriptionDeclare({
|
|
42308
|
-
table_id: tableId,
|
|
42309
|
-
spec: {
|
|
42310
|
-
name: spec.name,
|
|
42311
|
-
saved_view_id: spec.savedViewId,
|
|
42312
|
-
cadence: spec.cadence ?? "instant",
|
|
42313
|
-
schedule: spec.schedule ?? null,
|
|
42314
|
-
channel: spec.channel ?? "in_app",
|
|
42315
|
-
quiet_hours: spec.quietHours ?? null,
|
|
42316
|
-
// "Tell me" is the default, and the store refuses a subscription with
|
|
42317
|
-
// nobody to tell rather than writing one that fires at nobody.
|
|
42318
|
-
recipient_user_id: spec.recipientUserId ?? client.config.actor.user_id ?? null,
|
|
42319
|
-
event_key: spec.eventKey ?? "records.changed"
|
|
42320
|
-
}
|
|
42321
|
-
});
|
|
42322
|
-
if (!declared2.ok) {
|
|
42323
|
-
setError(declared2.error);
|
|
42324
|
-
return null;
|
|
42325
|
-
}
|
|
42326
|
-
return declared2.data;
|
|
42327
|
-
},
|
|
42328
|
-
[client, tableId]
|
|
42329
|
-
);
|
|
42330
|
-
(0, import_react232.useEffect)(() => {
|
|
42331
|
-
if (!subscriptions || !seed || seed.length === 0) return;
|
|
42332
|
-
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
42333
|
-
if (missing.length === 0) return;
|
|
42334
|
-
void (async () => {
|
|
42335
|
-
for (const spec of missing) {
|
|
42336
|
-
if (!await write(spec)) return;
|
|
42337
|
-
}
|
|
42338
|
-
await load();
|
|
42339
|
-
})();
|
|
42340
|
-
}, [subscriptions, seed, write, load]);
|
|
42341
|
-
async function subscribe(viewId, viewName, cadence, channel, schedule, quiet2) {
|
|
42342
|
-
setBusy(true);
|
|
42343
|
-
const ok = await write({
|
|
42344
|
-
name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
|
|
42345
|
-
savedViewId: viewId,
|
|
42346
|
-
cadence,
|
|
42347
|
-
channel,
|
|
42348
|
-
schedule: schedule.trim() === "" ? null : schedule.trim(),
|
|
42349
|
-
quietHours: quiet2
|
|
42350
|
-
});
|
|
42351
|
-
setBusy(false);
|
|
42352
|
-
if (!ok) return;
|
|
42353
|
-
onChoose?.(ok);
|
|
42354
|
-
await load();
|
|
42355
|
-
}
|
|
42356
|
-
async function makeEveryRecordView() {
|
|
42357
|
-
setBusy(true);
|
|
42358
|
-
const made = await client.viewDeclare({
|
|
42359
|
-
table_id: tableId,
|
|
42360
|
-
spec: { name: `Every ${table.data?.name ?? "record"}`, filters: null }
|
|
42361
|
-
});
|
|
42362
|
-
setBusy(false);
|
|
42363
|
-
if (!made.ok) {
|
|
42364
|
-
setError(made.error);
|
|
42365
|
-
return;
|
|
42366
|
-
}
|
|
42367
|
-
await load();
|
|
42368
|
-
}
|
|
42369
|
-
async function unsubscribe(ruleId) {
|
|
42370
|
-
setStopping(null);
|
|
42371
|
-
setBusy(true);
|
|
42372
|
-
const deleted = await client.recordDelete({ record_id: ruleId });
|
|
42373
|
-
setBusy(false);
|
|
42374
|
-
if (!deleted.ok) {
|
|
42375
|
-
setError(deleted.error);
|
|
42376
|
-
return;
|
|
42377
|
-
}
|
|
42378
|
-
await load();
|
|
42379
|
-
}
|
|
42380
|
-
if (error) {
|
|
42381
|
-
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42382
|
-
RefusalNotice,
|
|
42383
|
-
{
|
|
42384
|
-
error,
|
|
42385
|
-
className,
|
|
42386
|
-
actions: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
42387
|
-
}
|
|
42388
|
-
);
|
|
42389
|
-
}
|
|
42390
|
-
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) });
|
|
42391
|
-
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: (0, import_design_system117.cn)("flex flex-col gap-2", className), children: [
|
|
42392
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
42393
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "font-medium text-foreground", children: "Tell me when" }),
|
|
42394
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { children: [
|
|
42395
|
-
subscriptions.length,
|
|
42396
|
-
" subscription",
|
|
42397
|
-
subscriptions.length === 1 ? "" : "s"
|
|
42398
|
-
] })
|
|
42399
|
-
] }),
|
|
42400
|
-
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: [
|
|
42401
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "truncate font-medium", children: held.name }),
|
|
42402
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-muted-foreground", children: CADENCE_WORDS2[held.cadence] ?? held.cadence }),
|
|
42403
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-muted-foreground", children: CHANNEL_WORDS3[held.channel] ?? held.channel }),
|
|
42404
|
-
held.schedule ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-muted-foreground", children: scheduleWords(held.schedule, held.quiet_hours?.tz) }) : null,
|
|
42405
|
-
held.next_digest_at ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
42406
|
-
"next ",
|
|
42407
|
-
new Date(held.next_digest_at).toLocaleString()
|
|
42408
|
-
] }) : null,
|
|
42409
|
-
(0, import_records56.hasQuietHours)(held.quiet_hours) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
42410
|
-
"quiet ",
|
|
42411
|
-
held.quiet_hours.start,
|
|
42412
|
-
"\u2013",
|
|
42413
|
-
held.quiet_hours.end
|
|
42414
|
-
] }) : null,
|
|
42415
|
-
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,
|
|
42416
|
-
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,
|
|
42417
|
-
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,
|
|
42418
|
-
onChoose ? held.rule_id === chosenRuleId ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
42419
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "font-medium text-foreground", children: "Used for this form" }),
|
|
42420
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Button, { size: "sm", variant: "ghost", disabled: busy, onClick: () => onChoose(null), children: "Stop using" })
|
|
42421
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42422
|
-
import_design_system117.Button,
|
|
42423
|
-
{
|
|
42424
|
-
size: "sm",
|
|
42425
|
-
variant: "outline",
|
|
42426
|
-
className: "ml-auto",
|
|
42427
|
-
disabled: busy,
|
|
42428
|
-
onClick: () => onChoose(held.rule_id),
|
|
42429
|
-
children: "Use for this form"
|
|
42430
|
-
}
|
|
42431
|
-
) : null,
|
|
42432
|
-
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: [
|
|
42433
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs text-destructive", children: archiveConfirmSentence("this subscription") }),
|
|
42434
|
-
/* @__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" }),
|
|
42435
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Button, { size: "sm", variant: "ghost", onClick: () => setStopping(null), children: "Keep it" })
|
|
42436
|
-
] }) : rights.write && !(onChoose && held.rule_id === chosenRuleId) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42437
|
-
import_design_system117.Button,
|
|
42438
|
-
{
|
|
42439
|
-
size: "sm",
|
|
42440
|
-
variant: "ghost",
|
|
42441
|
-
className: onChoose ? void 0 : "ml-auto",
|
|
42442
|
-
disabled: busy,
|
|
42443
|
-
onClick: () => setStopping(held.rule_id),
|
|
42444
|
-
children: "Stop telling me"
|
|
42445
|
-
}
|
|
42446
|
-
) : null
|
|
42447
|
-
] }, held.rule_id)) }),
|
|
42448
|
-
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,
|
|
42449
|
-
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: [
|
|
42450
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { children: "A subscription watches a saved view, and this table has none yet." }),
|
|
42451
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system117.Button, { size: "sm", variant: "outline", disabled: busy, onClick: () => void makeEveryRecordView(), children: "Watch every record" })
|
|
42452
|
-
] }) : /* @__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)(
|
|
42453
|
-
"form",
|
|
42454
|
-
{
|
|
42455
|
-
className: "flex flex-wrap items-center gap-1",
|
|
42456
|
-
onSubmit: (event) => {
|
|
42457
|
-
event.preventDefault();
|
|
42458
|
-
const form = event.currentTarget;
|
|
42459
|
-
const view = form.elements.namedItem("view").value;
|
|
42460
|
-
const cadence = form.elements.namedItem("cadence").value;
|
|
42461
|
-
const channel = form.elements.namedItem("channel").value;
|
|
42462
|
-
const schedule = form.elements.namedItem("schedule").value;
|
|
42463
|
-
const start = form.elements.namedItem("quiet_start").value;
|
|
42464
|
-
const end = form.elements.namedItem("quiet_end").value;
|
|
42465
|
-
const name = views.find((v) => v.id === view)?.name ?? "View";
|
|
42466
|
-
const quiet2 = start && end ? { start, end, tz: Intl.DateTimeFormat().resolvedOptions().timeZone } : null;
|
|
42467
|
-
if (view) void subscribe(view, name, cadence, channel, schedule, quiet2);
|
|
42468
|
-
},
|
|
42469
|
-
children: [
|
|
42470
|
-
/* @__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)) }),
|
|
42471
|
-
/* @__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)) }),
|
|
42472
|
-
/* @__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)) }),
|
|
42473
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42474
|
-
"input",
|
|
42475
|
-
{
|
|
42476
|
-
name: "schedule",
|
|
42477
|
-
"aria-label": "When the summary goes out",
|
|
42478
|
-
placeholder: "monday 08:00",
|
|
42479
|
-
className: "h-7 w-28 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: "not between" }),
|
|
42483
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42484
|
-
"input",
|
|
42485
|
-
{
|
|
42486
|
-
name: "quiet_start",
|
|
42487
|
-
type: "time",
|
|
42488
|
-
"aria-label": "Quiet hours start",
|
|
42489
|
-
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
42490
|
-
}
|
|
42491
|
-
),
|
|
42492
|
-
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs text-muted-foreground", children: "and" }),
|
|
42493
|
-
/* @__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)(
|
|
42494
42433
|
"input",
|
|
42495
42434
|
{
|
|
42496
|
-
|
|
42497
|
-
type: "
|
|
42498
|
-
|
|
42499
|
-
|
|
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
|
+
}
|
|
42500
42451
|
}
|
|
42501
42452
|
),
|
|
42502
|
-
/* @__PURE__ */ (0,
|
|
42503
|
-
|
|
42504
|
-
|
|
42505
|
-
|
|
42506
|
-
|
|
42507
|
-
|
|
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
|
+
);
|
|
42508
42465
|
}
|
|
42509
42466
|
|
|
42510
42467
|
// src/FormBuilder.tsx
|
|
42511
|
-
var
|
|
42468
|
+
var import_jsx_runtime133 = require("react/jsx-runtime");
|
|
42512
42469
|
function freshFormName(base, taken) {
|
|
42513
42470
|
const names = new Set(taken.map((n) => n.trim().toLowerCase()));
|
|
42514
42471
|
if (!names.has(base.toLowerCase())) return base;
|
|
@@ -42525,7 +42482,21 @@ function isLookRefusal(error) {
|
|
|
42525
42482
|
return Boolean(error && LOOK_REFUSAL.test(refusalSentence(error)));
|
|
42526
42483
|
}
|
|
42527
42484
|
var KEPT_DRAFT_PREFIX = "records-ui:form-draft:";
|
|
42528
|
-
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
|
+
];
|
|
42529
42500
|
function readKeptDraft(formId) {
|
|
42530
42501
|
try {
|
|
42531
42502
|
const raw = window.localStorage.getItem(KEPT_DRAFT_PREFIX + formId);
|
|
@@ -42556,31 +42527,31 @@ function withKept(stored, spec) {
|
|
|
42556
42527
|
return out;
|
|
42557
42528
|
}
|
|
42558
42529
|
function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne, createOnMount, className }) {
|
|
42559
|
-
const client = (0,
|
|
42530
|
+
const client = (0, import_react233.useRecordsClient)();
|
|
42560
42531
|
const host = useRecordsUi();
|
|
42561
42532
|
const claimSeed = useSeedGuard();
|
|
42562
|
-
const table = (0,
|
|
42533
|
+
const table = (0, import_react233.useTable)(tableId);
|
|
42563
42534
|
const rights = useTableRights(table.data);
|
|
42564
|
-
const fields = (0,
|
|
42565
|
-
const [forms, setForms] = (0,
|
|
42566
|
-
const [error, setError] = (0,
|
|
42567
|
-
const [refused, setRefused] = (0,
|
|
42568
|
-
const [activeId, setActiveId] = (0,
|
|
42569
|
-
const [draft, setDraft] = (0,
|
|
42570
|
-
const [saving, setSaving] = (0,
|
|
42571
|
-
const [saved, setSaved] = (0,
|
|
42572
|
-
const [publishing, setPublishing] = (0,
|
|
42573
|
-
const [copied, setCopied] = (0,
|
|
42574
|
-
const [shownUrl, setShownUrl] = (0,
|
|
42575
|
-
const [lookOptions, setLookOptions] = (0,
|
|
42576
|
-
const [lookUnread, setLookUnread] = (0,
|
|
42577
|
-
const [restoredAt, setRestoredAt] = (0,
|
|
42578
|
-
const [showResults, setShowResults] = (0,
|
|
42579
|
-
const [adding, setAdding] = (0,
|
|
42580
|
-
const keysBeforeAdd = (0,
|
|
42581
|
-
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");
|
|
42582
42553
|
const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
42583
|
-
const load = (0,
|
|
42554
|
+
const load = (0, import_react232.useCallback)(async () => {
|
|
42584
42555
|
const answered = await client.forms({ table_id: tableId });
|
|
42585
42556
|
if (!answered.ok) {
|
|
42586
42557
|
setError(answered.error);
|
|
@@ -42607,10 +42578,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42607
42578
|
setError(null);
|
|
42608
42579
|
setForms(mine);
|
|
42609
42580
|
}, [client, tableId, seed, claimSeed]);
|
|
42610
|
-
(0,
|
|
42581
|
+
(0, import_react232.useEffect)(() => {
|
|
42611
42582
|
void load();
|
|
42612
42583
|
}, [load]);
|
|
42613
|
-
(0,
|
|
42584
|
+
(0, import_react232.useEffect)(() => {
|
|
42614
42585
|
if (!forms || forms.length === 0) return;
|
|
42615
42586
|
if (createOnMount && makeOnMount !== "made") return;
|
|
42616
42587
|
const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
|
|
@@ -42627,14 +42598,14 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42627
42598
|
}
|
|
42628
42599
|
onActiveForm?.(chosen);
|
|
42629
42600
|
}, [forms, activeFormId, makeOnMount]);
|
|
42630
|
-
const byKey = (0,
|
|
42631
|
-
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)(
|
|
42632
42603
|
() => (fields.data ?? []).map((f) => ({ id: String(f.id), key: f.key, label: f.label || f.key, field: f })),
|
|
42633
42604
|
[fields.data]
|
|
42634
42605
|
);
|
|
42635
|
-
const stored = (0,
|
|
42606
|
+
const stored = (0, import_react232.useMemo)(() => draft ? (forms ?? []).find((f) => f.id === draft.id) ?? null : null, [draft, forms]);
|
|
42636
42607
|
const dirty = Boolean(draft && stored && !sameForm(draft, stored));
|
|
42637
|
-
(0,
|
|
42608
|
+
(0, import_react232.useEffect)(() => {
|
|
42638
42609
|
if (!draft || !stored) return;
|
|
42639
42610
|
const timer = window.setTimeout(() => {
|
|
42640
42611
|
if (sameForm(draft, stored)) dropKeptDraft(draft.id);
|
|
@@ -42643,7 +42614,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42643
42614
|
return () => window.clearTimeout(timer);
|
|
42644
42615
|
}, [draft, stored]);
|
|
42645
42616
|
const lookKey = draft ? JSON.stringify(formLookDocument(draft.look ?? null)) : null;
|
|
42646
|
-
(0,
|
|
42617
|
+
(0, import_react232.useEffect)(() => {
|
|
42647
42618
|
if (lookKey === null) return;
|
|
42648
42619
|
let cancelled = false;
|
|
42649
42620
|
const timer = window.setTimeout(() => {
|
|
@@ -42660,7 +42631,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42660
42631
|
window.clearTimeout(timer);
|
|
42661
42632
|
};
|
|
42662
42633
|
}, [client, lookKey]);
|
|
42663
|
-
(0,
|
|
42634
|
+
(0, import_react232.useEffect)(() => {
|
|
42664
42635
|
const before = keysBeforeAdd.current;
|
|
42665
42636
|
if (!before || !fields.data) return;
|
|
42666
42637
|
const made = askableFields(fields.data).filter((f) => !before.has(f.key));
|
|
@@ -42699,7 +42670,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42699
42670
|
}
|
|
42700
42671
|
dropKeptDraft(draft.id);
|
|
42701
42672
|
setRestoredAt(null);
|
|
42702
|
-
setSaved("Saved
|
|
42673
|
+
setSaved("Saved");
|
|
42703
42674
|
await load();
|
|
42704
42675
|
return true;
|
|
42705
42676
|
}
|
|
@@ -42738,7 +42709,22 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42738
42709
|
setRefused(written.error);
|
|
42739
42710
|
return;
|
|
42740
42711
|
}
|
|
42741
|
-
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");
|
|
42742
42728
|
await load();
|
|
42743
42729
|
}
|
|
42744
42730
|
async function create(name) {
|
|
@@ -42755,15 +42741,15 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42755
42741
|
await load();
|
|
42756
42742
|
return true;
|
|
42757
42743
|
}
|
|
42758
|
-
const [started, setStarted] = (0,
|
|
42759
|
-
(0,
|
|
42744
|
+
const [started, setStarted] = (0, import_react232.useState)(false);
|
|
42745
|
+
(0, import_react232.useEffect)(() => {
|
|
42760
42746
|
if (!startWithOne || started || forms === null || forms.length > 0) return;
|
|
42761
42747
|
if (askableFields(fields.data ?? []).length === 0) return;
|
|
42762
42748
|
if (!table.data && !table.error) return;
|
|
42763
42749
|
setStarted(true);
|
|
42764
42750
|
void create(table.data?.name?.trim() ? `${table.data.name.trim()} form` : "Form 1");
|
|
42765
42751
|
}, [startWithOne, started, forms, fields.data, create, table.data, table.error]);
|
|
42766
|
-
(0,
|
|
42752
|
+
(0, import_react232.useEffect)(() => {
|
|
42767
42753
|
if (!createOnMount || makeOnMount !== "waiting" || forms === null) return;
|
|
42768
42754
|
if (!table.data && !table.error) return;
|
|
42769
42755
|
if (askableFields(fields.data ?? []).length === 0) {
|
|
@@ -42776,396 +42762,320 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42776
42762
|
(made) => setMakeOnMount(made ? "made" : "not-made")
|
|
42777
42763
|
);
|
|
42778
42764
|
}, [createOnMount, makeOnMount, forms, fields.data, create, table.data, table.error]);
|
|
42779
|
-
if (fields.error) return /* @__PURE__ */ (0,
|
|
42765
|
+
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(RefusalNotice, { error: fields.error, className });
|
|
42780
42766
|
if (error) {
|
|
42781
|
-
return /* @__PURE__ */ (0,
|
|
42767
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42782
42768
|
RefusalNotice,
|
|
42783
42769
|
{
|
|
42784
42770
|
error,
|
|
42785
42771
|
className,
|
|
42786
|
-
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" })
|
|
42787
42773
|
}
|
|
42788
42774
|
);
|
|
42789
42775
|
}
|
|
42790
|
-
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) });
|
|
42791
42777
|
if (createOnMount && (makeOnMount === "waiting" || makeOnMount === "making") && !refused) {
|
|
42792
|
-
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) });
|
|
42793
42779
|
}
|
|
42794
42780
|
if (!rights.admin) {
|
|
42795
|
-
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") });
|
|
42796
42782
|
}
|
|
42797
|
-
return
|
|
42798
|
-
|
|
42799
|
-
|
|
42800
|
-
|
|
42801
|
-
|
|
42802
|
-
|
|
42803
|
-
|
|
42804
|
-
|
|
42805
|
-
|
|
42806
|
-
|
|
42807
|
-
|
|
42808
|
-
|
|
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
|
|
42809
42800
|
},
|
|
42810
|
-
|
|
42811
|
-
|
|
42812
|
-
|
|
42813
|
-
|
|
42814
|
-
|
|
42815
|
-
|
|
42816
|
-
|
|
42817
|
-
|
|
42818
|
-
|
|
42819
|
-
|
|
42820
|
-
|
|
42821
|
-
|
|
42822
|
-
|
|
42823
|
-
|
|
42824
|
-
|
|
42825
|
-
|
|
42826
|
-
|
|
42827
|
-
|
|
42828
|
-
|
|
42829
|
-
|
|
42830
|
-
|
|
42831
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Button, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
42832
|
-
] })
|
|
42833
|
-
] }),
|
|
42834
|
-
refused && !isLookRefusal(refused) ? /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42835
|
-
RefusalNotice,
|
|
42836
|
-
{
|
|
42837
|
-
error: refused,
|
|
42838
|
-
onKeepEditing: () => setRefused(null),
|
|
42839
|
-
onDiscard: () => {
|
|
42840
|
-
const kept = forms.find((f) => f.id === activeId) ?? null;
|
|
42841
|
-
if (kept) dropKeptDraft(kept.id);
|
|
42842
|
-
setDraft(kept);
|
|
42843
|
-
setRestoredAt(null);
|
|
42844
|
-
setSaved(null);
|
|
42845
|
-
setRefused(null);
|
|
42846
|
-
}
|
|
42847
|
-
}
|
|
42848
|
-
) : null,
|
|
42849
|
-
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: [
|
|
42850
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("span", { children: [
|
|
42851
|
-
"Unsaved changes from",
|
|
42852
|
-
" ",
|
|
42853
|
-
new Date(restoredAt).toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" }),
|
|
42854
|
-
" 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
|
+
] })
|
|
42855
42822
|
] }),
|
|
42856
|
-
/* @__PURE__ */ (0,
|
|
42857
|
-
|
|
42823
|
+
refused && !isLookRefusal(refused) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42824
|
+
RefusalNotice,
|
|
42858
42825
|
{
|
|
42859
|
-
|
|
42860
|
-
|
|
42861
|
-
|
|
42862
|
-
|
|
42863
|
-
|
|
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);
|
|
42864
42832
|
setRestoredAt(null);
|
|
42865
|
-
|
|
42866
|
-
|
|
42867
|
-
|
|
42868
|
-
)
|
|
42869
|
-
] }) : null,
|
|
42870
|
-
!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: [
|
|
42871
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42872
|
-
PublishRow,
|
|
42873
|
-
{
|
|
42874
|
-
url: `${publicOrigin}${(0, import_records57.publicFormPath)(draft.id)}`,
|
|
42875
|
-
state: draft.state,
|
|
42876
|
-
mayPublish: rights.structure,
|
|
42877
|
-
why: rights.why("structure"),
|
|
42878
|
-
busy: publishing,
|
|
42879
|
-
copied,
|
|
42880
|
-
shownUrl,
|
|
42881
|
-
onPublish: (wanted) => void publish(wanted),
|
|
42882
|
-
onCopy: (url) => void copyLink(url),
|
|
42883
|
-
unsaved: dirty
|
|
42833
|
+
setSaved(null);
|
|
42834
|
+
setRefused(null);
|
|
42835
|
+
}
|
|
42884
42836
|
}
|
|
42885
|
-
),
|
|
42886
|
-
/* @__PURE__ */ (0,
|
|
42887
|
-
|
|
42888
|
-
|
|
42889
|
-
|
|
42890
|
-
|
|
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,
|
|
42891
42847
|
{
|
|
42892
|
-
|
|
42893
|
-
|
|
42894
|
-
|
|
42848
|
+
size: "sm",
|
|
42849
|
+
variant: "ghost",
|
|
42850
|
+
onClick: () => {
|
|
42851
|
+
dropKeptDraft(stored.id);
|
|
42852
|
+
setDraft(stored);
|
|
42853
|
+
setRestoredAt(null);
|
|
42854
|
+
},
|
|
42855
|
+
children: "Discard"
|
|
42895
42856
|
}
|
|
42896
42857
|
)
|
|
42897
|
-
] }),
|
|
42898
|
-
/* @__PURE__ */ (0,
|
|
42899
|
-
/* @__PURE__ */ (0,
|
|
42900
|
-
|
|
42901
|
-
|
|
42902
|
-
|
|
42903
|
-
|
|
42904
|
-
|
|
42905
|
-
|
|
42906
|
-
|
|
42907
|
-
|
|
42908
|
-
|
|
42909
|
-
|
|
42910
|
-
)
|
|
42911
|
-
|
|
42912
|
-
|
|
42913
|
-
|
|
42914
|
-
|
|
42915
|
-
|
|
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,
|
|
42916
42892
|
{
|
|
42917
|
-
|
|
42918
|
-
|
|
42919
|
-
|
|
42920
|
-
placeholder: "https://",
|
|
42921
|
-
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"
|
|
42922
42896
|
}
|
|
42923
|
-
)
|
|
42897
|
+
),
|
|
42898
|
+
"Show our logo and name"
|
|
42924
42899
|
] }),
|
|
42925
|
-
/* @__PURE__ */ (0,
|
|
42926
|
-
/* @__PURE__ */ (0,
|
|
42927
|
-
|
|
42928
|
-
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,
|
|
42929
42903
|
{
|
|
42930
|
-
|
|
42931
|
-
|
|
42932
|
-
|
|
42933
|
-
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"
|
|
42934
42907
|
}
|
|
42935
|
-
)
|
|
42908
|
+
),
|
|
42909
|
+
"Next after a choice"
|
|
42936
42910
|
] })
|
|
42937
|
-
] })
|
|
42938
|
-
] }),
|
|
42939
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(WelcomeEditor, { value: draft.welcome, pictures: lookOptions?.pictures ?? null, onChange: (welcome) => patch({ welcome }) }),
|
|
42940
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42941
|
-
EndingsEditor,
|
|
42942
|
-
{
|
|
42943
|
-
endings: draft.endings ?? [],
|
|
42944
|
-
pictures: lookOptions?.pictures ?? null,
|
|
42945
|
-
fieldKeys: conditionFields,
|
|
42946
|
-
scored: draft.questions.some((q) => q.points),
|
|
42947
|
-
onChange: (endings) => patch({ endings })
|
|
42948
|
-
}
|
|
42949
|
-
),
|
|
42950
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
42951
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42952
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Label, { className: "text-xs font-medium", children: "Name" }),
|
|
42953
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.BasicInput, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
|
|
42954
|
-
] }),
|
|
42955
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42956
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Label, { className: "text-xs font-medium", children: "Flow" }),
|
|
42957
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42958
|
-
"select",
|
|
42959
|
-
{
|
|
42960
|
-
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
42961
|
-
value: draft.flow ?? "one-at-a-time",
|
|
42962
|
-
onChange: (e) => patch({ flow: e.target.value }),
|
|
42963
|
-
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))
|
|
42964
|
-
}
|
|
42965
|
-
)
|
|
42966
42911
|
] }),
|
|
42967
|
-
/* @__PURE__ */ (0,
|
|
42968
|
-
/* @__PURE__ */ (0,
|
|
42969
|
-
/* @__PURE__ */ (0,
|
|
42970
|
-
|
|
42971
|
-
|
|
42972
|
-
|
|
42973
|
-
|
|
42974
|
-
|
|
42975
|
-
|
|
42976
|
-
|
|
42977
|
-
|
|
42978
|
-
|
|
42979
|
-
|
|
42980
|
-
|
|
42981
|
-
|
|
42982
|
-
|
|
42983
|
-
|
|
42984
|
-
|
|
42985
|
-
|
|
42986
|
-
|
|
42987
|
-
|
|
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
|
+
] })
|
|
42988
42952
|
] }),
|
|
42989
|
-
/* @__PURE__ */ (0,
|
|
42990
|
-
|
|
42991
|
-
|
|
42992
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Label, { className: "text-xs font-medium", children: "Look" }),
|
|
42993
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
42994
|
-
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,
|
|
42995
42956
|
{
|
|
42996
|
-
|
|
42997
|
-
|
|
42998
|
-
|
|
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 })
|
|
42999
42962
|
}
|
|
43000
42963
|
),
|
|
43001
|
-
/* @__PURE__ */ (0,
|
|
43002
|
-
|
|
43003
|
-
|
|
43004
|
-
/* @__PURE__ */ (0,
|
|
43005
|
-
|
|
43006
|
-
|
|
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",
|
|
43007
42973
|
{
|
|
43008
|
-
|
|
43009
|
-
|
|
43010
|
-
|
|
43011
|
-
|
|
43012
|
-
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))
|
|
43013
42978
|
}
|
|
43014
42979
|
)
|
|
43015
42980
|
] }),
|
|
43016
|
-
/* @__PURE__ */ (0,
|
|
43017
|
-
/* @__PURE__ */ (0,
|
|
43018
|
-
/* @__PURE__ */ (0,
|
|
43019
|
-
|
|
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,
|
|
43020
42985
|
{
|
|
43021
|
-
|
|
43022
|
-
|
|
43023
|
-
|
|
43024
|
-
noneLabel: "No cover",
|
|
43025
|
-
onChange: (id) => patchLook({ coverFileId: id || null })
|
|
42986
|
+
rows: 2,
|
|
42987
|
+
value: draft.intro ?? "",
|
|
42988
|
+
onChange: (e) => patch({ intro: e.target.value || null })
|
|
43026
42989
|
}
|
|
43027
42990
|
)
|
|
43028
|
-
] })
|
|
43029
|
-
|
|
43030
|
-
|
|
43031
|
-
|
|
43032
|
-
|
|
43033
|
-
|
|
43034
|
-
|
|
43035
|
-
|
|
43036
|
-
|
|
43037
|
-
|
|
43038
|
-
|
|
43039
|
-
|
|
43040
|
-
|
|
43041
|
-
] })
|
|
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 }) })
|
|
43042
43004
|
] }),
|
|
43043
|
-
|
|
43044
|
-
|
|
43045
|
-
|
|
43046
|
-
|
|
43047
|
-
|
|
43048
|
-
|
|
43049
|
-
|
|
43050
|
-
|
|
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,
|
|
43051
43021
|
{
|
|
43052
|
-
|
|
43053
|
-
|
|
43054
|
-
|
|
43055
|
-
|
|
43056
|
-
})
|
|
43022
|
+
label: "Logo",
|
|
43023
|
+
pictures: lookOptions.pictures,
|
|
43024
|
+
value: draft.look?.logoFileId ?? "",
|
|
43025
|
+
noneLabel: "Organization logo",
|
|
43026
|
+
onChange: (id) => patchLook({ logoFileId: id || null })
|
|
43057
43027
|
}
|
|
43058
|
-
)
|
|
43059
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)("span", { className: "text-xs font-medium", children: fieldName(field2) }),
|
|
43060
|
-
field2.required ? /* @__PURE__ */ (0, import_jsx_runtime134.jsx)("span", { className: "text-xs text-destructive", children: "required" }) : null
|
|
43028
|
+
)
|
|
43061
43029
|
] }),
|
|
43062
|
-
|
|
43063
|
-
/* @__PURE__ */ (0,
|
|
43064
|
-
|
|
43065
|
-
|
|
43066
|
-
className: "h-8 text-xs",
|
|
43067
|
-
placeholder: fieldName(field2),
|
|
43068
|
-
"aria-label": `How to ask for ${fieldName(field2)}`,
|
|
43069
|
-
value: question.ask ?? "",
|
|
43070
|
-
onChange: (e) => patchQuestion(index, { ask: e.target.value || null })
|
|
43071
|
-
}
|
|
43072
|
-
),
|
|
43073
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43074
|
-
import_design_system119.BasicInput,
|
|
43075
|
-
{
|
|
43076
|
-
className: "h-8 text-xs",
|
|
43077
|
-
placeholder: "Help text",
|
|
43078
|
-
"aria-label": `Help text for ${fieldName(field2)}`,
|
|
43079
|
-
value: question.help ?? "",
|
|
43080
|
-
onChange: (e) => patchQuestion(index, { help: e.target.value || null })
|
|
43081
|
-
}
|
|
43082
|
-
),
|
|
43083
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("label", { className: "col-span-2 flex items-center gap-2 text-xs", children: [
|
|
43084
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43085
|
-
import_design_system119.Checkbox,
|
|
43086
|
-
{
|
|
43087
|
-
checked: field2.required ? true : question.required === true,
|
|
43088
|
-
disabled: field2.required === true,
|
|
43089
|
-
"aria-label": `${fieldName(field2)} is required`,
|
|
43090
|
-
onCheckedChange: (checked) => patchQuestion(index, { required: checked === true ? true : null })
|
|
43091
|
-
}
|
|
43092
|
-
),
|
|
43093
|
-
"Required"
|
|
43094
|
-
] }),
|
|
43095
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43096
|
-
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,
|
|
43097
43034
|
{
|
|
43098
|
-
|
|
43099
|
-
|
|
43100
|
-
|
|
43101
|
-
|
|
43102
|
-
|
|
43103
|
-
field: f
|
|
43104
|
-
})),
|
|
43105
|
-
onChange: (showIf) => patchQuestion(index, { showIf })
|
|
43106
|
-
}
|
|
43107
|
-
),
|
|
43108
|
-
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43109
|
-
QuestionLogic,
|
|
43110
|
-
{
|
|
43111
|
-
question,
|
|
43112
|
-
index,
|
|
43113
|
-
questions: draft.questions,
|
|
43114
|
-
endings: draft.endings ?? [],
|
|
43115
|
-
field: field2,
|
|
43116
|
-
fieldKeys: conditionFields,
|
|
43117
|
-
label: (key) => {
|
|
43118
|
-
const f = byKey.get(key);
|
|
43119
|
-
return draft.questions.find((q) => q.field === key)?.ask || (f ? fieldName(f) : key);
|
|
43120
|
-
},
|
|
43121
|
-
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 })
|
|
43122
43040
|
}
|
|
43123
43041
|
)
|
|
43124
|
-
] })
|
|
43125
|
-
] },
|
|
43126
|
-
|
|
43127
|
-
|
|
43128
|
-
|
|
43129
|
-
|
|
43130
|
-
|
|
43131
|
-
className: "rounded border border-destructive/40 bg-destructive/5 px-2 py-1.5 text-xs text-destructive",
|
|
43132
|
-
message: requiredLeftUnasked(fields.data ?? [], draft.questions.map((q) => q.field)) ?? "",
|
|
43133
|
-
operation: "Build a form",
|
|
43134
|
-
origin: "records-ui/FormBuilder"
|
|
43135
|
-
}
|
|
43136
|
-
) : null,
|
|
43137
|
-
draft.questions.filter((q) => !byKey.has(q.field)).map((q) => /* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("p", { className: "text-xs text-destructive", children: [
|
|
43138
|
-
'This form asks for "',
|
|
43139
|
-
q.field,
|
|
43140
|
-
'" and this table has no such Field. It is shown here rather than quietly dropped.'
|
|
43141
|
-
] }, q.field)),
|
|
43142
|
-
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)(
|
|
43143
|
-
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,
|
|
43144
43049
|
{
|
|
43145
43050
|
tableId,
|
|
43146
|
-
|
|
43147
|
-
|
|
43148
|
-
|
|
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);
|
|
43149
43058
|
},
|
|
43150
|
-
|
|
43059
|
+
onAdded: () => setAdding(false),
|
|
43060
|
+
onQuestions: (questions) => patch({ questions }),
|
|
43061
|
+
onQuestion: patchQuestion
|
|
43151
43062
|
}
|
|
43152
|
-
)
|
|
43153
|
-
import_design_system119.Button,
|
|
43154
|
-
{
|
|
43155
|
-
size: "sm",
|
|
43156
|
-
variant: "outline",
|
|
43157
|
-
onClick: () => {
|
|
43158
|
-
keysBeforeAdd.current = new Set((fields.data ?? []).map((f) => f.key));
|
|
43159
|
-
setAdding(true);
|
|
43160
|
-
},
|
|
43161
|
-
children: "Add a question"
|
|
43162
|
-
}
|
|
43163
|
-
) })
|
|
43063
|
+
)
|
|
43164
43064
|
] })
|
|
43165
|
-
] })
|
|
43166
|
-
|
|
43167
|
-
|
|
43168
|
-
|
|
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
|
+
);
|
|
43169
43079
|
}
|
|
43170
43080
|
function PublishRow({
|
|
43171
43081
|
url,
|
|
@@ -43180,10 +43090,10 @@ function PublishRow({
|
|
|
43180
43090
|
unsaved = false
|
|
43181
43091
|
}) {
|
|
43182
43092
|
const open = state === "open";
|
|
43183
|
-
return /* @__PURE__ */ (0,
|
|
43184
|
-
/* @__PURE__ */ (0,
|
|
43185
|
-
mayPublish ? /* @__PURE__ */ (0,
|
|
43186
|
-
|
|
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,
|
|
43187
43097
|
{
|
|
43188
43098
|
size: "sm",
|
|
43189
43099
|
variant: open ? "ghost" : "default",
|
|
@@ -43192,8 +43102,8 @@ function PublishRow({
|
|
|
43192
43102
|
children: busy ? "\u2026" : open ? "Unpublish" : unsaved ? "Save and publish" : "Publish"
|
|
43193
43103
|
}
|
|
43194
43104
|
) : null,
|
|
43195
|
-
state !== "draft" ? /* @__PURE__ */ (0,
|
|
43196
|
-
/* @__PURE__ */ (0,
|
|
43105
|
+
state !== "draft" ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(import_jsx_runtime133.Fragment, { children: [
|
|
43106
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43197
43107
|
"a",
|
|
43198
43108
|
{
|
|
43199
43109
|
href: url,
|
|
@@ -43203,14 +43113,14 @@ function PublishRow({
|
|
|
43203
43113
|
children: url
|
|
43204
43114
|
}
|
|
43205
43115
|
),
|
|
43206
|
-
/* @__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" })
|
|
43207
43117
|
] }) : null
|
|
43208
43118
|
] }),
|
|
43209
|
-
/* @__PURE__ */ (0,
|
|
43210
|
-
open && unsaved ? /* @__PURE__ */ (0,
|
|
43211
|
-
!mayPublish ? /* @__PURE__ */ (0,
|
|
43212
|
-
shownUrl ? /* @__PURE__ */ (0,
|
|
43213
|
-
"
|
|
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: ",
|
|
43214
43124
|
shownUrl
|
|
43215
43125
|
] }) : null
|
|
43216
43126
|
] });
|
|
@@ -43220,7 +43130,7 @@ function Condition({
|
|
|
43220
43130
|
fieldKeys,
|
|
43221
43131
|
onChange
|
|
43222
43132
|
}) {
|
|
43223
|
-
return /* @__PURE__ */ (0,
|
|
43133
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43224
43134
|
ConditionGroup,
|
|
43225
43135
|
{
|
|
43226
43136
|
lead: "Ask only when",
|
|
@@ -43231,26 +43141,326 @@ function Condition({
|
|
|
43231
43141
|
}
|
|
43232
43142
|
);
|
|
43233
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
|
+
}
|
|
43234
43444
|
|
|
43235
43445
|
// src/DocTemplate.tsx
|
|
43236
|
-
var
|
|
43237
|
-
var
|
|
43238
|
-
var
|
|
43239
|
-
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");
|
|
43240
43450
|
function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, className }) {
|
|
43241
|
-
const client = (0,
|
|
43451
|
+
const client = (0, import_react235.useRecordsClient)();
|
|
43242
43452
|
const claimSeed = useSeedGuard();
|
|
43243
|
-
const table = (0,
|
|
43453
|
+
const table = (0, import_react235.useTable)(tableId);
|
|
43244
43454
|
const rights = useTableRights(table.data);
|
|
43245
|
-
const fields = (0,
|
|
43246
|
-
const [templates, setTemplates] = (0,
|
|
43247
|
-
const [error, setError] = (0,
|
|
43248
|
-
const [activeId, setActiveId] = (0,
|
|
43249
|
-
const [draftName, setDraftName] = (0,
|
|
43250
|
-
const [draftBody, setDraftBody] = (0,
|
|
43251
|
-
const [unresolved, setUnresolved] = (0,
|
|
43252
|
-
const [saving, setSaving] = (0,
|
|
43253
|
-
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 () => {
|
|
43254
43464
|
const held = await client.docTemplates({ table_id: tableId });
|
|
43255
43465
|
if (!held.ok) {
|
|
43256
43466
|
setError(held.error);
|
|
@@ -43281,10 +43491,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43281
43491
|
setError(null);
|
|
43282
43492
|
setTemplates(rows);
|
|
43283
43493
|
}, [client, tableId, seed, fields.data]);
|
|
43284
|
-
(0,
|
|
43494
|
+
(0, import_react234.useEffect)(() => {
|
|
43285
43495
|
void load();
|
|
43286
43496
|
}, [load]);
|
|
43287
|
-
(0,
|
|
43497
|
+
(0, import_react234.useEffect)(() => {
|
|
43288
43498
|
if (!templates || templates.length === 0) return;
|
|
43289
43499
|
const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
|
|
43290
43500
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -43292,7 +43502,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43292
43502
|
setDraftBody(chosen.body);
|
|
43293
43503
|
onActiveTemplate?.(chosen);
|
|
43294
43504
|
}, [templates, activeTemplateId]);
|
|
43295
|
-
(0,
|
|
43505
|
+
(0, import_react234.useEffect)(() => {
|
|
43296
43506
|
let cancelled = false;
|
|
43297
43507
|
if (draftBody.trim() === "") {
|
|
43298
43508
|
setUnresolved([]);
|
|
@@ -43322,25 +43532,25 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43322
43532
|
setActiveId(saved.data);
|
|
43323
43533
|
await load();
|
|
43324
43534
|
}
|
|
43325
|
-
if (fields.error) return /* @__PURE__ */ (0,
|
|
43535
|
+
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(RefusalNotice, { error: fields.error, className });
|
|
43326
43536
|
if (error) {
|
|
43327
|
-
return /* @__PURE__ */ (0,
|
|
43537
|
+
return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43328
43538
|
RefusalNotice,
|
|
43329
43539
|
{
|
|
43330
43540
|
error,
|
|
43331
43541
|
className,
|
|
43332
|
-
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" })
|
|
43333
43543
|
}
|
|
43334
43544
|
);
|
|
43335
43545
|
}
|
|
43336
|
-
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) });
|
|
43337
43547
|
if (!rights.admin) {
|
|
43338
|
-
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") });
|
|
43339
43549
|
}
|
|
43340
|
-
return /* @__PURE__ */ (0,
|
|
43341
|
-
/* @__PURE__ */ (0,
|
|
43342
|
-
templates.map((t) => /* @__PURE__ */ (0,
|
|
43343
|
-
|
|
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,
|
|
43344
43554
|
{
|
|
43345
43555
|
size: "sm",
|
|
43346
43556
|
variant: t.id === activeId ? "secondary" : "ghost",
|
|
@@ -43352,7 +43562,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43352
43562
|
},
|
|
43353
43563
|
children: [
|
|
43354
43564
|
t.name,
|
|
43355
|
-
/* @__PURE__ */ (0,
|
|
43565
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("span", { className: "ml-1 text-[10px] opacity-70", children: [
|
|
43356
43566
|
"v",
|
|
43357
43567
|
t.template_version
|
|
43358
43568
|
] })
|
|
@@ -43360,8 +43570,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43360
43570
|
},
|
|
43361
43571
|
t.id
|
|
43362
43572
|
)),
|
|
43363
|
-
/* @__PURE__ */ (0,
|
|
43364
|
-
|
|
43573
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43574
|
+
import_design_system119.Button,
|
|
43365
43575
|
{
|
|
43366
43576
|
size: "sm",
|
|
43367
43577
|
variant: "ghost",
|
|
@@ -43373,16 +43583,16 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43373
43583
|
children: "New template"
|
|
43374
43584
|
}
|
|
43375
43585
|
),
|
|
43376
|
-
/* @__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" })
|
|
43377
43587
|
] }),
|
|
43378
|
-
/* @__PURE__ */ (0,
|
|
43379
|
-
/* @__PURE__ */ (0,
|
|
43380
|
-
/* @__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" })
|
|
43381
43591
|
] }),
|
|
43382
|
-
/* @__PURE__ */ (0,
|
|
43383
|
-
/* @__PURE__ */ (0,
|
|
43384
|
-
(fields.data ?? []).map((field2) => /* @__PURE__ */ (0,
|
|
43385
|
-
|
|
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,
|
|
43386
43596
|
{
|
|
43387
43597
|
size: "sm",
|
|
43388
43598
|
variant: "ghost",
|
|
@@ -43393,8 +43603,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43393
43603
|
field2.id
|
|
43394
43604
|
))
|
|
43395
43605
|
] }),
|
|
43396
|
-
/* @__PURE__ */ (0,
|
|
43397
|
-
|
|
43606
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43607
|
+
import_design_system119.BasicTextarea,
|
|
43398
43608
|
{
|
|
43399
43609
|
rows: 10,
|
|
43400
43610
|
"aria-label": "Template body",
|
|
@@ -43403,33 +43613,33 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43403
43613
|
onChange: (e) => setDraftBody(e.target.value)
|
|
43404
43614
|
}
|
|
43405
43615
|
),
|
|
43406
|
-
unresolved.length > 0 ? /* @__PURE__ */ (0,
|
|
43407
|
-
/* @__PURE__ */ (0,
|
|
43408
|
-
/* @__PURE__ */ (0,
|
|
43409
|
-
/* @__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)(
|
|
43410
43620
|
RefusalLine,
|
|
43411
43621
|
{
|
|
43412
43622
|
error: refusal("invalid_argument", token.why, "Change it to a Field this table has, or take it out.")
|
|
43413
43623
|
}
|
|
43414
43624
|
)
|
|
43415
|
-
] }, 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
|
|
43416
43626
|
] });
|
|
43417
43627
|
}
|
|
43418
43628
|
|
|
43419
43629
|
// src/DocRender.tsx
|
|
43420
|
-
var
|
|
43421
|
-
var
|
|
43422
|
-
var
|
|
43423
|
-
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");
|
|
43424
43634
|
function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
43425
|
-
const client = (0,
|
|
43426
|
-
const [preview, setPreview] = (0,
|
|
43427
|
-
const [renders, setRenders] = (0,
|
|
43428
|
-
const [showing, setShowing] = (0,
|
|
43429
|
-
const [error, setError] = (0,
|
|
43430
|
-
const [busy, setBusy] = (0,
|
|
43431
|
-
const paper = (0,
|
|
43432
|
-
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 () => {
|
|
43433
43643
|
const [body, held] = await Promise.all([
|
|
43434
43644
|
client.docRenderBody({ template_id: templateId, record_id: recordId }),
|
|
43435
43645
|
client.docRenders({ record_id: recordId })
|
|
@@ -43446,7 +43656,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43446
43656
|
setPreview(body.data);
|
|
43447
43657
|
setRenders(held.data.filter((r) => r.template_id === templateId));
|
|
43448
43658
|
}, [client, templateId, recordId]);
|
|
43449
|
-
(0,
|
|
43659
|
+
(0, import_react236.useEffect)(() => {
|
|
43450
43660
|
void load();
|
|
43451
43661
|
}, [load]);
|
|
43452
43662
|
async function freeze() {
|
|
@@ -43486,22 +43696,22 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43486
43696
|
}
|
|
43487
43697
|
}
|
|
43488
43698
|
if (error) {
|
|
43489
|
-
return /* @__PURE__ */ (0,
|
|
43699
|
+
return /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(
|
|
43490
43700
|
RefusalNotice,
|
|
43491
43701
|
{
|
|
43492
43702
|
error,
|
|
43493
43703
|
className,
|
|
43494
|
-
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" })
|
|
43495
43705
|
}
|
|
43496
43706
|
);
|
|
43497
43707
|
}
|
|
43498
|
-
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) });
|
|
43499
43709
|
const frozen = showing ? renders.find((r) => r.id === showing) ?? null : null;
|
|
43500
43710
|
const text2 = frozen ? frozen.body : preview;
|
|
43501
|
-
return /* @__PURE__ */ (0,
|
|
43502
|
-
/* @__PURE__ */ (0,
|
|
43503
|
-
/* @__PURE__ */ (0,
|
|
43504
|
-
|
|
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,
|
|
43505
43715
|
{
|
|
43506
43716
|
size: "sm",
|
|
43507
43717
|
variant: frozen ? "ghost" : "secondary",
|
|
@@ -43510,8 +43720,8 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43510
43720
|
children: "As it reads now"
|
|
43511
43721
|
}
|
|
43512
43722
|
),
|
|
43513
|
-
renders.map((render) => /* @__PURE__ */ (0,
|
|
43514
|
-
|
|
43723
|
+
renders.map((render) => /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(
|
|
43724
|
+
import_design_system120.Button,
|
|
43515
43725
|
{
|
|
43516
43726
|
size: "sm",
|
|
43517
43727
|
variant: render.id === showing ? "secondary" : "ghost",
|
|
@@ -43522,33 +43732,33 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43522
43732
|
},
|
|
43523
43733
|
render.id
|
|
43524
43734
|
)),
|
|
43525
|
-
/* @__PURE__ */ (0,
|
|
43526
|
-
/* @__PURE__ */ (0,
|
|
43527
|
-
/* @__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" })
|
|
43528
43738
|
] })
|
|
43529
43739
|
] }),
|
|
43530
|
-
/* @__PURE__ */ (0,
|
|
43531
|
-
/* @__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." })
|
|
43532
43742
|
] });
|
|
43533
43743
|
}
|
|
43534
43744
|
|
|
43535
43745
|
// src/SignBlock.tsx
|
|
43536
|
-
var
|
|
43537
|
-
var
|
|
43538
|
-
var
|
|
43539
|
-
var
|
|
43540
|
-
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");
|
|
43541
43751
|
function SignBlock({ tableId, recordId, render, className }) {
|
|
43542
|
-
const client = (0,
|
|
43543
|
-
const table = (0,
|
|
43752
|
+
const client = (0, import_react239.useRecordsClient)();
|
|
43753
|
+
const table = (0, import_react240.useTable)(tableId);
|
|
43544
43754
|
const rights = useTableRights(table.data);
|
|
43545
|
-
const fields = (0,
|
|
43546
|
-
const [signatures, setSignatures] = (0,
|
|
43547
|
-
const [verdicts, setVerdicts] = (0,
|
|
43548
|
-
const [error, setError] = (0,
|
|
43549
|
-
const [name, setName] = (0,
|
|
43550
|
-
const [busy, setBusy] = (0,
|
|
43551
|
-
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 () => {
|
|
43552
43762
|
const held = await client.docSignatures({ record_id: recordId });
|
|
43553
43763
|
if (!held.ok) {
|
|
43554
43764
|
setError(held.error);
|
|
@@ -43563,7 +43773,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43563
43773
|
}
|
|
43564
43774
|
setVerdicts(answers);
|
|
43565
43775
|
}, [client, recordId]);
|
|
43566
|
-
(0,
|
|
43776
|
+
(0, import_react238.useEffect)(() => {
|
|
43567
43777
|
void load();
|
|
43568
43778
|
}, [load]);
|
|
43569
43779
|
async function sign(field2) {
|
|
@@ -43583,36 +43793,36 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43583
43793
|
setName("");
|
|
43584
43794
|
await load();
|
|
43585
43795
|
}
|
|
43586
|
-
if (fields.error) return /* @__PURE__ */ (0,
|
|
43796
|
+
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(RefusalNotice, { error: fields.error, className });
|
|
43587
43797
|
if (error) {
|
|
43588
|
-
return /* @__PURE__ */ (0,
|
|
43798
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(
|
|
43589
43799
|
RefusalNotice,
|
|
43590
43800
|
{
|
|
43591
43801
|
error,
|
|
43592
43802
|
className,
|
|
43593
|
-
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" })
|
|
43594
43804
|
}
|
|
43595
43805
|
);
|
|
43596
43806
|
}
|
|
43597
|
-
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) });
|
|
43598
43808
|
const signable = (fields.data ?? []).filter(isSignatureField);
|
|
43599
|
-
return /* @__PURE__ */ (0,
|
|
43600
|
-
/* @__PURE__ */ (0,
|
|
43601
|
-
/* @__PURE__ */ (0,
|
|
43602
|
-
/* @__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: [
|
|
43603
43813
|
signatures.length,
|
|
43604
43814
|
" on this record"
|
|
43605
43815
|
] })
|
|
43606
43816
|
] }),
|
|
43607
|
-
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) => {
|
|
43608
43818
|
const verdict = verdicts[signature.id];
|
|
43609
43819
|
const intact = typeof verdict === "object" && verdict !== null ? verdict.intact : void 0;
|
|
43610
|
-
return /* @__PURE__ */ (0,
|
|
43611
|
-
/* @__PURE__ */ (0,
|
|
43612
|
-
/* @__PURE__ */ (0,
|
|
43613
|
-
/* @__PURE__ */ (0,
|
|
43614
|
-
/* @__PURE__ */ (0,
|
|
43615
|
-
/* @__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: [
|
|
43616
43826
|
"v",
|
|
43617
43827
|
signature.document_version,
|
|
43618
43828
|
" \xB7 ",
|
|
@@ -43622,7 +43832,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43622
43832
|
intact === false ? (
|
|
43623
43833
|
// THE STORE REFUSED TO CALL IT INTACT: its reason goes through the one
|
|
43624
43834
|
// formatter, with what to do next, never raw in red (lane REFUSAL-SWEEP).
|
|
43625
|
-
/* @__PURE__ */ (0,
|
|
43835
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(
|
|
43626
43836
|
RefusalLine,
|
|
43627
43837
|
{
|
|
43628
43838
|
className: "mt-0.5",
|
|
@@ -43633,22 +43843,22 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43633
43843
|
)
|
|
43634
43844
|
}
|
|
43635
43845
|
)
|
|
43636
|
-
) : /* @__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 ?? "") })
|
|
43637
43847
|
] }, signature.id);
|
|
43638
43848
|
}) }) : null,
|
|
43639
|
-
!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: [
|
|
43640
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 ",
|
|
43641
|
-
/* @__PURE__ */ (0,
|
|
43851
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("code", { children: "signature" }),
|
|
43642
43852
|
" \u2014 add one and the signing appears."
|
|
43643
|
-
] }) : !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) => {
|
|
43644
43854
|
const already = signatures.find((s) => s.field_key === field2.key);
|
|
43645
43855
|
if (already) {
|
|
43646
|
-
return /* @__PURE__ */ (0,
|
|
43856
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
43647
43857
|
fieldName(field2),
|
|
43648
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."
|
|
43649
43859
|
] }, field2.id);
|
|
43650
43860
|
}
|
|
43651
|
-
return /* @__PURE__ */ (0,
|
|
43861
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)(
|
|
43652
43862
|
"form",
|
|
43653
43863
|
{
|
|
43654
43864
|
className: "flex items-center gap-1",
|
|
@@ -43657,8 +43867,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43657
43867
|
if (name.trim() !== "") void sign(field2);
|
|
43658
43868
|
},
|
|
43659
43869
|
children: [
|
|
43660
|
-
/* @__PURE__ */ (0,
|
|
43661
|
-
|
|
43870
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(
|
|
43871
|
+
import_design_system121.BasicInput,
|
|
43662
43872
|
{
|
|
43663
43873
|
className: "h-8 w-56 text-xs",
|
|
43664
43874
|
"aria-label": `Sign ${fieldName(field2)}`,
|
|
@@ -43667,8 +43877,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43667
43877
|
onChange: (e) => setName(e.target.value)
|
|
43668
43878
|
}
|
|
43669
43879
|
),
|
|
43670
|
-
/* @__PURE__ */ (0,
|
|
43671
|
-
/* @__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: [
|
|
43672
43882
|
"seals version ",
|
|
43673
43883
|
render.template_version,
|
|
43674
43884
|
" \xB7 ",
|
|
@@ -43683,15 +43893,270 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43683
43893
|
] });
|
|
43684
43894
|
}
|
|
43685
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
|
+
|
|
43686
44151
|
// src/DashboardCanvas.tsx
|
|
43687
44152
|
var import_react249 = require("react");
|
|
43688
44153
|
var import_react250 = require("@ai-matrx/records/react");
|
|
43689
44154
|
var import_design_system126 = require("@ai-matrx/design-system");
|
|
43690
44155
|
|
|
43691
44156
|
// src/DashboardGrid.tsx
|
|
43692
|
-
var
|
|
43693
|
-
var
|
|
43694
|
-
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");
|
|
43695
44160
|
var import_design_system123 = require("@ai-matrx/design-system");
|
|
43696
44161
|
|
|
43697
44162
|
// src/dashboardLayout.ts
|
|
@@ -43745,7 +44210,7 @@ function withBlockIds(blocks) {
|
|
|
43745
44210
|
// src/DashboardGrid.tsx
|
|
43746
44211
|
var import_jsx_runtime138 = require("react/jsx-runtime");
|
|
43747
44212
|
var dashboardCollision = (args) => {
|
|
43748
|
-
const under = (0,
|
|
44213
|
+
const under = (0, import_core23.pointerWithin)(args);
|
|
43749
44214
|
if (under.length > 0) return under;
|
|
43750
44215
|
const at = args.collisionRect;
|
|
43751
44216
|
return args.droppableContainers.flatMap((container) => {
|
|
@@ -43755,9 +44220,9 @@ var dashboardCollision = (args) => {
|
|
|
43755
44220
|
}).sort((a, b) => a.data.value - b.data.value);
|
|
43756
44221
|
};
|
|
43757
44222
|
function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
43758
|
-
const sensors = (0,
|
|
43759
|
-
(0,
|
|
43760
|
-
(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 })
|
|
43761
44226
|
);
|
|
43762
44227
|
const ids = items.map((item) => item.id);
|
|
43763
44228
|
function dragEnd(event) {
|
|
@@ -43767,7 +44232,7 @@ function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
|
43767
44232
|
}
|
|
43768
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)) });
|
|
43769
44234
|
if (!arranging) return grid;
|
|
43770
|
-
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 }) });
|
|
43771
44236
|
}
|
|
43772
44237
|
function GridCell2({
|
|
43773
44238
|
item,
|
|
@@ -43775,8 +44240,8 @@ function GridCell2({
|
|
|
43775
44240
|
arranging,
|
|
43776
44241
|
onSpan
|
|
43777
44242
|
}) {
|
|
43778
|
-
const sortable = (0,
|
|
43779
|
-
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;
|
|
43780
44245
|
return /* @__PURE__ */ (0, import_jsx_runtime138.jsxs)(
|
|
43781
44246
|
"div",
|
|
43782
44247
|
{
|
|
@@ -44980,7 +45445,7 @@ var import_react252 = require("@ai-matrx/records/react");
|
|
|
44980
45445
|
|
|
44981
45446
|
// src/question.ts
|
|
44982
45447
|
var import_records59 = require("@ai-matrx/records");
|
|
44983
|
-
var
|
|
45448
|
+
var import_core24 = require("@ai-matrx/records/core");
|
|
44984
45449
|
var QUESTION_OPS = ["count", "sum", "avg", "min", "max"];
|
|
44985
45450
|
var QUESTION_LIMIT_DEFAULT = 25;
|
|
44986
45451
|
var QUESTION_GROUP_CEILING = 200;
|
|
@@ -45232,7 +45697,7 @@ async function askAsOf(client, tableId, spec, fields, measures, keys) {
|
|
|
45232
45697
|
},
|
|
45233
45698
|
{ schema: config.schema ?? "custom" }
|
|
45234
45699
|
);
|
|
45235
|
-
if (response.error) return { ok: false, error: (0,
|
|
45700
|
+
if (response.error) return { ok: false, error: (0, import_core24.mapPgError)(response.error, "askQuestion.asOf") };
|
|
45236
45701
|
const rows2 = Array.isArray(response.data) ? response.data : [];
|
|
45237
45702
|
const bad = rows2.find((r) => Number(r.not_numbers ?? 0) > 0);
|
|
45238
45703
|
if (bad && m.op !== "count") {
|
|
@@ -45303,7 +45768,7 @@ function savedOf(row) {
|
|
|
45303
45768
|
}
|
|
45304
45769
|
async function questionsOf(client, tableId) {
|
|
45305
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 });
|
|
45306
|
-
if (response.error) return { ok: false, error: (0,
|
|
45771
|
+
if (response.error) return { ok: false, error: (0, import_core24.mapPgError)(response.error, "questionsOf") };
|
|
45307
45772
|
const me = client.config.actor?.user_id ?? null;
|
|
45308
45773
|
return {
|
|
45309
45774
|
ok: true,
|
|
@@ -45332,7 +45797,7 @@ async function questionSave(client, args) {
|
|
|
45332
45797
|
if (response.error.code === "23505") {
|
|
45333
45798
|
return { ok: false, error: { code: "already_exists", message: `You already have a question called \u201C${name}\u201D.` } };
|
|
45334
45799
|
}
|
|
45335
|
-
return { ok: false, error: (0,
|
|
45800
|
+
return { ok: false, error: (0, import_core24.mapPgError)(response.error, "questionSave") };
|
|
45336
45801
|
}
|
|
45337
45802
|
return { ok: true, data: response.data ? savedOf(response.data) : null };
|
|
45338
45803
|
}
|
|
@@ -45342,7 +45807,7 @@ async function questionArchive(client, id, version) {
|
|
|
45342
45807
|
{ p_surface_key: QUESTION_SURFACE, p_id: id, ...version !== void 0 ? { p_expected_version: version } : {} },
|
|
45343
45808
|
{ schema: "public" }
|
|
45344
45809
|
);
|
|
45345
|
-
if (response.error) return { ok: false, error: (0,
|
|
45810
|
+
if (response.error) return { ok: false, error: (0, import_core24.mapPgError)(response.error, "questionArchive") };
|
|
45346
45811
|
return { ok: true, data: response.data !== null };
|
|
45347
45812
|
}
|
|
45348
45813
|
|
|
@@ -46201,7 +46666,7 @@ function TableAsk({ tableId, onOpenInQuestions }) {
|
|
|
46201
46666
|
// src/PageScreen.tsx
|
|
46202
46667
|
var import_react255 = require("react");
|
|
46203
46668
|
var import_react256 = require("@ai-matrx/records/react");
|
|
46204
|
-
var
|
|
46669
|
+
var import_core25 = require("@ai-matrx/records/core");
|
|
46205
46670
|
var import_design_system129 = require("@ai-matrx/design-system");
|
|
46206
46671
|
var import_jsx_runtime145 = require("react/jsx-runtime");
|
|
46207
46672
|
var EDIT_LEVELS = /* @__PURE__ */ new Set(["edit_content", "editor", "admin", "owner"]);
|
|
@@ -46617,7 +47082,7 @@ function ListBlockView({
|
|
|
46617
47082
|
...sort.length > 0 ? { sort } : {}
|
|
46618
47083
|
});
|
|
46619
47084
|
const reload = page.reload;
|
|
46620
|
-
(0, import_react255.useEffect)(() => (0,
|
|
47085
|
+
(0, import_react255.useEffect)(() => (0, import_core25.onRecordChanged)(() => reload()), [reload]);
|
|
46621
47086
|
const rows = nobodyIsMe ? [] : page.data?.rows ?? [];
|
|
46622
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);
|
|
46623
47088
|
const firstId = rows[0]?.id ?? null;
|
|
@@ -48152,7 +48617,7 @@ var import_design_system142 = require("@ai-matrx/design-system");
|
|
|
48152
48617
|
var import_text_case19 = require("@ai-matrx/kit/text-case");
|
|
48153
48618
|
|
|
48154
48619
|
// src/viewShare.ts
|
|
48155
|
-
var
|
|
48620
|
+
var import_core26 = require("@ai-matrx/records/core");
|
|
48156
48621
|
var NO_VIEW_REASON = "No saved view yet";
|
|
48157
48622
|
function viewLink(tableId, viewId, origin) {
|
|
48158
48623
|
return `${tableLink(tableId, origin)}?view=${encodeURIComponent(viewId)}`;
|
|
@@ -48188,7 +48653,7 @@ async function downloadViewCsv(args) {
|
|
|
48188
48653
|
const columns = viewColumns(fields, view.presentation);
|
|
48189
48654
|
const rows = await rowsForFile({ client, host, fields: columns, records: read.rows, labels });
|
|
48190
48655
|
const name = fileNameOf(args.tableName) || "records";
|
|
48191
|
-
download(`${name}.csv`, "text/csv;charset=utf-8", (0,
|
|
48656
|
+
download(`${name}.csv`, "text/csv;charset=utf-8", (0, import_core26.exportCsv)(columns, rows));
|
|
48192
48657
|
host.notify?.success(read.cut ? cutWords(read.rows.length) : "CSV downloaded");
|
|
48193
48658
|
}
|
|
48194
48659
|
|