@proveanything/smartlinks-utils-ui 0.12.9 → 0.12.11

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.
@@ -525,6 +525,10 @@ interface RecordsAdminI18n {
525
525
  lifecycleStatusActive: string;
526
526
  lifecycleStatusArchived: string;
527
527
  lifecycleStatusDraft: string;
528
+ /** Product rail subtitle when the only records on a product are archived. */
529
+ subtitleArchivedOnly: string;
530
+ /** Product rail subtitle when the only records on a product are drafts. */
531
+ subtitleDraftOnly: string;
528
532
  /** Tooltip on the editor's status control. */
529
533
  lifecycleStatusHint: string;
530
534
  actionArchive: string;
@@ -838,6 +842,25 @@ interface RecordSlotContext {
838
842
  * to populate this directly.
839
843
  */
840
844
  actions?: RecordAction[];
845
+ /**
846
+ * Facet keys carried by the row's enclosing group header, when the rail
847
+ * is grouped by facet identity (Rules tab default). The default row
848
+ * renderer uses this to drop the facet prefix from chips whose facet is
849
+ * already named in the group header (so under "BREAD TYPE" you see
850
+ * "Slice" instead of "Bread Type: Slice"). Empty when ungrouped.
851
+ */
852
+ groupFacetKeys?: readonly string[];
853
+ /**
854
+ * Number of archived (history) records collapsed under this row's
855
+ * lifecycle slot. The default row renderer surfaces this as an inline
856
+ * pill the admin can click to expand/collapse the archived rows
857
+ * underneath. Undefined when there is no history block.
858
+ */
859
+ historyCount?: number;
860
+ /** Whether the history block for this row is currently expanded. */
861
+ historyExpanded?: boolean;
862
+ /** Toggle the history block. Only present when `historyCount > 0`. */
863
+ onToggleHistory?: () => void;
841
864
  }
842
865
  /**
843
866
  * Declarative column definition for the built-in default item table.
@@ -1106,6 +1129,12 @@ interface RailConfig<TData = unknown> {
1106
1129
  key: string;
1107
1130
  label: string;
1108
1131
  icon?: ReactNode;
1132
+ /**
1133
+ * Optional facet keys this group represents (Rules tab default
1134
+ * grouping populates this). Forwarded to row ctx so the default row
1135
+ * renderer can drop redundant facet prefixes from rule chips.
1136
+ */
1137
+ facetKeys?: readonly string[];
1109
1138
  /**
1110
1139
  * Whether this bucket should be expanded by default (records-driven
1111
1140
  * accordion mode). Last-write-wins per key. Has no effect in the
@@ -2381,9 +2410,25 @@ interface UseScopeCountsArgs {
2381
2410
  maxRecords?: number;
2382
2411
  /** Page size used for the underlying SDK list calls. Default 100. */
2383
2412
  pageSize?: number;
2413
+ /**
2414
+ * Lifecycle status values treated as "active" when bucketing records
2415
+ * per product. Records with `null`/empty status are ALWAYS active for
2416
+ * back-compat. Defaults to `['active']`.
2417
+ */
2418
+ activeStatuses?: readonly string[];
2384
2419
  }
2385
2420
  interface ScopeCounts extends Partial<Record<ScopeKind, number>> {
2386
2421
  }
