@proveanything/smartlinks-utils-ui 0.10.8 → 0.11.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/dist/{chunk-2MW54ZVG.js → chunk-BNC6Z6WB.js} +97 -4
- package/dist/chunk-BNC6Z6WB.js.map +1 -0
- package/dist/components/AssetPicker/index.css +18 -0
- package/dist/components/AssetPicker/index.css.map +1 -1
- package/dist/components/ConditionsEditor/index.css +18 -0
- package/dist/components/ConditionsEditor/index.css.map +1 -1
- package/dist/components/FontPicker/index.css +18 -0
- package/dist/components/FontPicker/index.css.map +1 -1
- package/dist/components/IconPicker/index.css +18 -0
- package/dist/components/IconPicker/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.css +18 -0
- package/dist/components/RecordsAdmin/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.d.ts +140 -8
- package/dist/components/RecordsAdmin/index.js +565 -93
- package/dist/components/RecordsAdmin/index.js.map +1 -1
- package/dist/index.css +18 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2MW54ZVG.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AdminPageHeader } from '../../chunk-
|
|
1
|
+
import { useIntroState, AdminPageHeader } from '../../chunk-BNC6Z6WB.js';
|
|
2
2
|
import { assertComponentStylesLoaded } from '../../chunk-OLYC54YT.js';
|
|
3
3
|
import '../../chunk-5UQQYXCX.js';
|
|
4
4
|
import { FacetRuleEditor } from '../../chunk-JMCV6FOW.js';
|
|
@@ -6,12 +6,105 @@ import { useFacets } from '../../chunk-4LHF5JB7.js';
|
|
|
6
6
|
import { cn } from '../../chunk-L7FQ52F5.js';
|
|
7
7
|
import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-KA4MKRHL.js';
|
|
8
8
|
export { bulkDelete, bulkUpsert, createRecord, getRecordById, listRecords, matchRecords, parsedRefToScope, parsedRefToTarget, removeRecord, restoreRecord, scopesEqual, upsertRecord } from '../../chunk-KA4MKRHL.js';
|
|
9
|
-
import { createContext, useState, useEffect, useCallback,
|
|
10
|
-
import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, List, SlidersHorizontal, Globe, Tag, Boxes, Layers, Package, Target, Rows3, ChevronRight, Eraser, ClipboardPaste, Box, X, Search, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, CornerDownLeft, Circle, CopyPlus, AlertCircle, Undo2, Save, Loader2,
|
|
9
|
+
import { createContext, useMemo, useState, useEffect, useCallback, useRef, isValidElement, useContext, useSyncExternalStore, useLayoutEffect, createElement } from 'react';
|
|
10
|
+
import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, List, SlidersHorizontal, Globe, Tag, Boxes, Layers, Package, Target, Rows3, ChevronRight, Eraser, ClipboardPaste, Box, X, Search, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, CornerDownLeft, Circle, ArrowUpDown, ArrowUp, ArrowDown, MinusCircle, XCircle, CopyPlus, AlertCircle, Undo2, Save, Loader2, ArrowRight, Globe2, Check, Settings2 } from 'lucide-react';
|
|
11
11
|
import { useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
|
|
12
12
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
13
13
|
import { createPortal } from 'react-dom';
|
|
14
14
|
|
|
15
|
+
// src/components/RecordsAdmin/data/recordCache.ts
|
|
16
|
+
var RECORD_LIST_QK = ["records-admin", "list"];
|
|
17
|
+
var COLLECTION_ITEMS_QK = ["records-admin", "collection-items"];
|
|
18
|
+
var matchesCtx = (queryKey, prefix, ctx) => {
|
|
19
|
+
if (queryKey.length < prefix.length + 3) return false;
|
|
20
|
+
for (let i = 0; i < prefix.length; i += 1) {
|
|
21
|
+
if (queryKey[i] !== prefix[i]) return false;
|
|
22
|
+
}
|
|
23
|
+
if (queryKey[prefix.length] !== ctx.collectionId) return false;
|
|
24
|
+
if (queryKey[prefix.length + 1] !== ctx.appId) return false;
|
|
25
|
+
if (queryKey[prefix.length + 2] !== (ctx.recordType ?? void 0) && queryKey[prefix.length + 2] !== ctx.recordType) {
|
|
26
|
+
const slot = queryKey[prefix.length + 2];
|
|
27
|
+
if (slot !== void 0 && slot !== ctx.recordType) return false;
|
|
28
|
+
if (slot === void 0 && ctx.recordType) return false;
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
};
|
|
32
|
+
var replaceOrAppend = (cache, record) => {
|
|
33
|
+
let replaced = false;
|
|
34
|
+
const nextPages = cache.pages.map((page) => {
|
|
35
|
+
const idx = page.data.findIndex((r) => r.id === record.id);
|
|
36
|
+
if (idx === -1) return page;
|
|
37
|
+
replaced = true;
|
|
38
|
+
const nextData = [...page.data];
|
|
39
|
+
nextData[idx] = record;
|
|
40
|
+
return { ...page, data: nextData };
|
|
41
|
+
});
|
|
42
|
+
if (replaced) return { ...cache, pages: nextPages };
|
|
43
|
+
if (nextPages.length === 0) {
|
|
44
|
+
return {
|
|
45
|
+
pageParams: [0],
|
|
46
|
+
pages: [{ data: [record], total: 1, hasMore: false, nextOffset: 1 }]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const last = nextPages[nextPages.length - 1];
|
|
50
|
+
const updatedLast = {
|
|
51
|
+
...last,
|
|
52
|
+
data: [...last.data, record],
|
|
53
|
+
total: (last.total ?? 0) + 1,
|
|
54
|
+
nextOffset: (last.nextOffset ?? last.data.length) + 1
|
|
55
|
+
};
|
|
56
|
+
const bumped = nextPages.map((p, i) => i === nextPages.length - 1 ? updatedLast : { ...p, total: (p.total ?? 0) + 1 });
|
|
57
|
+
return { ...cache, pages: bumped };
|
|
58
|
+
};
|
|
59
|
+
var removeFromPages = (cache, recordId) => {
|
|
60
|
+
let found = false;
|
|
61
|
+
const nextPages = cache.pages.map((page) => {
|
|
62
|
+
const idx = page.data.findIndex((r) => r.id === recordId);
|
|
63
|
+
if (idx === -1) return page;
|
|
64
|
+
found = true;
|
|
65
|
+
return {
|
|
66
|
+
...page,
|
|
67
|
+
data: page.data.filter((r) => r.id !== recordId),
|
|
68
|
+
total: Math.max(0, (page.total ?? 0) - 1)
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
if (!found) return null;
|
|
72
|
+
const bumped = nextPages.map((p) => p.data.some((r) => r.id === recordId) ? p : { ...p, total: Math.max(0, p.total ?? 0) });
|
|
73
|
+
return { ...cache, pages: bumped };
|
|
74
|
+
};
|
|
75
|
+
function patchRecordIntoCaches(queryClient, ctx, record) {
|
|
76
|
+
if (!record || !record.id) return;
|
|
77
|
+
const all = [
|
|
78
|
+
...queryClient.getQueriesData({ queryKey: RECORD_LIST_QK }),
|
|
79
|
+
...queryClient.getQueriesData({ queryKey: COLLECTION_ITEMS_QK })
|
|
80
|
+
];
|
|
81
|
+
for (const [key, cache] of all) {
|
|
82
|
+
if (!cache || !Array.isArray(cache.pages)) continue;
|
|
83
|
+
const prefix = key[0] === RECORD_LIST_QK[0] && key[1] === RECORD_LIST_QK[1] ? RECORD_LIST_QK : COLLECTION_ITEMS_QK;
|
|
84
|
+
if (!matchesCtx(key, prefix, ctx)) continue;
|
|
85
|
+
queryClient.setQueryData(key, (prev) => {
|
|
86
|
+
if (!prev || !Array.isArray(prev.pages)) return prev;
|
|
87
|
+
return replaceOrAppend(prev, record);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function removeRecordFromCaches(queryClient, ctx, recordId) {
|
|
92
|
+
if (!recordId) return;
|
|
93
|
+
const all = [
|
|
94
|
+
...queryClient.getQueriesData({ queryKey: RECORD_LIST_QK }),
|
|
95
|
+
...queryClient.getQueriesData({ queryKey: COLLECTION_ITEMS_QK })
|
|
96
|
+
];
|
|
97
|
+
for (const [key, cache] of all) {
|
|
98
|
+
if (!cache || !Array.isArray(cache.pages)) continue;
|
|
99
|
+
const prefix = key[0] === RECORD_LIST_QK[0] && key[1] === RECORD_LIST_QK[1] ? RECORD_LIST_QK : COLLECTION_ITEMS_QK;
|
|
100
|
+
if (!matchesCtx(key, prefix, ctx)) continue;
|
|
101
|
+
queryClient.setQueryData(key, (prev) => {
|
|
102
|
+
if (!prev || !Array.isArray(prev.pages)) return prev;
|
|
103
|
+
const next = removeFromPages(prev, recordId);
|
|
104
|
+
return next ?? prev;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
15
108
|
var DEFAULT_ICONS = {
|
|
16
109
|
scope: {
|
|
17
110
|
collection: Globe,
|
|
@@ -412,58 +505,12 @@ function useResolvedRecord(args) {
|
|
|
412
505
|
error: query.error ?? null
|
|
413
506
|
};
|
|
414
507
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
var useIntroDismissed = (SL,
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
} catch {
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
useEffect(() => {
|
|
426
|
-
let cancelled = false;
|
|
427
|
-
(async () => {
|
|
428
|
-
try {
|
|
429
|
-
const cfg = await SL?.appConfiguration?.getConfig?.({ collectionId, appId, admin: true });
|
|
430
|
-
if (cancelled) return;
|
|
431
|
-
const flag = cfg?._meta?.introDismissed?.[RT_KEY(recordType)];
|
|
432
|
-
if (flag) setDismissed(true);
|
|
433
|
-
} catch {
|
|
434
|
-
}
|
|
435
|
-
})();
|
|
436
|
-
return () => {
|
|
437
|
-
cancelled = true;
|
|
438
|
-
};
|
|
439
|
-
}, [SL, collectionId, appId, recordType]);
|
|
440
|
-
const dismiss = useCallback(async () => {
|
|
441
|
-
setDismissed(true);
|
|
442
|
-
try {
|
|
443
|
-
localStorage.setItem(lsKey(appId, recordType), "1");
|
|
444
|
-
} catch {
|
|
445
|
-
}
|
|
446
|
-
try {
|
|
447
|
-
const cfg = await SL?.appConfiguration?.getConfig?.({ collectionId, appId, admin: true }).catch(() => ({}));
|
|
448
|
-
const next = {
|
|
449
|
-
...cfg ?? {},
|
|
450
|
-
_meta: {
|
|
451
|
-
...cfg?._meta ?? {},
|
|
452
|
-
introDismissed: { ...cfg?._meta?.introDismissed ?? {}, [RT_KEY(recordType)]: true }
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
await SL?.appConfiguration?.setConfig?.({ collectionId, appId, admin: true, config: next });
|
|
456
|
-
} catch {
|
|
457
|
-
}
|
|
458
|
-
}, [SL, collectionId, appId, recordType]);
|
|
459
|
-
const undismiss = useCallback(() => {
|
|
460
|
-
setDismissed(false);
|
|
461
|
-
try {
|
|
462
|
-
localStorage.removeItem(lsKey(appId, recordType));
|
|
463
|
-
} catch {
|
|
464
|
-
}
|
|
465
|
-
}, [appId, recordType]);
|
|
466
|
-
return { dismissed, dismiss, undismiss };
|
|
508
|
+
|
|
509
|
+
// src/components/RecordsAdmin/hooks/useIntroDismissed.ts
|
|
510
|
+
var useIntroDismissed = (SL, _collectionId, appId, recordType) => {
|
|
511
|
+
const persistKey = recordType ? `${appId}:${recordType}` : appId;
|
|
512
|
+
const { dismissed, onDismiss, onReopen } = useIntroState({ SL, persistKey });
|
|
513
|
+
return { dismissed, dismiss: onDismiss, undismiss: onReopen };
|
|
467
514
|
};
|
|
468
515
|
var useScopeProbe = ({ SL, collectionId, admin = true, enabled = true }) => {
|
|
469
516
|
const query = useQuery({
|
|
@@ -1055,7 +1102,8 @@ function useShellBrowser(opts) {
|
|
|
1055
1102
|
probeIsLoading,
|
|
1056
1103
|
selectedProductId,
|
|
1057
1104
|
drillTab,
|
|
1058
|
-
classify: classify3
|
|
1105
|
+
classify: classify3,
|
|
1106
|
+
pageSize
|
|
1059
1107
|
} = opts;
|
|
1060
1108
|
const [search, setSearch] = useState("");
|
|
1061
1109
|
const [filter, setFilter] = useState("all");
|
|
@@ -1081,7 +1129,8 @@ function useShellBrowser(opts) {
|
|
|
1081
1129
|
filter,
|
|
1082
1130
|
classify: classify3,
|
|
1083
1131
|
contextScope,
|
|
1084
|
-
enabled: recordListEnabled
|
|
1132
|
+
enabled: recordListEnabled,
|
|
1133
|
+
pageSize
|
|
1085
1134
|
});
|
|
1086
1135
|
const facetBrowse = useFacetBrowse({
|
|
1087
1136
|
SL,
|
|
@@ -2352,6 +2401,7 @@ var createEditorStore = () => {
|
|
|
2352
2401
|
let nextOrder = 0;
|
|
2353
2402
|
let hooksBundle = null;
|
|
2354
2403
|
let notifier = null;
|
|
2404
|
+
let recordChangeNotifier = null;
|
|
2355
2405
|
const buildRecordSummary = (entry, value) => ({
|
|
2356
2406
|
id: entry.recordId ?? null,
|
|
2357
2407
|
ref: entry.spec.scope.raw,
|
|
@@ -2557,8 +2607,9 @@ var createEditorStore = () => {
|
|
|
2557
2607
|
});
|
|
2558
2608
|
try {
|
|
2559
2609
|
let nextRecordId = entry.recordId;
|
|
2610
|
+
let savedRecord = null;
|
|
2560
2611
|
if (entry.recordId && entry.source === "self") {
|
|
2561
|
-
await updateRecord(ctx, entry.recordId, {
|
|
2612
|
+
savedRecord = await updateRecord(ctx, entry.recordId, {
|
|
2562
2613
|
data: persistedValue,
|
|
2563
2614
|
facetRule: persistedFacetRule
|
|
2564
2615
|
});
|
|
@@ -2577,6 +2628,7 @@ var createEditorStore = () => {
|
|
|
2577
2628
|
facetRule: persistedFacetRule
|
|
2578
2629
|
});
|
|
2579
2630
|
nextRecordId = created?.id ?? nextRecordId;
|
|
2631
|
+
savedRecord = created;
|
|
2580
2632
|
} else {
|
|
2581
2633
|
const upserted = await upsertRecord(ctx, {
|
|
2582
2634
|
ref: spec.ref,
|
|
@@ -2585,6 +2637,7 @@ var createEditorStore = () => {
|
|
|
2585
2637
|
facetRule: persistedFacetRule
|
|
2586
2638
|
});
|
|
2587
2639
|
nextRecordId = upserted.record?.id ?? nextRecordId;
|
|
2640
|
+
savedRecord = upserted.record;
|
|
2588
2641
|
}
|
|
2589
2642
|
update(editorId, (e) => {
|
|
2590
2643
|
if (persistedValue !== entry.value) {
|
|
@@ -2597,6 +2650,12 @@ var createEditorStore = () => {
|
|
|
2597
2650
|
e.status = "saved";
|
|
2598
2651
|
e.error = void 0;
|
|
2599
2652
|
});
|
|
2653
|
+
if (savedRecord && savedRecord.id) {
|
|
2654
|
+
try {
|
|
2655
|
+
recordChangeNotifier?.({ kind: "saved", record: savedRecord, isCreate, ctx });
|
|
2656
|
+
} catch {
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2600
2659
|
if (hooksBundle?.afterSave) {
|
|
2601
2660
|
const refreshed = map.get(editorId);
|
|
2602
2661
|
const hookCtx = {
|
|
@@ -2654,6 +2713,10 @@ var createEditorStore = () => {
|
|
|
2654
2713
|
await removeRecord(entry.saveSpec.ctx, entry.recordId);
|
|
2655
2714
|
map.delete(editorId);
|
|
2656
2715
|
emit();
|
|
2716
|
+
try {
|
|
2717
|
+
recordChangeNotifier?.({ kind: "deleted", recordId: entry.recordId, ctx: entry.saveSpec.ctx });
|
|
2718
|
+
} catch {
|
|
2719
|
+
}
|
|
2657
2720
|
if (hooksBundle?.afterDelete && deletedSnapshot) {
|
|
2658
2721
|
try {
|
|
2659
2722
|
await hooksBundle.afterDelete(deletedSnapshot);
|
|
@@ -2688,6 +2751,9 @@ var createEditorStore = () => {
|
|
|
2688
2751
|
},
|
|
2689
2752
|
setNotifier(notify2) {
|
|
2690
2753
|
notifier = notify2 ?? null;
|
|
2754
|
+
},
|
|
2755
|
+
setRecordChangeNotifier(notify2) {
|
|
2756
|
+
recordChangeNotifier = notify2 ?? null;
|
|
2691
2757
|
}
|
|
2692
2758
|
};
|
|
2693
2759
|
};
|
|
@@ -2711,12 +2777,14 @@ var EditorSessionProvider = ({
|
|
|
2711
2777
|
maxOpenEditors = 8,
|
|
2712
2778
|
defaultValueFactory,
|
|
2713
2779
|
hooks,
|
|
2714
|
-
onHookNotice
|
|
2780
|
+
onHookNotice,
|
|
2781
|
+
onRecordChange
|
|
2715
2782
|
}) => {
|
|
2716
2783
|
const storeRef = useRef(null);
|
|
2717
2784
|
if (!storeRef.current) storeRef.current = createEditorStore();
|
|
2718
2785
|
storeRef.current.setHooks(hooks ?? null);
|
|
2719
2786
|
storeRef.current.setNotifier(onHookNotice ?? null);
|
|
2787
|
+
storeRef.current.setRecordChangeNotifier(onRecordChange ?? null);
|
|
2720
2788
|
const currentRef = useRef(void 0);
|
|
2721
2789
|
const listenersRef = useRef(/* @__PURE__ */ new Set());
|
|
2722
2790
|
const subscribeCurrent = useCallback((cb) => {
|
|
@@ -3250,7 +3318,43 @@ var resolveTone = (source, status) => {
|
|
|
3250
3318
|
if (source === "inherited" || status === "partial") return "shared";
|
|
3251
3319
|
return "missing";
|
|
3252
3320
|
};
|
|
3253
|
-
var
|
|
3321
|
+
var SEMANTIC_DEFAULT_ICONS = {
|
|
3322
|
+
success: CheckCircle2,
|
|
3323
|
+
warning: AlertTriangle,
|
|
3324
|
+
danger: XCircle,
|
|
3325
|
+
muted: MinusCircle,
|
|
3326
|
+
info: Info
|
|
3327
|
+
};
|
|
3328
|
+
var StatusIcon = ({
|
|
3329
|
+
source,
|
|
3330
|
+
status,
|
|
3331
|
+
className,
|
|
3332
|
+
size = "1.05rem",
|
|
3333
|
+
label,
|
|
3334
|
+
iconHint,
|
|
3335
|
+
semanticTone
|
|
3336
|
+
}) => {
|
|
3337
|
+
if (semanticTone || iconHint) {
|
|
3338
|
+
const toneClass = semanticTone && semanticTone !== "default" ? `ra-status-icon--${semanticTone}` : "ra-status-icon--muted";
|
|
3339
|
+
let content;
|
|
3340
|
+
if (iconHint) {
|
|
3341
|
+
content = isValidElement(iconHint) ? iconHint : /* @__PURE__ */ jsx(Fragment, { children: iconHint });
|
|
3342
|
+
} else {
|
|
3343
|
+
const Icon2 = semanticTone && semanticTone !== "default" ? SEMANTIC_DEFAULT_ICONS[semanticTone] : MinusCircle;
|
|
3344
|
+
content = /* @__PURE__ */ jsx(Icon2, { className: "w-full h-full" });
|
|
3345
|
+
}
|
|
3346
|
+
return /* @__PURE__ */ jsx(
|
|
3347
|
+
"span",
|
|
3348
|
+
{
|
|
3349
|
+
className: cn("ra-status-icon", toneClass, className),
|
|
3350
|
+
style: { width: size, height: size },
|
|
3351
|
+
role: label ? "img" : void 0,
|
|
3352
|
+
"aria-label": label,
|
|
3353
|
+
"aria-hidden": label ? void 0 : "true",
|
|
3354
|
+
children: content
|
|
3355
|
+
}
|
|
3356
|
+
);
|
|
3357
|
+
}
|
|
3254
3358
|
const tone = resolveTone(source, status);
|
|
3255
3359
|
const Icon = DEFAULT_ICONS.status[tone === "own" ? "own" : tone === "shared" ? "inherited" : "missing"];
|
|
3256
3360
|
return /* @__PURE__ */ jsx(
|
|
@@ -3483,7 +3587,9 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
|
|
|
3483
3587
|
StatusIcon,
|
|
3484
3588
|
{
|
|
3485
3589
|
status: record.status,
|
|
3486
|
-
label: statusToneLabel(tone)
|
|
3590
|
+
label: statusToneLabel(tone),
|
|
3591
|
+
iconHint: record.iconHint,
|
|
3592
|
+
semanticTone: record.toneHint
|
|
3487
3593
|
}
|
|
3488
3594
|
) }),
|
|
3489
3595
|
/* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
|
|
@@ -3711,6 +3817,56 @@ var ProductList = RecordList;
|
|
|
3711
3817
|
var FacetList = RecordList;
|
|
3712
3818
|
var VariantList = RecordList;
|
|
3713
3819
|
var BatchList = RecordList;
|
|
3820
|
+
function LoadMoreFooter({
|
|
3821
|
+
shown,
|
|
3822
|
+
total,
|
|
3823
|
+
hasNextPage,
|
|
3824
|
+
isFetchingNextPage,
|
|
3825
|
+
onLoadMore,
|
|
3826
|
+
label
|
|
3827
|
+
}) {
|
|
3828
|
+
if (!hasNextPage && (total === void 0 || total <= shown)) return null;
|
|
3829
|
+
const knownTotal = typeof total === "number" && total >= shown;
|
|
3830
|
+
const summary = knownTotal ? `Showing ${shown} of ${total}` : `${shown} shown`;
|
|
3831
|
+
return /* @__PURE__ */ jsxs(
|
|
3832
|
+
"div",
|
|
3833
|
+
{
|
|
3834
|
+
className: "ra-loadmore",
|
|
3835
|
+
style: {
|
|
3836
|
+
display: "flex",
|
|
3837
|
+
alignItems: "center",
|
|
3838
|
+
gap: "8px",
|
|
3839
|
+
padding: "8px 12px",
|
|
3840
|
+
borderTop: "1px solid hsl(var(--ra-border))",
|
|
3841
|
+
background: "hsl(var(--ra-surface))"
|
|
3842
|
+
},
|
|
3843
|
+
children: [
|
|
3844
|
+
/* @__PURE__ */ jsx(
|
|
3845
|
+
"span",
|
|
3846
|
+
{
|
|
3847
|
+
style: {
|
|
3848
|
+
fontSize: "11px",
|
|
3849
|
+
color: "hsl(var(--ra-muted-text))",
|
|
3850
|
+
flex: 1
|
|
3851
|
+
},
|
|
3852
|
+
children: summary
|
|
3853
|
+
}
|
|
3854
|
+
),
|
|
3855
|
+
hasNextPage && /* @__PURE__ */ jsx(
|
|
3856
|
+
"button",
|
|
3857
|
+
{
|
|
3858
|
+
type: "button",
|
|
3859
|
+
onClick: onLoadMore,
|
|
3860
|
+
disabled: isFetchingNextPage,
|
|
3861
|
+
className: "ra-btn",
|
|
3862
|
+
style: { fontSize: "11px", padding: "4px 10px" },
|
|
3863
|
+
children: isFetchingNextPage ? "Loading\u2026" : label ?? "Load more"
|
|
3864
|
+
}
|
|
3865
|
+
)
|
|
3866
|
+
]
|
|
3867
|
+
}
|
|
3868
|
+
);
|
|
3869
|
+
}
|
|
3714
3870
|
var COLLAPSED_FACET_CAP = 6;
|
|
3715
3871
|
var COLLAPSED_VALUE_CAP = 12;
|
|
3716
3872
|
var VALUE_SEARCH_THRESHOLD = 12;
|
|
@@ -5621,18 +5777,52 @@ function DefaultItemTable({
|
|
|
5621
5777
|
selectedId,
|
|
5622
5778
|
onOpen,
|
|
5623
5779
|
onDelete,
|
|
5780
|
+
sort,
|
|
5781
|
+
onToggleSort,
|
|
5624
5782
|
rowActions,
|
|
5625
5783
|
rowClipboard,
|
|
5626
5784
|
i18n
|
|
5627
5785
|
}) {
|
|
5628
5786
|
const cols = columns ?? [];
|
|
5629
5787
|
const useFallback = cols.length === 0;
|
|
5788
|
+
const renderSortIcon = (key, sortable) => {
|
|
5789
|
+
if (!sortable) return null;
|
|
5790
|
+
if (sort?.key !== key || !sort?.dir) {
|
|
5791
|
+
return /* @__PURE__ */ jsx(ArrowUpDown, { className: "w-3 h-3 opacity-40", "aria-hidden": "true" });
|
|
5792
|
+
}
|
|
5793
|
+
return sort.dir === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "w-3 h-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "w-3 h-3", "aria-hidden": "true" });
|
|
5794
|
+
};
|
|
5630
5795
|
return /* @__PURE__ */ jsx("div", { className: "ra-item-table-wrap", children: /* @__PURE__ */ jsxs("table", { className: "ra-item-table", children: [
|
|
5631
5796
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
5632
5797
|
useFallback ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5633
|
-
/* @__PURE__ */ jsx("th", { children:
|
|
5634
|
-
|
|
5635
|
-
|
|
5798
|
+
/* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsx(
|
|
5799
|
+
SortableHeader,
|
|
5800
|
+
{
|
|
5801
|
+
label: i18n.itemColumnLabel,
|
|
5802
|
+
sortable: !!onToggleSort,
|
|
5803
|
+
icon: renderSortIcon("__label", !!onToggleSort),
|
|
5804
|
+
onClick: () => onToggleSort?.("__label")
|
|
5805
|
+
}
|
|
5806
|
+
) }),
|
|
5807
|
+
/* @__PURE__ */ jsx("th", { style: { width: "12rem" }, children: /* @__PURE__ */ jsx(
|
|
5808
|
+
SortableHeader,
|
|
5809
|
+
{
|
|
5810
|
+
label: i18n.itemColumnUpdated,
|
|
5811
|
+
sortable: !!onToggleSort,
|
|
5812
|
+
icon: renderSortIcon("__updated", !!onToggleSort),
|
|
5813
|
+
onClick: () => onToggleSort?.("__updated")
|
|
5814
|
+
}
|
|
5815
|
+
) })
|
|
5816
|
+
] }) : cols.map((c) => /* @__PURE__ */ jsx("th", { style: { width: c.width, textAlign: c.align ?? "left" }, children: /* @__PURE__ */ jsx(
|
|
5817
|
+
SortableHeader,
|
|
5818
|
+
{
|
|
5819
|
+
label: c.header,
|
|
5820
|
+
sortable: !!c.sortBy && !!onToggleSort,
|
|
5821
|
+
icon: renderSortIcon(c.key, !!c.sortBy && !!onToggleSort),
|
|
5822
|
+
onClick: () => onToggleSort?.(c.key),
|
|
5823
|
+
align: c.align ?? "left"
|
|
5824
|
+
}
|
|
5825
|
+
) }, c.key)),
|
|
5636
5826
|
/* @__PURE__ */ jsx(
|
|
5637
5827
|
"th",
|
|
5638
5828
|
{
|
|
@@ -5720,6 +5910,39 @@ function DefaultItemTable({
|
|
|
5720
5910
|
}) })
|
|
5721
5911
|
] }) });
|
|
5722
5912
|
}
|
|
5913
|
+
function SortableHeader({
|
|
5914
|
+
label,
|
|
5915
|
+
sortable,
|
|
5916
|
+
icon,
|
|
5917
|
+
onClick,
|
|
5918
|
+
align = "left"
|
|
5919
|
+
}) {
|
|
5920
|
+
if (!sortable) return /* @__PURE__ */ jsx(Fragment, { children: label });
|
|
5921
|
+
return /* @__PURE__ */ jsxs(
|
|
5922
|
+
"button",
|
|
5923
|
+
{
|
|
5924
|
+
type: "button",
|
|
5925
|
+
onClick,
|
|
5926
|
+
className: "ra-item-th-sort",
|
|
5927
|
+
style: {
|
|
5928
|
+
display: "inline-flex",
|
|
5929
|
+
alignItems: "center",
|
|
5930
|
+
gap: "4px",
|
|
5931
|
+
background: "transparent",
|
|
5932
|
+
border: 0,
|
|
5933
|
+
padding: 0,
|
|
5934
|
+
font: "inherit",
|
|
5935
|
+
color: "inherit",
|
|
5936
|
+
cursor: "pointer",
|
|
5937
|
+
textAlign: align
|
|
5938
|
+
},
|
|
5939
|
+
children: [
|
|
5940
|
+
/* @__PURE__ */ jsx("span", { children: label }),
|
|
5941
|
+
icon
|
|
5942
|
+
]
|
|
5943
|
+
}
|
|
5944
|
+
);
|
|
5945
|
+
}
|
|
5723
5946
|
var initials = (label) => label.split(/\s+/).slice(0, 2).map((s) => s[0]?.toUpperCase() ?? "").join("") || "?";
|
|
5724
5947
|
function DefaultItemCards({
|
|
5725
5948
|
items,
|
|
@@ -5834,9 +6057,75 @@ function ItemListView({
|
|
|
5834
6057
|
cardSize = "md",
|
|
5835
6058
|
rowActions,
|
|
5836
6059
|
rowClipboard,
|
|
6060
|
+
searchableFields,
|
|
6061
|
+
searchable = true,
|
|
6062
|
+
total,
|
|
6063
|
+
hasNextPage,
|
|
6064
|
+
isFetchingNextPage,
|
|
6065
|
+
onLoadMore,
|
|
5837
6066
|
i18n
|
|
5838
6067
|
}) {
|
|
5839
6068
|
const newLabel = i18n.newItem.includes("{noun}") ? i18n.newItem.replace("{noun}", itemNoun) : i18n.newItem;
|
|
6069
|
+
const [search, setSearch] = useState("");
|
|
6070
|
+
const [sort, setSort] = useState({ key: null, dir: null });
|
|
6071
|
+
const onToggleSort = (key) => {
|
|
6072
|
+
setSort((cur) => {
|
|
6073
|
+
if (cur.key !== key) return { key, dir: "asc" };
|
|
6074
|
+
const next = cur.dir === "asc" ? "desc" : cur.dir === "desc" ? null : "asc";
|
|
6075
|
+
return { key: next ? key : null, dir: next };
|
|
6076
|
+
});
|
|
6077
|
+
};
|
|
6078
|
+
const wantsFullSet = search.trim().length > 0 || sort.dir !== null;
|
|
6079
|
+
const loadingNextRef = useRef(false);
|
|
6080
|
+
useEffect(() => {
|
|
6081
|
+
if (!wantsFullSet || !hasNextPage || !onLoadMore) return;
|
|
6082
|
+
if (isFetchingNextPage || loadingNextRef.current) return;
|
|
6083
|
+
loadingNextRef.current = true;
|
|
6084
|
+
onLoadMore();
|
|
6085
|
+
}, [wantsFullSet, hasNextPage, onLoadMore, isFetchingNextPage]);
|
|
6086
|
+
useEffect(() => {
|
|
6087
|
+
if (!isFetchingNextPage) loadingNextRef.current = false;
|
|
6088
|
+
}, [isFetchingNextPage]);
|
|
6089
|
+
const lookupColumn = useMemo(() => {
|
|
6090
|
+
const m = /* @__PURE__ */ new Map();
|
|
6091
|
+
(itemColumns ?? []).forEach((c) => m.set(c.key, c));
|
|
6092
|
+
return m;
|
|
6093
|
+
}, [itemColumns]);
|
|
6094
|
+
const visibleItems = useMemo(() => {
|
|
6095
|
+
let out = items;
|
|
6096
|
+
const q = search.trim().toLowerCase();
|
|
6097
|
+
if (q) {
|
|
6098
|
+
out = out.filter((it) => {
|
|
6099
|
+
const fields = [it.label, it.subtitle];
|
|
6100
|
+
if (searchableFields) fields.push(...searchableFields(it));
|
|
6101
|
+
return fields.some((f) => typeof f === "string" && f.toLowerCase().includes(q));
|
|
6102
|
+
});
|
|
6103
|
+
}
|
|
6104
|
+
if (sort.key && sort.dir) {
|
|
6105
|
+
const dir = sort.dir === "asc" ? 1 : -1;
|
|
6106
|
+
const getter = (it) => {
|
|
6107
|
+
if (sort.key === "__label") return it.label;
|
|
6108
|
+
if (sort.key === "__updated") return it.updatedAt;
|
|
6109
|
+
const col = lookupColumn.get(sort.key);
|
|
6110
|
+
return col?.sortBy ? col.sortBy(it) : void 0;
|
|
6111
|
+
};
|
|
6112
|
+
const norm = (v) => {
|
|
6113
|
+
if (v == null) return Number.POSITIVE_INFINITY;
|
|
6114
|
+
if (v instanceof Date) return v.getTime();
|
|
6115
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
6116
|
+
if (typeof v === "number") return v;
|
|
6117
|
+
return String(v).toLowerCase();
|
|
6118
|
+
};
|
|
6119
|
+
out = [...out].sort((a, b) => {
|
|
6120
|
+
const av = norm(getter(a));
|
|
6121
|
+
const bv = norm(getter(b));
|
|
6122
|
+
if (av < bv) return -1 * dir;
|
|
6123
|
+
if (av > bv) return 1 * dir;
|
|
6124
|
+
return 0;
|
|
6125
|
+
});
|
|
6126
|
+
}
|
|
6127
|
+
return out;
|
|
6128
|
+
}, [items, search, sort, lookupColumn, searchableFields]);
|
|
5840
6129
|
const [pendingDeleteId, setPendingDeleteId] = useState(null);
|
|
5841
6130
|
const pendingRecord = useMemo(
|
|
5842
6131
|
() => pendingDeleteId ? items.find((it) => (it.itemId ?? "") === pendingDeleteId) ?? null : null,
|
|
@@ -5851,9 +6140,62 @@ function ItemListView({
|
|
|
5851
6140
|
const toolbar = /* @__PURE__ */ jsxs("div", { className: "ra-item-toolbar", children: [
|
|
5852
6141
|
/* @__PURE__ */ jsxs("div", { className: "ra-item-toolbar-title", children: [
|
|
5853
6142
|
/* @__PURE__ */ jsx("h2", { className: "ra-display", style: { fontSize: "0.95rem", margin: 0 }, children: i18n.itemListTitle }),
|
|
5854
|
-
/* @__PURE__ */ jsx("span", { className: "ra-item-toolbar-count", children: items.length })
|
|
6143
|
+
/* @__PURE__ */ jsx("span", { className: "ra-item-toolbar-count", children: visibleItems.length === items.length ? items.length : `${visibleItems.length} / ${items.length}` })
|
|
5855
6144
|
] }),
|
|
5856
6145
|
/* @__PURE__ */ jsxs("div", { className: "ra-item-toolbar-actions", children: [
|
|
6146
|
+
searchable && items.length > 0 && /* @__PURE__ */ jsxs(
|
|
6147
|
+
"div",
|
|
6148
|
+
{
|
|
6149
|
+
className: "ra-item-search",
|
|
6150
|
+
style: {
|
|
6151
|
+
display: "inline-flex",
|
|
6152
|
+
alignItems: "center",
|
|
6153
|
+
gap: "6px",
|
|
6154
|
+
padding: "4px 8px",
|
|
6155
|
+
border: "1px solid hsl(var(--ra-border))",
|
|
6156
|
+
borderRadius: "6px",
|
|
6157
|
+
background: "hsl(var(--ra-surface))"
|
|
6158
|
+
},
|
|
6159
|
+
children: [
|
|
6160
|
+
/* @__PURE__ */ jsx(Search, { className: "w-3.5 h-3.5", "aria-hidden": "true", style: { opacity: 0.6 } }),
|
|
6161
|
+
/* @__PURE__ */ jsx(
|
|
6162
|
+
"input",
|
|
6163
|
+
{
|
|
6164
|
+
type: "text",
|
|
6165
|
+
value: search,
|
|
6166
|
+
onChange: (e) => setSearch(e.target.value),
|
|
6167
|
+
placeholder: `Search ${itemNoun}s`,
|
|
6168
|
+
"aria-label": `Search ${itemNoun}s`,
|
|
6169
|
+
style: {
|
|
6170
|
+
background: "transparent",
|
|
6171
|
+
border: 0,
|
|
6172
|
+
outline: "none",
|
|
6173
|
+
font: "inherit",
|
|
6174
|
+
fontSize: "12px",
|
|
6175
|
+
color: "inherit",
|
|
6176
|
+
width: "160px"
|
|
6177
|
+
}
|
|
6178
|
+
}
|
|
6179
|
+
),
|
|
6180
|
+
search && /* @__PURE__ */ jsx(
|
|
6181
|
+
"button",
|
|
6182
|
+
{
|
|
6183
|
+
type: "button",
|
|
6184
|
+
onClick: () => setSearch(""),
|
|
6185
|
+
"aria-label": "Clear search",
|
|
6186
|
+
style: {
|
|
6187
|
+
background: "transparent",
|
|
6188
|
+
border: 0,
|
|
6189
|
+
padding: 0,
|
|
6190
|
+
cursor: "pointer",
|
|
6191
|
+
color: "hsl(var(--ra-muted-text))"
|
|
6192
|
+
},
|
|
6193
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5", "aria-hidden": "true" })
|
|
6194
|
+
}
|
|
6195
|
+
)
|
|
6196
|
+
]
|
|
6197
|
+
}
|
|
6198
|
+
),
|
|
5857
6199
|
!renderItemList && /* @__PURE__ */ jsx(
|
|
5858
6200
|
ItemViewSwitcher,
|
|
5859
6201
|
{
|
|
@@ -5904,17 +6246,27 @@ function ItemListView({
|
|
|
5904
6246
|
)
|
|
5905
6247
|
}
|
|
5906
6248
|
);
|
|
6249
|
+
} else if (visibleItems.length === 0) {
|
|
6250
|
+
body = /* @__PURE__ */ jsx(
|
|
6251
|
+
EmptyState,
|
|
6252
|
+
{
|
|
6253
|
+
title: "No matches",
|
|
6254
|
+
body: `No ${itemNoun}s match "${search}".`
|
|
6255
|
+
}
|
|
6256
|
+
);
|
|
5907
6257
|
} else if (renderItemList) {
|
|
5908
|
-
body = renderItemList(
|
|
6258
|
+
body = renderItemList(visibleItems, guardedCtx);
|
|
5909
6259
|
} else if (view === "table") {
|
|
5910
6260
|
body = /* @__PURE__ */ jsx(
|
|
5911
6261
|
DefaultItemTable,
|
|
5912
6262
|
{
|
|
5913
|
-
items,
|
|
6263
|
+
items: visibleItems,
|
|
5914
6264
|
columns: itemColumns,
|
|
5915
6265
|
selectedId: guardedCtx.selectedId,
|
|
5916
6266
|
onOpen: guardedCtx.onOpen,
|
|
5917
6267
|
onDelete: guardedCtx.onDelete,
|
|
6268
|
+
sort,
|
|
6269
|
+
onToggleSort,
|
|
5918
6270
|
rowActions,
|
|
5919
6271
|
rowClipboard,
|
|
5920
6272
|
i18n
|
|
@@ -5924,7 +6276,7 @@ function ItemListView({
|
|
|
5924
6276
|
body = /* @__PURE__ */ jsx(
|
|
5925
6277
|
DefaultItemCards,
|
|
5926
6278
|
{
|
|
5927
|
-
items,
|
|
6279
|
+
items: visibleItems,
|
|
5928
6280
|
variant: view,
|
|
5929
6281
|
selectedId: guardedCtx.selectedId,
|
|
5930
6282
|
ctx: guardedCtx,
|
|
@@ -5960,6 +6312,29 @@ function ItemListView({
|
|
|
5960
6312
|
}
|
|
5961
6313
|
) : null,
|
|
5962
6314
|
/* @__PURE__ */ jsx("div", { className: "ra-item-list-body", children: body }),
|
|
6315
|
+
wantsFullSet && hasNextPage && /* @__PURE__ */ jsx(
|
|
6316
|
+
"div",
|
|
6317
|
+
{
|
|
6318
|
+
style: {
|
|
6319
|
+
padding: "6px 12px",
|
|
6320
|
+
fontSize: "11px",
|
|
6321
|
+
color: "hsl(var(--ra-muted-text))",
|
|
6322
|
+
borderTop: "1px solid hsl(var(--ra-border))",
|
|
6323
|
+
background: "hsl(var(--ra-muted) / 0.4)"
|
|
6324
|
+
},
|
|
6325
|
+
children: isFetchingNextPage ? `Loading more ${itemNoun}s to ${search ? "search" : "sort"}\u2026` : `${search ? "Searching" : "Sorting"} the ${items.length} ${itemNoun}s loaded so far. More available \u2014 keep loading for the full set.`
|
|
6326
|
+
}
|
|
6327
|
+
),
|
|
6328
|
+
onLoadMore && /* @__PURE__ */ jsx(
|
|
6329
|
+
LoadMoreFooter,
|
|
6330
|
+
{
|
|
6331
|
+
shown: items.length,
|
|
6332
|
+
total,
|
|
6333
|
+
hasNextPage: !!hasNextPage,
|
|
6334
|
+
isFetchingNextPage: !!isFetchingNextPage,
|
|
6335
|
+
onLoadMore
|
|
6336
|
+
}
|
|
6337
|
+
),
|
|
5963
6338
|
/* @__PURE__ */ jsx(
|
|
5964
6339
|
ConfirmDialog,
|
|
5965
6340
|
{
|
|
@@ -6048,7 +6423,11 @@ function SiblingRail({
|
|
|
6048
6423
|
itemNoun,
|
|
6049
6424
|
dirtyKeys,
|
|
6050
6425
|
errorKeys,
|
|
6051
|
-
i18n
|
|
6426
|
+
i18n,
|
|
6427
|
+
total,
|
|
6428
|
+
hasNextPage,
|
|
6429
|
+
isFetchingNextPage,
|
|
6430
|
+
onLoadMore
|
|
6052
6431
|
}) {
|
|
6053
6432
|
const ruleLabelLookup = useRuleLabelLookup();
|
|
6054
6433
|
const newLabel = i18n.newItem.includes("{noun}") ? i18n.newItem.replace("{noun}", itemNoun ?? "item") : i18n.newItem;
|
|
@@ -6128,6 +6507,16 @@ function SiblingRail({
|
|
|
6128
6507
|
) }, key);
|
|
6129
6508
|
}) })
|
|
6130
6509
|
] }),
|
|
6510
|
+
onLoadMore && /* @__PURE__ */ jsx(
|
|
6511
|
+
LoadMoreFooter,
|
|
6512
|
+
{
|
|
6513
|
+
shown: items.length,
|
|
6514
|
+
total,
|
|
6515
|
+
hasNextPage: !!hasNextPage,
|
|
6516
|
+
isFetchingNextPage: !!isFetchingNextPage,
|
|
6517
|
+
onLoadMore
|
|
6518
|
+
}
|
|
6519
|
+
),
|
|
6131
6520
|
onCreate && /* @__PURE__ */ jsx("div", { className: "ra-sibling-footer", children: /* @__PURE__ */ jsxs(
|
|
6132
6521
|
"button",
|
|
6133
6522
|
{
|
|
@@ -7296,12 +7685,17 @@ function RecordsAdminShell(props) {
|
|
|
7296
7685
|
}),
|
|
7297
7686
|
[props.SL, props.collectionId, props.appId, props.recordType]
|
|
7298
7687
|
);
|
|
7688
|
+
const recordChangeRef = useRef(null);
|
|
7689
|
+
const onRecordChange = useCallback((notice) => {
|
|
7690
|
+
recordChangeRef.current?.(notice);
|
|
7691
|
+
}, []);
|
|
7299
7692
|
return /* @__PURE__ */ jsx(
|
|
7300
7693
|
EditorSessionProvider,
|
|
7301
7694
|
{
|
|
7302
7695
|
ctx,
|
|
7303
7696
|
defaultValueFactory: props.defaultData,
|
|
7304
7697
|
hooks: props.hooks,
|
|
7698
|
+
onRecordChange,
|
|
7305
7699
|
onHookNotice: (notice) => {
|
|
7306
7700
|
console.warn(`[RecordsAdmin] ${notice.kind} hook failed`, notice.error);
|
|
7307
7701
|
try {
|
|
@@ -7314,7 +7708,7 @@ function RecordsAdminShell(props) {
|
|
|
7314
7708
|
} catch {
|
|
7315
7709
|
}
|
|
7316
7710
|
},
|
|
7317
|
-
children: /* @__PURE__ */ jsx(RecordsAdminShellInner, { ...props })
|
|
7711
|
+
children: /* @__PURE__ */ jsx(RecordsAdminShellInner, { ...props, recordChangeRef })
|
|
7318
7712
|
}
|
|
7319
7713
|
);
|
|
7320
7714
|
}
|
|
@@ -7347,7 +7741,8 @@ function RecordsAdminShellInner(props) {
|
|
|
7347
7741
|
recordActions,
|
|
7348
7742
|
icons: iconsOverride,
|
|
7349
7743
|
// Deep linking
|
|
7350
|
-
deepLink
|
|
7744
|
+
deepLink,
|
|
7745
|
+
recordChangeRef
|
|
7351
7746
|
} = props;
|
|
7352
7747
|
const {
|
|
7353
7748
|
show: showHeader,
|
|
@@ -7374,7 +7769,8 @@ function RecordsAdminShellInner(props) {
|
|
|
7374
7769
|
groupBy,
|
|
7375
7770
|
defaultGroupKey,
|
|
7376
7771
|
onGroupExpanded,
|
|
7377
|
-
density = "comfortable"
|
|
7772
|
+
density = "comfortable",
|
|
7773
|
+
pageSize: railPageSize
|
|
7378
7774
|
} = rail ?? {};
|
|
7379
7775
|
const {
|
|
7380
7776
|
tabs: editorTabs = "off",
|
|
@@ -7396,7 +7792,11 @@ function RecordsAdminShellInner(props) {
|
|
|
7396
7792
|
renderCard: renderItemCard,
|
|
7397
7793
|
renderEmpty: renderItemEmpty,
|
|
7398
7794
|
cardSize: itemCardSize = "md",
|
|
7399
|
-
railMode: collectionRailMode = "siblings"
|
|
7795
|
+
railMode: collectionRailMode = "siblings",
|
|
7796
|
+
toSummary: itemToSummary,
|
|
7797
|
+
pageSize: itemsPageSize,
|
|
7798
|
+
searchableFields: itemsSearchableFields,
|
|
7799
|
+
searchable: itemsSearchable
|
|
7400
7800
|
} = items ?? {};
|
|
7401
7801
|
const {
|
|
7402
7802
|
strategy: dirtyStrategy = "keep",
|
|
@@ -7456,6 +7856,21 @@ function RecordsAdminShellInner(props) {
|
|
|
7456
7856
|
[SL, collectionId, appId, recordType]
|
|
7457
7857
|
);
|
|
7458
7858
|
const queryClient = useQueryClient();
|
|
7859
|
+
useEffect(() => {
|
|
7860
|
+
recordChangeRef.current = (notice) => {
|
|
7861
|
+
if (notice.kind === "saved") {
|
|
7862
|
+
patchRecordIntoCaches(queryClient, notice.ctx, notice.record);
|
|
7863
|
+
} else {
|
|
7864
|
+
removeRecordFromCaches(queryClient, notice.ctx, notice.recordId);
|
|
7865
|
+
}
|
|
7866
|
+
queryClient.invalidateQueries({
|
|
7867
|
+
queryKey: scopeCountsQueryKey(notice.ctx.collectionId, notice.ctx.appId, notice.ctx.recordType)
|
|
7868
|
+
});
|
|
7869
|
+
};
|
|
7870
|
+
return () => {
|
|
7871
|
+
recordChangeRef.current = null;
|
|
7872
|
+
};
|
|
7873
|
+
}, [queryClient, recordChangeRef]);
|
|
7459
7874
|
const probe = useScopeProbe({ SL, collectionId });
|
|
7460
7875
|
const scopeCounts = useScopeCounts({ ctx });
|
|
7461
7876
|
const topLevelScopes = useMemo(() => {
|
|
@@ -7538,7 +7953,8 @@ function RecordsAdminShellInner(props) {
|
|
|
7538
7953
|
probeIsLoading: probe.isLoading,
|
|
7539
7954
|
selectedProductId,
|
|
7540
7955
|
drillTab,
|
|
7541
|
-
classify: classify3
|
|
7956
|
+
classify: classify3,
|
|
7957
|
+
pageSize: railPageSize
|
|
7542
7958
|
});
|
|
7543
7959
|
const {
|
|
7544
7960
|
search,
|
|
@@ -7618,7 +8034,9 @@ function RecordsAdminShellInner(props) {
|
|
|
7618
8034
|
// record across the whole collection (anchored, ruled, global) so
|
|
7619
8035
|
// host-supplied lifecycle grouping has the full picture.
|
|
7620
8036
|
includeAll: isCollection && activeScope === "all",
|
|
7621
|
-
enabled: isCollection
|
|
8037
|
+
enabled: isCollection,
|
|
8038
|
+
toSummary: itemToSummary,
|
|
8039
|
+
pageSize: itemsPageSize
|
|
7622
8040
|
});
|
|
7623
8041
|
useEffect(() => {
|
|
7624
8042
|
if (skipNextItemResetRef.current) {
|
|
@@ -7627,6 +8045,36 @@ function RecordsAdminShellInner(props) {
|
|
|
7627
8045
|
}
|
|
7628
8046
|
setSelectedItemId(null);
|
|
7629
8047
|
}, [editingScope?.raw]);
|
|
8048
|
+
const isLifecycleRailEarly = (activeScope === "all" || activeScope === "collection") && isCollection && !!groupBy;
|
|
8049
|
+
const lifecycleBucketLabel = useMemo(() => {
|
|
8050
|
+
if (!isLifecycleRailEarly || !selectedLifecycleKey || !groupBy) return null;
|
|
8051
|
+
for (const it of collectionItems.items) {
|
|
8052
|
+
const g = groupBy(it);
|
|
8053
|
+
if (g && g.key === selectedLifecycleKey) return g.label ?? null;
|
|
8054
|
+
}
|
|
8055
|
+
return null;
|
|
8056
|
+
}, [isLifecycleRailEarly, selectedLifecycleKey, groupBy, collectionItems.items]);
|
|
8057
|
+
const scopedCollectionItemsList = useMemo(() => {
|
|
8058
|
+
if (!isLifecycleRailEarly || !selectedLifecycleKey || !groupBy) return collectionItems.items;
|
|
8059
|
+
return collectionItems.items.filter((it) => {
|
|
8060
|
+
const g = groupBy(it);
|
|
8061
|
+
return g?.key === selectedLifecycleKey;
|
|
8062
|
+
});
|
|
8063
|
+
}, [isLifecycleRailEarly, selectedLifecycleKey, groupBy, collectionItems.items]);
|
|
8064
|
+
useEffect(() => {
|
|
8065
|
+
if (!isLifecycleRailEarly || !groupBy) return;
|
|
8066
|
+
if (selectedLifecycleKey || !selectedItemId) return;
|
|
8067
|
+
const row = collectionItems.items.find(
|
|
8068
|
+
(it) => it.itemId === selectedItemId || it.id === selectedItemId
|
|
8069
|
+
);
|
|
8070
|
+
if (!row) return;
|
|
8071
|
+
const g = groupBy(row);
|
|
8072
|
+
if (g?.key) setSelectedLifecycleKey(g.key);
|
|
8073
|
+
}, [isLifecycleRailEarly, groupBy, selectedItemId, selectedLifecycleKey, collectionItems.items, setSelectedLifecycleKey]);
|
|
8074
|
+
const scopedCollectionItems = useMemo(() => ({
|
|
8075
|
+
...collectionItems,
|
|
8076
|
+
items: scopedCollectionItemsList
|
|
8077
|
+
}), [collectionItems, scopedCollectionItemsList]);
|
|
7630
8078
|
const { lastAppliedDLRef } = useShellDeepLink({
|
|
7631
8079
|
deepLinkState,
|
|
7632
8080
|
editingScope,
|
|
@@ -7729,7 +8177,6 @@ function RecordsAdminShellInner(props) {
|
|
|
7729
8177
|
if (isCreate && isCollection && savedRecordId && isDraftId3(selectedItemId)) {
|
|
7730
8178
|
setSelectedItemId(savedRecordId);
|
|
7731
8179
|
}
|
|
7732
|
-
await refetchAll();
|
|
7733
8180
|
if (!isCollection && isCreate && activeScope === "collection") {
|
|
7734
8181
|
setSelectedRecordId(savedRecordId ?? null);
|
|
7735
8182
|
}
|
|
@@ -7748,7 +8195,6 @@ function RecordsAdminShellInner(props) {
|
|
|
7748
8195
|
setSelectedRecordId(null);
|
|
7749
8196
|
setDraftKind(null);
|
|
7750
8197
|
}
|
|
7751
|
-
await refetchAll();
|
|
7752
8198
|
setIsReconcilingRecordSelection(false);
|
|
7753
8199
|
}
|
|
7754
8200
|
});
|
|
@@ -7914,7 +8360,7 @@ function RecordsAdminShellInner(props) {
|
|
|
7914
8360
|
setSelectedItemId,
|
|
7915
8361
|
editingScope,
|
|
7916
8362
|
baseScopeRef,
|
|
7917
|
-
collectionItems,
|
|
8363
|
+
collectionItems: scopedCollectionItems,
|
|
7918
8364
|
queryClient,
|
|
7919
8365
|
ctx,
|
|
7920
8366
|
collectionId,
|
|
@@ -8404,8 +8850,11 @@ function RecordsAdminShellInner(props) {
|
|
|
8404
8850
|
status: b.items.length ? "configured" : "empty",
|
|
8405
8851
|
label: b.label,
|
|
8406
8852
|
subtitle: `${b.items.length} ${itemNoun}${b.items.length === 1 ? "" : "s"}`,
|
|
8407
|
-
// Tone
|
|
8408
|
-
|
|
8853
|
+
// Tone + icon drive the row's leading status icon (replacing the
|
|
8854
|
+
// generic green tick / dotted circle for these synthetic rows).
|
|
8855
|
+
// Hosts can override the icon entirely via `groupBy(...).icon`.
|
|
8856
|
+
iconHint: b.icon,
|
|
8857
|
+
toneHint: b.tone
|
|
8409
8858
|
});
|
|
8410
8859
|
}
|
|
8411
8860
|
return rows;
|
|
@@ -8627,7 +9076,7 @@ function RecordsAdminShellInner(props) {
|
|
|
8627
9076
|
!railHidden && /* @__PURE__ */ jsx("aside", { className: "border-r overflow-hidden flex flex-col", style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" }, children: isCollection && selectedItemId && collectionRailMode === "siblings" && ruleWizardStep === null ? /* @__PURE__ */ jsx(
|
|
8628
9077
|
SiblingRail,
|
|
8629
9078
|
{
|
|
8630
|
-
items:
|
|
9079
|
+
items: scopedCollectionItemsList,
|
|
8631
9080
|
selectedItemId,
|
|
8632
9081
|
isLoading: collectionItems.isLoading,
|
|
8633
9082
|
error: collectionItems.error,
|
|
@@ -8637,8 +9086,13 @@ function RecordsAdminShellInner(props) {
|
|
|
8637
9086
|
itemNoun: itemNounLabel,
|
|
8638
9087
|
dirtyKeys,
|
|
8639
9088
|
errorKeys,
|
|
8640
|
-
|
|
8641
|
-
|
|
9089
|
+
hasNextPage: !!collectionItems.hasNextPage,
|
|
9090
|
+
isFetchingNextPage: !!collectionItems.isFetchingNextPage,
|
|
9091
|
+
onLoadMore: () => {
|
|
9092
|
+
void collectionItems.fetchNextPage();
|
|
9093
|
+
},
|
|
9094
|
+
contextKind: isLifecycleRailEarly && lifecycleBucketLabel ? lifecycleBucketLabel : activeScope === "rule" ? "Rule" : activeScope === "product" ? "Product" : activeScope === "collection" ? "Global" : activeScope === "all" ? "All records" : activeScope === "variant" ? "Variant" : activeScope === "batch" ? "Batch" : activeScope === "facet" ? "Facet" : void 0,
|
|
9095
|
+
contextSummary: isLifecycleRailEarly && lifecycleBucketLabel ? `${scopedCollectionItemsList.length} ${itemNounLabel}${scopedCollectionItemsList.length === 1 ? "" : "s"}` : activeScope === "rule" ? activeRuleSummary : activeScope === "product" ? editorHeaderLabel ?? null : null,
|
|
8642
9096
|
i18n
|
|
8643
9097
|
}
|
|
8644
9098
|
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -8816,19 +9270,29 @@ function RecordsAdminShellInner(props) {
|
|
|
8816
9270
|
i18n
|
|
8817
9271
|
}
|
|
8818
9272
|
),
|
|
8819
|
-
isProductTab && !productPinned &&
|
|
8820
|
-
|
|
9273
|
+
isProductTab && !productPinned && /* @__PURE__ */ jsx(
|
|
9274
|
+
LoadMoreFooter,
|
|
8821
9275
|
{
|
|
8822
|
-
|
|
8823
|
-
|
|
9276
|
+
shown: leftItems.length,
|
|
9277
|
+
hasNextPage: !!productBrowse.hasNextPage,
|
|
9278
|
+
isFetchingNextPage: !!productBrowse.isFetchingNextPage,
|
|
9279
|
+
onLoadMore: () => {
|
|
8824
9280
|
void productBrowse.fetchNextPage();
|
|
8825
|
-
}
|
|
8826
|
-
disabled: productBrowse.isFetchingNextPage,
|
|
8827
|
-
className: "w-full text-xs py-2 rounded-md border transition-opacity disabled:opacity-50 hover:bg-[hsl(var(--ra-muted))]",
|
|
8828
|
-
style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
|
|
8829
|
-
children: productBrowse.isFetchingNextPage ? "Loading\u2026" : `Load more (${leftItems.length} shown)`
|
|
9281
|
+
}
|
|
8830
9282
|
}
|
|
8831
|
-
)
|
|
9283
|
+
),
|
|
9284
|
+
isRecordsTab && (!isCollection || !(isAllTab || isGlobalTab)) && /* @__PURE__ */ jsx(
|
|
9285
|
+
LoadMoreFooter,
|
|
9286
|
+
{
|
|
9287
|
+
shown: recordList.items.length,
|
|
9288
|
+
total: recordList.total,
|
|
9289
|
+
hasNextPage: !!recordList.hasNextPage,
|
|
9290
|
+
isFetchingNextPage: !!recordList.isFetchingNextPage,
|
|
9291
|
+
onLoadMore: () => {
|
|
9292
|
+
void recordList.fetchNextPage();
|
|
9293
|
+
}
|
|
9294
|
+
}
|
|
9295
|
+
)
|
|
8832
9296
|
] })
|
|
8833
9297
|
] }) })
|
|
8834
9298
|
] }) }),
|
|
@@ -8913,6 +9377,14 @@ function RecordsAdminShellInner(props) {
|
|
|
8913
9377
|
ruleSummary: activeRuleSummary,
|
|
8914
9378
|
rowActions: wrappedRecordActions,
|
|
8915
9379
|
rowClipboard,
|
|
9380
|
+
searchableFields: itemsSearchableFields,
|
|
9381
|
+
searchable: itemsSearchable,
|
|
9382
|
+
total: collectionItems.total,
|
|
9383
|
+
hasNextPage: !!collectionItems.hasNextPage,
|
|
9384
|
+
isFetchingNextPage: !!collectionItems.isFetchingNextPage,
|
|
9385
|
+
onLoadMore: () => {
|
|
9386
|
+
void collectionItems.fetchNextPage();
|
|
9387
|
+
},
|
|
8916
9388
|
i18n
|
|
8917
9389
|
}
|
|
8918
9390
|
),
|