@ai-matrx/records-ui 0.83.1 → 0.84.4
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 +153 -0
- package/dist/index.cjs +354 -194
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -3
- package/dist/index.d.ts +33 -3
- package/dist/index.js +355 -194
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -40,6 +40,7 @@ __export(src_exports, {
|
|
|
40
40
|
ArchivedDisclosure: () => ArchivedDisclosure,
|
|
41
41
|
ArchivedPortals: () => ArchivedPortals,
|
|
42
42
|
ArchivedView: () => ArchivedView,
|
|
43
|
+
BOOKING_NOT_HERE_LINE: () => BOOKING_NOT_HERE_LINE,
|
|
43
44
|
BOOKING_PAGE_STATE_LABEL: () => BOOKING_PAGE_STATE_LABEL,
|
|
44
45
|
BOOKING_PAGE_STATE_VALUES: () => BOOKING_PAGE_STATE_VALUES,
|
|
45
46
|
BookingBuilder: () => BookingBuilder,
|
|
@@ -256,6 +257,7 @@ __export(src_exports, {
|
|
|
256
257
|
mayNotRestoreLine: () => mayNotRestoreLine,
|
|
257
258
|
memberName: () => memberName,
|
|
258
259
|
nextInWords: () => nextInWords,
|
|
260
|
+
nextSummaryAt: () => nextSummaryAt,
|
|
259
261
|
openingRail: () => openingRail,
|
|
260
262
|
openingView: () => openingView,
|
|
261
263
|
pageViewFromParam: () => pageViewFromParam,
|
|
@@ -560,13 +562,20 @@ function plainSentence(message) {
|
|
|
560
562
|
}
|
|
561
563
|
return { kept: kept.length === 0 ? null : kept.join(" "), dropped };
|
|
562
564
|
}
|
|
565
|
+
function exampleOf(error) {
|
|
566
|
+
const detail = error.detail;
|
|
567
|
+
if (!detail || typeof detail !== "object") return null;
|
|
568
|
+
const example = detail.example;
|
|
569
|
+
return typeof example === "string" && example.trim() !== "" ? example.trim() : null;
|
|
570
|
+
}
|
|
563
571
|
function refusalForAPerson(error) {
|
|
564
572
|
const code = error.code ?? "internal";
|
|
565
573
|
const message = plainSentence(String(error.message ?? ""));
|
|
566
574
|
const hint = plainSentence(String(error.hint ?? ""));
|
|
567
575
|
const title = TITLE[code] ?? "The store refused this";
|
|
568
576
|
const hintIsForEngineers = isEngineerText(String(error.hint ?? ""));
|
|
569
|
-
const
|
|
577
|
+
const example = exampleOf(error);
|
|
578
|
+
const remedy = (example ? `Enter it like ${example}.` : null) ?? (hintIsForEngineers ? null : hint.kept) ?? REMEDY[code] ?? REMEDY["internal"];
|
|
570
579
|
const messageIsForEngineers = isEngineerText(String(error.message ?? ""));
|
|
571
580
|
const said = (messageIsForEngineers ? "" : message.kept ?? "").trim();
|
|
572
581
|
const forEngineers = [
|
|
@@ -590,7 +599,9 @@ function refusalForAPerson(error) {
|
|
|
590
599
|
}
|
|
591
600
|
function refusalLineForAPerson(error) {
|
|
592
601
|
const plain = refusalForAPerson(error);
|
|
593
|
-
|
|
602
|
+
const sentence2 = plain.sentence.trim();
|
|
603
|
+
const ended = sentence2 === "" || /[.!?…:]$/.test(sentence2) ? sentence2 : `${sentence2}.`;
|
|
604
|
+
return `${ended} ${plain.remedy}`.trim();
|
|
594
605
|
}
|
|
595
606
|
|
|
596
607
|
// src/names.ts
|
|
@@ -1060,6 +1071,42 @@ function asRefusal(thrown, sentence2, remedy, code = "internal") {
|
|
|
1060
1071
|
return refusalFromThrown(thrown, sentence2, remedy, code);
|
|
1061
1072
|
}
|
|
1062
1073
|
|
|
1074
|
+
// src/tellApart.ts
|
|
1075
|
+
function shown(value) {
|
|
1076
|
+
if (value === null || value === void 0) return null;
|
|
1077
|
+
if (typeof value === "string") return value.trim() === "" ? null : value.trim();
|
|
1078
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
function tellApart(rows, nameOf) {
|
|
1082
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1083
|
+
for (const row of rows) {
|
|
1084
|
+
const name = nameOf(row.id).trim().toLowerCase();
|
|
1085
|
+
byName.set(name, [...byName.get(name) ?? [], row]);
|
|
1086
|
+
}
|
|
1087
|
+
const out = /* @__PURE__ */ new Map();
|
|
1088
|
+
for (const [name, group] of byName) {
|
|
1089
|
+
if (group.length < 2) continue;
|
|
1090
|
+
const keys = [
|
|
1091
|
+
...new Set(
|
|
1092
|
+
group.flatMap(
|
|
1093
|
+
(row) => Object.keys(row.document ?? {}).filter((k) => !k.startsWith("_") && shown(row.document?.[k]) !== null)
|
|
1094
|
+
)
|
|
1095
|
+
)
|
|
1096
|
+
].filter((k) => group.some((row) => (shown(row.document?.[k]) ?? "").toLowerCase() !== name));
|
|
1097
|
+
const valuesOf = (k) => group.map((row) => shown(row.document?.[k]) ?? "");
|
|
1098
|
+
const differs = keys.filter((k) => new Set(valuesOf(k)).size > 1);
|
|
1099
|
+
const whole = differs.find((k) => new Set(valuesOf(k)).size === group.length);
|
|
1100
|
+
const chosen = whole ? [whole] : differs.slice(0, 2);
|
|
1101
|
+
if (chosen.length === 0) continue;
|
|
1102
|
+
for (const row of group) {
|
|
1103
|
+
const words = chosen.map((k) => shown(row.document?.[k])).filter((w) => w !== null);
|
|
1104
|
+
if (words.length > 0) out.set(row.id, words.join(" \xB7 "));
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
return out;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1063
1110
|
// src/RelationPicker.tsx
|
|
1064
1111
|
var import_jsx_runtime4 = (
|
|
1065
1112
|
// The Field's own name is already printed above this control by
|
|
@@ -1201,9 +1248,12 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1201
1248
|
setSearch("");
|
|
1202
1249
|
toggle(createdId);
|
|
1203
1250
|
};
|
|
1204
|
-
const
|
|
1205
|
-
|
|
1206
|
-
|
|
1251
|
+
const apart = tellApart(rows ?? [], titleOf);
|
|
1252
|
+
const visible = (rows ?? []).filter((row) => {
|
|
1253
|
+
const q = search.trim().toLowerCase();
|
|
1254
|
+
if (q === "") return true;
|
|
1255
|
+
return `${titleOf(row.id)} ${apart.get(row.id) ?? ""}`.toLowerCase().includes(q);
|
|
1256
|
+
});
|
|
1207
1257
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: (0, import_design_system3.cn)("flex flex-wrap items-center gap-1", className), children: [
|
|
1208
1258
|
picked.map((recordId) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1209
1259
|
RecordChip,
|
|
@@ -1234,17 +1284,21 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1234
1284
|
" at most. Remove one to add another."
|
|
1235
1285
|
] }) : null,
|
|
1236
1286
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system3.ScrollArea, { className: "max-h-56", children: [
|
|
1237
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { children: visible.map((row) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime4.
|
|
1287
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { children: visible.map((row) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1238
1288
|
"button",
|
|
1239
1289
|
{
|
|
1240
1290
|
type: "button",
|
|
1241
1291
|
onClick: () => toggle(row.id),
|
|
1242
|
-
title: titleOf(row.id),
|
|
1292
|
+
title: apart.has(row.id) ? `${titleOf(row.id)} \u2014 ${apart.get(row.id)}` : titleOf(row.id),
|
|
1293
|
+
"data-relation-candidate": row.id,
|
|
1243
1294
|
className: (0, import_design_system3.cn)(
|
|
1244
1295
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1245
1296
|
picked.includes(row.id) ? "bg-accent text-accent-foreground" : ""
|
|
1246
1297
|
),
|
|
1247
|
-
children:
|
|
1298
|
+
children: [
|
|
1299
|
+
titleOf(row.id),
|
|
1300
|
+
apart.has(row.id) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "block truncate text-[11px] text-muted-foreground", "data-tell-apart": "", children: apart.get(row.id) }) : null
|
|
1301
|
+
]
|
|
1248
1302
|
}
|
|
1249
1303
|
) }, row.id)) }),
|
|
1250
1304
|
rows && visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Nothing in that table matches." }) : null
|
|
@@ -2176,7 +2230,7 @@ var FIELD_RULE_KIND_VALUES = ["min", "max", "length", "pattern", "equals_field",
|
|
|
2176
2230
|
var FIELD_RULE_KIND_LABEL = {
|
|
2177
2231
|
min: "At least",
|
|
2178
2232
|
max: "At most",
|
|
2179
|
-
length: "
|
|
2233
|
+
length: "Number of characters",
|
|
2180
2234
|
pattern: "Looks like",
|
|
2181
2235
|
equals_field: "Same as the column",
|
|
2182
2236
|
differs_from_field: "Different from the column"
|
|
@@ -2208,6 +2262,20 @@ function envelopeFor(document2, key) {
|
|
|
2208
2262
|
const block = document2?._values;
|
|
2209
2263
|
return block?.[key];
|
|
2210
2264
|
}
|
|
2265
|
+
function unresolvedFor(document2, key) {
|
|
2266
|
+
const sources = document2?._sources;
|
|
2267
|
+
if (!sources || typeof sources !== "object") return void 0;
|
|
2268
|
+
for (const source of Object.values(sources)) {
|
|
2269
|
+
const held = source?.unresolved?.[key];
|
|
2270
|
+
if (typeof held === "string" && held) return held;
|
|
2271
|
+
if (Array.isArray(held) && held.length) return held.map(String).join(", ");
|
|
2272
|
+
}
|
|
2273
|
+
return void 0;
|
|
2274
|
+
}
|
|
2275
|
+
function hasFormula(field) {
|
|
2276
|
+
const config = field.config ?? {};
|
|
2277
|
+
return Boolean(config.expr) || Boolean(String(config.formula_text ?? "").trim());
|
|
2278
|
+
}
|
|
2211
2279
|
var ABSENCE_WORDS = ABSENCE_WORD_LABEL;
|
|
2212
2280
|
function renderValue(field, value, document2, labels, stillWaitingFor) {
|
|
2213
2281
|
const envelope = envelopeFor(document2, field.key);
|
|
@@ -2215,6 +2283,13 @@ function renderValue(field, value, document2, labels, stillWaitingFor) {
|
|
|
2215
2283
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs italic text-muted-foreground", title: `VAL-2: ${envelope.absent}`, children: ABSENCE_WORDS[String(envelope.absent)] ?? String(envelope.absent) });
|
|
2216
2284
|
}
|
|
2217
2285
|
if (value === null || value === void 0 || value === "") {
|
|
2286
|
+
const gone = unresolvedFor(document2, field.key);
|
|
2287
|
+
if (gone) {
|
|
2288
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs italic text-muted-foreground", title: `It pointed at ${gone}`, children: "The record this pointed at no longer exists" });
|
|
2289
|
+
}
|
|
2290
|
+
if (field.type === "formula" && !hasFormula(field)) {
|
|
2291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs italic text-muted-foreground", children: "Worked out once its formula is in place" });
|
|
2292
|
+
}
|
|
2218
2293
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
2219
2294
|
}
|
|
2220
2295
|
const kind = editorKindFor(field);
|
|
@@ -3241,7 +3316,7 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3241
3316
|
(0, import_react17.useEffect)(() => {
|
|
3242
3317
|
void load();
|
|
3243
3318
|
}, [load]);
|
|
3244
|
-
const
|
|
3319
|
+
const shown2 = (0, import_react17.useMemo)(
|
|
3245
3320
|
() => fieldId ? (rows ?? []).filter((r) => r.field_id === fieldId) : rows ?? [],
|
|
3246
3321
|
[rows, fieldId]
|
|
3247
3322
|
);
|
|
@@ -3300,7 +3375,7 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3300
3375
|
);
|
|
3301
3376
|
if (error) return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RefusalNotice, { error, className });
|
|
3302
3377
|
if (rows === null) return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_design_system8.Skeleton, { className: (0, import_design_system8.cn)("h-40 w-full", className) });
|
|
3303
|
-
if (
|
|
3378
|
+
if (shown2.length === 0 && fieldId) {
|
|
3304
3379
|
const target = (fields.data ?? []).find((f) => f.id === fieldId);
|
|
3305
3380
|
const setup = draft[fieldId] ?? { instruction: "", every: "30" };
|
|
3306
3381
|
if (!rights.admin) {
|
|
@@ -3363,13 +3438,13 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3363
3438
|
outcome ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground", children: outcome.message }) : null
|
|
3364
3439
|
] });
|
|
3365
3440
|
}
|
|
3366
|
-
if (
|
|
3441
|
+
if (shown2.length === 0) {
|
|
3367
3442
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: (0, import_design_system8.cn)("space-y-2 p-4 text-sm", className), children: [
|
|
3368
3443
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "font-medium", children: "No column here is filled in by a model." }),
|
|
3369
3444
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-muted-foreground", children: rights.admin ? "Open a column's menu and choose \u201CLet AI fill\u201D to set one up \u2014 or just ask for it in plain words and the agent will." : "An admin on this table sets that up." })
|
|
3370
3445
|
] });
|
|
3371
3446
|
}
|
|
3372
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: (0, import_design_system8.cn)("space-y-6 p-4", className), children:
|
|
3447
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: (0, import_design_system8.cn)("space-y-6 p-4", className), children: shown2.map((row) => {
|
|
3373
3448
|
const d = draft[row.field_id] ?? {
|
|
3374
3449
|
instruction: row.enrichment?.instruction ?? "",
|
|
3375
3450
|
every: row.review_interval_days ? String(row.review_interval_days) : ""
|
|
@@ -3658,18 +3733,18 @@ function Outcome({ outcome, onClose }) {
|
|
|
3658
3733
|
] });
|
|
3659
3734
|
}
|
|
3660
3735
|
function PlanTable({ plan }) {
|
|
3661
|
-
const
|
|
3662
|
-
const keys = Array.from(new Set(
|
|
3663
|
-
if (
|
|
3736
|
+
const shown2 = plan.rows.slice(0, 8);
|
|
3737
|
+
const keys = Array.from(new Set(shown2.flatMap((row) => row.cells.map((c) => c.key))));
|
|
3738
|
+
if (shown2.length === 0 || keys.length === 0) return null;
|
|
3664
3739
|
const columnName = /* @__PURE__ */ new Map();
|
|
3665
|
-
for (const row of
|
|
3740
|
+
for (const row of shown2) for (const cell of row.cells) if (!columnName.has(cell.key)) columnName.set(cell.key, cell.column);
|
|
3666
3741
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "overflow-auto rounded border", children: [
|
|
3667
3742
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("table", { className: "w-full text-xs", "data-matrx-paste-plan": true, children: [
|
|
3668
3743
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("thead", { className: "bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { children: [
|
|
3669
3744
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "Row" }),
|
|
3670
3745
|
keys.map((key) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: columnName.get(key) ?? key }, key))
|
|
3671
3746
|
] }) }),
|
|
3672
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tbody", { children:
|
|
3747
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tbody", { children: shown2.map((row) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { className: "border-t", children: [
|
|
3673
3748
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("td", { className: "whitespace-nowrap px-2 py-1", children: [
|
|
3674
3749
|
row.fromLine,
|
|
3675
3750
|
row.recordId === null ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_design_system9.Badge, { variant: "outline", className: "ml-1", children: "new" }) : null
|
|
@@ -3691,9 +3766,9 @@ function PlanTable({ plan }) {
|
|
|
3691
3766
|
})
|
|
3692
3767
|
] }, row.fromLine)) })
|
|
3693
3768
|
] }),
|
|
3694
|
-
plan.rows.length >
|
|
3769
|
+
plan.rows.length > shown2.length ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "px-2 py-1 text-xs text-muted-foreground", children: [
|
|
3695
3770
|
"\u2026and ",
|
|
3696
|
-
plan.rows.length -
|
|
3771
|
+
plan.rows.length - shown2.length,
|
|
3697
3772
|
" more rows the same way."
|
|
3698
3773
|
] }) : null
|
|
3699
3774
|
] });
|
|
@@ -4753,7 +4828,6 @@ function checkImportRules(fields, rows, mapping) {
|
|
|
4753
4828
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
4754
4829
|
var BATCH = 250;
|
|
4755
4830
|
var SAMPLES = 40;
|
|
4756
|
-
var MAPPING_COLUMNS = "minmax(9rem, 11rem) minmax(14rem, 17rem) minmax(13rem, 1fr) minmax(8rem, 10rem)";
|
|
4757
4831
|
var ABSENT = "__absent__";
|
|
4758
4832
|
function obviousTitleColumn(columns) {
|
|
4759
4833
|
const says = /^(title|name|record|customer|client|company|job|item|subject|label|description)\b|(\bname|\btitle|\bnumber|\bid)$/i;
|
|
@@ -4770,7 +4844,11 @@ async function sha256(bytes) {
|
|
|
4770
4844
|
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4771
4845
|
}
|
|
4772
4846
|
function typeWord(t) {
|
|
4773
|
-
|
|
4847
|
+
if (t === "range") return fieldTypeChoice("number")?.label ?? "Number";
|
|
4848
|
+
return fieldTypeChoice(t)?.label ?? (t === "relation" ? "Points at another table" : humanize(t));
|
|
4849
|
+
}
|
|
4850
|
+
function lineInYourFile(dataRow) {
|
|
4851
|
+
return dataRow + 1;
|
|
4774
4852
|
}
|
|
4775
4853
|
function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
4776
4854
|
const client = (0, import_react27.useRecordsClient)();
|
|
@@ -5011,6 +5089,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5011
5089
|
const refusedByRules = ruleVerdicts.reduce((n, v) => n + v.refused, 0);
|
|
5012
5090
|
const [goAheadAnyway, setGoAheadAnyway] = (0, import_react26.useState)(false);
|
|
5013
5091
|
(0, import_react26.useEffect)(() => setGoAheadAnyway(false), [ruleVerdicts]);
|
|
5092
|
+
const afterWrite = !busy && (phase === "done" || alreadyRan !== null || ledger !== null && ledger.seen > 0);
|
|
5014
5093
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: (0, import_design_system13.cn)("flex min-w-0 flex-col gap-2 text-xs", className), children: [
|
|
5015
5094
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
5016
5095
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: "Import" }),
|
|
@@ -5041,7 +5120,11 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5041
5120
|
problem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalNotice, { error: problem }) : null,
|
|
5042
5121
|
phase === "reading" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
|
|
5043
5122
|
phase === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
|
|
5044
|
-
plan && parsed ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
5123
|
+
plan && parsed && afterWrite ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "text-muted-foreground", "data-import-after-write": "", children: [
|
|
5124
|
+
fileName ? `${fileName} has been sent to this table` : "This file has been sent to this table",
|
|
5125
|
+
ledger && !ledger.final ? ", but the run stopped part-way. Importing it again would write the rows that already landed a second time, so fix the file (or keep only the rows below the ones that landed) and choose it again." : ". What landed and what was refused is below. To import another file, choose it above."
|
|
5126
|
+
] }) : null,
|
|
5127
|
+
plan && parsed && !afterWrite ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
5045
5128
|
titleKey ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-2 rounded border bg-muted/40 px-2 py-1.5", children: [
|
|
5046
5129
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: "What each record is called" }),
|
|
5047
5130
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
@@ -5075,30 +5158,19 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5075
5158
|
),
|
|
5076
5159
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-muted-foreground", children: titleMappedTo ? `goes into ${titleFieldLabel}, so every record this file makes has a name you can read in the grid and on a chip.` : `Nothing goes into ${titleFieldLabel} yet, so every record this file makes would land with no name at all. Pick the column that says what each row IS.` })
|
|
5077
5160
|
] }) : null,
|
|
5078
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "w-full
|
|
5079
|
-
|
|
5161
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex w-full min-w-0 flex-col rounded border", "data-import-mapping": "", children: plan.columns.map((column) => {
|
|
5162
|
+
const to = mapping[column.header] ?? "";
|
|
5163
|
+
const absent = unmapped === "ignore" ? "\u2014 leave it out \u2014" : "\u2014 offer it as a new column \u2014";
|
|
5164
|
+
const matchedField = column.field_id ? declared.find((f) => f.id === column.field_id) : void 0;
|
|
5165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
5080
5166
|
"div",
|
|
5081
5167
|
{
|
|
5082
|
-
|
|
5083
|
-
|
|
5168
|
+
"data-import-column": column.header,
|
|
5169
|
+
className: "flex min-w-0 flex-col gap-1 border-t px-2 py-1.5 first:border-t-0",
|
|
5084
5170
|
children: [
|
|
5085
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "First values" })
|
|
5089
|
-
]
|
|
5090
|
-
}
|
|
5091
|
-
),
|
|
5092
|
-
plan.columns.map((column) => {
|
|
5093
|
-
const to = mapping[column.header] ?? "";
|
|
5094
|
-
const absent = unmapped === "ignore" ? "\u2014 leave it out \u2014" : "\u2014 offer it as a new column \u2014";
|
|
5095
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
5096
|
-
"div",
|
|
5097
|
-
{
|
|
5098
|
-
className: "grid items-start gap-x-3 border-t px-2 py-1",
|
|
5099
|
-
style: { gridTemplateColumns: MAPPING_COLUMNS },
|
|
5100
|
-
children: [
|
|
5101
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate pt-1.5 font-medium", title: column.header, children: column.header }),
|
|
5171
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
|
|
5172
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "min-w-[8rem] flex-1 break-words font-medium", children: column.header }),
|
|
5173
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-muted-foreground", children: "goes to" }),
|
|
5102
5174
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
5103
5175
|
import_design_system13.Select,
|
|
5104
5176
|
{
|
|
@@ -5119,7 +5191,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5119
5191
|
size: "sm",
|
|
5120
5192
|
"aria-label": `Where ${column.header} goes`,
|
|
5121
5193
|
"data-goes-to": column.header,
|
|
5122
|
-
className: "
|
|
5194
|
+
className: "min-w-[14rem] flex-1",
|
|
5123
5195
|
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.SelectValue, {})
|
|
5124
5196
|
}
|
|
5125
5197
|
),
|
|
@@ -5129,42 +5201,53 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5129
5201
|
] })
|
|
5130
5202
|
]
|
|
5131
5203
|
}
|
|
5204
|
+
)
|
|
5205
|
+
] }),
|
|
5206
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "min-w-0", children: [
|
|
5207
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5208
|
+
import_design_system13.Badge,
|
|
5209
|
+
{
|
|
5210
|
+
variant: column.matched ? "secondary" : "outline",
|
|
5211
|
+
className: "text-[11px] font-normal",
|
|
5212
|
+
"data-import-kind": column.header,
|
|
5213
|
+
children: matchedField ? fieldTypeLabel(matchedField) : typeWord(column.type)
|
|
5214
|
+
}
|
|
5132
5215
|
),
|
|
5133
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
]
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5216
|
+
column.unit ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
|
|
5217
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mt-0.5 break-words text-muted-foreground", children: column.why }),
|
|
5218
|
+
column.ambiguous ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
|
|
5219
|
+
column.collides_with ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null,
|
|
5220
|
+
ruleVerdictOf.get(column.header) ? (() => {
|
|
5221
|
+
const v = ruleVerdictOf.get(column.header);
|
|
5222
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { "data-import-rule-refusal": column.header, className: "mt-1", children: [
|
|
5223
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5224
|
+
RefusalLine,
|
|
5225
|
+
{
|
|
5226
|
+
error: refusal(
|
|
5227
|
+
"refused_by_rule",
|
|
5228
|
+
`${v.refused} of ${v.judged} row${v.judged === 1 ? "" : "s"} would be refused by ${fieldName(v.field)}'s rules. ${v.firstWhy}`,
|
|
5229
|
+
"Send this column somewhere else, fix those rows in your file, or import the rest and they are listed afterwards."
|
|
5230
|
+
)
|
|
5231
|
+
}
|
|
5232
|
+
),
|
|
5233
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "text-muted-foreground", "data-import-rule-sample": "", children: [
|
|
5234
|
+
"Line ",
|
|
5235
|
+
lineInYourFile(v.firstLine),
|
|
5236
|
+
" in your file: ",
|
|
5237
|
+
v.firstRaw
|
|
5238
|
+
] })
|
|
5239
|
+
] });
|
|
5240
|
+
})() : null
|
|
5241
|
+
] }),
|
|
5242
|
+
(column.samples ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "break-words text-muted-foreground", children: [
|
|
5243
|
+
"First values: ",
|
|
5244
|
+
(column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ")
|
|
5245
|
+
] }) : null
|
|
5246
|
+
]
|
|
5247
|
+
},
|
|
5248
|
+
column.header
|
|
5249
|
+
);
|
|
5250
|
+
}) }),
|
|
5168
5251
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Separator, {}),
|
|
5169
5252
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
5170
5253
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-1", children: [
|
|
@@ -5301,7 +5384,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5301
5384
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "The row in your file" })
|
|
5302
5385
|
] }) }),
|
|
5303
5386
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children: interesting.slice(0, 200).map((o) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-t align-top", children: [
|
|
5304
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: o.row }),
|
|
5387
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: lineInYourFile(o.row) }),
|
|
5305
5388
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: o.outcome === "refused" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5306
5389
|
RefusalLine,
|
|
5307
5390
|
{
|
|
@@ -5325,8 +5408,10 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5325
5408
|
" that ",
|
|
5326
5409
|
ledger?.refused === 1 ? "was" : "were",
|
|
5327
5410
|
" refused",
|
|
5328
|
-
" ",
|
|
5329
|
-
"
|
|
5411
|
+
ledger?.refused === 1 ? " is" : " are",
|
|
5412
|
+
" kept with this import \u2014 open ",
|
|
5413
|
+
ledger?.refused === 1 ? "it" : "them",
|
|
5414
|
+
" again from the list of imports below, with the store's reason and the line from your file on each."
|
|
5330
5415
|
] }) : null
|
|
5331
5416
|
] }) : null
|
|
5332
5417
|
] }) : null,
|
|
@@ -5374,7 +5459,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5374
5459
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children: runRows.rows.map((row, i) => {
|
|
5375
5460
|
const source = row.source ?? {};
|
|
5376
5461
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-t align-top", children: [
|
|
5377
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: String(row.row ?? "") }),
|
|
5462
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: typeof row.row === "number" ? lineInYourFile(row.row) : String(row.row ?? "") }),
|
|
5378
5463
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5379
5464
|
RefusalLine,
|
|
5380
5465
|
{
|
|
@@ -5620,6 +5705,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5620
5705
|
const [sensitivity, setSensitivity] = (0, import_react30.useState)(String(field?.sensitivity ?? "internal"));
|
|
5621
5706
|
const [contextPolicy, setContextPolicy] = (0, import_react30.useState)(String(field?.context_policy ?? "include"));
|
|
5622
5707
|
const [rules, setRules] = (0, import_react30.useState)(field?.rules ?? []);
|
|
5708
|
+
const [allowOther, setAllowOther] = (0, import_react30.useState)(field?.config?.["allow_other"] === true);
|
|
5623
5709
|
const [optionText, setOptionText] = (0, import_react30.useState)("");
|
|
5624
5710
|
const [unit, setUnit] = (0, import_react30.useState)(field?.unit ?? "$");
|
|
5625
5711
|
const [withTime, setWithTime] = (0, import_react30.useState)(String(field?.config?.["kind"] ?? "date") === "datetime");
|
|
@@ -5676,7 +5762,10 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5676
5762
|
// REMOVES a spec and the column goes back to the target table's own
|
|
5677
5763
|
// reference column — a panel that only sent the key when it was set
|
|
5678
5764
|
// could turn the override on and never off.
|
|
5679
|
-
...field.type === "relation" ? { display } : {}
|
|
5765
|
+
...field.type === "relation" ? { display } : {},
|
|
5766
|
+
// STORE-RULE-GAPS: a choice list's own setting, sent for every list so
|
|
5767
|
+
// turning it off is as possible as turning it on.
|
|
5768
|
+
...field.type === "list" ? { allow_other: allowOther } : {}
|
|
5680
5769
|
};
|
|
5681
5770
|
const written2 = await mutation.update({ field_id: field.id, patch });
|
|
5682
5771
|
if (written2 !== null) onSaved?.();
|
|
@@ -5703,6 +5792,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5703
5792
|
context_policy: contextPolicy,
|
|
5704
5793
|
rules,
|
|
5705
5794
|
...type === "select" || type === "multi_select" ? { options } : {},
|
|
5795
|
+
...(type === "select" || type === "multi_select") && allowOther ? { allow_other: true } : {},
|
|
5706
5796
|
...type === "currency" ? { unit } : {},
|
|
5707
5797
|
...type === "datetime" ? { kind: withTime ? "datetime" : "date" } : {},
|
|
5708
5798
|
...type === "lookup" ? { via, pick } : {},
|
|
@@ -5834,7 +5924,18 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5834
5924
|
}
|
|
5835
5925
|
),
|
|
5836
5926
|
"Each value has its own date"
|
|
5837
|
-
] })
|
|
5927
|
+
] }),
|
|
5928
|
+
type === "select" || type === "multi_select" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-1.5", children: [
|
|
5929
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5930
|
+
import_design_system15.Checkbox,
|
|
5931
|
+
{
|
|
5932
|
+
checked: allowOther,
|
|
5933
|
+
"aria-label": "A value that is not one of the choices is added to them",
|
|
5934
|
+
onCheckedChange: (v) => setAllowOther(v === true)
|
|
5935
|
+
}
|
|
5936
|
+
),
|
|
5937
|
+
"A value that is not one of the choices is added to them"
|
|
5938
|
+
] }) : null
|
|
5838
5939
|
] }),
|
|
5839
5940
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_design_system15.Separator, {}),
|
|
5840
5941
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
@@ -5882,15 +5983,37 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5882
5983
|
]
|
|
5883
5984
|
}
|
|
5884
5985
|
),
|
|
5986
|
+
rule.kind === "length" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5987
|
+
import_design_system15.BasicInput,
|
|
5988
|
+
{
|
|
5989
|
+
className: "h-8 w-24",
|
|
5990
|
+
inputMode: "numeric",
|
|
5991
|
+
placeholder: "fewest",
|
|
5992
|
+
value: rule.min === void 0 ? "" : String(rule.min),
|
|
5993
|
+
"aria-label": `Check ${index + 1} fewest characters`,
|
|
5994
|
+
onChange: (e) => setRules(rules.map((r, i) => i === index ? withNumber(r, "min", e.target.value) : r))
|
|
5995
|
+
}
|
|
5996
|
+
) : null,
|
|
5885
5997
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5886
5998
|
import_design_system15.BasicInput,
|
|
5887
5999
|
{
|
|
5888
6000
|
className: "h-8",
|
|
6001
|
+
...rule.kind === "length" ? { inputMode: "numeric", placeholder: "most" } : {},
|
|
5889
6002
|
value: String(rule.value ?? ""),
|
|
5890
|
-
"aria-label": `Check ${index + 1} value`,
|
|
6003
|
+
"aria-label": rule.kind === "length" ? `Check ${index + 1} most characters` : `Check ${index + 1} value`,
|
|
5891
6004
|
onChange: (e) => setRules(rules.map((r, i) => i === index ? { ...r, value: e.target.value } : r))
|
|
5892
6005
|
}
|
|
5893
6006
|
),
|
|
6007
|
+
rule.kind === "pattern" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6008
|
+
import_design_system15.BasicInput,
|
|
6009
|
+
{
|
|
6010
|
+
className: "h-8",
|
|
6011
|
+
placeholder: "for example 949-555-0142",
|
|
6012
|
+
value: rule.example ?? "",
|
|
6013
|
+
"aria-label": `Check ${index + 1} example`,
|
|
6014
|
+
onChange: (e) => setRules(rules.map((r, i) => i === index ? withText(r, "example", e.target.value) : r))
|
|
6015
|
+
}
|
|
6016
|
+
) : null,
|
|
5894
6017
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5895
6018
|
import_design_system15.Button,
|
|
5896
6019
|
{
|
|
@@ -6196,6 +6319,17 @@ function ValueOnTheOtherSide({
|
|
|
6196
6319
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] text-muted-foreground", children: help })
|
|
6197
6320
|
] });
|
|
6198
6321
|
}
|
|
6322
|
+
function withNumber(rule, key, typed) {
|
|
6323
|
+
const { [key]: _gone, ...rest } = rule;
|
|
6324
|
+
const trimmed = typed.trim();
|
|
6325
|
+
if (trimmed === "") return rest;
|
|
6326
|
+
const n = Number(trimmed);
|
|
6327
|
+
return Number.isFinite(n) ? { ...rest, [key]: n } : { ...rest, [key]: trimmed };
|
|
6328
|
+
}
|
|
6329
|
+
function withText(rule, key, typed) {
|
|
6330
|
+
const { [key]: _gone, ...rest } = rule;
|
|
6331
|
+
return typed.trim() === "" ? rest : { ...rest, [key]: typed };
|
|
6332
|
+
}
|
|
6199
6333
|
|
|
6200
6334
|
// src/StageRules.tsx
|
|
6201
6335
|
var import_react33 = require("react");
|
|
@@ -6952,9 +7086,9 @@ function TableSettings({
|
|
|
6952
7086
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StageRulesSection, { tableId }),
|
|
6953
7087
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
6954
7088
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "font-medium", children: "Proposed" }),
|
|
6955
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-muted-foreground", children: proposals
|
|
7089
|
+
proposals === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-muted-foreground", children: proposals.length })
|
|
6956
7090
|
] }),
|
|
6957
|
-
proposals === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-muted-foreground", children: "
|
|
7091
|
+
proposals === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-muted-foreground", children: "Columns proposed for this table \u2014 by an import or by an agent \u2014 wait in this table's Inbox, where each one is accepted or turned down." }) : proposals.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-muted-foreground", children: "No field is waiting for a decision." }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { className: "divide-y rounded border", children: proposals.map((proposal) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex flex-wrap items-center gap-x-2 gap-y-1 px-2 py-1.5", children: [
|
|
6958
7092
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-[8rem] flex-1 break-words", children: proposal.field.label || proposal.field.key }),
|
|
6959
7093
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-[8rem] flex-1 break-words text-muted-foreground", children: proposal.why }),
|
|
6960
7094
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Badge, { variant: "secondary", className: "text-[10px] font-normal", children: proposal.proposed_by }),
|
|
@@ -7718,10 +7852,10 @@ function Peek({ tableId, recordId, onClose, className }) {
|
|
|
7718
7852
|
}
|
|
7719
7853
|
function computedInPlainWords(row, field) {
|
|
7720
7854
|
const name = field ? fieldName(field) : humanize(row.field_key);
|
|
7721
|
-
const
|
|
7855
|
+
const shown2 = row.value === null || row.value === void 0 || row.value === "" ? "nothing" : String(row.value);
|
|
7722
7856
|
const when = new Date(row.computed_at);
|
|
7723
7857
|
const at = Number.isNaN(when.getTime()) ? "" : ` on ${when.toLocaleDateString()}`;
|
|
7724
|
-
return `${name} was worked out as ${
|
|
7858
|
+
return `${name} was worked out as ${shown2}${at}.`;
|
|
7725
7859
|
}
|
|
7726
7860
|
|
|
7727
7861
|
// src/views.ts
|
|
@@ -8672,7 +8806,7 @@ function Gallery({
|
|
|
8672
8806
|
onOpenRecord,
|
|
8673
8807
|
note
|
|
8674
8808
|
}) {
|
|
8675
|
-
const
|
|
8809
|
+
const shown2 = fields.slice(0, 4);
|
|
8676
8810
|
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex min-h-0 flex-col gap-2", children: [
|
|
8677
8811
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Note, { children: note }),
|
|
8678
8812
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("ul", { className: "grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-2 overflow-y-auto", children: rows.map((row) => {
|
|
@@ -8685,7 +8819,7 @@ function Gallery({
|
|
|
8685
8819
|
onClick: () => onOpenRecord?.(row.id),
|
|
8686
8820
|
children: [
|
|
8687
8821
|
typeof image === "string" && image !== "" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("img", { src: image, alt: "", className: "h-28 w-full rounded object-cover" }) : null,
|
|
8688
|
-
|
|
8822
|
+
shown2.map((field) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "flex min-w-0 items-baseline gap-1 text-xs", children: [
|
|
8689
8823
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "shrink-0 text-muted-foreground", children: fieldName(field) }),
|
|
8690
8824
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "min-w-0 truncate", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(RecordValue, { field, value: (row.document ?? {})[field.key], document: row.document }) })
|
|
8691
8825
|
] }, field.key))
|
|
@@ -10015,18 +10149,18 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
10015
10149
|
(0, import_react63.useEffect)(() => {
|
|
10016
10150
|
void load();
|
|
10017
10151
|
}, [load]);
|
|
10018
|
-
const
|
|
10152
|
+
const shown2 = (0, import_react63.useMemo)(() => {
|
|
10019
10153
|
const all = items ?? [];
|
|
10020
10154
|
if (!tableId) return all;
|
|
10021
10155
|
return all.filter((i) => i.kind !== "assignment" || i.subject_kind !== "record" || true);
|
|
10022
10156
|
}, [items, tableId]);
|
|
10023
10157
|
(0, import_react63.useEffect)(() => {
|
|
10024
|
-
if (cursor >=
|
|
10025
|
-
}, [
|
|
10158
|
+
if (cursor >= shown2.length) setCursor(Math.max(0, shown2.length - 1));
|
|
10159
|
+
}, [shown2.length, cursor]);
|
|
10026
10160
|
(0, import_react63.useEffect)(() => {
|
|
10027
10161
|
const el = listRef.current?.querySelector(`[data-row="${cursor}"]`);
|
|
10028
10162
|
el?.scrollIntoView({ block: "nearest" });
|
|
10029
|
-
}, [cursor,
|
|
10163
|
+
}, [cursor, shown2.length]);
|
|
10030
10164
|
const decide = (0, import_react63.useCallback)(
|
|
10031
10165
|
async (item, approve) => {
|
|
10032
10166
|
if (item.kind === "assignment") return;
|
|
@@ -10051,11 +10185,11 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
10051
10185
|
);
|
|
10052
10186
|
const onKeyDown = (0, import_react63.useCallback)(
|
|
10053
10187
|
(event) => {
|
|
10054
|
-
const item =
|
|
10188
|
+
const item = shown2[cursor];
|
|
10055
10189
|
const key = event.key;
|
|
10056
10190
|
if (key === "j" || key === "ArrowDown") {
|
|
10057
10191
|
event.preventDefault();
|
|
10058
|
-
setCursor((c) => Math.min(
|
|
10192
|
+
setCursor((c) => Math.min(shown2.length - 1, c + 1));
|
|
10059
10193
|
} else if (key === "k" || key === "ArrowUp") {
|
|
10060
10194
|
event.preventDefault();
|
|
10061
10195
|
setCursor((c) => Math.max(0, c - 1));
|
|
@@ -10070,7 +10204,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
10070
10204
|
void decide(item, key === "a");
|
|
10071
10205
|
}
|
|
10072
10206
|
},
|
|
10073
|
-
[
|
|
10207
|
+
[shown2, cursor, decide, open, load]
|
|
10074
10208
|
);
|
|
10075
10209
|
if (items === null) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_design_system30.Skeleton, { className: (0, import_design_system30.cn)("h-32 w-full", className) });
|
|
10076
10210
|
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
@@ -10083,12 +10217,12 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
10083
10217
|
children: [
|
|
10084
10218
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
10085
10219
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h3", { className: "text-sm font-medium", children: "Inbox" }),
|
|
10086
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-xs text-muted-foreground", children:
|
|
10220
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-xs text-muted-foreground", children: shown2.length }),
|
|
10087
10221
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "ml-auto hidden text-[10px] text-muted-foreground sm:inline", children: "j/k move \xB7 a approve \xB7 d decline \xB7 o open \xB7 r refresh" }),
|
|
10088
10222
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_design_system30.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Refresh" })
|
|
10089
10223
|
] }),
|
|
10090
10224
|
error ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(RefusalNotice, { error }) : null,
|
|
10091
|
-
|
|
10225
|
+
shown2.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-xs text-muted-foreground", children: includeSettled ? "Nothing has come through this inbox yet." : "Nothing is waiting on you." }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ol", { ref: listRef, className: "flex min-h-0 flex-col gap-1 overflow-y-auto", children: shown2.map((item, index) => {
|
|
10092
10226
|
const settled = outcome[item.item_id];
|
|
10093
10227
|
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
10094
10228
|
"li",
|
|
@@ -10164,6 +10298,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
|
|
|
10164
10298
|
const [pending, setPending] = (0, import_react65.useState)({ phase: "idle" });
|
|
10165
10299
|
const [said, setSaid] = (0, import_react65.useState)(null);
|
|
10166
10300
|
const listRef = (0, import_react65.useRef)(null);
|
|
10301
|
+
const changed = (0, import_react66.useRecordChangeRevision)(recordId);
|
|
10167
10302
|
const load = (0, import_react65.useCallback)(async () => {
|
|
10168
10303
|
const answered = await client.recordHistory({ record_id: recordId });
|
|
10169
10304
|
if (!answered.ok) {
|
|
@@ -10172,7 +10307,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
|
|
|
10172
10307
|
}
|
|
10173
10308
|
setError(null);
|
|
10174
10309
|
setEntries(answered.data);
|
|
10175
|
-
}, [client, recordId]);
|
|
10310
|
+
}, [client, recordId, changed]);
|
|
10176
10311
|
(0, import_react65.useEffect)(() => {
|
|
10177
10312
|
void load();
|
|
10178
10313
|
}, [load]);
|
|
@@ -10514,9 +10649,9 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
|
|
|
10514
10649
|
}, [mentionQuery, people, picked]);
|
|
10515
10650
|
if (error) return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(RefusalNotice, { error, className });
|
|
10516
10651
|
if (!thread || table.loading || fields.loading) return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_design_system32.Skeleton, { className: (0, import_design_system32.cn)("h-32 w-full", className) });
|
|
10517
|
-
const
|
|
10518
|
-
const roots =
|
|
10519
|
-
const repliesOf = (id) =>
|
|
10652
|
+
const shown2 = fieldKey ? thread.comments.filter((c) => c.field_key === fieldKey) : thread.comments;
|
|
10653
|
+
const roots = shown2.filter((c) => !c.parent_comment_id);
|
|
10654
|
+
const repliesOf = (id) => shown2.filter((c) => c.parent_comment_id === id);
|
|
10520
10655
|
const fieldLabel = fieldKey ? (fields.data ?? []).find((f) => f.key === fieldKey)?.label || humanize(fieldKey) : null;
|
|
10521
10656
|
async function post() {
|
|
10522
10657
|
if (draft.trim() === "") return;
|
|
@@ -10559,7 +10694,7 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
|
|
|
10559
10694
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("section", { className: (0, import_design_system32.cn)("flex min-h-0 flex-col gap-2", className), children: [
|
|
10560
10695
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-baseline gap-2", children: [
|
|
10561
10696
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h3", { className: "text-sm font-medium", children: fieldLabel ? `Comments on ${fieldLabel}` : "Comments" }),
|
|
10562
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "text-xs text-muted-foreground", children:
|
|
10697
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "text-xs text-muted-foreground", children: shown2.length }),
|
|
10563
10698
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
10564
10699
|
import_design_system32.Button,
|
|
10565
10700
|
{
|
|
@@ -11311,16 +11446,16 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11311
11446
|
const holdingThis = new Set(
|
|
11312
11447
|
exposures.filter((e) => e.table_id === tableId).map((e) => e.portal_id)
|
|
11313
11448
|
);
|
|
11314
|
-
const
|
|
11449
|
+
const shown2 = onATable ? portals.filter((p) => holdingThis.has(p.portal_id)) : portals;
|
|
11315
11450
|
const couldTakeIt = onATable ? portals.filter((p) => !holdingThis.has(p.portal_id)) : [];
|
|
11316
11451
|
const elsewhere = couldTakeIt.length;
|
|
11317
11452
|
const origin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
11318
11453
|
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("section", { className: (0, import_design_system36.cn)("flex flex-col gap-3", className), children: [
|
|
11319
11454
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("header", { className: "flex items-center gap-2", children: [
|
|
11320
11455
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("h3", { className: "text-sm font-medium", children: "Portals" }),
|
|
11321
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-xs text-muted-foreground", children:
|
|
11456
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-xs text-muted-foreground", children: shown2.length === 0 ? "none for this table" : `${shown2.length}` }),
|
|
11322
11457
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "flex-1" }),
|
|
11323
|
-
|
|
11458
|
+
shown2.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_design_system36.Button, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a portal" }) : null
|
|
11324
11459
|
] }),
|
|
11325
11460
|
listError ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(RefusalNotice, { error: listError }) : null,
|
|
11326
11461
|
building ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
@@ -11345,7 +11480,7 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11345
11480
|
onClose: () => setAdding(null)
|
|
11346
11481
|
}
|
|
11347
11482
|
) : null,
|
|
11348
|
-
|
|
11483
|
+
shown2.length === 0 && !listError && !building && !adding ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
11349
11484
|
BuildOrAsk,
|
|
11350
11485
|
{
|
|
11351
11486
|
mayBuild: true,
|
|
@@ -11367,7 +11502,7 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11367
11502
|
]
|
|
11368
11503
|
}
|
|
11369
11504
|
) : null,
|
|
11370
|
-
|
|
11505
|
+
shown2.length === 0 && !listError && !building && !adding && elsewhere > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-col gap-1.5 rounded-md border border-dashed border-border p-2.5", children: [
|
|
11371
11506
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "text-xs text-muted-foreground", children: "Or add this table to a portal your clients already sign in to:" }),
|
|
11372
11507
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "flex flex-wrap gap-1.5", children: couldTakeIt.map((portal) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
11373
11508
|
import_design_system36.Button,
|
|
@@ -11384,8 +11519,8 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11384
11519
|
portal.portal_id
|
|
11385
11520
|
)) })
|
|
11386
11521
|
] }) : null,
|
|
11387
|
-
activePortalId && !
|
|
11388
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("ul", { className: "flex flex-col gap-2", children:
|
|
11522
|
+
activePortalId && !shown2.some((p) => p.portal_id === activePortalId) ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: PORTAL_NOT_HERE_LINE }) : null,
|
|
11523
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("ul", { className: "flex flex-col gap-2", children: shown2.map((portal) => {
|
|
11389
11524
|
const url = `${origin}${(0, import_records9.portalPath)(portal.slug)}`;
|
|
11390
11525
|
const open = openId === portal.portal_id;
|
|
11391
11526
|
const linked = portal.portal_id === activePortalId;
|
|
@@ -11532,7 +11667,7 @@ function ArchivePortalControl({
|
|
|
11532
11667
|
}
|
|
11533
11668
|
function CopyLink({ url, what }) {
|
|
11534
11669
|
const [copied, setCopied] = (0, import_react73.useState)(false);
|
|
11535
|
-
const [
|
|
11670
|
+
const [shown2, setShown] = (0, import_react73.useState)(false);
|
|
11536
11671
|
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
11537
11672
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
11538
11673
|
import_design_system36.Button,
|
|
@@ -11554,7 +11689,7 @@ function CopyLink({ url, what }) {
|
|
|
11554
11689
|
children: copied ? "Copied" : what ? `Copy ${what}` : "Copy link"
|
|
11555
11690
|
}
|
|
11556
11691
|
),
|
|
11557
|
-
|
|
11692
|
+
shown2 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "mt-1.5 w-full break-all rounded border border-dashed border-border px-2 py-1 text-xs", children: [
|
|
11558
11693
|
"This browser would not let the page copy for you, so here it is to copy by hand: ",
|
|
11559
11694
|
url
|
|
11560
11695
|
] }) : null
|
|
@@ -14142,20 +14277,23 @@ function ChartBlock({ block, subject, className }) {
|
|
|
14142
14277
|
label: point.label === "All records" ? block.title : `${block.title} \xB7 ${point.label}`
|
|
14143
14278
|
});
|
|
14144
14279
|
} : null;
|
|
14145
|
-
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
typeof block.ms === "number" ?
|
|
14150
|
-
|
|
14151
|
-
"
|
|
14152
|
-
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
14280
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
14281
|
+
"figure",
|
|
14282
|
+
{
|
|
14283
|
+
className: (0, import_design_system46.cn)("flex min-w-0 flex-col gap-1.5 rounded border p-2", className),
|
|
14284
|
+
...typeof block.ms === "number" ? { "data-store-ms": pretty(block.ms) } : {},
|
|
14285
|
+
children: [
|
|
14286
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("figcaption", { className: "flex items-baseline gap-2 text-xs", children: [
|
|
14287
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "truncate font-medium", children: block.title || CHART_KIND_LABEL[kind] }),
|
|
14288
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "ml-auto shrink-0 text-muted-foreground", children: kind === "stuck" ? `${block.days ?? 14} days` : primary.replace("_", " of ") })
|
|
14289
|
+
] }),
|
|
14290
|
+
block.refused ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(RefusalNotice, { error: asRefusal2(block) }) : needs ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: needs }) : kind === "stuck" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(StuckList, { rows: block.rows ?? [] }) : points.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: "The store answered this question with no groups at all, so there is nothing to draw yet." }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_jsx_runtime49.Fragment, { children: kind === "number" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(BigNumber, { points, measure: primary, onDrill: drill }) : kind === "table" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(GroupTable, { points, series, onDrill: drill }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
|
|
14291
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Drawing, { kind, points, series, config, onDrill: drill }),
|
|
14292
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Values, { points, measure: primary, config, onDrill: drill })
|
|
14293
|
+
] }) })
|
|
14294
|
+
]
|
|
14295
|
+
}
|
|
14296
|
+
);
|
|
14159
14297
|
}
|
|
14160
14298
|
function BigNumber({
|
|
14161
14299
|
points,
|
|
@@ -14767,7 +14905,7 @@ function FormsPanel({ tableId, activeFormId, className }) {
|
|
|
14767
14905
|
},
|
|
14768
14906
|
[client, load]
|
|
14769
14907
|
);
|
|
14770
|
-
const [
|
|
14908
|
+
const [shown2, setShown] = (0, import_react98.useState)(null);
|
|
14771
14909
|
const copy = (0, import_react98.useCallback)(async (url, formId) => {
|
|
14772
14910
|
try {
|
|
14773
14911
|
await navigator.clipboard.writeText(url);
|
|
@@ -14866,7 +15004,7 @@ function FormsPanel({ tableId, activeFormId, className }) {
|
|
|
14866
15004
|
) : null
|
|
14867
15005
|
] }),
|
|
14868
15006
|
rights.structure && publishGate.why ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { className: "mt-1.5 text-xs text-muted-foreground", children: publishGate.why }) : null,
|
|
14869
|
-
|
|
15007
|
+
shown2 && shown2 === url ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
14870
15008
|
"This browser would not let the page copy for you, so here it is to copy by hand:",
|
|
14871
15009
|
" ",
|
|
14872
15010
|
url
|
|
@@ -15028,7 +15166,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
15028
15166
|
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("p", { className: (0, import_design_system49.cn)("text-xs text-muted-foreground", className), children: rights.why("structure") ?? "Making a booking page needs the admin level on this table, because it lets people with no account take time in your calendar." });
|
|
15029
15167
|
}
|
|
15030
15168
|
const url = formId ? `${publicOrigin}${(0, import_records13.bookingPath)(formId)}` : null;
|
|
15031
|
-
const
|
|
15169
|
+
const shown2 = offer ?? null;
|
|
15032
15170
|
return (
|
|
15033
15171
|
// THE RAIL IS 384px WIDE AND THE VIEWPORT IS NOT (walk 1, 2026-09-21):
|
|
15034
15172
|
// every split below is a CONTAINER query, so this screen is the same
|
|
@@ -15176,21 +15314,21 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
15176
15314
|
}
|
|
15177
15315
|
)
|
|
15178
15316
|
] }),
|
|
15179
|
-
|
|
15317
|
+
shown2 ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("p", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
15180
15318
|
"Saved. The page offers ",
|
|
15181
|
-
|
|
15319
|
+
shown2.slot_minutes,
|
|
15182
15320
|
"-minute appointments in ",
|
|
15183
|
-
|
|
15321
|
+
shown2.timezone,
|
|
15184
15322
|
", up to",
|
|
15185
15323
|
" ",
|
|
15186
|
-
|
|
15324
|
+
shown2.max_per_day,
|
|
15187
15325
|
" a day, ",
|
|
15188
|
-
|
|
15326
|
+
shown2.days,
|
|
15189
15327
|
" days ahead",
|
|
15190
|
-
|
|
15328
|
+
shown2.buffer_minutes > 0 ? `, with ${shown2.buffer_minutes} minutes between them` : "",
|
|
15191
15329
|
":",
|
|
15192
15330
|
" ",
|
|
15193
|
-
|
|
15331
|
+
shown2.windows.map(
|
|
15194
15332
|
(w) => `${DAYS.find((d) => d.weekday === w.weekday)?.label ?? w.weekday} ${w.from}\u2013${w.to}`
|
|
15195
15333
|
).join(", "),
|
|
15196
15334
|
"."
|
|
@@ -15232,21 +15370,22 @@ var import_react103 = require("@ai-matrx/records/react");
|
|
|
15232
15370
|
var import_records14 = require("@ai-matrx/records");
|
|
15233
15371
|
var import_design_system50 = require("@ai-matrx/design-system");
|
|
15234
15372
|
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
15373
|
+
var BOOKING_NOT_HERE_LINE = "The link named a booking page this table does not have \u2014 it may have been closed for good, or it writes into another table. The booking pages this table does have are listed here.";
|
|
15235
15374
|
var WHAT_A_BOOKING_PAGE_IS = "A booking page offers times you are free and writes each appointment into this table as an ordinary record.";
|
|
15236
15375
|
function bookingSuggestion(tableName2) {
|
|
15237
|
-
const
|
|
15238
|
-
return `Let clients book a 30-minute ${
|
|
15376
|
+
const name = tableName2?.trim();
|
|
15377
|
+
return name ? `Let clients book a 30-minute appointment on Tuesday and Thursday afternoons, and put each one in ${name}.` : "Let clients book a 30-minute appointment on Tuesday and Thursday afternoons.";
|
|
15239
15378
|
}
|
|
15240
15379
|
var NO_ADMIN2 = "Opening a booking page lets people with no account take time in your calendar, so it needs the admin level on the table the appointments land in.";
|
|
15241
15380
|
var STATE_WORDS = BOOKING_PAGE_STATE_LABEL;
|
|
15242
|
-
function BookingSlots({ tableId, className }) {
|
|
15381
|
+
function BookingSlots({ tableId, activeBookingId, className }) {
|
|
15243
15382
|
const client = (0, import_react103.useRecordsClient)();
|
|
15244
15383
|
const host = useRecordsUi();
|
|
15245
15384
|
const [pages, setPages] = (0, import_react102.useState)(null);
|
|
15246
15385
|
const [error, setError] = (0, import_react102.useState)(null);
|
|
15247
15386
|
const [busy, setBusy] = (0, import_react102.useState)(null);
|
|
15248
15387
|
const [copied, setCopied] = (0, import_react102.useState)(null);
|
|
15249
|
-
const [
|
|
15388
|
+
const [shown2, setShown] = (0, import_react102.useState)(null);
|
|
15250
15389
|
const [building, setBuilding] = (0, import_react102.useState)(false);
|
|
15251
15390
|
const load = (0, import_react102.useCallback)(async () => {
|
|
15252
15391
|
const answered = await client.bookings(tableId ? { table_id: tableId } : {});
|
|
@@ -15312,6 +15451,7 @@ function BookingSlots({ tableId, className }) {
|
|
|
15312
15451
|
tableId && pages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_design_system50.Button, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a booking page" }) : null
|
|
15313
15452
|
] }),
|
|
15314
15453
|
error ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(RefusalNotice, { error }) : null,
|
|
15454
|
+
activeBookingId && !pages.some((p) => p.form_id === activeBookingId) ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: BOOKING_NOT_HERE_LINE }) : null,
|
|
15315
15455
|
building && tableId ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
15316
15456
|
BookingBuilder,
|
|
15317
15457
|
{
|
|
@@ -15347,53 +15487,66 @@ function BookingSlots({ tableId, className }) {
|
|
|
15347
15487
|
const url = `${origin}${(0, import_records14.bookingPath)(page.form_id)}`;
|
|
15348
15488
|
const mayOpen = levels.data?.[page.table_id] === "admin";
|
|
15349
15489
|
const open = page.published_at !== null && page.closed_at === null;
|
|
15350
|
-
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
15351
|
-
|
|
15352
|
-
|
|
15353
|
-
|
|
15354
|
-
|
|
15355
|
-
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
"
|
|
15366
|
-
|
|
15367
|
-
|
|
15368
|
-
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15372
|
-
|
|
15373
|
-
|
|
15374
|
-
|
|
15375
|
-
|
|
15376
|
-
|
|
15377
|
-
|
|
15378
|
-
|
|
15379
|
-
|
|
15380
|
-
|
|
15381
|
-
|
|
15382
|
-
|
|
15383
|
-
|
|
15384
|
-
|
|
15385
|
-
|
|
15386
|
-
|
|
15387
|
-
|
|
15388
|
-
|
|
15389
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15395
|
-
|
|
15396
|
-
|
|
15490
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
15491
|
+
"li",
|
|
15492
|
+
{
|
|
15493
|
+
"data-testid": "booking-page",
|
|
15494
|
+
"data-linked": page.form_id === activeBookingId ? "true" : void 0,
|
|
15495
|
+
ref: page.form_id === activeBookingId ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
15496
|
+
className: (0, import_design_system50.cn)(
|
|
15497
|
+
"rounded-md border p-2.5",
|
|
15498
|
+
page.form_id === activeBookingId && "border-primary ring-1 ring-primary"
|
|
15499
|
+
),
|
|
15500
|
+
children: [
|
|
15501
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
15502
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: page.title ?? page.slug }),
|
|
15503
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_design_system50.Badge, { variant: page.state === "open" ? "default" : "secondary", children: page.state })
|
|
15504
|
+
] }),
|
|
15505
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "mt-0.5 text-xs text-muted-foreground", children: STATE_WORDS[page.state] ?? page.state }),
|
|
15506
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("p", { className: "mt-1.5 text-xs", children: [
|
|
15507
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "font-medium", children: page.upcoming }),
|
|
15508
|
+
" coming up",
|
|
15509
|
+
" \xB7 ",
|
|
15510
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "font-medium", children: page.booked }),
|
|
15511
|
+
" booked in all",
|
|
15512
|
+
page.held > 0 ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
|
|
15513
|
+
" \xB7 ",
|
|
15514
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "font-medium", children: page.held }),
|
|
15515
|
+
" being held right now"
|
|
15516
|
+
] }) : null,
|
|
15517
|
+
page.cancelled > 0 ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
|
|
15518
|
+
" \xB7 ",
|
|
15519
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "font-medium", children: page.cancelled }),
|
|
15520
|
+
" cancelled"
|
|
15521
|
+
] }) : null,
|
|
15522
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
15523
|
+
" \xB7 ",
|
|
15524
|
+
page.slot_minutes,
|
|
15525
|
+
" minutes each"
|
|
15526
|
+
] })
|
|
15527
|
+
] }),
|
|
15528
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "mt-1 text-xs text-muted-foreground", children: nextInWords(page) }),
|
|
15529
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
15530
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_design_system50.Button, { size: "sm", variant: "outline", onClick: () => void copy(url, page.form_id), children: copied === page.form_id ? "Copied" : "Copy link" }),
|
|
15531
|
+
mayOpen ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
15532
|
+
import_design_system50.Button,
|
|
15533
|
+
{
|
|
15534
|
+
size: "sm",
|
|
15535
|
+
variant: "ghost",
|
|
15536
|
+
disabled: busy === page.form_id,
|
|
15537
|
+
onClick: () => void toggle(page),
|
|
15538
|
+
children: busy === page.form_id ? "\u2026" : open ? "Close" : "Open"
|
|
15539
|
+
}
|
|
15540
|
+
) : null
|
|
15541
|
+
] }),
|
|
15542
|
+
shown2 && shown2 === url ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
15543
|
+
"This browser would not let the page copy for you, so here it is to copy by hand: ",
|
|
15544
|
+
url
|
|
15545
|
+
] }) : null
|
|
15546
|
+
]
|
|
15547
|
+
},
|
|
15548
|
+
page.form_id
|
|
15549
|
+
);
|
|
15397
15550
|
}) }),
|
|
15398
15551
|
pages.length > 0 && !levels.loading && levels.data && subjectIds.every((id) => levels.data?.[id] !== "admin") ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "text-xs text-muted-foreground", children: NO_ADMIN2 }) : null
|
|
15399
15552
|
] });
|
|
@@ -16532,8 +16685,15 @@ var LANE_TITLE = {
|
|
|
16532
16685
|
};
|
|
16533
16686
|
var APP_TABLE_PREFIX = "records_ui_";
|
|
16534
16687
|
var LANE_EMPTY = {
|
|
16535
|
-
|
|
16536
|
-
|
|
16688
|
+
// 🚨 BOTH SENTENCES USED TO DESCRIBE A PRODUCT THAT DOES NOT EXIST
|
|
16689
|
+
// (VERIFIER-15 M2, VERIFIER-16 M6). "New table makes one that only you can
|
|
16690
|
+
// see" — New table makes one shared with the organization, and it lands under
|
|
16691
|
+
// My organization. "A table you make is yours until you change its visibility
|
|
16692
|
+
// to internal" — it is internal from the start, and no screen here changes a
|
|
16693
|
+
// table's visibility at all. An empty lane says what is true and names only a
|
|
16694
|
+
// control that exists.
|
|
16695
|
+
mine: "Nothing here is visible to you alone. A new table is shared with your organization from the start.",
|
|
16696
|
+
organization: "No tables shared across your organization yet. New table makes one, and everyone here can open it.",
|
|
16537
16697
|
system: "The platform's own tables are not in this organization's store.",
|
|
16538
16698
|
community: "Nothing shared beyond your organization. A table published by link or to the world lands here.",
|
|
16539
16699
|
app: "The app has not needed a table of its own in this organization yet. Saving a view, writing a comment or building a form makes one, and it appears here rather than among your own data."
|
|
@@ -17178,7 +17338,7 @@ function TablePage({
|
|
|
17178
17338
|
}
|
|
17179
17339
|
) : null,
|
|
17180
17340
|
rail === "forms" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(FormsPanel, { tableId, activeFormId: itemFor("forms") }) : null,
|
|
17181
|
-
rail === "bookings" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(BookingSlots, { tableId }) : null,
|
|
17341
|
+
rail === "bookings" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(BookingSlots, { tableId, activeBookingId: itemFor("bookings") }) : null,
|
|
17182
17342
|
rail === "checklists" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
17183
17343
|
ChecklistsPanel,
|
|
17184
17344
|
{
|