2422
+ /**
2423
+ * Per-product lifecycle bucket counts. `other` covers any non-active
2424
+ * status that isn't draft or archived (e.g. host-defined "scheduled").
2425
+ */
2426
+ interface ProductLifecycleBuckets {
2427
+ active: number;
2428
+ draft: number;
2429
+ archived: number;
2430
+ other: number;
2431
+ }
2387
2432
  interface UseScopeCountsResult {
2388
2433
  counts: ScopeCounts;
2389
2434
  total: number;
@@ -2401,6 +2446,13 @@ interface UseScopeCountsResult {
2401
2446
  * Lower bound when `truncated` is true.
2402
2447
  */
2403
2448
  productIds: ReadonlySet<string>;
2449
+ /**
2450
+ * Per-product lifecycle bucket counts, derived from the same scanned
2451
+ * records (no extra network cost). Used by the rail to distinguish a
2452
+ * product that has only-archived or only-draft records from one with
2453
+ * a live record — so the green tick doesn't lie.
2454
+ */
2455
+ productLifecycle: ReadonlyMap<string, ProductLifecycleBuckets>;
2404
2456
  }
2405
2457
  declare const useScopeCounts: (args: UseScopeCountsArgs) => UseScopeCountsResult;
2406
2458
  /** React Query key factory for cache invalidation from save/delete sites. */
@@ -15,6 +15,7 @@ import { createPortal } from 'react-dom';
15
15
  // src/components/RecordsAdmin/data/recordCache.ts
16
16
  var RECORD_LIST_QK = ["records-admin", "list"];
17
17
  var COLLECTION_ITEMS_QK = ["records-admin", "collection-items"];
18
+ var SCOPE_COUNTS_QK = ["records-admin", "scope-counts"];
18
19
  var matchesCtx = (queryKey, prefix, ctx) => {
19
20
  if (queryKey.length < prefix.length + 3) return false;
20
21
  for (let i = 0; i < prefix.length; i += 1) {
@@ -87,6 +88,54 @@ function patchRecordIntoCaches(queryClient, ctx, record) {
87
88
  return replaceOrAppend(prev, record);
88
89
  });
89
90
  }
91
+ patchRecordIntoScopeCounts(queryClient, ctx, record);
92
+ }
93
+ var matchesScopeCountsCtx = (queryKey, ctx) => {
94
+ if (queryKey.length < 5) return false;
95
+ if (queryKey[0] !== SCOPE_COUNTS_QK[0]) return false;
96
+ if (queryKey[1] !== SCOPE_COUNTS_QK[1]) return false;
97
+ if (queryKey[2] !== ctx.collectionId) return false;
98
+ if (queryKey[3] !== ctx.appId) return false;
99
+ const slot = queryKey[4];
100
+ const expected = ctx.recordType ?? null;
101
+ if (slot !== expected && !(slot == null && expected == null)) return false;
102
+ return true;
103
+ };
104
+ function patchRecordIntoScopeCounts(queryClient, ctx, record) {
105
+ if (!record || !record.id) return;
106
+ const all = queryClient.getQueriesData({
107
+ queryKey: SCOPE_COUNTS_QK
108
+ });
109
+ for (const [key, cache] of all) {
110
+ if (!cache || !Array.isArray(cache.records)) continue;
111
+ if (!matchesScopeCountsCtx(key, ctx)) continue;
112
+ queryClient.setQueryData(key, (prev) => {
113
+ if (!prev || !Array.isArray(prev.records)) return prev;
114
+ const idx = prev.records.findIndex((r) => r.id === record.id);
115
+ if (idx === -1) {
116
+ return { ...prev, records: [...prev.records, record] };
117
+ }
118
+ const next = [...prev.records];
119
+ next[idx] = record;
120
+ return { ...prev, records: next };
121
+ });
122
+ }
123
+ }
124
+ function removeRecordFromScopeCounts(queryClient, ctx, recordId) {
125
+ if (!recordId) return;
126
+ const all = queryClient.getQueriesData({
127
+ queryKey: SCOPE_COUNTS_QK
128
+ });
129
+ for (const [key, cache] of all) {
130
+ if (!cache || !Array.isArray(cache.records)) continue;
131
+ if (!matchesScopeCountsCtx(key, ctx)) continue;
132
+ queryClient.setQueryData(key, (prev) => {
133
+ if (!prev || !Array.isArray(prev.records)) return prev;
134
+ const next = prev.records.filter((r) => r.id !== recordId);
135
+ if (next.length === prev.records.length) return prev;
136
+ return { ...prev, records: next };
137
+ });
138
+ }
90
139
  }
