@proveanything/smartlinks-utils-ui 0.9.1 → 0.9.2

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.
@@ -424,6 +424,14 @@ interface RecordsAdminI18n {
424
424
  rulesTabTooltip: string;
425
425
  /** Empty-state body shown inside the Rules tab when no rules exist yet. */
426
426
  rulesEmptyBody: string;
427
+ /**
428
+ * Lifecycle-hook failure toasts. `{message}` is replaced with the
429
+ * thrown error's message (or a generic fallback when none is provided).
430
+ */
431
+ hookBeforeSaveFailed: string;
432
+ hookAfterSaveFailed: string;
433
+ hookBeforeDeleteFailed: string;
434
+ hookAfterDeleteFailed: string;
427
435
  }
428
436
  declare const DEFAULT_I18N: RecordsAdminI18n;
429
437
 
@@ -555,6 +563,54 @@ declare function mergeIcons(override?: Partial<RecordsAdminIcons>): RecordsAdmin
555
563
  * explicit `headerIcon` > `icons.header.byRecordType[type]` > `icons.header.default`. */
556
564
  declare function pickHeaderIcon(icons: RecordsAdminIcons, recordType?: string): LucideIcon;
557
565
 
566
+ /** Common context fields passed to every lifecycle hook. */
567
+ interface RecordHookCtx {
568
+ collectionId: string;
569
+ appId: string;
570
+ recordType?: string;
571
+ /** Editing scope kind (`'global' | 'rule' | 'product' | …`). */
572
+ scope: ScopeKind;
573
+ /** Resolved scope ref string when available (e.g. `product:abc`). Most
574
+ * hosts ignore this; provided for parity with non-shell callers. */
575
+ targetRef?: string;
576
+ /** Always `true` from inside the shell; included for symmetry with
577
+ * future non-shell callers. */
578
+ admin: true;
579
+ }
580
+ interface SaveHookCtx<T = unknown> extends RecordHookCtx {
581
+ isCreate: boolean;
582
+ /** Record as it WILL be (in `beforeSave`) or HAS been (in `afterSave`)
583
+ * written. `id` is the resolved UUID when known, otherwise undefined. */
584
+ record: RecordSummary<T>;
585
+ /** Snapshot of the record's previous server state. Undefined on create. */
586
+ before?: RecordSummary<T>;
587
+ }
588
+ interface DeleteHookCtx<T = unknown> extends RecordHookCtx {
589
+ /** Snapshot of the record taken immediately before deletion. */
590
+ record: RecordSummary<T>;
591
+ }
592
+ /**
593
+ * Opt-in lifecycle hooks for `RecordsAdminShell`. All four are awaited.
594
+ *
595
+ * - `beforeSave` — return `false` (or throw) to cancel the write. Return
596
+ * a `Partial<T>` to merge into the record's `data` payload before it
597
+ * hits the server (e.g. inject computed `status`, normalised dates).
598
+ * - `afterSave` — runs after a successful write. Throwing surfaces a
599
+ * toast but does NOT roll back the server state.
600
+ * - `beforeDelete` — return `false` (or throw) to cancel the delete.
601
+ * - `afterDelete` — runs after a successful delete. Throwing surfaces a
602
+ * toast; the record is already gone.
603
+ *
604
+ * `data` is the only host-shaped portion of the payload — anchors and
605
+ * `facetRule` are owned by the shell, so `beforeSave` returns a partial
606
+ * of the typed `data` shape, not of the full SDK record input.
607
+ */
608
+ interface RecordsAdminShellHooks<T = unknown> {
609
+ beforeSave?: (ctx: SaveHookCtx<T>) => Promise<Partial<T> | false | void> | Partial<T> | false | void;
610
+ afterSave?: (ctx: SaveHookCtx<T>) => Promise<void> | void;
611
+ beforeDelete?: (ctx: DeleteHookCtx<T>) => Promise<false | void> | false | void;
612
+ afterDelete?: (ctx: DeleteHookCtx<T>) => Promise<void> | void;
613
+ }
558
614
  /**
559
615
  * Footer / banner action keys whose label and icon can be customised by
560
616
  * the host. `save`, `discard`, `delete` are wired today. `saveAll` /
@@ -714,6 +770,15 @@ interface RecordsAdminShellProps<TData = unknown> {
714
770
  unsaved?: UnsavedConfig<TData>;
715
771
  clipboard?: ClipboardConfig<TData>;
716
772
  actions?: ActionsConfig;
773
+ /**
774
+ * Opt-in awaited callbacks fired around record save/delete. See
775
+ * {@link RecordsAdminShellHooks} for the contract.
776
+ *
777
+ * Today these fire on the single-record paths (editor footer Save/Delete
778
+ * and the right-pane item-list trash). Bulk paths (CSV import, paste,
779
+ * Save-all) do NOT fire hooks yet — tracked as a follow-up.
780
+ */
781
+ hooks?: RecordsAdminShellHooks<TData>;
717
782
  /**
718
783
  * Mirror the shell's runtime state into URL params. Off by default. The
719
784
  * shell only owns `item`, `scope`, `view` — host platform params are
@@ -2134,6 +2199,12 @@ interface Props$5<T> {
2134
2199
  error: Error | null;
2135
2200
  ctx: ItemViewContext;
2136
2201
  itemNoun: string;
2202
+ /**
2203
+ * Optional one-line rule summary shown beneath the toolbar — used in
2204
+ * collection-mode when an item list is scoped to a specific rule, so the
2205
+ * admin sees which rule's items they're viewing without leaving the pane.
2206
+ */
2207
+ ruleSummary?: string | null;
2137
2208
  view: ItemView;
