@proveanything/smartlinks-utils-ui 0.12.5 → 0.12.7

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,11 @@
1
+ import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-KA4MKRHL.js';
2
+ export { bulkDelete, bulkUpsert, createRecord, getRecordById, listRecords, matchRecords, parsedRefToScope, parsedRefToTarget, removeRecord, restoreRecord, scopesEqual, upsertRecord } from '../../chunk-KA4MKRHL.js';
1
3
  import { useIntroState, AdminPageHeader } from '../../chunk-3RRHM4LP.js';
2
- import { assertComponentStylesLoaded } from '../../chunk-OLYC54YT.js';
3
- import '../../chunk-5UQQYXCX.js';
4
4
  import { FacetRuleEditor } from '../../chunk-JMCV6FOW.js';
5
5
  import { useFacets } from '../../chunk-4LHF5JB7.js';
6
+ import { assertComponentStylesLoaded } from '../../chunk-OLYC54YT.js';
7
+ import '../../chunk-5UQQYXCX.js';
6
8
  import { cn } from '../../chunk-L7FQ52F5.js';
7
- import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-KA4MKRHL.js';
8
- export { bulkDelete, bulkUpsert, createRecord, getRecordById, listRecords, matchRecords, parsedRefToScope, parsedRefToTarget, removeRecord, restoreRecord, scopesEqual, upsertRecord } from '../../chunk-KA4MKRHL.js';
9
9
  import { createContext, useMemo, useState, useEffect, useCallback, useRef, isValidElement, useLayoutEffect, useContext, useSyncExternalStore, createElement } from 'react';
10
10
  import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, List, SlidersHorizontal, Globe, Tag, Boxes, Layers, Package, Target, Check, Rows3, ChevronRight, Eraser, FilePlus2, CopyPlus, ClipboardPaste, Box, X, Search, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, CornerDownLeft, Circle, ArrowUpDown, ArrowUp, ArrowDown, MinusCircle, XCircle, AlertCircle, Undo2, Save, Loader2, Archive, ArrowRight, Globe2, Sparkles, Settings2 } from 'lucide-react';
11
11
  import { useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
@@ -276,7 +276,15 @@ var DEFAULT_I18N = {
276
276
  conflictArchiveDuplicates: "Archive duplicates",
277
277
  conflictDeleteDuplicates: "Delete duplicates",
278
278
  conflictDeleteConfirm: "Permanently delete {n} duplicate record(s)? This cannot be undone.",
279
- conflictResolveLabel: "Resolve"
279
+ conflictResolveLabel: "Resolve",
280
+ ruleSortLabel: "Sort",
281
+ ruleSortRecent: "Recently updated",
282
+ ruleSortName: "Name",
283
+ ruleSortActiveCount: "Active count",
284
+ ruleSortHasArchived: "Has archived",
285
+ lifecycleBadgeActive: "{n} active",
286
+ lifecycleBadgeArchived: "{n} archived",
287
+ lifecycleBadgeDraft: "{n} draft"
280
288
  };
281
289
 
282
290
  // src/components/RecordsAdmin/types/presentation.ts
@@ -851,6 +859,24 @@ var useRecordList = (args) => {
851
859
  }
852
860
  return map;
853
861
  }, [historyItems]);
862
+ const ruleLifecycleCounts = useMemo(() => {
863
+ const map = /* @__PURE__ */ new Map();
864
+ for (const r of items) {
865
+ const h = ruleHash(r.facetRule ?? null);
866
+ if (!h) continue;
867
+ let bucket = map.get(h);
868
+ if (!bucket) {
869
+ bucket = { active: 0, archived: 0, draft: 0, other: 0 };
870
+ map.set(h, bucket);
871
+ }
872
+ const ls = r.lifecycleStatus;
873
+ if (ls == null || ls === "" || activeStatuses.includes(ls)) bucket.active += 1;
874
+ else if (ls === "archived") bucket.archived += 1;
875
+ else if (ls === "draft") bucket.draft += 1;
876
+ else bucket.other += 1;
877
+ }
878
+ return map;
879
+ }, [items, activeStatuses]);
854
880
  const refetch = useCallback(() => queryClient.refetchQueries({
855
881
  queryKey: [...QK_BASE2, ctx.collectionId, ctx.appId, ctx.recordType]
856
882
  }), [queryClient, ctx.collectionId, ctx.appId, ctx.recordType]);