91
140
  function removeRecordFromCaches(queryClient, ctx, recordId) {
92
141
  if (!recordId) return;
@@ -104,6 +153,7 @@ function removeRecordFromCaches(queryClient, ctx, recordId) {
104
153
  return next ?? prev;
105
154
  });
106
155
  }
156
+ removeRecordFromScopeCounts(queryClient, ctx, recordId);
107
157
  }
108
158
  var DEFAULT_ICONS = {
109
159
  scope: {
@@ -266,6 +316,8 @@ var DEFAULT_I18N = {
266
316
  lifecycleStatusActive: "Active",
267
317
  lifecycleStatusArchived: "Archived",
268
318
  lifecycleStatusDraft: "Draft",
319
+ subtitleArchivedOnly: "Archived only",
320
+ subtitleDraftOnly: "Draft only",
269
321
  lifecycleStatusHint: "Archived records aren't returned to public consumers.",
270
322
  actionArchive: "Archive",
271
323
  actionRestore: "Restore",
@@ -582,7 +634,17 @@ var classify = (rec) => {
582
634
  return "collection";
583
635
  };
584
636
  var useScopeCounts = (args) => {
585
- const { ctx, enabled = true, maxRecords = 500, pageSize = 100 } = args;
637
+ const {
638
+ ctx,
639
+ enabled = true,
640
+ maxRecords = 500,
641
+ pageSize = 100,
642
+ activeStatuses
643
+ } = args;
644
+ const activeSet = useMemo(
645
+ () => new Set(activeStatuses ?? ["active"]),
646
+ [activeStatuses]
647
+ );
586
648
  const queryKey = useMemo(
587
649
  () => [...QK_BASE, ctx.collectionId, ctx.appId, ctx.recordType ?? null],
588
650
  [ctx.collectionId, ctx.appId, ctx.recordType]
@@ -621,10 +683,24 @@ var useScopeCounts = (args) => {
621
683
  all: 0
622
684
  };
623
685
  const productIds = /* @__PURE__ */ new Set();
686
+ const productLifecycle = /* @__PURE__ */ new Map();
624
687
  for (const rec of records) {
625
688
  counts[classify(rec)] += 1;
626
689
  const pid = rec.productId ?? void 0;
627
- if (pid) productIds.add(pid);
690
+ if (pid) {
691
+ productIds.add(pid);
692
+ const raw = rec.status ?? void 0;
693
+ const isActive2 = raw == null || raw === "" || activeSet.has(raw);
694
+ let bucket = productLifecycle.get(pid);
695
+ if (!bucket) {
696
+ bucket = { active: 0, draft: 0, archived: 0, other: 0 };
697
+ productLifecycle.set(pid, bucket);
698
+ }
699
+ if (isActive2) bucket.active += 1;
700
+ else if (raw === "draft") bucket.draft += 1;
701
+ else if (raw === "archived") bucket.archived += 1;
702
+ else bucket.other += 1;
703
+ }
628
704
  }
629
705
  counts.all = records.length;
630
706
  return {
@@ -633,9 +709,10 @@ var useScopeCounts = (args) => {
633
709
  isLoading: query.isLoading,
634
710
  error: query.error ?? null,
635
711
  truncated,
636
- productIds
712
+ productIds,
713
+ productLifecycle
637
714
  };
638
- }, [query.data, query.isLoading, query.error]);
715
+ }, [query.data, query.isLoading, query.error, activeSet]);
639
716
  return result;
640
717
  };
641
718
  var scopeCountsQueryKey = (collectionId, appId, recordType) => [...QK_BASE, collectionId, appId, recordType ?? null];
@@ -4070,13 +4147,30 @@ var RuleLabelLookupProvider = ({
4070
4147
  return /* @__PURE__ */ jsx(RuleLabelLookupContext.Provider, { value: v, children });
4071
4148
  };
4072
4149
  var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4073
- const { selected, onSelect, isDirty, hasError, onCopy, onDuplicate, onCopyAndNewRule, actions } = ctx;
4150
+ const {
4151
+ selected,
4152
+ onSelect,
4153
+ isDirty,
4154
+ hasError,
4155
+ onCopy,
4156
+ onDuplicate,
4157
+ onCopyAndNewRule,
4158
+ actions,
4159
+ historyCount,
4160
+ historyExpanded,
4161
+ onToggleHistory
4162
+ } = ctx;
4074
4163
  const ruleLabelLookup = useRuleLabelLookup();
4075
4164
  const ScopeIcon = record.scope.kind && record.scope.kind !== "collection" ? DEFAULT_ICONS.scope[record.scope.kind] : DEFAULT_ICONS.scope.product;
4076
4165
  const tone = resolveTone(void 0, record.status);
4077
4166
  const ruleSummary = formatFacetRule(record.facetRule, ruleLabelLookup);
4078
4167
  const ruleClauses = summarizeFacetRule(record.facetRule, ruleLabelLookup);
4079
4168
  const isRuleRecord = ruleClauses.length > 0;
4169
+ const groupKeys = ctx.groupFacetKeys;
4170
+ const singleGroupFacet = groupKeys && groupKeys.length === 1 ? groupKeys[0] : null;
4171
+ const displayClauses = singleGroupFacet ? ruleClauses.map(
4172
+ (c) => c.facetKey === singleGroupFacet ? { ...c, label: c.values.length <= 3 ? c.values.join(", ") : `${c.values.slice(0, 2).join(", ")} +${c.values.length - 2}` } : c
4173
+ ) : ruleClauses;
4080
4174
  const i18n = ctx.i18n ?? DEFAULT_I18N;
4081
4175
  const subtitle = record.subtitle ?? (isRuleRecord ? null : ruleSummary) ?? (tone === "missing" ? i18n.subtitleEmpty : tone === "own" ? i18n.subtitleConfigured : i18n.subtitleInherited);
4082
4176
  return /* @__PURE__ */ jsxs(
@@ -4084,7 +4178,7 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4084
4178
  {
4085
4179
  "data-selected": selected ? "true" : "false",
4086
4180
  "data-tone": tone,
4087
- className: cn("ra-row", compact && "ra-row-compact"),
4181
+ className: cn("ra-row", compact && "ra-row-compact", isRuleRecord && !compact && "ra-row-rule"),
4088
4182
  style: compact ? { paddingTop: "0.4rem", paddingBottom: "0.4rem" } : void 0,
4089
4183
  children: [
4090
4184
  /* @__PURE__ */ jsxs("button", { type: "button", onClick: onSelect, className: "ra-row-hit", children: [
@@ -4098,7 +4192,7 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4098
4192
  }
4099
4193
  ) }),
4100
4194
  /* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
4101
- /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: record.label }),
4195
+ !(isRuleRecord && !compact) && /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: record.label }),
4102
4196
  !compact && isRuleRecord && /* @__PURE__ */ jsxs(
4103
4197
  "div",
4104
4198
  {
@@ -4106,10 +4200,10 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4106
4200
  title: ruleSummary ?? void 0,
4107
4201
  "aria-label": ruleSummary ?? void 0,
4108
4202
  children: [
4109
- ruleClauses.slice(0, 4).map((clause, i) => /* @__PURE__ */ jsx("span", { className: "ra-rule-chip", children: clause.label }, `${clause.facetKey}-${i}`)),
4110
- ruleClauses.length > 4 && /* @__PURE__ */ jsxs("span", { className: "ra-rule-chip ra-rule-chip-more", children: [
4203
+ displayClauses.slice(0, 4).map((clause, i) => /* @__PURE__ */ jsx("span", { className: "ra-rule-chip", children: clause.label }, `${clause.facetKey}-${i}`)),
4204
+ displayClauses.length > 4 && /* @__PURE__ */ jsxs("span", { className: "ra-rule-chip ra-rule-chip-more", children: [
4111
4205
  "+",
4112
- ruleClauses.length - 4
4206
+ displayClauses.length - 4
4113
4207
  ] })
4114
4208
  ]
4115
4209
  }
@@ -4144,6 +4238,25 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
4144
4238
  }
4145
4239
  )
4146
4240
  ] }),
4241
+ historyCount && historyCount > 0 && onToggleHistory ? /* @__PURE__ */ jsxs(
4242
+ "button",
4243
+ {
4244
+ type: "button",
4245
+ className: "ra-row-history-pill",
4246
+ "data-expanded": historyExpanded ? "true" : "false",
4247
+ onClick: (e) => {
4248
+ e.stopPropagation();
4249
+ onToggleHistory();
4250
+ },
4251
+ "aria-expanded": historyExpanded,
4252
+ "aria-label": historyExpanded ? `Hide ${historyCount} archived` : `Show ${historyCount} archived`,
4253
+ title: historyExpanded ? `Hide ${historyCount} archived` : `Show ${historyCount} archived`,
4254
+ children: [
4255
+ /* @__PURE__ */ jsx("span", { className: "ra-row-history-pill-count", children: historyCount }),
4256
+ /* @__PURE__ */ jsx("span", { className: "ra-row-history-pill-label", children: "archived" })
4257
+ ]
4258
+ }
4259
+ ) : null,
4147
4260
  (onCopy || onDuplicate || onCopyAndNewRule || actions && actions.length > 0) && /* @__PURE__ */ jsx(
4148
4261
  RowContextMenu,
4149
4262
  {
@@ -4205,7 +4318,7 @@ var RecordList = ({
4205
4318
  buttons[nextIdx].scrollIntoView({ block: "nearest" });
4206
4319
  }
4207
4320
  }, []);
4208
- const buildCtx = (item) => {
4321
+ const buildCtx = (item, groupFacetKeys) => {
4209
4322
  const cb = rowClipboard ? rowClipboard(item) : null;
4210
4323
  const extraActions = rowActions ? rowActions(item) ?? void 0 : void 0;
4211
4324
  const itemAnchorKey = anchorKey(item.scope);
@@ -4222,6 +4335,7 @@ var RecordList = ({
4222
4335
  isDirty: dirtyIdMatch || dirtyAnchorMatch || idInStore || anchorInStore,
4223
4336
  hasError: errorInStore,
4224
4337
  i18n,
4338
+ groupFacetKeys,
4225
4339
  ...cb ?? {},
4226
4340
  actions: extraActions && extraActions.length > 0 ? extraActions : void 0
4227
4341
  };
@@ -4249,43 +4363,30 @@ var RecordList = ({
4249
4363
  return next;
4250
4364
  });
4251
4365
  }, []);
4252
- const renderItems = (rows) => {
4366
+ const renderItems = (rows, groupFacetKeys) => {
4253
4367
  const compact = presentation === "compact";
4254
4368
  return /* @__PURE__ */ jsx("ul", { children: rows.map((item, idx) => {
4255
- const ctx = buildCtx(item);
4369
+ const ctx = buildCtx(item, groupFacetKeys);
4256
4370
  const key = item.id ?? (anchorKey(item.scope) || `pos:${idx}`);
4257
4371
  const sKey = historyBySlot ? slotKey(item) : null;
4258
4372
  const history = sKey ? historyBySlot.get(sKey) : void 0;
4259
4373
  const expanded = sKey ? expandedSlots.has(sKey) : false;
4260
- const showLabel = i18n?.historyDisclosureShow ?? "Show {n} archived";
4261
- const hideLabel = i18n?.historyDisclosureHide ?? "Hide {n} archived";
4262
- const label = (expanded ? hideLabel : showLabel).replace("{n}", String(history?.length ?? 0));
4374
+ const historyCount = history?.length ?? 0;
4375
+ const ctxWithHistory = historyCount > 0 && sKey ? { ...ctx, historyCount, historyExpanded: expanded, onToggleHistory: () => toggleSlot(sKey) } : ctx;
4263
4376
  return /* @__PURE__ */ jsxs("li", { children: [
4264
- renderListRow ? renderListRow(item, ctx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: item, ctx, compact }),
4265
- history && history.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "ra-history-block", children: [
4266
- expanded ? /* @__PURE__ */ jsx("ul", { className: "ra-history-rows", "aria-label": "Archived records", children: history.map((h, hIdx) => {
4267
- const hCtx = buildCtx(h);
4268
- const hKey = h.id ?? `hist:${idx}:${hIdx}`;
4269
- const badged = {
4270
- ...h,
4271
- badges: [
4272
- ...h.badges ?? [],
4273
- { label: h.lifecycleStatus ?? "archived", tone: "warning" }
4274
- ]
4275
- };
4276
- return /* @__PURE__ */ jsx("li", { className: "ra-history-row", "data-history": "true", children: renderListRow ? renderListRow(badged, hCtx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: badged, ctx: hCtx, compact }) }, hKey);
4277
- }) }) : null,
4278
- /* @__PURE__ */ jsx(
4279
- "button",
4280
- {
4281
- type: "button",
4282
- className: "ra-history-disclosure",
4283
- onClick: () => sKey && toggleSlot(sKey),
4284
- "aria-expanded": expanded,
4285
- children: label
4286
- }
4287
- )
4288
- ] }) : null
4377
+ renderListRow ? renderListRow(item, ctxWithHistory) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: item, ctx: ctxWithHistory, compact }),
4378
+ history && history.length > 0 ? /* @__PURE__ */ jsx("div", { className: "ra-history-block", children: expanded ? /* @__PURE__ */ jsx("ul", { className: "ra-history-rows", "aria-label": "Archived records", children: history.map((h, hIdx) => {
4379
+ const hCtx = buildCtx(h, groupFacetKeys);
4380
+ const hKey = h.id ?? `hist:${idx}:${hIdx}`;
4381
+ const badged = {
4382
+ ...h,
4383
+ badges: [
4384
+ ...h.badges ?? [],
4385
+ { label: h.lifecycleStatus ?? "archived", tone: "warning" }
4386
+ ]
4387
+ };
4388
+ return /* @__PURE__ */ jsx("li", { className: "ra-history-row", "data-history": "true", children: renderListRow ? renderListRow(badged, hCtx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: badged, ctx: hCtx, compact }) }, hKey);
4389
+ }) }) : null }) : null
4289
4390
  ] }, key);
4290
4391
  }) });
