@ai-matrx/records-ui 0.106.0 → 0.107.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.cjs +1389 -875
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -2
- package/dist/index.d.ts +38 -2
- package/dist/index.js +1385 -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,53 @@ 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 || spec.notify.emailRespondent) ? {
|
|
37961
|
+
notify: strip({
|
|
37962
|
+
webhook_url: text(spec.notify.webhookUrl),
|
|
37963
|
+
webhook_automation_id: text(spec.notify.webhookAutomationId),
|
|
37964
|
+
email_respondent: spec.notify.emailRespondent ? true : null,
|
|
37965
|
+
respondent_field: spec.notify.emailRespondent ? text(spec.notify.respondentField) : null
|
|
37966
|
+
})
|
|
37967
|
+
} : {}
|
|
37924
37968
|
};
|
|
37925
37969
|
}
|
|
37970
|
+
function notifyFromDocument(raw) {
|
|
37971
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
37972
|
+
const d = raw;
|
|
37973
|
+
const n = {
|
|
37974
|
+
webhookUrl: text(d["webhook_url"]),
|
|
37975
|
+
webhookAutomationId: text(d["webhook_automation_id"]),
|
|
37976
|
+
emailRespondent: d["email_respondent"] === true ? true : null,
|
|
37977
|
+
respondentField: text(d["respondent_field"])
|
|
37978
|
+
};
|
|
37979
|
+
return n.webhookUrl || n.webhookAutomationId || n.emailRespondent ? n : null;
|
|
37980
|
+
}
|
|
37981
|
+
var bool = (v) => typeof v === "boolean" ? v : null;
|
|
37982
|
+
function answerRefusal(kind, required, value) {
|
|
37983
|
+
const empty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
|
|
37984
|
+
if (empty) return required ? "This field is required" : null;
|
|
37985
|
+
if (typeof value !== "string") return null;
|
|
37986
|
+
const v = value.trim();
|
|
37987
|
+
if (kind === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v)) return "Enter a valid email";
|
|
37988
|
+
if (kind === "phone" && (v.replace(/\D/g, "").length < 7 || /[^\d\s()+.\-x]/i.test(v))) return "Enter a valid phone number";
|
|
37989
|
+
if (kind === "url" && !/^(https?:\/\/)?[^\s.\/]+(\.[^\s.\/]+)+(\/\S*)?$/i.test(v)) return "Enter a valid link";
|
|
37990
|
+
return null;
|
|
37991
|
+
}
|
|
37992
|
+
function pipeAnswers(text2, answers, questions, labelOf) {
|
|
37993
|
+
if (!text2.includes("{{")) return text2;
|
|
37994
|
+
return text2.replace(/\{\{\s*([^{}]+?)\s*\}\}/g, (_all, name) => {
|
|
37995
|
+
const wanted = name.trim().toLowerCase();
|
|
37996
|
+
const q = questions.find((x) => x.key.toLowerCase() === wanted) ?? questions.find((x) => (x.ask ?? "").trim().toLowerCase() === wanted);
|
|
37997
|
+
if (!q) return "";
|
|
37998
|
+
const value = answers[q.key];
|
|
37999
|
+
if (value === void 0 || value === null) return "";
|
|
38000
|
+
const one = (v) => labelOf?.(q.key, v) ?? String(v);
|
|
38001
|
+
return Array.isArray(value) ? value.map(one).join(", ") : one(value);
|
|
38002
|
+
});
|
|
38003
|
+
}
|
|
37926
38004
|
var text = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : null;
|
|
37927
38005
|
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
37928
38006
|
var strip = (doc) => Object.fromEntries(Object.entries(doc).filter(([, v]) => v !== null && v !== void 0));
|
|
@@ -38097,6 +38175,9 @@ function formFromSummary(summary) {
|
|
|
38097
38175
|
thankYou: thankYouFromDocument(presentation["thank_you"]),
|
|
38098
38176
|
look: formLookFromDocument(presentation["look"]),
|
|
38099
38177
|
submitLabel: presentation["submit_label"] ?? null,
|
|
38178
|
+
showOwnerHeader: bool(presentation["show_owner_header"]),
|
|
38179
|
+
autoAdvance: bool(presentation["auto_advance"]),
|
|
38180
|
+
notify: notifyFromDocument(presentation["notify"]),
|
|
38100
38181
|
// A form is public when the store says it is OPEN. A screen never decides
|
|
38101
38182
|
// this from a flag of its own: the link answers or it does not.
|
|
38102
38183
|
isPublic: summary.state === "open",
|
|
@@ -38880,6 +38961,50 @@ function compactTick(value) {
|
|
|
38880
38961
|
if (Math.abs(value) < 1e3 && !Number.isInteger(value)) return String(Math.round(value * 10) / 10);
|
|
38881
38962
|
return (0, import_format10.formatCount)(value, { style: "compact" });
|
|
38882
38963
|
}
|
|
38964
|
+
function wrapTickWords(label, perLine, maxLines = 3) {
|
|
38965
|
+
const words2 = label.split(/\s+/).filter(Boolean).map((w) => w.length > perLine ? `${w.slice(0, perLine - 1)}\u2026` : w);
|
|
38966
|
+
const lines = [];
|
|
38967
|
+
let line = "";
|
|
38968
|
+
for (const word of words2) {
|
|
38969
|
+
const next = line ? `${line} ${word}` : word;
|
|
38970
|
+
if (next.length <= perLine) line = next;
|
|
38971
|
+
else {
|
|
38972
|
+
lines.push(line);
|
|
38973
|
+
line = word;
|
|
38974
|
+
}
|
|
38975
|
+
}
|
|
38976
|
+
if (line) lines.push(line);
|
|
38977
|
+
if (lines.length <= maxLines) return lines;
|
|
38978
|
+
const kept = lines.slice(0, maxLines);
|
|
38979
|
+
const last = kept[maxLines - 1].replace(/…$/, "");
|
|
38980
|
+
kept[maxLines - 1] = `${last.length >= perLine ? last.slice(0, perLine - 1) : last}\u2026`;
|
|
38981
|
+
return kept;
|
|
38982
|
+
}
|
|
38983
|
+
function WrappedTick(props) {
|
|
38984
|
+
const { x = 0, y = 0, payload, across, perLine } = props;
|
|
38985
|
+
const lines = wrapTickWords(String(payload?.value ?? ""), perLine);
|
|
38986
|
+
const lineHeight = 11;
|
|
38987
|
+
return /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(
|
|
38988
|
+
"text",
|
|
38989
|
+
{
|
|
38990
|
+
x,
|
|
38991
|
+
y,
|
|
38992
|
+
fontSize: 10,
|
|
38993
|
+
fill: "currentColor",
|
|
38994
|
+
textAnchor: across ? "end" : "middle",
|
|
38995
|
+
"data-records-chart-tick": "",
|
|
38996
|
+
children: lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(
|
|
38997
|
+
"tspan",
|
|
38998
|
+
{
|
|
38999
|
+
x: across ? x - 4 : x,
|
|
39000
|
+
dy: i === 0 ? across ? -((lines.length - 1) * lineHeight) / 2 + 3 : 10 : lineHeight,
|
|
39001
|
+
children: line
|
|
39002
|
+
},
|
|
39003
|
+
i
|
|
39004
|
+
))
|
|
39005
|
+
}
|
|
39006
|
+
);
|
|
39007
|
+
}
|
|
38883
39008
|
function Drawing({
|
|
38884
39009
|
kind,
|
|
38885
39010
|
points,
|
|
@@ -38985,9 +39110,9 @@ function Drawing({
|
|
|
38985
39110
|
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.CartesianGrid, { vertical: horizontal, horizontal: !horizontal }),
|
|
38986
39111
|
horizontal ? /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(import_jsx_runtime120.Fragment, { children: [
|
|
38987
39112
|
/* @__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:
|
|
39113
|
+
/* @__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
39114
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(import_jsx_runtime120.Fragment, { children: [
|
|
38990
|
-
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.XAxis, { dataKey: "label", ...axis }),
|
|
39115
|
+
/* @__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
39116
|
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_recharts.YAxis, { width: 44, ...axis })
|
|
38992
39117
|
] }),
|
|
38993
39118
|
tooltip,
|
|
@@ -40631,7 +40756,7 @@ function FormLookLogo({ view, size = "md" }) {
|
|
|
40631
40756
|
}
|
|
40632
40757
|
);
|
|
40633
40758
|
}
|
|
40634
|
-
function FormLookFrame({ look, title, children, contained = false, className }) {
|
|
40759
|
+
function FormLookFrame({ look, title, children, contained = false, showOwnerHeader = true, className }) {
|
|
40635
40760
|
const view = formLookView(look, title);
|
|
40636
40761
|
return /* @__PURE__ */ (0, import_jsx_runtime127.jsxs)(
|
|
40637
40762
|
"div",
|
|
@@ -40648,10 +40773,10 @@ function FormLookFrame({ look, title, children, contained = false, className })
|
|
|
40648
40773
|
}
|
|
40649
40774
|
) : null,
|
|
40650
40775
|
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: [
|
|
40776
|
+
showOwnerHeader ? /* @__PURE__ */ (0, import_jsx_runtime127.jsxs)("div", { className: "flex items-center gap-3", "data-records-form-owner": "", children: [
|
|
40652
40777
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)(FormLookLogo, { view }),
|
|
40653
40778
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)("p", { className: "min-w-0 truncate text-base font-semibold tracking-tight text-foreground", children: view.name })
|
|
40654
|
-
] }),
|
|
40779
|
+
] }) : null,
|
|
40655
40780
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
|
|
40656
40781
|
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)("div", { className: "w-full", children }),
|
|
40657
40782
|
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 +41598,14 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
|
41473
41598
|
|
|
41474
41599
|
// src/FormBuilder.tsx
|
|
41475
41600
|
var import_clipboard9 = require("@ai-matrx/kit/clipboard");
|
|
41601
|
+
var import_design_system117 = require("@ai-matrx/design-system");
|
|
41602
|
+
var import_react232 = require("react");
|
|
41603
|
+
var import_core22 = require("@dnd-kit/core");
|
|
41604
|
+
var import_sortable = require("@dnd-kit/sortable");
|
|
41605
|
+
var import_utilities = require("@dnd-kit/utilities");
|
|
41606
|
+
var import_react233 = require("@ai-matrx/records/react");
|
|
41607
|
+
var import_records56 = require("@ai-matrx/records");
|
|
41476
41608
|
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
41609
|
|
|
41482
41610
|
// src/FormFlowEditor.tsx
|
|
41483
41611
|
var import_react229 = require("react");
|
|
@@ -41824,7 +41952,8 @@ function FormStage({
|
|
|
41824
41952
|
className,
|
|
41825
41953
|
connected = false,
|
|
41826
41954
|
onStart,
|
|
41827
|
-
onQuestionShown
|
|
41955
|
+
onQuestionShown,
|
|
41956
|
+
autoAdvance
|
|
41828
41957
|
}) {
|
|
41829
41958
|
const host = useRecordsUi();
|
|
41830
41959
|
const [answers, setAnswers] = (0, import_react230.useState)(() => ({ ...initialAnswers ?? {} }));
|
|
@@ -41837,6 +41966,11 @@ function FormStage({
|
|
|
41837
41966
|
const [ending, setEnding] = (0, import_react230.useState)(null);
|
|
41838
41967
|
const [started, setStarted] = (0, import_react230.useState)(() => !form.welcome || resumed);
|
|
41839
41968
|
const routes = formRoutes(form);
|
|
41969
|
+
const [routeKnown, setRouteKnown] = (0, import_react230.useState)(() => !routes || !whichAsked);
|
|
41970
|
+
const [stopped, setStopped] = (0, import_react230.useState)(null);
|
|
41971
|
+
const answersRef = (0, import_react230.useRef)(answers);
|
|
41972
|
+
answersRef.current = answers;
|
|
41973
|
+
const choose = autoAdvance ?? form.autoAdvance ?? true;
|
|
41840
41974
|
const decoy = (0, import_react230.useRef)("");
|
|
41841
41975
|
const stage = (0, import_react230.useRef)(null);
|
|
41842
41976
|
const questions = (0, import_react230.useMemo)(() => {
|
|
@@ -41872,6 +42006,7 @@ function FormStage({
|
|
|
41872
42006
|
};
|
|
41873
42007
|
}
|
|
41874
42008
|
if (cancelled) return;
|
|
42009
|
+
setRouteKnown(true);
|
|
41875
42010
|
if (!told2.ok) {
|
|
41876
42011
|
setHidden({});
|
|
41877
42012
|
setError(told2.error ?? refusal("not_supported", told2.message, "Answer every question shown; none were skipped."));
|
|
@@ -41893,7 +42028,7 @@ function FormStage({
|
|
|
41893
42028
|
) : null
|
|
41894
42029
|
);
|
|
41895
42030
|
})();
|
|
41896
|
-
}, 250);
|
|
42031
|
+
}, routeKnown ? 250 : 0);
|
|
41897
42032
|
return () => {
|
|
41898
42033
|
cancelled = true;
|
|
41899
42034
|
clearTimeout(timer);
|
|
@@ -41928,11 +42063,24 @@ function FormStage({
|
|
|
41928
42063
|
const v = answers[q.key];
|
|
41929
42064
|
return v !== void 0 && v !== null && v !== "";
|
|
41930
42065
|
}).length;
|
|
41931
|
-
const
|
|
41932
|
-
|
|
41933
|
-
|
|
41934
|
-
|
|
42066
|
+
const refusalFor = (q, values = answersRef.current) => answerRefusal(q.field ? String(editorKindFor(q.field)) : "text", q.required, values[q.key]);
|
|
42067
|
+
const refuse = (q, say2) => {
|
|
42068
|
+
setStopped({ key: q.key, say: say2 });
|
|
42069
|
+
const i = live.findIndex((x) => x.key === q.key);
|
|
42070
|
+
if (oneAtATime && i >= 0 && i !== index) setAt(i);
|
|
42071
|
+
window.setTimeout(() => {
|
|
42072
|
+
const box = stage.current?.querySelector(`[data-records-form-question="${CSS.escape(q.key)}"]`);
|
|
42073
|
+
box?.querySelector("[role='radio'][aria-checked='true'], input:not([tabindex='-1']), textarea, select, [role='combobox'], [role='radio'], [role='checkbox']")?.focus();
|
|
42074
|
+
}, 0);
|
|
42075
|
+
};
|
|
41935
42076
|
const submit = (0, import_react230.useCallback)(async () => {
|
|
42077
|
+
for (const q of live) {
|
|
42078
|
+
const say2 = refusalFor(q);
|
|
42079
|
+
if (say2) {
|
|
42080
|
+
refuse(q, say2);
|
|
42081
|
+
return;
|
|
42082
|
+
}
|
|
42083
|
+
}
|
|
41936
42084
|
if (preview) {
|
|
41937
42085
|
setDone("preview");
|
|
41938
42086
|
return;
|
|
@@ -41964,20 +42112,28 @@ function FormStage({
|
|
|
41964
42112
|
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
41965
42113
|
async function advance() {
|
|
41966
42114
|
if (!oneAtATime) return;
|
|
42115
|
+
if (current) {
|
|
42116
|
+
const say2 = refusalFor(current);
|
|
42117
|
+
if (say2) {
|
|
42118
|
+
refuse(current, say2);
|
|
42119
|
+
return;
|
|
42120
|
+
}
|
|
42121
|
+
}
|
|
42122
|
+
setStopped(null);
|
|
41967
42123
|
if (!routes || !whichAsked || !current) {
|
|
41968
42124
|
if (index < live.length - 1) setAt(index + 1);
|
|
41969
|
-
else
|
|
42125
|
+
else void submit();
|
|
41970
42126
|
return;
|
|
41971
42127
|
}
|
|
41972
42128
|
let told2;
|
|
41973
42129
|
try {
|
|
41974
|
-
told2 = await whichAsked(
|
|
42130
|
+
told2 = await whichAsked(answersRef.current);
|
|
41975
42131
|
} catch {
|
|
41976
42132
|
told2 = { ok: false, message: "This form could not work out which question comes next, so the next one is shown." };
|
|
41977
42133
|
}
|
|
41978
42134
|
if (!told2.ok) {
|
|
41979
42135
|
if (index < live.length - 1) setAt(index + 1);
|
|
41980
|
-
else
|
|
42136
|
+
else void submit();
|
|
41981
42137
|
return;
|
|
41982
42138
|
}
|
|
41983
42139
|
const next = {};
|
|
@@ -41990,13 +42146,18 @@ function FormStage({
|
|
|
41990
42146
|
setAt(here + 1);
|
|
41991
42147
|
return;
|
|
41992
42148
|
}
|
|
41993
|
-
const stillMissing = onRoute.
|
|
41994
|
-
|
|
41995
|
-
|
|
41996
|
-
});
|
|
41997
|
-
if (stillMissing.length === 0) void submit();
|
|
42149
|
+
const stillMissing = onRoute.find((q) => refusalFor(q) !== null);
|
|
42150
|
+
if (stillMissing) refuse(stillMissing, refusalFor(stillMissing));
|
|
42151
|
+
else void submit();
|
|
41998
42152
|
}
|
|
42153
|
+
const advanceRef = (0, import_react230.useRef)(advance);
|
|
42154
|
+
advanceRef.current = advance;
|
|
42155
|
+
const chosen = (0, import_react230.useCallback)(() => {
|
|
42156
|
+
if (!oneAtATime || !choose) return;
|
|
42157
|
+
window.setTimeout(() => void advanceRef.current(), 450);
|
|
42158
|
+
}, [oneAtATime, choose]);
|
|
41999
42159
|
function retreat() {
|
|
42160
|
+
setStopped(null);
|
|
42000
42161
|
if (oneAtATime && index > 0) setAt(index - 1);
|
|
42001
42162
|
}
|
|
42002
42163
|
function onKeyDown(event) {
|
|
@@ -42012,12 +42173,11 @@ function FormStage({
|
|
|
42012
42173
|
else advance();
|
|
42013
42174
|
}
|
|
42014
42175
|
(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.
|
|
42176
|
+
const input = stage.current?.querySelector("[role='radio'][aria-checked='true']") ?? stage.current?.querySelector(
|
|
42017
42177
|
"input:not([tabindex='-1']), textarea, select, [role='combobox'], [role='radio'], [role='checkbox']"
|
|
42018
42178
|
);
|
|
42019
42179
|
input?.focus();
|
|
42020
|
-
}, [index, done]);
|
|
42180
|
+
}, [index, done, started]);
|
|
42021
42181
|
const startTold = (0, import_react230.useRef)(false);
|
|
42022
42182
|
(0, import_react230.useEffect)(() => {
|
|
42023
42183
|
if (startTold.current || !onStart) return;
|
|
@@ -42036,6 +42196,10 @@ function FormStage({
|
|
|
42036
42196
|
}
|
|
42037
42197
|
}, [current, live, oneAtATime, onQuestionShown, started, done]);
|
|
42038
42198
|
const theme = (0, import_design_system116.formThemeClasses)(form.theme ?? null);
|
|
42199
|
+
const pipe = (text2) => pipeAnswers(text2, answers, questions, (key, v) => {
|
|
42200
|
+
const f = questions.find((q) => q.key === key)?.field;
|
|
42201
|
+
return f ? publicChoicesOf(f).find((c) => c.key === String(v))?.label ?? null : null;
|
|
42202
|
+
});
|
|
42039
42203
|
if (done) {
|
|
42040
42204
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42041
42205
|
ThankYou,
|
|
@@ -42044,6 +42208,7 @@ function FormStage({
|
|
|
42044
42208
|
done,
|
|
42045
42209
|
ending: (form.endings ?? []).find((e) => e.id === ending) ?? null,
|
|
42046
42210
|
buttonClass: theme.buttonClass,
|
|
42211
|
+
pipe,
|
|
42047
42212
|
className: (0, import_design_system116.cn)("mx-auto max-w-xl py-10", centred && "text-center", className)
|
|
42048
42213
|
}
|
|
42049
42214
|
);
|
|
@@ -42055,14 +42220,14 @@ function FormStage({
|
|
|
42055
42220
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42056
42221
|
"section",
|
|
42057
42222
|
{
|
|
42058
|
-
className: (0, import_design_system116.cn)("mx-auto flex w-full max-w-
|
|
42223
|
+
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
42224
|
style: theme.surfaceStyle,
|
|
42060
42225
|
"data-records-form-welcome": "",
|
|
42061
42226
|
children: [
|
|
42062
42227
|
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: "
|
|
42228
|
+
w.title ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("h2", { className: "text-2xl font-semibold sm:text-3xl", children: w.title }) : null,
|
|
42229
|
+
w.body ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "text-base text-muted-foreground", children: authored(host, w.body, "form-intro") }) : null,
|
|
42230
|
+
/* @__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
42231
|
]
|
|
42067
42232
|
}
|
|
42068
42233
|
);
|
|
@@ -42071,7 +42236,15 @@ function FormStage({
|
|
|
42071
42236
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42072
42237
|
"section",
|
|
42073
42238
|
{
|
|
42074
|
-
className: (0, import_design_system116.cn)(
|
|
42239
|
+
className: (0, import_design_system116.cn)(
|
|
42240
|
+
"mx-auto flex w-full max-w-2xl flex-col gap-4 py-4",
|
|
42241
|
+
// TYPEFORM'S SIZE (TYPEFORM-2): one question at a time fills the screen, centred in it.
|
|
42242
|
+
oneAtATime && "min-h-[min(70dvh,640px)]",
|
|
42243
|
+
theme.surface,
|
|
42244
|
+
theme.background !== "plain" && "rounded-lg px-5 sm:px-8",
|
|
42245
|
+
centred && "text-center",
|
|
42246
|
+
className
|
|
42247
|
+
),
|
|
42075
42248
|
style: theme.surfaceStyle,
|
|
42076
42249
|
"data-records-form-font": theme.font,
|
|
42077
42250
|
onKeyDown,
|
|
@@ -42081,20 +42254,41 @@ function FormStage({
|
|
|
42081
42254
|
import_design_system116.Progress,
|
|
42082
42255
|
{
|
|
42083
42256
|
value: live.length === 0 ? 0 : (oneAtATime ? index + 1 : answered) / live.length * 100,
|
|
42084
|
-
className: "h-1 flex-1"
|
|
42257
|
+
className: "h-1 flex-1",
|
|
42258
|
+
"aria-label": "Form progress",
|
|
42259
|
+
...routeKnown ? { "aria-valuetext": oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` } : {}
|
|
42085
42260
|
}
|
|
42086
42261
|
),
|
|
42087
|
-
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "tabular-nums", children: oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` })
|
|
42262
|
+
/* @__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
42263
|
] }),
|
|
42089
|
-
signedInPublic ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42090
|
-
|
|
42091
|
-
|
|
42092
|
-
|
|
42093
|
-
|
|
42094
|
-
|
|
42095
|
-
|
|
42096
|
-
|
|
42097
|
-
|
|
42264
|
+
signedInPublic ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42265
|
+
"p",
|
|
42266
|
+
{
|
|
42267
|
+
className: "w-fit rounded border border-dashed px-2 py-0.5 text-left text-xs text-muted-foreground",
|
|
42268
|
+
title: "The record will carry your name. Strangers answer at the public link.",
|
|
42269
|
+
children: "Signed in \xB7 saved as you"
|
|
42270
|
+
}
|
|
42271
|
+
) : null,
|
|
42272
|
+
unanswerable ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42273
|
+
"p",
|
|
42274
|
+
{
|
|
42275
|
+
className: "w-fit rounded border border-dashed px-2 py-0.5 text-left text-xs text-muted-foreground",
|
|
42276
|
+
title: "Nothing here can work out which conditional questions apply, so all of them are shown.",
|
|
42277
|
+
children: "Showing every question"
|
|
42278
|
+
}
|
|
42279
|
+
) : null,
|
|
42280
|
+
unresolved.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42281
|
+
"p",
|
|
42282
|
+
{
|
|
42283
|
+
className: "w-fit rounded border border-destructive/40 px-2 py-0.5 text-left text-xs text-destructive",
|
|
42284
|
+
title: "The table has no such field. Add the field, or take the question out.",
|
|
42285
|
+
children: [
|
|
42286
|
+
"Missing field: ",
|
|
42287
|
+
unresolved.map((q) => q.ask).join(", ")
|
|
42288
|
+
]
|
|
42289
|
+
}
|
|
42290
|
+
) : null,
|
|
42291
|
+
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
42292
|
error ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(RefusalNotice, { error, className: "text-left" }) : null,
|
|
42099
42293
|
refusal4 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42100
42294
|
RefusalNotice,
|
|
@@ -42116,14 +42310,20 @@ function FormStage({
|
|
|
42116
42310
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42117
42311
|
Question,
|
|
42118
42312
|
{
|
|
42119
|
-
question: q,
|
|
42313
|
+
question: { ...q, ask: pipe(q.ask), help: q.help ? pipe(q.help) : null },
|
|
42120
42314
|
value: answers[q.key],
|
|
42121
|
-
onChange: (v) =>
|
|
42315
|
+
onChange: (v) => {
|
|
42316
|
+
if (stopped?.key === q.key) setStopped(null);
|
|
42317
|
+
setAnswers((prev) => ({ ...prev, [q.key]: v }));
|
|
42318
|
+
},
|
|
42319
|
+
onChosen: chosen,
|
|
42320
|
+
refused: stopped?.key === q.key ? stopped.say : null,
|
|
42321
|
+
large: oneAtATime,
|
|
42122
42322
|
upload: host.upload
|
|
42123
42323
|
},
|
|
42124
42324
|
q.key
|
|
42125
42325
|
)),
|
|
42126
|
-
live.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "text-xs text-muted-foreground",
|
|
42326
|
+
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
42327
|
] }),
|
|
42128
42328
|
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
42329
|
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("label", { htmlFor: `hp-${honeypotKey}`, children: "Leave this field empty" }),
|
|
@@ -42142,16 +42342,14 @@ function FormStage({
|
|
|
42142
42342
|
}
|
|
42143
42343
|
)
|
|
42144
42344
|
] }) : 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."
|
|
42345
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("footer", { className: (0, import_design_system116.cn)("flex flex-wrap items-center gap-3", centred && "justify-center"), children: [
|
|
42346
|
+
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,
|
|
42347
|
+
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" }),
|
|
42348
|
+
oneAtATime ? /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("span", { className: "hidden text-xs text-muted-foreground sm:inline", "data-records-form-enter-hint": "", children: [
|
|
42349
|
+
"press ",
|
|
42350
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("strong", { className: "font-semibold text-foreground", children: "Enter \u21B5" })
|
|
42153
42351
|
] }) : null,
|
|
42154
|
-
preview ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-xs text-muted-foreground",
|
|
42352
|
+
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
42353
|
] })
|
|
42156
42354
|
]
|
|
42157
42355
|
}
|
|
@@ -42163,12 +42361,15 @@ function ThankYou({
|
|
|
42163
42361
|
done,
|
|
42164
42362
|
className,
|
|
42165
42363
|
ending,
|
|
42166
|
-
buttonClass
|
|
42364
|
+
buttonClass,
|
|
42365
|
+
pipe = (t) => t
|
|
42167
42366
|
}) {
|
|
42168
42367
|
const host = useRecordsUi();
|
|
42169
42368
|
const redirect = (ending ? ending.redirectUrl?.trim() : form.thankYou?.redirectUrl?.trim()) || null;
|
|
42170
|
-
const
|
|
42171
|
-
const
|
|
42369
|
+
const rawTitle = ending ? ending.title : form.thankYou?.title;
|
|
42370
|
+
const rawBody = ending ? ending.body : form.thankYou?.body;
|
|
42371
|
+
const title = rawTitle ? pipe(rawTitle) : rawTitle;
|
|
42372
|
+
const body = rawBody ? pipe(rawBody) : rawBody;
|
|
42172
42373
|
const going = redirect !== null && done !== "preview";
|
|
42173
42374
|
(0, import_react230.useEffect)(() => {
|
|
42174
42375
|
if (!going || !redirect) return;
|
|
@@ -42185,8 +42386,8 @@ function ThankYou({
|
|
|
42185
42386
|
}
|
|
42186
42387
|
),
|
|
42187
42388
|
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-
|
|
42389
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("h2", { className: "text-2xl font-semibold", "data-records-form-ending": ending?.id ?? "", children: title || "Thank you" }),
|
|
42390
|
+
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
42391
|
going ? /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("p", { className: "mt-4 flex flex-wrap items-center gap-3 text-sm", children: [
|
|
42191
42392
|
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42192
42393
|
"a",
|
|
@@ -42199,11 +42400,11 @@ function ThankYou({
|
|
|
42199
42400
|
),
|
|
42200
42401
|
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-muted-foreground", children: "Taking you there\u2026" })
|
|
42201
42402
|
] }) : null,
|
|
42202
|
-
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("p", { className: "mt-3 text-xs text-muted-foreground", children: done === "preview" ? redirect ? `
|
|
42403
|
+
/* @__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
42404
|
// SAID TO THE PERSON WHO ANSWERED (lane HANDOVER, 2026-09-29): "Saved to Form 1, with
|
|
42204
42405
|
// this form stamped on it" was the owner's bookkeeping read out to a front-desk member
|
|
42205
42406
|
// and to strangers. Where it landed is the owner's to see on the table.
|
|
42206
|
-
"
|
|
42407
|
+
"Answers received"
|
|
42207
42408
|
) : done })
|
|
42208
42409
|
] });
|
|
42209
42410
|
}
|
|
@@ -42211,6 +42412,9 @@ function Question({
|
|
|
42211
42412
|
question,
|
|
42212
42413
|
value,
|
|
42213
42414
|
onChange,
|
|
42415
|
+
onChosen,
|
|
42416
|
+
refused,
|
|
42417
|
+
large,
|
|
42214
42418
|
upload
|
|
42215
42419
|
}) {
|
|
42216
42420
|
const host = useRecordsUi();
|
|
@@ -42218,297 +42422,62 @@ function Question({
|
|
|
42218
42422
|
const field2 = question.field;
|
|
42219
42423
|
if (!field2) return null;
|
|
42220
42424
|
const id = `form-${field2.key}`;
|
|
42425
|
+
const sayId = `${id}-refused`;
|
|
42221
42426
|
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
|
-
}
|
|
42427
|
+
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
|
|
42428
|
+
"div",
|
|
42429
|
+
{
|
|
42430
|
+
className: (0, import_design_system116.cn)(
|
|
42431
|
+
"flex flex-col",
|
|
42432
|
+
large ? "gap-3 [&_input]:text-base [&_textarea]:text-base [&_input]:min-h-12" : "gap-1.5 [&_input]:text-base [&_textarea]:text-base"
|
|
42246
42433
|
),
|
|
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)(
|
|
42434
|
+
"data-records-form-question": field2.key,
|
|
42435
|
+
"aria-describedby": refused ? sayId : void 0,
|
|
42436
|
+
children: [
|
|
42437
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsxs)("div", { className: "flex flex-wrap items-baseline gap-x-2", children: [
|
|
42438
|
+
/* @__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 }),
|
|
42439
|
+
question.required ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-sm text-foreground/80", children: "required" }) : null,
|
|
42440
|
+
field2.unit ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { className: "text-sm text-foreground/70", children: field2.unit }) : null
|
|
42441
|
+
] }),
|
|
42442
|
+
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,
|
|
42443
|
+
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: [
|
|
42444
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
|
|
42494
42445
|
"input",
|
|
42495
42446
|
{
|
|
42496
|
-
|
|
42497
|
-
type: "
|
|
42498
|
-
|
|
42499
|
-
|
|
42447
|
+
id,
|
|
42448
|
+
type: "file",
|
|
42449
|
+
className: "text-sm",
|
|
42450
|
+
onChange: (e) => {
|
|
42451
|
+
const file = e.target.files?.[0];
|
|
42452
|
+
if (!file) return;
|
|
42453
|
+
setUploadError(null);
|
|
42454
|
+
void upload?.(file).then((result) => {
|
|
42455
|
+
if (!result) return;
|
|
42456
|
+
if (result.ok) onChange(result.fileId);
|
|
42457
|
+
else
|
|
42458
|
+
setUploadError(
|
|
42459
|
+
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
42460
|
+
);
|
|
42461
|
+
});
|
|
42462
|
+
}
|
|
42500
42463
|
}
|
|
42501
42464
|
),
|
|
42502
|
-
/* @__PURE__ */ (0,
|
|
42503
|
-
|
|
42504
|
-
|
|
42505
|
-
|
|
42506
|
-
|
|
42507
|
-
|
|
42465
|
+
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(RefusalLine, { error: uploadError }) : null
|
|
42466
|
+
] }) : (
|
|
42467
|
+
// A choice is laid out as lettered rows in the form's colour (`ChoiceAnswer`).
|
|
42468
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(FieldControl, { field: field2, value, onChange, id, asForm: true, onChosen })
|
|
42469
|
+
),
|
|
42470
|
+
/* @__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: [
|
|
42471
|
+
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)("span", { "aria-hidden": "true", children: "!" }),
|
|
42472
|
+
refused
|
|
42473
|
+
] }) : null })
|
|
42474
|
+
]
|
|
42475
|
+
}
|
|
42476
|
+
);
|
|
42508
42477
|
}
|
|
42509
42478
|
|
|
42510
42479
|
// src/FormBuilder.tsx
|
|
42511
|
-
var
|
|
42480
|
+
var import_jsx_runtime133 = require("react/jsx-runtime");
|
|
42512
42481
|
function freshFormName(base, taken) {
|
|
42513
42482
|
const names = new Set(taken.map((n) => n.trim().toLowerCase()));
|
|
42514
42483
|
if (!names.has(base.toLowerCase())) return base;
|
|
@@ -42525,7 +42494,21 @@ function isLookRefusal(error) {
|
|
|
42525
42494
|
return Boolean(error && LOOK_REFUSAL.test(refusalSentence(error)));
|
|
42526
42495
|
}
|
|
42527
42496
|
var KEPT_DRAFT_PREFIX = "records-ui:form-draft:";
|
|
42528
|
-
var SPEC_KEYS = [
|
|
42497
|
+
var SPEC_KEYS = [
|
|
42498
|
+
"name",
|
|
42499
|
+
"intro",
|
|
42500
|
+
"questions",
|
|
42501
|
+
"flow",
|
|
42502
|
+
"theme",
|
|
42503
|
+
"look",
|
|
42504
|
+
"thankYou",
|
|
42505
|
+
"submitLabel",
|
|
42506
|
+
"welcome",
|
|
42507
|
+
"endings",
|
|
42508
|
+
"hiddenFields",
|
|
42509
|
+
"showOwnerHeader",
|
|
42510
|
+
"autoAdvance"
|
|
42511
|
+
];
|
|
42529
42512
|
function readKeptDraft(formId) {
|
|
42530
42513
|
try {
|
|
42531
42514
|
const raw = window.localStorage.getItem(KEPT_DRAFT_PREFIX + formId);
|
|
@@ -42556,31 +42539,32 @@ function withKept(stored, spec) {
|
|
|
42556
42539
|
return out;
|
|
42557
42540
|
}
|
|
42558
42541
|
function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne, createOnMount, className }) {
|
|
42559
|
-
const client = (0,
|
|
42542
|
+
const client = (0, import_react233.useRecordsClient)();
|
|
42560
42543
|
const host = useRecordsUi();
|
|
42561
42544
|
const claimSeed = useSeedGuard();
|
|
42562
|
-
const table = (0,
|
|
42545
|
+
const table = (0, import_react233.useTable)(tableId);
|
|
42563
42546
|
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 [
|
|
42578
|
-
const [
|
|
42579
|
-
const [
|
|
42580
|
-
const
|
|
42581
|
-
const
|
|
42547
|
+
const fields = (0, import_react233.useFields)(tableId);
|
|
42548
|
+
const [forms, setForms] = (0, import_react232.useState)(null);
|
|
42549
|
+
const [error, setError] = (0, import_react232.useState)(null);
|
|
42550
|
+
const [refused, setRefused] = (0, import_react232.useState)(null);
|
|
42551
|
+
const [activeId, setActiveId] = (0, import_react232.useState)(activeFormId ?? null);
|
|
42552
|
+
const [draft, setDraft] = (0, import_react232.useState)(null);
|
|
42553
|
+
const [saving, setSaving] = (0, import_react232.useState)(false);
|
|
42554
|
+
const [saved, setSaved] = (0, import_react232.useState)(null);
|
|
42555
|
+
const [publishing, setPublishing] = (0, import_react232.useState)(false);
|
|
42556
|
+
const [copied, setCopied] = (0, import_react232.useState)(false);
|
|
42557
|
+
const [shownUrl, setShownUrl] = (0, import_react232.useState)(null);
|
|
42558
|
+
const [lookOptions, setLookOptions] = (0, import_react232.useState)(null);
|
|
42559
|
+
const [lookUnread, setLookUnread] = (0, import_react232.useState)(null);
|
|
42560
|
+
const [orgKnobs, setOrgKnobs] = (0, import_react232.useState)(null);
|
|
42561
|
+
const [restoredAt, setRestoredAt] = (0, import_react232.useState)(null);
|
|
42562
|
+
const [showResults, setShowResults] = (0, import_react232.useState)(false);
|
|
42563
|
+
const [adding, setAdding] = (0, import_react232.useState)(false);
|
|
42564
|
+
const keysBeforeAdd = (0, import_react232.useRef)(null);
|
|
42565
|
+
const [makeOnMount, setMakeOnMount] = (0, import_react232.useState)("waiting");
|
|
42582
42566
|
const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
42583
|
-
const load = (0,
|
|
42567
|
+
const load = (0, import_react232.useCallback)(async () => {
|
|
42584
42568
|
const answered = await client.forms({ table_id: tableId });
|
|
42585
42569
|
if (!answered.ok) {
|
|
42586
42570
|
setError(answered.error);
|
|
@@ -42607,10 +42591,19 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42607
42591
|
setError(null);
|
|
42608
42592
|
setForms(mine);
|
|
42609
42593
|
}, [client, tableId, seed, claimSeed]);
|
|
42610
|
-
(0,
|
|
42594
|
+
(0, import_react232.useEffect)(() => {
|
|
42611
42595
|
void load();
|
|
42612
42596
|
}, [load]);
|
|
42613
|
-
(0,
|
|
42597
|
+
(0, import_react232.useEffect)(() => {
|
|
42598
|
+
let cancelled = false;
|
|
42599
|
+
Promise.resolve().then(() => client.formKnobs()).then((r) => {
|
|
42600
|
+
if (!cancelled && r.ok) setOrgKnobs(r.data);
|
|
42601
|
+
}).catch(() => void 0);
|
|
42602
|
+
return () => {
|
|
42603
|
+
cancelled = true;
|
|
42604
|
+
};
|
|
42605
|
+
}, [client]);
|
|
42606
|
+
(0, import_react232.useEffect)(() => {
|
|
42614
42607
|
if (!forms || forms.length === 0) return;
|
|
42615
42608
|
if (createOnMount && makeOnMount !== "made") return;
|
|
42616
42609
|
const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
|
|
@@ -42627,14 +42620,14 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42627
42620
|
}
|
|
42628
42621
|
onActiveForm?.(chosen);
|
|
42629
42622
|
}, [forms, activeFormId, makeOnMount]);
|
|
42630
|
-
const byKey = (0,
|
|
42631
|
-
const conditionFields = (0,
|
|
42623
|
+
const byKey = (0, import_react232.useMemo)(() => new Map((fields.data ?? []).map((f) => [f.key, f])), [fields.data]);
|
|
42624
|
+
const conditionFields = (0, import_react232.useMemo)(
|
|
42632
42625
|
() => (fields.data ?? []).map((f) => ({ id: String(f.id), key: f.key, label: f.label || f.key, field: f })),
|
|
42633
42626
|
[fields.data]
|
|
42634
42627
|
);
|
|
42635
|
-
const stored = (0,
|
|
42628
|
+
const stored = (0, import_react232.useMemo)(() => draft ? (forms ?? []).find((f) => f.id === draft.id) ?? null : null, [draft, forms]);
|
|
42636
42629
|
const dirty = Boolean(draft && stored && !sameForm(draft, stored));
|
|
42637
|
-
(0,
|
|
42630
|
+
(0, import_react232.useEffect)(() => {
|
|
42638
42631
|
if (!draft || !stored) return;
|
|
42639
42632
|
const timer = window.setTimeout(() => {
|
|
42640
42633
|
if (sameForm(draft, stored)) dropKeptDraft(draft.id);
|
|
@@ -42643,7 +42636,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42643
42636
|
return () => window.clearTimeout(timer);
|
|
42644
42637
|
}, [draft, stored]);
|
|
42645
42638
|
const lookKey = draft ? JSON.stringify(formLookDocument(draft.look ?? null)) : null;
|
|
42646
|
-
(0,
|
|
42639
|
+
(0, import_react232.useEffect)(() => {
|
|
42647
42640
|
if (lookKey === null) return;
|
|
42648
42641
|
let cancelled = false;
|
|
42649
42642
|
const timer = window.setTimeout(() => {
|
|
@@ -42660,7 +42653,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42660
42653
|
window.clearTimeout(timer);
|
|
42661
42654
|
};
|
|
42662
42655
|
}, [client, lookKey]);
|
|
42663
|
-
(0,
|
|
42656
|
+
(0, import_react232.useEffect)(() => {
|
|
42664
42657
|
const before = keysBeforeAdd.current;
|
|
42665
42658
|
if (!before || !fields.data) return;
|
|
42666
42659
|
const made = askableFields(fields.data).filter((f) => !before.has(f.key));
|
|
@@ -42699,7 +42692,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42699
42692
|
}
|
|
42700
42693
|
dropKeptDraft(draft.id);
|
|
42701
42694
|
setRestoredAt(null);
|
|
42702
|
-
setSaved("Saved
|
|
42695
|
+
setSaved("Saved");
|
|
42703
42696
|
await load();
|
|
42704
42697
|
return true;
|
|
42705
42698
|
}
|
|
@@ -42738,7 +42731,22 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42738
42731
|
setRefused(written.error);
|
|
42739
42732
|
return;
|
|
42740
42733
|
}
|
|
42741
|
-
setSaved(
|
|
42734
|
+
setSaved("Saved");
|
|
42735
|
+
await load();
|
|
42736
|
+
}
|
|
42737
|
+
async function chooseWebhook(notify) {
|
|
42738
|
+
if (!draft) return;
|
|
42739
|
+
const next = { ...draft, notify };
|
|
42740
|
+
setDraft(next);
|
|
42741
|
+
setSaving(true);
|
|
42742
|
+
setRefused(null);
|
|
42743
|
+
const written = await client.formDeclare(formDeclareArgs(next));
|
|
42744
|
+
setSaving(false);
|
|
42745
|
+
if (!written.ok) {
|
|
42746
|
+
setRefused(written.error);
|
|
42747
|
+
return;
|
|
42748
|
+
}
|
|
42749
|
+
setSaved("Saved");
|
|
42742
42750
|
await load();
|
|
42743
42751
|
}
|
|
42744
42752
|
async function create(name) {
|
|
@@ -42755,15 +42763,15 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42755
42763
|
await load();
|
|
42756
42764
|
return true;
|
|
42757
42765
|
}
|
|
42758
|
-
const [started, setStarted] = (0,
|
|
42759
|
-
(0,
|
|
42766
|
+
const [started, setStarted] = (0, import_react232.useState)(false);
|
|
42767
|
+
(0, import_react232.useEffect)(() => {
|
|
42760
42768
|
if (!startWithOne || started || forms === null || forms.length > 0) return;
|
|
42761
42769
|
if (askableFields(fields.data ?? []).length === 0) return;
|
|
42762
42770
|
if (!table.data && !table.error) return;
|
|
42763
42771
|
setStarted(true);
|
|
42764
42772
|
void create(table.data?.name?.trim() ? `${table.data.name.trim()} form` : "Form 1");
|
|
42765
42773
|
}, [startWithOne, started, forms, fields.data, create, table.data, table.error]);
|
|
42766
|
-
(0,
|
|
42774
|
+
(0, import_react232.useEffect)(() => {
|
|
42767
42775
|
if (!createOnMount || makeOnMount !== "waiting" || forms === null) return;
|
|
42768
42776
|
if (!table.data && !table.error) return;
|
|
42769
42777
|
if (askableFields(fields.data ?? []).length === 0) {
|
|
@@ -42776,396 +42784,325 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
|
|
|
42776
42784
|
(made) => setMakeOnMount(made ? "made" : "not-made")
|
|
42777
42785
|
);
|
|
42778
42786
|
}, [createOnMount, makeOnMount, forms, fields.data, create, table.data, table.error]);
|
|
42779
|
-
if (fields.error) return /* @__PURE__ */ (0,
|
|
42787
|
+
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(RefusalNotice, { error: fields.error, className });
|
|
42780
42788
|
if (error) {
|
|
42781
|
-
return /* @__PURE__ */ (0,
|
|
42789
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42782
42790
|
RefusalNotice,
|
|
42783
42791
|
{
|
|
42784
42792
|
error,
|
|
42785
42793
|
className,
|
|
42786
|
-
actions: /* @__PURE__ */ (0,
|
|
42794
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
42787
42795
|
}
|
|
42788
42796
|
);
|
|
42789
42797
|
}
|
|
42790
|
-
if (fields.loading || forms === null) return /* @__PURE__ */ (0,
|
|
42798
|
+
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
42799
|
if (createOnMount && (makeOnMount === "waiting" || makeOnMount === "making") && !refused) {
|
|
42792
|
-
return /* @__PURE__ */ (0,
|
|
42800
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Skeleton, { className: (0, import_design_system118.cn)("h-64 w-full", className) });
|
|
42793
42801
|
}
|
|
42794
42802
|
if (!rights.admin) {
|
|
42795
|
-
return /* @__PURE__ */ (0,
|
|
42803
|
+
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
42804
|
}
|
|
42797
|
-
return
|
|
42798
|
-
|
|
42799
|
-
|
|
42800
|
-
|
|
42801
|
-
|
|
42802
|
-
|
|
42803
|
-
|
|
42804
|
-
|
|
42805
|
-
|
|
42806
|
-
|
|
42807
|
-
|
|
42808
|
-
|
|
42805
|
+
return (
|
|
42806
|
+
// THE CONTAINER IS THE PARENT (TYPEFORM-2): a container query asks an ANCESTOR, so `@3xl:` on the
|
|
42807
|
+
// element that is itself `@container` never fired and the preview sat 3,000 px below the editor.
|
|
42808
|
+
/* @__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: [
|
|
42809
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
|
|
42810
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Forms", children: [
|
|
42811
|
+
forms.map((form) => /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42812
|
+
import_design_system118.Button,
|
|
42813
|
+
{
|
|
42814
|
+
size: "sm",
|
|
42815
|
+
variant: form.id === activeId ? "secondary" : "ghost",
|
|
42816
|
+
"aria-pressed": form.id === activeId,
|
|
42817
|
+
onClick: () => {
|
|
42818
|
+
setActiveId(form.id);
|
|
42819
|
+
setDraft(form);
|
|
42820
|
+
},
|
|
42821
|
+
children: form.name
|
|
42809
42822
|
},
|
|
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"
|
|
42823
|
+
form.id
|
|
42824
|
+
)),
|
|
42825
|
+
(fields.data ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42826
|
+
import_design_system118.Button,
|
|
42827
|
+
{
|
|
42828
|
+
size: "sm",
|
|
42829
|
+
variant: "ghost",
|
|
42830
|
+
onClick: () => void create(
|
|
42831
|
+
freshFormName(
|
|
42832
|
+
table.data?.name?.trim() ? `${table.data.name.trim()} form` : "Form",
|
|
42833
|
+
forms.map((f) => f.name)
|
|
42834
|
+
)
|
|
42835
|
+
),
|
|
42836
|
+
children: "New form"
|
|
42837
|
+
}
|
|
42838
|
+
) : null,
|
|
42839
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "ml-auto flex items-center gap-2", children: [
|
|
42840
|
+
saved ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs text-muted-foreground", children: saved }) : null,
|
|
42841
|
+
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,
|
|
42842
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
42843
|
+
] })
|
|
42855
42844
|
] }),
|
|
42856
|
-
/* @__PURE__ */ (0,
|
|
42857
|
-
|
|
42845
|
+
refused && !isLookRefusal(refused) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42846
|
+
RefusalNotice,
|
|
42858
42847
|
{
|
|
42859
|
-
|
|
42860
|
-
|
|
42861
|
-
|
|
42862
|
-
|
|
42863
|
-
|
|
42848
|
+
error: refused,
|
|
42849
|
+
onKeepEditing: () => setRefused(null),
|
|
42850
|
+
onDiscard: () => {
|
|
42851
|
+
const kept = forms.find((f) => f.id === activeId) ?? null;
|
|
42852
|
+
if (kept) dropKeptDraft(kept.id);
|
|
42853
|
+
setDraft(kept);
|
|
42864
42854
|
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
|
|
42855
|
+
setSaved(null);
|
|
42856
|
+
setRefused(null);
|
|
42857
|
+
}
|
|
42884
42858
|
}
|
|
42885
|
-
),
|
|
42886
|
-
/* @__PURE__ */ (0,
|
|
42887
|
-
|
|
42888
|
-
|
|
42889
|
-
|
|
42890
|
-
|
|
42859
|
+
) : null,
|
|
42860
|
+
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: [
|
|
42861
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { children: [
|
|
42862
|
+
"Unsaved changes from",
|
|
42863
|
+
" ",
|
|
42864
|
+
new Date(restoredAt).toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" }),
|
|
42865
|
+
" restored"
|
|
42866
|
+
] }),
|
|
42867
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42868
|
+
import_design_system118.Button,
|
|
42891
42869
|
{
|
|
42892
|
-
|
|
42893
|
-
|
|
42894
|
-
|
|
42870
|
+
size: "sm",
|
|
42871
|
+
variant: "ghost",
|
|
42872
|
+
onClick: () => {
|
|
42873
|
+
dropKeptDraft(stored.id);
|
|
42874
|
+
setDraft(stored);
|
|
42875
|
+
setRestoredAt(null);
|
|
42876
|
+
},
|
|
42877
|
+
children: "Discard"
|
|
42895
42878
|
}
|
|
42896
42879
|
)
|
|
42897
|
-
] }),
|
|
42898
|
-
/* @__PURE__ */ (0,
|
|
42899
|
-
/* @__PURE__ */ (0,
|
|
42900
|
-
|
|
42901
|
-
|
|
42902
|
-
|
|
42903
|
-
|
|
42904
|
-
|
|
42905
|
-
|
|
42906
|
-
|
|
42907
|
-
|
|
42908
|
-
|
|
42909
|
-
|
|
42910
|
-
)
|
|
42911
|
-
|
|
42912
|
-
|
|
42913
|
-
|
|
42914
|
-
|
|
42915
|
-
|
|
42880
|
+
] }) : null,
|
|
42881
|
+
!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: [
|
|
42882
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42883
|
+
PublishRow,
|
|
42884
|
+
{
|
|
42885
|
+
url: `${publicOrigin}${(0, import_records56.publicFormPath)(draft.id)}`,
|
|
42886
|
+
state: draft.state,
|
|
42887
|
+
mayPublish: rights.structure,
|
|
42888
|
+
why: rights.why("structure"),
|
|
42889
|
+
busy: publishing,
|
|
42890
|
+
copied,
|
|
42891
|
+
shownUrl,
|
|
42892
|
+
onPublish: (wanted) => void publish(wanted),
|
|
42893
|
+
onCopy: (url) => void copyLink(url),
|
|
42894
|
+
unsaved: dirty
|
|
42895
|
+
}
|
|
42896
|
+
),
|
|
42897
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(EmbedSnippets, { url: `${publicOrigin}${(0, import_records56.publicFormPath)(draft.id)}`, label: draft.name }),
|
|
42898
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42899
|
+
NotifyOnAnswer,
|
|
42900
|
+
{
|
|
42901
|
+
tableId,
|
|
42902
|
+
tableName: table.data?.name ?? "record",
|
|
42903
|
+
formName: draft.name,
|
|
42904
|
+
ruleId: draft.notifyRuleId,
|
|
42905
|
+
notify: draft.notify ?? null,
|
|
42906
|
+
emailQuestion: (
|
|
42907
|
+
// An email field first; else a question whose field is named for an email (an imported
|
|
42908
|
+
// "Contact email" kept as text). The store's resolver refuses an answer that is not one.
|
|
42909
|
+
(draft.questions.map((q) => byKey.get(q.field)).find((f) => f && String(editorKindFor(f)) === "email") ?? draft.questions.map((q) => byKey.get(q.field)).find((f) => f && /e-?mail/i.test(`${f.key} ${f.label ?? ""}`)))?.key ?? null
|
|
42910
|
+
),
|
|
42911
|
+
onRule: (ruleId) => void chooseNotify(ruleId),
|
|
42912
|
+
onNotify: (notify) => void chooseWebhook(notify)
|
|
42913
|
+
}
|
|
42914
|
+
),
|
|
42915
|
+
/* @__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: [
|
|
42916
|
+
/* @__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: [
|
|
42917
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42918
|
+
import_design_system118.Checkbox,
|
|
42916
42919
|
{
|
|
42917
|
-
|
|
42918
|
-
|
|
42919
|
-
|
|
42920
|
-
placeholder: "https://",
|
|
42921
|
-
onChange: (e) => patch({ thankYou: { ...draft.thankYou, redirectUrl: e.target.value || null } })
|
|
42920
|
+
checked: draft.showOwnerHeader ?? orgKnobs?.show_owner_header ?? true,
|
|
42921
|
+
onCheckedChange: (checked) => patch({ showOwnerHeader: checked === true }),
|
|
42922
|
+
"aria-label": "Show our logo and name"
|
|
42922
42923
|
}
|
|
42923
|
-
)
|
|
42924
|
+
),
|
|
42925
|
+
"Show our logo and name"
|
|
42924
42926
|
] }),
|
|
42925
|
-
/* @__PURE__ */ (0,
|
|
42926
|
-
/* @__PURE__ */ (0,
|
|
42927
|
-
|
|
42928
|
-
import_design_system119.BasicTextarea,
|
|
42927
|
+
/* @__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: [
|
|
42928
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42929
|
+
import_design_system118.Checkbox,
|
|
42929
42930
|
{
|
|
42930
|
-
|
|
42931
|
-
|
|
42932
|
-
|
|
42933
|
-
onChange: (e) => patch({ thankYou: { ...draft.thankYou, body: e.target.value || null } })
|
|
42931
|
+
checked: draft.autoAdvance ?? orgKnobs?.choice_auto_advance ?? true,
|
|
42932
|
+
onCheckedChange: (checked) => patch({ autoAdvance: checked === true }),
|
|
42933
|
+
"aria-label": "Next after a choice"
|
|
42934
42934
|
}
|
|
42935
|
-
)
|
|
42935
|
+
),
|
|
42936
|
+
"Next after a choice"
|
|
42936
42937
|
] })
|
|
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
42938
|
] }),
|
|
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
|
-
|
|
42939
|
+
/* @__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: [
|
|
42940
|
+
/* @__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" }),
|
|
42941
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
42942
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42943
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Thank-you title" }),
|
|
42944
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42945
|
+
import_design_system118.BasicInput,
|
|
42946
|
+
{
|
|
42947
|
+
value: draft.thankYou?.title ?? "",
|
|
42948
|
+
placeholder: "Thank you",
|
|
42949
|
+
onChange: (e) => patch({ thankYou: { ...draft.thankYou, title: e.target.value || null } })
|
|
42950
|
+
}
|
|
42951
|
+
)
|
|
42952
|
+
] }),
|
|
42953
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42954
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Then go to (optional)" }),
|
|
42955
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42956
|
+
import_design_system118.BasicInput,
|
|
42957
|
+
{
|
|
42958
|
+
type: "url",
|
|
42959
|
+
inputMode: "url",
|
|
42960
|
+
value: draft.thankYou?.redirectUrl ?? "",
|
|
42961
|
+
placeholder: "https://",
|
|
42962
|
+
onChange: (e) => patch({ thankYou: { ...draft.thankYou, redirectUrl: e.target.value || null } })
|
|
42963
|
+
}
|
|
42964
|
+
)
|
|
42965
|
+
] }),
|
|
42966
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "col-span-2 flex flex-col gap-1", children: [
|
|
42967
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Thank-you message" }),
|
|
42968
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42969
|
+
import_design_system118.BasicTextarea,
|
|
42970
|
+
{
|
|
42971
|
+
rows: 2,
|
|
42972
|
+
value: draft.thankYou?.body ?? "",
|
|
42973
|
+
placeholder: "What happens next, in your words.",
|
|
42974
|
+
onChange: (e) => patch({ thankYou: { ...draft.thankYou, body: e.target.value || null } })
|
|
42975
|
+
}
|
|
42976
|
+
)
|
|
42977
|
+
] })
|
|
42978
|
+
] })
|
|
42988
42979
|
] }),
|
|
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,
|
|
42980
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(WelcomeEditor, { value: draft.welcome, pictures: lookOptions?.pictures ?? null, onChange: (welcome) => patch({ welcome }) }),
|
|
42981
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42982
|
+
EndingsEditor,
|
|
42995
42983
|
{
|
|
42996
|
-
|
|
42997
|
-
|
|
42998
|
-
|
|
42984
|
+
endings: draft.endings ?? [],
|
|
42985
|
+
pictures: lookOptions?.pictures ?? null,
|
|
42986
|
+
fieldKeys: conditionFields,
|
|
42987
|
+
scored: draft.questions.some((q) => q.points),
|
|
42988
|
+
onChange: (endings) => patch({ endings })
|
|
42999
42989
|
}
|
|
43000
42990
|
),
|
|
43001
|
-
/* @__PURE__ */ (0,
|
|
43002
|
-
|
|
43003
|
-
|
|
43004
|
-
/* @__PURE__ */ (0,
|
|
43005
|
-
|
|
43006
|
-
|
|
42991
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
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: "Name" }),
|
|
42994
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.BasicInput, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
|
|
42995
|
+
] }),
|
|
42996
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
42997
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Flow" }),
|
|
42998
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
42999
|
+
"select",
|
|
43007
43000
|
{
|
|
43008
|
-
|
|
43009
|
-
|
|
43010
|
-
|
|
43011
|
-
|
|
43012
|
-
onChange: (id) => patchLook({ logoFileId: id || null })
|
|
43001
|
+
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
43002
|
+
value: draft.flow ?? "one-at-a-time",
|
|
43003
|
+
onChange: (e) => patch({ flow: e.target.value }),
|
|
43004
|
+
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
43005
|
}
|
|
43014
43006
|
)
|
|
43015
43007
|
] }),
|
|
43016
|
-
/* @__PURE__ */ (0,
|
|
43017
|
-
/* @__PURE__ */ (0,
|
|
43018
|
-
/* @__PURE__ */ (0,
|
|
43019
|
-
|
|
43008
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "col-span-2 flex flex-col gap-1", children: [
|
|
43009
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Intro" }),
|
|
43010
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43011
|
+
import_design_system118.BasicTextarea,
|
|
43020
43012
|
{
|
|
43021
|
-
|
|
43022
|
-
|
|
43023
|
-
|
|
43024
|
-
noneLabel: "No cover",
|
|
43025
|
-
onChange: (id) => patchLook({ coverFileId: id || null })
|
|
43013
|
+
rows: 2,
|
|
43014
|
+
value: draft.intro ?? "",
|
|
43015
|
+
onChange: (e) => patch({ intro: e.target.value || null })
|
|
43026
43016
|
}
|
|
43027
43017
|
)
|
|
43028
|
-
] })
|
|
43029
|
-
|
|
43030
|
-
|
|
43031
|
-
|
|
43032
|
-
|
|
43033
|
-
|
|
43034
|
-
|
|
43035
|
-
|
|
43036
|
-
|
|
43037
|
-
|
|
43038
|
-
|
|
43039
|
-
|
|
43040
|
-
|
|
43041
|
-
] })
|
|
43018
|
+
] }),
|
|
43019
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
43020
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Submit button" }),
|
|
43021
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43022
|
+
import_design_system118.BasicInput,
|
|
43023
|
+
{
|
|
43024
|
+
value: draft.submitLabel ?? "",
|
|
43025
|
+
placeholder: "Submit",
|
|
43026
|
+
onChange: (e) => patch({ submitLabel: e.target.value || null })
|
|
43027
|
+
}
|
|
43028
|
+
)
|
|
43029
|
+
] }),
|
|
43030
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(HiddenFieldsEditor, { value: draft.hiddenFields ?? [], onChange: (hiddenFields) => patch({ hiddenFields }) })
|
|
43042
43031
|
] }),
|
|
43043
|
-
|
|
43044
|
-
|
|
43045
|
-
|
|
43046
|
-
|
|
43047
|
-
|
|
43048
|
-
|
|
43049
|
-
|
|
43050
|
-
|
|
43032
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1.5", "data-records-form-look-editor": "", children: [
|
|
43033
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Label, { className: "text-xs font-medium", children: "Look" }),
|
|
43034
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43035
|
+
AccentSwatches,
|
|
43036
|
+
{
|
|
43037
|
+
value: draft.look?.accent ?? "",
|
|
43038
|
+
noneLabel: "Organization colour",
|
|
43039
|
+
onChange: (accent) => patchLook({ accent: accent || null })
|
|
43040
|
+
}
|
|
43041
|
+
),
|
|
43042
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(ThemeEditor, { theme: draft.theme, pictures: lookOptions?.pictures ?? null, onChange: (theme) => patch({ theme }) }),
|
|
43043
|
+
lookOptions ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
43044
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
43045
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs", children: "Logo" }),
|
|
43046
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43047
|
+
PublicPicturePicker,
|
|
43051
43048
|
{
|
|
43052
|
-
|
|
43053
|
-
|
|
43054
|
-
|
|
43055
|
-
|
|
43056
|
-
})
|
|
43049
|
+
label: "Logo",
|
|
43050
|
+
pictures: lookOptions.pictures,
|
|
43051
|
+
value: draft.look?.logoFileId ?? "",
|
|
43052
|
+
noneLabel: "Organization logo",
|
|
43053
|
+
onChange: (id) => patchLook({ logoFileId: id || null })
|
|
43057
43054
|
}
|
|
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
|
|
43055
|
+
)
|
|
43061
43056
|
] }),
|
|
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,
|
|
43057
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
43058
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs", children: "Cover" }),
|
|
43059
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43060
|
+
PublicPicturePicker,
|
|
43075
43061
|
{
|
|
43076
|
-
|
|
43077
|
-
|
|
43078
|
-
|
|
43079
|
-
|
|
43080
|
-
onChange: (
|
|
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,
|
|
43097
|
-
{
|
|
43098
|
-
question,
|
|
43099
|
-
fieldKeys: (fields.data ?? []).map((f) => ({
|
|
43100
|
-
id: String(f.id),
|
|
43101
|
-
key: f.key,
|
|
43102
|
-
label: f.label || f.key,
|
|
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)
|
|
43062
|
+
label: "Cover",
|
|
43063
|
+
pictures: lookOptions.pictures,
|
|
43064
|
+
value: draft.look?.coverFileId ?? "",
|
|
43065
|
+
noneLabel: "No cover",
|
|
43066
|
+
onChange: (id) => patchLook({ coverFileId: id || null })
|
|
43122
43067
|
}
|
|
43123
43068
|
)
|
|
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,
|
|
43069
|
+
] })
|
|
43070
|
+
] }) : lookUnread ? null : /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Skeleton, { className: "h-12 w-full" }),
|
|
43071
|
+
lookUnread ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(RefusalLine, { error: lookUnread }) : null,
|
|
43072
|
+
refused && isLookRefusal(refused) ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(RefusalLine, { error: refused }) : null
|
|
43073
|
+
] }),
|
|
43074
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43075
|
+
QuestionList,
|
|
43144
43076
|
{
|
|
43145
43077
|
tableId,
|
|
43146
|
-
|
|
43147
|
-
|
|
43148
|
-
|
|
43149
|
-
|
|
43150
|
-
|
|
43151
|
-
|
|
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);
|
|
43078
|
+
draft,
|
|
43079
|
+
fields: fields.data ?? [],
|
|
43080
|
+
byKey,
|
|
43081
|
+
adding,
|
|
43082
|
+
onAdding: (on) => {
|
|
43083
|
+
keysBeforeAdd.current = on ? new Set((fields.data ?? []).map((f) => f.key)) : null;
|
|
43084
|
+
setAdding(on);
|
|
43160
43085
|
},
|
|
43161
|
-
|
|
43086
|
+
onAdded: () => setAdding(false),
|
|
43087
|
+
onQuestions: (questions) => patch({ questions }),
|
|
43088
|
+
onQuestion: patchQuestion
|
|
43162
43089
|
}
|
|
43163
|
-
)
|
|
43090
|
+
)
|
|
43164
43091
|
] })
|
|
43165
|
-
] })
|
|
43166
|
-
|
|
43167
|
-
|
|
43168
|
-
|
|
43092
|
+
] }),
|
|
43093
|
+
draft ? (
|
|
43094
|
+
// BESIDE THE EDITOR, ALWAYS IN VIEW (TYPEFORM-2): it follows the scroll and updates as you type.
|
|
43095
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43096
|
+
"aside",
|
|
43097
|
+
{
|
|
43098
|
+
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)]",
|
|
43099
|
+
"data-records-form-preview": "",
|
|
43100
|
+
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 ?? orgKnobs?.show_owner_header ?? true, children: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormRunner, { form: draft, preview: true, autoAdvance: draft.autoAdvance ?? orgKnobs?.choice_auto_advance ?? true }) })
|
|
43101
|
+
}
|
|
43102
|
+
)
|
|
43103
|
+
) : null
|
|
43104
|
+
] }) })
|
|
43105
|
+
);
|
|
43169
43106
|
}
|
|
43170
43107
|
function PublishRow({
|
|
43171
43108
|
url,
|
|
@@ -43180,10 +43117,10 @@ function PublishRow({
|
|
|
43180
43117
|
unsaved = false
|
|
43181
43118
|
}) {
|
|
43182
43119
|
const open = state === "open";
|
|
43183
|
-
return /* @__PURE__ */ (0,
|
|
43184
|
-
/* @__PURE__ */ (0,
|
|
43185
|
-
mayPublish ? /* @__PURE__ */ (0,
|
|
43186
|
-
|
|
43120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1.5 rounded border px-2.5 py-2", children: [
|
|
43121
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
43122
|
+
mayPublish ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43123
|
+
import_design_system118.Button,
|
|
43187
43124
|
{
|
|
43188
43125
|
size: "sm",
|
|
43189
43126
|
variant: open ? "ghost" : "default",
|
|
@@ -43192,8 +43129,8 @@ function PublishRow({
|
|
|
43192
43129
|
children: busy ? "\u2026" : open ? "Unpublish" : unsaved ? "Save and publish" : "Publish"
|
|
43193
43130
|
}
|
|
43194
43131
|
) : null,
|
|
43195
|
-
state !== "draft" ? /* @__PURE__ */ (0,
|
|
43196
|
-
/* @__PURE__ */ (0,
|
|
43132
|
+
state !== "draft" ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(import_jsx_runtime133.Fragment, { children: [
|
|
43133
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43197
43134
|
"a",
|
|
43198
43135
|
{
|
|
43199
43136
|
href: url,
|
|
@@ -43203,14 +43140,14 @@ function PublishRow({
|
|
|
43203
43140
|
children: url
|
|
43204
43141
|
}
|
|
43205
43142
|
),
|
|
43206
|
-
/* @__PURE__ */ (0,
|
|
43143
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", variant: "outline", onClick: () => onCopy(url), children: copied ? "Copied" : "Copy link" })
|
|
43207
43144
|
] }) : null
|
|
43208
43145
|
] }),
|
|
43209
|
-
/* @__PURE__ */ (0,
|
|
43210
|
-
open && unsaved ? /* @__PURE__ */ (0,
|
|
43211
|
-
!mayPublish ? /* @__PURE__ */ (0,
|
|
43212
|
-
shownUrl ? /* @__PURE__ */ (0,
|
|
43213
|
-
"
|
|
43146
|
+
/* @__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] }),
|
|
43147
|
+
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,
|
|
43148
|
+
!mayPublish ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("p", { className: "w-fit text-xs text-muted-foreground", title: why, children: "Publishing needs table admin" }) : null,
|
|
43149
|
+
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: [
|
|
43150
|
+
"Copy by hand: ",
|
|
43214
43151
|
shownUrl
|
|
43215
43152
|
] }) : null
|
|
43216
43153
|
] });
|
|
@@ -43220,7 +43157,7 @@ function Condition({
|
|
|
43220
43157
|
fieldKeys,
|
|
43221
43158
|
onChange
|
|
43222
43159
|
}) {
|
|
43223
|
-
return /* @__PURE__ */ (0,
|
|
43160
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43224
43161
|
ConditionGroup,
|
|
43225
43162
|
{
|
|
43226
43163
|
lead: "Ask only when",
|
|
@@ -43231,26 +43168,348 @@ function Condition({
|
|
|
43231
43168
|
}
|
|
43232
43169
|
);
|
|
43233
43170
|
}
|
|
43171
|
+
var FORM_STATE_LABELS = {
|
|
43172
|
+
draft: "Not published",
|
|
43173
|
+
open: "Open",
|
|
43174
|
+
closed: "Closed",
|
|
43175
|
+
full: "Full"
|
|
43176
|
+
};
|
|
43177
|
+
function QuestionList({
|
|
43178
|
+
tableId,
|
|
43179
|
+
draft,
|
|
43180
|
+
fields,
|
|
43181
|
+
byKey,
|
|
43182
|
+
adding,
|
|
43183
|
+
onAdding,
|
|
43184
|
+
onAdded,
|
|
43185
|
+
onQuestions,
|
|
43186
|
+
onQuestion
|
|
43187
|
+
}) {
|
|
43188
|
+
const sensors = (0, import_core22.useSensors)(
|
|
43189
|
+
(0, import_core22.useSensor)(import_core22.PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
43190
|
+
(0, import_core22.useSensor)(import_core22.KeyboardSensor, { coordinateGetter: import_sortable.sortableKeyboardCoordinates })
|
|
43191
|
+
);
|
|
43192
|
+
const askable = askableFields([...fields]);
|
|
43193
|
+
const unasked = askable.filter((f) => !draft.questions.some((q) => q.field === f.key));
|
|
43194
|
+
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 }));
|
|
43195
|
+
const label = (key) => {
|
|
43196
|
+
const f = byKey.get(key);
|
|
43197
|
+
return draft.questions.find((q) => q.field === key)?.ask || (f ? fieldName(f) : key);
|
|
43198
|
+
};
|
|
43199
|
+
function dragEnd(event) {
|
|
43200
|
+
const from = draft.questions.findIndex((q) => q.field === event.active.id);
|
|
43201
|
+
const to = event.over ? draft.questions.findIndex((q) => q.field === event.over.id) : -1;
|
|
43202
|
+
if (from < 0 || to < 0 || from === to) return;
|
|
43203
|
+
onQuestions((0, import_sortable.arrayMove)(draft.questions, from, to));
|
|
43204
|
+
}
|
|
43205
|
+
const notAskable = whyNotAskable([...fields]);
|
|
43206
|
+
const leftUnasked = requiredLeftUnasked([...fields], draft.questions.map((q) => q.field));
|
|
43207
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-col gap-1.5", "data-records-form-questions": "", children: [
|
|
43208
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
43209
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "font-medium text-foreground", children: "Questions" }),
|
|
43210
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "tabular-nums", children: draft.questions.length })
|
|
43211
|
+
] }),
|
|
43212
|
+
/* @__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)(
|
|
43213
|
+
QuestionRow,
|
|
43214
|
+
{
|
|
43215
|
+
index,
|
|
43216
|
+
question,
|
|
43217
|
+
field: byKey.get(question.field) ?? null,
|
|
43218
|
+
draft,
|
|
43219
|
+
conditionFields: asked3.filter((f) => f.key !== question.field),
|
|
43220
|
+
label,
|
|
43221
|
+
onChange: (change) => onQuestion(index, change),
|
|
43222
|
+
onRemove: () => onQuestions(draft.questions.filter((q) => q.field !== question.field))
|
|
43223
|
+
},
|
|
43224
|
+
question.field
|
|
43225
|
+
)) }) }),
|
|
43226
|
+
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)(
|
|
43227
|
+
import_design_system118.Button,
|
|
43228
|
+
{
|
|
43229
|
+
size: "sm",
|
|
43230
|
+
variant: "outline",
|
|
43231
|
+
"aria-label": `Ask for ${fieldName(f)}`,
|
|
43232
|
+
onClick: () => onQuestions([...draft.questions, { field: f.key }]),
|
|
43233
|
+
children: [
|
|
43234
|
+
"+ ",
|
|
43235
|
+
fieldName(f)
|
|
43236
|
+
]
|
|
43237
|
+
},
|
|
43238
|
+
f.id
|
|
43239
|
+
)) }) : null,
|
|
43240
|
+
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,
|
|
43241
|
+
leftUnasked ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43242
|
+
import_design_system117.ErrorBox,
|
|
43243
|
+
{
|
|
43244
|
+
className: "rounded border border-destructive/40 bg-destructive/5 px-2 py-1.5 text-xs text-destructive",
|
|
43245
|
+
message: leftUnasked,
|
|
43246
|
+
operation: "Build a form",
|
|
43247
|
+
origin: "records-ui/FormBuilder"
|
|
43248
|
+
}
|
|
43249
|
+
) : null,
|
|
43250
|
+
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" }) })
|
|
43251
|
+
] });
|
|
43252
|
+
}
|
|
43253
|
+
function QuestionRow({
|
|
43254
|
+
index,
|
|
43255
|
+
question,
|
|
43256
|
+
field: field2,
|
|
43257
|
+
draft,
|
|
43258
|
+
conditionFields,
|
|
43259
|
+
label,
|
|
43260
|
+
onChange,
|
|
43261
|
+
onRemove
|
|
43262
|
+
}) {
|
|
43263
|
+
const sortable = (0, import_sortable.useSortable)({ id: question.field });
|
|
43264
|
+
const [open, setOpen] = (0, import_react232.useState)(true);
|
|
43265
|
+
const name = field2 ? fieldName(field2) : question.field;
|
|
43266
|
+
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(
|
|
43267
|
+
"div",
|
|
43268
|
+
{
|
|
43269
|
+
ref: sortable.setNodeRef,
|
|
43270
|
+
style: { transform: import_utilities.CSS.Transform.toString(sortable.transform), transition: sortable.transition },
|
|
43271
|
+
className: (0, import_design_system118.cn)("rounded border bg-background px-2 py-1.5", sortable.isDragging && "z-10 shadow-md"),
|
|
43272
|
+
"data-records-form-question-row": question.field,
|
|
43273
|
+
children: [
|
|
43274
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
43275
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43276
|
+
"button",
|
|
43277
|
+
{
|
|
43278
|
+
type: "button",
|
|
43279
|
+
className: "cursor-grab touch-none rounded px-1 text-muted-foreground hover:bg-muted active:cursor-grabbing",
|
|
43280
|
+
"aria-label": `Move ${name}`,
|
|
43281
|
+
title: "Drag to reorder",
|
|
43282
|
+
"data-records-form-drag": "",
|
|
43283
|
+
...sortable.attributes,
|
|
43284
|
+
...sortable.listeners,
|
|
43285
|
+
children: "\u22EE\u22EE"
|
|
43286
|
+
}
|
|
43287
|
+
),
|
|
43288
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "w-5 text-right text-xs tabular-nums text-muted-foreground", children: index + 1 }),
|
|
43289
|
+
/* @__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 }),
|
|
43290
|
+
field2?.required || question.required ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "text-xs text-foreground/80", children: "required" }) : null,
|
|
43291
|
+
!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,
|
|
43292
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Button, { size: "sm", variant: "ghost", onClick: onRemove, "aria-label": `Stop asking ${name}`, children: "Remove" })
|
|
43293
|
+
] }),
|
|
43294
|
+
open && field2 ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "mt-1.5 grid grid-cols-2 gap-2 pl-7", children: [
|
|
43295
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43296
|
+
import_design_system118.BasicInput,
|
|
43297
|
+
{
|
|
43298
|
+
className: "h-8 text-xs",
|
|
43299
|
+
placeholder: name,
|
|
43300
|
+
"aria-label": `How to ask for ${name}`,
|
|
43301
|
+
title: "Use {{question}} to recall an earlier answer",
|
|
43302
|
+
value: question.ask ?? "",
|
|
43303
|
+
onChange: (e) => onChange({ ask: e.target.value || null })
|
|
43304
|
+
}
|
|
43305
|
+
),
|
|
43306
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43307
|
+
import_design_system118.BasicInput,
|
|
43308
|
+
{
|
|
43309
|
+
className: "h-8 text-xs",
|
|
43310
|
+
placeholder: "Help text",
|
|
43311
|
+
"aria-label": `Help text for ${name}`,
|
|
43312
|
+
value: question.help ?? "",
|
|
43313
|
+
onChange: (e) => onChange({ help: e.target.value || null })
|
|
43314
|
+
}
|
|
43315
|
+
),
|
|
43316
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "col-span-2 flex items-center gap-2 text-xs", children: [
|
|
43317
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43318
|
+
import_design_system118.Checkbox,
|
|
43319
|
+
{
|
|
43320
|
+
checked: field2.required ? true : question.required === true,
|
|
43321
|
+
disabled: field2.required === true,
|
|
43322
|
+
"aria-label": `${name} is required`,
|
|
43323
|
+
onCheckedChange: (checked) => onChange({ required: checked === true ? true : null })
|
|
43324
|
+
}
|
|
43325
|
+
),
|
|
43326
|
+
"Required"
|
|
43327
|
+
] }),
|
|
43328
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(Condition, { question, fieldKeys: conditionFields, onChange: (showIf) => onChange({ showIf }) }),
|
|
43329
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43330
|
+
QuestionLogic,
|
|
43331
|
+
{
|
|
43332
|
+
question,
|
|
43333
|
+
index,
|
|
43334
|
+
questions: draft.questions,
|
|
43335
|
+
endings: draft.endings ?? [],
|
|
43336
|
+
field: field2,
|
|
43337
|
+
fieldKeys: conditionFields,
|
|
43338
|
+
label,
|
|
43339
|
+
onChange
|
|
43340
|
+
}
|
|
43341
|
+
)
|
|
43342
|
+
] }) : null
|
|
43343
|
+
]
|
|
43344
|
+
}
|
|
43345
|
+
);
|
|
43346
|
+
}
|
|
43347
|
+
function NotifyOnAnswer({
|
|
43348
|
+
tableId,
|
|
43349
|
+
tableName: tableName2,
|
|
43350
|
+
formName,
|
|
43351
|
+
ruleId,
|
|
43352
|
+
notify,
|
|
43353
|
+
emailQuestion,
|
|
43354
|
+
onRule,
|
|
43355
|
+
onNotify
|
|
43356
|
+
}) {
|
|
43357
|
+
const client = (0, import_react233.useRecordsClient)();
|
|
43358
|
+
const [subs, setSubs] = (0, import_react232.useState)(null);
|
|
43359
|
+
const [busy, setBusy] = (0, import_react232.useState)(false);
|
|
43360
|
+
const [error, setError] = (0, import_react232.useState)(null);
|
|
43361
|
+
const [said2, setSaid] = (0, import_react232.useState)(null);
|
|
43362
|
+
const [url, setUrl] = (0, import_react232.useState)(notify?.webhookUrl ?? "");
|
|
43363
|
+
(0, import_react232.useEffect)(() => setUrl(notify?.webhookUrl ?? ""), [notify?.webhookUrl]);
|
|
43364
|
+
(0, import_react232.useEffect)(() => {
|
|
43365
|
+
let cancelled = false;
|
|
43366
|
+
void client.subscriptions({ table_id: tableId }).then((r) => {
|
|
43367
|
+
if (!cancelled && r.ok) setSubs(r.data);
|
|
43368
|
+
});
|
|
43369
|
+
return () => {
|
|
43370
|
+
cancelled = true;
|
|
43371
|
+
};
|
|
43372
|
+
}, [client, tableId, ruleId]);
|
|
43373
|
+
const chosen = subs?.find((s) => s.rule_id === ruleId) ?? null;
|
|
43374
|
+
async function emailMe(on) {
|
|
43375
|
+
setBusy(true);
|
|
43376
|
+
setError(null);
|
|
43377
|
+
try {
|
|
43378
|
+
if (!on) {
|
|
43379
|
+
onRule(null);
|
|
43380
|
+
if (ruleId) await client.recordDelete({ record_id: ruleId });
|
|
43381
|
+
return;
|
|
43382
|
+
}
|
|
43383
|
+
const listed = await client.views({ table_id: tableId });
|
|
43384
|
+
if (!listed.ok) return setError(listed.error);
|
|
43385
|
+
let viewId = listed.data.find((v) => !v.filters || Object.keys(v.filters).length === 0)?.view_id ?? null;
|
|
43386
|
+
if (!viewId) {
|
|
43387
|
+
const made = await client.viewDeclare({ table_id: tableId, spec: { name: `Every ${tableName2}`, filters: null } });
|
|
43388
|
+
if (!made.ok) return setError(made.error);
|
|
43389
|
+
viewId = made.data;
|
|
43390
|
+
}
|
|
43391
|
+
const declared2 = await client.subscriptionDeclare({
|
|
43392
|
+
table_id: tableId,
|
|
43393
|
+
spec: {
|
|
43394
|
+
name: `${formName} \u2014 new answers`,
|
|
43395
|
+
saved_view_id: viewId,
|
|
43396
|
+
cadence: "instant",
|
|
43397
|
+
schedule: null,
|
|
43398
|
+
channel: "email",
|
|
43399
|
+
quiet_hours: null,
|
|
43400
|
+
recipient_user_id: client.config.actor.user_id ?? null,
|
|
43401
|
+
event_key: "records.changed"
|
|
43402
|
+
}
|
|
43403
|
+
});
|
|
43404
|
+
if (!declared2.ok) return setError(declared2.error);
|
|
43405
|
+
onRule(declared2.data);
|
|
43406
|
+
} finally {
|
|
43407
|
+
setBusy(false);
|
|
43408
|
+
}
|
|
43409
|
+
}
|
|
43410
|
+
async function saveWebhook() {
|
|
43411
|
+
const next = url.trim();
|
|
43412
|
+
if (next === (notify?.webhookUrl ?? "")) return;
|
|
43413
|
+
setBusy(true);
|
|
43414
|
+
setError(null);
|
|
43415
|
+
setSaid(null);
|
|
43416
|
+
try {
|
|
43417
|
+
if (next === "") {
|
|
43418
|
+
if (notify?.webhookAutomationId) await client.automationArchive({ automation_id: notify.webhookAutomationId });
|
|
43419
|
+
onNotify(notify?.emailRespondent ? { ...notify, webhookUrl: null, webhookAutomationId: null } : null);
|
|
43420
|
+
return;
|
|
43421
|
+
}
|
|
43422
|
+
const declared2 = await client.automationDeclare({
|
|
43423
|
+
table_id: tableId,
|
|
43424
|
+
automation_id: notify?.webhookAutomationId ?? null,
|
|
43425
|
+
spec: { name: `${formName} \u2014 webhook`, trigger: { on: "form_answered" }, actions: [{ do: "webhook", url: next }] }
|
|
43426
|
+
});
|
|
43427
|
+
if (!declared2.ok) return setError(declared2.error);
|
|
43428
|
+
if (!declared2.data.ok) {
|
|
43429
|
+
setSaid(Object.values(declared2.data._errors)[0] ?? "Not saved");
|
|
43430
|
+
return;
|
|
43431
|
+
}
|
|
43432
|
+
onNotify({ ...notify ?? {}, webhookUrl: next, webhookAutomationId: declared2.data.automation_id });
|
|
43433
|
+
} finally {
|
|
43434
|
+
setBusy(false);
|
|
43435
|
+
}
|
|
43436
|
+
}
|
|
43437
|
+
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: [
|
|
43438
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "font-medium", children: "Notify on answer" }),
|
|
43439
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1", children: [
|
|
43440
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex items-center gap-2", title: "An email to you each time somebody answers.", children: [
|
|
43441
|
+
/* @__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" }),
|
|
43442
|
+
"Email me",
|
|
43443
|
+
chosen && chosen.channel !== "email" ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
43444
|
+
"(",
|
|
43445
|
+
chosen.channel === "sms" ? "text" : "in app",
|
|
43446
|
+
")"
|
|
43447
|
+
] }) : null
|
|
43448
|
+
] }),
|
|
43449
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(
|
|
43450
|
+
"label",
|
|
43451
|
+
{
|
|
43452
|
+
className: (0, import_design_system118.cn)("flex items-center gap-2", !emailQuestion && "text-muted-foreground"),
|
|
43453
|
+
title: emailQuestion ? "An email to the person who answered, in your ending's words." : "Add an email question first.",
|
|
43454
|
+
children: [
|
|
43455
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43456
|
+
import_design_system118.Checkbox,
|
|
43457
|
+
{
|
|
43458
|
+
checked: Boolean(notify?.emailRespondent) && Boolean(emailQuestion),
|
|
43459
|
+
disabled: busy || !emailQuestion,
|
|
43460
|
+
onCheckedChange: (c) => onNotify({ ...notify ?? {}, emailRespondent: c === true ? true : null, respondentField: c === true ? emailQuestion : null }),
|
|
43461
|
+
"aria-label": "Email the respondent"
|
|
43462
|
+
}
|
|
43463
|
+
),
|
|
43464
|
+
"Email the respondent"
|
|
43465
|
+
]
|
|
43466
|
+
}
|
|
43467
|
+
)
|
|
43468
|
+
] }),
|
|
43469
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex items-center gap-2", children: [
|
|
43470
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "shrink-0", children: "Webhook" }),
|
|
43471
|
+
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
|
|
43472
|
+
import_design_system118.BasicInput,
|
|
43473
|
+
{
|
|
43474
|
+
type: "url",
|
|
43475
|
+
inputMode: "url",
|
|
43476
|
+
className: "h-8 text-xs",
|
|
43477
|
+
placeholder: "https://",
|
|
43478
|
+
"aria-label": "Webhook URL",
|
|
43479
|
+
value: url,
|
|
43480
|
+
disabled: busy,
|
|
43481
|
+
onChange: (e) => setUrl(e.target.value),
|
|
43482
|
+
onBlur: () => void saveWebhook(),
|
|
43483
|
+
onKeyDown: (e) => {
|
|
43484
|
+
if (e.key === "Enter") void saveWebhook();
|
|
43485
|
+
}
|
|
43486
|
+
}
|
|
43487
|
+
)
|
|
43488
|
+
] }),
|
|
43489
|
+
said2 ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("p", { className: "text-destructive", children: said2 }) : null,
|
|
43490
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(RefusalLine, { error }) : null
|
|
43491
|
+
] });
|
|
43492
|
+
}
|
|
43234
43493
|
|
|
43235
43494
|
// src/DocTemplate.tsx
|
|
43236
|
-
var
|
|
43237
|
-
var
|
|
43238
|
-
var
|
|
43239
|
-
var
|
|
43495
|
+
var import_react234 = require("react");
|
|
43496
|
+
var import_react235 = require("@ai-matrx/records/react");
|
|
43497
|
+
var import_design_system119 = require("@ai-matrx/design-system");
|
|
43498
|
+
var import_jsx_runtime134 = require("react/jsx-runtime");
|
|
43240
43499
|
function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, className }) {
|
|
43241
|
-
const client = (0,
|
|
43500
|
+
const client = (0, import_react235.useRecordsClient)();
|
|
43242
43501
|
const claimSeed = useSeedGuard();
|
|
43243
|
-
const table = (0,
|
|
43502
|
+
const table = (0, import_react235.useTable)(tableId);
|
|
43244
43503
|
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,
|
|
43504
|
+
const fields = (0, import_react235.useFields)(tableId);
|
|
43505
|
+
const [templates, setTemplates] = (0, import_react234.useState)(null);
|
|
43506
|
+
const [error, setError] = (0, import_react234.useState)(null);
|
|
43507
|
+
const [activeId, setActiveId] = (0, import_react234.useState)(activeTemplateId ?? null);
|
|
43508
|
+
const [draftName, setDraftName] = (0, import_react234.useState)("");
|
|
43509
|
+
const [draftBody, setDraftBody] = (0, import_react234.useState)("");
|
|
43510
|
+
const [unresolved, setUnresolved] = (0, import_react234.useState)([]);
|
|
43511
|
+
const [saving, setSaving] = (0, import_react234.useState)(false);
|
|
43512
|
+
const load = (0, import_react234.useCallback)(async () => {
|
|
43254
43513
|
const held = await client.docTemplates({ table_id: tableId });
|
|
43255
43514
|
if (!held.ok) {
|
|
43256
43515
|
setError(held.error);
|
|
@@ -43281,10 +43540,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43281
43540
|
setError(null);
|
|
43282
43541
|
setTemplates(rows);
|
|
43283
43542
|
}, [client, tableId, seed, fields.data]);
|
|
43284
|
-
(0,
|
|
43543
|
+
(0, import_react234.useEffect)(() => {
|
|
43285
43544
|
void load();
|
|
43286
43545
|
}, [load]);
|
|
43287
|
-
(0,
|
|
43546
|
+
(0, import_react234.useEffect)(() => {
|
|
43288
43547
|
if (!templates || templates.length === 0) return;
|
|
43289
43548
|
const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
|
|
43290
43549
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -43292,7 +43551,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43292
43551
|
setDraftBody(chosen.body);
|
|
43293
43552
|
onActiveTemplate?.(chosen);
|
|
43294
43553
|
}, [templates, activeTemplateId]);
|
|
43295
|
-
(0,
|
|
43554
|
+
(0, import_react234.useEffect)(() => {
|
|
43296
43555
|
let cancelled = false;
|
|
43297
43556
|
if (draftBody.trim() === "") {
|
|
43298
43557
|
setUnresolved([]);
|
|
@@ -43322,25 +43581,25 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43322
43581
|
setActiveId(saved.data);
|
|
43323
43582
|
await load();
|
|
43324
43583
|
}
|
|
43325
|
-
if (fields.error) return /* @__PURE__ */ (0,
|
|
43584
|
+
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(RefusalNotice, { error: fields.error, className });
|
|
43326
43585
|
if (error) {
|
|
43327
|
-
return /* @__PURE__ */ (0,
|
|
43586
|
+
return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43328
43587
|
RefusalNotice,
|
|
43329
43588
|
{
|
|
43330
43589
|
error,
|
|
43331
43590
|
className,
|
|
43332
|
-
actions: /* @__PURE__ */ (0,
|
|
43591
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43333
43592
|
}
|
|
43334
43593
|
);
|
|
43335
43594
|
}
|
|
43336
|
-
if (templates === null || fields.loading && !fields.data) return /* @__PURE__ */ (0,
|
|
43595
|
+
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
43596
|
if (!rights.admin) {
|
|
43338
|
-
return /* @__PURE__ */ (0,
|
|
43597
|
+
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
43598
|
}
|
|
43340
|
-
return /* @__PURE__ */ (0,
|
|
43341
|
-
/* @__PURE__ */ (0,
|
|
43342
|
-
templates.map((t) => /* @__PURE__ */ (0,
|
|
43343
|
-
|
|
43599
|
+
return /* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("div", { className: (0, import_design_system119.cn)("flex flex-col gap-2", className), children: [
|
|
43600
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Templates", children: [
|
|
43601
|
+
templates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime134.jsxs)(
|
|
43602
|
+
import_design_system119.Button,
|
|
43344
43603
|
{
|
|
43345
43604
|
size: "sm",
|
|
43346
43605
|
variant: t.id === activeId ? "secondary" : "ghost",
|
|
@@ -43352,7 +43611,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43352
43611
|
},
|
|
43353
43612
|
children: [
|
|
43354
43613
|
t.name,
|
|
43355
|
-
/* @__PURE__ */ (0,
|
|
43614
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("span", { className: "ml-1 text-[10px] opacity-70", children: [
|
|
43356
43615
|
"v",
|
|
43357
43616
|
t.template_version
|
|
43358
43617
|
] })
|
|
@@ -43360,8 +43619,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43360
43619
|
},
|
|
43361
43620
|
t.id
|
|
43362
43621
|
)),
|
|
43363
|
-
/* @__PURE__ */ (0,
|
|
43364
|
-
|
|
43622
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43623
|
+
import_design_system119.Button,
|
|
43365
43624
|
{
|
|
43366
43625
|
size: "sm",
|
|
43367
43626
|
variant: "ghost",
|
|
@@ -43373,16 +43632,16 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43373
43632
|
children: "New template"
|
|
43374
43633
|
}
|
|
43375
43634
|
),
|
|
43376
|
-
/* @__PURE__ */ (0,
|
|
43635
|
+
/* @__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
43636
|
] }),
|
|
43378
|
-
/* @__PURE__ */ (0,
|
|
43379
|
-
/* @__PURE__ */ (0,
|
|
43380
|
-
/* @__PURE__ */ (0,
|
|
43637
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
43638
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.Label, { className: "text-xs font-medium", children: "Name" }),
|
|
43639
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(import_design_system119.BasicInput, { value: draftName, onChange: (e) => setDraftName(e.target.value), placeholder: "Agreement" })
|
|
43381
43640
|
] }),
|
|
43382
|
-
/* @__PURE__ */ (0,
|
|
43383
|
-
/* @__PURE__ */ (0,
|
|
43384
|
-
(fields.data ?? []).map((field2) => /* @__PURE__ */ (0,
|
|
43385
|
-
|
|
43641
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
43642
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)("span", { className: "text-xs text-muted-foreground", children: "Insert" }),
|
|
43643
|
+
(fields.data ?? []).map((field2) => /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43644
|
+
import_design_system119.Button,
|
|
43386
43645
|
{
|
|
43387
43646
|
size: "sm",
|
|
43388
43647
|
variant: "ghost",
|
|
@@ -43393,8 +43652,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43393
43652
|
field2.id
|
|
43394
43653
|
))
|
|
43395
43654
|
] }),
|
|
43396
|
-
/* @__PURE__ */ (0,
|
|
43397
|
-
|
|
43655
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43656
|
+
import_design_system119.BasicTextarea,
|
|
43398
43657
|
{
|
|
43399
43658
|
rows: 10,
|
|
43400
43659
|
"aria-label": "Template body",
|
|
@@ -43403,33 +43662,33 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
43403
43662
|
onChange: (e) => setDraftBody(e.target.value)
|
|
43404
43663
|
}
|
|
43405
43664
|
),
|
|
43406
|
-
unresolved.length > 0 ? /* @__PURE__ */ (0,
|
|
43407
|
-
/* @__PURE__ */ (0,
|
|
43408
|
-
/* @__PURE__ */ (0,
|
|
43409
|
-
/* @__PURE__ */ (0,
|
|
43665
|
+
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: [
|
|
43666
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)("code", { className: "text-destructive", children: token.raw }),
|
|
43667
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)("span", { "aria-hidden": "true", children: "\u2014" }),
|
|
43668
|
+
/* @__PURE__ */ (0, import_jsx_runtime134.jsx)(
|
|
43410
43669
|
RefusalLine,
|
|
43411
43670
|
{
|
|
43412
43671
|
error: refusal("invalid_argument", token.why, "Change it to a Field this table has, or take it out.")
|
|
43413
43672
|
}
|
|
43414
43673
|
)
|
|
43415
|
-
] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */ (0,
|
|
43674
|
+
] }, 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
43675
|
] });
|
|
43417
43676
|
}
|
|
43418
43677
|
|
|
43419
43678
|
// src/DocRender.tsx
|
|
43420
|
-
var
|
|
43421
|
-
var
|
|
43422
|
-
var
|
|
43423
|
-
var
|
|
43679
|
+
var import_react236 = require("react");
|
|
43680
|
+
var import_react237 = require("@ai-matrx/records/react");
|
|
43681
|
+
var import_design_system120 = require("@ai-matrx/design-system");
|
|
43682
|
+
var import_jsx_runtime135 = require("react/jsx-runtime");
|
|
43424
43683
|
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,
|
|
43684
|
+
const client = (0, import_react237.useRecordsClient)();
|
|
43685
|
+
const [preview, setPreview] = (0, import_react236.useState)(null);
|
|
43686
|
+
const [renders, setRenders] = (0, import_react236.useState)(null);
|
|
43687
|
+
const [showing, setShowing] = (0, import_react236.useState)(null);
|
|
43688
|
+
const [error, setError] = (0, import_react236.useState)(null);
|
|
43689
|
+
const [busy, setBusy] = (0, import_react236.useState)(null);
|
|
43690
|
+
const paper = (0, import_react236.useRef)(null);
|
|
43691
|
+
const load = (0, import_react236.useCallback)(async () => {
|
|
43433
43692
|
const [body, held] = await Promise.all([
|
|
43434
43693
|
client.docRenderBody({ template_id: templateId, record_id: recordId }),
|
|
43435
43694
|
client.docRenders({ record_id: recordId })
|
|
@@ -43446,7 +43705,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43446
43705
|
setPreview(body.data);
|
|
43447
43706
|
setRenders(held.data.filter((r) => r.template_id === templateId));
|
|
43448
43707
|
}, [client, templateId, recordId]);
|
|
43449
|
-
(0,
|
|
43708
|
+
(0, import_react236.useEffect)(() => {
|
|
43450
43709
|
void load();
|
|
43451
43710
|
}, [load]);
|
|
43452
43711
|
async function freeze() {
|
|
@@ -43486,22 +43745,22 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43486
43745
|
}
|
|
43487
43746
|
}
|
|
43488
43747
|
if (error) {
|
|
43489
|
-
return /* @__PURE__ */ (0,
|
|
43748
|
+
return /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(
|
|
43490
43749
|
RefusalNotice,
|
|
43491
43750
|
{
|
|
43492
43751
|
error,
|
|
43493
43752
|
className,
|
|
43494
|
-
actions: /* @__PURE__ */ (0,
|
|
43753
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(import_design_system120.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43495
43754
|
}
|
|
43496
43755
|
);
|
|
43497
43756
|
}
|
|
43498
|
-
if (preview === null || renders === null) return /* @__PURE__ */ (0,
|
|
43757
|
+
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
43758
|
const frozen = showing ? renders.find((r) => r.id === showing) ?? null : null;
|
|
43500
43759
|
const text2 = frozen ? frozen.body : preview;
|
|
43501
|
-
return /* @__PURE__ */ (0,
|
|
43502
|
-
/* @__PURE__ */ (0,
|
|
43503
|
-
/* @__PURE__ */ (0,
|
|
43504
|
-
|
|
43760
|
+
return /* @__PURE__ */ (0, import_jsx_runtime135.jsxs)("div", { className: (0, import_design_system120.cn)("flex flex-col gap-2", className), children: [
|
|
43761
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsxs)("div", { className: "flex items-center gap-1 text-xs", children: [
|
|
43762
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsx)(
|
|
43763
|
+
import_design_system120.Button,
|
|
43505
43764
|
{
|
|
43506
43765
|
size: "sm",
|
|
43507
43766
|
variant: frozen ? "ghost" : "secondary",
|
|
@@ -43510,8 +43769,8 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43510
43769
|
children: "As it reads now"
|
|
43511
43770
|
}
|
|
43512
43771
|
),
|
|
43513
|
-
renders.map((render) => /* @__PURE__ */ (0,
|
|
43514
|
-
|
|
43772
|
+
renders.map((render) => /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(
|
|
43773
|
+
import_design_system120.Button,
|
|
43515
43774
|
{
|
|
43516
43775
|
size: "sm",
|
|
43517
43776
|
variant: render.id === showing ? "secondary" : "ghost",
|
|
@@ -43522,33 +43781,33 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
43522
43781
|
},
|
|
43523
43782
|
render.id
|
|
43524
43783
|
)),
|
|
43525
|
-
/* @__PURE__ */ (0,
|
|
43526
|
-
/* @__PURE__ */ (0,
|
|
43527
|
-
/* @__PURE__ */ (0,
|
|
43784
|
+
/* @__PURE__ */ (0, import_jsx_runtime135.jsxs)("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
43785
|
+
/* @__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" }),
|
|
43786
|
+
/* @__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
43787
|
] })
|
|
43529
43788
|
] }),
|
|
43530
|
-
/* @__PURE__ */ (0,
|
|
43531
|
-
/* @__PURE__ */ (0,
|
|
43789
|
+
/* @__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 }),
|
|
43790
|
+
/* @__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
43791
|
] });
|
|
43533
43792
|
}
|
|
43534
43793
|
|
|
43535
43794
|
// src/SignBlock.tsx
|
|
43536
|
-
var
|
|
43537
|
-
var
|
|
43538
|
-
var
|
|
43539
|
-
var
|
|
43540
|
-
var
|
|
43795
|
+
var import_react238 = require("react");
|
|
43796
|
+
var import_react239 = require("@ai-matrx/records/react");
|
|
43797
|
+
var import_design_system121 = require("@ai-matrx/design-system");
|
|
43798
|
+
var import_react240 = require("@ai-matrx/records/react");
|
|
43799
|
+
var import_jsx_runtime136 = require("react/jsx-runtime");
|
|
43541
43800
|
function SignBlock({ tableId, recordId, render, className }) {
|
|
43542
|
-
const client = (0,
|
|
43543
|
-
const table = (0,
|
|
43801
|
+
const client = (0, import_react239.useRecordsClient)();
|
|
43802
|
+
const table = (0, import_react240.useTable)(tableId);
|
|
43544
43803
|
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,
|
|
43804
|
+
const fields = (0, import_react239.useFields)(tableId);
|
|
43805
|
+
const [signatures, setSignatures] = (0, import_react238.useState)(null);
|
|
43806
|
+
const [verdicts, setVerdicts] = (0, import_react238.useState)({});
|
|
43807
|
+
const [error, setError] = (0, import_react238.useState)(null);
|
|
43808
|
+
const [name, setName] = (0, import_react238.useState)("");
|
|
43809
|
+
const [busy, setBusy] = (0, import_react238.useState)(false);
|
|
43810
|
+
const load = (0, import_react238.useCallback)(async () => {
|
|
43552
43811
|
const held = await client.docSignatures({ record_id: recordId });
|
|
43553
43812
|
if (!held.ok) {
|
|
43554
43813
|
setError(held.error);
|
|
@@ -43563,7 +43822,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43563
43822
|
}
|
|
43564
43823
|
setVerdicts(answers);
|
|
43565
43824
|
}, [client, recordId]);
|
|
43566
|
-
(0,
|
|
43825
|
+
(0, import_react238.useEffect)(() => {
|
|
43567
43826
|
void load();
|
|
43568
43827
|
}, [load]);
|
|
43569
43828
|
async function sign(field2) {
|
|
@@ -43583,36 +43842,36 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43583
43842
|
setName("");
|
|
43584
43843
|
await load();
|
|
43585
43844
|
}
|
|
43586
|
-
if (fields.error) return /* @__PURE__ */ (0,
|
|
43845
|
+
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(RefusalNotice, { error: fields.error, className });
|
|
43587
43846
|
if (error) {
|
|
43588
|
-
return /* @__PURE__ */ (0,
|
|
43847
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(
|
|
43589
43848
|
RefusalNotice,
|
|
43590
43849
|
{
|
|
43591
43850
|
error,
|
|
43592
43851
|
className,
|
|
43593
|
-
actions: /* @__PURE__ */ (0,
|
|
43852
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(import_design_system121.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
43594
43853
|
}
|
|
43595
43854
|
);
|
|
43596
43855
|
}
|
|
43597
|
-
if (fields.loading || signatures === null) return /* @__PURE__ */ (0,
|
|
43856
|
+
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
43857
|
const signable = (fields.data ?? []).filter(isSignatureField);
|
|
43599
|
-
return /* @__PURE__ */ (0,
|
|
43600
|
-
/* @__PURE__ */ (0,
|
|
43601
|
-
/* @__PURE__ */ (0,
|
|
43602
|
-
/* @__PURE__ */ (0,
|
|
43858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("div", { className: (0, import_design_system121.cn)("flex flex-col gap-2", className), children: [
|
|
43859
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
43860
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("span", { className: "font-medium text-foreground", children: "Signatures" }),
|
|
43861
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("span", { children: [
|
|
43603
43862
|
signatures.length,
|
|
43604
43863
|
" on this record"
|
|
43605
43864
|
] })
|
|
43606
43865
|
] }),
|
|
43607
|
-
signatures.length > 0 ? /* @__PURE__ */ (0,
|
|
43866
|
+
signatures.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime136.jsx)("ul", { className: "flex flex-col gap-1", children: signatures.map((signature) => {
|
|
43608
43867
|
const verdict = verdicts[signature.id];
|
|
43609
43868
|
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,
|
|
43869
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("li", { className: "rounded border px-2 py-1.5 text-xs", children: [
|
|
43870
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
43871
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("span", { className: "font-medium", children: signature.signer_name }),
|
|
43872
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("span", { className: "text-muted-foreground", children: signature.field_key }),
|
|
43873
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("span", { className: "text-muted-foreground", children: new Date(signature.signed_at).toLocaleString() }),
|
|
43874
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("span", { className: "ml-auto font-mono text-[10px] text-muted-foreground", children: [
|
|
43616
43875
|
"v",
|
|
43617
43876
|
signature.document_version,
|
|
43618
43877
|
" \xB7 ",
|
|
@@ -43622,7 +43881,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43622
43881
|
intact === false ? (
|
|
43623
43882
|
// THE STORE REFUSED TO CALL IT INTACT: its reason goes through the one
|
|
43624
43883
|
// formatter, with what to do next, never raw in red (lane REFUSAL-SWEEP).
|
|
43625
|
-
/* @__PURE__ */ (0,
|
|
43884
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(
|
|
43626
43885
|
RefusalLine,
|
|
43627
43886
|
{
|
|
43628
43887
|
className: "mt-0.5",
|
|
@@ -43633,22 +43892,22 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43633
43892
|
)
|
|
43634
43893
|
}
|
|
43635
43894
|
)
|
|
43636
|
-
) : /* @__PURE__ */ (0,
|
|
43895
|
+
) : /* @__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
43896
|
] }, signature.id);
|
|
43638
43897
|
}) }) : null,
|
|
43639
|
-
!render ? /* @__PURE__ */ (0,
|
|
43898
|
+
!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
43899
|
"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,
|
|
43900
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)("code", { children: "signature" }),
|
|
43642
43901
|
" \u2014 add one and the signing appears."
|
|
43643
|
-
] }) : !rights.write ? /* @__PURE__ */ (0,
|
|
43902
|
+
] }) : !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
43903
|
const already = signatures.find((s) => s.field_key === field2.key);
|
|
43645
43904
|
if (already) {
|
|
43646
|
-
return /* @__PURE__ */ (0,
|
|
43905
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
43647
43906
|
fieldName(field2),
|
|
43648
43907
|
" 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
43908
|
] }, field2.id);
|
|
43650
43909
|
}
|
|
43651
|
-
return /* @__PURE__ */ (0,
|
|
43910
|
+
return /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)(
|
|
43652
43911
|
"form",
|
|
43653
43912
|
{
|
|
43654
43913
|
className: "flex items-center gap-1",
|
|
@@ -43657,8 +43916,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43657
43916
|
if (name.trim() !== "") void sign(field2);
|
|
43658
43917
|
},
|
|
43659
43918
|
children: [
|
|
43660
|
-
/* @__PURE__ */ (0,
|
|
43661
|
-
|
|
43919
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(
|
|
43920
|
+
import_design_system121.BasicInput,
|
|
43662
43921
|
{
|
|
43663
43922
|
className: "h-8 w-56 text-xs",
|
|
43664
43923
|
"aria-label": `Sign ${fieldName(field2)}`,
|
|
@@ -43667,8 +43926,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43667
43926
|
onChange: (e) => setName(e.target.value)
|
|
43668
43927
|
}
|
|
43669
43928
|
),
|
|
43670
|
-
/* @__PURE__ */ (0,
|
|
43671
|
-
/* @__PURE__ */ (0,
|
|
43929
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(import_design_system121.Button, { size: "sm", type: "submit", disabled: busy || name.trim() === "", children: "Sign" }),
|
|
43930
|
+
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
43672
43931
|
"seals version ",
|
|
43673
43932
|
render.template_version,
|
|
43674
43933
|
" \xB7 ",
|
|
@@ -43683,15 +43942,270 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
43683
43942
|
] });
|
|
43684
43943
|
}
|
|
43685
43944
|
|
|
43945
|
+
// src/NotifyRuleEditor.tsx
|
|
43946
|
+
var import_react241 = require("react");
|
|
43947
|
+
var import_react242 = require("@ai-matrx/records/react");
|
|
43948
|
+
var import_records57 = require("@ai-matrx/records");
|
|
43949
|
+
var import_design_system122 = require("@ai-matrx/design-system");
|
|
43950
|
+
var import_jsx_runtime137 = require("react/jsx-runtime");
|
|
43951
|
+
var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
|
|
43952
|
+
var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
43953
|
+
function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className }) {
|
|
43954
|
+
const client = (0, import_react242.useRecordsClient)();
|
|
43955
|
+
const host = useRecordsUi();
|
|
43956
|
+
const table = (0, import_react242.useTable)(tableId);
|
|
43957
|
+
const rights = useTableRights(table.data);
|
|
43958
|
+
const [subscriptions, setSubscriptions] = (0, import_react241.useState)(null);
|
|
43959
|
+
const [cadences, setCadences] = (0, import_react241.useState)([]);
|
|
43960
|
+
const [views, setViews] = (0, import_react241.useState)(null);
|
|
43961
|
+
const [error, setError] = (0, import_react241.useState)(null);
|
|
43962
|
+
const [busy, setBusy] = (0, import_react241.useState)(false);
|
|
43963
|
+
const [stopping, setStopping] = (0, import_react241.useState)(null);
|
|
43964
|
+
const load = (0, import_react241.useCallback)(async () => {
|
|
43965
|
+
const [held, offered] = await Promise.all([
|
|
43966
|
+
// THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
|
|
43967
|
+
// to them plus — only where they hold admin on this Table — anyone's over
|
|
43968
|
+
// it, and it is narrowed to Tables they can already open.
|
|
43969
|
+
client.subscriptions({ table_id: tableId }),
|
|
43970
|
+
// The cadences are ASKED of the store, never a list typed into a picker
|
|
43971
|
+
// here that could drift from what the digest runner understands.
|
|
43972
|
+
client.subscriptionCadences()
|
|
43973
|
+
]);
|
|
43974
|
+
if (!held.ok) {
|
|
43975
|
+
setError(held.error);
|
|
43976
|
+
return;
|
|
43977
|
+
}
|
|
43978
|
+
setError(null);
|
|
43979
|
+
setSubscriptions(held.data);
|
|
43980
|
+
if (offered.ok) setCadences(offered.data);
|
|
43981
|
+
if (host.savedViews) {
|
|
43982
|
+
setViews(await host.savedViews());
|
|
43983
|
+
return;
|
|
43984
|
+
}
|
|
43985
|
+
const listed = await client.views({ table_id: tableId });
|
|
43986
|
+
if (!listed.ok) {
|
|
43987
|
+
setError(listed.error);
|
|
43988
|
+
return;
|
|
43989
|
+
}
|
|
43990
|
+
setViews(listed.data.map((v) => ({ id: v.view_id, name: v.name })));
|
|
43991
|
+
}, [client, host, tableId]);
|
|
43992
|
+
(0, import_react241.useEffect)(() => {
|
|
43993
|
+
void load();
|
|
43994
|
+
}, [load]);
|
|
43995
|
+
const write = (0, import_react241.useCallback)(
|
|
43996
|
+
async (spec) => {
|
|
43997
|
+
const declared2 = await client.subscriptionDeclare({
|
|
43998
|
+
table_id: tableId,
|
|
43999
|
+
spec: {
|
|
44000
|
+
name: spec.name,
|
|
44001
|
+
saved_view_id: spec.savedViewId,
|
|
44002
|
+
cadence: spec.cadence ?? "instant",
|
|
44003
|
+
schedule: spec.schedule ?? null,
|
|
44004
|
+
channel: spec.channel ?? "in_app",
|
|
44005
|
+
quiet_hours: spec.quietHours ?? null,
|
|
44006
|
+
// "Tell me" is the default, and the store refuses a subscription with
|
|
44007
|
+
// nobody to tell rather than writing one that fires at nobody.
|
|
44008
|
+
recipient_user_id: spec.recipientUserId ?? client.config.actor.user_id ?? null,
|
|
44009
|
+
event_key: spec.eventKey ?? "records.changed"
|
|
44010
|
+
}
|
|
44011
|
+
});
|
|
44012
|
+
if (!declared2.ok) {
|
|
44013
|
+
setError(declared2.error);
|
|
44014
|
+
return null;
|
|
44015
|
+
}
|
|
44016
|
+
return declared2.data;
|
|
44017
|
+
},
|
|
44018
|
+
[client, tableId]
|
|
44019
|
+
);
|
|
44020
|
+
(0, import_react241.useEffect)(() => {
|
|
44021
|
+
if (!subscriptions || !seed || seed.length === 0) return;
|
|
44022
|
+
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
44023
|
+
if (missing.length === 0) return;
|
|
44024
|
+
void (async () => {
|
|
44025
|
+
for (const spec of missing) {
|
|
44026
|
+
if (!await write(spec)) return;
|
|
44027
|
+
}
|
|
44028
|
+
await load();
|
|
44029
|
+
})();
|
|
44030
|
+
}, [subscriptions, seed, write, load]);
|
|
44031
|
+
async function subscribe(viewId, viewName, cadence, channel, schedule, quiet2) {
|
|
44032
|
+
setBusy(true);
|
|
44033
|
+
const ok = await write({
|
|
44034
|
+
name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
|
|
44035
|
+
savedViewId: viewId,
|
|
44036
|
+
cadence,
|
|
44037
|
+
channel,
|
|
44038
|
+
schedule: schedule.trim() === "" ? null : schedule.trim(),
|
|
44039
|
+
quietHours: quiet2
|
|
44040
|
+
});
|
|
44041
|
+
setBusy(false);
|
|
44042
|
+
if (!ok) return;
|
|
44043
|
+
onChoose?.(ok);
|
|
44044
|
+
await load();
|
|
44045
|
+
}
|
|
44046
|
+
async function makeEveryRecordView() {
|
|
44047
|
+
setBusy(true);
|
|
44048
|
+
const made = await client.viewDeclare({
|
|
44049
|
+
table_id: tableId,
|
|
44050
|
+
spec: { name: `Every ${table.data?.name ?? "record"}`, filters: null }
|
|
44051
|
+
});
|
|
44052
|
+
setBusy(false);
|
|
44053
|
+
if (!made.ok) {
|
|
44054
|
+
setError(made.error);
|
|
44055
|
+
return;
|
|
44056
|
+
}
|
|
44057
|
+
await load();
|
|
44058
|
+
}
|
|
44059
|
+
async function unsubscribe(ruleId) {
|
|
44060
|
+
setStopping(null);
|
|
44061
|
+
setBusy(true);
|
|
44062
|
+
const deleted = await client.recordDelete({ record_id: ruleId });
|
|
44063
|
+
setBusy(false);
|
|
44064
|
+
if (!deleted.ok) {
|
|
44065
|
+
setError(deleted.error);
|
|
44066
|
+
return;
|
|
44067
|
+
}
|
|
44068
|
+
await load();
|
|
44069
|
+
}
|
|
44070
|
+
if (error) {
|
|
44071
|
+
return /* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44072
|
+
RefusalNotice,
|
|
44073
|
+
{
|
|
44074
|
+
error,
|
|
44075
|
+
className,
|
|
44076
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
44077
|
+
}
|
|
44078
|
+
);
|
|
44079
|
+
}
|
|
44080
|
+
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) });
|
|
44081
|
+
return /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("div", { className: (0, import_design_system122.cn)("flex flex-col gap-2", className), children: [
|
|
44082
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
44083
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "font-medium text-foreground", children: "Tell me when" }),
|
|
44084
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("span", { children: [
|
|
44085
|
+
subscriptions.length,
|
|
44086
|
+
" subscription",
|
|
44087
|
+
subscriptions.length === 1 ? "" : "s"
|
|
44088
|
+
] })
|
|
44089
|
+
] }),
|
|
44090
|
+
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: [
|
|
44091
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "truncate font-medium", children: held.name }),
|
|
44092
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-muted-foreground", children: CADENCE_WORDS2[held.cadence] ?? held.cadence }),
|
|
44093
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-muted-foreground", children: CHANNEL_WORDS3[held.channel] ?? held.channel }),
|
|
44094
|
+
held.schedule ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-muted-foreground", children: scheduleWords(held.schedule, held.quiet_hours?.tz) }) : null,
|
|
44095
|
+
held.next_digest_at ? /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
44096
|
+
"next ",
|
|
44097
|
+
new Date(held.next_digest_at).toLocaleString()
|
|
44098
|
+
] }) : null,
|
|
44099
|
+
(0, import_records57.hasQuietHours)(held.quiet_hours) ? /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
44100
|
+
"quiet ",
|
|
44101
|
+
held.quiet_hours.start,
|
|
44102
|
+
"\u2013",
|
|
44103
|
+
held.quiet_hours.end
|
|
44104
|
+
] }) : null,
|
|
44105
|
+
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,
|
|
44106
|
+
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,
|
|
44107
|
+
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,
|
|
44108
|
+
onChoose ? held.rule_id === chosenRuleId ? /* @__PURE__ */ (0, import_jsx_runtime137.jsxs)("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
44109
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "font-medium text-foreground", children: "Used for this form" }),
|
|
44110
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", variant: "ghost", disabled: busy, onClick: () => onChoose(null), children: "Stop using" })
|
|
44111
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44112
|
+
import_design_system122.Button,
|
|
44113
|
+
{
|
|
44114
|
+
size: "sm",
|
|
44115
|
+
variant: "outline",
|
|
44116
|
+
className: "ml-auto",
|
|
44117
|
+
disabled: busy,
|
|
44118
|
+
onClick: () => onChoose(held.rule_id),
|
|
44119
|
+
children: "Use for this form"
|
|
44120
|
+
}
|
|
44121
|
+
) : null,
|
|
44122
|
+
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: [
|
|
44123
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-xs text-destructive", children: archiveConfirmSentence("this subscription") }),
|
|
44124
|
+
/* @__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" }),
|
|
44125
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", variant: "ghost", onClick: () => setStopping(null), children: "Keep it" })
|
|
44126
|
+
] }) : rights.write && !(onChoose && held.rule_id === chosenRuleId) ? /* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44127
|
+
import_design_system122.Button,
|
|
44128
|
+
{
|
|
44129
|
+
size: "sm",
|
|
44130
|
+
variant: "ghost",
|
|
44131
|
+
className: onChoose ? void 0 : "ml-auto",
|
|
44132
|
+
disabled: busy,
|
|
44133
|
+
onClick: () => setStopping(held.rule_id),
|
|
44134
|
+
children: "Stop telling me"
|
|
44135
|
+
}
|
|
44136
|
+
) : null
|
|
44137
|
+
] }, held.rule_id)) }),
|
|
44138
|
+
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,
|
|
44139
|
+
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: [
|
|
44140
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { children: "A subscription watches a saved view, and this table has none yet." }),
|
|
44141
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", variant: "outline", disabled: busy, onClick: () => void makeEveryRecordView(), children: "Watch every record" })
|
|
44142
|
+
] }) : /* @__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)(
|
|
44143
|
+
"form",
|
|
44144
|
+
{
|
|
44145
|
+
className: "flex flex-wrap items-center gap-1",
|
|
44146
|
+
onSubmit: (event) => {
|
|
44147
|
+
event.preventDefault();
|
|
44148
|
+
const form = event.currentTarget;
|
|
44149
|
+
const view = form.elements.namedItem("view").value;
|
|
44150
|
+
const cadence = form.elements.namedItem("cadence").value;
|
|
44151
|
+
const channel = form.elements.namedItem("channel").value;
|
|
44152
|
+
const schedule = form.elements.namedItem("schedule").value;
|
|
44153
|
+
const start = form.elements.namedItem("quiet_start").value;
|
|
44154
|
+
const end = form.elements.namedItem("quiet_end").value;
|
|
44155
|
+
const name = views.find((v) => v.id === view)?.name ?? "View";
|
|
44156
|
+
const quiet2 = start && end ? { start, end, tz: Intl.DateTimeFormat().resolvedOptions().timeZone } : null;
|
|
44157
|
+
if (view) void subscribe(view, name, cadence, channel, schedule, quiet2);
|
|
44158
|
+
},
|
|
44159
|
+
children: [
|
|
44160
|
+
/* @__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)) }),
|
|
44161
|
+
/* @__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)) }),
|
|
44162
|
+
/* @__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)) }),
|
|
44163
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44164
|
+
"input",
|
|
44165
|
+
{
|
|
44166
|
+
name: "schedule",
|
|
44167
|
+
"aria-label": "When the summary goes out",
|
|
44168
|
+
placeholder: "monday 08:00",
|
|
44169
|
+
className: "h-7 w-28 rounded border bg-background px-1 text-xs"
|
|
44170
|
+
}
|
|
44171
|
+
),
|
|
44172
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-xs text-muted-foreground", children: "not between" }),
|
|
44173
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44174
|
+
"input",
|
|
44175
|
+
{
|
|
44176
|
+
name: "quiet_start",
|
|
44177
|
+
type: "time",
|
|
44178
|
+
"aria-label": "Quiet hours start",
|
|
44179
|
+
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
44180
|
+
}
|
|
44181
|
+
),
|
|
44182
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)("span", { className: "text-xs text-muted-foreground", children: "and" }),
|
|
44183
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
|
|
44184
|
+
"input",
|
|
44185
|
+
{
|
|
44186
|
+
name: "quiet_end",
|
|
44187
|
+
type: "time",
|
|
44188
|
+
"aria-label": "Quiet hours end",
|
|
44189
|
+
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
44190
|
+
}
|
|
44191
|
+
),
|
|
44192
|
+
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(import_design_system122.Button, { size: "sm", type: "submit", disabled: busy, children: "Tell me" }),
|
|
44193
|
+
/* @__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." })
|
|
44194
|
+
]
|
|
44195
|
+
}
|
|
44196
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime137.jsx)("p", { className: "text-xs text-muted-foreground", children: rights.why("write") })
|
|
44197
|
+
] });
|
|
44198
|
+
}
|
|
44199
|
+
|
|
43686
44200
|
// src/DashboardCanvas.tsx
|
|
43687
44201
|
var import_react249 = require("react");
|
|
43688
44202
|
var import_react250 = require("@ai-matrx/records/react");
|
|
43689
44203
|
var import_design_system126 = require("@ai-matrx/design-system");
|
|
43690
44204
|
|
|
43691
44205
|
// src/DashboardGrid.tsx
|
|
43692
|
-
var
|
|
43693
|
-
var
|
|
43694
|
-
var
|
|
44206
|
+
var import_core23 = require("@dnd-kit/core");
|
|
44207
|
+
var import_sortable2 = require("@dnd-kit/sortable");
|
|
44208
|
+
var import_utilities2 = require("@dnd-kit/utilities");
|
|
43695
44209
|
var import_design_system123 = require("@ai-matrx/design-system");
|
|
43696
44210
|
|
|
43697
44211
|
// src/dashboardLayout.ts
|
|
@@ -43745,7 +44259,7 @@ function withBlockIds(blocks) {
|
|
|
43745
44259
|
// src/DashboardGrid.tsx
|
|
43746
44260
|
var import_jsx_runtime138 = require("react/jsx-runtime");
|
|
43747
44261
|
var dashboardCollision = (args) => {
|
|
43748
|
-
const under = (0,
|
|
44262
|
+
const under = (0, import_core23.pointerWithin)(args);
|
|
43749
44263
|
if (under.length > 0) return under;
|
|
43750
44264
|
const at = args.collisionRect;
|
|
43751
44265
|
return args.droppableContainers.flatMap((container) => {
|
|
@@ -43755,9 +44269,9 @@ var dashboardCollision = (args) => {
|
|
|
43755
44269
|
}).sort((a, b) => a.data.value - b.data.value);
|
|
43756
44270
|
};
|
|
43757
44271
|
function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
43758
|
-
const sensors = (0,
|
|
43759
|
-
(0,
|
|
43760
|
-
(0,
|
|
44272
|
+
const sensors = (0, import_core23.useSensors)(
|
|
44273
|
+
(0, import_core23.useSensor)(import_core23.PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
44274
|
+
(0, import_core23.useSensor)(import_core23.KeyboardSensor, { coordinateGetter: import_sortable2.sortableKeyboardCoordinates })
|
|
43761
44275
|
);
|
|
43762
44276
|
const ids = items.map((item) => item.id);
|
|
43763
44277
|
function dragEnd(event) {
|
|
@@ -43767,7 +44281,7 @@ function DashboardGrid({ items, arranging, onMove, onSpan }) {
|
|
|
43767
44281
|
}
|
|
43768
44282
|
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
44283
|
if (!arranging) return grid;
|
|
43770
|
-
return /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(
|
|
44284
|
+
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
44285
|
}
|
|
43772
44286
|
function GridCell2({
|
|
43773
44287
|
item,
|
|
@@ -43775,8 +44289,8 @@ function GridCell2({
|
|
|
43775
44289
|
arranging,
|
|
43776
44290
|
onSpan
|
|
43777
44291
|
}) {
|
|
43778
|
-
const sortable = (0,
|
|
43779
|
-
const style = arranging ? { transform:
|
|
44292
|
+
const sortable = (0, import_sortable2.useSortable)({ id: item.id, disabled: !arranging });
|
|
44293
|
+
const style = arranging ? { transform: import_utilities2.CSS.Transform.toString(sortable.transform), transition: sortable.transition } : void 0;
|
|
43780
44294
|
return /* @__PURE__ */ (0, import_jsx_runtime138.jsxs)(
|
|
43781
44295
|
"div",
|
|
43782
44296
|
{
|
|
@@ -44980,7 +45494,7 @@ var import_react252 = require("@ai-matrx/records/react");
|
|
|
44980
45494
|
|
|
44981
45495
|
// src/question.ts
|
|
44982
45496
|
var import_records59 = require("@ai-matrx/records");
|
|
44983
|
-
var
|
|
45497
|
+
var import_core24 = require("@ai-matrx/records/core");
|
|
44984
45498
|
var QUESTION_OPS = ["count", "sum", "avg", "min", "max"];
|
|
44985
45499
|
var QUESTION_LIMIT_DEFAULT = 25;
|
|
44986
45500
|
var QUESTION_GROUP_CEILING = 200;
|
|
@@ -45232,7 +45746,7 @@ async function askAsOf(client, tableId, spec, fields, measures, keys) {
|
|
|
45232
45746
|
},
|
|
45233
45747
|
{ schema: config.schema ?? "custom" }
|
|
45234
45748
|
);
|
|
45235
|
-
if (response.error) return { ok: false, error: (0,
|
|
45749
|
+
if (response.error) return { ok: false, error: (0, import_core24.mapPgError)(response.error, "askQuestion.asOf") };
|
|
45236
45750
|
const rows2 = Array.isArray(response.data) ? response.data : [];
|
|
45237
45751
|
const bad = rows2.find((r) => Number(r.not_numbers ?? 0) > 0);
|
|
45238
45752
|
if (bad && m.op !== "count") {
|
|
@@ -45303,7 +45817,7 @@ function savedOf(row) {
|
|
|
45303
45817
|
}
|
|
45304
45818
|
async function questionsOf(client, tableId) {
|
|
45305
45819
|
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,
|
|
45820
|
+
if (response.error) return { ok: false, error: (0, import_core24.mapPgError)(response.error, "questionsOf") };
|
|
45307
45821
|
const me = client.config.actor?.user_id ?? null;
|
|
45308
45822
|
return {
|
|
45309
45823
|
ok: true,
|
|
@@ -45332,7 +45846,7 @@ async function questionSave(client, args) {
|
|
|
45332
45846
|
if (response.error.code === "23505") {
|
|
45333
45847
|
return { ok: false, error: { code: "already_exists", message: `You already have a question called \u201C${name}\u201D.` } };
|
|
45334
45848
|
}
|
|
45335
|
-
return { ok: false, error: (0,
|
|
45849
|
+
return { ok: false, error: (0, import_core24.mapPgError)(response.error, "questionSave") };
|
|
45336
45850
|
}
|
|
45337
45851
|
return { ok: true, data: response.data ? savedOf(response.data) : null };
|
|
45338
45852
|
}
|
|
@@ -45342,7 +45856,7 @@ async function questionArchive(client, id, version) {
|
|
|
45342
45856
|
{ p_surface_key: QUESTION_SURFACE, p_id: id, ...version !== void 0 ? { p_expected_version: version } : {} },
|
|
45343
45857
|
{ schema: "public" }
|
|
45344
45858
|
);
|
|
45345
|
-
if (response.error) return { ok: false, error: (0,
|
|
45859
|
+
if (response.error) return { ok: false, error: (0, import_core24.mapPgError)(response.error, "questionArchive") };
|
|
45346
45860
|
return { ok: true, data: response.data !== null };
|
|
45347
45861
|
}
|
|
45348
45862
|
|
|
@@ -46201,7 +46715,7 @@ function TableAsk({ tableId, onOpenInQuestions }) {
|
|
|
46201
46715
|
// src/PageScreen.tsx
|
|
46202
46716
|
var import_react255 = require("react");
|
|
46203
46717
|
var import_react256 = require("@ai-matrx/records/react");
|
|
46204
|
-
var
|
|
46718
|
+
var import_core25 = require("@ai-matrx/records/core");
|
|
46205
46719
|
var import_design_system129 = require("@ai-matrx/design-system");
|
|
46206
46720
|
var import_jsx_runtime145 = require("react/jsx-runtime");
|
|
46207
46721
|
var EDIT_LEVELS = /* @__PURE__ */ new Set(["edit_content", "editor", "admin", "owner"]);
|
|
@@ -46617,7 +47131,7 @@ function ListBlockView({
|
|
|
46617
47131
|
...sort.length > 0 ? { sort } : {}
|
|
46618
47132
|
});
|
|
46619
47133
|
const reload = page.reload;
|
|
46620
|
-
(0, import_react255.useEffect)(() => (0,
|
|
47134
|
+
(0, import_react255.useEffect)(() => (0, import_core25.onRecordChanged)(() => reload()), [reload]);
|
|
46621
47135
|
const rows = nobodyIsMe ? [] : page.data?.rows ?? [];
|
|
46622
47136
|
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
47137
|
const firstId = rows[0]?.id ?? null;
|
|
@@ -48152,7 +48666,7 @@ var import_design_system142 = require("@ai-matrx/design-system");
|
|
|
48152
48666
|
var import_text_case19 = require("@ai-matrx/kit/text-case");
|
|
48153
48667
|
|
|
48154
48668
|
// src/viewShare.ts
|
|
48155
|
-
var
|
|
48669
|
+
var import_core26 = require("@ai-matrx/records/core");
|
|
48156
48670
|
var NO_VIEW_REASON = "No saved view yet";
|
|
48157
48671
|
function viewLink(tableId, viewId, origin) {
|
|
48158
48672
|
return `${tableLink(tableId, origin)}?view=${encodeURIComponent(viewId)}`;
|
|
@@ -48188,7 +48702,7 @@ async function downloadViewCsv(args) {
|
|
|
48188
48702
|
const columns = viewColumns(fields, view.presentation);
|
|
48189
48703
|
const rows = await rowsForFile({ client, host, fields: columns, records: read.rows, labels });
|
|
48190
48704
|
const name = fileNameOf(args.tableName) || "records";
|
|
48191
|
-
download(`${name}.csv`, "text/csv;charset=utf-8", (0,
|
|
48705
|
+
download(`${name}.csv`, "text/csv;charset=utf-8", (0, import_core26.exportCsv)(columns, rows));
|
|
48192
48706
|
host.notify?.success(read.cut ? cutWords(read.rows.length) : "CSV downloaded");
|
|
48193
48707
|
}
|
|
48194
48708
|
|