@ai-matrx/records-ui 0.84.0 → 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 +107 -0
- package/dist/index.cjs +353 -194
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +354 -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,
|
|
@@ -561,13 +562,20 @@ function plainSentence(message) {
|
|
|
561
562
|
}
|
|
562
563
|
return { kept: kept.length === 0 ? null : kept.join(" "), dropped };
|
|
563
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
|
+
}
|
|
564
571
|
function refusalForAPerson(error) {
|
|
565
572
|
const code = error.code ?? "internal";
|
|
566
573
|
const message = plainSentence(String(error.message ?? ""));
|
|
567
574
|
const hint = plainSentence(String(error.hint ?? ""));
|
|
568
575
|
const title = TITLE[code] ?? "The store refused this";
|
|
569
576
|
const hintIsForEngineers = isEngineerText(String(error.hint ?? ""));
|
|
570
|
-
const
|
|
577
|
+
const example = exampleOf(error);
|
|
578
|
+
const remedy = (example ? `Enter it like ${example}.` : null) ?? (hintIsForEngineers ? null : hint.kept) ?? REMEDY[code] ?? REMEDY["internal"];
|
|
571
579
|
const messageIsForEngineers = isEngineerText(String(error.message ?? ""));
|
|
572
580
|
const said = (messageIsForEngineers ? "" : message.kept ?? "").trim();
|
|
573
581
|
const forEngineers = [
|
|
@@ -591,7 +599,9 @@ function refusalForAPerson(error) {
|
|
|
591
599
|
}
|
|
592
600
|
function refusalLineForAPerson(error) {
|
|
593
601
|
const plain = refusalForAPerson(error);
|
|
594
|
-
|
|
602
|
+
const sentence2 = plain.sentence.trim();
|
|
603
|
+
const ended = sentence2 === "" || /[.!?…:]$/.test(sentence2) ? sentence2 : `${sentence2}.`;
|
|
604
|
+
return `${ended} ${plain.remedy}`.trim();
|
|
595
605
|
}
|
|
596
606
|
|
|
597
607
|
// src/names.ts
|
|
@@ -1061,6 +1071,42 @@ function asRefusal(thrown, sentence2, remedy, code = "internal") {
|
|
|
1061
1071
|
return refusalFromThrown(thrown, sentence2, remedy, code);
|
|
1062
1072
|
}
|
|
1063
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
|
+
|
|
1064
1110
|
// src/RelationPicker.tsx
|
|
1065
1111
|
var import_jsx_runtime4 = (
|
|
1066
1112
|
// The Field's own name is already printed above this control by
|
|
@@ -1202,9 +1248,12 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1202
1248
|
setSearch("");
|
|
1203
1249
|
toggle(createdId);
|
|
1204
1250
|
};
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
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
|
+
});
|
|
1208
1257
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: (0, import_design_system3.cn)("flex flex-wrap items-center gap-1", className), children: [
|
|
1209
1258
|
picked.map((recordId) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1210
1259
|
RecordChip,
|
|
@@ -1235,17 +1284,21 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1235
1284
|
" at most. Remove one to add another."
|
|
1236
1285
|
] }) : null,
|
|
1237
1286
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system3.ScrollArea, { className: "max-h-56", children: [
|
|
1238
|
-
/* @__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)(
|
|
1239
1288
|
"button",
|
|
1240
1289
|
{
|
|
1241
1290
|
type: "button",
|
|
1242
1291
|
onClick: () => toggle(row.id),
|
|
1243
|
-
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,
|
|
1244
1294
|
className: (0, import_design_system3.cn)(
|
|
1245
1295
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1246
1296
|
picked.includes(row.id) ? "bg-accent text-accent-foreground" : ""
|
|
1247
1297
|
),
|
|
1248
|
-
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
|
+
]
|
|
1249
1302
|
}
|
|
1250
1303
|
) }, row.id)) }),
|
|
1251
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
|
|
@@ -2177,7 +2230,7 @@ var FIELD_RULE_KIND_VALUES = ["min", "max", "length", "pattern", "equals_field",
|
|
|
2177
2230
|
var FIELD_RULE_KIND_LABEL = {
|
|
2178
2231
|
min: "At least",
|
|
2179
2232
|
max: "At most",
|
|
2180
|
-
length: "
|
|
2233
|
+
length: "Number of characters",
|
|
2181
2234
|
pattern: "Looks like",
|
|
2182
2235
|
equals_field: "Same as the column",
|
|
2183
2236
|
differs_from_field: "Different from the column"
|
|
@@ -2209,6 +2262,20 @@ function envelopeFor(document2, key) {
|
|
|
2209
2262
|
const block = document2?._values;
|
|
2210
2263
|
return block?.[key];
|
|
2211
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
|
+
}
|
|
2212
2279
|
var ABSENCE_WORDS = ABSENCE_WORD_LABEL;
|
|
2213
2280
|
function renderValue(field, value, document2, labels, stillWaitingFor) {
|
|
2214
2281
|
const envelope = envelopeFor(document2, field.key);
|
|
@@ -2216,6 +2283,13 @@ function renderValue(field, value, document2, labels, stillWaitingFor) {
|
|
|
2216
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) });
|
|
2217
2284
|
}
|
|
2218
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
|
+
}
|
|
2219
2293
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
2220
2294
|
}
|
|
2221
2295
|
const kind = editorKindFor(field);
|
|
@@ -3242,7 +3316,7 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3242
3316
|
(0, import_react17.useEffect)(() => {
|
|
3243
3317
|
void load();
|
|
3244
3318
|
}, [load]);
|
|
3245
|
-
const
|
|
3319
|
+
const shown2 = (0, import_react17.useMemo)(
|
|
3246
3320
|
() => fieldId ? (rows ?? []).filter((r) => r.field_id === fieldId) : rows ?? [],
|
|
3247
3321
|
[rows, fieldId]
|
|
3248
3322
|
);
|
|
@@ -3301,7 +3375,7 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3301
3375
|
);
|
|
3302
3376
|
if (error) return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RefusalNotice, { error, className });
|
|
3303
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) });
|
|
3304
|
-
if (
|
|
3378
|
+
if (shown2.length === 0 && fieldId) {
|
|
3305
3379
|
const target = (fields.data ?? []).find((f) => f.id === fieldId);
|
|
3306
3380
|
const setup = draft[fieldId] ?? { instruction: "", every: "30" };
|
|
3307
3381
|
if (!rights.admin) {
|
|
@@ -3364,13 +3438,13 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3364
3438
|
outcome ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground", children: outcome.message }) : null
|
|
3365
3439
|
] });
|
|
3366
3440
|
}
|
|
3367
|
-
if (
|
|
3441
|
+
if (shown2.length === 0) {
|
|
3368
3442
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: (0, import_design_system8.cn)("space-y-2 p-4 text-sm", className), children: [
|
|
3369
3443
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "font-medium", children: "No column here is filled in by a model." }),
|
|
3370
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." })
|
|
3371
3445
|
] });
|
|
3372
3446
|
}
|
|
3373
|
-
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) => {
|
|
3374
3448
|
const d = draft[row.field_id] ?? {
|
|
3375
3449
|
instruction: row.enrichment?.instruction ?? "",
|
|
3376
3450
|
every: row.review_interval_days ? String(row.review_interval_days) : ""
|
|
@@ -3659,18 +3733,18 @@ function Outcome({ outcome, onClose }) {
|
|
|
3659
3733
|
] });
|
|
3660
3734
|
}
|
|
3661
3735
|
function PlanTable({ plan }) {
|
|
3662
|
-
const
|
|
3663
|
-
const keys = Array.from(new Set(
|
|
3664
|
-
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;
|
|
3665
3739
|
const columnName = /* @__PURE__ */ new Map();
|
|
3666
|
-
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);
|
|
3667
3741
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "overflow-auto rounded border", children: [
|
|
3668
3742
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("table", { className: "w-full text-xs", "data-matrx-paste-plan": true, children: [
|
|
3669
3743
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("thead", { className: "bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { children: [
|
|
3670
3744
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "Row" }),
|
|
3671
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))
|
|
3672
3746
|
] }) }),
|
|
3673
|
-
/* @__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: [
|
|
3674
3748
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("td", { className: "whitespace-nowrap px-2 py-1", children: [
|
|
3675
3749
|
row.fromLine,
|
|
3676
3750
|
row.recordId === null ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_design_system9.Badge, { variant: "outline", className: "ml-1", children: "new" }) : null
|
|
@@ -3692,9 +3766,9 @@ function PlanTable({ plan }) {
|
|
|
3692
3766
|
})
|
|
3693
3767
|
] }, row.fromLine)) })
|
|
3694
3768
|
] }),
|
|
3695
|
-
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: [
|
|
3696
3770
|
"\u2026and ",
|
|
3697
|
-
plan.rows.length -
|
|
3771
|
+
plan.rows.length - shown2.length,
|
|
3698
3772
|
" more rows the same way."
|
|
3699
3773
|
] }) : null
|
|
3700
3774
|
] });
|
|
@@ -4754,7 +4828,6 @@ function checkImportRules(fields, rows, mapping) {
|
|
|
4754
4828
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
4755
4829
|
var BATCH = 250;
|
|
4756
4830
|
var SAMPLES = 40;
|
|
4757
|
-
var MAPPING_COLUMNS = "minmax(9rem, 11rem) minmax(14rem, 17rem) minmax(13rem, 1fr) minmax(8rem, 10rem)";
|
|
4758
4831
|
var ABSENT = "__absent__";
|
|
4759
4832
|
function obviousTitleColumn(columns) {
|
|
4760
4833
|
const says = /^(title|name|record|customer|client|company|job|item|subject|label|description)\b|(\bname|\btitle|\bnumber|\bid)$/i;
|
|
@@ -4771,7 +4844,11 @@ async function sha256(bytes) {
|
|
|
4771
4844
|
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4772
4845
|
}
|
|
4773
4846
|
function typeWord(t) {
|
|
4774
|
-
|
|
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;
|
|
4775
4852
|
}
|
|
4776
4853
|
function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
4777
4854
|
const client = (0, import_react27.useRecordsClient)();
|
|
@@ -5012,6 +5089,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5012
5089
|
const refusedByRules = ruleVerdicts.reduce((n, v) => n + v.refused, 0);
|
|
5013
5090
|
const [goAheadAnyway, setGoAheadAnyway] = (0, import_react26.useState)(false);
|
|
5014
5091
|
(0, import_react26.useEffect)(() => setGoAheadAnyway(false), [ruleVerdicts]);
|
|
5092
|
+
const afterWrite = !busy && (phase === "done" || alreadyRan !== null || ledger !== null && ledger.seen > 0);
|
|
5015
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: [
|
|
5016
5094
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
5017
5095
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: "Import" }),
|
|
@@ -5042,7 +5120,11 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5042
5120
|
problem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalNotice, { error: problem }) : null,
|
|
5043
5121
|
phase === "reading" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
|
|
5044
5122
|
phase === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
|
|
5045
|
-
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: [
|
|
5046
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: [
|
|
5047
5129
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: "What each record is called" }),
|
|
5048
5130
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
@@ -5076,30 +5158,19 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5076
5158
|
),
|
|
5077
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.` })
|
|
5078
5160
|
] }) : null,
|
|
5079
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "w-full
|
|
5080
|
-
|
|
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)(
|
|
5081
5166
|
"div",
|
|
5082
5167
|
{
|
|
5083
|
-
|
|
5084
|
-
|
|
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",
|
|
5085
5170
|
children: [
|
|
5086
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "First values" })
|
|
5090
|
-
]
|
|
5091
|
-
}
|
|
5092
|
-
),
|
|
5093
|
-
plan.columns.map((column) => {
|
|
5094
|
-
const to = mapping[column.header] ?? "";
|
|
5095
|
-
const absent = unmapped === "ignore" ? "\u2014 leave it out \u2014" : "\u2014 offer it as a new column \u2014";
|
|
5096
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
5097
|
-
"div",
|
|
5098
|
-
{
|
|
5099
|
-
className: "grid items-start gap-x-3 border-t px-2 py-1",
|
|
5100
|
-
style: { gridTemplateColumns: MAPPING_COLUMNS },
|
|
5101
|
-
children: [
|
|
5102
|
-
/* @__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" }),
|
|
5103
5174
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
5104
5175
|
import_design_system13.Select,
|
|
5105
5176
|
{
|
|
@@ -5120,7 +5191,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5120
5191
|
size: "sm",
|
|
5121
5192
|
"aria-label": `Where ${column.header} goes`,
|
|
5122
5193
|
"data-goes-to": column.header,
|
|
5123
|
-
className: "
|
|
5194
|
+
className: "min-w-[14rem] flex-1",
|
|
5124
5195
|
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.SelectValue, {})
|
|
5125
5196
|
}
|
|
5126
5197
|
),
|
|
@@ -5130,42 +5201,53 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5130
5201
|
] })
|
|
5131
5202
|
]
|
|
5132
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
|
+
}
|
|
5133
5215
|
),
|
|
5134
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.
|
|
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
|
-
|
|
5168
|
-
|
|
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
|
+
}) }),
|
|
5169
5251
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Separator, {}),
|
|
5170
5252
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
5171
5253
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-1", children: [
|
|
@@ -5302,7 +5384,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5302
5384
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "The row in your file" })
|
|
5303
5385
|
] }) }),
|
|
5304
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: [
|
|
5305
|
-
/* @__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) }),
|
|
5306
5388
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: o.outcome === "refused" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5307
5389
|
RefusalLine,
|
|
5308
5390
|
{
|
|
@@ -5326,8 +5408,10 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5326
5408
|
" that ",
|
|
5327
5409
|
ledger?.refused === 1 ? "was" : "were",
|
|
5328
5410
|
" refused",
|
|
5329
|
-
" ",
|
|
5330
|
-
"
|
|
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."
|
|
5331
5415
|
] }) : null
|
|
5332
5416
|
] }) : null
|
|
5333
5417
|
] }) : null,
|
|
@@ -5375,7 +5459,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5375
5459
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children: runRows.rows.map((row, i) => {
|
|
5376
5460
|
const source = row.source ?? {};
|
|
5377
5461
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-t align-top", children: [
|
|
5378
|
-
/* @__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 ?? "") }),
|
|
5379
5463
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5380
5464
|
RefusalLine,
|
|
5381
5465
|
{
|
|
@@ -5621,6 +5705,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5621
5705
|
const [sensitivity, setSensitivity] = (0, import_react30.useState)(String(field?.sensitivity ?? "internal"));
|
|
5622
5706
|
const [contextPolicy, setContextPolicy] = (0, import_react30.useState)(String(field?.context_policy ?? "include"));
|
|
5623
5707
|
const [rules, setRules] = (0, import_react30.useState)(field?.rules ?? []);
|
|
5708
|
+
const [allowOther, setAllowOther] = (0, import_react30.useState)(field?.config?.["allow_other"] === true);
|
|
5624
5709
|
const [optionText, setOptionText] = (0, import_react30.useState)("");
|
|
5625
5710
|
const [unit, setUnit] = (0, import_react30.useState)(field?.unit ?? "$");
|
|
5626
5711
|
const [withTime, setWithTime] = (0, import_react30.useState)(String(field?.config?.["kind"] ?? "date") === "datetime");
|
|
@@ -5677,7 +5762,10 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5677
5762
|
// REMOVES a spec and the column goes back to the target table's own
|
|
5678
5763
|
// reference column — a panel that only sent the key when it was set
|
|
5679
5764
|
// could turn the override on and never off.
|
|
5680
|
-
...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 } : {}
|
|
5681
5769
|
};
|
|
5682
5770
|
const written2 = await mutation.update({ field_id: field.id, patch });
|
|
5683
5771
|
if (written2 !== null) onSaved?.();
|
|
@@ -5704,6 +5792,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5704
5792
|
context_policy: contextPolicy,
|
|
5705
5793
|
rules,
|
|
5706
5794
|
...type === "select" || type === "multi_select" ? { options } : {},
|
|
5795
|
+
...(type === "select" || type === "multi_select") && allowOther ? { allow_other: true } : {},
|
|
5707
5796
|
...type === "currency" ? { unit } : {},
|
|
5708
5797
|
...type === "datetime" ? { kind: withTime ? "datetime" : "date" } : {},
|
|
5709
5798
|
...type === "lookup" ? { via, pick } : {},
|
|
@@ -5835,7 +5924,18 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5835
5924
|
}
|
|
5836
5925
|
),
|
|
5837
5926
|
"Each value has its own date"
|
|
5838
|
-
] })
|
|
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
|
|
5839
5939
|
] }),
|
|
5840
5940
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_design_system15.Separator, {}),
|
|
5841
5941
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
@@ -5883,15 +5983,37 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5883
5983
|
]
|
|
5884
5984
|
}
|
|
5885
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,
|
|
5886
5997
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5887
5998
|
import_design_system15.BasicInput,
|
|
5888
5999
|
{
|
|
5889
6000
|
className: "h-8",
|
|
6001
|
+
...rule.kind === "length" ? { inputMode: "numeric", placeholder: "most" } : {},
|
|
5890
6002
|
value: String(rule.value ?? ""),
|
|
5891
|
-
"aria-label": `Check ${index + 1} value`,
|
|
6003
|
+
"aria-label": rule.kind === "length" ? `Check ${index + 1} most characters` : `Check ${index + 1} value`,
|
|
5892
6004
|
onChange: (e) => setRules(rules.map((r, i) => i === index ? { ...r, value: e.target.value } : r))
|
|
5893
6005
|
}
|
|
5894
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,
|
|
5895
6017
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5896
6018
|
import_design_system15.Button,
|
|
5897
6019
|
{
|
|
@@ -6197,6 +6319,17 @@ function ValueOnTheOtherSide({
|
|
|
6197
6319
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] text-muted-foreground", children: help })
|
|
6198
6320
|
] });
|
|
6199
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
|
+
}
|
|
6200
6333
|
|
|
6201
6334
|
// src/StageRules.tsx
|
|
6202
6335
|
var import_react33 = require("react");
|
|
@@ -6953,9 +7086,9 @@ function TableSettings({
|
|
|
6953
7086
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StageRulesSection, { tableId }),
|
|
6954
7087
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
6955
7088
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "font-medium", children: "Proposed" }),
|
|
6956
|
-
/* @__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 })
|
|
6957
7090
|
] }),
|
|
6958
|
-
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: [
|
|
6959
7092
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-[8rem] flex-1 break-words", children: proposal.field.label || proposal.field.key }),
|
|
6960
7093
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-[8rem] flex-1 break-words text-muted-foreground", children: proposal.why }),
|
|
6961
7094
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Badge, { variant: "secondary", className: "text-[10px] font-normal", children: proposal.proposed_by }),
|
|
@@ -7719,10 +7852,10 @@ function Peek({ tableId, recordId, onClose, className }) {
|
|
|
7719
7852
|
}
|
|
7720
7853
|
function computedInPlainWords(row, field) {
|
|
7721
7854
|
const name = field ? fieldName(field) : humanize(row.field_key);
|
|
7722
|
-
const
|
|
7855
|
+
const shown2 = row.value === null || row.value === void 0 || row.value === "" ? "nothing" : String(row.value);
|
|
7723
7856
|
const when = new Date(row.computed_at);
|
|
7724
7857
|
const at = Number.isNaN(when.getTime()) ? "" : ` on ${when.toLocaleDateString()}`;
|
|
7725
|
-
return `${name} was worked out as ${
|
|
7858
|
+
return `${name} was worked out as ${shown2}${at}.`;
|
|
7726
7859
|
}
|
|
7727
7860
|
|
|
7728
7861
|
// src/views.ts
|
|
@@ -8673,7 +8806,7 @@ function Gallery({
|
|
|
8673
8806
|
onOpenRecord,
|
|
8674
8807
|
note
|
|
8675
8808
|
}) {
|
|
8676
|
-
const
|
|
8809
|
+
const shown2 = fields.slice(0, 4);
|
|
8677
8810
|
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex min-h-0 flex-col gap-2", children: [
|
|
8678
8811
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Note, { children: note }),
|
|
8679
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) => {
|
|
@@ -8686,7 +8819,7 @@ function Gallery({
|
|
|
8686
8819
|
onClick: () => onOpenRecord?.(row.id),
|
|
8687
8820
|
children: [
|
|
8688
8821
|
typeof image === "string" && image !== "" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("img", { src: image, alt: "", className: "h-28 w-full rounded object-cover" }) : null,
|
|
8689
|
-
|
|
8822
|
+
shown2.map((field) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "flex min-w-0 items-baseline gap-1 text-xs", children: [
|
|
8690
8823
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "shrink-0 text-muted-foreground", children: fieldName(field) }),
|
|
8691
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 }) })
|
|
8692
8825
|
] }, field.key))
|
|
@@ -10016,18 +10149,18 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
10016
10149
|
(0, import_react63.useEffect)(() => {
|
|
10017
10150
|
void load();
|
|
10018
10151
|
}, [load]);
|
|
10019
|
-
const
|
|
10152
|
+
const shown2 = (0, import_react63.useMemo)(() => {
|
|
10020
10153
|
const all = items ?? [];
|
|
10021
10154
|
if (!tableId) return all;
|
|
10022
10155
|
return all.filter((i) => i.kind !== "assignment" || i.subject_kind !== "record" || true);
|
|
10023
10156
|
}, [items, tableId]);
|
|
10024
10157
|
(0, import_react63.useEffect)(() => {
|
|
10025
|
-
if (cursor >=
|
|
10026
|
-
}, [
|
|
10158
|
+
if (cursor >= shown2.length) setCursor(Math.max(0, shown2.length - 1));
|
|
10159
|
+
}, [shown2.length, cursor]);
|
|
10027
10160
|
(0, import_react63.useEffect)(() => {
|
|
10028
10161
|
const el = listRef.current?.querySelector(`[data-row="${cursor}"]`);
|
|
10029
10162
|
el?.scrollIntoView({ block: "nearest" });
|
|
10030
|
-
}, [cursor,
|
|
10163
|
+
}, [cursor, shown2.length]);
|
|
10031
10164
|
const decide = (0, import_react63.useCallback)(
|
|
10032
10165
|
async (item, approve) => {
|
|
10033
10166
|
if (item.kind === "assignment") return;
|
|
@@ -10052,11 +10185,11 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
10052
10185
|
);
|
|
10053
10186
|
const onKeyDown = (0, import_react63.useCallback)(
|
|
10054
10187
|
(event) => {
|
|
10055
|
-
const item =
|
|
10188
|
+
const item = shown2[cursor];
|
|
10056
10189
|
const key = event.key;
|
|
10057
10190
|
if (key === "j" || key === "ArrowDown") {
|
|
10058
10191
|
event.preventDefault();
|
|
10059
|
-
setCursor((c) => Math.min(
|
|
10192
|
+
setCursor((c) => Math.min(shown2.length - 1, c + 1));
|
|
10060
10193
|
} else if (key === "k" || key === "ArrowUp") {
|
|
10061
10194
|
event.preventDefault();
|
|
10062
10195
|
setCursor((c) => Math.max(0, c - 1));
|
|
@@ -10071,7 +10204,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
10071
10204
|
void decide(item, key === "a");
|
|
10072
10205
|
}
|
|
10073
10206
|
},
|
|
10074
|
-
[
|
|
10207
|
+
[shown2, cursor, decide, open, load]
|
|
10075
10208
|
);
|
|
10076
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) });
|
|
10077
10210
|
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
@@ -10084,12 +10217,12 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
10084
10217
|
children: [
|
|
10085
10218
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
10086
10219
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h3", { className: "text-sm font-medium", children: "Inbox" }),
|
|
10087
|
-
/* @__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 }),
|
|
10088
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" }),
|
|
10089
10222
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_design_system30.Button, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Refresh" })
|
|
10090
10223
|
] }),
|
|
10091
10224
|
error ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(RefusalNotice, { error }) : null,
|
|
10092
|
-
|
|
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) => {
|
|
10093
10226
|
const settled = outcome[item.item_id];
|
|
10094
10227
|
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
10095
10228
|
"li",
|
|
@@ -10165,6 +10298,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
|
|
|
10165
10298
|
const [pending, setPending] = (0, import_react65.useState)({ phase: "idle" });
|
|
10166
10299
|
const [said, setSaid] = (0, import_react65.useState)(null);
|
|
10167
10300
|
const listRef = (0, import_react65.useRef)(null);
|
|
10301
|
+
const changed = (0, import_react66.useRecordChangeRevision)(recordId);
|
|
10168
10302
|
const load = (0, import_react65.useCallback)(async () => {
|
|
10169
10303
|
const answered = await client.recordHistory({ record_id: recordId });
|
|
10170
10304
|
if (!answered.ok) {
|
|
@@ -10173,7 +10307,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
|
|
|
10173
10307
|
}
|
|
10174
10308
|
setError(null);
|
|
10175
10309
|
setEntries(answered.data);
|
|
10176
|
-
}, [client, recordId]);
|
|
10310
|
+
}, [client, recordId, changed]);
|
|
10177
10311
|
(0, import_react65.useEffect)(() => {
|
|
10178
10312
|
void load();
|
|
10179
10313
|
}, [load]);
|
|
@@ -10515,9 +10649,9 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
|
|
|
10515
10649
|
}, [mentionQuery, people, picked]);
|
|
10516
10650
|
if (error) return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(RefusalNotice, { error, className });
|
|
10517
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) });
|
|
10518
|
-
const
|
|
10519
|
-
const roots =
|
|
10520
|
-
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);
|
|
10521
10655
|
const fieldLabel = fieldKey ? (fields.data ?? []).find((f) => f.key === fieldKey)?.label || humanize(fieldKey) : null;
|
|
10522
10656
|
async function post() {
|
|
10523
10657
|
if (draft.trim() === "") return;
|
|
@@ -10560,7 +10694,7 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
|
|
|
10560
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: [
|
|
10561
10695
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-baseline gap-2", children: [
|
|
10562
10696
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h3", { className: "text-sm font-medium", children: fieldLabel ? `Comments on ${fieldLabel}` : "Comments" }),
|
|
10563
|
-
/* @__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 }),
|
|
10564
10698
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
10565
10699
|
import_design_system32.Button,
|
|
10566
10700
|
{
|
|
@@ -11312,16 +11446,16 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11312
11446
|
const holdingThis = new Set(
|
|
11313
11447
|
exposures.filter((e) => e.table_id === tableId).map((e) => e.portal_id)
|
|
11314
11448
|
);
|
|
11315
|
-
const
|
|
11449
|
+
const shown2 = onATable ? portals.filter((p) => holdingThis.has(p.portal_id)) : portals;
|
|
11316
11450
|
const couldTakeIt = onATable ? portals.filter((p) => !holdingThis.has(p.portal_id)) : [];
|
|
11317
11451
|
const elsewhere = couldTakeIt.length;
|
|
11318
11452
|
const origin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
11319
11453
|
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("section", { className: (0, import_design_system36.cn)("flex flex-col gap-3", className), children: [
|
|
11320
11454
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("header", { className: "flex items-center gap-2", children: [
|
|
11321
11455
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("h3", { className: "text-sm font-medium", children: "Portals" }),
|
|
11322
|
-
/* @__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}` }),
|
|
11323
11457
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "flex-1" }),
|
|
11324
|
-
|
|
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
|
|
11325
11459
|
] }),
|
|
11326
11460
|
listError ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(RefusalNotice, { error: listError }) : null,
|
|
11327
11461
|
building ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
@@ -11346,7 +11480,7 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11346
11480
|
onClose: () => setAdding(null)
|
|
11347
11481
|
}
|
|
11348
11482
|
) : null,
|
|
11349
|
-
|
|
11483
|
+
shown2.length === 0 && !listError && !building && !adding ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
11350
11484
|
BuildOrAsk,
|
|
11351
11485
|
{
|
|
11352
11486
|
mayBuild: true,
|
|
@@ -11368,7 +11502,7 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11368
11502
|
]
|
|
11369
11503
|
}
|
|
11370
11504
|
) : null,
|
|
11371
|
-
|
|
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: [
|
|
11372
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:" }),
|
|
11373
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)(
|
|
11374
11508
|
import_design_system36.Button,
|
|
@@ -11385,8 +11519,8 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11385
11519
|
portal.portal_id
|
|
11386
11520
|
)) })
|
|
11387
11521
|
] }) : null,
|
|
11388
|
-
activePortalId && !
|
|
11389
|
-
/* @__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) => {
|
|
11390
11524
|
const url = `${origin}${(0, import_records9.portalPath)(portal.slug)}`;
|
|
11391
11525
|
const open = openId === portal.portal_id;
|
|
11392
11526
|
const linked = portal.portal_id === activePortalId;
|
|
@@ -11533,7 +11667,7 @@ function ArchivePortalControl({
|
|
|
11533
11667
|
}
|
|
11534
11668
|
function CopyLink({ url, what }) {
|
|
11535
11669
|
const [copied, setCopied] = (0, import_react73.useState)(false);
|
|
11536
|
-
const [
|
|
11670
|
+
const [shown2, setShown] = (0, import_react73.useState)(false);
|
|
11537
11671
|
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
11538
11672
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
11539
11673
|
import_design_system36.Button,
|
|
@@ -11555,7 +11689,7 @@ function CopyLink({ url, what }) {
|
|
|
11555
11689
|
children: copied ? "Copied" : what ? `Copy ${what}` : "Copy link"
|
|
11556
11690
|
}
|
|
11557
11691
|
),
|
|
11558
|
-
|
|
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: [
|
|
11559
11693
|
"This browser would not let the page copy for you, so here it is to copy by hand: ",
|
|
11560
11694
|
url
|
|
11561
11695
|
] }) : null
|
|
@@ -14143,20 +14277,23 @@ function ChartBlock({ block, subject, className }) {
|
|
|
14143
14277
|
label: point.label === "All records" ? block.title : `${block.title} \xB7 ${point.label}`
|
|
14144
14278
|
});
|
|
14145
14279
|
} : null;
|
|
14146
|
-
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
typeof block.ms === "number" ?
|
|
14151
|
-
|
|
14152
|
-
"
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
14159
|
-
|
|
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
|
+
);
|
|
14160
14297
|
}
|
|
14161
14298
|
function BigNumber({
|
|
14162
14299
|
points,
|
|
@@ -14768,7 +14905,7 @@ function FormsPanel({ tableId, activeFormId, className }) {
|
|
|
14768
14905
|
},
|
|
14769
14906
|
[client, load]
|
|
14770
14907
|
);
|
|
14771
|
-
const [
|
|
14908
|
+
const [shown2, setShown] = (0, import_react98.useState)(null);
|
|
14772
14909
|
const copy = (0, import_react98.useCallback)(async (url, formId) => {
|
|
14773
14910
|
try {
|
|
14774
14911
|
await navigator.clipboard.writeText(url);
|
|
@@ -14867,7 +15004,7 @@ function FormsPanel({ tableId, activeFormId, className }) {
|
|
|
14867
15004
|
) : null
|
|
14868
15005
|
] }),
|
|
14869
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,
|
|
14870
|
-
|
|
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: [
|
|
14871
15008
|
"This browser would not let the page copy for you, so here it is to copy by hand:",
|
|
14872
15009
|
" ",
|
|
14873
15010
|
url
|
|
@@ -15029,7 +15166,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
15029
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." });
|
|
15030
15167
|
}
|
|
15031
15168
|
const url = formId ? `${publicOrigin}${(0, import_records13.bookingPath)(formId)}` : null;
|
|
15032
|
-
const
|
|
15169
|
+
const shown2 = offer ?? null;
|
|
15033
15170
|
return (
|
|
15034
15171
|
// THE RAIL IS 384px WIDE AND THE VIEWPORT IS NOT (walk 1, 2026-09-21):
|
|
15035
15172
|
// every split below is a CONTAINER query, so this screen is the same
|
|
@@ -15177,21 +15314,21 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
15177
15314
|
}
|
|
15178
15315
|
)
|
|
15179
15316
|
] }),
|
|
15180
|
-
|
|
15317
|
+
shown2 ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("p", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
15181
15318
|
"Saved. The page offers ",
|
|
15182
|
-
|
|
15319
|
+
shown2.slot_minutes,
|
|
15183
15320
|
"-minute appointments in ",
|
|
15184
|
-
|
|
15321
|
+
shown2.timezone,
|
|
15185
15322
|
", up to",
|
|
15186
15323
|
" ",
|
|
15187
|
-
|
|
15324
|
+
shown2.max_per_day,
|
|
15188
15325
|
" a day, ",
|
|
15189
|
-
|
|
15326
|
+
shown2.days,
|
|
15190
15327
|
" days ahead",
|
|
15191
|
-
|
|
15328
|
+
shown2.buffer_minutes > 0 ? `, with ${shown2.buffer_minutes} minutes between them` : "",
|
|
15192
15329
|
":",
|
|
15193
15330
|
" ",
|
|
15194
|
-
|
|
15331
|
+
shown2.windows.map(
|
|
15195
15332
|
(w) => `${DAYS.find((d) => d.weekday === w.weekday)?.label ?? w.weekday} ${w.from}\u2013${w.to}`
|
|
15196
15333
|
).join(", "),
|
|
15197
15334
|
"."
|
|
@@ -15233,21 +15370,22 @@ var import_react103 = require("@ai-matrx/records/react");
|
|
|
15233
15370
|
var import_records14 = require("@ai-matrx/records");
|
|
15234
15371
|
var import_design_system50 = require("@ai-matrx/design-system");
|
|
15235
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.";
|
|
15236
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.";
|
|
15237
15375
|
function bookingSuggestion(tableName2) {
|
|
15238
|
-
const
|
|
15239
|
-
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.";
|
|
15240
15378
|
}
|
|
15241
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.";
|
|
15242
15380
|
var STATE_WORDS = BOOKING_PAGE_STATE_LABEL;
|
|
15243
|
-
function BookingSlots({ tableId, className }) {
|
|
15381
|
+
function BookingSlots({ tableId, activeBookingId, className }) {
|
|
15244
15382
|
const client = (0, import_react103.useRecordsClient)();
|
|
15245
15383
|
const host = useRecordsUi();
|
|
15246
15384
|
const [pages, setPages] = (0, import_react102.useState)(null);
|
|
15247
15385
|
const [error, setError] = (0, import_react102.useState)(null);
|
|
15248
15386
|
const [busy, setBusy] = (0, import_react102.useState)(null);
|
|
15249
15387
|
const [copied, setCopied] = (0, import_react102.useState)(null);
|
|
15250
|
-
const [
|
|
15388
|
+
const [shown2, setShown] = (0, import_react102.useState)(null);
|
|
15251
15389
|
const [building, setBuilding] = (0, import_react102.useState)(false);
|
|
15252
15390
|
const load = (0, import_react102.useCallback)(async () => {
|
|
15253
15391
|
const answered = await client.bookings(tableId ? { table_id: tableId } : {});
|
|
@@ -15313,6 +15451,7 @@ function BookingSlots({ tableId, className }) {
|
|
|
15313
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
|
|
15314
15452
|
] }),
|
|
15315
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,
|
|
15316
15455
|
building && tableId ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
15317
15456
|
BookingBuilder,
|
|
15318
15457
|
{
|
|
@@ -15348,53 +15487,66 @@ function BookingSlots({ tableId, className }) {
|
|
|
15348
15487
|
const url = `${origin}${(0, import_records14.bookingPath)(page.form_id)}`;
|
|
15349
15488
|
const mayOpen = levels.data?.[page.table_id] === "admin";
|
|
15350
15489
|
const open = page.published_at !== null && page.closed_at === null;
|
|
15351
|
-
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
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
|
-
|
|
15397
|
-
|
|
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
|
+
);
|
|
15398
15550
|
}) }),
|
|
15399
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
|
|
15400
15552
|
] });
|
|
@@ -16533,8 +16685,15 @@ var LANE_TITLE = {
|
|
|
16533
16685
|
};
|
|
16534
16686
|
var APP_TABLE_PREFIX = "records_ui_";
|
|
16535
16687
|
var LANE_EMPTY = {
|
|
16536
|
-
|
|
16537
|
-
|
|
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.",
|
|
16538
16697
|
system: "The platform's own tables are not in this organization's store.",
|
|
16539
16698
|
community: "Nothing shared beyond your organization. A table published by link or to the world lands here.",
|
|
16540
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."
|
|
@@ -17179,7 +17338,7 @@ function TablePage({
|
|
|
17179
17338
|
}
|
|
17180
17339
|
) : null,
|
|
17181
17340
|
rail === "forms" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(FormsPanel, { tableId, activeFormId: itemFor("forms") }) : null,
|
|
17182
|
-
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,
|
|
17183
17342
|
rail === "checklists" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
17184
17343
|
ChecklistsPanel,
|
|
17185
17344
|
{
|