4291
4392
  };
@@ -4358,7 +4459,7 @@ var GroupedList = ({
4358
4459
  }
4359
4460
  ) : null
4360
4461
  ] }),
4361
- /* @__PURE__ */ jsx("div", { className: "ra-group-body", children: renderItems(g.items) })
4462
+ /* @__PURE__ */ jsx("div", { className: "ra-group-body", children: renderItems(g.items, g.facetKeys) })
4362
4463
  ] }, g.key);
4363
4464
  }) });
4364
4465
  };
@@ -8937,8 +9038,12 @@ var EditorPoolBody = ({
8937
9038
  }) => {
8938
9039
  const ctx = useEditorSlotContext(editorId);
8939
9040
  if (!ctx) return null;
8940
- return renderEditor(ctx);
9041
+ return /* @__PURE__ */ jsx(CallbackRenderer, { render: renderEditor, args: ctx });
8941
9042
  };
9043
+ var CallbackRenderer = ({
9044
+ render,
9045
+ args
9046
+ }) => /* @__PURE__ */ jsx(Fragment, { children: render(args) });
8942
9047
  var TOP_LEVEL_SCOPES = ["collection", "rule", "product"];
8943
9048
  var OPT_IN_TOP_LEVEL_SCOPES = ["all"];
8944
9049
  var DRAFT_ID3 = "__draft__";
