@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.js
CHANGED
|
@@ -239,13 +239,20 @@ function plainSentence(message) {
|
|
|
239
239
|
}
|
|
240
240
|
return { kept: kept.length === 0 ? null : kept.join(" "), dropped };
|
|
241
241
|
}
|
|
242
|
+
function exampleOf(error) {
|
|
243
|
+
const detail = error.detail;
|
|
244
|
+
if (!detail || typeof detail !== "object") return null;
|
|
245
|
+
const example = detail.example;
|
|
246
|
+
return typeof example === "string" && example.trim() !== "" ? example.trim() : null;
|
|
247
|
+
}
|
|
242
248
|
function refusalForAPerson(error) {
|
|
243
249
|
const code = error.code ?? "internal";
|
|
244
250
|
const message = plainSentence(String(error.message ?? ""));
|
|
245
251
|
const hint = plainSentence(String(error.hint ?? ""));
|
|
246
252
|
const title = TITLE[code] ?? "The store refused this";
|
|
247
253
|
const hintIsForEngineers = isEngineerText(String(error.hint ?? ""));
|
|
248
|
-
const
|
|
254
|
+
const example = exampleOf(error);
|
|
255
|
+
const remedy = (example ? `Enter it like ${example}.` : null) ?? (hintIsForEngineers ? null : hint.kept) ?? REMEDY[code] ?? REMEDY["internal"];
|
|
249
256
|
const messageIsForEngineers = isEngineerText(String(error.message ?? ""));
|
|
250
257
|
const said = (messageIsForEngineers ? "" : message.kept ?? "").trim();
|
|
251
258
|
const forEngineers = [
|
|
@@ -269,7 +276,9 @@ function refusalForAPerson(error) {
|
|
|
269
276
|
}
|
|
270
277
|
function refusalLineForAPerson(error) {
|
|
271
278
|
const plain = refusalForAPerson(error);
|
|
272
|
-
|
|
279
|
+
const sentence2 = plain.sentence.trim();
|
|
280
|
+
const ended = sentence2 === "" || /[.!?…:]$/.test(sentence2) ? sentence2 : `${sentence2}.`;
|
|
281
|
+
return `${ended} ${plain.remedy}`.trim();
|
|
273
282
|
}
|
|
274
283
|
|
|
275
284
|
// src/names.ts
|
|
@@ -783,6 +792,42 @@ function asRefusal(thrown, sentence2, remedy, code = "internal") {
|
|
|
783
792
|
return refusalFromThrown(thrown, sentence2, remedy, code);
|
|
784
793
|
}
|
|
785
794
|
|
|
795
|
+
// src/tellApart.ts
|
|
796
|
+
function shown(value) {
|
|
797
|
+
if (value === null || value === void 0) return null;
|
|
798
|
+
if (typeof value === "string") return value.trim() === "" ? null : value.trim();
|
|
799
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
function tellApart(rows, nameOf) {
|
|
803
|
+
const byName = /* @__PURE__ */ new Map();
|
|
804
|
+
for (const row of rows) {
|
|
805
|
+
const name = nameOf(row.id).trim().toLowerCase();
|
|
806
|
+
byName.set(name, [...byName.get(name) ?? [], row]);
|
|
807
|
+
}
|
|
808
|
+
const out = /* @__PURE__ */ new Map();
|
|
809
|
+
for (const [name, group] of byName) {
|
|
810
|
+
if (group.length < 2) continue;
|
|
811
|
+
const keys = [
|
|
812
|
+
...new Set(
|
|
813
|
+
group.flatMap(
|
|
814
|
+
(row) => Object.keys(row.document ?? {}).filter((k) => !k.startsWith("_") && shown(row.document?.[k]) !== null)
|
|
815
|
+
)
|
|
816
|
+
)
|
|
817
|
+
].filter((k) => group.some((row) => (shown(row.document?.[k]) ?? "").toLowerCase() !== name));
|
|
818
|
+
const valuesOf = (k) => group.map((row) => shown(row.document?.[k]) ?? "");
|
|
819
|
+
const differs = keys.filter((k) => new Set(valuesOf(k)).size > 1);
|
|
820
|
+
const whole = differs.find((k) => new Set(valuesOf(k)).size === group.length);
|
|
821
|
+
const chosen = whole ? [whole] : differs.slice(0, 2);
|
|
822
|
+
if (chosen.length === 0) continue;
|
|
823
|
+
for (const row of group) {
|
|
824
|
+
const words = chosen.map((k) => shown(row.document?.[k])).filter((w) => w !== null);
|
|
825
|
+
if (words.length > 0) out.set(row.id, words.join(" \xB7 "));
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return out;
|
|
829
|
+
}
|
|
830
|
+
|
|
786
831
|
// src/RelationPicker.tsx
|
|
787
832
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
788
833
|
var NO_STORE_ON_THIS_SCREEN_REASON = "This question points at a record in somebody's own data, and a page answered without an account is never allowed to search that. Leave it blank \u2014 whoever receives your answer will fill it in.";
|
|
@@ -924,9 +969,12 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
924
969
|
setSearch("");
|
|
925
970
|
toggle(createdId);
|
|
926
971
|
};
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
|
|
972
|
+
const apart = tellApart(rows ?? [], titleOf);
|
|
973
|
+
const visible = (rows ?? []).filter((row) => {
|
|
974
|
+
const q = search.trim().toLowerCase();
|
|
975
|
+
if (q === "") return true;
|
|
976
|
+
return `${titleOf(row.id)} ${apart.get(row.id) ?? ""}`.toLowerCase().includes(q);
|
|
977
|
+
});
|
|
930
978
|
return /* @__PURE__ */ jsxs3("div", { className: cn3("flex flex-wrap items-center gap-1", className), children: [
|
|
931
979
|
picked.map((recordId) => /* @__PURE__ */ jsx4(
|
|
932
980
|
RecordChip,
|
|
@@ -957,17 +1005,21 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
957
1005
|
" at most. Remove one to add another."
|
|
958
1006
|
] }) : null,
|
|
959
1007
|
/* @__PURE__ */ jsxs3(ScrollArea, { className: "max-h-56", children: [
|
|
960
|
-
/* @__PURE__ */ jsx4("ul", { children: visible.map((row) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */
|
|
1008
|
+
/* @__PURE__ */ jsx4("ul", { children: visible.map((row) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs3(
|
|
961
1009
|
"button",
|
|
962
1010
|
{
|
|
963
1011
|
type: "button",
|
|
964
1012
|
onClick: () => toggle(row.id),
|
|
965
|
-
title: titleOf(row.id),
|
|
1013
|
+
title: apart.has(row.id) ? `${titleOf(row.id)} \u2014 ${apart.get(row.id)}` : titleOf(row.id),
|
|
1014
|
+
"data-relation-candidate": row.id,
|
|
966
1015
|
className: cn3(
|
|
967
1016
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
968
1017
|
picked.includes(row.id) ? "bg-accent text-accent-foreground" : ""
|
|
969
1018
|
),
|
|
970
|
-
children:
|
|
1019
|
+
children: [
|
|
1020
|
+
titleOf(row.id),
|
|
1021
|
+
apart.has(row.id) ? /* @__PURE__ */ jsx4("span", { className: "block truncate text-[11px] text-muted-foreground", "data-tell-apart": "", children: apart.get(row.id) }) : null
|
|
1022
|
+
]
|
|
971
1023
|
}
|
|
972
1024
|
) }, row.id)) }),
|
|
973
1025
|
rows && visible.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "p-2 text-xs text-muted-foreground", children: "Nothing in that table matches." }) : null
|
|
@@ -1902,7 +1954,7 @@ var FIELD_RULE_KIND_VALUES = ["min", "max", "length", "pattern", "equals_field",
|
|
|
1902
1954
|
var FIELD_RULE_KIND_LABEL = {
|
|
1903
1955
|
min: "At least",
|
|
1904
1956
|
max: "At most",
|
|
1905
|
-
length: "
|
|
1957
|
+
length: "Number of characters",
|
|
1906
1958
|
pattern: "Looks like",
|
|
1907
1959
|
equals_field: "Same as the column",
|
|
1908
1960
|
differs_from_field: "Different from the column"
|
|
@@ -1934,6 +1986,20 @@ function envelopeFor(document2, key) {
|
|
|
1934
1986
|
const block = document2?._values;
|
|
1935
1987
|
return block?.[key];
|
|
1936
1988
|
}
|
|
1989
|
+
function unresolvedFor(document2, key) {
|
|
1990
|
+
const sources = document2?._sources;
|
|
1991
|
+
if (!sources || typeof sources !== "object") return void 0;
|
|
1992
|
+
for (const source of Object.values(sources)) {
|
|
1993
|
+
const held = source?.unresolved?.[key];
|
|
1994
|
+
if (typeof held === "string" && held) return held;
|
|
1995
|
+
if (Array.isArray(held) && held.length) return held.map(String).join(", ");
|
|
1996
|
+
}
|
|
1997
|
+
return void 0;
|
|
1998
|
+
}
|
|
1999
|
+
function hasFormula(field) {
|
|
2000
|
+
const config = field.config ?? {};
|
|
2001
|
+
return Boolean(config.expr) || Boolean(String(config.formula_text ?? "").trim());
|
|
2002
|
+
}
|
|
1937
2003
|
var ABSENCE_WORDS = ABSENCE_WORD_LABEL;
|
|
1938
2004
|
function renderValue(field, value, document2, labels, stillWaitingFor) {
|
|
1939
2005
|
const envelope = envelopeFor(document2, field.key);
|
|
@@ -1941,6 +2007,13 @@ function renderValue(field, value, document2, labels, stillWaitingFor) {
|
|
|
1941
2007
|
return /* @__PURE__ */ jsx9("span", { className: "text-xs italic text-muted-foreground", title: `VAL-2: ${envelope.absent}`, children: ABSENCE_WORDS[String(envelope.absent)] ?? String(envelope.absent) });
|
|
1942
2008
|
}
|
|
1943
2009
|
if (value === null || value === void 0 || value === "") {
|
|
2010
|
+
const gone = unresolvedFor(document2, field.key);
|
|
2011
|
+
if (gone) {
|
|
2012
|
+
return /* @__PURE__ */ jsx9("span", { className: "text-xs italic text-muted-foreground", title: `It pointed at ${gone}`, children: "The record this pointed at no longer exists" });
|
|
2013
|
+
}
|
|
2014
|
+
if (field.type === "formula" && !hasFormula(field)) {
|
|
2015
|
+
return /* @__PURE__ */ jsx9("span", { className: "text-xs italic text-muted-foreground", children: "Worked out once its formula is in place" });
|
|
2016
|
+
}
|
|
1944
2017
|
return /* @__PURE__ */ jsx9("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
1945
2018
|
}
|
|
1946
2019
|
const kind = editorKindFor(field);
|
|
@@ -2991,7 +3064,7 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
2991
3064
|
useEffect7(() => {
|
|
2992
3065
|
void load();
|
|
2993
3066
|
}, [load]);
|
|
2994
|
-
const
|
|
3067
|
+
const shown2 = useMemo8(
|
|
2995
3068
|
() => fieldId ? (rows ?? []).filter((r) => r.field_id === fieldId) : rows ?? [],
|
|
2996
3069
|
[rows, fieldId]
|
|
2997
3070
|
);
|
|
@@ -3050,7 +3123,7 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3050
3123
|
);
|
|
3051
3124
|
if (error) return /* @__PURE__ */ jsx11(RefusalNotice, { error, className });
|
|
3052
3125
|
if (rows === null) return /* @__PURE__ */ jsx11(Skeleton, { className: cn8("h-40 w-full", className) });
|
|
3053
|
-
if (
|
|
3126
|
+
if (shown2.length === 0 && fieldId) {
|
|
3054
3127
|
const target = (fields.data ?? []).find((f) => f.id === fieldId);
|
|
3055
3128
|
const setup = draft[fieldId] ?? { instruction: "", every: "30" };
|
|
3056
3129
|
if (!rights.admin) {
|
|
@@ -3113,13 +3186,13 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3113
3186
|
outcome ? /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: outcome.message }) : null
|
|
3114
3187
|
] });
|
|
3115
3188
|
}
|
|
3116
|
-
if (
|
|
3189
|
+
if (shown2.length === 0) {
|
|
3117
3190
|
return /* @__PURE__ */ jsxs7("div", { className: cn8("space-y-2 p-4 text-sm", className), children: [
|
|
3118
3191
|
/* @__PURE__ */ jsx11("p", { className: "font-medium", children: "No column here is filled in by a model." }),
|
|
3119
3192
|
/* @__PURE__ */ jsx11("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." })
|
|
3120
3193
|
] });
|
|
3121
3194
|
}
|
|
3122
|
-
return /* @__PURE__ */ jsx11("div", { className: cn8("space-y-6 p-4", className), children:
|
|
3195
|
+
return /* @__PURE__ */ jsx11("div", { className: cn8("space-y-6 p-4", className), children: shown2.map((row) => {
|
|
3123
3196
|
const d = draft[row.field_id] ?? {
|
|
3124
3197
|
instruction: row.enrichment?.instruction ?? "",
|
|
3125
3198
|
every: row.review_interval_days ? String(row.review_interval_days) : ""
|
|
@@ -3408,18 +3481,18 @@ function Outcome({ outcome, onClose }) {
|
|
|
3408
3481
|
] });
|
|
3409
3482
|
}
|
|
3410
3483
|
function PlanTable({ plan }) {
|
|
3411
|
-
const
|
|
3412
|
-
const keys = Array.from(new Set(
|
|
3413
|
-
if (
|
|
3484
|
+
const shown2 = plan.rows.slice(0, 8);
|
|
3485
|
+
const keys = Array.from(new Set(shown2.flatMap((row) => row.cells.map((c) => c.key))));
|
|
3486
|
+
if (shown2.length === 0 || keys.length === 0) return null;
|
|
3414
3487
|
const columnName = /* @__PURE__ */ new Map();
|
|
3415
|
-
for (const row of
|
|
3488
|
+
for (const row of shown2) for (const cell of row.cells) if (!columnName.has(cell.key)) columnName.set(cell.key, cell.column);
|
|
3416
3489
|
return /* @__PURE__ */ jsxs8("div", { className: "overflow-auto rounded border", children: [
|
|
3417
3490
|
/* @__PURE__ */ jsxs8("table", { className: "w-full text-xs", "data-matrx-paste-plan": true, children: [
|
|
3418
3491
|
/* @__PURE__ */ jsx12("thead", { className: "bg-muted/50", children: /* @__PURE__ */ jsxs8("tr", { children: [
|
|
3419
3492
|
/* @__PURE__ */ jsx12("th", { className: "px-2 py-1 text-left font-medium", children: "Row" }),
|
|
3420
3493
|
keys.map((key) => /* @__PURE__ */ jsx12("th", { className: "px-2 py-1 text-left font-medium", children: columnName.get(key) ?? key }, key))
|
|
3421
3494
|
] }) }),
|
|
3422
|
-
/* @__PURE__ */ jsx12("tbody", { children:
|
|
3495
|
+
/* @__PURE__ */ jsx12("tbody", { children: shown2.map((row) => /* @__PURE__ */ jsxs8("tr", { className: "border-t", children: [
|
|
3423
3496
|
/* @__PURE__ */ jsxs8("td", { className: "whitespace-nowrap px-2 py-1", children: [
|
|
3424
3497
|
row.fromLine,
|
|
3425
3498
|
row.recordId === null ? /* @__PURE__ */ jsx12(Badge4, { variant: "outline", className: "ml-1", children: "new" }) : null
|
|
@@ -3441,9 +3514,9 @@ function PlanTable({ plan }) {
|
|
|
3441
3514
|
})
|
|
3442
3515
|
] }, row.fromLine)) })
|
|
3443
3516
|
] }),
|
|
3444
|
-
plan.rows.length >
|
|
3517
|
+
plan.rows.length > shown2.length ? /* @__PURE__ */ jsxs8("p", { className: "px-2 py-1 text-xs text-muted-foreground", children: [
|
|
3445
3518
|
"\u2026and ",
|
|
3446
|
-
plan.rows.length -
|
|
3519
|
+
plan.rows.length - shown2.length,
|
|
3447
3520
|
" more rows the same way."
|
|
3448
3521
|
] }) : null
|
|
3449
3522
|
] });
|
|
@@ -4516,7 +4589,6 @@ function checkImportRules(fields, rows, mapping) {
|
|
|
4516
4589
|
import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4517
4590
|
var BATCH = 250;
|
|
4518
4591
|
var SAMPLES = 40;
|
|
4519
|
-
var MAPPING_COLUMNS = "minmax(9rem, 11rem) minmax(14rem, 17rem) minmax(13rem, 1fr) minmax(8rem, 10rem)";
|
|
4520
4592
|
var ABSENT = "__absent__";
|
|
4521
4593
|
function obviousTitleColumn(columns) {
|
|
4522
4594
|
const says = /^(title|name|record|customer|client|company|job|item|subject|label|description)\b|(\bname|\btitle|\bnumber|\bid)$/i;
|
|
@@ -4533,7 +4605,11 @@ async function sha256(bytes) {
|
|
|
4533
4605
|
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4534
4606
|
}
|
|
4535
4607
|
function typeWord(t) {
|
|
4536
|
-
|
|
4608
|
+
if (t === "range") return fieldTypeChoice("number")?.label ?? "Number";
|
|
4609
|
+
return fieldTypeChoice(t)?.label ?? (t === "relation" ? "Points at another table" : humanize(t));
|
|
4610
|
+
}
|
|
4611
|
+
function lineInYourFile(dataRow) {
|
|
4612
|
+
return dataRow + 1;
|
|
4537
4613
|
}
|
|
4538
4614
|
function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
4539
4615
|
const client = useRecordsClient6();
|
|
@@ -4774,6 +4850,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4774
4850
|
const refusedByRules = ruleVerdicts.reduce((n, v) => n + v.refused, 0);
|
|
4775
4851
|
const [goAheadAnyway, setGoAheadAnyway] = useState13(false);
|
|
4776
4852
|
useEffect8(() => setGoAheadAnyway(false), [ruleVerdicts]);
|
|
4853
|
+
const afterWrite = !busy && (phase === "done" || alreadyRan !== null || ledger !== null && ledger.seen > 0);
|
|
4777
4854
|
return /* @__PURE__ */ jsxs12("div", { className: cn12("flex min-w-0 flex-col gap-2 text-xs", className), children: [
|
|
4778
4855
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
4779
4856
|
/* @__PURE__ */ jsx16("span", { className: "font-medium", children: "Import" }),
|
|
@@ -4804,7 +4881,11 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4804
4881
|
problem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: problem }) : null,
|
|
4805
4882
|
phase === "reading" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
|
|
4806
4883
|
phase === "planning" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
|
|
4807
|
-
plan && parsed ? /* @__PURE__ */ jsxs12(
|
|
4884
|
+
plan && parsed && afterWrite ? /* @__PURE__ */ jsxs12("p", { className: "text-muted-foreground", "data-import-after-write": "", children: [
|
|
4885
|
+
fileName ? `${fileName} has been sent to this table` : "This file has been sent to this table",
|
|
4886
|
+
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."
|
|
4887
|
+
] }) : null,
|
|
4888
|
+
plan && parsed && !afterWrite ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
4808
4889
|
titleKey ? /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-2 rounded border bg-muted/40 px-2 py-1.5", children: [
|
|
4809
4890
|
/* @__PURE__ */ jsx16("span", { className: "font-medium", children: "What each record is called" }),
|
|
4810
4891
|
/* @__PURE__ */ jsxs12(
|
|
@@ -4838,30 +4919,19 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4838
4919
|
),
|
|
4839
4920
|
/* @__PURE__ */ jsx16("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.` })
|
|
4840
4921
|
] }) : null,
|
|
4841
|
-
/* @__PURE__ */ jsx16("div", { className: "w-full
|
|
4842
|
-
|
|
4922
|
+
/* @__PURE__ */ jsx16("div", { className: "flex w-full min-w-0 flex-col rounded border", "data-import-mapping": "", children: plan.columns.map((column) => {
|
|
4923
|
+
const to = mapping[column.header] ?? "";
|
|
4924
|
+
const absent = unmapped === "ignore" ? "\u2014 leave it out \u2014" : "\u2014 offer it as a new column \u2014";
|
|
4925
|
+
const matchedField = column.field_id ? declared.find((f) => f.id === column.field_id) : void 0;
|
|
4926
|
+
return /* @__PURE__ */ jsxs12(
|
|
4843
4927
|
"div",
|
|
4844
4928
|
{
|
|
4845
|
-
|
|
4846
|
-
|
|
4929
|
+
"data-import-column": column.header,
|
|
4930
|
+
className: "flex min-w-0 flex-col gap-1 border-t px-2 py-1.5 first:border-t-0",
|
|
4847
4931
|
children: [
|
|
4848
|
-
/* @__PURE__ */
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
/* @__PURE__ */ jsx16("span", { children: "First values" })
|
|
4852
|
-
]
|
|
4853
|
-
}
|
|
4854
|
-
),
|
|
4855
|
-
plan.columns.map((column) => {
|
|
4856
|
-
const to = mapping[column.header] ?? "";
|
|
4857
|
-
const absent = unmapped === "ignore" ? "\u2014 leave it out \u2014" : "\u2014 offer it as a new column \u2014";
|
|
4858
|
-
return /* @__PURE__ */ jsxs12(
|
|
4859
|
-
"div",
|
|
4860
|
-
{
|
|
4861
|
-
className: "grid items-start gap-x-3 border-t px-2 py-1",
|
|
4862
|
-
style: { gridTemplateColumns: MAPPING_COLUMNS },
|
|
4863
|
-
children: [
|
|
4864
|
-
/* @__PURE__ */ jsx16("span", { className: "truncate pt-1.5 font-medium", title: column.header, children: column.header }),
|
|
4932
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
|
|
4933
|
+
/* @__PURE__ */ jsx16("span", { className: "min-w-[8rem] flex-1 break-words font-medium", children: column.header }),
|
|
4934
|
+
/* @__PURE__ */ jsx16("span", { className: "text-muted-foreground", children: "goes to" }),
|
|
4865
4935
|
/* @__PURE__ */ jsxs12(
|
|
4866
4936
|
Select3,
|
|
4867
4937
|
{
|
|
@@ -4882,7 +4952,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4882
4952
|
size: "sm",
|
|
4883
4953
|
"aria-label": `Where ${column.header} goes`,
|
|
4884
4954
|
"data-goes-to": column.header,
|
|
4885
|
-
className: "
|
|
4955
|
+
className: "min-w-[14rem] flex-1",
|
|
4886
4956
|
children: /* @__PURE__ */ jsx16(SelectValue3, {})
|
|
4887
4957
|
}
|
|
4888
4958
|
),
|
|
@@ -4892,42 +4962,53 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4892
4962
|
] })
|
|
4893
4963
|
]
|
|
4894
4964
|
}
|
|
4965
|
+
)
|
|
4966
|
+
] }),
|
|
4967
|
+
/* @__PURE__ */ jsxs12("div", { className: "min-w-0", children: [
|
|
4968
|
+
/* @__PURE__ */ jsx16(
|
|
4969
|
+
Badge5,
|
|
4970
|
+
{
|
|
4971
|
+
variant: column.matched ? "secondary" : "outline",
|
|
4972
|
+
className: "text-[11px] font-normal",
|
|
4973
|
+
"data-import-kind": column.header,
|
|
4974
|
+
children: matchedField ? fieldTypeLabel(matchedField) : typeWord(column.type)
|
|
4975
|
+
}
|
|
4895
4976
|
),
|
|
4896
|
-
/* @__PURE__ */
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
]
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4977
|
+
column.unit ? /* @__PURE__ */ jsx16("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
|
|
4978
|
+
/* @__PURE__ */ jsx16("p", { className: "mt-0.5 break-words text-muted-foreground", children: column.why }),
|
|
4979
|
+
column.ambiguous ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
|
|
4980
|
+
column.collides_with ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null,
|
|
4981
|
+
ruleVerdictOf.get(column.header) ? (() => {
|
|
4982
|
+
const v = ruleVerdictOf.get(column.header);
|
|
4983
|
+
return /* @__PURE__ */ jsxs12("div", { "data-import-rule-refusal": column.header, className: "mt-1", children: [
|
|
4984
|
+
/* @__PURE__ */ jsx16(
|
|
4985
|
+
RefusalLine,
|
|
4986
|
+
{
|
|
4987
|
+
error: refusal(
|
|
4988
|
+
"refused_by_rule",
|
|
4989
|
+
`${v.refused} of ${v.judged} row${v.judged === 1 ? "" : "s"} would be refused by ${fieldName(v.field)}'s rules. ${v.firstWhy}`,
|
|
4990
|
+
"Send this column somewhere else, fix those rows in your file, or import the rest and they are listed afterwards."
|
|
4991
|
+
)
|
|
4992
|
+
}
|
|
4993
|
+
),
|
|
4994
|
+
/* @__PURE__ */ jsxs12("p", { className: "text-muted-foreground", "data-import-rule-sample": "", children: [
|
|
4995
|
+
"Line ",
|
|
4996
|
+
lineInYourFile(v.firstLine),
|
|
4997
|
+
" in your file: ",
|
|
4998
|
+
v.firstRaw
|
|
4999
|
+
] })
|
|
5000
|
+
] });
|
|
5001
|
+
})() : null
|
|
5002
|
+
] }),
|
|
5003
|
+
(column.samples ?? []).length > 0 ? /* @__PURE__ */ jsxs12("p", { className: "break-words text-muted-foreground", children: [
|
|
5004
|
+
"First values: ",
|
|
5005
|
+
(column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ")
|
|
5006
|
+
] }) : null
|
|
5007
|
+
]
|
|
5008
|
+
},
|
|
5009
|
+
column.header
|
|
5010
|
+
);
|
|
5011
|
+
}) }),
|
|
4931
5012
|
/* @__PURE__ */ jsx16(Separator2, {}),
|
|
4932
5013
|
/* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
4933
5014
|
/* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-1", children: [
|
|
@@ -5064,7 +5145,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5064
5145
|
/* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "The row in your file" })
|
|
5065
5146
|
] }) }),
|
|
5066
5147
|
/* @__PURE__ */ jsx16("tbody", { children: interesting.slice(0, 200).map((o) => /* @__PURE__ */ jsxs12("tr", { className: "border-t align-top", children: [
|
|
5067
|
-
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: o.row }),
|
|
5148
|
+
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: lineInYourFile(o.row) }),
|
|
5068
5149
|
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: o.outcome === "refused" ? /* @__PURE__ */ jsx16(
|
|
5069
5150
|
RefusalLine,
|
|
5070
5151
|
{
|
|
@@ -5088,8 +5169,10 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5088
5169
|
" that ",
|
|
5089
5170
|
ledger?.refused === 1 ? "was" : "were",
|
|
5090
5171
|
" refused",
|
|
5091
|
-
" ",
|
|
5092
|
-
"
|
|
5172
|
+
ledger?.refused === 1 ? " is" : " are",
|
|
5173
|
+
" kept with this import \u2014 open ",
|
|
5174
|
+
ledger?.refused === 1 ? "it" : "them",
|
|
5175
|
+
" again from the list of imports below, with the store's reason and the line from your file on each."
|
|
5093
5176
|
] }) : null
|
|
5094
5177
|
] }) : null
|
|
5095
5178
|
] }) : null,
|
|
@@ -5137,7 +5220,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5137
5220
|
/* @__PURE__ */ jsx16("tbody", { children: runRows.rows.map((row, i) => {
|
|
5138
5221
|
const source = row.source ?? {};
|
|
5139
5222
|
return /* @__PURE__ */ jsxs12("tr", { className: "border-t align-top", children: [
|
|
5140
|
-
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: String(row.row ?? "") }),
|
|
5223
|
+
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: typeof row.row === "number" ? lineInYourFile(row.row) : String(row.row ?? "") }),
|
|
5141
5224
|
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: /* @__PURE__ */ jsx16(
|
|
5142
5225
|
RefusalLine,
|
|
5143
5226
|
{
|
|
@@ -5402,6 +5485,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5402
5485
|
const [sensitivity, setSensitivity] = useState15(String(field?.sensitivity ?? "internal"));
|
|
5403
5486
|
const [contextPolicy, setContextPolicy] = useState15(String(field?.context_policy ?? "include"));
|
|
5404
5487
|
const [rules, setRules] = useState15(field?.rules ?? []);
|
|
5488
|
+
const [allowOther, setAllowOther] = useState15(field?.config?.["allow_other"] === true);
|
|
5405
5489
|
const [optionText, setOptionText] = useState15("");
|
|
5406
5490
|
const [unit, setUnit] = useState15(field?.unit ?? "$");
|
|
5407
5491
|
const [withTime, setWithTime] = useState15(String(field?.config?.["kind"] ?? "date") === "datetime");
|
|
@@ -5458,7 +5542,10 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5458
5542
|
// REMOVES a spec and the column goes back to the target table's own
|
|
5459
5543
|
// reference column — a panel that only sent the key when it was set
|
|
5460
5544
|
// could turn the override on and never off.
|
|
5461
|
-
...field.type === "relation" ? { display } : {}
|
|
5545
|
+
...field.type === "relation" ? { display } : {},
|
|
5546
|
+
// STORE-RULE-GAPS: a choice list's own setting, sent for every list so
|
|
5547
|
+
// turning it off is as possible as turning it on.
|
|
5548
|
+
...field.type === "list" ? { allow_other: allowOther } : {}
|
|
5462
5549
|
};
|
|
5463
5550
|
const written2 = await mutation.update({ field_id: field.id, patch });
|
|
5464
5551
|
if (written2 !== null) onSaved?.();
|
|
@@ -5485,6 +5572,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5485
5572
|
context_policy: contextPolicy,
|
|
5486
5573
|
rules,
|
|
5487
5574
|
...type === "select" || type === "multi_select" ? { options } : {},
|
|
5575
|
+
...(type === "select" || type === "multi_select") && allowOther ? { allow_other: true } : {},
|
|
5488
5576
|
...type === "currency" ? { unit } : {},
|
|
5489
5577
|
...type === "datetime" ? { kind: withTime ? "datetime" : "date" } : {},
|
|
5490
5578
|
...type === "lookup" ? { via, pick } : {},
|
|
@@ -5616,7 +5704,18 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5616
5704
|
}
|
|
5617
5705
|
),
|
|
5618
5706
|
"Each value has its own date"
|
|
5619
|
-
] })
|
|
5707
|
+
] }),
|
|
5708
|
+
type === "select" || type === "multi_select" ? /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1.5", children: [
|
|
5709
|
+
/* @__PURE__ */ jsx18(
|
|
5710
|
+
Checkbox4,
|
|
5711
|
+
{
|
|
5712
|
+
checked: allowOther,
|
|
5713
|
+
"aria-label": "A value that is not one of the choices is added to them",
|
|
5714
|
+
onCheckedChange: (v) => setAllowOther(v === true)
|
|
5715
|
+
}
|
|
5716
|
+
),
|
|
5717
|
+
"A value that is not one of the choices is added to them"
|
|
5718
|
+
] }) : null
|
|
5620
5719
|
] }),
|
|
5621
5720
|
/* @__PURE__ */ jsx18(Separator4, {}),
|
|
5622
5721
|
/* @__PURE__ */ jsxs14("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
@@ -5664,15 +5763,37 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5664
5763
|
]
|
|
5665
5764
|
}
|
|
5666
5765
|
),
|
|
5766
|
+
rule.kind === "length" ? /* @__PURE__ */ jsx18(
|
|
5767
|
+
BasicInput5,
|
|
5768
|
+
{
|
|
5769
|
+
className: "h-8 w-24",
|
|
5770
|
+
inputMode: "numeric",
|
|
5771
|
+
placeholder: "fewest",
|
|
5772
|
+
value: rule.min === void 0 ? "" : String(rule.min),
|
|
5773
|
+
"aria-label": `Check ${index + 1} fewest characters`,
|
|
5774
|
+
onChange: (e) => setRules(rules.map((r, i) => i === index ? withNumber(r, "min", e.target.value) : r))
|
|
5775
|
+
}
|
|
5776
|
+
) : null,
|
|
5667
5777
|
/* @__PURE__ */ jsx18(
|
|
5668
5778
|
BasicInput5,
|
|
5669
5779
|
{
|
|
5670
5780
|
className: "h-8",
|
|
5781
|
+
...rule.kind === "length" ? { inputMode: "numeric", placeholder: "most" } : {},
|
|
5671
5782
|
value: String(rule.value ?? ""),
|
|
5672
|
-
"aria-label": `Check ${index + 1} value`,
|
|
5783
|
+
"aria-label": rule.kind === "length" ? `Check ${index + 1} most characters` : `Check ${index + 1} value`,
|
|
5673
5784
|
onChange: (e) => setRules(rules.map((r, i) => i === index ? { ...r, value: e.target.value } : r))
|
|
5674
5785
|
}
|
|
5675
5786
|
),
|
|
5787
|
+
rule.kind === "pattern" ? /* @__PURE__ */ jsx18(
|
|
5788
|
+
BasicInput5,
|
|
5789
|
+
{
|
|
5790
|
+
className: "h-8",
|
|
5791
|
+
placeholder: "for example 949-555-0142",
|
|
5792
|
+
value: rule.example ?? "",
|
|
5793
|
+
"aria-label": `Check ${index + 1} example`,
|
|
5794
|
+
onChange: (e) => setRules(rules.map((r, i) => i === index ? withText(r, "example", e.target.value) : r))
|
|
5795
|
+
}
|
|
5796
|
+
) : null,
|
|
5676
5797
|
/* @__PURE__ */ jsx18(
|
|
5677
5798
|
Button13,
|
|
5678
5799
|
{
|
|
@@ -5978,6 +6099,17 @@ function ValueOnTheOtherSide({
|
|
|
5978
6099
|
/* @__PURE__ */ jsx18("p", { className: "text-[11px] text-muted-foreground", children: help })
|
|
5979
6100
|
] });
|
|
5980
6101
|
}
|
|
6102
|
+
function withNumber(rule, key, typed) {
|
|
6103
|
+
const { [key]: _gone, ...rest } = rule;
|
|
6104
|
+
const trimmed = typed.trim();
|
|
6105
|
+
if (trimmed === "") return rest;
|
|
6106
|
+
const n = Number(trimmed);
|
|
6107
|
+
return Number.isFinite(n) ? { ...rest, [key]: n } : { ...rest, [key]: trimmed };
|
|
6108
|
+
}
|
|
6109
|
+
function withText(rule, key, typed) {
|
|
6110
|
+
const { [key]: _gone, ...rest } = rule;
|
|
6111
|
+
return typed.trim() === "" ? rest : { ...rest, [key]: typed };
|
|
6112
|
+
}
|
|
5981
6113
|
|
|
5982
6114
|
// src/StageRules.tsx
|
|
5983
6115
|
import { useCallback as useCallback9, useEffect as useEffect9, useMemo as useMemo13, useState as useState17 } from "react";
|
|
@@ -6739,9 +6871,9 @@ function TableSettings({
|
|
|
6739
6871
|
/* @__PURE__ */ jsx21(StageRulesSection, { tableId }),
|
|
6740
6872
|
/* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
|
|
6741
6873
|
/* @__PURE__ */ jsx21("span", { className: "font-medium", children: "Proposed" }),
|
|
6742
|
-
/* @__PURE__ */ jsx21("span", { className: "text-muted-foreground", children: proposals
|
|
6874
|
+
proposals === void 0 ? null : /* @__PURE__ */ jsx21("span", { className: "text-muted-foreground", children: proposals.length })
|
|
6743
6875
|
] }),
|
|
6744
|
-
proposals === void 0 ? /* @__PURE__ */ jsx21("p", { className: "text-muted-foreground", children: "
|
|
6876
|
+
proposals === void 0 ? /* @__PURE__ */ jsx21("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__ */ jsx21("p", { className: "text-muted-foreground", children: "No field is waiting for a decision." }) : /* @__PURE__ */ jsx21("ul", { className: "divide-y rounded border", children: proposals.map((proposal) => /* @__PURE__ */ jsxs17("li", { className: "flex flex-wrap items-center gap-x-2 gap-y-1 px-2 py-1.5", children: [
|
|
6745
6877
|
/* @__PURE__ */ jsx21("span", { className: "min-w-[8rem] flex-1 break-words", children: proposal.field.label || proposal.field.key }),
|
|
6746
6878
|
/* @__PURE__ */ jsx21("span", { className: "min-w-[8rem] flex-1 break-words text-muted-foreground", children: proposal.why }),
|
|
6747
6879
|
/* @__PURE__ */ jsx21(Badge6, { variant: "secondary", className: "text-[10px] font-normal", children: proposal.proposed_by }),
|
|
@@ -7510,10 +7642,10 @@ function Peek({ tableId, recordId, onClose, className }) {
|
|
|
7510
7642
|
}
|
|
7511
7643
|
function computedInPlainWords(row, field) {
|
|
7512
7644
|
const name = field ? fieldName(field) : humanize(row.field_key);
|
|
7513
|
-
const
|
|
7645
|
+
const shown2 = row.value === null || row.value === void 0 || row.value === "" ? "nothing" : String(row.value);
|
|
7514
7646
|
const when = new Date(row.computed_at);
|
|
7515
7647
|
const at = Number.isNaN(when.getTime()) ? "" : ` on ${when.toLocaleDateString()}`;
|
|
7516
|
-
return `${name} was worked out as ${
|
|
7648
|
+
return `${name} was worked out as ${shown2}${at}.`;
|
|
7517
7649
|
}
|
|
7518
7650
|
|
|
7519
7651
|
// src/views.ts
|
|
@@ -8468,7 +8600,7 @@ function Gallery({
|
|
|
8468
8600
|
onOpenRecord,
|
|
8469
8601
|
note
|
|
8470
8602
|
}) {
|
|
8471
|
-
const
|
|
8603
|
+
const shown2 = fields.slice(0, 4);
|
|
8472
8604
|
return /* @__PURE__ */ jsxs23("div", { className: "flex min-h-0 flex-col gap-2", children: [
|
|
8473
8605
|
/* @__PURE__ */ jsx27(Note, { children: note }),
|
|
8474
8606
|
/* @__PURE__ */ jsx27("ul", { className: "grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-2 overflow-y-auto", children: rows.map((row) => {
|
|
@@ -8481,7 +8613,7 @@ function Gallery({
|
|
|
8481
8613
|
onClick: () => onOpenRecord?.(row.id),
|
|
8482
8614
|
children: [
|
|
8483
8615
|
typeof image === "string" && image !== "" ? /* @__PURE__ */ jsx27("img", { src: image, alt: "", className: "h-28 w-full rounded object-cover" }) : null,
|
|
8484
|
-
|
|
8616
|
+
shown2.map((field) => /* @__PURE__ */ jsxs23("span", { className: "flex min-w-0 items-baseline gap-1 text-xs", children: [
|
|
8485
8617
|
/* @__PURE__ */ jsx27("span", { className: "shrink-0 text-muted-foreground", children: fieldName(field) }),
|
|
8486
8618
|
/* @__PURE__ */ jsx27("span", { className: "min-w-0 truncate", children: /* @__PURE__ */ jsx27(RecordValue, { field, value: (row.document ?? {})[field.key], document: row.document }) })
|
|
8487
8619
|
] }, field.key))
|
|
@@ -9822,18 +9954,18 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9822
9954
|
useEffect20(() => {
|
|
9823
9955
|
void load();
|
|
9824
9956
|
}, [load]);
|
|
9825
|
-
const
|
|
9957
|
+
const shown2 = useMemo20(() => {
|
|
9826
9958
|
const all = items ?? [];
|
|
9827
9959
|
if (!tableId) return all;
|
|
9828
9960
|
return all.filter((i) => i.kind !== "assignment" || i.subject_kind !== "record" || true);
|
|
9829
9961
|
}, [items, tableId]);
|
|
9830
9962
|
useEffect20(() => {
|
|
9831
|
-
if (cursor >=
|
|
9832
|
-
}, [
|
|
9963
|
+
if (cursor >= shown2.length) setCursor(Math.max(0, shown2.length - 1));
|
|
9964
|
+
}, [shown2.length, cursor]);
|
|
9833
9965
|
useEffect20(() => {
|
|
9834
9966
|
const el = listRef.current?.querySelector(`[data-row="${cursor}"]`);
|
|
9835
9967
|
el?.scrollIntoView({ block: "nearest" });
|
|
9836
|
-
}, [cursor,
|
|
9968
|
+
}, [cursor, shown2.length]);
|
|
9837
9969
|
const decide = useCallback16(
|
|
9838
9970
|
async (item, approve) => {
|
|
9839
9971
|
if (item.kind === "assignment") return;
|
|
@@ -9858,11 +9990,11 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9858
9990
|
);
|
|
9859
9991
|
const onKeyDown = useCallback16(
|
|
9860
9992
|
(event) => {
|
|
9861
|
-
const item =
|
|
9993
|
+
const item = shown2[cursor];
|
|
9862
9994
|
const key = event.key;
|
|
9863
9995
|
if (key === "j" || key === "ArrowDown") {
|
|
9864
9996
|
event.preventDefault();
|
|
9865
|
-
setCursor((c) => Math.min(
|
|
9997
|
+
setCursor((c) => Math.min(shown2.length - 1, c + 1));
|
|
9866
9998
|
} else if (key === "k" || key === "ArrowUp") {
|
|
9867
9999
|
event.preventDefault();
|
|
9868
10000
|
setCursor((c) => Math.max(0, c - 1));
|
|
@@ -9877,7 +10009,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9877
10009
|
void decide(item, key === "a");
|
|
9878
10010
|
}
|
|
9879
10011
|
},
|
|
9880
|
-
[
|
|
10012
|
+
[shown2, cursor, decide, open, load]
|
|
9881
10013
|
);
|
|
9882
10014
|
if (items === null) return /* @__PURE__ */ jsx33(Skeleton12, { className: cn27("h-32 w-full", className) });
|
|
9883
10015
|
return /* @__PURE__ */ jsxs29(
|
|
@@ -9890,12 +10022,12 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9890
10022
|
children: [
|
|
9891
10023
|
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2", children: [
|
|
9892
10024
|
/* @__PURE__ */ jsx33("h3", { className: "text-sm font-medium", children: "Inbox" }),
|
|
9893
|
-
/* @__PURE__ */ jsx33("span", { className: "text-xs text-muted-foreground", children:
|
|
10025
|
+
/* @__PURE__ */ jsx33("span", { className: "text-xs text-muted-foreground", children: shown2.length }),
|
|
9894
10026
|
/* @__PURE__ */ jsx33("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" }),
|
|
9895
10027
|
/* @__PURE__ */ jsx33(Button27, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Refresh" })
|
|
9896
10028
|
] }),
|
|
9897
10029
|
error ? /* @__PURE__ */ jsx33(RefusalNotice, { error }) : null,
|
|
9898
|
-
|
|
10030
|
+
shown2.length === 0 ? /* @__PURE__ */ jsx33("p", { className: "text-xs text-muted-foreground", children: includeSettled ? "Nothing has come through this inbox yet." : "Nothing is waiting on you." }) : /* @__PURE__ */ jsx33("ol", { ref: listRef, className: "flex min-h-0 flex-col gap-1 overflow-y-auto", children: shown2.map((item, index) => {
|
|
9899
10031
|
const settled = outcome[item.item_id];
|
|
9900
10032
|
return /* @__PURE__ */ jsxs29(
|
|
9901
10033
|
"li",
|
|
@@ -9959,6 +10091,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9959
10091
|
import { useCallback as useCallback17, useEffect as useEffect21, useRef as useRef10, useState as useState32 } from "react";
|
|
9960
10092
|
import {
|
|
9961
10093
|
useFields as useFields14,
|
|
10094
|
+
useRecordChangeRevision,
|
|
9962
10095
|
useRecordsClient as useRecordsClient21,
|
|
9963
10096
|
useTable as useTable11
|
|
9964
10097
|
} from "@ai-matrx/records/react";
|
|
@@ -9975,6 +10108,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
|
|
|
9975
10108
|
const [pending, setPending] = useState32({ phase: "idle" });
|
|
9976
10109
|
const [said, setSaid] = useState32(null);
|
|
9977
10110
|
const listRef = useRef10(null);
|
|
10111
|
+
const changed = useRecordChangeRevision(recordId);
|
|
9978
10112
|
const load = useCallback17(async () => {
|
|
9979
10113
|
const answered = await client.recordHistory({ record_id: recordId });
|
|
9980
10114
|
if (!answered.ok) {
|
|
@@ -9983,7 +10117,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
|
|
|
9983
10117
|
}
|
|
9984
10118
|
setError(null);
|
|
9985
10119
|
setEntries(answered.data);
|
|
9986
|
-
}, [client, recordId]);
|
|
10120
|
+
}, [client, recordId, changed]);
|
|
9987
10121
|
useEffect21(() => {
|
|
9988
10122
|
void load();
|
|
9989
10123
|
}, [load]);
|
|
@@ -10329,9 +10463,9 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
|
|
|
10329
10463
|
}, [mentionQuery, people, picked]);
|
|
10330
10464
|
if (error) return /* @__PURE__ */ jsx35(RefusalNotice, { error, className });
|
|
10331
10465
|
if (!thread || table.loading || fields.loading) return /* @__PURE__ */ jsx35(Skeleton14, { className: cn29("h-32 w-full", className) });
|
|
10332
|
-
const
|
|
10333
|
-
const roots =
|
|
10334
|
-
const repliesOf = (id) =>
|
|
10466
|
+
const shown2 = fieldKey ? thread.comments.filter((c) => c.field_key === fieldKey) : thread.comments;
|
|
10467
|
+
const roots = shown2.filter((c) => !c.parent_comment_id);
|
|
10468
|
+
const repliesOf = (id) => shown2.filter((c) => c.parent_comment_id === id);
|
|
10335
10469
|
const fieldLabel = fieldKey ? (fields.data ?? []).find((f) => f.key === fieldKey)?.label || humanize(fieldKey) : null;
|
|
10336
10470
|
async function post() {
|
|
10337
10471
|
if (draft.trim() === "") return;
|
|
@@ -10374,7 +10508,7 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
|
|
|
10374
10508
|
return /* @__PURE__ */ jsxs31("section", { className: cn29("flex min-h-0 flex-col gap-2", className), children: [
|
|
10375
10509
|
/* @__PURE__ */ jsxs31("div", { className: "flex items-baseline gap-2", children: [
|
|
10376
10510
|
/* @__PURE__ */ jsx35("h3", { className: "text-sm font-medium", children: fieldLabel ? `Comments on ${fieldLabel}` : "Comments" }),
|
|
10377
|
-
/* @__PURE__ */ jsx35("span", { className: "text-xs text-muted-foreground", children:
|
|
10511
|
+
/* @__PURE__ */ jsx35("span", { className: "text-xs text-muted-foreground", children: shown2.length }),
|
|
10378
10512
|
/* @__PURE__ */ jsx35(
|
|
10379
10513
|
Button29,
|
|
10380
10514
|
{
|
|
@@ -11135,16 +11269,16 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11135
11269
|
const holdingThis = new Set(
|
|
11136
11270
|
exposures.filter((e) => e.table_id === tableId).map((e) => e.portal_id)
|
|
11137
11271
|
);
|
|
11138
|
-
const
|
|
11272
|
+
const shown2 = onATable ? portals.filter((p) => holdingThis.has(p.portal_id)) : portals;
|
|
11139
11273
|
const couldTakeIt = onATable ? portals.filter((p) => !holdingThis.has(p.portal_id)) : [];
|
|
11140
11274
|
const elsewhere = couldTakeIt.length;
|
|
11141
11275
|
const origin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
11142
11276
|
return /* @__PURE__ */ jsxs35("section", { className: cn33("flex flex-col gap-3", className), children: [
|
|
11143
11277
|
/* @__PURE__ */ jsxs35("header", { className: "flex items-center gap-2", children: [
|
|
11144
11278
|
/* @__PURE__ */ jsx39("h3", { className: "text-sm font-medium", children: "Portals" }),
|
|
11145
|
-
/* @__PURE__ */ jsx39("span", { className: "text-xs text-muted-foreground", children:
|
|
11279
|
+
/* @__PURE__ */ jsx39("span", { className: "text-xs text-muted-foreground", children: shown2.length === 0 ? "none for this table" : `${shown2.length}` }),
|
|
11146
11280
|
/* @__PURE__ */ jsx39("div", { className: "flex-1" }),
|
|
11147
|
-
|
|
11281
|
+
shown2.length > 0 ? /* @__PURE__ */ jsx39(Button33, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a portal" }) : null
|
|
11148
11282
|
] }),
|
|
11149
11283
|
listError ? /* @__PURE__ */ jsx39(RefusalNotice, { error: listError }) : null,
|
|
11150
11284
|
building ? /* @__PURE__ */ jsx39(
|
|
@@ -11169,7 +11303,7 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11169
11303
|
onClose: () => setAdding(null)
|
|
11170
11304
|
}
|
|
11171
11305
|
) : null,
|
|
11172
|
-
|
|
11306
|
+
shown2.length === 0 && !listError && !building && !adding ? /* @__PURE__ */ jsxs35(
|
|
11173
11307
|
BuildOrAsk,
|
|
11174
11308
|
{
|
|
11175
11309
|
mayBuild: true,
|
|
@@ -11191,7 +11325,7 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11191
11325
|
]
|
|
11192
11326
|
}
|
|
11193
11327
|
) : null,
|
|
11194
|
-
|
|
11328
|
+
shown2.length === 0 && !listError && !building && !adding && elsewhere > 0 ? /* @__PURE__ */ jsxs35("div", { className: "flex flex-col gap-1.5 rounded-md border border-dashed border-border p-2.5", children: [
|
|
11195
11329
|
/* @__PURE__ */ jsx39("p", { className: "text-xs text-muted-foreground", children: "Or add this table to a portal your clients already sign in to:" }),
|
|
11196
11330
|
/* @__PURE__ */ jsx39("div", { className: "flex flex-wrap gap-1.5", children: couldTakeIt.map((portal) => /* @__PURE__ */ jsxs35(
|
|
11197
11331
|
Button33,
|
|
@@ -11208,8 +11342,8 @@ function PortalsPanel({ tableId, activePortalId, className }) {
|
|
|
11208
11342
|
portal.portal_id
|
|
11209
11343
|
)) })
|
|
11210
11344
|
] }) : null,
|
|
11211
|
-
activePortalId && !
|
|
11212
|
-
/* @__PURE__ */ jsx39("ul", { className: "flex flex-col gap-2", children:
|
|
11345
|
+
activePortalId && !shown2.some((p) => p.portal_id === activePortalId) ? /* @__PURE__ */ jsx39("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,
|
|
11346
|
+
/* @__PURE__ */ jsx39("ul", { className: "flex flex-col gap-2", children: shown2.map((portal) => {
|
|
11213
11347
|
const url = `${origin}${portalPath(portal.slug)}`;
|
|
11214
11348
|
const open = openId === portal.portal_id;
|
|
11215
11349
|
const linked = portal.portal_id === activePortalId;
|
|
@@ -11356,7 +11490,7 @@ function ArchivePortalControl({
|
|
|
11356
11490
|
}
|
|
11357
11491
|
function CopyLink({ url, what }) {
|
|
11358
11492
|
const [copied, setCopied] = useState36(false);
|
|
11359
|
-
const [
|
|
11493
|
+
const [shown2, setShown] = useState36(false);
|
|
11360
11494
|
return /* @__PURE__ */ jsxs35(Fragment16, { children: [
|
|
11361
11495
|
/* @__PURE__ */ jsx39(
|
|
11362
11496
|
Button33,
|
|
@@ -11378,7 +11512,7 @@ function CopyLink({ url, what }) {
|
|
|
11378
11512
|
children: copied ? "Copied" : what ? `Copy ${what}` : "Copy link"
|
|
11379
11513
|
}
|
|
11380
11514
|
),
|
|
11381
|
-
|
|
11515
|
+
shown2 ? /* @__PURE__ */ jsxs35("p", { className: "mt-1.5 w-full break-all rounded border border-dashed border-border px-2 py-1 text-xs", children: [
|
|
11382
11516
|
"This browser would not let the page copy for you, so here it is to copy by hand: ",
|
|
11383
11517
|
url
|
|
11384
11518
|
] }) : null
|
|
@@ -13994,20 +14128,23 @@ function ChartBlock({ block, subject, className }) {
|
|
|
13994
14128
|
label: point.label === "All records" ? block.title : `${block.title} \xB7 ${point.label}`
|
|
13995
14129
|
});
|
|
13996
14130
|
} : null;
|
|
13997
|
-
return /* @__PURE__ */ jsxs45(
|
|
13998
|
-
|
|
13999
|
-
|
|
14000
|
-
|
|
14001
|
-
typeof block.ms === "number" ?
|
|
14002
|
-
|
|
14003
|
-
"
|
|
14004
|
-
|
|
14005
|
-
|
|
14006
|
-
|
|
14007
|
-
|
|
14008
|
-
|
|
14009
|
-
|
|
14010
|
-
|
|
14131
|
+
return /* @__PURE__ */ jsxs45(
|
|
14132
|
+
"figure",
|
|
14133
|
+
{
|
|
14134
|
+
className: cn43("flex min-w-0 flex-col gap-1.5 rounded border p-2", className),
|
|
14135
|
+
...typeof block.ms === "number" ? { "data-store-ms": pretty(block.ms) } : {},
|
|
14136
|
+
children: [
|
|
14137
|
+
/* @__PURE__ */ jsxs45("figcaption", { className: "flex items-baseline gap-2 text-xs", children: [
|
|
14138
|
+
/* @__PURE__ */ jsx49("span", { className: "truncate font-medium", children: block.title || CHART_KIND_LABEL[kind] }),
|
|
14139
|
+
/* @__PURE__ */ jsx49("span", { className: "ml-auto shrink-0 text-muted-foreground", children: kind === "stuck" ? `${block.days ?? 14} days` : primary.replace("_", " of ") })
|
|
14140
|
+
] }),
|
|
14141
|
+
block.refused ? /* @__PURE__ */ jsx49(RefusalNotice, { error: asRefusal2(block) }) : needs ? /* @__PURE__ */ jsx49("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: needs }) : kind === "stuck" ? /* @__PURE__ */ jsx49(StuckList, { rows: block.rows ?? [] }) : points.length === 0 ? /* @__PURE__ */ jsx49("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__ */ jsx49(Fragment20, { children: kind === "number" ? /* @__PURE__ */ jsx49(BigNumber, { points, measure: primary, onDrill: drill }) : kind === "table" ? /* @__PURE__ */ jsx49(GroupTable, { points, series, onDrill: drill }) : /* @__PURE__ */ jsxs45(Fragment20, { children: [
|
|
14142
|
+
/* @__PURE__ */ jsx49(Drawing, { kind, points, series, config, onDrill: drill }),
|
|
14143
|
+
/* @__PURE__ */ jsx49(Values, { points, measure: primary, config, onDrill: drill })
|
|
14144
|
+
] }) })
|
|
14145
|
+
]
|
|
14146
|
+
}
|
|
14147
|
+
);
|
|
14011
14148
|
}
|
|
14012
14149
|
function BigNumber({
|
|
14013
14150
|
points,
|
|
@@ -14629,7 +14766,7 @@ function FormsPanel({ tableId, activeFormId, className }) {
|
|
|
14629
14766
|
},
|
|
14630
14767
|
[client, load]
|
|
14631
14768
|
);
|
|
14632
|
-
const [
|
|
14769
|
+
const [shown2, setShown] = useState48(null);
|
|
14633
14770
|
const copy = useCallback31(async (url, formId) => {
|
|
14634
14771
|
try {
|
|
14635
14772
|
await navigator.clipboard.writeText(url);
|
|
@@ -14728,7 +14865,7 @@ function FormsPanel({ tableId, activeFormId, className }) {
|
|
|
14728
14865
|
) : null
|
|
14729
14866
|
] }),
|
|
14730
14867
|
rights.structure && publishGate.why ? /* @__PURE__ */ jsx51("p", { className: "mt-1.5 text-xs text-muted-foreground", children: publishGate.why }) : null,
|
|
14731
|
-
|
|
14868
|
+
shown2 && shown2 === url ? /* @__PURE__ */ jsxs47("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
14732
14869
|
"This browser would not let the page copy for you, so here it is to copy by hand:",
|
|
14733
14870
|
" ",
|
|
14734
14871
|
url
|
|
@@ -14892,7 +15029,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
14892
15029
|
return /* @__PURE__ */ jsx52("p", { className: cn46("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." });
|
|
14893
15030
|
}
|
|
14894
15031
|
const url = formId ? `${publicOrigin}${bookingPath(formId)}` : null;
|
|
14895
|
-
const
|
|
15032
|
+
const shown2 = offer ?? null;
|
|
14896
15033
|
return (
|
|
14897
15034
|
// THE RAIL IS 384px WIDE AND THE VIEWPORT IS NOT (walk 1, 2026-09-21):
|
|
14898
15035
|
// every split below is a CONTAINER query, so this screen is the same
|
|
@@ -15040,21 +15177,21 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
15040
15177
|
}
|
|
15041
15178
|
)
|
|
15042
15179
|
] }),
|
|
15043
|
-
|
|
15180
|
+
shown2 ? /* @__PURE__ */ jsxs48("p", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
15044
15181
|
"Saved. The page offers ",
|
|
15045
|
-
|
|
15182
|
+
shown2.slot_minutes,
|
|
15046
15183
|
"-minute appointments in ",
|
|
15047
|
-
|
|
15184
|
+
shown2.timezone,
|
|
15048
15185
|
", up to",
|
|
15049
15186
|
" ",
|
|
15050
|
-
|
|
15187
|
+
shown2.max_per_day,
|
|
15051
15188
|
" a day, ",
|
|
15052
|
-
|
|
15189
|
+
shown2.days,
|
|
15053
15190
|
" days ahead",
|
|
15054
|
-
|
|
15191
|
+
shown2.buffer_minutes > 0 ? `, with ${shown2.buffer_minutes} minutes between them` : "",
|
|
15055
15192
|
":",
|
|
15056
15193
|
" ",
|
|
15057
|
-
|
|
15194
|
+
shown2.windows.map(
|
|
15058
15195
|
(w) => `${DAYS.find((d) => d.weekday === w.weekday)?.label ?? w.weekday} ${w.from}\u2013${w.to}`
|
|
15059
15196
|
).join(", "),
|
|
15060
15197
|
"."
|
|
@@ -15096,21 +15233,22 @@ import { useRecordsClient as useRecordsClient37, useMyLevels as useMyLevels2 } f
|
|
|
15096
15233
|
import { bookingPath as bookingPath2 } from "@ai-matrx/records";
|
|
15097
15234
|
import { Badge as Badge16, Button as Button45, Skeleton as Skeleton29, cn as cn47 } from "@ai-matrx/design-system";
|
|
15098
15235
|
import { Fragment as Fragment24, jsx as jsx53, jsxs as jsxs49 } from "react/jsx-runtime";
|
|
15236
|
+
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.";
|
|
15099
15237
|
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.";
|
|
15100
15238
|
function bookingSuggestion(tableName2) {
|
|
15101
|
-
const
|
|
15102
|
-
return `Let clients book a 30-minute ${
|
|
15239
|
+
const name = tableName2?.trim();
|
|
15240
|
+
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.";
|
|
15103
15241
|
}
|
|
15104
15242
|
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.";
|
|
15105
15243
|
var STATE_WORDS = BOOKING_PAGE_STATE_LABEL;
|
|
15106
|
-
function BookingSlots({ tableId, className }) {
|
|
15244
|
+
function BookingSlots({ tableId, activeBookingId, className }) {
|
|
15107
15245
|
const client = useRecordsClient37();
|
|
15108
15246
|
const host = useRecordsUi();
|
|
15109
15247
|
const [pages, setPages] = useState50(null);
|
|
15110
15248
|
const [error, setError] = useState50(null);
|
|
15111
15249
|
const [busy, setBusy] = useState50(null);
|
|
15112
15250
|
const [copied, setCopied] = useState50(null);
|
|
15113
|
-
const [
|
|
15251
|
+
const [shown2, setShown] = useState50(null);
|
|
15114
15252
|
const [building, setBuilding] = useState50(false);
|
|
15115
15253
|
const load = useCallback33(async () => {
|
|
15116
15254
|
const answered = await client.bookings(tableId ? { table_id: tableId } : {});
|
|
@@ -15176,6 +15314,7 @@ function BookingSlots({ tableId, className }) {
|
|
|
15176
15314
|
tableId && pages.length > 0 ? /* @__PURE__ */ jsx53(Button45, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a booking page" }) : null
|
|
15177
15315
|
] }),
|
|
15178
15316
|
error ? /* @__PURE__ */ jsx53(RefusalNotice, { error }) : null,
|
|
15317
|
+
activeBookingId && !pages.some((p) => p.form_id === activeBookingId) ? /* @__PURE__ */ jsx53("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,
|
|
15179
15318
|
building && tableId ? /* @__PURE__ */ jsx53(
|
|
15180
15319
|
BookingBuilder,
|
|
15181
15320
|
{
|
|
@@ -15211,53 +15350,66 @@ function BookingSlots({ tableId, className }) {
|
|
|
15211
15350
|
const url = `${origin}${bookingPath2(page.form_id)}`;
|
|
15212
15351
|
const mayOpen = levels.data?.[page.table_id] === "admin";
|
|
15213
15352
|
const open = page.published_at !== null && page.closed_at === null;
|
|
15214
|
-
return /* @__PURE__ */ jsxs49(
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
|
|
15219
|
-
|
|
15220
|
-
|
|
15221
|
-
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
"
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
|
|
15233
|
-
|
|
15234
|
-
|
|
15235
|
-
|
|
15236
|
-
|
|
15237
|
-
|
|
15238
|
-
|
|
15239
|
-
|
|
15240
|
-
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15246
|
-
|
|
15247
|
-
|
|
15248
|
-
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15353
|
+
return /* @__PURE__ */ jsxs49(
|
|
15354
|
+
"li",
|
|
15355
|
+
{
|
|
15356
|
+
"data-testid": "booking-page",
|
|
15357
|
+
"data-linked": page.form_id === activeBookingId ? "true" : void 0,
|
|
15358
|
+
ref: page.form_id === activeBookingId ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
15359
|
+
className: cn47(
|
|
15360
|
+
"rounded-md border p-2.5",
|
|
15361
|
+
page.form_id === activeBookingId && "border-primary ring-1 ring-primary"
|
|
15362
|
+
),
|
|
15363
|
+
children: [
|
|
15364
|
+
/* @__PURE__ */ jsxs49("div", { className: "flex items-center gap-2", children: [
|
|
15365
|
+
/* @__PURE__ */ jsx53("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: page.title ?? page.slug }),
|
|
15366
|
+
/* @__PURE__ */ jsx53(Badge16, { variant: page.state === "open" ? "default" : "secondary", children: page.state })
|
|
15367
|
+
] }),
|
|
15368
|
+
/* @__PURE__ */ jsx53("p", { className: "mt-0.5 text-xs text-muted-foreground", children: STATE_WORDS[page.state] ?? page.state }),
|
|
15369
|
+
/* @__PURE__ */ jsxs49("p", { className: "mt-1.5 text-xs", children: [
|
|
15370
|
+
/* @__PURE__ */ jsx53("span", { className: "font-medium", children: page.upcoming }),
|
|
15371
|
+
" coming up",
|
|
15372
|
+
" \xB7 ",
|
|
15373
|
+
/* @__PURE__ */ jsx53("span", { className: "font-medium", children: page.booked }),
|
|
15374
|
+
" booked in all",
|
|
15375
|
+
page.held > 0 ? /* @__PURE__ */ jsxs49(Fragment24, { children: [
|
|
15376
|
+
" \xB7 ",
|
|
15377
|
+
/* @__PURE__ */ jsx53("span", { className: "font-medium", children: page.held }),
|
|
15378
|
+
" being held right now"
|
|
15379
|
+
] }) : null,
|
|
15380
|
+
page.cancelled > 0 ? /* @__PURE__ */ jsxs49(Fragment24, { children: [
|
|
15381
|
+
" \xB7 ",
|
|
15382
|
+
/* @__PURE__ */ jsx53("span", { className: "font-medium", children: page.cancelled }),
|
|
15383
|
+
" cancelled"
|
|
15384
|
+
] }) : null,
|
|
15385
|
+
/* @__PURE__ */ jsxs49("span", { className: "text-muted-foreground", children: [
|
|
15386
|
+
" \xB7 ",
|
|
15387
|
+
page.slot_minutes,
|
|
15388
|
+
" minutes each"
|
|
15389
|
+
] })
|
|
15390
|
+
] }),
|
|
15391
|
+
/* @__PURE__ */ jsx53("p", { className: "mt-1 text-xs text-muted-foreground", children: nextInWords(page) }),
|
|
15392
|
+
/* @__PURE__ */ jsxs49("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
15393
|
+
/* @__PURE__ */ jsx53(Button45, { size: "sm", variant: "outline", onClick: () => void copy(url, page.form_id), children: copied === page.form_id ? "Copied" : "Copy link" }),
|
|
15394
|
+
mayOpen ? /* @__PURE__ */ jsx53(
|
|
15395
|
+
Button45,
|
|
15396
|
+
{
|
|
15397
|
+
size: "sm",
|
|
15398
|
+
variant: "ghost",
|
|
15399
|
+
disabled: busy === page.form_id,
|
|
15400
|
+
onClick: () => void toggle(page),
|
|
15401
|
+
children: busy === page.form_id ? "\u2026" : open ? "Close" : "Open"
|
|
15402
|
+
}
|
|
15403
|
+
) : null
|
|
15404
|
+
] }),
|
|
15405
|
+
shown2 && shown2 === url ? /* @__PURE__ */ jsxs49("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
15406
|
+
"This browser would not let the page copy for you, so here it is to copy by hand: ",
|
|
15407
|
+
url
|
|
15408
|
+
] }) : null
|
|
15409
|
+
]
|
|
15410
|
+
},
|
|
15411
|
+
page.form_id
|
|
15412
|
+
);
|
|
15261
15413
|
}) }),
|
|
15262
15414
|
pages.length > 0 && !levels.loading && levels.data && subjectIds.every((id) => levels.data?.[id] !== "admin") ? /* @__PURE__ */ jsx53("p", { className: "text-xs text-muted-foreground", children: NO_ADMIN2 }) : null
|
|
15263
15415
|
] });
|
|
@@ -16400,8 +16552,15 @@ var LANE_TITLE = {
|
|
|
16400
16552
|
};
|
|
16401
16553
|
var APP_TABLE_PREFIX = "records_ui_";
|
|
16402
16554
|
var LANE_EMPTY = {
|
|
16403
|
-
|
|
16404
|
-
|
|
16555
|
+
// 🚨 BOTH SENTENCES USED TO DESCRIBE A PRODUCT THAT DOES NOT EXIST
|
|
16556
|
+
// (VERIFIER-15 M2, VERIFIER-16 M6). "New table makes one that only you can
|
|
16557
|
+
// see" — New table makes one shared with the organization, and it lands under
|
|
16558
|
+
// My organization. "A table you make is yours until you change its visibility
|
|
16559
|
+
// to internal" — it is internal from the start, and no screen here changes a
|
|
16560
|
+
// table's visibility at all. An empty lane says what is true and names only a
|
|
16561
|
+
// control that exists.
|
|
16562
|
+
mine: "Nothing here is visible to you alone. A new table is shared with your organization from the start.",
|
|
16563
|
+
organization: "No tables shared across your organization yet. New table makes one, and everyone here can open it.",
|
|
16405
16564
|
system: "The platform's own tables are not in this organization's store.",
|
|
16406
16565
|
community: "Nothing shared beyond your organization. A table published by link or to the world lands here.",
|
|
16407
16566
|
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."
|
|
@@ -17046,7 +17205,7 @@ function TablePage({
|
|
|
17046
17205
|
}
|
|
17047
17206
|
) : null,
|
|
17048
17207
|
rail === "forms" ? /* @__PURE__ */ jsx61(FormsPanel, { tableId, activeFormId: itemFor("forms") }) : null,
|
|
17049
|
-
rail === "bookings" ? /* @__PURE__ */ jsx61(BookingSlots, { tableId }) : null,
|
|
17208
|
+
rail === "bookings" ? /* @__PURE__ */ jsx61(BookingSlots, { tableId, activeBookingId: itemFor("bookings") }) : null,
|
|
17050
17209
|
rail === "checklists" ? /* @__PURE__ */ jsx61(
|
|
17051
17210
|
ChecklistsPanel,
|
|
17052
17211
|
{
|
|
@@ -17117,6 +17276,7 @@ export {
|
|
|
17117
17276
|
ArchivedDisclosure,
|
|
17118
17277
|
ArchivedPortals,
|
|
17119
17278
|
ArchivedView,
|
|
17279
|
+
BOOKING_NOT_HERE_LINE,
|
|
17120
17280
|
BOOKING_PAGE_STATE_LABEL,
|
|
17121
17281
|
BOOKING_PAGE_STATE_VALUES,
|
|
17122
17282
|
BookingBuilder,
|
|
@@ -17333,6 +17493,7 @@ export {
|
|
|
17333
17493
|
mayNotRestoreLine,
|
|
17334
17494
|
memberName,
|
|
17335
17495
|
nextInWords,
|
|
17496
|
+
nextSummaryAt,
|
|
17336
17497
|
openingRail,
|
|
17337
17498
|
openingView,
|
|
17338
17499
|
pageViewFromParam,
|