@@ -861,6 +887,7 @@ var useRecordList = (args) => {
861
887
  activeItems,
862
888
  historyItems,
863
889
  historyBySlot,
890
+ ruleLifecycleCounts,
864
891
  total,
865
892
  counts,
866
893
  isLoading: query.isLoading,
@@ -2046,7 +2073,11 @@ function useShellClipboard(args) {
2046
2073
  const newId = create(scopeArg);
2047
2074
  if (!newId) return;
2048
2075
  setPendingPasteTarget({ kind: "record", recordId: newId });
2049
- setPendingSeed({ value: transformed, sourceLabel: source.label });
2076
+ setPendingSeed({
2077
+ value: transformed,
2078
+ sourceScope: source.scope,
2079
+ sourceLabel: source.label
2080
+ });
2050
2081
  onTelemetry?.({
2051
2082
  type: "clipboard.copy",
2052
2083
  recordType,
@@ -2076,7 +2107,11 @@ function useShellClipboard(args) {
2076
2107
  if (pendingSeed) {
2077
2108
  const seed = pendingSeed;
2078
2109
  setPendingSeed(null);
2079
- editorCtx.onChange(seed.value);
2110
+ const finalValue = onPasteOverride ? onPasteOverride(
2111
+ { value: seed.value, sourceScope: seed.sourceScope },
2112
+ { scope: editingScope, currentValue: null }
2113
+ ) ?? seed.value : seed.value;
2114
+ editorCtx.onChange(finalValue);
2080
2115
  onTelemetry?.({
2081
2116
  type: "clipboard.paste",
2082
2117
  recordType,
@@ -2089,7 +2124,7 @@ function useShellClipboard(args) {
2089
2124
  void pasteCurrent();
2090
2125
  }, 0);
2091
2126
  return () => window.clearTimeout(t);
2092
- }, [pendingPasteTarget, editingScope, isCollection, selectedItemId, selectedRecordId, pasteCurrent, pendingSeed, editorCtx, onTelemetry, recordType]);
2127
+ }, [pendingPasteTarget, editingScope, isCollection, selectedItemId, selectedRecordId, pasteCurrent, pendingSeed, editorCtx, onTelemetry, recordType, onPasteOverride]);
2093
2128
  const rowClipboard = enabled ? (record) => {
2094
2129
  const summaryHasData = record.data != null;
2095
2130
  const sourceParsed = record.scope;
@@ -6873,6 +6908,8 @@ function ItemListView({
6873
6908
  hasNextPage,
6874
6909
  isFetchingNextPage,
6875
6910
  onLoadMore,
6911
+ groupByLifecycle = false,
6912
+ lifecycle,
6876
6913
  i18n
6877
6914
  }) {
6878
6915
  const newLabel = i18n.newItem.includes("{noun}") ? i18n.newItem.replace("{noun}", itemNoun) : i18n.newItem;
@@ -6947,6 +6984,69 @@ function ItemListView({
6947
6984
  }), [ctx]);
6948
6985
  const confirmName = pendingRecord?.label ?? itemNoun;
6949
6986
  const confirmBody = i18n.deleteConfirmBody.includes("{name}") ? i18n.deleteConfirmBody.replace("{name}", confirmName) : i18n.deleteConfirmBody;
6987
+ const activeValues = useMemo(
6988
+ () => getActiveStatusValues(lifecycle),
6989
+ [lifecycle]
6990
+ );
6991
+ const buckets = useMemo(() => {
6992
+ if (!groupByLifecycle) return null;
6993
+ const map = /* @__PURE__ */ new Map();
6994
+ for (const item of visibleItems) {
6995
+ const def = resolveLifecycleStatus(item, lifecycle);
6996
+ const existing = map.get(def.value);
6997
+ if (existing) existing.items.push(item);
6998
+ else map.set(def.value, { label: def.label, items: [item] });
6999
+ }
7000
+ return Array.from(map.entries()).map(([key, v]) => ({
7001
+ key,
7002
+ label: v.label,
7003
+ items: v.items,
7004
+ isActive: activeValues.includes(key)
7005
+ })).sort((a, b) => compareLifecycleBuckets(a.key, b.key)).filter((b) => b.items.length > 0);
7006
+ }, [groupByLifecycle, visibleItems, lifecycle, activeValues]);
7007
+ const [collapsedBuckets, setCollapsedBuckets] = useState(() => /* @__PURE__ */ new Set());
7008
+ const toggleBucket = (key) => {
7009
+ setCollapsedBuckets((prev) => {
7010
+ const next = new Set(prev);
7011
+ if (next.has(key)) next.delete(key);
7012
+ else next.add(key);
7013
+ return next;
7014
+ });
7015
+ };
7016
+ const renderBody = (rows) => {
7017
+ if (renderItemList) return renderItemList(rows, guardedCtx);
7018
+ if (view === "table") {
7019
+ return /* @__PURE__ */ jsx(
7020
+ DefaultItemTable,
7021
+ {
7022
+ items: rows,
7023
+ columns: itemColumns,
7024
+ selectedId: guardedCtx.selectedId,
7025
+ onOpen: guardedCtx.onOpen,
7026
+ onDelete: guardedCtx.onDelete,
7027
+ sort,
7028
+ onToggleSort,
7029
+ rowActions,
7030
+ rowClipboard,
7031
+ i18n
7032
+ }
7033
+ );
7034
+ }
7035
+ return /* @__PURE__ */ jsx(
7036
+ DefaultItemCards,
7037
+ {
7038
+ items: rows,
7039
+ variant: view,
7040
+ selectedId: guardedCtx.selectedId,
7041
+ ctx: guardedCtx,
7042
+ renderCard: renderItemCard,
7043
+ cardSize,
7044
+ rowActions,
7045
+ rowClipboard,
7046
+ i18n
7047
+ }
7048
+ );
7049
+ };
6950
7050
  const toolbar = /* @__PURE__ */ jsxs("div", { className: "ra-item-toolbar", children: [
6951
7051
  /* @__PURE__ */ jsxs("div", { className: "ra-item-toolbar-title", children: [
6952
7052
  /* @__PURE__ */ jsx("h2", { className: "ra-display", style: { fontSize: "0.95rem", margin: 0 }, children: i18n.itemListTitle }),
@@ -7064,39 +7164,62 @@ function ItemListView({
7064
7164
  body: `No ${itemNoun}s match "${search}".`
7065
7165
  }
7066
7166
  );
7067
- } else if (renderItemList) {
7068
- body = renderItemList(visibleItems, guardedCtx);
7069
- } else if (view === "table") {
7070
- body = /* @__PURE__ */ jsx(
7071
- DefaultItemTable,
7072
- {
7073
- items: visibleItems,
7074
- columns: itemColumns,
7075
- selectedId: guardedCtx.selectedId,
7076
- onOpen: guardedCtx.onOpen,
7077
- onDelete: guardedCtx.onDelete,
7078
- sort,
7079
- onToggleSort,
7080
- rowActions,
7081
- rowClipboard,
7082
- i18n
7083
- }
7084
- );
7167
+ } else if (buckets && buckets.length > 0) {
7168
+ body = /* @__PURE__ */ jsx("div", { className: "ra-item-buckets", children: buckets.map((bucket) => {
7169
+ const open = bucket.isActive || !collapsedBuckets.has(bucket.key);
7170
+ return /* @__PURE__ */ jsxs(
7171
+ "section",
7172
+ {
7173
+ className: "ra-item-bucket",
7174
+ "data-bucket": bucket.key,
7175
+ children: [
7176
+ /* @__PURE__ */ jsxs(
7177
+ "button",
7178
+ {
7179
+ type: "button",
7180
+ className: "ra-item-bucket-header",
7181
+ onClick: () => !bucket.isActive && toggleBucket(bucket.key),
7182
+ "aria-expanded": open,
7183
+ disabled: bucket.isActive,
7184
+ style: {
7185
+ display: "flex",
7186
+ alignItems: "center",
7187
+ gap: "6px",
7188
+ width: "100%",
7189
+ padding: "6px 12px",
7190
+ background: "transparent",
7191
+ border: 0,
7192
+ borderTop: "1px solid hsl(var(--ra-border))",
7193
+ font: "inherit",
7194
+ fontSize: "11px",
7195
+ fontWeight: 600,
7196
+ textTransform: "uppercase",
7197
+ letterSpacing: "0.04em",
7198
+ color: "hsl(var(--ra-muted-text))",
7199
+ cursor: bucket.isActive ? "default" : "pointer"
7200
+ },
7201
+ children: [
7202
+ !bucket.isActive ? open ? /* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3", "aria-hidden": "true" }) : null,
7203
+ /* @__PURE__ */ jsx("span", { children: bucket.label }),
7204
+ /* @__PURE__ */ jsx("span", { style: { marginLeft: "auto", fontWeight: 500 }, children: bucket.items.length })
7205
+ ]
7206
+ }
7207
+ ),
7208
+ open && /* @__PURE__ */ jsx(
7209
+ "div",
7210
+ {
7211
+ className: "ra-item-bucket-body",
7212
+ style: bucket.isActive ? void 0 : { opacity: 0.75 },
7213
+ children: renderBody(bucket.items)
7214
+ }
7215
+ )
7216
+ ]
7217
+ },
7218
+ bucket.key
7219
+ );
7220
+ }) });
7085
7221
  } else {
7086
- body = /* @__PURE__ */ jsx(
7087
- DefaultItemCards,
7088
- {
7089
- items: visibleItems,
7090
- variant: view,
7091
- selectedId: guardedCtx.selectedId,
7092
- ctx: guardedCtx,
7093
- renderCard: renderItemCard,
7094
- cardSize,
7095
- rowActions,
7096
- rowClipboard,
7097
- i18n
7098
- }
7099
- );
7222
+ body = renderBody(visibleItems);
7100
7223
  }
7101
7224
  return /* @__PURE__ */ jsxs("div", { className: "ra-item-list", children: [
7102
7225
  toolbar,
@@ -7239,10 +7362,42 @@ function SiblingRail({
7239
7362
  isFetchingNextPage,
7240
7363
  onLoadMore,
7241
7364
  rowClipboard,
7242
- rowActions
7365
+ rowActions,
7366
+ groupByLifecycle = false,
7367
+ lifecycle
7243
7368
  }) {
7244
7369
  const ruleLabelLookup = useRuleLabelLookup();
7245
7370
  const newLabel = i18n.newItem.includes("{noun}") ? i18n.newItem.replace("{noun}", itemNoun ?? "item") : i18n.newItem;
7371
+ const activeValues = useMemo(
7372
+ () => getActiveStatusValues(lifecycle),
7373
+ [lifecycle]
7374
+ );
7375
+ const buckets = useMemo(() => {
7376
+ if (!groupByLifecycle) return null;
7377
+ const map = /* @__PURE__ */ new Map();
7378
+ for (const item of items) {
7379
+ const def = resolveLifecycleStatus(item, lifecycle);
7380
+ const key = def.value;
7381
+ const existing = map.get(key);
7382
+ if (existing) existing.items.push(item);
7383
+ else map.set(key, { label: def.label, items: [item] });
7384
+ }
7385
+ return Array.from(map.entries()).map(([key, v]) => ({
7386
+ key,
7387
+ label: v.label,
7388
+ items: v.items,
7389
+ isActive: activeValues.includes(key)
7390
+ })).sort((a, b) => compareLifecycleBuckets(a.key, b.key)).filter((b) => b.items.length > 0);
7391
+ }, [groupByLifecycle, items, lifecycle, activeValues]);
7392
+ const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
7393
+ const toggleBucket = (key) => {
7394
+ setCollapsed((prev) => {
7395
+ const next = new Set(prev);
7396
+ if (next.has(key)) next.delete(key);
7397
+ else next.add(key);
7398
+ return next;
7399
+ });
7400
+ };
7246
7401
  return /* @__PURE__ */ jsxs("div", { className: "ra-sibling-rail", children: [
7247
7402
  (onBack || contextKind) && /* @__PURE__ */ jsxs("div", { className: "ra-sibling-context", children: [
7248
7403
  onBack && /* @__PURE__ */ jsx(
@@ -7268,79 +7423,125 @@ function SiblingRail({
7268
7423
  isLoading && /* @__PURE__ */ jsx(LoadingState, {}),
7269
7424
  !isLoading && error && /* @__PURE__ */ jsx(ErrorState, { error }),
7270
7425
  !isLoading && !error && items.length === 0 && /* @__PURE__ */ jsx(EmptyState, { title: i18n.noItemsTitle, body: i18n.noItemsBody }),
7271
- !isLoading && !error && items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "ra-sibling-list", children: items.map((item, idx) => {
7272
- const id = item.itemId ?? "";
7273
- const key = item.id ?? (id || anchorKey(item.scope) || `pos:${idx}`);
7274
- const akey = anchorKey(item.scope);
7275
- const selected = selectedItemId === id;
7276
- const isDirty = !!(id && dirtyKeys?.has(id) || akey && dirtyKeys?.has(akey));
7277
- const hasError = !!(id && errorKeys?.has(id) || akey && errorKeys?.has(akey));
7278
- const ruleClauses = item.facetRule ? summarizeFacetRule(item.facetRule, ruleLabelLookup) : [];
7279
- const isTargeted = ruleClauses.length > 0;
7280
- const ruleSummary = isTargeted ? ruleClauses.map((c) => c.label).join(" \xB7 ") : null;
7281
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", { className: "ra-row-shell", "data-selected": selected, children: [
7282
- /* @__PURE__ */ jsxs(
7283
- "button",
7284
- {
7285
- type: "button",
7286
- onClick: () => onSelect(id),
7287
- className: "ra-row",
7288
- "data-selected": selected,
7289
- children: [
7290
- /* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
7291
- /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: item.label }),
7292
- item.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: item.subtitle })
7293
- ] }),
7294
- isTargeted && /* @__PURE__ */ jsx(
7295
- "span",
7296
- {
7297
- className: "ra-row-rule-pip",
7298
- title: ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
7299
- "aria-label": ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
7300
- children: /* @__PURE__ */ jsx(Target, { className: "w-3 h-3", "aria-hidden": "true" })
7301
- }
7302
- ),
7303
- hasError ? /* @__PURE__ */ jsx(
7304
- "span",
7305
- {
7306
- className: "ra-error-pip",
7307
- title: "Save failed",
7308
- "aria-label": "Save failed"
7309
- }
7310
- ) : isDirty ? /* @__PURE__ */ jsx(
7311
- "span",
7312
- {
7313
- className: "ra-dirty-pip",
7314
- title: "Unsaved changes",
7315
- "aria-label": "Unsaved changes"
7316
- }
7317
- ) : null
7318
- ]
7319
- }
7320
- ),
7321
- (() => {
7322
- const cb = rowClipboard ? rowClipboard(item) : null;
7323
- const extra = rowActions ? rowActions(item) ?? void 0 : void 0;
7324
- if (!cb?.onCopy && !cb?.onDuplicate && !cb?.onCopyAndNewRule && !(extra && extra.length)) {
7325
- return null;
7326
- }
7327
- return /* @__PURE__ */ jsx(
7328
- RowContextMenu,
7426
+ !isLoading && !error && items.length > 0 && (() => {
7427
+ const renderRow = (item, idx, dimmed) => {
7428
+ const id = item.itemId ?? "";
7429
+ const key = item.id ?? (id || anchorKey(item.scope) || `pos:${idx}`);
7430
+ const akey = anchorKey(item.scope);
7431
+ const selected = selectedItemId === id;
7432
+ const isDirty = !!(id && dirtyKeys?.has(id) || akey && dirtyKeys?.has(akey));
7433
+ const hasError = !!(id && errorKeys?.has(id) || akey && errorKeys?.has(akey));
7434
+ const ruleClauses = item.facetRule ? summarizeFacetRule(item.facetRule, ruleLabelLookup) : [];
7435
+ const isTargeted = ruleClauses.length > 0;
7436
+ const ruleSummary = isTargeted ? ruleClauses.map((c) => c.label).join(" \xB7 ") : null;
7437
+ return /* @__PURE__ */ jsx("li", { "data-dimmed": dimmed || void 0, children: /* @__PURE__ */ jsxs("div", { className: "ra-row-shell", "data-selected": selected, children: [
7438
+ /* @__PURE__ */ jsxs(
7439
+ "button",
7329
7440
  {
7330
- onCopy: cb?.onCopy,
7331
- onDuplicate: cb?.onDuplicate,
7332
- onCopyAndNewRule: cb?.onCopyAndNewRule,
7333
- actions: extra,
7334
- i18n: {
7335
- copy: i18n.copy,
7336
- duplicateAction: i18n.duplicateAction,
7337
- copyAndNewRuleAction: i18n.copyAndNewRuleAction
7441
+ type: "button",
7442
+ onClick: () => onSelect(id),
7443
+ className: "ra-row",
7444
+ "data-selected": selected,
7445
+ style: dimmed ? { opacity: 0.7 } : void 0,
7446
+ children: [
7447
+ /* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
7448
+ /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: item.label }),
7449
+ item.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: item.subtitle })
7450
+ ] }),
7451
+ isTargeted && /* @__PURE__ */ jsx(
7452
+ "span",
7453
+ {
7454
+ className: "ra-row-rule-pip",
7455
+ title: ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
7456
+ "aria-label": ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
7457
+ children: /* @__PURE__ */ jsx(Target, { className: "w-3 h-3", "aria-hidden": "true" })
7458
+ }
7459
+ ),
7460
+ hasError ? /* @__PURE__ */ jsx(
7461
+ "span",
7462
+ {
7463
+ className: "ra-error-pip",
7464
+ title: "Save failed",
7465
+ "aria-label": "Save failed"
7466
+ }
7467
+ ) : isDirty ? /* @__PURE__ */ jsx(
7468
+ "span",
7469
+ {
7470
+ className: "ra-dirty-pip",
7471
+ title: "Unsaved changes",
7472
+ "aria-label": "Unsaved changes"
7473
+ }
7474
+ ) : null
7475
+ ]
7476
+ }
7477
+ ),
7478
+ (() => {
7479
+ const cb = rowClipboard ? rowClipboard(item) : null;
7480
+ const extra = rowActions ? rowActions(item) ?? void 0 : void 0;
7481
+ if (!cb?.onCopy && !cb?.onDuplicate && !cb?.onCopyAndNewRule && !(extra && extra.length)) {
7482
+ return null;
7483
+ }
7484
+ return /* @__PURE__ */ jsx(
7485
+ RowContextMenu,
7486
+ {
7487
+ onCopy: cb?.onCopy,
7488
+ onDuplicate: cb?.onDuplicate,
7489
+ onCopyAndNewRule: cb?.onCopyAndNewRule,
7490
+ actions: extra,
7491
+ i18n: {
7492
+ copy: i18n.copy,
7493
+ duplicateAction: i18n.duplicateAction,
7494
+ copyAndNewRuleAction: i18n.copyAndNewRuleAction
7495
+ }
7338
7496
  }
7497
+ );
7498
+ })()
7499
+ ] }) }, key);
7500
+ };
7501
+ if (!buckets) {
7502
+ return /* @__PURE__ */ jsx("ul", { className: "ra-sibling-list", children: items.map((item, idx) => renderRow(item, idx, false)) });
7503
+ }
7504
+ return /* @__PURE__ */ jsx("div", { className: "ra-sibling-buckets", children: buckets.map((bucket) => {
7505
+ const open = bucket.isActive || !collapsed.has(bucket.key);
7506
+ return /* @__PURE__ */ jsxs("section", { className: "ra-sibling-bucket", "data-bucket": bucket.key, children: [
7507
+ /* @__PURE__ */ jsxs(
7508
+ "button",
7509
+ {
7510
+ type: "button",
7511
+ className: "ra-sibling-bucket-header",
7512
+ onClick: () => !bucket.isActive && toggleBucket(bucket.key),
7513
+ "aria-expanded": open,
7514
+ disabled: bucket.isActive,
7515
+ style: {
7516
+ display: "flex",
7517
+ alignItems: "center",
7518
+ gap: "6px",
7519
+ width: "100%",
7520
+ padding: "6px 10px",
7521
+ background: "transparent",
7522
+ border: 0,
7523
+ borderTop: "1px solid hsl(var(--ra-border))",
7524
+ font: "inherit",
7525
+ fontSize: "11px",
7526
+ fontWeight: 600,
7527
+ textTransform: "uppercase",
7528
+ letterSpacing: "0.04em",
7529
+ color: "hsl(var(--ra-muted-text))",
7530
+ cursor: bucket.isActive ? "default" : "pointer"
7531
+ },
7532
+ children: [
7533
+ !bucket.isActive ? open ? /* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3", "aria-hidden": "true" }) : null,
7534
+ /* @__PURE__ */ jsx("span", { children: bucket.label }),
7535
+ /* @__PURE__ */ jsx("span", { style: { marginLeft: "auto", fontWeight: 500 }, children: bucket.items.length })
7536
+ ]
7339
7537
  }
7340
- );
7341
- })()
7342
- ] }) }, key);
7343
- }) })
7538
+ ),
7539
+ open && /* @__PURE__ */ jsx("ul", { className: "ra-sibling-list", children: bucket.items.map(
7540
+ (item, idx) => renderRow(item, idx, !bucket.isActive)
7541
+ ) })
7542
+ ] }, bucket.key);
7543
+ }) });
7544
+ })()
7344
7545
  ] }),
7345
7546
  onLoadMore && /* @__PURE__ */ jsx(
7346
7547
  LoadMoreFooter,
@@ -8884,6 +9085,7 @@ function RecordsAdminShellInner(props) {
8884
9085
  onTelemetry?.({ type: "presentation.change", recordType, from: presentation, to: next });
8885
9086
  setPresentation(next);
8886
9087
  }, [onTelemetry, recordType, presentation, setPresentation]);
9088
+ const [ruleSort, setRuleSort] = useState("recent");
8887
9089
  const [itemView, setItemView] = useItemViewPref({
8888
9090
  appId,
8889
9091
  recordType,
@@ -9524,7 +9726,7 @@ function RecordsAdminShellInner(props) {
9524
9726
  setDrawerOpen,
9525
9727
  sidePreviewOpen,
9526
9728
  setSidePreviewOpen,
9527
- editorHeaderLabel,
9729
+ editorHeaderLabel: editorHeaderLabelRaw,
9528
9730
  editorHeaderSubtitle,
9529
9731
  editorHeaderMeta
9530
9732
  } = useShellPreviewPane({
@@ -9533,6 +9735,14 @@ function RecordsAdminShellInner(props) {
9533
9735
  selectedProductId,
9534
9736
  productBrowseItems: productLookupItems
9535
9737
  });
9738
+ const editorHeaderLabel = useMemo(() => {
9739
+ if (editorHeaderLabelRaw) return editorHeaderLabelRaw;
9740
+ if (isCollection && selectedItemId && !isDraftId3(selectedItemId)) {
9741
+ const hit = collectionItems.items.find((r) => r.id === selectedItemId || r.itemId === selectedItemId);
9742
+ if (hit?.label) return hit.label;
9743
+ }
9744
+ return void 0;
9745
+ }, [editorHeaderLabelRaw, isCollection, selectedItemId, collectionItems.items]);
9536
9746
  const shellClipboard = useShellClipboard({
9537
9747
  enabled: !!enableClipboard,
9538
9748
  appId,
@@ -10053,6 +10263,11 @@ function RecordsAdminShellInner(props) {
10053
10263
  const onRuleWizardNext = useCallback(() => {
10054
10264
  if (cardinality === "collection") {
10055
10265
  setRuleWizardStep(2);
10266
+ if (ruleWizardSeedMode) {
10267
+ skipNextItemResetRef.current = true;
10268
+ const id = coerceDraftItemId2(generateItemId);
10269
+ setSelectedItemId(id);
10270
+ }
10056
10271
  } else {
10057
10272
  setRuleWizardStep(2);
10058
10273
  if (ruleWizardSeedMode) {
@@ -10060,7 +10275,7 @@ function RecordsAdminShellInner(props) {
10060
10275
  setSelectedRecordId(DRAFT_ID3);
10061
10276
  }
10062
10277
  }
10063
- }, [cardinality, ruleWizardSeedMode, mintRuleWizardDraftKey]);
10278
+ }, [cardinality, ruleWizardSeedMode, mintRuleWizardDraftKey, generateItemId, skipNextItemResetRef]);
10064
10279
  const startRuleWizardDraft = useCallback((seed) => {
10065
10280
  setRuleWizardSeedMode(seed);
10066
10281
  setRuleWizardDraftKey(mintRuleWizardDraftKey());
@@ -10114,17 +10329,51 @@ function RecordsAdminShellInner(props) {
10114
10329
  buckets.set(hash, { rep: item, count: 1 });
10115
10330
  }
10116
10331
  }
10117
- return Array.from(buckets.values()).map(({ rep, count }) => ({
10118
- ...rep,
10119
- // Title carries the count + identity; the rule chips below render
10120
- // the friendly facet/value labels (via the lookup) so we don't
10121
- // repeat the same information in two places. Without this, a row
10122
- // had three echoes of the same rule (raw-key title, friendly chip,
10123
- // and the right-pane "Rule · …" header).
10124
- label: `${count} ${itemNoun}${count === 1 ? "" : "s"}`,
10125
- subtitle: void 0
10126
- }));
10127
- }, [isRuleTab, isCollection, filteredRuleItems, itemNoun]);
10332
+ const counts = recordList.ruleLifecycleCounts;
10333
+ return Array.from(buckets.entries()).map(([hash, { rep, count }]) => {
10334
+ const lc = counts.get(hash) ?? { active: 0, archived: 0, draft: 0, other: 0 };
10335
+ const lifecycleBadges = [];
10336
+ if (lc.active > 0) lifecycleBadges.push({
10337
+ label: i18n.lifecycleBadgeActive.replace("{n}", String(lc.active)),
10338
+ tone: "success"
10339
+ });
10340
+ if (lc.draft > 0) lifecycleBadges.push({
10341
+ label: i18n.lifecycleBadgeDraft.replace("{n}", String(lc.draft)),
10342
+ tone: "warning"
10343
+ });
10344
+ if (lc.archived > 0) lifecycleBadges.push({
10345
+ label: i18n.lifecycleBadgeArchived.replace("{n}", String(lc.archived)),
10346
+ tone: "neutral"
10347
+ });
10348
+ return {
10349
+ ...rep,
10350
+ // Title carries the count + identity; the rule chips below render
10351
+ // the friendly facet/value labels (via the lookup) so we don't
10352
+ // repeat the same information in two places.
10353
+ label: `${count} ${itemNoun}${count === 1 ? "" : "s"}`,
10354
+ subtitle: void 0,
10355
+ badges: [...rep.badges ?? [], ...lifecycleBadges],
10356
+ // Stash counts on the row for the sort comparator below.
10357
+ __lifecycleCounts: lc
10358
+ };
10359
+ });
10360
+ }, [isRuleTab, isCollection, filteredRuleItems, itemNoun, recordList.ruleLifecycleCounts, i18n]);
10361
+ const sortedCollectionRuleRailItems = useMemo(() => {
10362
+ if (!isRuleTab || !isCollection || ruleSort === "recent") return collectionRuleRailItems;
10363
+ const arr = [...collectionRuleRailItems];
10364
+ const counts = (r) => r.__lifecycleCounts ?? { active: 0, archived: 0 };
10365
+ if (ruleSort === "name") arr.sort((a, b) => a.label.localeCompare(b.label));
10366
+ else if (ruleSort === "activeCount") arr.sort((a, b) => counts(b).active - counts(a).active);
10367
+ else if (ruleSort === "hasArchived") {
10368
+ arr.sort((a, b) => {
10369
+ const ah = counts(a).archived > 0 ? 1 : 0;
10370
+ const bh = counts(b).archived > 0 ? 1 : 0;
10371
+ if (ah !== bh) return bh - ah;
10372
+ return counts(b).archived - counts(a).archived;
10373
+ });
10374
+ }
10375
+ return arr;
10376
+ }, [collectionRuleRailItems, isRuleTab, isCollection, ruleSort]);
10128
10377
  const activeRuleSummary = useMemo(() => {
10129
10378
  if (!isRuleTab || !isCollection) return null;
10130
10379
  if (!selectedRecordId || isDraftId3(selectedRecordId)) return null;
@@ -10263,9 +10512,9 @@ function RecordsAdminShellInner(props) {
10263
10512
  });
10264
10513
  }, [isLifecycleRail, selectedLifecycleKey, lcGroupBy, collectionItems.items]);
10265
10514
  const leftItems = isProductTab ? productListItems : isRuleTab ? applyFacetBrowseFilter(
10266
- isCollection ? collectionRuleRailItems : filteredRuleItems
10515
+ isCollection ? sortedCollectionRuleRailItems : filteredRuleItems
10267
10516
  ) : isLifecycleRail ? lifecycleRows : (isGlobalTab || isAllTab) && isCollection ? [collectionGlobalAllRow] : isRecordsTab ? applyFacetBrowseFilter(recordList.items) : [];
10268
- const railShowsHistoryDisclosure = isRecordsTab && !isLifecycleRail && !((isGlobalTab || isAllTab) && isCollection);
10517
+ const railShowsHistoryDisclosure = isRecordsTab && !isLifecycleRail && !isCollection;
10269
10518
  const filteredLeftItems = useMemo(() => {
10270
10519
  if (!railShowsHistoryDisclosure) return leftItems;
10271
10520
  return leftItems.filter((r) => {
@@ -10533,6 +10782,8 @@ function RecordsAdminShellInner(props) {
10533
10782
  },
10534
10783
  rowClipboard,
10535
10784
  rowActions: wrappedRecordActions,
10785
+ groupByLifecycle: true,
10786
+ lifecycle: lifecycleConfig,
10536
10787
  contextKind: isLifecycleRailEarly && lifecycleBucketLabel ? lifecycleBucketLabel : activeScope === "rule" ? "Rule" : activeScope === "product" ? "Product" : activeScope === "collection" ? "Global" : activeScope === "all" ? "All records" : activeScope === "variant" ? "Variant" : activeScope === "batch" ? "Batch" : activeScope === "facet" ? "Facet" : void 0,
10537
10788
  contextSummary: isLifecycleRailEarly && lifecycleBucketLabel ? `${scopedCollectionItemsList.length} ${itemNounLabel}${scopedCollectionItemsList.length === 1 ? "" : "s"}` : activeScope === "rule" ? activeRuleSummary : activeScope === "product" ? editorHeaderLabel ?? null : null,
10538
10789
  i18n
@@ -10663,7 +10914,33 @@ function RecordsAdminShellInner(props) {
10663
10914
  onChange: setFacetBrowseFilter,
10664
10915
  isLoading: facetBrowse.isLoading
10665
10916
  }
10666
- )
10917
+ ),
10918
+ isRuleTab && isCollection && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
10919
+ /* @__PURE__ */ jsx(
10920
+ "label",
10921
+ {
10922
+ htmlFor: "ra-rule-sort",
10923
+ style: { color: "hsl(var(--ra-text-muted))" },
10924
+ children: i18n.ruleSortLabel
10925
+ }
10926
+ ),
10927
+ /* @__PURE__ */ jsxs(
10928
+ "select",
10929
+ {
10930
+ id: "ra-rule-sort",
10931
+ value: ruleSort,
10932
+ onChange: (e) => setRuleSort(e.target.value),
10933
+ className: "text-xs px-2 py-1 rounded-md border bg-transparent focus:outline-none focus:ring-1",
10934
+ style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
10935
+ children: [
10936
+ /* @__PURE__ */ jsx("option", { value: "recent", children: i18n.ruleSortRecent }),
10937
+ /* @__PURE__ */ jsx("option", { value: "name", children: i18n.ruleSortName }),
10938
+ /* @__PURE__ */ jsx("option", { value: "activeCount", children: i18n.ruleSortActiveCount }),
10939
+ /* @__PURE__ */ jsx("option", { value: "hasArchived", children: i18n.ruleSortHasArchived })
10940
+ ]
10941
+ }
10942
+ )
10943
+ ] })
10667
10944
  ] })
10668
10945
  ] }),
10669
10946
  /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
@@ -10900,6 +11177,8 @@ function RecordsAdminShellInner(props) {
10900
11177
  onLoadMore: () => {
10901
11178
  void collectionItems.fetchNextPage();
10902
11179
  },
11180
+ groupByLifecycle: true,
11181
+ lifecycle: lifecycleConfig,
10903
11182
  i18n
10904
11183
  }
10905
11184
  ),