@@ -8951,16 +9056,39 @@ var coerceDraftItemId2 = (generateItemId) => {
8951
9056
  const candidate = generateItemId ? generateItemId() : mintDraftItemId2();
8952
9057
  return isDraftId3(candidate) ? candidate : `draft:${candidate}`;
8953
9058
  };
8954
- var productItemToSummary = (p, configured) => {
9059
+ var productItemToSummary = (p, buckets, i18n) => {
8955
9060
  const ref = buildRef({ productId: p.id });
9061
+ const total = buckets ? buckets.active + buckets.draft + buckets.archived + buckets.other : 0;
9062
+ const hasAnyActive = (buckets?.active ?? 0) > 0;
9063
+ let status;
9064
+ let toneHint;
9065
+ let subtitle = p.sku ?? void 0;
9066
+ if (hasAnyActive) {
9067
+ status = "configured";
9068
+ } else if (total === 0) {
9069
+ status = "empty";
9070
+ } else {
9071
+ status = "partial";
9072
+ if ((buckets.archived ?? 0) > 0 && (buckets.draft ?? 0) === 0) {
9073
+ toneHint = "muted";
9074
+ subtitle = i18n.subtitleArchivedOnly;
9075
+ } else if ((buckets.draft ?? 0) > 0 && (buckets.archived ?? 0) === 0) {
9076
+ toneHint = "warning";
9077
+ subtitle = i18n.subtitleDraftOnly;
9078
+ } else {
9079
+ toneHint = "warning";
9080
+ subtitle = (buckets.draft ?? 0) > 0 ? i18n.subtitleDraftOnly : i18n.subtitleArchivedOnly;
9081
+ }
9082
+ }
8956
9083
  return {
8957
9084
  id: null,
8958
9085
  ref,
8959
9086
  scope: parseRef(ref),
8960
9087
  data: null,
8961
- status: configured?.has(p.id) ? "configured" : "empty",
9088
+ status,
8962
9089
  label: p.name,
8963
- subtitle: p.sku ?? void 0
9090
+ subtitle,
9091
+ toneHint
8964
9092
  };
8965
9093
  };
8966
9094
  function RecordsAdminShell(props) {
@@ -9121,7 +9249,10 @@ function RecordsAdminShellInner(props) {
9121
9249
  labels: actionLabels,
9122
9250
  icons: actionIcons
9123
9251
  } = actions ?? {};
9124
- const i18n = { ...DEFAULT_I18N, ...i18nOverride ?? {} };
9252
+ const i18n = useMemo(
9253
+ () => ({ ...DEFAULT_I18N, ...i18nOverride ?? {} }),
9254
+ [i18nOverride]
9255
+ );
9125
9256
  const icons = useMemo(() => mergeIcons(iconsOverride), [iconsOverride]);
9126
9257
  const deepLinkState = useDeepLinkState(deepLink);
9127
9258
  const multiOpenWarnedRef = useRef(false);
@@ -9181,7 +9312,7 @@ function RecordsAdminShellInner(props) {
9181
9312
  };
9182
9313
  }, [queryClient, recordChangeRef]);