2138
2209
  views: ItemView[];
2139
2210
  onViewChange: (view: ItemView) => void;
@@ -2145,7 +2216,7 @@ interface Props$5<T> {
2145
2216
  cardSize?: 'sm' | 'md' | 'lg';
2146
2217
  i18n: RecordsAdminI18n;
2147
2218
  }
2148
- declare function ItemListView<T>({ items, isLoading, error, ctx, itemNoun, view, views, onViewChange, renderItemList, renderItemCard, renderItemEmpty, itemColumns, cardSize, i18n, }: Props$5<T>): react_jsx_runtime.JSX.Element;
2219
+ declare function ItemListView<T>({ items, isLoading, error, ctx, itemNoun, ruleSummary, view, views, onViewChange, renderItemList, renderItemCard, renderItemEmpty, itemColumns, cardSize, i18n, }: Props$5<T>): react_jsx_runtime.JSX.Element;
2149
2220
 
2150
2221
  interface Props$4 {
2151
2222
  options: ItemView[];
@@ -146,7 +146,11 @@ var DEFAULT_I18N = {
146
146
  subtitleConfigured: "Configured",
147
147
  subtitleInherited: "Inherited",
148
148
  rulesTabTooltip: "Use rules to scope records to a targeted audience or a subset of products.",
149
- rulesEmptyBody: "Create a rule to target a subset of products or a specific audience \u2014 for example, \u201Conly premium tier customers in Germany\u201D."
149
+ rulesEmptyBody: "Create a rule to target a subset of products or a specific audience \u2014 for example, \u201Conly premium tier customers in Germany\u201D.",
150
+ hookBeforeSaveFailed: "Couldn't save: {message}",
151
+ hookAfterSaveFailed: "Saved, but a follow-up step failed: {message}",
152
+ hookBeforeDeleteFailed: "Couldn't delete: {message}",
153
+ hookAfterDeleteFailed: "Deleted, but a follow-up step failed: {message}"
150
154
  };
151
155
 
152
156
  // src/components/RecordsAdmin/types/presentation.ts
@@ -1819,7 +1823,8 @@ function useShellNavigation(args) {
1819
1823
  deepLinkState,
1820
1824
  onTelemetry,
1821
1825
  onBeforeDelete,
1822
- generateItemId
1826
+ generateItemId,
1827
+ hooks
1823
1828
  } = args;
1824
1829
  const buildItemUrlValue = useCallback((id) => {
1825
1830
  if (!baseScopeRef && id.startsWith("item:")) return id;
@@ -1904,6 +1909,34 @@ function useShellNavigation(args) {
1904
1909
  const ok = await onBeforeDelete(editingScope ?? parseRef(""));
1905
1910
  if (!ok) return;
1906
1911
  }
1912
+ const row = collectionItems.items.find(
1913
+ (it) => it.itemId === itemId || it.id === itemId
1914
+ );
1915
+ const hookCtxBase = {
1916
+ collectionId: ctx.collectionId,
1917
+ appId: ctx.appId,
1918
+ recordType: ctx.recordType,
1919
+ scope: editingScope?.kind ?? "collection",
1920
+ targetRef: editingScope?.raw || void 0,
1921
+ admin: true
1922
+ };
1923
+ const recordSummary = row ?? {
1924
+ id: itemId,
1925
+ ref: editingScope?.raw ?? "",
1926
+ scope: editingScope ?? parseRef(""),
1927
+ data: null,
1928
+ status: "configured",
1929
+ label: itemId
1930
+ };
1931
+ if (hooks?.beforeDelete) {
1932
+ try {
1933
+ const result = await hooks.beforeDelete({ ...hookCtxBase, record: recordSummary });
1934
+ if (result === false) return;
1935
+ } catch (err) {
1936
+ console.warn("[RecordsAdmin] item beforeDelete hook cancelled delete", err);
1937
+ return;
1938
+ }
1939
+ }
1907
1940
  try {
1908
1941
  const { removeRecord: removeRecord2 } = await import('../../records-AYYQSP7E.js');
1909
1942
  await removeRecord2(ctx, itemId);
@@ -1911,6 +1944,13 @@ function useShellNavigation(args) {
1911
1944
  if (selectedItemId === itemId) setSelectedItemId(null);
1912
1945
  if (selectedItemId === itemId) deepLinkState.emit({ recordId: null }, "record.close");
1913
1946
  collectionItems.refetch();
1947
+ if (hooks?.afterDelete) {
1948
+ try {
1949
+ await hooks.afterDelete({ ...hookCtxBase, record: recordSummary });
1950
+ } catch (err) {
1951
+ console.warn("[RecordsAdmin] item afterDelete hook failed", err);
1952
+ }
1953
+ }
1914
1954
  } catch (err) {
1915
1955
  console.error("[RecordsAdminShell] item delete failed", err);
1916
1956
  }
@@ -1925,7 +1965,8 @@ function useShellNavigation(args) {
1925
1965
  collectionItems,
1926
1966
  deepLinkState,
1927
1967
  editingScope,
1928
- setSelectedItemId
1968
+ setSelectedItemId,
1969
+ hooks
1929
1970
  ]);
1930
1971
  const itemViewCtx = useMemo(() => ({
1931
1972
  onOpen: onItemOpen,
@@ -2231,6 +2272,25 @@ var createEditorStore = () => {
2231
2272
  const listeners = /* @__PURE__ */ new Set();
2232
2273
  let cachedList = [];
2233
2274
  let nextOrder = 0;
2275
+ let hooksBundle = null;
2276
+ let notifier = null;
2277
+ const buildRecordSummary = (entry, value) => ({
2278
+ id: entry.recordId ?? null,
2279
+ ref: entry.spec.scope.raw,
2280
+ scope: entry.spec.scope,
2281
+ label: entry.label,
2282
+ status: "configured",
2283
+ data: value,
2284
+ facetRule: entry.facetRule
2285
+ });
2286
+ const buildHookCtxBase = (entry) => ({
2287
+ collectionId: entry.saveSpec.ctx.collectionId,
2288
+ appId: entry.saveSpec.ctx.appId,
2289
+ recordType: entry.saveSpec.ctx.recordType,
2290
+ scope: entry.spec.scope.kind,
2291
+ targetRef: entry.spec.scope.raw || void 0,
2292
+ admin: true
2293
+ });
2234
2294
  const recompute = () => {
2235
2295
  cachedList = Array.from(map.values()).sort((a, b) => a.order - b.order);
2236
2296
  };
@@ -2384,10 +2444,35 @@ var createEditorStore = () => {
2384
2444
  const entry = map.get(editorId);
2385
2445
  if (!entry) return;
2386
2446
  if (entry.status !== "dirty" && entry.status !== "error") return;
2387
- const persistedValue = entry.value;
2447
+ let persistedValue = entry.value;
2388
2448
  const persistedFacetRule = entry.facetRule;
2389
2449
  const { ctx, anchors } = entry.saveSpec;
2390
2450
  const spec = entry.spec;
2451
+ const isCreate = !(entry.recordId && entry.source === "self") || !!spec.createMode;
2452
+ if (hooksBundle?.beforeSave) {
2453
+ try {
2454
+ const hookCtx = {
2455
+ ...buildHookCtxBase(entry),
2456
+ isCreate,
2457
+ record: buildRecordSummary(entry, persistedValue),
2458
+ before: !isCreate ? buildRecordSummary(entry, entry.baseline) : void 0
2459
+ };
2460
+ const result = await hooksBundle.beforeSave(hookCtx);
2461
+ if (result === false) {
2462
+ return;
2463
+ }
2464
+ if (result && typeof result === "object") {
2465
+ persistedValue = persistedValue && typeof persistedValue === "object" ? { ...persistedValue, ...result } : result;
2466
+ }
2467
+ } catch (err) {
2468
+ notifier?.({ kind: "hook-before-save", error: err, recordLabel: entry.label });
2469
+ update(editorId, (e) => {
2470
+ e.status = "error";
2471
+ e.error = err;
2472
+ });
2473
+ return;
2474
+ }
2475
+ }
2391
2476
  update(editorId, (e) => {
2392
2477
  e.status = "saving";
2393
2478
  e.error = void 0;
@@ -2424,6 +2509,9 @@ var createEditorStore = () => {
2424
2509
  nextRecordId = upserted.record?.id ?? nextRecordId;
2425
2510
  }
2426
2511
  update(editorId, (e) => {
2512
+ if (persistedValue !== entry.value) {
2513
+ e.value = cloneDeep(persistedValue);
2514
+ }
2427
2515
  e.baseline = cloneDeep(e.value);
2428
2516
  e.baselineFacetRule = e.facetRule;
2429
2517
  e.recordId = nextRecordId;
@@ -2431,6 +2519,21 @@ var createEditorStore = () => {
2431
2519
  e.status = "saved";
2432
2520
  e.error = void 0;
2433
2521
  });
2522
+ if (hooksBundle?.afterSave) {
2523
+ const refreshed = map.get(editorId);
2524
+ const hookCtx = {
2525
+ ...buildHookCtxBase(refreshed ?? entry),
2526
+ isCreate,
2527
+ record: buildRecordSummary(refreshed ?? entry, persistedValue),
2528
+ before: !isCreate ? buildRecordSummary(entry, entry.baseline) : void 0
2529
+ };
2530
+ try {
2531
+ await hooksBundle.afterSave(hookCtx);
2532
+ } catch (err) {
2533
+ notifier?.({ kind: "hook-after-save", error: err, recordLabel: entry.label });
2534
+ console.warn("[RecordsAdmin] afterSave hook failed", err);
2535
+ }
2536
+ }
2434
2537
  } catch (err) {
2435
2538
  update(editorId, (e) => {
2436
2539
  e.status = "error";
@@ -2452,9 +2555,35 @@ var createEditorStore = () => {
2452
2555
  if (!entry) return;
2453
2556
  if (entry.source !== "self") return;
2454
2557
  if (!entry.recordId) return;
2558
+ if (hooksBundle?.beforeDelete) {
2559
+ try {
2560
+ const hookCtx = {
2561
+ ...buildHookCtxBase(entry),
2562
+ record: buildRecordSummary(entry, entry.value)
2563
+ };
2564
+ const result = await hooksBundle.beforeDelete(hookCtx);
2565
+ if (result === false) return;
2566
+ } catch (err) {
2567
+ notifier?.({ kind: "hook-before-delete", error: err, recordLabel: entry.label });
2568
+ console.warn("[RecordsAdmin] beforeDelete hook cancelled delete", err);
2569
+ return;
2570
+ }
2571
+ }
2572
+ const deletedSnapshot = hooksBundle?.afterDelete ? {
2573
+ ...buildHookCtxBase(entry),
2574
+ record: buildRecordSummary(entry, entry.value)
2575
+ } : null;
2455
2576
  await removeRecord(entry.saveSpec.ctx, entry.recordId);
2456
2577
  map.delete(editorId);
2457
2578
  emit();
2579
+ if (hooksBundle?.afterDelete && deletedSnapshot) {
2580
+ try {
2581
+ await hooksBundle.afterDelete(deletedSnapshot);
2582
+ } catch (err) {
2583
+ notifier?.({ kind: "hook-after-delete", error: err, recordLabel: entry.label });
2584
+ console.warn("[RecordsAdmin] afterDelete hook failed", err);
2585
+ }
2586
+ }
2458
2587
  },
2459
2588
  enforcePoolLimit(max, exceptEditorId) {
2460
2589
  const alive = Array.from(map.values());
@@ -2475,6 +2604,12 @@ var createEditorStore = () => {
2475
2604
  return () => {
2476
2605
  listeners.delete(listener);
2477
2606
  };
2607
+ },
2608
+ setHooks(hooks) {
2609
+ hooksBundle = hooks ?? null;
2610
+ },
2611
+ setNotifier(notify2) {
2612
+ notifier = notify2 ?? null;
2478
2613
  }
2479
2614
  };
2480
2615
  };
@@ -2496,10 +2631,14 @@ var EditorSessionProvider = ({
2496
2631
  ctx,
2497
2632
  children,
2498
2633
  maxOpenEditors = 8,
2499
- defaultValueFactory
2634
+ defaultValueFactory,
2635
+ hooks,
2636
+ onHookNotice
2500
2637
  }) => {
2501
2638
  const storeRef = useRef(null);
2502
2639
  if (!storeRef.current) storeRef.current = createEditorStore();
2640
+ storeRef.current.setHooks(hooks ?? null);
2641
+ storeRef.current.setNotifier(onHookNotice ?? null);
2503
2642
  const currentRef = useRef(void 0);
2504
2643
  const listenersRef = useRef(/* @__PURE__ */ new Set());
2505
2644
  const subscribeCurrent = useCallback((cb) => {
@@ -4100,8 +4239,15 @@ function RecordEditor({
4100
4239
  const DeleteIcon = actionIcons?.delete;
4101
4240
  const showInherited = ctx.source === "inherited";
4102
4241
  const showEmpty = ctx.source === "empty";
4242
+ const hasBreadcrumb = (() => {
4243
+ const s = ctx.scope;
4244
+ return Boolean(s?.facetId || s?.productId || s?.variantId || s?.batchId);
4245
+ })();
4246
+ const hasLeftContent = Boolean(headerLabel) || hasBreadcrumb;
4247
+ const hasRightContent = showInherited || showEmpty || Boolean(headerMeta) || Boolean(bulkActions) || Boolean(targetingControl);
4248
+ const showHeader = hasLeftContent || hasRightContent;
4103
4249
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
4104
- /* @__PURE__ */ jsxs(
4250
+ showHeader && /* @__PURE__ */ jsxs(
4105
4251
  "header",
4106
4252
  {
4107
4253
  className: "sticky top-0 z-40 px-5 py-3 border-b flex items-start justify-between gap-3",
@@ -5309,6 +5455,7 @@ function ItemListView({
5309
5455
  error,
5310
5456
  ctx,
5311
5457
  itemNoun,
5458
+ ruleSummary,
5312
5459
  view,
5313
5460
  views,
5314
5461
  onViewChange,
@@ -5406,6 +5553,27 @@ function ItemListView({
5406
5553
  }
5407
5554
  return /* @__PURE__ */ jsxs("div", { className: "ra-item-list", children: [
5408
5555
  toolbar,
5556
+ ruleSummary ? /* @__PURE__ */ jsxs(
5557
+ "div",
5558
+ {
5559
+ className: "ra-item-rule-summary",
5560
+ style: {
5561
+ padding: "6px 12px",
5562
+ fontSize: "11px",
5563
+ color: "hsl(var(--ra-muted-text))",
5564
+ borderBottom: "1px solid hsl(var(--ra-border))",
5565
+ background: "hsl(var(--ra-muted) / 0.4)",
5566
+ display: "flex",
5567
+ alignItems: "center",
5568
+ gap: "6px"
5569
+ },
5570
+ children: [
5571
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 500, color: "hsl(var(--ra-text))" }, children: "Rule" }),
5572
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
5573
+ /* @__PURE__ */ jsx("span", { children: ruleSummary })
5574
+ ]
5575
+ }
5576
+ ) : null,
5409
5577
  /* @__PURE__ */ jsx("div", { className: "ra-item-list-body", children: body })
5410
5578
  ] });
5411
5579
  }
@@ -6730,6 +6898,19 @@ function RecordsAdminShell(props) {
6730
6898
  {
6731
6899
  ctx,
6732
6900
  defaultValueFactory: props.defaultData,
6901
+ hooks: props.hooks,
6902
+ onHookNotice: (notice) => {
6903
+ console.warn(`[RecordsAdmin] ${notice.kind} hook failed`, notice.error);
6904
+ try {
6905
+ props.onTelemetry?.({
6906
+ type: "record.hook.error",
6907
+ recordType: props.recordType,
6908
+ kind: notice.kind,
6909
+ message: notice.error instanceof Error ? notice.error.message : String(notice.error)
6910
+ });
6911
+ } catch {
6912
+ }
6913
+ },
6733
6914
  children: /* @__PURE__ */ jsx(RecordsAdminShellInner, { ...props })
6734
6915
  }
6735
6916
  );
@@ -7290,7 +7471,8 @@ function RecordsAdminShellInner(props) {
7290
7471
  deepLinkState,
7291
7472
  onTelemetry,
7292
7473
  onBeforeDelete,
7293
- generateItemId
7474
+ generateItemId,
7475
+ hooks: props.hooks
7294
7476
  });
7295
7477
  const renderEditorWithPreview = () => {
7296
7478
  if (!editingTargetScope) return null;
@@ -7497,12 +7679,13 @@ function RecordsAdminShellInner(props) {
7497
7679
  if (groupBy) return groupBy;
7498
7680
  if (isAllTab) return void 0;
7499
7681
  if (!isRuleTab) return void 0;
7682
+ if (isCollection) return void 0;
7500
7683
  return (record) => {
7501
7684
  const hash = ruleHash(record.facetRule);
7502
7685
  if (!hash) return null;
7503
7686
  return { key: `rule:${hash}`, label: summariseRule(record.facetRule) };
7504
7687
  };
7505
- }, [groupBy, isRuleTab, isAllTab]);
7688
+ }, [groupBy, isRuleTab, isAllTab, isCollection]);
7506
7689
  const [bulkRuleEditTarget, setBulkRuleEditTarget] = useState(null);
7507
7690
  const ruleCatalogue = useMemo(() => {
7508
7691
  const buckets = /* @__PURE__ */ new Map();
@@ -7601,6 +7784,32 @@ function RecordsAdminShellInner(props) {
7601
7784
  () => isRuleTab ? applyRuleFilters(recordList.items, ruleFilters) : recordList.items,
7602
7785
  [isRuleTab, recordList.items, ruleFilters]
7603
7786
  );
7787
+ const collectionRuleRailItems = useMemo(() => {
7788
+ if (!isRuleTab || !isCollection) return filteredRuleItems;
7789
+ const buckets = /* @__PURE__ */ new Map();
7790
+ for (const item of filteredRuleItems) {
7791
+ const hash = ruleHash(item.facetRule);
7792
+ if (!hash) continue;
7793
+ const existing = buckets.get(hash);
7794
+ if (existing) {
7795
+ existing.count += 1;
7796
+ } else {
7797
+ buckets.set(hash, { rep: item, count: 1 });
7798
+ }
7799
+ }
7800
+ return Array.from(buckets.values()).map(({ rep, count }) => ({
7801
+ ...rep,
7802
+ label: summariseRule(rep.facetRule),
7803
+ subtitle: `${count} ${itemNoun}${count === 1 ? "" : "s"}`
7804
+ }));
7805
+ }, [isRuleTab, isCollection, filteredRuleItems, itemNoun]);
7806
+ const activeRuleSummary = useMemo(() => {
7807
+ if (!isRuleTab || !isCollection) return null;
7808
+ if (!selectedRecordId || isDraftId3(selectedRecordId)) return null;
7809
+ const hit = recordList.items.find((it) => it.id === selectedRecordId);
7810
+ if (!hit?.facetRule) return null;
7811
+ return summariseRule(hit.facetRule);
7812
+ }, [isRuleTab, isCollection, selectedRecordId, recordList.items]);
7604
7813
  const applyFacetBrowseFilter = useCallback(
7605
7814
  (items2) => {
7606
7815
  if (!facetBrowseFilter) return items2;
@@ -7648,7 +7857,9 @@ function RecordsAdminShellInner(props) {
7648
7857
  }),
7649
7858
  [i18n.itemsAllLabel, collectionItems.items.length, itemNoun]
7650
7859
  );
7651
- const leftItems = isProductTab ? productListItems : isRuleTab ? applyFacetBrowseFilter(filteredRuleItems) : (isGlobalTab || isAllTab) && isCollection ? [collectionGlobalAllRow] : isRecordsTab ? applyFacetBrowseFilter(recordList.items) : [];
7860
+ const leftItems = isProductTab ? productListItems : isRuleTab ? applyFacetBrowseFilter(
7861
+ isCollection ? collectionRuleRailItems : filteredRuleItems
7862
+ ) : (isGlobalTab || isAllTab) && isCollection ? [collectionGlobalAllRow] : isRecordsTab ? applyFacetBrowseFilter(recordList.items) : [];
7652
7863
  const leftLoading = isProductTab ? !productPinned && productBrowse.isLoading : isRecordsTab ? recordList.isLoading || probe.isLoading : false;
7653
7864
  const leftError = isProductTab ? productBrowse.error : isRecordsTab ? recordList.error : null;
7654
7865
  const leftSelectedId = isProductTab ? void 0 : selectedRecordId && selectedRecordId !== DRAFT_ID3 ? selectedRecordId : void 0;
@@ -8089,6 +8300,7 @@ function RecordsAdminShellInner(props) {
8089
8300
  renderItemEmpty,
8090
8301
  itemColumns,
8091
8302
  cardSize: itemCardSize,
8303
+ ruleSummary: activeRuleSummary,
8092
8304
  i18n
8093
8305
  }
8094
8306
  ),