@ai-matrx/records-ui 0.68.0 → 0.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/dist/index.cjs +178 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -44
- package/dist/index.d.ts +85 -44
- package/dist/index.js +179 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.70.0
|
|
4
|
+
|
|
5
|
+
**The bar that said four now opens four.** (lane DRILL.)
|
|
6
|
+
|
|
7
|
+
* **`TablePage` takes a `filter`** — the store's one `RecordFilter` — and hands it to the grid
|
|
8
|
+
AND the board, so switching layout after a drill-through does not quietly widen back to the
|
|
9
|
+
whole table. It is a question for this visit, like `activeView` and `activeGroupField`, and
|
|
10
|
+
is never written onto the saved view. `Grid` and `ViewSwitcher` (and `useViewRecords`) take
|
|
11
|
+
it too; the narrowing happens in the STORE, never in the browser, because this screen holds
|
|
12
|
+
one page and a browser-side filter would drop every match on page two.
|
|
13
|
+
* **`cameFromLine` says the narrow thing, and means it.** TAILS-6 shipped *"This is every
|
|
14
|
+
record grouped into the same columns the chart used — not only the ones that number
|
|
15
|
+
counted"*, which was TRUE and useless: no door took a filter. It now reads *"These are only
|
|
16
|
+
the records that number counted: Status is Cancelled. Everything else in this table is being
|
|
17
|
+
held back"*, and it still refuses to claim narrowing when no question came with the click.
|
|
18
|
+
* **`filterInWords`** turns the question into the words of the person whose business it is —
|
|
19
|
+
the Field's own name through the same `fieldName` the column headers use, never a column
|
|
20
|
+
key. `null` becomes "Status is not filled in"; a window becomes "from … up to …", because
|
|
21
|
+
the store's windows are half-open and "to 2026-02-01" would read as including February 1st.
|
|
22
|
+
* **`DigestScheduler.filters` is a `RecordFilter`**, and `DashboardCanvas`'s
|
|
23
|
+
`as Record<string, unknown>` is gone — that cast was the seam where the dashboard's filter,
|
|
24
|
+
the saved view it schedules over and the drill-through it opens could have drifted apart.
|
|
25
|
+
* **Guard `a-number-opens-only-its-own-rows.test.tsx`** asserts the DOOR the page opened and
|
|
26
|
+
the argument it carried, and refuses by name the moment anything opens the unfiltered
|
|
27
|
+
`read_records` while holding a filter. Shown RED on the pre-fix bytes (four clauses,
|
|
28
|
+
including the old sentence) and green on these.
|
|
29
|
+
|
|
30
|
+
## 0.69.0
|
|
31
|
+
|
|
32
|
+
**She can make the one that is missing, from where she is looking for it.** (lane TAILS-7.)
|
|
33
|
+
|
|
34
|
+
* **The relation picker offers to create.** Rincon Plumbing's office opens a job for a customer
|
|
35
|
+
who has never been written down, and the picker's last word was *"Nothing in that table
|
|
36
|
+
matches."* — a dead end with nowhere to go: leave the job, find the Customers table, add a
|
|
37
|
+
row, come back, re-open the picker, search again. It now ends the list the way Airtable and
|
|
38
|
+
Notion do: type a name that matches nothing and the control reads **Create "Delfina
|
|
39
|
+
Arreola"**, one press; type nothing and it reads **New customer** and asks for a name. The
|
|
40
|
+
new record is written through `record_write` into the TARGET TABLE'S OWN `title_field` —
|
|
41
|
+
never a guessed key — and it comes back named by the store (`relation_words_many`), so a
|
|
42
|
+
column configured to show two of the target's columns shows both on the new chip too.
|
|
43
|
+
* **It is ONE control, so every relation surface inherited it at once**: the grid cell
|
|
44
|
+
(`gridEditing` → `FieldControl`), the generated record form, the record rail's edit mode and
|
|
45
|
+
the public form runner all draw `RelationPicker`. Nothing was forked per surface.
|
|
46
|
+
* **Except the one place creating would be wrong.** `Condition` — the filter builder — passes
|
|
47
|
+
`allowCreate={false}`: choosing a value to LOOK by is not a reason to put a customer in the
|
|
48
|
+
book. The guard asserts that absence, so a later "make it consistent" cannot spread it there
|
|
49
|
+
silently.
|
|
50
|
+
* **Absent or honest, never dead.** A target Table with no `title_field` cannot name a new
|
|
51
|
+
record, so the create says exactly that and names who fixes it, instead of writing a nameless
|
|
52
|
+
row. A refused write shows the store's own refusal in a person's words.
|
|
53
|
+
* Guard: `src/a-relation-can-make-the-one-that-is-missing.test.tsx`, in the blocking gate.
|
|
54
|
+
Shown RED against the pre-change picker — 4 of 5 clauses failing, the fifth being the filter
|
|
55
|
+
builder's absence, which must pass in both states — then GREEN. Package gate 251/251.
|
|
56
|
+
|
|
3
57
|
## 0.68.0
|
|
4
58
|
|
|
5
59
|
**Two things a screen was still doing behind a person's back.** (lane TAILS-6, found while
|
package/dist/index.cjs
CHANGED
|
@@ -919,22 +919,28 @@ var import_react5 = require("react");
|
|
|
919
919
|
var import_react6 = require("@ai-matrx/records/react");
|
|
920
920
|
var import_design_system2 = require("@ai-matrx/design-system");
|
|
921
921
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
922
|
-
function RelationPicker({ field, value, onChange, disabled, id, className }) {
|
|
922
|
+
function RelationPicker({ field, value, onChange, disabled, id, className, allowCreate = true }) {
|
|
923
923
|
const client = (0, import_react6.useRecordsClient)();
|
|
924
924
|
const [rows, setRows] = (0, import_react5.useState)(null);
|
|
925
925
|
const [words, setWords] = (0, import_react5.useState)({});
|
|
926
926
|
const [refusal, setRefusal] = (0, import_react5.useState)(null);
|
|
927
927
|
const [search, setSearch] = (0, import_react5.useState)("");
|
|
928
928
|
const [open, setOpen] = (0, import_react5.useState)(false);
|
|
929
|
+
const [target, setTarget] = (0, import_react5.useState)(null);
|
|
930
|
+
const [targetRefusal, setTargetRefusal] = (0, import_react5.useState)(null);
|
|
931
|
+
const [creating, setCreating] = (0, import_react5.useState)(false);
|
|
932
|
+
const [newTitle, setNewTitle] = (0, import_react5.useState)("");
|
|
933
|
+
const [busy, setBusy] = (0, import_react5.useState)(false);
|
|
934
|
+
const [createRefusal, setCreateRefusal] = (0, import_react5.useState)(null);
|
|
929
935
|
const picked = (0, import_react5.useMemo)(
|
|
930
936
|
() => Array.isArray(value) ? value.map(String) : value === null || value === void 0 ? [] : [String(value)],
|
|
931
937
|
[value]
|
|
932
938
|
);
|
|
933
939
|
(0, import_react5.useEffect)(() => {
|
|
934
|
-
const
|
|
935
|
-
if (!
|
|
940
|
+
const target2 = field.relation_target;
|
|
941
|
+
if (!target2) return;
|
|
936
942
|
let cancelled = false;
|
|
937
|
-
void client.list({ table_id:
|
|
943
|
+
void client.list({ table_id: target2, limit: 200 }).then((result) => {
|
|
938
944
|
if (cancelled) return;
|
|
939
945
|
if (result.ok) setRows(result.data.rows);
|
|
940
946
|
else setRefusal(refusalLineForAPerson(result.error));
|
|
@@ -963,6 +969,28 @@ function RelationPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
963
969
|
cancelled = true;
|
|
964
970
|
};
|
|
965
971
|
}, [client, field.id, rows, picked, words]);
|
|
972
|
+
(0, import_react5.useEffect)(() => {
|
|
973
|
+
if (!open || !allowCreate || target || targetRefusal) return;
|
|
974
|
+
const id2 = field.relation_target;
|
|
975
|
+
if (!id2) return;
|
|
976
|
+
let cancelled = false;
|
|
977
|
+
void client.tableList().then((result) => {
|
|
978
|
+
if (cancelled) return;
|
|
979
|
+
if (!result.ok) {
|
|
980
|
+
setTargetRefusal(refusalLineForAPerson(result.error));
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const found = result.data.find((t) => t.id === id2) ?? null;
|
|
984
|
+
if (found) setTarget(found);
|
|
985
|
+
else
|
|
986
|
+
setTargetRefusal(
|
|
987
|
+
"That Table is not one you can open, so a new record cannot be added to it from here."
|
|
988
|
+
);
|
|
989
|
+
});
|
|
990
|
+
return () => {
|
|
991
|
+
cancelled = true;
|
|
992
|
+
};
|
|
993
|
+
}, [client, open, allowCreate, target, targetRefusal, field.relation_target]);
|
|
966
994
|
if (!field.relation_target) {
|
|
967
995
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
968
996
|
fieldName(field),
|
|
@@ -982,6 +1010,39 @@ function RelationPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
982
1010
|
else if (!full) next.add(recordId);
|
|
983
1011
|
onChange(next.size === 0 ? null : [...next]);
|
|
984
1012
|
};
|
|
1013
|
+
const targetWord = (target?.label_singular ?? "").trim() || (target?.name ?? "").trim() || "record";
|
|
1014
|
+
const create = async (rawTitle) => {
|
|
1015
|
+
const title = rawTitle.trim();
|
|
1016
|
+
const tableId = field.relation_target;
|
|
1017
|
+
if (!tableId || title === "" || busy) return;
|
|
1018
|
+
if (!target) {
|
|
1019
|
+
setCreateRefusal("Still reading that Table. Try again in a moment.");
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const titleField = target.title_field;
|
|
1023
|
+
if (!titleField) {
|
|
1024
|
+
setCreateRefusal(
|
|
1025
|
+
`${target.name} has no column that names a record yet, so a new one would have nothing to show here. A table admin picks its title column in that table's settings.`
|
|
1026
|
+
);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
setBusy(true);
|
|
1030
|
+
setCreateRefusal(null);
|
|
1031
|
+
const written = await client.recordWrite({ table_id: tableId, data: { [titleField]: title } });
|
|
1032
|
+
if (!written.ok) {
|
|
1033
|
+
setBusy(false);
|
|
1034
|
+
setCreateRefusal(refusalLineForAPerson(written.error));
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const createdId = String(written.data);
|
|
1038
|
+
const refreshed = await client.list({ table_id: tableId, limit: 200 });
|
|
1039
|
+
if (refreshed.ok) setRows(refreshed.data.rows);
|
|
1040
|
+
setBusy(false);
|
|
1041
|
+
setCreating(false);
|
|
1042
|
+
setNewTitle("");
|
|
1043
|
+
setSearch("");
|
|
1044
|
+
toggle(createdId);
|
|
1045
|
+
};
|
|
985
1046
|
const visible = (rows ?? []).filter(
|
|
986
1047
|
(row) => search.trim() === "" ? true : titleOf(row.id).toLowerCase().includes(search.trim().toLowerCase())
|
|
987
1048
|
);
|
|
@@ -1028,7 +1089,57 @@ function RelationPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1028
1089
|
}
|
|
1029
1090
|
) }, row.id)) }),
|
|
1030
1091
|
rows && visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Nothing in that table matches." }) : null
|
|
1031
|
-
] })
|
|
1092
|
+
] }),
|
|
1093
|
+
allowCreate && !full ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mt-1 border-t pt-1", children: [
|
|
1094
|
+
targetRefusal ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "p-2 text-xs text-destructive", children: targetRefusal }) : !target ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading that table\u2026" }) : creating ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-1 p-1", children: [
|
|
1095
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1096
|
+
import_design_system2.BasicInput,
|
|
1097
|
+
{
|
|
1098
|
+
autoFocus: true,
|
|
1099
|
+
value: newTitle,
|
|
1100
|
+
onChange: (e) => setNewTitle(e.target.value),
|
|
1101
|
+
onKeyDown: (e) => {
|
|
1102
|
+
if (e.key === "Enter") {
|
|
1103
|
+
e.preventDefault();
|
|
1104
|
+
void create(newTitle);
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
placeholder: `New ${targetWord}`,
|
|
1108
|
+
className: "h-7 flex-1 text-xs",
|
|
1109
|
+
"aria-label": `Name the new ${targetWord}`
|
|
1110
|
+
}
|
|
1111
|
+
),
|
|
1112
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1113
|
+
import_design_system2.Button,
|
|
1114
|
+
{
|
|
1115
|
+
type: "button",
|
|
1116
|
+
size: "sm",
|
|
1117
|
+
variant: "default",
|
|
1118
|
+
disabled: busy || newTitle.trim() === "",
|
|
1119
|
+
onClick: () => void create(newTitle),
|
|
1120
|
+
children: busy ? "Creating\u2026" : "Create"
|
|
1121
|
+
}
|
|
1122
|
+
)
|
|
1123
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1124
|
+
import_design_system2.Button,
|
|
1125
|
+
{
|
|
1126
|
+
type: "button",
|
|
1127
|
+
size: "sm",
|
|
1128
|
+
variant: "ghost",
|
|
1129
|
+
className: "w-full justify-start text-xs",
|
|
1130
|
+
disabled: busy,
|
|
1131
|
+
onClick: () => {
|
|
1132
|
+
if (search.trim() !== "") void create(search);
|
|
1133
|
+
else {
|
|
1134
|
+
setNewTitle("");
|
|
1135
|
+
setCreating(true);
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
children: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}`
|
|
1139
|
+
}
|
|
1140
|
+
),
|
|
1141
|
+
createRefusal ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "p-2 text-xs text-destructive", children: createRefusal }) : null
|
|
1142
|
+
] }) : null
|
|
1032
1143
|
] })
|
|
1033
1144
|
] })
|
|
1034
1145
|
] });
|
|
@@ -3446,6 +3557,7 @@ var NO_COLUMNS_WHY = "A column is what a record holds \u2014 a name, a date, an
|
|
|
3446
3557
|
function Grid({
|
|
3447
3558
|
tableId,
|
|
3448
3559
|
pageSize = 50,
|
|
3560
|
+
filter,
|
|
3449
3561
|
onOpenRecord,
|
|
3450
3562
|
onAddField,
|
|
3451
3563
|
onNewRecordForm,
|
|
@@ -3461,7 +3573,7 @@ function Grid({
|
|
|
3461
3573
|
const table = (0, import_react21.useTable)(tableId);
|
|
3462
3574
|
const fields = (0, import_react21.useFields)(tableId);
|
|
3463
3575
|
const [page, setPage] = (0, import_react20.useState)(0);
|
|
3464
|
-
const records = (0, import_react21.useRecords)(tableId, { pageSize, page });
|
|
3576
|
+
const records = (0, import_react21.useRecords)(tableId, { pageSize, page, ...filter ? { filter } : {} });
|
|
3465
3577
|
const rights = useTableRights(table.data);
|
|
3466
3578
|
const rows = (0, import_react20.useMemo)(() => records.data?.rows ?? [], [records.data]);
|
|
3467
3579
|
const rowIds = (0, import_react20.useMemo)(() => rows.map((row) => row.id), [rows]);
|
|
@@ -5413,7 +5525,8 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5413
5525
|
value: value === "" ? null : value,
|
|
5414
5526
|
onChange: (next) => write({ value: next === null || next === void 0 ? "" : String(next) }),
|
|
5415
5527
|
id: "condition-value-record",
|
|
5416
|
-
className: "min-w-[8rem]"
|
|
5528
|
+
className: "min-w-[8rem]",
|
|
5529
|
+
allowCreate: false
|
|
5417
5530
|
}
|
|
5418
5531
|
)
|
|
5419
5532
|
) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
@@ -7209,9 +7322,12 @@ function unplacedName(record, field, labels) {
|
|
|
7209
7322
|
// src/ViewSwitcher.tsx
|
|
7210
7323
|
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
7211
7324
|
var NO_FIELDS = [];
|
|
7212
|
-
function useViewRecords(view, pageSize = 200) {
|
|
7325
|
+
function useViewRecords(view, pageSize = 200, filter) {
|
|
7213
7326
|
const client = (0, import_react49.useRecordsClient)();
|
|
7214
|
-
const table = (0, import_react49.useRecords)(view.ruleId ? null : view.subject, {
|
|
7327
|
+
const table = (0, import_react49.useRecords)(view.ruleId ? null : view.subject, {
|
|
7328
|
+
pageSize,
|
|
7329
|
+
...filter ? { filter } : {}
|
|
7330
|
+
});
|
|
7215
7331
|
const [ruled, setRuled] = (0, import_react48.useState)({
|
|
7216
7332
|
rows: [],
|
|
7217
7333
|
loading: Boolean(view.ruleId),
|
|
@@ -7266,6 +7382,7 @@ function ViewSwitcher({
|
|
|
7266
7382
|
onNewRecordForm,
|
|
7267
7383
|
onAskWhoChanged,
|
|
7268
7384
|
pageSize = 200,
|
|
7385
|
+
filter,
|
|
7269
7386
|
className
|
|
7270
7387
|
}) {
|
|
7271
7388
|
const [layout, setLayout] = (0, import_react48.useState)(view.layout);
|
|
@@ -7303,6 +7420,7 @@ function ViewSwitcher({
|
|
|
7303
7420
|
{
|
|
7304
7421
|
tableId: view.subject,
|
|
7305
7422
|
pageSize,
|
|
7423
|
+
...filter ? { filter } : {},
|
|
7306
7424
|
...local.presentation ?? view.presentation ? { presentation: local.presentation ?? view.presentation } : {},
|
|
7307
7425
|
...onViewChange ? { onPresentationChange: (next) => change({ presentation: next }) } : {},
|
|
7308
7426
|
...onOpenRecord ? { onOpenRecord } : {},
|
|
@@ -7314,6 +7432,7 @@ function ViewSwitcher({
|
|
|
7314
7432
|
{
|
|
7315
7433
|
view: { ...view, ...local, layout },
|
|
7316
7434
|
pageSize,
|
|
7435
|
+
...filter ? { filter } : {},
|
|
7317
7436
|
onChange: change,
|
|
7318
7437
|
remembered: Boolean(onViewChange),
|
|
7319
7438
|
...onOpenRecord ? { onOpenRecord } : {},
|
|
@@ -7361,13 +7480,14 @@ function boardColumnChoice(fields, chosenKey, stage) {
|
|
|
7361
7480
|
function Board({
|
|
7362
7481
|
view,
|
|
7363
7482
|
pageSize,
|
|
7483
|
+
filter,
|
|
7364
7484
|
onChange,
|
|
7365
7485
|
remembered,
|
|
7366
7486
|
onOpenRecord,
|
|
7367
7487
|
onMoved
|
|
7368
7488
|
}) {
|
|
7369
7489
|
const fields = (0, import_react49.useFields)(view.subject);
|
|
7370
|
-
const records = useViewRecords(view, pageSize);
|
|
7490
|
+
const records = useViewRecords(view, pageSize, filter);
|
|
7371
7491
|
const stage = useStageField(view.subject);
|
|
7372
7492
|
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(RefusalNotice, { error: fields.error });
|
|
7373
7493
|
if (records.error) return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(RefusalNotice, { error: records.error });
|
|
@@ -12888,14 +13008,20 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
12888
13008
|
rights.admin ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_design_system44.Button, { size: "sm", variant: "ghost", onClick: () => void remove(board), children: "Delete" }) : null
|
|
12889
13009
|
] }) : null
|
|
12890
13010
|
] }),
|
|
12891
|
-
board && scheduling ?
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
13011
|
+
board && scheduling ? (
|
|
13012
|
+
// ONE SHAPE, SO NO CAST. The dashboard's own filter, the saved view it
|
|
13013
|
+
// schedules over and the drill-through it opens are all the same
|
|
13014
|
+
// `RecordFilter`; the `as Record<string, unknown>` that used to be here
|
|
13015
|
+
// was the seam where they could have drifted.
|
|
13016
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
13017
|
+
DigestScheduler,
|
|
13018
|
+
{
|
|
13019
|
+
tableId,
|
|
13020
|
+
subjectName: board.name,
|
|
13021
|
+
...filter ? { filters: filter } : {},
|
|
13022
|
+
onClose: () => setScheduling(false)
|
|
13023
|
+
}
|
|
13024
|
+
)
|
|
12899
13025
|
) : null,
|
|
12900
13026
|
board ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
12901
13027
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
@@ -14895,9 +15021,35 @@ var import_react111 = require("react");
|
|
|
14895
15021
|
var import_react112 = require("@ai-matrx/records/react");
|
|
14896
15022
|
var import_design_system54 = require("@ai-matrx/design-system");
|
|
14897
15023
|
var import_jsx_runtime58 = require("react/jsx-runtime");
|
|
14898
|
-
function
|
|
14899
|
-
const
|
|
14900
|
-
|
|
15024
|
+
function filterInWords(filter, fields) {
|
|
15025
|
+
const nameOf = (key) => {
|
|
15026
|
+
const field = fields.find((f) => f.key === key);
|
|
15027
|
+
return field ? fieldName(field) : humanize(key);
|
|
15028
|
+
};
|
|
15029
|
+
const parts = Object.keys(filter).map((key) => {
|
|
15030
|
+
const value = filter[key];
|
|
15031
|
+
if (value === null || value === void 0) return `${nameOf(key)} is not filled in`;
|
|
15032
|
+
if (typeof value === "object") {
|
|
15033
|
+
const from = (value.from ?? "").toString().trim();
|
|
15034
|
+
const to = (value.to ?? "").toString().trim();
|
|
15035
|
+
if (from !== "" && to !== "") return `${nameOf(key)} from ${from} up to ${to}`;
|
|
15036
|
+
if (from !== "") return `${nameOf(key)} from ${from} onwards`;
|
|
15037
|
+
if (to !== "") return `${nameOf(key)} up to ${to}`;
|
|
15038
|
+
return `${nameOf(key)} in a period nobody named`;
|
|
15039
|
+
}
|
|
15040
|
+
return `${nameOf(key)} is ${String(value)}`;
|
|
15041
|
+
});
|
|
15042
|
+
if (parts.length === 0) return "";
|
|
15043
|
+
if (parts.length === 1) return parts[0];
|
|
15044
|
+
return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
|
|
15045
|
+
}
|
|
15046
|
+
function cameFromLine(label, groupField, question) {
|
|
15047
|
+
const asked = (question ?? "").trim();
|
|
15048
|
+
if (asked === "") {
|
|
15049
|
+
const grouped = groupField ? " grouped into the same columns the chart used" : "";
|
|
15050
|
+
return `You came here from \u201C${label}\u201D. This is every record in this table${grouped} \u2014 that number was counted over all of them.`;
|
|
15051
|
+
}
|
|
15052
|
+
return `You came here from \u201C${label}\u201D. These are only the records that number counted: ${asked}. Everything else in this table is being held back \u2014 clear the filter to see all of it.`;
|
|
14901
15053
|
}
|
|
14902
15054
|
var TABLE_NOT_REACHABLE = "This table is not in the organization you are working in, so there is nothing here to show. Switch to the organization that owns it and open it again \u2014 or it may have been deleted.";
|
|
14903
15055
|
var DEFAULT_VIEW_NAME = "All records";
|
|
@@ -14944,11 +15096,14 @@ function TablePage({
|
|
|
14944
15096
|
onViewChanged,
|
|
14945
15097
|
activeGroupField,
|
|
14946
15098
|
cameFrom,
|
|
15099
|
+
filter,
|
|
14947
15100
|
className
|
|
14948
15101
|
}) {
|
|
14949
15102
|
const client = (0, import_react112.useRecordsClient)();
|
|
14950
15103
|
const table = (0, import_react112.useTable)(tableId);
|
|
14951
15104
|
const rights = useTableRights(table.data);
|
|
15105
|
+
const pageFields = (0, import_react112.useFields)(filter ? tableId : null);
|
|
15106
|
+
const askedInWords = filter ? filterInWords(filter, pageFields.data ?? []) : null;
|
|
14952
15107
|
const organizationId = (0, import_react112.useRecordsClient)().config.organizationId;
|
|
14953
15108
|
const [view, setView] = (0, import_react111.useState)(null);
|
|
14954
15109
|
const opening = openingRail(activeRecordId);
|
|
@@ -15142,7 +15297,7 @@ function TablePage({
|
|
|
15142
15297
|
{
|
|
15143
15298
|
"data-testid": "came-from-a-number",
|
|
15144
15299
|
className: "rounded-md border px-3 py-2 text-xs leading-relaxed text-muted-foreground",
|
|
15145
|
-
children: cameFromLine(cameFrom, activeGroupField ?? null)
|
|
15300
|
+
children: cameFromLine(cameFrom, activeGroupField ?? null, askedInWords)
|
|
15146
15301
|
}
|
|
15147
15302
|
) : null,
|
|
15148
15303
|
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
@@ -15156,6 +15311,7 @@ function TablePage({
|
|
|
15156
15311
|
...activeGroupField ? { groupField: activeGroupField } : {}
|
|
15157
15312
|
},
|
|
15158
15313
|
pageSize,
|
|
15314
|
+
...filter ? { filter } : {},
|
|
15159
15315
|
onLayoutChange: (layout) => {
|
|
15160
15316
|
setLayoutFromLink(layout);
|
|
15161
15317
|
onViewChanged?.(layout);
|