9183
9314
  const probe = useScopeProbe({ SL, collectionId });
9184
- const scopeCounts = useScopeCounts({ ctx });
9315
+ const scopeCounts = useScopeCounts({ ctx, activeStatuses: resolvedActiveStatuses });
9185
9316
  const topLevelScopes = useMemo(() => {
9186
9317
  const requested = requestedScopes ?? [];
9187
9318
  const allowed = /* @__PURE__ */ new Set([...TOP_LEVEL_SCOPES, ...OPT_IN_TOP_LEVEL_SCOPES]);
@@ -9931,7 +10062,13 @@ function RecordsAdminShellInner(props) {
9931
10062
  ] });
9932
10063
  }
9933
10064
  const previewAnchorRef = previewReopenAnchorRef;
9934
- const previewBody = renderPreview && effectivePreviewScope ? renderPreview({ resolved: editorCtx.value, previewScope: effectivePreviewScope }) : null;
10065
+ const previewBody = renderPreview && effectivePreviewScope ? /* @__PURE__ */ jsx(
10066
+ CallbackRenderer,
10067
+ {
10068
+ render: renderPreview,
10069
+ args: { resolved: editorCtx.value, previewScope: effectivePreviewScope }
10070
+ }
10071
+ ) : null;
9935
10072
  const scopePicker = previewScopePicker && effectivePreviewScope ? /* @__PURE__ */ jsx(
9936
10073
  PreviewScopePicker,
9937
10074
  {
@@ -10089,7 +10226,13 @@ function RecordsAdminShellInner(props) {
10089
10226
  renderEditor
10090
10227
  }
10091
10228
  ),
10092
- fallback: renderEditor(editorCtx)
10229
+ fallback: /* @__PURE__ */ jsx(
10230
+ CallbackRenderer,
10231
+ {
10232
+ render: renderEditor,
10233
+ args: editorCtx
10234
+ }
10235
+ )
10093
10236
  }
