@ai-matrx/records-ui 0.82.0 → 0.83.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 +22 -0
- package/dist/index.cjs +279 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -4
- package/dist/index.d.ts +67 -4
- package/dist/index.js +278 -45
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.83.0
|
|
4
|
+
|
|
5
|
+
**Four visibility lanes a filter can read, and the store's own choice lists stop sitting
|
|
6
|
+
among a person's tables.** (lane HUB-FIX, VERIFIER-15.)
|
|
7
|
+
|
|
8
|
+
* New `VISIBILITY_LANES` (`mine`, `organization`, `community`, `world`),
|
|
9
|
+
`VISIBILITY_LANE_TITLE` and `visibilityLaneFor(table)`: who can SEE a table, from its
|
|
10
|
+
own `visibility` — personal → Mine, internal → My organization, link → Community,
|
|
11
|
+
public → World. A kernel table, the app's bookkeeping and a store-kept choice list
|
|
12
|
+
answer `null`: nobody chose a visibility for them. `TABLE_LANES` (the table list's
|
|
13
|
+
SECTIONS: who MADE a table) is unchanged.
|
|
14
|
+
* `laneFor` puts a table whose record carries `kept_by_the_app: true` in the `app` lane.
|
|
15
|
+
On Rincon Plumbing Co that moves 27 per-field "Crew choices" / "Status choices" tables
|
|
16
|
+
out of My organization, where the hub above already counted them as the app's.
|
|
17
|
+
* New export `keptByTheApp(table)`.
|
|
18
|
+
* Test: `src/visibility-lanes.test.tsx` (4 cases).
|
|
19
|
+
|
|
20
|
+
**Consumer action (matrx-frontend):** the organization hub's lane strip
|
|
21
|
+
(`features/unified-data/hub/OrganizationHub.tsx`) renders `VISIBILITY_LANES` with
|
|
22
|
+
`VISIBILITY_LANE_TITLE`, and `features/unified-data/hub/capabilities.ts` decides an item's
|
|
23
|
+
lane with `visibilityLaneFor` (dropping its local `keptByTheApp`).
|
|
24
|
+
|
|
3
25
|
## 0.82.0
|
|
4
26
|
|
|
5
27
|
**A link can open a table's Forms, Notifications, Portals or Share — and the thing inside
|
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, resent = false) => {
|
|
2387
2392
|
const id = cellKey(rowId, key);
|
|
2388
2393
|
setOptimistic((held) => ({ ...held, [id]: value }));
|
|
2389
2394
|
setSaving((held) => ({ ...held, [id]: true }));
|
|
@@ -2426,16 +2431,25 @@ 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 && !Object.prototype.hasOwnProperty.call(conflict.contested_fields, key)) {
|
|
2439
|
+
await writeCellNow(rowId, key, value, 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)(
|
|
@@ -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);
|
|
@@ -2561,6 +2602,8 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
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",
|
|
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",
|
|
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",
|
|
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" })
|
|
@@ -7237,6 +7321,8 @@ function RecordForm({
|
|
|
7237
7321
|
const mutation = (0, import_react42.useRecordMutation)();
|
|
7238
7322
|
const [draft, setDraft] = (0, import_react41.useState)({});
|
|
7239
7323
|
const [touched, setTouched] = (0, import_react41.useState)(false);
|
|
7324
|
+
const [changed, setChanged] = (0, import_react41.useState)({});
|
|
7325
|
+
const [answered, setAnswered] = (0, import_react41.useState)(null);
|
|
7240
7326
|
const loaded = useRecordVersion(recordId ?? null);
|
|
7241
7327
|
const loadedVersion = loaded.version;
|
|
7242
7328
|
(0, import_react41.useEffect)(() => {
|
|
@@ -7257,8 +7343,38 @@ function RecordForm({
|
|
|
7257
7343
|
);
|
|
7258
7344
|
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(RefusalNotice, { error: fields.error, className });
|
|
7259
7345
|
if (existing.error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(RefusalNotice, { error: existing.error, className });
|
|
7346
|
+
const storeSaid = mutation.error && mutation.error !== answered ? mutation.error : null;
|
|
7347
|
+
const fieldProblems = predicted.filter((p) => p.field_key);
|
|
7348
|
+
const problemsShown = (key) => touched || changed[key] ? fieldProblems.filter((p) => p.field_key === key) : [];
|
|
7349
|
+
const storeRefusalIsOnAField = storeSaid !== null && !mutation.conflict && storeSaid.code === "refused_by_rule" && (fields.data ?? []).some((f) => problemsShown(f.key).length > 0);
|
|
7350
|
+
const stored = existing.data?.document;
|
|
7351
|
+
const focusField = (field) => {
|
|
7352
|
+
if (!field) return;
|
|
7353
|
+
globalThis.document?.getElementById(`field-${field.id}`)?.focus();
|
|
7354
|
+
};
|
|
7355
|
+
const keepEditing = (field) => {
|
|
7356
|
+
if (storeSaid) setAnswered(storeSaid);
|
|
7357
|
+
const first = field ?? (fields.data ?? []).find((f) => problemsShown(f.key).length > 0) ?? (fields.data ?? [])[0];
|
|
7358
|
+
focusField(first);
|
|
7359
|
+
};
|
|
7360
|
+
const discard = (field) => {
|
|
7361
|
+
if (storeSaid) setAnswered(storeSaid);
|
|
7362
|
+
if (field) {
|
|
7363
|
+
setDraft((d) => ({ ...d, [field.key]: stored ? stored[field.key] : void 0 }));
|
|
7364
|
+
setChanged((c) => {
|
|
7365
|
+
const next = { ...c };
|
|
7366
|
+
delete next[field.key];
|
|
7367
|
+
return next;
|
|
7368
|
+
});
|
|
7369
|
+
return;
|
|
7370
|
+
}
|
|
7371
|
+
setDraft(stored ? { ...stored } : {});
|
|
7372
|
+
setChanged({});
|
|
7373
|
+
setTouched(false);
|
|
7374
|
+
};
|
|
7260
7375
|
const save = async () => {
|
|
7261
7376
|
setTouched(true);
|
|
7377
|
+
setAnswered(null);
|
|
7262
7378
|
if (recordId) {
|
|
7263
7379
|
const version = loadedVersion;
|
|
7264
7380
|
const moved = await mutation.update({
|
|
@@ -7295,8 +7411,13 @@ function RecordForm({
|
|
|
7295
7411
|
field,
|
|
7296
7412
|
value: draft[field.key],
|
|
7297
7413
|
document: existing.data?.document,
|
|
7298
|
-
problems:
|
|
7299
|
-
onChange: (value) =>
|
|
7414
|
+
problems: problemsShown(field.key),
|
|
7415
|
+
onChange: (value) => {
|
|
7416
|
+
setDraft((d) => ({ ...d, [field.key]: value }));
|
|
7417
|
+
setChanged((c) => c[field.key] ? c : { ...c, [field.key]: true });
|
|
7418
|
+
},
|
|
7419
|
+
onKeepEditing: () => keepEditing(field),
|
|
7420
|
+
onDiscard: () => discard(field)
|
|
7300
7421
|
},
|
|
7301
7422
|
field.id
|
|
7302
7423
|
)) }),
|
|
@@ -7307,10 +7428,10 @@ function RecordForm({
|
|
|
7307
7428
|
},
|
|
7308
7429
|
p.message
|
|
7309
7430
|
)) : null,
|
|
7310
|
-
|
|
7431
|
+
storeSaid && !storeRefusalIsOnAField ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7311
7432
|
RefusalNotice,
|
|
7312
7433
|
{
|
|
7313
|
-
error:
|
|
7434
|
+
error: storeSaid,
|
|
7314
7435
|
...mutation.conflict ? {
|
|
7315
7436
|
actions: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "mt-1 space-y-1", children: [
|
|
7316
7437
|
Object.entries(mutation.conflict.contested_fields).map(([key, theirs]) => {
|
|
@@ -7331,14 +7452,16 @@ function RecordForm({
|
|
|
7331
7452
|
size: "sm",
|
|
7332
7453
|
variant: "outline",
|
|
7333
7454
|
onClick: () => {
|
|
7455
|
+
if (mutation.conflict) loaded.note(mutation.conflict.current_version);
|
|
7334
7456
|
existing.reload();
|
|
7335
7457
|
mutation.clearConflict();
|
|
7458
|
+
setAnswered(storeSaid);
|
|
7336
7459
|
},
|
|
7337
7460
|
children: "Take theirs and start again"
|
|
7338
7461
|
}
|
|
7339
7462
|
)
|
|
7340
7463
|
] })
|
|
7341
|
-
} : {}
|
|
7464
|
+
} : { onKeepEditing: () => keepEditing(), onDiscard: () => discard() }
|
|
7342
7465
|
}
|
|
7343
7466
|
) : null,
|
|
7344
7467
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_design_system20.Separator, {}),
|
|
@@ -7355,17 +7478,20 @@ function FieldRow({
|
|
|
7355
7478
|
value,
|
|
7356
7479
|
document: document2,
|
|
7357
7480
|
problems,
|
|
7358
|
-
onChange
|
|
7481
|
+
onChange,
|
|
7482
|
+
onKeepEditing,
|
|
7483
|
+
onDiscard
|
|
7359
7484
|
}) {
|
|
7360
7485
|
const id = `field-${field.id}`;
|
|
7361
7486
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
7362
7487
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldLabel, { field, htmlFor: id, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ProvenanceBadge, { document: document2, fieldKey: field.key }) }),
|
|
7363
7488
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldControl, { field, value, onChange, id }),
|
|
7364
|
-
problems.map((problem) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7489
|
+
problems.map((problem, index) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7365
7490
|
RefusalNotice,
|
|
7366
7491
|
{
|
|
7367
7492
|
error: { code: "refused_by_rule", message: problem.message },
|
|
7368
|
-
className: "border-0 p-0"
|
|
7493
|
+
className: "border-0 p-0",
|
|
7494
|
+
...index === 0 ? { onKeepEditing, onDiscard } : {}
|
|
7369
7495
|
},
|
|
7370
7496
|
problem.message
|
|
7371
7497
|
))
|
|
@@ -7535,6 +7661,7 @@ function computedInPlainWords(row, field) {
|
|
|
7535
7661
|
}
|
|
7536
7662
|
|
|
7537
7663
|
// src/views.ts
|
|
7664
|
+
var import_core7 = require("@ai-matrx/records/core");
|
|
7538
7665
|
var VIEW_LAYOUTS = ["grid", "kanban", "calendar", "gallery"];
|
|
7539
7666
|
var LAYOUT_LABEL = {
|
|
7540
7667
|
grid: "Grid",
|
|
@@ -7658,6 +7785,18 @@ function viewFromRecord(row) {
|
|
|
7658
7785
|
presentation: parseGridPresentation(data["presentation"])
|
|
7659
7786
|
};
|
|
7660
7787
|
}
|
|
7788
|
+
async function saveViewPatch(client, viewId, document2, expectedVersion) {
|
|
7789
|
+
const send = (expected) => client.recordUpdate({
|
|
7790
|
+
record_id: viewId,
|
|
7791
|
+
patch: document2,
|
|
7792
|
+
...expected === null ? {} : { expectedVersion: expected }
|
|
7793
|
+
});
|
|
7794
|
+
const first = await send(expectedVersion);
|
|
7795
|
+
if (first.ok) return first;
|
|
7796
|
+
const conflict = (0, import_core7.asWriteConflict)(first.error, viewId);
|
|
7797
|
+
if (!conflict) return first;
|
|
7798
|
+
return send(conflict.current_version);
|
|
7799
|
+
}
|
|
7661
7800
|
function viewPatchDocument(patch) {
|
|
7662
7801
|
const out = {};
|
|
7663
7802
|
if (patch.name !== void 0) out["name"] = patch.name;
|
|
@@ -12053,6 +12192,21 @@ function whenItFires(subscription) {
|
|
|
12053
12192
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
12054
12193
|
return subscription.schedule ? `${every}, ${subscription.schedule}` : every;
|
|
12055
12194
|
}
|
|
12195
|
+
var PERIOD_MS = {
|
|
12196
|
+
hourly: 60 * 60 * 1e3,
|
|
12197
|
+
daily: 24 * 60 * 60 * 1e3,
|
|
12198
|
+
weekly: 7 * 24 * 60 * 60 * 1e3
|
|
12199
|
+
};
|
|
12200
|
+
function nextSummaryAt(subscription, now) {
|
|
12201
|
+
if (!subscription.next_digest_at) return null;
|
|
12202
|
+
const due = new Date(subscription.next_digest_at);
|
|
12203
|
+
if (Number.isNaN(due.getTime())) return null;
|
|
12204
|
+
if (due.getTime() > now.getTime()) return due;
|
|
12205
|
+
const period = PERIOD_MS[subscription.cadence];
|
|
12206
|
+
if (!period) return due;
|
|
12207
|
+
const behind = now.getTime() - due.getTime();
|
|
12208
|
+
return new Date(due.getTime() + (Math.floor(behind / period) + 1) * period);
|
|
12209
|
+
}
|
|
12056
12210
|
var CHANNEL_WORDS2 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
12057
12211
|
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
12058
12212
|
function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
@@ -12180,7 +12334,7 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
|
12180
12334
|
/* @__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
12335
|
subscription.next_digest_at ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
12182
12336
|
"Next summary ",
|
|
12183
|
-
new Date(
|
|
12337
|
+
nextSummaryAt(subscription, /* @__PURE__ */ new Date())?.toLocaleString()
|
|
12184
12338
|
] }) : subscription.cadence === "instant" ? null : subscription.muted ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "No next summary while it is off." }) : null,
|
|
12185
12339
|
subscription.last_sent_at ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
12186
12340
|
"Last told you ",
|
|
@@ -13876,14 +14030,14 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13876
14030
|
var import_react95 = require("react");
|
|
13877
14031
|
var import_recharts = require("recharts");
|
|
13878
14032
|
var import_records11 = require("@ai-matrx/records");
|
|
13879
|
-
var
|
|
14033
|
+
var import_core8 = require("@ai-matrx/records/core");
|
|
13880
14034
|
var import_design_system46 = require("@ai-matrx/design-system");
|
|
13881
14035
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
13882
14036
|
function pretty(n) {
|
|
13883
14037
|
return Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { maximumFractionDigits: 2 });
|
|
13884
14038
|
}
|
|
13885
14039
|
function asRefusal2(block) {
|
|
13886
|
-
return (0,
|
|
14040
|
+
return (0, import_core8.mapPgError)(
|
|
13887
14041
|
{
|
|
13888
14042
|
message: block.refused ?? "",
|
|
13889
14043
|
...block.sqlstate ? { code: block.sqlstate } : {}
|
|
@@ -16330,13 +16484,31 @@ var LANE_EMPTY = {
|
|
|
16330
16484
|
community: "Nothing shared beyond your organization. A table published by link or to the world lands here.",
|
|
16331
16485
|
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
16486
|
};
|
|
16487
|
+
function keptByTheApp(table) {
|
|
16488
|
+
return table.kept_by_the_app === true;
|
|
16489
|
+
}
|
|
16333
16490
|
function laneFor(table) {
|
|
16334
16491
|
if (table.is_kernel) return "system";
|
|
16335
|
-
if ((table.slug ?? "").startsWith(APP_TABLE_PREFIX)) return "app";
|
|
16492
|
+
if ((table.slug ?? "").startsWith(APP_TABLE_PREFIX) || keptByTheApp(table)) return "app";
|
|
16336
16493
|
if (table.visibility === "public" || table.visibility === "link") return "community";
|
|
16337
16494
|
if (table.visibility === "personal") return "mine";
|
|
16338
16495
|
return "organization";
|
|
16339
16496
|
}
|
|
16497
|
+
var VISIBILITY_LANES = ["mine", "organization", "community", "world"];
|
|
16498
|
+
var VISIBILITY_LANE_TITLE = {
|
|
16499
|
+
mine: "Mine",
|
|
16500
|
+
organization: "My organization",
|
|
16501
|
+
community: "Community",
|
|
16502
|
+
world: "World"
|
|
16503
|
+
};
|
|
16504
|
+
function visibilityLaneFor(table) {
|
|
16505
|
+
if (table.is_kernel) return null;
|
|
16506
|
+
if ((table.slug ?? "").startsWith(APP_TABLE_PREFIX) || keptByTheApp(table)) return null;
|
|
16507
|
+
if (table.visibility === "personal") return "mine";
|
|
16508
|
+
if (table.visibility === "link") return "community";
|
|
16509
|
+
if (table.visibility === "public") return "world";
|
|
16510
|
+
return "organization";
|
|
16511
|
+
}
|
|
16340
16512
|
function TablesHome({ onOpenTable, className }) {
|
|
16341
16513
|
const client = (0, import_react117.useRecordsClient)();
|
|
16342
16514
|
const tables = (0, import_react117.useTables)();
|
|
@@ -16484,6 +16656,43 @@ var import_react118 = require("react");
|
|
|
16484
16656
|
var import_react119 = require("@ai-matrx/records/react");
|
|
16485
16657
|
var import_design_system57 = require("@ai-matrx/design-system");
|
|
16486
16658
|
var import_jsx_runtime61 = require("react/jsx-runtime");
|
|
16659
|
+
var RAIL_TITLE = {
|
|
16660
|
+
settings: "Table settings",
|
|
16661
|
+
inbox: "Inbox",
|
|
16662
|
+
import: "Import",
|
|
16663
|
+
field: "Add a field",
|
|
16664
|
+
record: "Record",
|
|
16665
|
+
"new-record": "New record",
|
|
16666
|
+
forms: "Forms",
|
|
16667
|
+
bookings: "Bookings",
|
|
16668
|
+
checklists: "Checklists",
|
|
16669
|
+
notifications: "Notifications",
|
|
16670
|
+
portals: "Portals",
|
|
16671
|
+
"who-changed": "Who changed this"
|
|
16672
|
+
};
|
|
16673
|
+
function RailFrame({
|
|
16674
|
+
phone,
|
|
16675
|
+
title,
|
|
16676
|
+
onClose,
|
|
16677
|
+
children
|
|
16678
|
+
}) {
|
|
16679
|
+
if (phone) {
|
|
16680
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
16681
|
+
import_design_system57.BottomSheet,
|
|
16682
|
+
{
|
|
16683
|
+
open: true,
|
|
16684
|
+
onOpenChange: (open) => {
|
|
16685
|
+
if (!open) onClose();
|
|
16686
|
+
},
|
|
16687
|
+
title,
|
|
16688
|
+
size: "full",
|
|
16689
|
+
surface: "solid",
|
|
16690
|
+
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 })
|
|
16691
|
+
}
|
|
16692
|
+
);
|
|
16693
|
+
}
|
|
16694
|
+
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 });
|
|
16695
|
+
}
|
|
16487
16696
|
function filterInWords(filter, fields) {
|
|
16488
16697
|
const nameOf = (key) => {
|
|
16489
16698
|
const field = fields.find((f) => f.key === key);
|
|
@@ -16623,6 +16832,7 @@ function TablePage({
|
|
|
16623
16832
|
const setRail = (next) => setSurface((now) => ({ ...now, rail: next }));
|
|
16624
16833
|
const [openRecord, setOpenRecord] = (0, import_react118.useState)(opening.record);
|
|
16625
16834
|
const viewVersion = useRecordVersion(view?.id ?? null);
|
|
16835
|
+
const phone = (0, import_design_system57.useIsMobile)();
|
|
16626
16836
|
const shownLayout = layoutFromLink ?? view?.layout ?? "grid";
|
|
16627
16837
|
const press = (pressed) => {
|
|
16628
16838
|
const next = chooseSurface(surface, pressed);
|
|
@@ -16650,6 +16860,7 @@ function TablePage({
|
|
|
16650
16860
|
setLayoutFromLink(isLayout ? named : null);
|
|
16651
16861
|
setSurface((now) => ({ ...now, main: isLayout ? "records" : named }));
|
|
16652
16862
|
}, [activeView]);
|
|
16863
|
+
const [viewSaveRefused, setViewSaveRefused] = (0, import_react118.useState)(null);
|
|
16653
16864
|
const patchView = (0, import_react118.useCallback)(
|
|
16654
16865
|
async (patch) => {
|
|
16655
16866
|
const current = view;
|
|
@@ -16657,12 +16868,13 @@ function TablePage({
|
|
|
16657
16868
|
setView({ ...current, ...patch });
|
|
16658
16869
|
const document2 = viewPatchDocument(patch);
|
|
16659
16870
|
if (Object.keys(document2).length === 0) return;
|
|
16660
|
-
|
|
16661
|
-
|
|
16662
|
-
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16871
|
+
setViewSaveRefused(null);
|
|
16872
|
+
const written = await saveViewPatch(client, current.id, document2, viewVersion.version);
|
|
16873
|
+
if (written.ok) {
|
|
16874
|
+
viewVersion.note(written.data);
|
|
16875
|
+
return;
|
|
16876
|
+
}
|
|
16877
|
+
setViewSaveRefused({ error: written.error, patch, before: current });
|
|
16666
16878
|
},
|
|
16667
16879
|
[client, view, viewVersion]
|
|
16668
16880
|
);
|
|
@@ -16813,6 +17025,27 @@ function TablePage({
|
|
|
16813
17025
|
),
|
|
16814
17026
|
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ExportMenu, { tableId })
|
|
16815
17027
|
] }),
|
|
17028
|
+
viewSaveRefused ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
17029
|
+
RefusalNotice,
|
|
17030
|
+
{
|
|
17031
|
+
error: viewSaveRefused.error,
|
|
17032
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "flex flex-wrap gap-1 pt-0.5", "data-view-save-refused": "", children: [
|
|
17033
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(import_design_system57.Button, { size: "sm", variant: "outline", onClick: () => void patchView(viewSaveRefused.patch), children: "Try again" }),
|
|
17034
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
17035
|
+
import_design_system57.Button,
|
|
17036
|
+
{
|
|
17037
|
+
size: "sm",
|
|
17038
|
+
variant: "ghost",
|
|
17039
|
+
onClick: () => {
|
|
17040
|
+
setView(viewSaveRefused.before);
|
|
17041
|
+
setViewSaveRefused(null);
|
|
17042
|
+
},
|
|
17043
|
+
children: "Discard"
|
|
17044
|
+
}
|
|
17045
|
+
)
|
|
17046
|
+
] })
|
|
17047
|
+
}
|
|
17048
|
+
) : null,
|
|
16816
17049
|
main === "dashboards" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : main === "archived" ? (
|
|
16817
17050
|
/* THE ROWS THAT LEFT. Restoring one puts it back in the grid, so the
|
|
16818
17051
|
page returns to the records rather than leaving the person looking
|
|
@@ -16873,7 +17106,7 @@ function TablePage({
|
|
|
16873
17106
|
view ? null : /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "text-xs text-muted-foreground", children: VIEW_NOT_SAVED_YET })
|
|
16874
17107
|
] })
|
|
16875
17108
|
] }),
|
|
16876
|
-
rail === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
|
|
17109
|
+
rail === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(RailFrame, { phone, title: RAIL_TITLE[rail], onClose: () => setRail("none"), children: [
|
|
16877
17110
|
rail === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
16878
17111
|
TableSettings,
|
|
16879
17112
|
{
|