@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.
@@ -1,4 +1,4 @@
1
- import { AdminPageHeader } from '../../chunk-2MW54ZVG.js';
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, useMemo, useRef, 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, CopyPlus, AlertCircle, Undo2, Save, Loader2, XCircle, ArrowRight, Globe2, Check, Settings2 } from 'lucide-react';
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
- var RT_KEY = (recordType) => recordType ?? "_default";
416
- var lsKey = (appId, recordType) => `ra:intro:${appId}:${RT_KEY(recordType)}`;
417
- var useIntroDismissed = (SL, collectionId, appId, recordType) => {
418
- const [dismissed, setDismissed] = useState(() => {
419
- try {
420
- return localStorage.getItem(lsKey(appId, recordType)) === "1";
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 StatusIcon = ({ source, status, className, size = "1.05rem", label }) => {
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: i18n.itemColumnLabel }),
5634
- /* @__PURE__ */ jsx("th", { style: { width: "12rem" }, children: i18n.itemColumnUpdated })
5635
- ] }) : cols.map((c) => /* @__PURE__ */ jsx("th", { style: { width: c.width, textAlign: c.align ?? "left" }, children: c.header }, c.key)),
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(items, guardedCtx);
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 surfaces as a chip; we reuse `badges` for that.
8408
- badges: b.tone && b.tone !== "default" ? [{ label: b.tone, tone: b.tone === "success" ? "success" : b.tone === "warning" ? "warning" : b.tone === "danger" ? "danger" : "neutral" }] : void 0
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: collectionItems.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
- contextKind: activeScope === "rule" ? "Rule" : activeScope === "product" ? "Product" : activeScope === "collection" ? "Global" : activeScope === "all" ? "All records" : activeScope === "variant" ? "Variant" : activeScope === "batch" ? "Batch" : activeScope === "facet" ? "Facet" : void 0,
8641
- contextSummary: activeScope === "rule" ? activeRuleSummary : activeScope === "product" ? editorHeaderLabel ?? null : null,
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 && productBrowse.hasNextPage && /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
8820
- "button",
9273
+ isProductTab && !productPinned && /* @__PURE__ */ jsx(
9274
+ LoadMoreFooter,
8821
9275
  {
8822
- type: "button",
8823
- onClick: () => {
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
  ),