10094
10237
  )
10095
10238
  }
@@ -10214,7 +10357,12 @@ function RecordsAdminShellInner(props) {
10214
10357
  }];
10215
10358
  }
10216
10359
  const configured = scopeCounts.productIds;
10217
- const all = productBrowse.items.map((p) => productItemToSummary(p, configured));
10360
+ const lifecycle = scopeCounts.productLifecycle;
10361
+ const lifecycleI18n = {
10362
+ subtitleArchivedOnly: i18n.subtitleArchivedOnly,
10363
+ subtitleDraftOnly: i18n.subtitleDraftOnly
10364
+ };
10365
+ const all = productBrowse.items.map((p) => productItemToSummary(p, lifecycle.get(p.id), lifecycleI18n));
10218
10366
  const isConfigured = (s) => {
10219
10367
  const pid = s.scope.productId;
10220
10368
  return !!pid && configured.has(pid);
@@ -10231,7 +10379,10 @@ function RecordsAdminShellInner(props) {
10231
10379
  productBrowse.items,
10232
10380
  pinnedProduct.item,
10233
10381
  scopeCounts.productIds,
10234
- filter
10382
+ scopeCounts.productLifecycle,
10383
+ filter,
10384
+ i18n.subtitleArchivedOnly,
10385
+ i18n.subtitleDraftOnly
10235
10386
  ]);
