@proveanything/smartlinks-utils-ui 0.9.2 → 0.9.4

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,11 +1,12 @@
1
+ import { assertComponentStylesLoaded } from '../../chunk-OLYC54YT.js';
1
2
  import '../../chunk-5UQQYXCX.js';
2
3
  import { FacetRuleEditor } from '../../chunk-JMCV6FOW.js';
3
4
  import { useFacets } from '../../chunk-4LHF5JB7.js';
4
5
  import { cn } from '../../chunk-L7FQ52F5.js';
5
- import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-KFKVGUUP.js';
6
- export { bulkDelete, bulkUpsert, createRecord, getRecordById, listRecords, matchRecords, parsedRefToScope, parsedRefToTarget, removeRecord, restoreRecord, scopesEqual, upsertRecord } from '../../chunk-KFKVGUUP.js';
7
- import { createContext, useState, useEffect, useCallback, useMemo, useRef, useContext, useSyncExternalStore, createElement, useId } from 'react';
8
- 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, Rows3, ChevronRight, Eraser, ClipboardPaste, Box, X, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, Search, CornerDownLeft, Circle, AlertCircle, Undo2, Save, Loader2, XCircle, ArrowRight, BookOpen, Globe2, Target, Check, Settings2 } from 'lucide-react';
6
+ import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-KA4MKRHL.js';
7
+ export { bulkDelete, bulkUpsert, createRecord, getRecordById, listRecords, matchRecords, parsedRefToScope, parsedRefToTarget, removeRecord, restoreRecord, scopesEqual, upsertRecord } from '../../chunk-KA4MKRHL.js';
8
+ import { createContext, useState, useEffect, useCallback, useMemo, useRef, useContext, useSyncExternalStore, useLayoutEffect, createElement, useId } from 'react';
9
+ 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, Rows3, ChevronRight, Eraser, ClipboardPaste, Box, X, Search, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, CornerDownLeft, Circle, AlertCircle, Undo2, Save, Loader2, XCircle, ArrowRight, BookOpen, Globe2, Target, Check, Settings2 } from 'lucide-react';
9
10
  import { useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
10
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
12
  import { createPortal } from 'react-dom';
@@ -267,10 +268,12 @@ var ruleHash = (rule) => {
267
268
  if (!norm) return null;
268
269
  return fnv1a(JSON.stringify(norm));
269
270
  };
270
- var summariseRule = (rule) => {
271
+ var summariseRule = (rule, lookup) => {
271
272
  const norm = normaliseRule(rule);
272
273
  if (!norm) return "No rule";
273
- return norm.all.map((c) => `${c.facetKey}=${c.anyOf.join(",")}`).join(" \xB7 ");
274
+ const fLabel = (k) => lookup?.facetLabel?.(k) ?? k;
275
+ const vLabel = (k, v) => lookup?.valueLabel?.(k, v) ?? v;
276
+ return norm.all.map((c) => `${fLabel(c.facetKey)}: ${c.anyOf.map((v) => vLabel(c.facetKey, v)).join(", ")}`).join(" \xB7 ");
274
277
  };
275
278
  var rulesEqual = (a, b) => ruleHash(a) === ruleHash(b);
276
279
 
@@ -280,18 +283,6 @@ var resolveRecord = async (args) => {
280
283
  const editingScope = parsedRefToScope(args.target);
281
284
  const result = await matchRecords(args.ctx, target, { strategy: "all" }).catch(() => null);
282
285
  const entries = result?.data ?? [];
283
- console.info("[RecordsAdmin/resolveRecord]", {
284
- editingScope,
285
- target,
286
- matchCount: entries.length,
287
- winnerAnchors: entries[0] && {
288
- productId: entries[0].productId,
289
- variantId: entries[0].variantId,
290
- batchId: entries[0].batchId,
291
- proofId: entries[0].proofId
292
- },
293
- winnerRef: entries[0]?.ref
294
- });
295
286
  if (entries.length === 0) {
296
287
  return { data: null, source: "empty" };
297
288
  }
@@ -305,16 +296,6 @@ var resolveRecord = async (args) => {
305
296
  },
306
297
  editingScope
307
298
  );
308
- console.info("[RecordsAdmin/resolveRecord] classification", {
309
- winnerIsSelf,
310
- winnerAnchors: {
311
- productId: winner.productId,
312
- variantId: winner.variantId,
313
- batchId: winner.batchId,
314
- proofId: winner.proofId
315
- },
316
- editingScope
317
- });
318
299
  if (winnerIsSelf) {
319
300
  const parent = entries[1];
320
301
  return {
@@ -1833,11 +1814,6 @@ function useShellNavigation(args) {
1833
1814
  const onItemOpen = useCallback((itemId) => {
1834
1815
  if (!isCollection) return;
1835
1816
  void runWithGuard(() => {
1836
- console.info("[RecordsAdminShell] item open requested", {
1837
- itemId,
1838
- scopeRef: baseScopeRef,
1839
- previousSelectedItemId: selectedItemId
1840
- });
1841
1817
  if (!isDraftId(itemId)) {
1842
1818
  const row = collectionItems.items.find((it) => it.itemId === itemId || it.id === itemId);
1843
1819
  if (row && row.data !== void 0) {
@@ -1938,7 +1914,7 @@ function useShellNavigation(args) {
1938
1914
  }
1939
1915
  }
1940
1916
  try {
1941
- const { removeRecord: removeRecord2 } = await import('../../records-AYYQSP7E.js');
1917
+ const { removeRecord: removeRecord2 } = await import('../../records-4NN757SG.js');
1942
1918
  await removeRecord2(ctx, itemId);
1943
1919
  onTelemetry?.({ type: "item.delete", recordType, scopeRef: baseScopeRef, itemId });
1944
1920
  if (selectedItemId === itemId) setSelectedItemId(null);
@@ -2113,16 +2089,30 @@ var useShellDeepLink = (args) => {
2113
2089
  setSelectedVariantId,
2114
2090
  selectedBatchId,
2115
2091
  setSelectedBatchId,
2116
- setFacetBrowseFilter
2092
+ setFacetBrowseFilter,
2093
+ isCollection,
2094
+ setSelectedItemId,
2095
+ collectionItems,
2096
+ recordListItems,
2097
+ recordListLoading,
2098
+ skipNextItemResetRef
2117
2099
  } = args;
2118
2100
  const lastAppliedDLRef = useRef("");
2119
2101
  const [pendingDeepLinkRecordId, setPendingDeepLinkRecordId] = useState(null);
2102
+ const preserveInitialRecordIdRef = useRef(!!deepLinkState.urlState.recordId);
2103
+ const warnedMissingRef = useRef(/* @__PURE__ */ new Set());
2120
2104
  useEffect(() => {
2121
2105
  if (!deepLinkState.enabled) return;
2122
2106
  if (selectedItemId) return;
2123
- deepLinkState.emit({ scope: editingScope?.raw ?? null, recordId: null }, "scope");
2124
- lastAppliedDLRef.current = `${""}|${editingScope?.raw ?? ""}|${itemView}`;
2125
- }, [deepLinkState.enabled, editingScope?.raw, selectedItemId, itemView]);
2107
+ const echoView = deepLinkState.urlState.view ?? "";
2108
+ const currentDeepLinkedRecordId = pendingDeepLinkRecordId ?? deepLinkState.urlState.recordId ?? null;
2109
+ const preservingPendingRecordId = preserveInitialRecordIdRef.current && !!currentDeepLinkedRecordId;
2110
+ deepLinkState.emit(
2111
+ preservingPendingRecordId ? { scope: editingScope?.raw ?? null } : { scope: editingScope?.raw ?? null, recordId: null },
2112
+ "scope"
2113
+ );
2114
+ lastAppliedDLRef.current = `${preservingPendingRecordId ? currentDeepLinkedRecordId ?? "" : ""}|${editingScope?.raw ?? ""}|${echoView}`;
2115
+ }, [deepLinkState.enabled, deepLinkState.urlState.recordId, editingScope?.raw, selectedItemId, itemView, pendingDeepLinkRecordId]);
2126
2116
  useEffect(() => {
2127
2117
  if (!deepLinkState.enabled) return;
2128
2118
  const { recordId, scope, view } = deepLinkState.urlState;
@@ -2151,6 +2141,7 @@ var useShellDeepLink = (args) => {
2151
2141
  if (selectedRecordId !== null) setSelectedRecordId(null);
2152
2142
  }
2153
2143
  setPendingDeepLinkRecordId(recordId ?? null);
2144
+ if (recordId) preserveInitialRecordIdRef.current = true;
2154
2145
  if (!recordId && selectedItemId !== null) {
2155
2146
  console.info("[RecordsAdminShell] preserving selected item during restore without recordId", {
2156
2147
  selectedItemId,
@@ -2159,6 +2150,57 @@ var useShellDeepLink = (args) => {
2159
2150
  });
2160
2151
  }
2161
2152
  }, [deepLinkState.enabled, deepLinkState.urlState]);
2153
+ useEffect(() => {
2154
+ const pending = pendingDeepLinkRecordId;
2155
+ if (!pending) return;
2156
+ if (isCollection) {
2157
+ const hit2 = collectionItems.items.find((it) => it.id === pending || it.itemId === pending);
2158
+ if (hit2) {
2159
+ skipNextItemResetRef.current = true;
2160
+ setSelectedItemId(pending);
2161
+ preserveInitialRecordIdRef.current = false;
2162
+ setPendingDeepLinkRecordId(null);
2163
+ return;
2164
+ }
2165
+ if (!collectionItems.isLoading) {
2166
+ if (!warnedMissingRef.current.has(pending)) {
2167
+ warnedMissingRef.current.add(pending);
2168
+ console.warn("[RecordsAdminShell] deep-linked recordId not found in collection items \u2014 clearing pending selection", {
2169
+ recordId: pending,
2170
+ scope: editingScope?.raw ?? null
2171
+ });
2172
+ }
2173
+ preserveInitialRecordIdRef.current = false;
2174
+ setPendingDeepLinkRecordId(null);
2175
+ }
2176
+ return;
2177
+ }
2178
+ const hit = recordListItems.find((it) => it.id === pending);
2179
+ if (hit) {
2180
+ if (selectedRecordId !== pending) setSelectedRecordId(pending);
2181
+ preserveInitialRecordIdRef.current = false;
2182
+ setPendingDeepLinkRecordId(null);
2183
+ return;
2184
+ }
2185
+ if (!recordListLoading) {
2186
+ if (!warnedMissingRef.current.has(pending)) {
2187
+ warnedMissingRef.current.add(pending);
2188
+ console.warn("[RecordsAdminShell] deep-linked recordId not found in records list \u2014 clearing pending selection", {
2189
+ recordId: pending,
2190
+ scope: editingScope?.raw ?? null
2191
+ });
2192
+ }
2193
+ preserveInitialRecordIdRef.current = false;
2194
+ setPendingDeepLinkRecordId(null);
2195
+ }
2196
+ }, [
2197
+ pendingDeepLinkRecordId,
2198
+ isCollection,
2199
+ collectionItems.items,
2200
+ collectionItems.isLoading,
2201
+ recordListItems,
2202
+ recordListLoading
2203
+ ]);
2162
2204
  return { pendingDeepLinkRecordId, lastAppliedDLRef };
2163
2205
  };
2164
2206
  var isFacetRuleValid = (rule) => {
@@ -3196,10 +3238,16 @@ var RowContextMenu = ({
3196
3238
  }) => {
3197
3239
  const [open, setOpen] = useState(false);
3198
3240
  const wrapperRef = useRef(null);
3241
+ const triggerRef = useRef(null);
3242
+ const menuRef = useRef(null);
3243
+ const [pos, setPos] = useState(null);
3199
3244
  useEffect(() => {
3200
3245
  if (!open) return;
3201
3246
  const onDoc = (e) => {
3202
- if (!wrapperRef.current?.contains(e.target)) setOpen(false);
3247
+ const t = e.target;
3248
+ if (wrapperRef.current?.contains(t)) return;
3249
+ if (menuRef.current?.contains(t)) return;
3250
+ setOpen(false);
3203
3251
  };
3204
3252
  const onKey = (e) => {
3205
3253
  if (e.key === "Escape") setOpen(false);
@@ -3211,12 +3259,36 @@ var RowContextMenu = ({
3211
3259
  document.removeEventListener("keydown", onKey);
3212
3260
  };
3213
3261
  }, [open]);
3262
+ useLayoutEffect(() => {
3263
+ if (!open) {
3264
+ setPos(null);
3265
+ return;
3266
+ }
3267
+ const update = () => {
3268
+ const el = triggerRef.current;
3269
+ if (!el) return;
3270
+ const r = el.getBoundingClientRect();
3271
+ const menuWidth = menuRef.current?.offsetWidth ?? 176;
3272
+ const margin = 8;
3273
+ const left = Math.max(margin, Math.min(window.innerWidth - menuWidth - margin, r.right - menuWidth));
3274
+ const top = r.bottom + 4;
3275
+ setPos({ top, left });
3276
+ };
3277
+ update();
3278
+ window.addEventListener("resize", update);
3279
+ window.addEventListener("scroll", update, true);
3280
+ return () => {
3281
+ window.removeEventListener("resize", update);
3282
+ window.removeEventListener("scroll", update, true);
3283
+ };
3284
+ }, [open]);
3214
3285
  if (!onCopy && !onPaste) return null;
3215
3286
  const pasteLabel = !canPaste ? i18n.clipboardEmpty : pasteSourceLabel ? i18n.pasteFrom.replace("{name}", pasteSourceLabel) : pasteWillReplace ? i18n.pasteReplace : i18n.paste;
3216
3287
  return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: "ra-row-menu-wrap relative", children: [
3217
3288
  /* @__PURE__ */ jsx(
3218
3289
  "button",
3219
3290
  {
3291
+ ref: triggerRef,
3220
3292
  type: "button",
3221
3293
  className: "ra-row-menu-trigger",
3222
3294
  "aria-label": "Row actions",
@@ -3229,75 +3301,106 @@ var RowContextMenu = ({
3229
3301
  children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "w-3.5 h-3.5", "aria-hidden": "true" })
3230
3302
  }
3231
3303
  ),
3232
- open && /* @__PURE__ */ jsxs("div", { role: "menu", className: "ra-row-menu", onClick: (e) => e.stopPropagation(), children: [
3233
- onCopy && /* @__PURE__ */ jsxs(
3234
- "button",
3304
+ open && typeof document !== "undefined" && createPortal(
3305
+ /* @__PURE__ */ jsxs(
3306
+ "div",
3235
3307
  {
3236
- type: "button",
3237
- role: "menuitem",
3238
- className: "ra-row-menu-item",
3239
- onClick: (e) => {
3240
- e.stopPropagation();
3241
- setOpen(false);
3242
- onCopy();
3243
- },
3308
+ ref: menuRef,
3309
+ role: "menu",
3310
+ className: "ra-row-menu ra-row-menu-portal",
3311
+ style: pos ? { top: pos.top, left: pos.left } : { visibility: "hidden" },
3312
+ onClick: (e) => e.stopPropagation(),
3313
+ onMouseDown: (e) => e.stopPropagation(),
3244
3314
  children: [
3245
- /* @__PURE__ */ jsx(Copy, { className: "w-3 h-3", "aria-hidden": "true" }),
3246
- /* @__PURE__ */ jsx("span", { children: i18n.copy })
3315
+ onCopy && /* @__PURE__ */ jsxs(
3316
+ "button",
3317
+ {
3318
+ type: "button",
3319
+ role: "menuitem",
3320
+ className: "ra-row-menu-item",
3321
+ onClick: (e) => {
3322
+ e.stopPropagation();
3323
+ setOpen(false);
3324
+ onCopy();
3325
+ },
3326
+ children: [
3327
+ /* @__PURE__ */ jsx(Copy, { className: "w-3 h-3", "aria-hidden": "true" }),
3328
+ /* @__PURE__ */ jsx("span", { children: i18n.copy })
3329
+ ]
3330
+ }
3331
+ ),
3332
+ onPaste && /* @__PURE__ */ jsxs(
3333
+ "button",
3334
+ {
3335
+ type: "button",
3336
+ role: "menuitem",
3337
+ className: "ra-row-menu-item",
3338
+ disabled: !canPaste,
3339
+ onClick: (e) => {
3340
+ e.stopPropagation();
3341
+ setOpen(false);
3342
+ if (canPaste) onPaste();
3343
+ },
3344
+ children: [
3345
+ /* @__PURE__ */ jsx(ClipboardPaste, { className: "w-3 h-3", "aria-hidden": "true" }),
3346
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: pasteLabel })
3347
+ ]
3348
+ }
3349
+ )
3247
3350
  ]
3248
3351
  }
3249
3352
  ),
3250
- onPaste && /* @__PURE__ */ jsxs(
3251
- "button",
3252
- {
3253
- type: "button",
3254
- role: "menuitem",
3255
- className: "ra-row-menu-item",
3256
- disabled: !canPaste,
3257
- onClick: (e) => {
3258
- e.stopPropagation();
3259
- setOpen(false);
3260
- if (canPaste) onPaste();
3261
- },
3262
- children: [
3263
- /* @__PURE__ */ jsx(ClipboardPaste, { className: "w-3 h-3", "aria-hidden": "true" }),
3264
- /* @__PURE__ */ jsx("span", { className: "truncate", children: pasteLabel })
3265
- ]
3266
- }
3267
- )
3268
- ] })
3353
+ document.body
3354
+ )
3269
3355
  ] });
3270
3356
  };
3271
3357
 
3272
3358
  // src/components/RecordsAdmin/browser/formatFacetRule.ts
3273
- function formatFacetRule(rule) {
3359
+ function formatFacetRule(rule, lookup) {
3274
3360
  if (!rule || !rule.all || rule.all.length === 0) return null;
3361
+ const fLabel = (k) => lookup?.facetLabel?.(k) ?? k;
3362
+ const vLabel = (k, v) => lookup?.valueLabel?.(k, v) ?? v;
3275
3363
  const parts = rule.all.map((c) => {
3276
3364
  const vals = (c.anyOf ?? []).filter(Boolean);
3277
- if (vals.length === 0) return `${c.facetKey} (no values)`;
3278
- if (vals.length === 1) return `${c.facetKey} = ${vals[0]}`;
3279
- return `${c.facetKey} \u2208 {${vals.join(", ")}}`;
3365
+ const fk = fLabel(c.facetKey);
3366
+ if (vals.length === 0) return `${fk} (no values)`;
3367
+ if (vals.length === 1) return `${fk} = ${vLabel(c.facetKey, vals[0])}`;
3368
+ return `${fk} \u2208 {${vals.map((v) => vLabel(c.facetKey, v)).join(", ")}}`;
3280
3369
  });
3281
3370
  return parts.join(" AND ");
3282
3371
  }
3283
- function summarizeFacetRule(rule) {
3372
+ function summarizeFacetRule(rule, lookup) {
3284
3373
  if (!rule || !rule.all || rule.all.length === 0) return [];
3374
+ const fLabel = (k) => lookup?.facetLabel?.(k) ?? k;
3375
+ const vLabel = (k, v) => lookup?.valueLabel?.(k, v) ?? v;
3285
3376
  return rule.all.map((c) => {
3286
- const values = (c.anyOf ?? []).filter(Boolean);
3377
+ const rawValues = (c.anyOf ?? []).filter(Boolean);
3378
+ const values = rawValues.map((v) => vLabel(c.facetKey, v));
3379
+ const fk = fLabel(c.facetKey);
3287
3380
  let label;
3288
- if (values.length === 0) label = `${c.facetKey}: \u2014`;
3289
- else if (values.length === 1) label = `${c.facetKey}: ${values[0]}`;
3290
- else if (values.length <= 3) label = `${c.facetKey}: ${values.join(", ")}`;
3291
- else label = `${c.facetKey}: ${values.slice(0, 2).join(", ")} +${values.length - 2}`;
3381
+ if (values.length === 0) label = `${fk}: \u2014`;
3382
+ else if (values.length === 1) label = `${fk}: ${values[0]}`;
3383
+ else if (values.length <= 3) label = `${fk}: ${values.join(", ")}`;
3384
+ else label = `${fk}: ${values.slice(0, 2).join(", ")} +${values.length - 2}`;
3292
3385
  return { facetKey: c.facetKey, values, label };
3293
3386
  });
3294
3387
  }
3388
+ var RuleLabelLookupContext = createContext(null);
3389
+ var useRuleLabelLookup = () => useContext(RuleLabelLookupContext) ?? void 0;
3390
+ var RuleLabelLookupProvider = ({
3391
+ value,
3392
+ children
3393
+ }) => {
3394
+ const v = useMemo(() => value, [value]);
3395
+ return /* @__PURE__ */ jsx(RuleLabelLookupContext.Provider, { value: v, children });
3396
+ };
3295
3397
  var DefaultRecordRow = ({ record, ctx, compact = false }) => {
3296
3398
  const { selected, onSelect, isDirty, hasError, onCopy, onPaste, canPaste, pasteWillReplace, clipboardSourceLabel } = ctx;
3399
+ const ruleLabelLookup = useRuleLabelLookup();
3297
3400
  const ScopeIcon = record.scope.kind && record.scope.kind !== "collection" ? DEFAULT_ICONS.scope[record.scope.kind] : DEFAULT_ICONS.scope.product;
3298
3401
  const tone = resolveTone(void 0, record.status);
3299
- const ruleSummary = formatFacetRule(record.facetRule);
3300
- const ruleClauses = summarizeFacetRule(record.facetRule);
3402
+ const ruleSummary = formatFacetRule(record.facetRule, ruleLabelLookup);
3403
+ const ruleClauses = summarizeFacetRule(record.facetRule, ruleLabelLookup);
3301
3404
  const isRuleRecord = ruleClauses.length > 0;
3302
3405
  const i18n = ctx.i18n ?? DEFAULT_I18N;
3303
3406
  const subtitle = record.subtitle ?? (isRuleRecord ? null : ruleSummary) ?? (tone === "missing" ? i18n.subtitleEmpty : tone === "own" ? i18n.subtitleConfigured : i18n.subtitleInherited);
@@ -3338,9 +3441,9 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
3338
3441
  !compact && !isRuleRecord && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: subtitle })
3339
3442
  ] }),
3340
3443
  compact && /* @__PURE__ */ jsx(StatusIcon, { status: record.status, size: "0.85rem" }),
3341
- !compact && record.scope.kind && record.scope.kind !== "collection" && /* @__PURE__ */ jsx("span", { className: "ra-row-scope", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ScopeIcon, { className: "w-3 h-3" }) }),
3444
+ !compact && record.scope.kind && record.scope.kind !== "collection" && record.scope.kind !== "rule" && /* @__PURE__ */ jsx("span", { className: "ra-row-scope", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ScopeIcon, { className: "w-3 h-3" }) }),
3342
3445
  record.badges?.slice(0, 1).map((b, i) => /* @__PURE__ */ jsx("span", { className: "ra-chip", "data-tone": "muted", children: b.label }, `${b.label}-${i}`)),
3343
- record.facetRule && !compact && /* @__PURE__ */ jsx(
3446
+ record.facetRule && !compact && !isRuleRecord && /* @__PURE__ */ jsx(
3344
3447
  "span",
3345
3448
  {
3346
3449
  className: "ra-row-rule-pip",
@@ -3848,15 +3951,19 @@ function useCollectionItems(args) {
3848
3951
 
3849
3952
  // src/components/RecordsAdmin/data/deepLinkAdapter.ts
3850
3953
  var findQueryHost = (loc) => {
3851
- if (loc.search && loc.search.length > 1) return "search";
3954
+ if (loc.hash && loc.hash.startsWith("#/")) return "hash";
3852
3955
  if (loc.hash && loc.hash.includes("?")) return "hash";
3956
+ if (loc.search && loc.search.length > 1) return "search";
3853
3957
  return "search";
3854
3958
  };
3855
- var getQueryString = (loc) => {
3856
- const host = findQueryHost(loc);
3857
- if (host === "search") return loc.search.startsWith("?") ? loc.search.slice(1) : loc.search;
3959
+ var getSearchParams = (loc) => new URLSearchParams(loc.search.startsWith("?") ? loc.search.slice(1) : loc.search);
3960
+ var getHashParams = (loc) => {
3858
3961
  const idx = loc.hash.indexOf("?");
3859
- return idx >= 0 ? loc.hash.slice(idx + 1) : "";
3962
+ return new URLSearchParams(idx >= 0 ? loc.hash.slice(idx + 1) : "");
3963
+ };
3964
+ var getReadParams = (loc) => {
3965
+ if (loc.hash && loc.hash.includes("?")) return getHashParams(loc);
3966
+ return getSearchParams(loc);
3860
3967
  };
3861
3968
  var buildUrl = (loc, nextQuery) => {
3862
3969
  const host = findQueryHost(loc);
@@ -3870,7 +3977,7 @@ var buildUrl = (loc, nextQuery) => {
3870
3977
  var createDefaultDeepLinkAdapter = (paramNames) => ({
3871
3978
  read() {
3872
3979
  if (typeof window === "undefined") return {};
3873
- const params = new URLSearchParams(getQueryString(window.location));
3980
+ const params = getReadParams(window.location);
3874
3981
  return {
3875
3982
  recordId: params.get(paramNames.recordId),
3876
3983
  scope: params.get(paramNames.scope),
@@ -3879,7 +3986,7 @@ var createDefaultDeepLinkAdapter = (paramNames) => ({
3879
3986
  },
3880
3987
  write(partial, mode) {
3881
3988
  if (typeof window === "undefined") return;
3882
- const params = new URLSearchParams(getQueryString(window.location));
3989
+ const params = findQueryHost(window.location) === "hash" ? getHashParams(window.location) : getSearchParams(window.location);
3883
3990
  const apply = (key, value) => {
3884
3991
  if (value == null || value === "") params.delete(key);
3885
3992
  else params.set(key, value);
@@ -3913,14 +4020,22 @@ var CONTEXT_KEYS = [
3913
4020
  "lang",
3914
4021
  "theme"
3915
4022
  ];
3916
- var findQueryString = (loc) => {
3917
- if (loc.search && loc.search.length > 1) {
3918
- return loc.search.startsWith("?") ? loc.search.slice(1) : loc.search;
3919
- }
3920
- if (loc.hash && loc.hash.includes("?")) {
3921
- return loc.hash.slice(loc.hash.indexOf("?") + 1);
3922
- }
3923
- return "";
4023
+ var getSearchParams2 = (loc) => new URLSearchParams(loc.search.startsWith("?") ? loc.search.slice(1) : loc.search);
4024
+ var getHashParams2 = (loc) => {
4025
+ const idx = loc.hash.indexOf("?");
4026
+ return new URLSearchParams(idx >= 0 ? loc.hash.slice(idx + 1) : "");
4027
+ };
4028
+ var getReadParams2 = (loc) => {
4029
+ if (loc.hash && loc.hash.includes("?")) return getHashParams2(loc);
4030
+ return getSearchParams2(loc);
4031
+ };
4032
+ var getMergedParams = (loc) => {
4033
+ const merged = getSearchParams2(loc);
4034
+ const hashParams = getHashParams2(loc);
4035
+ hashParams.forEach((value, key) => {
4036
+ merged.set(key, value);
4037
+ });
4038
+ return merged;
3924
4039
  };
3925
4040
  var findPath = (loc) => {
3926
4041
  if (loc.hash && loc.hash.startsWith("#")) {
@@ -3943,7 +4058,7 @@ var createPostMessageDeepLinkAdapter = (paramNames) => {
3943
4058
  const lastShellState = {};
3944
4059
  const post = (path) => {
3945
4060
  if (typeof window === "undefined") return;
3946
- const params = new URLSearchParams(findQueryString(window.location));
4061
+ const params = getMergedParams(window.location);
3947
4062
  const context = {};
3948
4063
  const state = {};
3949
4064
  params.forEach((value, key) => {
@@ -3967,9 +4082,6 @@ var createPostMessageDeepLinkAdapter = (paramNames) => {
3967
4082
  state,
3968
4083
  appId: context.appId
3969
4084
  };
3970
- console.debug("[smartlinks-ui] postMessage \u2192 parent", message, {
3971
- sameWindow: window.parent === window
3972
- });
3973
4085
  window.parent.postMessage(message, "*");
3974
4086
  } catch {
3975
4087
  }
@@ -3977,7 +4089,7 @@ var createPostMessageDeepLinkAdapter = (paramNames) => {
3977
4089
  return {
3978
4090
  read() {
3979
4091
  if (typeof window === "undefined") return {};
3980
- const params = new URLSearchParams(findQueryString(window.location));
4092
+ const params = getReadParams2(window.location);
3981
4093
  return {
3982
4094
  recordId: params.get(paramNames.recordId),
3983
4095
  scope: params.get(paramNames.scope),
@@ -4012,7 +4124,7 @@ var classify2 = (mode, kind) => {
4012
4124
  return SMART_PUSH.includes(kind) ? "push" : "replace";
4013
4125
  };
4014
4126
  function useDeepLinkState(options) {
4015
- const enabled = !!options?.enabled;
4127
+ const enabled = options?.enabled !== false;
4016
4128
  const history = options?.history ?? "smart";
4017
4129
  const paramNames = useMemo(() => ({
4018
4130
  ...DEFAULT_DEEP_LINK_PARAM_NAMES,
@@ -4024,11 +4136,6 @@ function useDeepLinkState(options) {
4024
4136
  if (options?.adapter) return options.adapter;
4025
4137
  if (!defaultAdapterRef.current) {
4026
4138
  const inIframe = isInSmartLinksIframe();
4027
- console.debug("[smartlinks-ui] deep-link adapter selected", {
4028
- adapter: inIframe ? "postMessage" : "default",
4029
- inIframe,
4030
- href: typeof window !== "undefined" ? window.location.href : "(ssr)"
4031
- });
4032
4139
  defaultAdapterRef.current = inIframe ? createPostMessageDeepLinkAdapter(paramNames) : createDefaultDeepLinkAdapter(paramNames);
4033
4140
  }
4034
4141
  return defaultAdapterRef.current;
@@ -4042,7 +4149,9 @@ function useDeepLinkState(options) {
4042
4149
  return adapter.subscribe(() => setUrlState(adapter.read()));
4043
4150
  }, [adapter]);
4044
4151
  const emit = useCallback((partial, kind) => {
4045
- if (!adapter) return;
4152
+ if (!adapter) {
4153
+ return;
4154
+ }
4046
4155
  const mode = classify2(history, kind);
4047
4156
  adapter.write(partial, mode);
4048
4157
  setUrlState((prev) => ({ ...prev, ...partial }));
@@ -5101,14 +5210,16 @@ var PreviewScopePicker = ({
5101
5210
  onChange,
5102
5211
  showVariants,
5103
5212
  showBatches,
5213
+ activeScope,
5104
5214
  i18n
5105
5215
  }) => {
5106
5216
  const productPinned = !!editingScope.productId;
5217
+ const hideForTab = activeScope === "product" || activeScope === "variant" || activeScope === "batch";
5107
5218
  const products = useProductBrowse({
5108
5219
  SL,
5109
5220
  collectionId,
5110
5221
  pageSize: 100,
5111
- enabled: !productPinned
5222
+ enabled: !productPinned && !hideForTab
5112
5223
  });
5113
5224
  const variants = useProductChildren({
5114
5225
  SL,
@@ -5122,11 +5233,8 @@ var PreviewScopePicker = ({
5122
5233
  productId: value.productId,
5123
5234
  kind: showBatches ? "batch" : null
5124
5235
  });
5125
- const isDefault = useMemo(
5126
- () => value.raw === editingScope.raw,
5127
- [value.raw, editingScope.raw]
5128
- );
5129
5236
  useEffect(() => {
5237
+ if (hideForTab) return;
5130
5238
  if (productPinned) return;
5131
5239
  if (value.productId) return;
5132
5240
  const first = products.items[0];
@@ -5138,9 +5246,10 @@ var PreviewScopePicker = ({
5138
5246
  batchId: void 0,
5139
5247
  raw: `product:${first.id}`
5140
5248
  });
5141
- }, [productPinned, value.productId, products.items]);
5142
- const [productPickerOpen, setProductPickerOpen] = useState(false);
5143
- const showProductPicker = !productPinned && products.items.length > 1;
5249
+ }, [hideForTab, productPinned, value.productId, products.items]);
5250
+ const [lightboxOpen, setLightboxOpen] = useState(false);
5251
+ if (hideForTab) return null;
5252
+ const showProductPicker = !productPinned;
5144
5253
  const showVariantPicker = showVariants && !!value.productId && variants.items.length > 0;
5145
5254
  const showBatchPicker = showBatches && !!value.productId && batches.items.length > 0;
5146
5255
  if (!showProductPicker && !showVariantPicker && !showBatchPicker) {
@@ -5151,6 +5260,16 @@ var PreviewScopePicker = ({
5151
5260
  borderColor: "hsl(var(--ra-border))",
5152
5261
  color: "hsl(var(--ra-text))"
5153
5262
  };
5263
+ const pickProduct = (productId) => {
5264
+ onChange({
5265
+ ...value,
5266
+ productId,
5267
+ variantId: void 0,
5268
+ batchId: void 0,
5269
+ raw: `product:${productId}`
5270
+ });
5271
+ setLightboxOpen(false);
5272
+ };
5154
5273
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
5155
5274
  /* @__PURE__ */ jsxs(
5156
5275
  "span",
@@ -5163,56 +5282,27 @@ var PreviewScopePicker = ({
5163
5282
  ]
5164
5283
  }
5165
5284
  ),
5166
- !isDefault && /* @__PURE__ */ jsxs(
5167
- "button",
5168
- {
5169
- type: "button",
5170
- onClick: () => onChange(editingScope),
5171
- className: "text-[10px] px-2 py-1 rounded-md border hover:bg-[hsl(var(--ra-muted))]",
5172
- style: selectStyle,
5173
- children: [
5174
- "\u21BA ",
5175
- i18n?.previewAsDefault ?? "Same as edited"
5176
- ]
5177
- }
5178
- ),
5179
- showProductPicker && !productPickerOpen && currentProductName && /* @__PURE__ */ jsxs(
5180
- "button",
5285
+ showProductPicker && /* @__PURE__ */ jsxs(
5286
+ "span",
5181
5287
  {
5182
- type: "button",
5183
- onClick: () => setProductPickerOpen(true),
5184
- className: "text-[10px] px-2 py-1 rounded-md border hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1 max-w-[12rem] truncate",
5185
- style: selectStyle,
5186
- title: `Preview as ${currentProductName} \u2014 click to change`,
5288
+ className: "inline-flex items-center gap-1.5 text-[11px] truncate max-w-[18rem]",
5289
+ style: { color: "hsl(var(--ra-text))" },
5187
5290
  children: [
5188
- "as ",
5189
- /* @__PURE__ */ jsx("span", { className: "font-medium truncate", children: currentProductName }),
5190
- /* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3 opacity-60" })
5291
+ /* @__PURE__ */ jsx("span", { className: "font-medium truncate", title: currentProductName, children: currentProductName || "\u2014" }),
5292
+ /* @__PURE__ */ jsx(
5293
+ "button",
5294
+ {
5295
+ type: "button",
5296
+ onClick: () => setLightboxOpen(true),
5297
+ className: "text-[10px] px-1.5 py-0.5 rounded-md border hover:bg-[hsl(var(--ra-muted))]",
5298
+ style: selectStyle,
5299
+ title: i18n?.change ?? "Change preview product",
5300
+ children: i18n?.change ?? "Change"
5301
+ }
5302
+ )
5191
5303
  ]
5192
5304
  }
5193
5305
  ),
5194
- showProductPicker && productPickerOpen && /* @__PURE__ */ jsx(
5195
- "select",
5196
- {
5197
- className: SELECT_CLS,
5198
- style: selectStyle,
5199
- autoFocus: true,
5200
- onBlur: () => setProductPickerOpen(false),
5201
- value: value.productId ?? "",
5202
- onChange: (e) => {
5203
- const productId = e.target.value || void 0;
5204
- onChange({
5205
- ...value,
5206
- productId,
5207
- variantId: void 0,
5208
- batchId: void 0,
5209
- raw: productId ? `product:${productId}` : ""
5210
- });
5211
- setProductPickerOpen(false);
5212
- },
5213
- children: products.items.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
5214
- }
5215
- ),
5216
5306
  showVariantPicker && /* @__PURE__ */ jsxs(
5217
5307
  "select",
5218
5308
  {
@@ -5253,9 +5343,161 @@ var PreviewScopePicker = ({
5253
5343
  batches.items.map((b) => /* @__PURE__ */ jsx("option", { value: b.id, children: b.name }, b.id))
5254
5344
  ]
5255
5345
  }
5346
+ ),
5347
+ lightboxOpen && /* @__PURE__ */ jsx(
5348
+ ProductPickerLightbox,
5349
+ {
5350
+ SL,
5351
+ collectionId,
5352
+ currentProductId: value.productId,
5353
+ onPick: pickProduct,
5354
+ onClose: () => setLightboxOpen(false),
5355
+ i18n
5356
+ }
5256
5357
  )
5257
5358
  ] });
5258
5359
  };
5360
+ var ProductPickerLightbox = ({
5361
+ SL,
5362
+ collectionId,
5363
+ currentProductId,
5364
+ onPick,
5365
+ onClose,
5366
+ i18n
5367
+ }) => {
5368
+ const [search, setSearch] = useState("");
5369
+ const inputRef = useRef(null);
5370
+ const browse = useProductBrowse({
5371
+ SL,
5372
+ collectionId,
5373
+ search,
5374
+ pageSize: 50,
5375
+ enabled: true
5376
+ });
5377
+ useEffect(() => {
5378
+ const prev = document.body.style.overflow;
5379
+ document.body.style.overflow = "hidden";
5380
+ const t = window.setTimeout(() => inputRef.current?.focus(), 0);
5381
+ const onKey = (e) => {
5382
+ if (e.key === "Escape") {
5383
+ e.stopPropagation();
5384
+ onClose();
5385
+ }
5386
+ };
5387
+ window.addEventListener("keydown", onKey, true);
5388
+ return () => {
5389
+ window.clearTimeout(t);
5390
+ window.removeEventListener("keydown", onKey, true);
5391
+ document.body.style.overflow = prev;
5392
+ };
5393
+ }, [onClose]);
5394
+ if (typeof document === "undefined") return null;
5395
+ return createPortal(
5396
+ /* @__PURE__ */ jsxs(
5397
+ "div",
5398
+ {
5399
+ className: "ra-shell ra-confirm-root",
5400
+ role: "presentation",
5401
+ onMouseDown: (e) => e.stopPropagation(),
5402
+ onClick: (e) => e.stopPropagation(),
5403
+ onTouchStart: (e) => e.stopPropagation(),
5404
+ children: [
5405
+ /* @__PURE__ */ jsx("div", { className: "ra-confirm-backdrop", onClick: onClose }),
5406
+ /* @__PURE__ */ jsxs(
5407
+ "div",
5408
+ {
5409
+ role: "dialog",
5410
+ "aria-modal": "true",
5411
+ "aria-label": i18n?.searchProducts ?? "Search products",
5412
+ className: "ra-confirm-card",
5413
+ style: { width: "min(520px, 100%)", padding: "1rem" },
5414
+ children: [
5415
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
5416
+ /* @__PURE__ */ jsx(Search, { className: "w-4 h-4 opacity-60" }),
5417
+ /* @__PURE__ */ jsx(
5418
+ "input",
5419
+ {
5420
+ ref: inputRef,
5421
+ type: "text",
5422
+ value: search,
5423
+ onChange: (e) => setSearch(e.target.value),
5424
+ placeholder: i18n?.searchProducts ?? "Search products\u2026",
5425
+ className: "flex-1 text-sm px-2 py-1.5 rounded-md border bg-transparent focus:outline-none focus:ring-1",
5426
+ style: {
5427
+ borderColor: "hsl(var(--ra-border))",
5428
+ color: "hsl(var(--ra-text))"
5429
+ }
5430
+ }
5431
+ ),
5432
+ /* @__PURE__ */ jsx(
5433
+ "button",
5434
+ {
5435
+ type: "button",
5436
+ onClick: onClose,
5437
+ className: "p-1 rounded-md hover:bg-[hsl(var(--ra-muted))]",
5438
+ "aria-label": i18n?.close ?? "Close",
5439
+ style: { color: "hsl(var(--ra-muted-text))" },
5440
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
5441
+ }
5442
+ )
5443
+ ] }),
5444
+ /* @__PURE__ */ jsxs(
5445
+ "div",
5446
+ {
5447
+ className: "overflow-y-auto rounded-md border",
5448
+ style: {
5449
+ maxHeight: "50vh",
5450
+ borderColor: "hsl(var(--ra-border))"
5451
+ },
5452
+ children: [
5453
+ browse.isLoading && browse.items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-xs px-3 py-4 text-center", style: { color: "hsl(var(--ra-muted-text))" }, children: "Loading\u2026" }) : browse.items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-xs px-3 py-4 text-center", style: { color: "hsl(var(--ra-muted-text))" }, children: "No products match." }) : /* @__PURE__ */ jsx("ul", { className: "divide-y", style: { borderColor: "hsl(var(--ra-border))" }, children: browse.items.map((p) => {
5454
+ const active = p.id === currentProductId;
5455
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
5456
+ "button",
5457
+ {
5458
+ type: "button",
5459
+ onClick: () => onPick(p.id),
5460
+ className: "w-full text-left px-3 py-2 text-sm hover:bg-[hsl(var(--ra-muted))] flex items-center justify-between gap-3",
5461
+ style: {
5462
+ color: "hsl(var(--ra-text))",
5463
+ background: active ? "hsl(var(--ra-muted))" : "transparent"
5464
+ },
5465
+ children: [
5466
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-col min-w-0", children: [
5467
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: p.name }),
5468
+ p.sku ? /* @__PURE__ */ jsx("span", { className: "truncate text-[11px]", style: { color: "hsl(var(--ra-muted-text))" }, children: p.sku }) : null
5469
+ ] }),
5470
+ active ? /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wide", style: { color: "hsl(var(--ra-muted-text))" }, children: "current" }) : null
5471
+ ]
5472
+ }
5473
+ ) }, p.id);
5474
+ }) }),
5475
+ browse.hasNextPage && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-t", style: { borderColor: "hsl(var(--ra-border))" }, children: /* @__PURE__ */ jsx(
5476
+ "button",
5477
+ {
5478
+ type: "button",
5479
+ onClick: () => browse.fetchNextPage(),
5480
+ disabled: browse.isFetchingNextPage,
5481
+ className: "text-xs w-full py-1 rounded-md border hover:bg-[hsl(var(--ra-muted))]",
5482
+ style: {
5483
+ borderColor: "hsl(var(--ra-border))",
5484
+ color: "hsl(var(--ra-text))"
5485
+ },
5486
+ children: browse.isFetchingNextPage ? "Loading\u2026" : "Load more"
5487
+ }
5488
+ ) })
5489
+ ]
5490
+ }
5491
+ )
5492
+ ]
5493
+ }
5494
+ )
5495
+ ]
5496
+ }
5497
+ ),
5498
+ document.body
5499
+ );
5500
+ };
5259
5501
  var ICONS2 = {
5260
5502
  table: Table,
5261
5503
  cards: LayoutGrid,
@@ -7154,6 +7396,11 @@ function RecordsAdminShellInner(props) {
7154
7396
  variantChildren,
7155
7397
  batchChildren
7156
7398
  } = browser;
7399
+ const ruleScopedList = useRecordList({
7400
+ ctx,
7401
+ scopeKind: "rule",
7402
+ enabled: true
7403
+ });
7157
7404
  const pinnedProduct = useSingleProduct({
7158
7405
  SL,
7159
7406
  collectionId,
@@ -7240,7 +7487,18 @@ function RecordsAdminShellInner(props) {
7240
7487
  setSelectedVariantId,
7241
7488
  selectedBatchId,
7242
7489
  setSelectedBatchId,
7243
- setFacetBrowseFilter
7490
+ setFacetBrowseFilter,
7491
+ // Pending-recordId resolution — without these the URL's `recordId` is
7492
+ // parsed but never opens the editor on refresh / share-link load.
7493
+ isCollection,
7494
+ setSelectedItemId,
7495
+ collectionItems: {
7496
+ items: collectionItems.items,
7497
+ isLoading: collectionItems.isLoading
7498
+ },
7499
+ recordListItems: recordList.items,
7500
+ recordListLoading: recordList.isLoading,
7501
+ skipNextItemResetRef
7244
7502
  });
7245
7503
  const supportedForResolution = useMemo(() => requestedScopes, [requestedScopes]);
7246
7504
  const resolved = useResolvedRecord({
@@ -7487,6 +7745,7 @@ function RecordsAdminShellInner(props) {
7487
7745
  onChange: setPreviewScope,
7488
7746
  showVariants: drillVariantsAllowed,
7489
7747
  showBatches: drillBatchesAllowed,
7748
+ activeScope,
7490
7749
  i18n: { previewAs: i18n.previewAs, previewAsDefault: i18n.previewAsDefault }
7491
7750
  }
7492
7751
  ) : null;
@@ -7675,6 +7934,26 @@ function RecordsAdminShellInner(props) {
7675
7934
  const isGlobalTab = activeScope === "collection";
7676
7935
  const isAllTab = activeScope === "all";
7677
7936
  const isRecordsTab = isRuleTab || isGlobalTab || isAllTab;
7937
+ const ruleLabelLookup = useMemo(() => {
7938
+ const map = /* @__PURE__ */ new Map();
7939
+ for (const it of facetBrowse.items) {
7940
+ const k = it.scope.facetId;
7941
+ const v = it.scope.facetValue;
7942
+ if (!k || !v) continue;
7943
+ const facetLabel = it.subtitle ?? k;
7944
+ const valueLabel = it.label ?? v;
7945
+ let info = map.get(k);
7946
+ if (!info) {
7947
+ info = { label: facetLabel, values: /* @__PURE__ */ new Map() };
7948
+ map.set(k, info);
7949
+ }
7950
+ if (!info.values.has(v)) info.values.set(v, valueLabel);
7951
+ }
7952
+ return {
7953
+ facetLabel: (k) => map.get(k)?.label,
7954
+ valueLabel: (k, v) => map.get(k)?.values.get(v)
7955
+ };
7956
+ }, [facetBrowse.items]);
7678
7957
  const effectiveGroupBy = useMemo(() => {
7679
7958
  if (groupBy) return groupBy;
7680
7959
  if (isAllTab) return void 0;
@@ -7683,13 +7962,25 @@ function RecordsAdminShellInner(props) {
7683
7962
  return (record) => {
7684
7963
  const hash = ruleHash(record.facetRule);
7685
7964
  if (!hash) return null;
7686
- return { key: `rule:${hash}`, label: summariseRule(record.facetRule) };
7965
+ return { key: `rule:${hash}`, label: summariseRule(record.facetRule, ruleLabelLookup) };
7687
7966
  };
7688
- }, [groupBy, isRuleTab, isAllTab, isCollection]);
7967
+ }, [groupBy, isRuleTab, isAllTab, isCollection, ruleLabelLookup]);
7689
7968
  const [bulkRuleEditTarget, setBulkRuleEditTarget] = useState(null);
7690
7969
  const ruleCatalogue = useMemo(() => {
7691
7970
  const buckets = /* @__PURE__ */ new Map();
7971
+ const seenIds = /* @__PURE__ */ new Set();
7972
+ const merged = [];
7973
+ for (const item of ruleScopedList.allItems) {
7974
+ if (item.id && seenIds.has(item.id)) continue;
7975
+ if (item.id) seenIds.add(item.id);
7976
+ merged.push(item);
7977
+ }
7692
7978
  for (const item of recordList.items) {
7979
+ if (item.id && seenIds.has(item.id)) continue;
7980
+ if (item.id) seenIds.add(item.id);
7981
+ merged.push(item);
7982
+ }
7983
+ for (const item of merged) {
7693
7984
  const hash = ruleHash(item.facetRule);
7694
7985
  if (!hash || !item.facetRule) continue;
7695
7986
  const existing = buckets.get(hash);
@@ -7699,7 +7990,7 @@ function RecordsAdminShellInner(props) {
7699
7990
  buckets.set(hash, {
7700
7991
  hash,
7701
7992
  rule: item.facetRule,
7702
- summary: summariseRule(item.facetRule),
7993
+ summary: summariseRule(item.facetRule, ruleLabelLookup),
7703
7994
  count: 1
7704
7995
  });
7705
7996
  }
@@ -7708,7 +7999,7 @@ function RecordsAdminShellInner(props) {
7708
7999
  if (b.count !== a.count) return b.count - a.count;
7709
8000
  return a.summary.localeCompare(b.summary);
7710
8001
  });
7711
- }, [recordList.items]);
8002
+ }, [recordList.items, ruleScopedList.allItems, ruleLabelLookup]);
7712
8003
  const [targetingExpandNonce, setTargetingExpandNonce] = useState(0);
7713
8004
  const renderRuleGroupActions = useCallback(
7714
8005
  (group) => {
@@ -7799,8 +8090,13 @@ function RecordsAdminShellInner(props) {
7799
8090
  }
7800
8091
  return Array.from(buckets.values()).map(({ rep, count }) => ({
7801
8092
  ...rep,
7802
- label: summariseRule(rep.facetRule),
7803
- subtitle: `${count} ${itemNoun}${count === 1 ? "" : "s"}`
8093
+ // Title carries the count + identity; the rule chips below render
8094
+ // the friendly facet/value labels (via the lookup) so we don't
8095
+ // repeat the same information in two places. Without this, a row
8096
+ // had three echoes of the same rule (raw-key title, friendly chip,
8097
+ // and the right-pane "Rule · …" header).
8098
+ label: `${count} ${itemNoun}${count === 1 ? "" : "s"}`,
8099
+ subtitle: void 0
7804
8100
  }));
7805
8101
  }, [isRuleTab, isCollection, filteredRuleItems, itemNoun]);
7806
8102
  const activeRuleSummary = useMemo(() => {
@@ -7808,8 +8104,8 @@ function RecordsAdminShellInner(props) {
7808
8104
  if (!selectedRecordId || isDraftId3(selectedRecordId)) return null;
7809
8105
  const hit = recordList.items.find((it) => it.id === selectedRecordId);
7810
8106
  if (!hit?.facetRule) return null;
7811
- return summariseRule(hit.facetRule);
7812
- }, [isRuleTab, isCollection, selectedRecordId, recordList.items]);
8107
+ return summariseRule(hit.facetRule, ruleLabelLookup);
8108
+ }, [isRuleTab, isCollection, selectedRecordId, recordList.items, ruleLabelLookup]);
7813
8109
  const applyFacetBrowseFilter = useCallback(
7814
8110
  (items2) => {
7815
8111
  if (!facetBrowseFilter) return items2;
@@ -7884,7 +8180,7 @@ function RecordsAdminShellInner(props) {
7884
8180
  });
7885
8181
  };
7886
8182
  onLeftSelectRef.current = onLeftSelect;
7887
- return /* @__PURE__ */ jsxs(
8183
+ return /* @__PURE__ */ jsx(RuleLabelLookupProvider, { value: ruleLabelLookup, children: /* @__PURE__ */ jsxs(
7888
8184
  "div",
7889
8185
  {
7890
8186
  className: `ra-shell flex flex-col h-full ${className ?? ""}`,
@@ -8365,7 +8661,7 @@ function RecordsAdminShellInner(props) {
8365
8661
  )
8366
8662
  ]
8367
8663
  }
8368
- );
8664
+ ) });
8369
8665
  }
8370
8666
  var RecordBrowser = ({
8371
8667
  scopes,
@@ -9220,6 +9516,9 @@ function useMergedRecord(args) {
9220
9516
  };
9221
9517
  }
9222
9518
 
9519
+ // src/components/RecordsAdmin/index.ts
9520
+ assertComponentStylesLoaded("records-admin");
9521
+
9223
9522
  export { ALL_ITEM_VIEWS, ALL_PRESENTATIONS, BatchList, BulkActionsMenu, DEFAULT_DEEP_LINK_PARAM_NAMES, DEFAULT_I18N, DEFAULT_ICONS, DefaultItemCards, DefaultItemTable, DefaultRecordCard, DefaultRecordRow, DeleteButton, DirtyDraftProvider, DrawerPreview, EditorItemNav, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, ItemListView, ItemViewSwitcher, LoadingState, PresentationSwitcher, PreviewScopePicker, PreviewToggleButton, ProductDrillDown, ProductList, RecordBrowser, RecordEditor, RecordList, RecordsAdminShell, ResolvedPreview, ScopeBreadcrumb, ScopeTabs, SiblingRail, SidePreview, StatusDot, StatusFilterPills, StatusIcon, TabbedPreview, UtilityRow, VariantList, buildDraftKey, buildRef, checkPasteCompatibility, cloneValue, createDefaultDeepLinkAdapter, createPostMessageDeepLinkAdapter, downloadBlob, exportCsv, importCsv, isInSmartLinksIframe, mergeIcons, normaliseRule, parseRef, pickHeaderIcon, resolutionChain, resolveRecord, ruleHash, rulesEqual, scopeCountsQueryKey, statusToneLabel, summariseRule, useCollectedRecords, useCollectionItems, useDeepLinkState, useDirtyDraft, useDirtyDraftActions, useDirtyDraftStore, useDirtyDrafts, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useItemViewPref, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordClipboard, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeCounts, useScopeProbe, useUnsavedGuard };
9224
9523
  //# sourceMappingURL=index.js.map
9225
9524
  //# sourceMappingURL=index.js.map