@ai-matrx/records-ui 0.79.0 → 0.81.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 +62 -0
- package/dist/index.cjs +196 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +306 -170
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.81.0
|
|
4
|
+
|
|
5
|
+
**The relation picker stops cutting a customer's name in half, and a grid mount stops
|
|
6
|
+
screaming at the console.** (lane FIX-14, from FIX-13's "named, not fixed" list.)
|
|
7
|
+
|
|
8
|
+
**1. `Create “Marisol Okonkwo — 418 Calle Pu`.** The picker pasted a per-surface `w-64`
|
|
9
|
+
over the design system's popover, and the Create offer had no truncation of its own, so the
|
|
10
|
+
one control that exists to show the name you are about to create cut it mid-word. Fixed
|
|
11
|
+
where it belongs: `@ai-matrx/design-system` 0.35.0 gained `sizing="content"` and decides
|
|
12
|
+
the width; this package sets no width at all. The offer and every candidate row truncate
|
|
13
|
+
with a real ellipsis and carry the whole name as a `title`, and `RecordChip` does the same
|
|
14
|
+
everywhere it is drawn. Measured headlessly against the live store: 408px showing the whole
|
|
15
|
+
name at 1600×1000, 343px with a real `text-overflow: ellipsis` at 375 wide, inside the
|
|
16
|
+
viewport both times, 0 page errors.
|
|
17
|
+
|
|
18
|
+
**2. `custom.enrich_cells … does not exist` on every grid mount.** The DOOR was never
|
|
19
|
+
missing — `custom.enrich_cells(uuid, uuid, text[], uuid[])` is live, granted to
|
|
20
|
+
`authenticated`, and answers 320 rows for a real organization from the seat. `demo/branch-server.ts`
|
|
21
|
+
formatted every non-scalar argument as `'<json>'::jsonb`, because `typeof [] === "object"`,
|
|
22
|
+
so no door with a Postgres array parameter could ever be matched; that 42883 was recorded
|
|
23
|
+
into `demo/fixtures/doors.json` and replayed on every mount. The seam now asks `pg_proc`
|
|
24
|
+
what each parameter is declared as and formats by that — the class, not the instance — and
|
|
25
|
+
an array whose type it cannot read is named loudly instead of quietly re-sent as jsonb.
|
|
26
|
+
|
|
27
|
+
* New guard `demo/a-grid-mount-logs-no-console-error.test.tsx`: mounts `Grid` against the
|
|
28
|
+
LIVE store and fails on ANY `console.error`, with one named exclusion (React's jsdom-only
|
|
29
|
+
`act()` warning). RED on the pre-fix bytes with exactly the 42883 above, GREEN on these.
|
|
30
|
+
* The demo harness seeds a PLAIN relation column ("Related proposal", a self-relation on
|
|
31
|
+
Northbay Commercial Solar's proposals table) — until now every relation it seeded carried
|
|
32
|
+
`parity: "member"` and routed to `PersonPicker`, so `RelationPicker` had no browser
|
|
33
|
+
surface at all and its only coverage was jsdom, which has no geometry.
|
|
34
|
+
|
|
35
|
+
Suite: 51 files / 319 tests green, `tsc --noEmit` clean.
|
|
36
|
+
|
|
37
|
+
## 0.80.0
|
|
38
|
+
|
|
39
|
+
**An import's report outlives the run, and its refusals outlive the panel.** (lane FIX-12,
|
|
40
|
+
finding F2 from VERIFIER-12; published by PUBLISH-SWEEP-2, aidream `675cc61fe8`.)
|
|
41
|
+
|
|
42
|
+
VERIFIER-12 watched a 40-row file land 28 rows and refuse 12 against the live store and saw
|
|
43
|
+
NOTHING on screen: no summary, no refusal list, and afterwards nothing on any of that
|
|
44
|
+
table's screens naming the twelve rows. The store had been right the whole time — the
|
|
45
|
+
ledger said seen 40 / written 28 / refusals 12, and every refused row was kept with a
|
|
46
|
+
sentence a person understands. The report was not the defect. `importFinish` called
|
|
47
|
+
`onDone`, every host wires `onDone` to close the rail, and a run that fits in ONE call
|
|
48
|
+
finishes between two paints — so the report was destroyed in the tick it became true. A long
|
|
49
|
+
file only looked better because there was time to read the progress.
|
|
50
|
+
|
|
51
|
+
* The wizard tells the host `onWrote` when rows land; `onDone` is now the person pressing
|
|
52
|
+
**Done** on the report, and nothing else closes the panel.
|
|
53
|
+
* The report says where the refused rows now live.
|
|
54
|
+
* **"Imports into this table"** — every run this table has had, newest first, off
|
|
55
|
+
`custom.io_imports`; a run with refusals opens them off `custom.io_import_report`, with
|
|
56
|
+
the store's own reason and the line of the file. Both doors have existed since lane
|
|
57
|
+
IMPORT; no screen had ever asked.
|
|
58
|
+
* Guard: two clauses in `src/import-wizard.test.tsx`, mounted the way a HOST mounts the
|
|
59
|
+
wizard (closing on `onDone`). RED on the old bytes ("expected 'the import is closed' not
|
|
60
|
+
to contain 'the import is closed'"; the past run never on screen), GREEN on these.
|
|
61
|
+
|
|
62
|
+
Built against `@ai-matrx/records@0.54.0`. Suite at publish: 317/317 across 50 files,
|
|
63
|
+
including `demo/live-smoke.test.tsx`, this package's blocking live publish gate.
|
|
64
|
+
|
|
3
65
|
## 0.79.0
|
|
4
66
|
|
|
5
67
|
**A joined column says what joins it.** (lane STORE-TAILS-2, VERIFIER-11 finding F5.)
|
package/dist/index.cjs
CHANGED
|
@@ -1082,7 +1082,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1082
1082
|
)),
|
|
1083
1083
|
disabled ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.Popover, { open, onOpenChange: setOpen, children: [
|
|
1084
1084
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Button, { id, type: "button", size: "sm", variant: "outline", "aria-label": `Pick for ${fieldName(field)}`, children: picked.length === 0 ? "Pick" : "Change" }) }),
|
|
1085
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.PopoverContent, { className: "
|
|
1085
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.PopoverContent, { sizing: "content", className: "p-1", align: "start", children: [
|
|
1086
1086
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1087
1087
|
import_design_system2.BasicInput,
|
|
1088
1088
|
{
|
|
@@ -1106,6 +1106,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1106
1106
|
{
|
|
1107
1107
|
type: "button",
|
|
1108
1108
|
onClick: () => toggle(row.id),
|
|
1109
|
+
title: titleOf(row.id),
|
|
1109
1110
|
className: (0, import_design_system2.cn)(
|
|
1110
1111
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1111
1112
|
picked.includes(row.id) ? "bg-accent text-accent-foreground" : ""
|
|
@@ -1151,6 +1152,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1151
1152
|
type: "button",
|
|
1152
1153
|
size: "sm",
|
|
1153
1154
|
variant: "ghost",
|
|
1155
|
+
title: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}`,
|
|
1154
1156
|
className: "w-full justify-start text-xs",
|
|
1155
1157
|
disabled: busy,
|
|
1156
1158
|
onClick: () => {
|
|
@@ -1160,7 +1162,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1160
1162
|
setCreating(true);
|
|
1161
1163
|
}
|
|
1162
1164
|
},
|
|
1163
|
-
children: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}`
|
|
1165
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "block w-full truncate text-left", children: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}` })
|
|
1164
1166
|
}
|
|
1165
1167
|
),
|
|
1166
1168
|
createRefusal ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "p-2 text-xs text-destructive", children: createRefusal }) : null
|
|
@@ -1421,10 +1423,17 @@ function RecordChip({
|
|
|
1421
1423
|
onRemove,
|
|
1422
1424
|
className
|
|
1423
1425
|
}) {
|
|
1424
|
-
return
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1426
|
+
return (
|
|
1427
|
+
// A chip carries a RECORD'S OWN TITLE, which nobody here chose the length of —
|
|
1428
|
+
// "Marisol Okonkwo — 418 Calle Puerto Vallarta, Camarillo" is an ordinary customer
|
|
1429
|
+
// name. So it caps at its container and ends in an ellipsis with the whole name in
|
|
1430
|
+
// its tooltip, rather than being cut mid-word by whatever box it landed in
|
|
1431
|
+
// (lane FIX-14, 2026-09-22).
|
|
1432
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system3.Badge, { variant: "secondary", className: (0, import_design_system3.cn)("max-w-full gap-1 text-[11px]", className), title, children: [
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "min-w-0 truncate", children: title }),
|
|
1434
|
+
onRemove ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: onRemove, "aria-label": `Remove ${title}`, className: "shrink-0 opacity-60 hover:opacity-100", children: "\xD7" }) : null
|
|
1435
|
+
] })
|
|
1436
|
+
);
|
|
1428
1437
|
}
|
|
1429
1438
|
|
|
1430
1439
|
// src/PersonPicker.tsx
|
|
@@ -1570,7 +1579,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1570
1579
|
children: picked.length === 0 ? "Pick a person" : "Change"
|
|
1571
1580
|
}
|
|
1572
1581
|
) }),
|
|
1573
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system4.PopoverContent, { className: "
|
|
1582
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system4.PopoverContent, { sizing: "content", className: "p-1", align: "start", children: [
|
|
1574
1583
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1575
1584
|
import_design_system4.BasicInput,
|
|
1576
1585
|
{
|
|
@@ -3691,6 +3700,9 @@ function Grid({
|
|
|
3691
3700
|
[client, host, refreshEnrich]
|
|
3692
3701
|
);
|
|
3693
3702
|
const [pastePlan, setPastePlan] = (0, import_react20.useState)(null);
|
|
3703
|
+
const [deleting, setDeleting] = (0, import_react20.useState)(null);
|
|
3704
|
+
const [deleteBusy, setDeleteBusy] = (0, import_react20.useState)(false);
|
|
3705
|
+
const [archived, setArchived] = (0, import_react20.useState)(null);
|
|
3694
3706
|
const choiceOptions = (0, import_react20.useRef)(/* @__PURE__ */ new Map());
|
|
3695
3707
|
const loadChoiceOptions = (0, import_react20.useCallback)(
|
|
3696
3708
|
async (fieldList) => {
|
|
@@ -3978,6 +3990,21 @@ function Grid({
|
|
|
3978
3990
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ml-auto flex items-center gap-1", children: toolbarActions })
|
|
3979
3991
|
] }),
|
|
3980
3992
|
records.error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RefusalNotice, { error: records.error }) : null,
|
|
3993
|
+
archived ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
3994
|
+
"p",
|
|
3995
|
+
{
|
|
3996
|
+
"data-records-archived-notice": "",
|
|
3997
|
+
role: "status",
|
|
3998
|
+
className: "flex items-center gap-2 rounded border border-border bg-muted/40 px-2 py-1 text-xs",
|
|
3999
|
+
children: [
|
|
4000
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
4001
|
+
archived,
|
|
4002
|
+
" is archived. Nothing was destroyed \u2014 it is in this table\u2019s Archived items, where Restore brings it back."
|
|
4003
|
+
] }),
|
|
4004
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.Button, { size: "sm", variant: "ghost", className: "ml-auto h-6", onClick: () => setArchived(null), children: "Dismiss" })
|
|
4005
|
+
]
|
|
4006
|
+
}
|
|
4007
|
+
) : null,
|
|
3981
4008
|
editing.rowError ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3982
4009
|
RefusalNotice,
|
|
3983
4010
|
{
|
|
@@ -4094,11 +4121,13 @@ function Grid({
|
|
|
4094
4121
|
}
|
|
4095
4122
|
) : null,
|
|
4096
4123
|
canWrite && mayWriteRow(row.id) ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4097
|
-
|
|
4124
|
+
import_design_system10.Button,
|
|
4098
4125
|
{
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4126
|
+
size: "sm",
|
|
4127
|
+
variant: "ghost",
|
|
4128
|
+
title: "Delete this record",
|
|
4129
|
+
onClick: () => setDeleting(row),
|
|
4130
|
+
children: "Delete"
|
|
4102
4131
|
}
|
|
4103
4132
|
) : null
|
|
4104
4133
|
] })
|
|
@@ -4141,7 +4170,32 @@ function Grid({
|
|
|
4141
4170
|
onPage: setPage
|
|
4142
4171
|
}
|
|
4143
4172
|
)
|
|
4144
|
-
] })
|
|
4173
|
+
] }),
|
|
4174
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4175
|
+
import_design_system10.ConfirmDialog,
|
|
4176
|
+
{
|
|
4177
|
+
open: deleting !== null,
|
|
4178
|
+
onOpenChange: (next) => {
|
|
4179
|
+
if (!next && !deleteBusy) setDeleting(null);
|
|
4180
|
+
},
|
|
4181
|
+
variant: "destructive",
|
|
4182
|
+
title: `Delete ${deleting ? recordNameIn(table.data, deleting.document ?? {}) : "this record"}?`,
|
|
4183
|
+
description: table.data?.retention_days ? `It leaves the table and waits in Archived items. Nothing is destroyed: Restore brings it back, for ${table.data.retention_days} days.` : "It leaves the table and waits in Archived items. Nothing is destroyed: Restore brings it back.",
|
|
4184
|
+
confirmLabel: "Delete",
|
|
4185
|
+
cancelLabel: "Keep",
|
|
4186
|
+
busy: deleteBusy,
|
|
4187
|
+
onConfirm: async () => {
|
|
4188
|
+
const row = deleting;
|
|
4189
|
+
if (!row) return;
|
|
4190
|
+
const name = recordNameIn(table.data, row.document ?? {});
|
|
4191
|
+
setDeleteBusy(true);
|
|
4192
|
+
const failed = await editing.remove(row.id);
|
|
4193
|
+
setDeleteBusy(false);
|
|
4194
|
+
setDeleting(null);
|
|
4195
|
+
if (!failed) setArchived(name);
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
)
|
|
4145
4199
|
] }) });
|
|
4146
4200
|
}
|
|
4147
4201
|
function columnLabel(fields, columnId) {
|
|
@@ -4161,42 +4215,6 @@ function AddFieldButton({ onClick }) {
|
|
|
4161
4215
|
}
|
|
4162
4216
|
);
|
|
4163
4217
|
}
|
|
4164
|
-
function DeleteRecordButton({
|
|
4165
|
-
row,
|
|
4166
|
-
retentionDays,
|
|
4167
|
-
onDelete
|
|
4168
|
-
}) {
|
|
4169
|
-
const [asking, setAsking] = (0, import_react20.useState)(false);
|
|
4170
|
-
if (!asking) {
|
|
4171
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4172
|
-
import_design_system10.Button,
|
|
4173
|
-
{
|
|
4174
|
-
size: "sm",
|
|
4175
|
-
variant: "ghost",
|
|
4176
|
-
title: "Delete this record",
|
|
4177
|
-
onClick: () => setAsking(true),
|
|
4178
|
-
children: "Delete"
|
|
4179
|
-
}
|
|
4180
|
-
);
|
|
4181
|
-
}
|
|
4182
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1 text-[11px]", children: [
|
|
4183
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-muted-foreground", children: retentionDays ? `Removes it from the table. Restorable for ${retentionDays} days.` : "Removes it from the table. It can be restored from the record's history." }),
|
|
4184
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4185
|
-
import_design_system10.Button,
|
|
4186
|
-
{
|
|
4187
|
-
size: "sm",
|
|
4188
|
-
variant: "destructive",
|
|
4189
|
-
onClick: () => {
|
|
4190
|
-
setAsking(false);
|
|
4191
|
-
void onDelete();
|
|
4192
|
-
},
|
|
4193
|
-
children: "Delete"
|
|
4194
|
-
}
|
|
4195
|
-
),
|
|
4196
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.Button, { size: "sm", variant: "ghost", onClick: () => setAsking(false), children: "Keep" }),
|
|
4197
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "sr-only", children: row.id })
|
|
4198
|
-
] });
|
|
4199
|
-
}
|
|
4200
4218
|
function Pager({
|
|
4201
4219
|
page,
|
|
4202
4220
|
pageSize,
|
|
@@ -4495,7 +4513,7 @@ async function sha256(bytes) {
|
|
|
4495
4513
|
function typeWord(t) {
|
|
4496
4514
|
return fieldTypeChoice(t)?.label ?? (t === "relation" ? "Points at another table" : t);
|
|
4497
4515
|
}
|
|
4498
|
-
function ImportWizard({ tableId, onDone, className }) {
|
|
4516
|
+
function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
4499
4517
|
const client = (0, import_react27.useRecordsClient)();
|
|
4500
4518
|
const table = (0, import_react27.useTable)(tableId);
|
|
4501
4519
|
const fields = (0, import_react27.useFields)(tableId);
|
|
@@ -4521,6 +4539,46 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4521
4539
|
const [columnsAdded, setColumnsAdded] = (0, import_react26.useState)(null);
|
|
4522
4540
|
const [show, setShow] = (0, import_react26.useState)("all");
|
|
4523
4541
|
const [ledger, setLedger] = (0, import_react26.useState)(null);
|
|
4542
|
+
const [runs, setRuns] = (0, import_react26.useState)(null);
|
|
4543
|
+
const [openRun, setOpenRun] = (0, import_react26.useState)(null);
|
|
4544
|
+
const [runRows, setRunRows] = (0, import_react26.useState)(null);
|
|
4545
|
+
const [runProblem, setRunProblem] = (0, import_react26.useState)(null);
|
|
4546
|
+
const loadRuns = (0, import_react26.useCallback)(async () => {
|
|
4547
|
+
const answered = await client.imports({ table_id: tableId, limit: 10 });
|
|
4548
|
+
if (answered.ok) setRuns(answered.data);
|
|
4549
|
+
}, [client, tableId]);
|
|
4550
|
+
(0, import_react26.useEffect)(() => {
|
|
4551
|
+
void loadRuns();
|
|
4552
|
+
}, [loadRuns]);
|
|
4553
|
+
const openRefusals = (0, import_react26.useCallback)(
|
|
4554
|
+
async (importId) => {
|
|
4555
|
+
setRunProblem(null);
|
|
4556
|
+
if (openRun === importId) {
|
|
4557
|
+
setOpenRun(null);
|
|
4558
|
+
setRunRows(null);
|
|
4559
|
+
return;
|
|
4560
|
+
}
|
|
4561
|
+
setOpenRun(importId);
|
|
4562
|
+
setRunRows(null);
|
|
4563
|
+
const answered = await client.importReport({
|
|
4564
|
+
import_id: importId,
|
|
4565
|
+
state: "refused",
|
|
4566
|
+
limit: 200
|
|
4567
|
+
});
|
|
4568
|
+
if (!answered.ok) {
|
|
4569
|
+
setRunProblem(answered.error.message);
|
|
4570
|
+
return;
|
|
4571
|
+
}
|
|
4572
|
+
setRunRows({
|
|
4573
|
+
import_id: answered.data.import_id,
|
|
4574
|
+
rows: answered.data.rows,
|
|
4575
|
+
kept: answered.data.kept,
|
|
4576
|
+
total: answered.data.total,
|
|
4577
|
+
note: answered.data.note
|
|
4578
|
+
});
|
|
4579
|
+
},
|
|
4580
|
+
[client, openRun]
|
|
4581
|
+
);
|
|
4524
4582
|
const declared = fields.data ?? [];
|
|
4525
4583
|
const take = (0, import_react26.useCallback)(
|
|
4526
4584
|
async (file) => {
|
|
@@ -4654,8 +4712,9 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4654
4712
|
final: true
|
|
4655
4713
|
});
|
|
4656
4714
|
setPhase("done");
|
|
4657
|
-
|
|
4658
|
-
|
|
4715
|
+
onWrote?.(finished.data.rows_written);
|
|
4716
|
+
void loadRuns();
|
|
4717
|
+
}, [client, dedupeKey, fileBytes, fileHash, fileName, format, loadRuns, mapping, onDuplicate, onWrote, plan, rows, tableId, unmapped]);
|
|
4659
4718
|
const counts = (0, import_react26.useMemo)(() => {
|
|
4660
4719
|
let landed = 0;
|
|
4661
4720
|
let duplicate = 0;
|
|
@@ -4932,7 +4991,73 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4932
4991
|
proposals.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-0.5", children: proposals.map((p) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("li", { className: "text-muted-foreground", children: [
|
|
4933
4992
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium text-foreground", children: p.column }),
|
|
4934
4993
|
p.state === "waiting" ? " is waiting in the approvals inbox." : p.state === "accepted" ? " was added as a column." : p.state === "ignored" ? " was left out." : p.state === "refused" ? ` could not be added: ${p.reason ?? ""}` : ` \u2014 ${p.state}`
|
|
4935
|
-
] }, p.column)) }) : null
|
|
4994
|
+
] }, p.column)) }) : null,
|
|
4995
|
+
phase === "done" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
4996
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Button, { size: "sm", onClick: () => onDone?.(ledger?.written ?? 0), children: "Done" }),
|
|
4997
|
+
(ledger?.refused ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
4998
|
+
"The ",
|
|
4999
|
+
ledger?.refused,
|
|
5000
|
+
" row",
|
|
5001
|
+
ledger?.refused === 1 ? "" : "s",
|
|
5002
|
+
" that ",
|
|
5003
|
+
ledger?.refused === 1 ? "was" : "were",
|
|
5004
|
+
" refused",
|
|
5005
|
+
" ",
|
|
5006
|
+
"are kept with this import \u2014 open them again from the list of imports below, with the store's reason and the line from your file on each."
|
|
5007
|
+
] }) : null
|
|
5008
|
+
] }) : null
|
|
5009
|
+
] }) : null,
|
|
5010
|
+
runs && runs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
5011
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Separator, {}),
|
|
5012
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "font-medium", children: "Imports into this table" }),
|
|
5013
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-1", children: runs.map((r) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("li", { className: "flex flex-col gap-1", children: [
|
|
5014
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5015
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: r.source_name ?? "a file" }),
|
|
5016
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
5017
|
+
new Date(r.opened_at).toLocaleString(),
|
|
5018
|
+
" \xB7 ",
|
|
5019
|
+
r.rows_written,
|
|
5020
|
+
" landed \xB7",
|
|
5021
|
+
" ",
|
|
5022
|
+
r.rows_duplicate,
|
|
5023
|
+
" already here \xB7 ",
|
|
5024
|
+
r.rows_refused,
|
|
5025
|
+
" refused, of ",
|
|
5026
|
+
r.rows_seen,
|
|
5027
|
+
" row",
|
|
5028
|
+
r.rows_seen === 1 ? "" : "s",
|
|
5029
|
+
" offered"
|
|
5030
|
+
] }),
|
|
5031
|
+
r.rows_refused > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5032
|
+
"button",
|
|
5033
|
+
{
|
|
5034
|
+
type: "button",
|
|
5035
|
+
className: "underline underline-offset-2",
|
|
5036
|
+
"aria-expanded": openRun === r.import_id,
|
|
5037
|
+
onClick: () => void openRefusals(r.import_id),
|
|
5038
|
+
children: openRun === r.import_id ? "hide the rows it refused" : `see the ${r.rows_refused} row${r.rows_refused === 1 ? "" : "s"} it refused`
|
|
5039
|
+
}
|
|
5040
|
+
) : null
|
|
5041
|
+
] }),
|
|
5042
|
+
openRun === r.import_id ? runProblem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: runProblem }) : runRows ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
5043
|
+
runRows.note ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: runRows.note }) : null,
|
|
5044
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "max-h-64 overflow-auto rounded border", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("table", { className: "w-full", children: [
|
|
5045
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("thead", { className: "sticky top-0 bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { children: [
|
|
5046
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "Line in your file" }),
|
|
5047
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "What happened" }),
|
|
5048
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "The row in your file" })
|
|
5049
|
+
] }) }),
|
|
5050
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children: runRows.rows.map((row, i) => {
|
|
5051
|
+
const source = row.source ?? {};
|
|
5052
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-t align-top", children: [
|
|
5053
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: String(row.row ?? "") }),
|
|
5054
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1 text-destructive", children: String(row.reason ?? "") }),
|
|
5055
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "max-w-96 px-2 py-1 text-muted-foreground", children: Object.entries(source).map(([k, v]) => `${k}: ${String(v ?? "")}`).join(" \xB7 ") })
|
|
5056
|
+
] }, `${r.import_id}-${String(row.row ?? i)}`);
|
|
5057
|
+
}) })
|
|
5058
|
+
] }) })
|
|
5059
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Reading the rows this import refused\u2026" }) : null
|
|
5060
|
+
] }, r.import_id)) })
|
|
4936
5061
|
] }) : null,
|
|
4937
5062
|
table.error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalNotice, { error: table.error }) : null
|
|
4938
5063
|
] });
|
|
@@ -7392,8 +7517,8 @@ function PipelineBoard({
|
|
|
7392
7517
|
const asked = await client.pipelineTransitionRefusal({ record_id: recordId, to });
|
|
7393
7518
|
if (!alive.current) return;
|
|
7394
7519
|
if (!asked.ok) {
|
|
7395
|
-
|
|
7396
|
-
|
|
7520
|
+
setPending({ kind: "clash", recordId, to, error: asked.error });
|
|
7521
|
+
onMoved?.();
|
|
7397
7522
|
return;
|
|
7398
7523
|
}
|
|
7399
7524
|
const verdict = asked.data;
|
|
@@ -7406,8 +7531,8 @@ function PipelineBoard({
|
|
|
7406
7531
|
const moved = await client.pipelineMove({ record_id: recordId, to });
|
|
7407
7532
|
if (!alive.current) return;
|
|
7408
7533
|
if (!moved.ok) {
|
|
7409
|
-
setPending({ kind: "
|
|
7410
|
-
|
|
7534
|
+
setPending({ kind: "clash", recordId, to, error: moved.error });
|
|
7535
|
+
onMoved?.();
|
|
7411
7536
|
return;
|
|
7412
7537
|
}
|
|
7413
7538
|
const result = moved.data;
|
|
@@ -7428,21 +7553,22 @@ function PipelineBoard({
|
|
|
7428
7553
|
const moved = await client.pipelineMove({ record_id: recordId, to });
|
|
7429
7554
|
if (!alive.current) return;
|
|
7430
7555
|
if (!moved.ok) {
|
|
7431
|
-
setPending({ kind: "
|
|
7432
|
-
|
|
7556
|
+
setPending({ kind: "clash", recordId, to, error: moved.error });
|
|
7557
|
+
onMoved?.();
|
|
7433
7558
|
return;
|
|
7434
7559
|
}
|
|
7435
7560
|
setPending({ kind: "filed", recordId, to, result: moved.data });
|
|
7436
7561
|
void reload();
|
|
7437
7562
|
},
|
|
7438
|
-
[client, reload]
|
|
7563
|
+
[client, onMoved, reload]
|
|
7439
7564
|
);
|
|
7440
7565
|
const moveWith = (0, import_react47.useCallback)(
|
|
7441
7566
|
async (recordId, to, also) => {
|
|
7442
7567
|
const moved = await client.pipelineMove({ record_id: recordId, to, also });
|
|
7443
7568
|
if (!alive.current) return;
|
|
7444
7569
|
if (!moved.ok) {
|
|
7445
|
-
|
|
7570
|
+
setPending({ kind: "clash", recordId, to, error: moved.error });
|
|
7571
|
+
onMoved?.();
|
|
7446
7572
|
return;
|
|
7447
7573
|
}
|
|
7448
7574
|
setPending({ kind: "none" });
|
|
@@ -7570,6 +7696,16 @@ function Held({
|
|
|
7570
7696
|
if (pending.kind === "asking") {
|
|
7571
7697
|
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "px-2 py-1 text-xs text-muted-foreground", children: "Asking\u2026" });
|
|
7572
7698
|
}
|
|
7699
|
+
if (pending.kind === "clash") {
|
|
7700
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7701
|
+
RefusalNotice,
|
|
7702
|
+
{
|
|
7703
|
+
className: "mt-1",
|
|
7704
|
+
error: pending.error,
|
|
7705
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "mt-1", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_design_system23.Button, { size: "sm", variant: "ghost", className: "h-6", onClick: onCancel, children: "Close" }) })
|
|
7706
|
+
}
|
|
7707
|
+
);
|
|
7708
|
+
}
|
|
7573
7709
|
if (pending.kind === "refused") {
|
|
7574
7710
|
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "mt-1 rounded border border-destructive/50 bg-destructive/5 p-2 text-xs", children: [
|
|
7575
7711
|
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { children: pending.verdict.why }),
|