10236
10387
  const isRuleTab = activeScope === "rule";
10237
10388
  const isGlobalTab = activeScope === "collection";
@@ -10269,7 +10420,7 @@ function RecordsAdminShellInner(props) {
10269
10420
  if (keys.length === 0) return null;
10270
10421
  const key = `facets:${keys.join("|")}`;
10271
10422
  const label2 = keys.map((k) => ruleLabelLookup?.facetLabel?.(k) ?? k).join(" \xB7 ");
10272
- return { key, label: label2 };
10423
+ return { key, label: label2, facetKeys: keys };
10273
10424
  };
10274
10425
  }, [groupBy, isRuleTab, isAllTab, isCollection, ruleLabelLookup]);
10275
10426
  const [bulkRuleEditTarget, setBulkRuleEditTarget] = useState(null);
@@ -11117,7 +11268,7 @@ function RecordsAdminShellInner(props) {
11117
11268
  isGlobalTab && !isCollection && !hasSingletonConflicts ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
11118
11269
  leftLoading && /* @__PURE__ */ jsx(LoadingState, {}),
11119
11270
  !leftLoading && leftError && /* @__PURE__ */ jsx(ErrorState, { error: leftError }),
11120
- !leftLoading && !leftError && decoratedLeftItems.length === 0 && (renderEmptyState ? renderEmptyState({ scope: activeScope }) : /* @__PURE__ */ jsx(
11271
+ !leftLoading && !leftError && decoratedLeftItems.length === 0 && (renderEmptyState ? /* @__PURE__ */ jsx(CallbackRenderer, { render: renderEmptyState, args: { scope: activeScope } }) : /* @__PURE__ */ jsx(
11121
11272
  EmptyState,
11122
11273
  {
11123
11274
  icon: search ? icons.empty.search : icons.empty.default,