@ai-matrx/records-ui 0.82.0 → 0.83.1
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 +37 -0
- package/dist/index.cjs +347 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -4
- package/dist/index.d.ts +73 -4
- package/dist/index.js +447 -160
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -189,6 +189,8 @@ __export(src_exports, {
|
|
|
189
189
|
VIEW_LAYOUTS: () => VIEW_LAYOUTS,
|
|
190
190
|
VIEW_NOT_SAVED_YET: () => VIEW_NOT_SAVED_YET,
|
|
191
191
|
VIEW_TABLE: () => VIEW_TABLE,
|
|
192
|
+
VISIBILITY_LANES: () => VISIBILITY_LANES,
|
|
193
|
+
VISIBILITY_LANE_TITLE: () => VISIBILITY_LANE_TITLE,
|
|
192
194
|
ViewBar: () => ViewBar,
|
|
193
195
|
ViewSwitcher: () => ViewSwitcher,
|
|
194
196
|
WITHHELD_RECORD_LABEL: () => WITHHELD_RECORD_LABEL,
|
|
@@ -244,6 +246,7 @@ __export(src_exports, {
|
|
|
244
246
|
isPlainFieldType: () => isPlainFieldType,
|
|
245
247
|
isRelationFieldType: () => isRelationFieldType,
|
|
246
248
|
isSignatureField: () => isSignatureField,
|
|
249
|
+
keptByTheApp: () => keptByTheApp,
|
|
247
250
|
keyFor: () => keyFor,
|
|
248
251
|
kindsWithNoChoice: () => kindsWithNoChoice,
|
|
249
252
|
laneFor: () => laneFor,
|
|
@@ -279,6 +282,7 @@ __export(src_exports, {
|
|
|
279
282
|
revokeConsequence: () => revokeConsequence,
|
|
280
283
|
rowName: () => rowName,
|
|
281
284
|
rowNameIn: () => rowNameIn,
|
|
285
|
+
saveViewPatch: () => saveViewPatch,
|
|
282
286
|
scalarText: () => scalarText,
|
|
283
287
|
shareUnavailableReason: () => shareUnavailableReason,
|
|
284
288
|
specFromBlock: () => specFromBlock,
|
|
@@ -308,6 +312,7 @@ __export(src_exports, {
|
|
|
308
312
|
viewDocument: () => viewDocument,
|
|
309
313
|
viewFromRecord: () => viewFromRecord,
|
|
310
314
|
viewPatchDocument: () => viewPatchDocument,
|
|
315
|
+
visibilityLaneFor: () => visibilityLaneFor,
|
|
311
316
|
whatIsMissing: () => whatIsMissing,
|
|
312
317
|
whatYouMayDo: () => whatYouMayDo,
|
|
313
318
|
whatYouMayDoWithTable: () => whatYouMayDoWithTable,
|
|
@@ -2383,7 +2388,7 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2383
2388
|
startedFrom.current = null;
|
|
2384
2389
|
}, []);
|
|
2385
2390
|
const writeCellNow = (0, import_react15.useCallback)(
|
|
2386
|
-
async (rowId, key, value) => {
|
|
2391
|
+
async (rowId, key, value, base, resent = false) => {
|
|
2387
2392
|
const id = cellKey(rowId, key);
|
|
2388
2393
|
setOptimistic((held) => ({ ...held, [id]: value }));
|
|
2389
2394
|
setSaving((held) => ({ ...held, [id]: true }));
|
|
@@ -2426,22 +2431,31 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2426
2431
|
delete next[id];
|
|
2427
2432
|
return next;
|
|
2428
2433
|
});
|
|
2429
|
-
|
|
2434
|
+
const conflict = (0, import_core.asWriteConflict)(written.error, rowId);
|
|
2435
|
+
if (written.error.code === "stale_write") {
|
|
2436
|
+
if (conflict) rememberVersion(versions.current, rowId, conflict.current_version);
|
|
2437
|
+
else versions.current.delete(rowId);
|
|
2438
|
+
if (conflict && !resent && sameValue(conflict.contested_fields[key], base)) {
|
|
2439
|
+
await writeCellNow(rowId, key, value, base, true);
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2430
2443
|
setRefusals((held) => ({
|
|
2431
2444
|
...held,
|
|
2432
2445
|
[id]: {
|
|
2433
2446
|
error: written.error,
|
|
2434
|
-
conflict
|
|
2447
|
+
conflict,
|
|
2435
2448
|
attempted: value
|
|
2436
2449
|
}
|
|
2437
2450
|
}));
|
|
2438
2451
|
},
|
|
2452
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2439
2453
|
[client, reload]
|
|
2440
2454
|
);
|
|
2441
2455
|
const writeCell = (0, import_react15.useCallback)(
|
|
2442
|
-
(rowId, key, value) => {
|
|
2456
|
+
(rowId, key, value, base) => {
|
|
2443
2457
|
const behind = queues.current.get(rowId) ?? Promise.resolve();
|
|
2444
|
-
const next = behind.then(() => writeCellNow(rowId, key, value));
|
|
2458
|
+
const next = behind.then(() => writeCellNow(rowId, key, value, base));
|
|
2445
2459
|
queues.current.set(
|
|
2446
2460
|
rowId,
|
|
2447
2461
|
next.then(
|
|
@@ -2459,7 +2473,7 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2459
2473
|
const open = editing;
|
|
2460
2474
|
if (!open) return;
|
|
2461
2475
|
const value = typed.current;
|
|
2462
|
-
if (!sameValue(startedFrom.current, value)) void writeCell(open.rowId, open.key, value);
|
|
2476
|
+
if (!sameValue(startedFrom.current, value)) void writeCell(open.rowId, open.key, value, startedFrom.current);
|
|
2463
2477
|
if (!move) {
|
|
2464
2478
|
setEditing(null);
|
|
2465
2479
|
setDraft(null);
|
|
@@ -2488,7 +2502,7 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2488
2502
|
(rowId, key) => {
|
|
2489
2503
|
const held = refusals[cellKey(rowId, key)];
|
|
2490
2504
|
if (!held) return;
|
|
2491
|
-
void writeCell(rowId, key, held.attempted);
|
|
2505
|
+
void writeCell(rowId, key, held.attempted, held.conflict?.contested_fields[key]);
|
|
2492
2506
|
},
|
|
2493
2507
|
[refusals, writeCell]
|
|
2494
2508
|
);
|
|
@@ -2510,6 +2524,33 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2510
2524
|
},
|
|
2511
2525
|
[reload]
|
|
2512
2526
|
);
|
|
2527
|
+
const keepEditing = (0, import_react15.useCallback)(
|
|
2528
|
+
(rowId, key) => {
|
|
2529
|
+
const held = refusals[cellKey(rowId, key)];
|
|
2530
|
+
if (!held || !writable(rowId)) return;
|
|
2531
|
+
startedFrom.current = shownValue(rowId, key);
|
|
2532
|
+
typed.current = held.attempted;
|
|
2533
|
+
setDraft(held.attempted);
|
|
2534
|
+
setEditing({ rowId, key });
|
|
2535
|
+
},
|
|
2536
|
+
[refusals, writable, shownValue]
|
|
2537
|
+
);
|
|
2538
|
+
const discard = (0, import_react15.useCallback)((rowId, key) => {
|
|
2539
|
+
const id = cellKey(rowId, key);
|
|
2540
|
+
setRefusals((held) => {
|
|
2541
|
+
if (!held[id]) return held;
|
|
2542
|
+
const next = { ...held };
|
|
2543
|
+
delete next[id];
|
|
2544
|
+
return next;
|
|
2545
|
+
});
|
|
2546
|
+
setOptimistic((held) => {
|
|
2547
|
+
if (!(id in held)) return held;
|
|
2548
|
+
const next = { ...held };
|
|
2549
|
+
delete next[id];
|
|
2550
|
+
return next;
|
|
2551
|
+
});
|
|
2552
|
+
setEditing((open) => open && open.rowId === rowId && open.key === key ? null : open);
|
|
2553
|
+
}, []);
|
|
2513
2554
|
const remove = (0, import_react15.useCallback)(
|
|
2514
2555
|
async (rowId) => {
|
|
2515
2556
|
setBusy(true);
|
|
@@ -2557,10 +2598,12 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2557
2598
|
commit,
|
|
2558
2599
|
setCell: (rowId, key, value) => {
|
|
2559
2600
|
if (!writable(rowId)) return;
|
|
2560
|
-
void writeCell(rowId, key, value);
|
|
2601
|
+
void writeCell(rowId, key, value, shownValue(rowId, key));
|
|
2561
2602
|
},
|
|
2562
2603
|
retry,
|
|
2563
2604
|
keepTheirs,
|
|
2605
|
+
keepEditing,
|
|
2606
|
+
discard,
|
|
2564
2607
|
remove,
|
|
2565
2608
|
addRecord,
|
|
2566
2609
|
rowError,
|
|
@@ -2629,13 +2672,28 @@ function GridCell({
|
|
|
2629
2672
|
draft: editing.draft,
|
|
2630
2673
|
onType: editing.type,
|
|
2631
2674
|
onCommit: (move) => editing.commit(move),
|
|
2632
|
-
onCancel: editing.cancel
|
|
2675
|
+
onCancel: editing.cancel,
|
|
2676
|
+
below: (
|
|
2677
|
+
// KEPT EDITING: the refusal stays beside the value it is about, so the
|
|
2678
|
+
// person reads what to change while changing it. Keep editing is the
|
|
2679
|
+
// state they are in, so only Discard is offered. It is INSIDE the open
|
|
2680
|
+
// cell, so pressing Discard is not "focus left the cell" — which would
|
|
2681
|
+
// commit the refused value one more time on its way out.
|
|
2682
|
+
refusal2 && !refusal2.conflict ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2683
|
+
RefusalNotice,
|
|
2684
|
+
{
|
|
2685
|
+
error: refusal2.error,
|
|
2686
|
+
className: "py-1.5 text-left",
|
|
2687
|
+
onDiscard: () => editing.discard(row.id, field.key)
|
|
2688
|
+
}
|
|
2689
|
+
) : null
|
|
2690
|
+
)
|
|
2633
2691
|
}
|
|
2634
2692
|
);
|
|
2635
2693
|
}
|
|
2636
2694
|
if (editorKindFor(field) === "checkbox") {
|
|
2637
2695
|
const answered = value === true || value === false;
|
|
2638
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { "data-matrx-cell-control": "", className: "flex w-full items-center gap-1.5", children: [
|
|
2696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { "data-matrx-cell-control": "", className: "flex w-full flex-wrap items-center gap-1.5", children: [
|
|
2639
2697
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2640
2698
|
import_design_system7.Checkbox,
|
|
2641
2699
|
{
|
|
@@ -2648,10 +2706,7 @@ function GridCell({
|
|
|
2648
2706
|
}
|
|
2649
2707
|
),
|
|
2650
2708
|
answered ? null : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-xs text-muted-foreground", title: "nobody has answered this yet", children: "\u2014" }),
|
|
2651
|
-
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime10.
|
|
2652
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RefusalLine, { error: refusal2.error }),
|
|
2653
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system7.Button, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" })
|
|
2654
|
-
] }) : null
|
|
2709
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CellRefusalNotice, { refusal: refusal2, field, row, editing, typed: false }) : null
|
|
2655
2710
|
] });
|
|
2656
2711
|
}
|
|
2657
2712
|
const openThisCell = (event) => {
|
|
@@ -2724,22 +2779,48 @@ function GridCell({
|
|
|
2724
2779
|
),
|
|
2725
2780
|
onAskWhoChanged ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WhoBadge, { field, row, onAskWhoChanged }) : null,
|
|
2726
2781
|
state === "saving" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "px-1 text-[10px] text-muted-foreground", children: "Saving\u2026" }) : null,
|
|
2727
|
-
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime10.
|
|
2728
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RefusalLine, { error: refusal2.error }),
|
|
2729
|
-
refusal2.conflict ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "text-[11px]", children: [
|
|
2730
|
-
"Theirs: ",
|
|
2731
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: formatTheirs(refusal2.conflict, field.key) })
|
|
2732
|
-
] }) : null,
|
|
2733
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex gap-1", children: [
|
|
2734
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system7.Button, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" }),
|
|
2735
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system7.Button, { size: "sm", variant: "ghost", onClick: () => editing?.keepTheirs(row.id, field.key), children: "Keep theirs" })
|
|
2736
|
-
] })
|
|
2737
|
-
] }) : null
|
|
2782
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CellRefusalNotice, { refusal: refusal2, field, row, editing, typed: true }) : null
|
|
2738
2783
|
]
|
|
2739
2784
|
}
|
|
2740
2785
|
)
|
|
2741
2786
|
);
|
|
2742
2787
|
}
|
|
2788
|
+
function CellRefusalNotice({
|
|
2789
|
+
refusal: refusal2,
|
|
2790
|
+
field,
|
|
2791
|
+
row,
|
|
2792
|
+
editing,
|
|
2793
|
+
typed
|
|
2794
|
+
}) {
|
|
2795
|
+
if (refusal2.conflict) {
|
|
2796
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2797
|
+
RefusalNotice,
|
|
2798
|
+
{
|
|
2799
|
+
error: refusal2.error,
|
|
2800
|
+
className: "py-1.5 text-left",
|
|
2801
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex flex-col gap-1", "data-refusal-conflict": "", children: [
|
|
2802
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "text-[11px]", children: [
|
|
2803
|
+
"Theirs: ",
|
|
2804
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: formatTheirs(refusal2.conflict, field.key) })
|
|
2805
|
+
] }),
|
|
2806
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex flex-wrap gap-1", children: [
|
|
2807
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system7.Button, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" }),
|
|
2808
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system7.Button, { size: "sm", variant: "ghost", onClick: () => editing?.keepTheirs(row.id, field.key), children: "Keep theirs" })
|
|
2809
|
+
] })
|
|
2810
|
+
] })
|
|
2811
|
+
}
|
|
2812
|
+
);
|
|
2813
|
+
}
|
|
2814
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2815
|
+
RefusalNotice,
|
|
2816
|
+
{
|
|
2817
|
+
error: refusal2.error,
|
|
2818
|
+
className: "py-1.5 text-left",
|
|
2819
|
+
...typed ? { onKeepEditing: () => editing?.keepEditing(row.id, field.key) } : {},
|
|
2820
|
+
onDiscard: () => editing?.discard(row.id, field.key)
|
|
2821
|
+
}
|
|
2822
|
+
);
|
|
2823
|
+
}
|
|
2743
2824
|
function WhoBadge({
|
|
2744
2825
|
field,
|
|
2745
2826
|
row,
|
|
@@ -2792,7 +2873,8 @@ function OpenCell({
|
|
|
2792
2873
|
draft,
|
|
2793
2874
|
onType,
|
|
2794
2875
|
onCommit,
|
|
2795
|
-
onCancel
|
|
2876
|
+
onCancel,
|
|
2877
|
+
below
|
|
2796
2878
|
}) {
|
|
2797
2879
|
const multiline = takesANewline(field);
|
|
2798
2880
|
const blurCommits = closesOnBlur(field);
|
|
@@ -2821,6 +2903,7 @@ function OpenCell({
|
|
|
2821
2903
|
className: "flex w-full min-w-[10rem] flex-col gap-1",
|
|
2822
2904
|
onDoubleClick: (event) => event.stopPropagation(),
|
|
2823
2905
|
onKeyDown: (event) => {
|
|
2906
|
+
if (event.target?.closest?.("[data-refusal-notice]")) return;
|
|
2824
2907
|
if (event.key === "Escape") {
|
|
2825
2908
|
event.preventDefault();
|
|
2826
2909
|
event.stopPropagation();
|
|
@@ -2846,7 +2929,8 @@ function OpenCell({
|
|
|
2846
2929
|
blurCommits ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-[10px] text-muted-foreground", children: multiline ? "\u23CE saves \xB7 \u21E7\u23CE new line \xB7 Esc cancels \xB7 Tab next" : "\u23CE saves \xB7 Esc cancels \xB7 Tab next" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex items-center gap-1", children: [
|
|
2847
2930
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system7.Button, { size: "sm", onClick: () => once(() => onCommit(null)), children: "Save" }),
|
|
2848
2931
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system7.Button, { size: "sm", variant: "ghost", onClick: () => once(onCancel), children: "Cancel" })
|
|
2849
|
-
] })
|
|
2932
|
+
] }),
|
|
2933
|
+
below
|
|
2850
2934
|
]
|
|
2851
2935
|
}
|
|
2852
2936
|
);
|
|
@@ -6812,8 +6896,8 @@ function TableSettings({
|
|
|
6812
6896
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "ghost", onClick: () => setAskingToRemove(null), children: "Keep it" })
|
|
6813
6897
|
] }) : null,
|
|
6814
6898
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("ul", { className: "divide-y rounded border", children: [
|
|
6815
|
-
rows.map((field, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-center gap-2 px-2 py-1.5", children: [
|
|
6816
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-
|
|
6899
|
+
rows.map((field, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { "data-settings-field": "", className: "flex flex-wrap items-center gap-x-2 gap-y-1 px-2 py-1.5", children: [
|
|
6900
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "data-settings-field-name": "", className: "min-w-[8rem] flex-1 break-words", children: fieldName(field) }),
|
|
6817
6901
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Badge, { variant: "outline", className: "text-[10px] font-normal", children: fieldTypeLabel(field) }),
|
|
6818
6902
|
field.required ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[10px] text-destructive", children: "required" }) : null,
|
|
6819
6903
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
@@ -6870,9 +6954,9 @@ function TableSettings({
|
|
|
6870
6954
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "font-medium", children: "Proposed" }),
|
|
6871
6955
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-muted-foreground", children: proposals?.length ?? 0 })
|
|
6872
6956
|
] }),
|
|
6873
|
-
proposals === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-muted-foreground", children: "Nothing is listing proposals here yet: the store has no `custom.field_propose` door, so this panel would be inventing an empty queue. When the door lands, its rows appear in this list with the same accept and reject." }) : proposals.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-muted-foreground", children: "No field is waiting for a decision." }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { className: "divide-y rounded border", children: proposals.map((proposal) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-center gap-2 px-2 py-1.5", children: [
|
|
6874
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-
|
|
6875
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-
|
|
6957
|
+
proposals === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-muted-foreground", children: "Nothing is listing proposals here yet: the store has no `custom.field_propose` door, so this panel would be inventing an empty queue. When the door lands, its rows appear in this list with the same accept and reject." }) : proposals.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-muted-foreground", children: "No field is waiting for a decision." }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { className: "divide-y rounded border", children: proposals.map((proposal) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex flex-wrap items-center gap-x-2 gap-y-1 px-2 py-1.5", children: [
|
|
6958
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-[8rem] flex-1 break-words", children: proposal.field.label || proposal.field.key }),
|
|
6959
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-[8rem] flex-1 break-words text-muted-foreground", children: proposal.why }),
|
|
6876
6960
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Badge, { variant: "secondary", className: "text-[10px] font-normal", children: proposal.proposed_by }),
|
|
6877
6961
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "outline", onClick: () => onAcceptProposal?.(proposal), children: "Accept" }),
|
|
6878
6962
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "ghost", onClick: () => onRejectProposal?.(proposal), children: "Reject" })
|
|
@@ -7047,7 +7131,39 @@ var import_design_system22 = require("@ai-matrx/design-system");
|
|
|
7047
7131
|
// src/RecordForm.tsx
|
|
7048
7132
|
var import_react41 = require("react");
|
|
7049
7133
|
var import_react42 = require("@ai-matrx/records/react");
|
|
7134
|
+
var import_core7 = require("@ai-matrx/records/core");
|
|
7135
|
+
|
|
7136
|
+
// src/writeOnTheWinningVersion.ts
|
|
7050
7137
|
var import_core6 = require("@ai-matrx/records/core");
|
|
7138
|
+
function sameStoredValue(left, right) {
|
|
7139
|
+
if (left === right) return true;
|
|
7140
|
+
const empty = (v) => v === null || v === void 0 || v === "";
|
|
7141
|
+
if (empty(left) || empty(right)) return empty(left) && empty(right);
|
|
7142
|
+
if (typeof left === "object" || typeof right === "object") {
|
|
7143
|
+
try {
|
|
7144
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
7145
|
+
} catch {
|
|
7146
|
+
return false;
|
|
7147
|
+
}
|
|
7148
|
+
}
|
|
7149
|
+
return false;
|
|
7150
|
+
}
|
|
7151
|
+
async function updateOnTheWinningVersion(client, recordId, patch, expectedVersion, base) {
|
|
7152
|
+
const send = (expected) => client.recordUpdate({
|
|
7153
|
+
record_id: recordId,
|
|
7154
|
+
patch,
|
|
7155
|
+
...expected === null ? {} : { expectedVersion: expected }
|
|
7156
|
+
});
|
|
7157
|
+
const first = await send(expectedVersion);
|
|
7158
|
+
if (first.ok) return first;
|
|
7159
|
+
const conflict = (0, import_core6.asWriteConflict)(first.error, recordId);
|
|
7160
|
+
if (!conflict) return first;
|
|
7161
|
+
const untouched = base === "always" || Object.keys(patch).every((key) => sameStoredValue(conflict.contested_fields[key], base[key]));
|
|
7162
|
+
if (!untouched) return first;
|
|
7163
|
+
return send(conflict.current_version);
|
|
7164
|
+
}
|
|
7165
|
+
|
|
7166
|
+
// src/RecordForm.tsx
|
|
7051
7167
|
var import_design_system20 = require("@ai-matrx/design-system");
|
|
7052
7168
|
|
|
7053
7169
|
// src/systemTable.ts
|
|
@@ -7235,13 +7351,38 @@ function RecordForm({
|
|
|
7235
7351
|
const fields = (0, import_react42.useFields)(tableId, recordType);
|
|
7236
7352
|
const existing = (0, import_react42.useRecord)(recordId ?? null);
|
|
7237
7353
|
const mutation = (0, import_react42.useRecordMutation)();
|
|
7354
|
+
const client = (0, import_react42.useRecordsClient)();
|
|
7355
|
+
const [edit, setEdit] = (0, import_react41.useState)({
|
|
7356
|
+
saving: false,
|
|
7357
|
+
error: null,
|
|
7358
|
+
conflict: null
|
|
7359
|
+
});
|
|
7360
|
+
const said = {
|
|
7361
|
+
saving: mutation.saving || edit.saving,
|
|
7362
|
+
error: recordId ? edit.error : mutation.error,
|
|
7363
|
+
conflict: recordId ? edit.conflict : mutation.conflict,
|
|
7364
|
+
clearConflict: () => {
|
|
7365
|
+
mutation.clearConflict();
|
|
7366
|
+
setEdit((e) => ({ ...e, conflict: null }));
|
|
7367
|
+
}
|
|
7368
|
+
};
|
|
7238
7369
|
const [draft, setDraft] = (0, import_react41.useState)({});
|
|
7239
7370
|
const [touched, setTouched] = (0, import_react41.useState)(false);
|
|
7371
|
+
const [changed, setChanged] = (0, import_react41.useState)({});
|
|
7372
|
+
const [answered, setAnswered] = (0, import_react41.useState)(null);
|
|
7240
7373
|
const loaded = useRecordVersion(recordId ?? null);
|
|
7241
7374
|
const loadedVersion = loaded.version;
|
|
7375
|
+
const changedRef = (0, import_react41.useRef)({});
|
|
7376
|
+
changedRef.current = changed;
|
|
7242
7377
|
(0, import_react41.useEffect)(() => {
|
|
7243
7378
|
const document3 = existing.data?.document;
|
|
7244
|
-
if (document3)
|
|
7379
|
+
if (!document3) return;
|
|
7380
|
+
const mine = changedRef.current;
|
|
7381
|
+
setDraft((held) => {
|
|
7382
|
+
const next = { ...document3 };
|
|
7383
|
+
for (const key of Object.keys(mine)) next[key] = held[key];
|
|
7384
|
+
return next;
|
|
7385
|
+
});
|
|
7245
7386
|
}, [existing.data?.record_id, loadedVersion]);
|
|
7246
7387
|
const document2 = (0, import_react41.useMemo)(() => {
|
|
7247
7388
|
const out = {};
|
|
@@ -7252,22 +7393,61 @@ function RecordForm({
|
|
|
7252
7393
|
return out;
|
|
7253
7394
|
}, [draft]);
|
|
7254
7395
|
const predicted = (0, import_react41.useMemo)(
|
|
7255
|
-
() => fields.data ? (0,
|
|
7396
|
+
() => fields.data ? (0, import_core7.predictWriteRefusals)({ fields: fields.data, document: document2, ...recordType ? { recordType } : {} }) : [],
|
|
7256
7397
|
[fields.data, document2, recordType]
|
|
7257
7398
|
);
|
|
7258
7399
|
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(RefusalNotice, { error: fields.error, className });
|
|
7259
7400
|
if (existing.error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(RefusalNotice, { error: existing.error, className });
|
|
7401
|
+
const storeSaid = said.error && said.error !== answered ? said.error : null;
|
|
7402
|
+
const fieldProblems = predicted.filter((p) => p.field_key);
|
|
7403
|
+
const problemsShown = (key) => touched || changed[key] ? fieldProblems.filter((p) => p.field_key === key) : [];
|
|
7404
|
+
const storeRefusalIsOnAField = storeSaid !== null && !said.conflict && storeSaid.code === "refused_by_rule" && (fields.data ?? []).some((f) => problemsShown(f.key).length > 0);
|
|
7405
|
+
const stored = existing.data?.document;
|
|
7406
|
+
const focusField = (field) => {
|
|
7407
|
+
if (!field) return;
|
|
7408
|
+
globalThis.document?.getElementById(`field-${field.id}`)?.focus();
|
|
7409
|
+
};
|
|
7410
|
+
const keepEditing = (field) => {
|
|
7411
|
+
if (storeSaid) setAnswered(storeSaid);
|
|
7412
|
+
const first = field ?? (fields.data ?? []).find((f) => problemsShown(f.key).length > 0) ?? (fields.data ?? [])[0];
|
|
7413
|
+
focusField(first);
|
|
7414
|
+
};
|
|
7415
|
+
const discard = (field) => {
|
|
7416
|
+
if (storeSaid) setAnswered(storeSaid);
|
|
7417
|
+
if (field) {
|
|
7418
|
+
setDraft((d) => ({ ...d, [field.key]: stored ? stored[field.key] : void 0 }));
|
|
7419
|
+
setChanged((c) => {
|
|
7420
|
+
const next = { ...c };
|
|
7421
|
+
delete next[field.key];
|
|
7422
|
+
return next;
|
|
7423
|
+
});
|
|
7424
|
+
return;
|
|
7425
|
+
}
|
|
7426
|
+
setDraft(stored ? { ...stored } : {});
|
|
7427
|
+
setChanged({});
|
|
7428
|
+
setTouched(false);
|
|
7429
|
+
};
|
|
7260
7430
|
const save = async () => {
|
|
7261
7431
|
setTouched(true);
|
|
7432
|
+
setAnswered(null);
|
|
7262
7433
|
if (recordId) {
|
|
7263
7434
|
const version = loadedVersion;
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7435
|
+
if (Object.keys(changed).length === 0) {
|
|
7436
|
+
onSaved?.(recordId);
|
|
7437
|
+
return;
|
|
7438
|
+
}
|
|
7439
|
+
const patch = Object.fromEntries(Object.entries(document2).filter(([key]) => changed[key]));
|
|
7440
|
+
setEdit({ saving: true, error: null, conflict: null });
|
|
7441
|
+
const answered2 = await updateOnTheWinningVersion(client, recordId, patch, version, stored ?? {});
|
|
7442
|
+
setEdit({
|
|
7443
|
+
saving: false,
|
|
7444
|
+
error: answered2.ok ? null : answered2.error,
|
|
7445
|
+
conflict: answered2.ok ? null : (0, import_core7.asWriteConflict)(answered2.error, recordId)
|
|
7268
7446
|
});
|
|
7447
|
+
const moved = answered2.ok ? answered2.data : null;
|
|
7269
7448
|
if (moved !== null) {
|
|
7270
7449
|
loaded.note(moved);
|
|
7450
|
+
setChanged({});
|
|
7271
7451
|
host.notify?.success("Saved");
|
|
7272
7452
|
existing.reload();
|
|
7273
7453
|
onSaved?.(recordId);
|
|
@@ -7295,8 +7475,13 @@ function RecordForm({
|
|
|
7295
7475
|
field,
|
|
7296
7476
|
value: draft[field.key],
|
|
7297
7477
|
document: existing.data?.document,
|
|
7298
|
-
problems:
|
|
7299
|
-
onChange: (value) =>
|
|
7478
|
+
problems: problemsShown(field.key),
|
|
7479
|
+
onChange: (value) => {
|
|
7480
|
+
setDraft((d) => ({ ...d, [field.key]: value }));
|
|
7481
|
+
setChanged((c) => c[field.key] ? c : { ...c, [field.key]: true });
|
|
7482
|
+
},
|
|
7483
|
+
onKeepEditing: () => keepEditing(field),
|
|
7484
|
+
onDiscard: () => discard(field)
|
|
7300
7485
|
},
|
|
7301
7486
|
field.id
|
|
7302
7487
|
)) }),
|
|
@@ -7307,13 +7492,13 @@ function RecordForm({
|
|
|
7307
7492
|
},
|
|
7308
7493
|
p.message
|
|
7309
7494
|
)) : null,
|
|
7310
|
-
|
|
7495
|
+
storeSaid && !storeRefusalIsOnAField ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7311
7496
|
RefusalNotice,
|
|
7312
7497
|
{
|
|
7313
|
-
error:
|
|
7314
|
-
...
|
|
7498
|
+
error: storeSaid,
|
|
7499
|
+
...said.conflict ? {
|
|
7315
7500
|
actions: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "mt-1 space-y-1", children: [
|
|
7316
|
-
Object.entries(
|
|
7501
|
+
Object.entries(said.conflict.contested_fields).map(([key, theirs]) => {
|
|
7317
7502
|
const column = (fields.data ?? []).find((f) => f.key === key);
|
|
7318
7503
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("p", { className: "text-[11px]", children: [
|
|
7319
7504
|
column ? fieldName(column) : humanize(key),
|
|
@@ -7331,19 +7516,21 @@ function RecordForm({
|
|
|
7331
7516
|
size: "sm",
|
|
7332
7517
|
variant: "outline",
|
|
7333
7518
|
onClick: () => {
|
|
7519
|
+
if (said.conflict) loaded.note(said.conflict.current_version);
|
|
7334
7520
|
existing.reload();
|
|
7335
|
-
|
|
7521
|
+
said.clearConflict();
|
|
7522
|
+
setAnswered(storeSaid);
|
|
7336
7523
|
},
|
|
7337
7524
|
children: "Take theirs and start again"
|
|
7338
7525
|
}
|
|
7339
7526
|
)
|
|
7340
7527
|
] })
|
|
7341
|
-
} : {}
|
|
7528
|
+
} : { onKeepEditing: () => keepEditing(), onDiscard: () => discard() }
|
|
7342
7529
|
}
|
|
7343
7530
|
) : null,
|
|
7344
7531
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_design_system20.Separator, {}),
|
|
7345
7532
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
7346
|
-
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_design_system20.Button, { type: "submit", size: "sm", disabled:
|
|
7533
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_design_system20.Button, { type: "submit", size: "sm", disabled: said.saving, children: said.saving ? "Saving\u2026" : recordId ? "Save" : "Create" }),
|
|
7347
7534
|
onCancel ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_design_system20.Button, { type: "button", size: "sm", variant: "ghost", onClick: onCancel, children: "Cancel" }) : null
|
|
7348
7535
|
] })
|
|
7349
7536
|
]
|
|
@@ -7355,17 +7542,20 @@ function FieldRow({
|
|
|
7355
7542
|
value,
|
|
7356
7543
|
document: document2,
|
|
7357
7544
|
problems,
|
|
7358
|
-
onChange
|
|
7545
|
+
onChange,
|
|
7546
|
+
onKeepEditing,
|
|
7547
|
+
onDiscard
|
|
7359
7548
|
}) {
|
|
7360
7549
|
const id = `field-${field.id}`;
|
|
7361
7550
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
7362
7551
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldLabel, { field, htmlFor: id, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ProvenanceBadge, { document: document2, fieldKey: field.key }) }),
|
|
7363
7552
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldControl, { field, value, onChange, id }),
|
|
7364
|
-
problems.map((problem) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7553
|
+
problems.map((problem, index) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7365
7554
|
RefusalNotice,
|
|
7366
7555
|
{
|
|
7367
7556
|
error: { code: "refused_by_rule", message: problem.message },
|
|
7368
|
-
className: "border-0 p-0"
|
|
7557
|
+
className: "border-0 p-0",
|
|
7558
|
+
...index === 0 ? { onKeepEditing, onDiscard } : {}
|
|
7369
7559
|
},
|
|
7370
7560
|
problem.message
|
|
7371
7561
|
))
|
|
@@ -7658,6 +7848,9 @@ function viewFromRecord(row) {
|
|
|
7658
7848
|
presentation: parseGridPresentation(data["presentation"])
|
|
7659
7849
|
};
|
|
7660
7850
|
}
|
|
7851
|
+
async function saveViewPatch(client, viewId, document2, expectedVersion) {
|
|
7852
|
+
return updateOnTheWinningVersion(client, viewId, document2, expectedVersion, "always");
|
|
7853
|
+
}
|
|
7661
7854
|
function viewPatchDocument(patch) {
|
|
7662
7855
|
const out = {};
|
|
7663
7856
|
if (patch.name !== void 0) out["name"] = patch.name;
|
|
@@ -12053,6 +12246,21 @@ function whenItFires(subscription) {
|
|
|
12053
12246
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
12054
12247
|
return subscription.schedule ? `${every}, ${subscription.schedule}` : every;
|
|
12055
12248
|
}
|
|
12249
|
+
var PERIOD_MS = {
|
|
12250
|
+
hourly: 60 * 60 * 1e3,
|
|
12251
|
+
daily: 24 * 60 * 60 * 1e3,
|
|
12252
|
+
weekly: 7 * 24 * 60 * 60 * 1e3
|
|
12253
|
+
};
|
|
12254
|
+
function nextSummaryAt(subscription, now) {
|
|
12255
|
+
if (!subscription.next_digest_at) return null;
|
|
12256
|
+
const due = new Date(subscription.next_digest_at);
|
|
12257
|
+
if (Number.isNaN(due.getTime())) return null;
|
|
12258
|
+
if (due.getTime() > now.getTime()) return due;
|
|
12259
|
+
const period = PERIOD_MS[subscription.cadence];
|
|
12260
|
+
if (!period) return due;
|
|
12261
|
+
const behind = now.getTime() - due.getTime();
|
|
12262
|
+
return new Date(due.getTime() + (Math.floor(behind / period) + 1) * period);
|
|
12263
|
+
}
|
|
12056
12264
|
var CHANNEL_WORDS2 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
12057
12265
|
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
12058
12266
|
function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
@@ -12180,7 +12388,7 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
|
12180
12388
|
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
12181
12389
|
subscription.next_digest_at ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
12182
12390
|
"Next summary ",
|
|
12183
|
-
new Date(
|
|
12391
|
+
nextSummaryAt(subscription, /* @__PURE__ */ new Date())?.toLocaleString()
|
|
12184
12392
|
] }) : subscription.cadence === "instant" ? null : subscription.muted ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "No next summary while it is off." }) : null,
|
|
12185
12393
|
subscription.last_sent_at ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
12186
12394
|
"Last told you ",
|
|
@@ -13876,14 +14084,14 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13876
14084
|
var import_react95 = require("react");
|
|
13877
14085
|
var import_recharts = require("recharts");
|
|
13878
14086
|
var import_records11 = require("@ai-matrx/records");
|
|
13879
|
-
var
|
|
14087
|
+
var import_core8 = require("@ai-matrx/records/core");
|
|
13880
14088
|
var import_design_system46 = require("@ai-matrx/design-system");
|
|
13881
14089
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
13882
14090
|
function pretty(n) {
|
|
13883
14091
|
return Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { maximumFractionDigits: 2 });
|
|
13884
14092
|
}
|
|
13885
14093
|
function asRefusal2(block) {
|
|
13886
|
-
return (0,
|
|
14094
|
+
return (0, import_core8.mapPgError)(
|
|
13887
14095
|
{
|
|
13888
14096
|
message: block.refused ?? "",
|
|
13889
14097
|
...block.sqlstate ? { code: block.sqlstate } : {}
|
|
@@ -16330,13 +16538,30 @@ var LANE_EMPTY = {
|
|
|
16330
16538
|
community: "Nothing shared beyond your organization. A table published by link or to the world lands here.",
|
|
16331
16539
|
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."
|
|
16332
16540
|
};
|
|
16541
|
+
function keptByTheApp(table) {
|
|
16542
|
+
return table.kept_by_the_app === true;
|
|
16543
|
+
}
|
|
16333
16544
|
function laneFor(table) {
|
|
16334
16545
|
if (table.is_kernel) return "system";
|
|
16335
|
-
if ((table.slug ?? "").startsWith(APP_TABLE_PREFIX)) return "app";
|
|
16546
|
+
if ((table.slug ?? "").startsWith(APP_TABLE_PREFIX) || keptByTheApp(table)) return "app";
|
|
16336
16547
|
if (table.visibility === "public" || table.visibility === "link") return "community";
|
|
16337
16548
|
if (table.visibility === "personal") return "mine";
|
|
16338
16549
|
return "organization";
|
|
16339
16550
|
}
|
|
16551
|
+
var VISIBILITY_LANES = ["mine", "organization", "community", "world"];
|
|
16552
|
+
var VISIBILITY_LANE_TITLE = {
|
|
16553
|
+
mine: "Mine",
|
|
16554
|
+
organization: "My organization",
|
|
16555
|
+
community: "Community",
|
|
16556
|
+
world: "World"
|
|
16557
|
+
};
|
|
16558
|
+
function visibilityLaneFor(table) {
|
|
16559
|
+
if (table.is_kernel) return null;
|
|
16560
|
+
if ((table.slug ?? "").startsWith(APP_TABLE_PREFIX) || keptByTheApp(table)) return null;
|
|
16561
|
+
if (table.visibility === "personal") return "mine";
|
|
16562
|
+
if (table.visibility === "link" || table.visibility === "public") return "world";
|
|
16563
|
+
return "organization";
|
|
16564
|
+
}
|
|
16340
16565
|
function TablesHome({ onOpenTable, className }) {
|
|
16341
16566
|
const client = (0, import_react117.useRecordsClient)();
|
|
16342
16567
|
const tables = (0, import_react117.useTables)();
|
|
@@ -16484,6 +16709,43 @@ var import_react118 = require("react");
|
|
|
16484
16709
|
var import_react119 = require("@ai-matrx/records/react");
|
|
16485
16710
|
var import_design_system57 = require("@ai-matrx/design-system");
|
|
16486
16711
|
var import_jsx_runtime61 = require("react/jsx-runtime");
|
|
16712
|
+
var RAIL_TITLE = {
|
|
16713
|
+
settings: "Table settings",
|
|
16714
|
+
inbox: "Inbox",
|
|
16715
|
+
import: "Import",
|
|
16716
|
+
field: "Add a field",
|
|
16717
|
+
record: "Record",
|
|
16718
|
+
"new-record": "New record",
|
|
16719
|
+
forms: "Forms",
|
|
16720
|
+
bookings: "Bookings",
|
|
16721
|
+
checklists: "Checklists",
|
|
16722
|
+
notifications: "Notifications",
|
|
16723
|
+
portals: "Portals",
|
|
16724
|
+
"who-changed": "Who changed this"
|
|
16725
|
+
};
|
|
16726
|
+
function RailFrame({
|
|
16727
|
+
phone,
|
|
16728
|
+
title,
|
|
16729
|
+
onClose,
|
|
16730
|
+
children
|
|
16731
|
+
}) {
|
|
16732
|
+
if (phone) {
|
|
16733
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
16734
|
+
import_design_system57.BottomSheet,
|
|
16735
|
+
{
|
|
16736
|
+
open: true,
|
|
16737
|
+
onOpenChange: (open) => {
|
|
16738
|
+
if (!open) onClose();
|
|
16739
|
+
},
|
|
16740
|
+
title,
|
|
16741
|
+
size: "full",
|
|
16742
|
+
surface: "solid",
|
|
16743
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { "data-rail-sheet": "", className: "min-h-0 flex-1 overflow-y-auto px-3 pb-6", children })
|
|
16744
|
+
}
|
|
16745
|
+
);
|
|
16746
|
+
}
|
|
16747
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("aside", { "data-rail-column": "", className: "w-[26rem] shrink-0 overflow-y-auto rounded-md border p-3", children });
|
|
16748
|
+
}
|
|
16487
16749
|
function filterInWords(filter, fields) {
|
|
16488
16750
|
const nameOf = (key) => {
|
|
16489
16751
|
const field = fields.find((f) => f.key === key);
|
|
@@ -16623,6 +16885,7 @@ function TablePage({
|
|
|
16623
16885
|
const setRail = (next) => setSurface((now) => ({ ...now, rail: next }));
|
|
16624
16886
|
const [openRecord, setOpenRecord] = (0, import_react118.useState)(opening.record);
|
|
16625
16887
|
const viewVersion = useRecordVersion(view?.id ?? null);
|
|
16888
|
+
const phone = (0, import_design_system57.useIsMobile)();
|
|
16626
16889
|
const shownLayout = layoutFromLink ?? view?.layout ?? "grid";
|
|
16627
16890
|
const press = (pressed) => {
|
|
16628
16891
|
const next = chooseSurface(surface, pressed);
|
|
@@ -16650,6 +16913,7 @@ function TablePage({
|
|
|
16650
16913
|
setLayoutFromLink(isLayout ? named : null);
|
|
16651
16914
|
setSurface((now) => ({ ...now, main: isLayout ? "records" : named }));
|
|
16652
16915
|
}, [activeView]);
|
|
16916
|
+
const [viewSaveRefused, setViewSaveRefused] = (0, import_react118.useState)(null);
|
|
16653
16917
|
const patchView = (0, import_react118.useCallback)(
|
|
16654
16918
|
async (patch) => {
|
|
16655
16919
|
const current = view;
|
|
@@ -16657,12 +16921,13 @@ function TablePage({
|
|
|
16657
16921
|
setView({ ...current, ...patch });
|
|
16658
16922
|
const document2 = viewPatchDocument(patch);
|
|
16659
16923
|
if (Object.keys(document2).length === 0) return;
|
|
16660
|
-
|
|
16661
|
-
|
|
16662
|
-
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16924
|
+
setViewSaveRefused(null);
|
|
16925
|
+
const written = await saveViewPatch(client, current.id, document2, viewVersion.version);
|
|
16926
|
+
if (written.ok) {
|
|
16927
|
+
viewVersion.note(written.data);
|
|
16928
|
+
return;
|
|
16929
|
+
}
|
|
16930
|
+
setViewSaveRefused({ error: written.error, patch, before: current });
|
|
16666
16931
|
},
|
|
16667
16932
|
[client, view, viewVersion]
|
|
16668
16933
|
);
|
|
@@ -16813,6 +17078,27 @@ function TablePage({
|
|
|
16813
17078
|
),
|
|
16814
17079
|
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ExportMenu, { tableId })
|
|
16815
17080
|
] }),
|
|
17081
|
+
viewSaveRefused ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
17082
|
+
RefusalNotice,
|
|
17083
|
+
{
|
|
17084
|
+
error: viewSaveRefused.error,
|
|
17085
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "flex flex-wrap gap-1 pt-0.5", "data-view-save-refused": "", children: [
|
|
17086
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(import_design_system57.Button, { size: "sm", variant: "outline", onClick: () => void patchView(viewSaveRefused.patch), children: "Try again" }),
|
|
17087
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
17088
|
+
import_design_system57.Button,
|
|
17089
|
+
{
|
|
17090
|
+
size: "sm",
|
|
17091
|
+
variant: "ghost",
|
|
17092
|
+
onClick: () => {
|
|
17093
|
+
setView(viewSaveRefused.before);
|
|
17094
|
+
setViewSaveRefused(null);
|
|
17095
|
+
},
|
|
17096
|
+
children: "Discard"
|
|
17097
|
+
}
|
|
17098
|
+
)
|
|
17099
|
+
] })
|
|
17100
|
+
}
|
|
17101
|
+
) : null,
|
|
16816
17102
|
main === "dashboards" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : main === "archived" ? (
|
|
16817
17103
|
/* THE ROWS THAT LEFT. Restoring one puts it back in the grid, so the
|
|
16818
17104
|
page returns to the records rather than leaving the person looking
|
|
@@ -16873,7 +17159,7 @@ function TablePage({
|
|
|
16873
17159
|
view ? null : /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "text-xs text-muted-foreground", children: VIEW_NOT_SAVED_YET })
|
|
16874
17160
|
] })
|
|
16875
17161
|
] }),
|
|
16876
|
-
rail === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
|
|
17162
|
+
rail === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(RailFrame, { phone, title: RAIL_TITLE[rail], onClose: () => setRail("none"), children: [
|
|
16877
17163
|
rail === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
16878
17164
|
TableSettings,
|
|
16879
17165
|
{
|