@proveanything/smartlinks-utils-ui 0.12.2 → 0.12.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.
@@ -1689,6 +1689,14 @@ interface Props$c<T> {
1689
1689
  onCopyAndNewRule?: () => void;
1690
1690
  /** Label for the "Copy and start new rule" button. */
1691
1691
  copyAndNewRuleLabel?: string;
1692
+ /**
1693
+ * Collection-cardinality affordance — clone the current editor value
1694
+ * into a fresh draft item under the same scope. Mirrors the row menu
1695
+ * Duplicate action.
1696
+ */
1697
+ onDuplicate?: () => void;
1698
+ /** Optional label override for the Duplicate button. */
1699
+ duplicateLabel?: string;
1692
1700
  };
1693
1701
  /** Host-provided labels for save / discard / delete (resting state only). */
1694
1702
  actionLabels?: Partial<Record<RecordsAdminActionKey, string>>;
@@ -2885,9 +2893,22 @@ interface Props$1<T> {
2885
2893
  hasNextPage?: boolean;
2886
2894
  isFetchingNextPage?: boolean;
2887
2895
  onLoadMore?: () => void;
2888
- i18n: Pick<RecordsAdminI18n, 'backToScopes' | 'siblingsHeading' | 'noItemsTitle' | 'noItemsBody' | 'backToList' | 'newItem'>;
2896
+ /**
2897
+ * Per-row clipboard actions (Copy / Duplicate / Copy and start new
2898
+ * rule). Mirrors the menu surfaced by `ItemListView` so the open-item
2899
+ * sibling rail offers the same affordances without bouncing back to
2900
+ * the list. Hidden when omitted.
2901
+ */
2902
+ rowClipboard?: (record: RecordSummary<T>) => {
2903
+ onCopy?: () => void;
2904
+ onDuplicate?: () => void;
2905
+ onCopyAndNewRule?: () => void;
2906
+ } | null | undefined;
2907
+ /** Host-defined extra row actions, surfaced below the built-ins. */
2908
+ rowActions?: (record: RecordSummary<T>) => RecordAction[] | null | undefined;
2909
+ i18n: Pick<RecordsAdminI18n, 'backToScopes' | 'siblingsHeading' | 'noItemsTitle' | 'noItemsBody' | 'backToList' | 'newItem' | 'copy' | 'duplicateAction' | 'copyAndNewRuleAction'>;
2889
2910
  }
2890
- declare function SiblingRail<T>({ items, selectedItemId, isLoading, error, onBack, onSelect, contextKind, contextSummary, onCreate, itemNoun, dirtyKeys, errorKeys, i18n, total, hasNextPage, isFetchingNextPage, onLoadMore, }: Props$1<T>): react_jsx_runtime.JSX.Element;
2911
+ declare function SiblingRail<T>({ items, selectedItemId, isLoading, error, onBack, onSelect, contextKind, contextSummary, onCreate, itemNoun, dirtyKeys, errorKeys, i18n, total, hasNextPage, isFetchingNextPage, onLoadMore, rowClipboard, rowActions, }: Props$1<T>): react_jsx_runtime.JSX.Element;
2891
2912
 
2892
2913
  interface ClipboardEntry<T = unknown> {
2893
2914
  value: T;
@@ -2974,7 +2995,7 @@ interface Props {
2974
2995
  /** Optional veto hook — return false / throw to abort the change. */
2975
2996
  beforeChange?: (ctx: LifecycleChangeCtx) => boolean | Promise<boolean>;
2976
2997
  /** Fires after a successful flip with the new value. */
2977
- onChanged?: (next: string) => void;
2998
+ onChanged?: (next: string, record?: AppRecord) => void;
2978
2999
  /** Telemetry hook — receives `{ from, to }`. */
2979
3000
  onTelemetry?: (event: {
2980
3001
  from: string | undefined;
@@ -7,7 +7,7 @@ import { cn } from '../../chunk-L7FQ52F5.js';
7
7
  import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-KA4MKRHL.js';
8
8
  export { bulkDelete, bulkUpsert, createRecord, getRecordById, listRecords, matchRecords, parsedRefToScope, parsedRefToTarget, removeRecord, restoreRecord, scopesEqual, upsertRecord } from '../../chunk-KA4MKRHL.js';
9
9
  import { createContext, useMemo, useState, useEffect, useCallback, useRef, isValidElement, useLayoutEffect, useContext, useSyncExternalStore, createElement } from 'react';
10
- import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, List, SlidersHorizontal, Globe, Tag, Boxes, Layers, Package, Target, Check, Rows3, ChevronRight, Eraser, FilePlus2, ClipboardPaste, Box, X, Search, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, CornerDownLeft, Circle, ArrowUpDown, ArrowUp, ArrowDown, MinusCircle, XCircle, CopyPlus, AlertCircle, Undo2, Save, Loader2, Archive, ArrowRight, Globe2, Settings2 } from 'lucide-react';
10
+ import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, List, SlidersHorizontal, Globe, Tag, Boxes, Layers, Package, Target, 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, Settings2 } from 'lucide-react';
11
11
  import { useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
12
12
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
13
  import { createPortal } from 'react-dom';
@@ -1765,6 +1765,19 @@ function useShellClipboard(args) {
1765
1765
  window.setTimeout(() => {
1766
1766
  onCreateRuleFromClipboardRef?.current?.();
1767
1767
  }, 0);
1768
+ } : void 0,
1769
+ // Collection-cardinality clone affordance — only shown when the host
1770
+ // wired item-draft creation. Pushes the editor value to the clipboard
1771
+ // (so the existing pendingPasteTarget effect lands it on the new
1772
+ // draft) and mints a fresh draft.
1773
+ onDuplicate: isCollection && !!onCreateItemDraftRef ? () => {
1774
+ copyCurrent();
1775
+ window.setTimeout(() => {
1776
+ const create = onCreateItemDraftRef?.current;
1777
+ if (!create) return;
1778
+ const newId = create();
1779
+ if (newId) setPendingPasteTarget({ kind: "record", recordId: newId });
1780
+ }, 0);
1768
1781
  } : void 0
1769
1782
  } : void 0;
1770
1783
  const [pendingPasteTarget, setPendingPasteTarget] = useState(null);
@@ -3981,8 +3994,8 @@ var LifecycleStatusControl = ({
3981
3994
  if (next === value) return;
3982
3995
  setBusy(true);
3983
3996
  try {
3984
- await SL.app.records.update(collectionId, appId, recordId, { status: next }, true);
3985
- onChanged?.(next);
3997
+ const updated = await SL.app.records.update(collectionId, appId, recordId, { status: next }, true);
3998
+ onChanged?.(next, updated);
3986
3999
  } catch (err) {
3987
4000
  console.warn("[LifecycleStatusControl] update failed", err);
3988
4001
  } finally {
@@ -4176,9 +4189,9 @@ var LifecycleStatusMenu = ({
4176
4189
  }
4177
4190
  setBusy(next.value);
4178
4191
  try {
4179
- await SL.app.records.update(collectionId, appId, recordId, { status: next.value }, true);
4192
+ const updated = await SL.app.records.update(collectionId, appId, recordId, { status: next.value }, true);
4180
4193
  onTelemetry?.({ from: current, to: next.value });
4181
- onChanged?.(next.value);
4194
+ onChanged?.(next.value, updated);
4182
4195
  } catch (err) {
4183
4196
  console.warn("[LifecycleStatusMenu] update failed", err);
4184
4197
  } finally {
@@ -4684,7 +4697,8 @@ function useCollectionItems(args) {
4684
4697
  label: stableItemId,
4685
4698
  updatedAt: rec.updatedAt,
4686
4699
  itemId: stableItemId,
4687
- facetRule: recFacetRule
4700
+ facetRule: recFacetRule,
4701
+ lifecycleStatus: rec.status ?? void 0
4688
4702
  };
4689
4703
  return toSummary2(rec, base);
4690
4704
  }).filter((x) => x !== null);
@@ -5159,6 +5173,22 @@ function RecordEditor({
5159
5173
  ]
5160
5174
  }
5161
5175
  ),
5176
+ clipboard.onDuplicate && /* @__PURE__ */ jsxs(
5177
+ "button",
5178
+ {
5179
+ type: "button",
5180
+ onClick: clipboard.onDuplicate,
5181
+ disabled: !clipboard.canCopy || !!ctx.isSaving,
5182
+ title: clipboard.duplicateLabel ?? i18n.duplicateAction ?? "Duplicate",
5183
+ "aria-label": clipboard.duplicateLabel ?? i18n.duplicateAction ?? "Duplicate",
5184
+ className: "text-xs px-2.5 py-1.5 rounded-md border transition-opacity disabled:opacity-40 hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1.5",
5185
+ style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
5186
+ children: [
5187
+ /* @__PURE__ */ jsx(CopyPlus, { className: "w-3 h-3" }),
5188
+ clipboard.duplicateLabel ?? i18n.duplicateAction ?? "Duplicate"
5189
+ ]
5190
+ }
5191
+ ),
5162
5192
  /* @__PURE__ */ jsxs(
5163
5193
  "button",
5164
5194
  {
@@ -6895,7 +6925,9 @@ function SiblingRail({
6895
6925
  total,
6896
6926
  hasNextPage,
6897
6927
  isFetchingNextPage,
6898
- onLoadMore
6928
+ onLoadMore,
6929
+ rowClipboard,
6930
+ rowActions
6899
6931
  }) {
6900
6932
  const ruleLabelLookup = useRuleLabelLookup();
6901
6933
  const newLabel = i18n.newItem.includes("{noun}") ? i18n.newItem.replace("{noun}", itemNoun ?? "item") : i18n.newItem;
@@ -6934,45 +6966,68 @@ function SiblingRail({
6934
6966
  const ruleClauses = item.facetRule ? summarizeFacetRule(item.facetRule, ruleLabelLookup) : [];
6935
6967
  const isTargeted = ruleClauses.length > 0;
6936
6968
  const ruleSummary = isTargeted ? ruleClauses.map((c) => c.label).join(" \xB7 ") : null;
6937
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
6938
- "button",
6939
- {
6940
- type: "button",
6941
- onClick: () => onSelect(id),
6942
- className: "ra-row",
6943
- "data-selected": selected,
6944
- children: [
6945
- /* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
6946
- /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: item.label }),
6947
- item.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: item.subtitle })
6948
- ] }),
6949
- isTargeted && /* @__PURE__ */ jsx(
6950
- "span",
6951
- {
6952
- className: "ra-row-rule-pip",
6953
- title: ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
6954
- "aria-label": ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
6955
- children: /* @__PURE__ */ jsx(Target, { className: "w-3 h-3", "aria-hidden": "true" })
6956
- }
6957
- ),
6958
- hasError ? /* @__PURE__ */ jsx(
6959
- "span",
6960
- {
6961
- className: "ra-error-pip",
6962
- title: "Save failed",
6963
- "aria-label": "Save failed"
6964
- }
6965
- ) : isDirty ? /* @__PURE__ */ jsx(
6966
- "span",
6967
- {
6968
- className: "ra-dirty-pip",
6969
- title: "Unsaved changes",
6970
- "aria-label": "Unsaved changes"
6969
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", { className: "ra-row-shell", "data-selected": selected, children: [
6970
+ /* @__PURE__ */ jsxs(
6971
+ "button",
6972
+ {
6973
+ type: "button",
6974
+ onClick: () => onSelect(id),
6975
+ className: "ra-row",
6976
+ "data-selected": selected,
6977
+ children: [
6978
+ /* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
6979
+ /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: item.label }),
6980
+ item.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: item.subtitle })
6981
+ ] }),
6982
+ isTargeted && /* @__PURE__ */ jsx(
6983
+ "span",
6984
+ {
6985
+ className: "ra-row-rule-pip",
6986
+ title: ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
6987
+ "aria-label": ruleSummary ? `Targeted: ${ruleSummary}` : "This item has targeting rules",
6988
+ children: /* @__PURE__ */ jsx(Target, { className: "w-3 h-3", "aria-hidden": "true" })
6989
+ }
6990
+ ),
6991
+ hasError ? /* @__PURE__ */ jsx(
6992
+ "span",
6993
+ {
6994
+ className: "ra-error-pip",
6995
+ title: "Save failed",
6996
+ "aria-label": "Save failed"
6997
+ }
6998
+ ) : isDirty ? /* @__PURE__ */ jsx(
6999
+ "span",
7000
+ {
7001
+ className: "ra-dirty-pip",
7002
+ title: "Unsaved changes",
7003
+ "aria-label": "Unsaved changes"
7004
+ }
7005
+ ) : null
7006
+ ]
7007
+ }
7008
+ ),
7009
+ (() => {
7010
+ const cb = rowClipboard ? rowClipboard(item) : null;
7011
+ const extra = rowActions ? rowActions(item) ?? void 0 : void 0;
7012
+ if (!cb?.onCopy && !cb?.onDuplicate && !cb?.onCopyAndNewRule && !(extra && extra.length)) {
7013
+ return null;
7014
+ }
7015
+ return /* @__PURE__ */ jsx(
7016
+ RowContextMenu,
7017
+ {
7018
+ onCopy: cb?.onCopy,
7019
+ onDuplicate: cb?.onDuplicate,
7020
+ onCopyAndNewRule: cb?.onCopyAndNewRule,
7021
+ actions: extra,
7022
+ i18n: {
7023
+ copy: i18n.copy,
7024
+ duplicateAction: i18n.duplicateAction,
7025
+ copyAndNewRuleAction: i18n.copyAndNewRuleAction
6971
7026
  }
6972
- ) : null
6973
- ]
6974
- }
6975
- ) }, key);
7027
+ }
7028
+ );
7029
+ })()
7030
+ ] }) }, key);
6976
7031
  }) })
6977
7032
  ] }),
6978
7033
  onLoadMore && /* @__PURE__ */ jsx(
@@ -8327,14 +8382,14 @@ var coerceDraftItemId2 = (generateItemId) => {
8327
8382
  const candidate = generateItemId ? generateItemId() : mintDraftItemId2();
8328
8383
  return isDraftId3(candidate) ? candidate : `draft:${candidate}`;
8329
8384
  };
8330
- var productItemToSummary = (p) => {
8385
+ var productItemToSummary = (p, configured) => {
8331
8386
  const ref = buildRef({ productId: p.id });
8332
8387
  return {
8333
8388
  id: null,
8334
8389
  ref,
8335
8390
  scope: parseRef(ref),
8336
8391
  data: null,
8337
- status: "empty",
8392
+ status: configured?.has(p.id) ? "configured" : "empty",
8338
8393
  label: p.name,
8339
8394
  subtitle: p.sku ?? void 0
8340
8395
  };
@@ -9219,11 +9274,12 @@ function RecordsAdminShellInner(props) {
9219
9274
  label: label2,
9220
9275
  onAction: async () => {
9221
9276
  try {
9222
- await SL.app.records.update(collectionId, appId, record.id, { status: next }, true);
9223
- recordList.refetch();
9224
- if (cardinality === "singleton") {
9225
- ruleScopedList.refetch();
9226
- globalScopedList.refetch();
9277
+ const updated = await SL.app.records.update(collectionId, appId, record.id, { status: next }, true);
9278
+ if (updated) {
9279
+ patchRecordIntoCaches(queryClient, ctx, updated);
9280
+ queryClient.invalidateQueries({
9281
+ queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
9282
+ });
9227
9283
  }
9228
9284
  } catch (err) {
9229
9285
  console.warn("[RecordsAdminShell] lifecycle update failed", err);
@@ -9263,6 +9319,14 @@ function RecordsAdminShellInner(props) {
9263
9319
  const itemViewCtx = baseItemViewCtx;
9264
9320
  const renderEditorWithPreview = () => {
9265
9321
  if (!editingTargetScope) return null;
9322
+ const targetingSavedRecord = isCollection && selectedItemId && !isDraftId3(selectedItemId) || !!selectedRecordId && !isDraftId3(selectedRecordId);
9323
+ if (resolved.isLoading && targetingSavedRecord && resolved.source !== "self") {
9324
+ return /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-3", "aria-busy": "true", "aria-live": "polite", children: [
9325
+ /* @__PURE__ */ jsx("div", { className: "h-6 rounded-md animate-pulse", style: { background: "hsl(var(--ra-muted))", width: "40%" } }),
9326
+ /* @__PURE__ */ jsx("div", { className: "h-24 rounded-md animate-pulse", style: { background: "hsl(var(--ra-muted))" } }),
9327
+ /* @__PURE__ */ jsx("div", { className: "h-24 rounded-md animate-pulse", style: { background: "hsl(var(--ra-muted))" } })
9328
+ ] });
9329
+ }
9266
9330
  const previewAnchorRef = previewReopenAnchorRef;
9267
9331
  const previewBody = renderPreview && effectivePreviewScope ? renderPreview({ resolved: editorCtx.value, previewScope: effectivePreviewScope }) : null;
9268
9332
  const scopePicker = previewScopePicker && effectivePreviewScope ? /* @__PURE__ */ jsx(
@@ -9314,7 +9378,7 @@ function RecordsAdminShellInner(props) {
9314
9378
  ]
9315
9379
  }
9316
9380
  ) : null;
9317
- const selectedSummary = selectedRecordId && selectedRecordId !== DRAFT_ID3 ? recordList.items.find((r) => r.id === selectedRecordId) ?? globalScopedList.items.find((r) => r.id === selectedRecordId) ?? ruleScopedList.items.find((r) => r.id === selectedRecordId) : void 0;
9381
+ const selectedSummary = selectedRecordId && selectedRecordId !== DRAFT_ID3 ? recordList.items.find((r) => r.id === selectedRecordId) ?? globalScopedList.items.find((r) => r.id === selectedRecordId) ?? ruleScopedList.items.find((r) => r.id === selectedRecordId) ?? collectionItems.items.find((r) => r.id === selectedRecordId) : isCollection && selectedItemId && !isDraftId3(selectedItemId) ? collectionItems.items.find((r) => r.id === selectedItemId) : void 0;
9318
9382
  const editorLifecycleControl = selectedSummary?.id ? /* @__PURE__ */ jsx(
9319
9383
  LifecycleStatusControl,
9320
9384
  {
@@ -9324,8 +9388,13 @@ function RecordsAdminShellInner(props) {
9324
9388
  recordId: selectedSummary.id,
9325
9389
  current: selectedSummary.lifecycleStatus,
9326
9390
  i18n,
9327
- onChanged: () => {
9328
- void recordList.refetch();
9391
+ onChanged: (_next, updated) => {
9392
+ if (updated) {
9393
+ patchRecordIntoCaches(queryClient, ctx, updated);
9394
+ queryClient.invalidateQueries({
9395
+ queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
9396
+ });
9397
+ }
9329
9398
  }
9330
9399
  }
9331
9400
  ) : null;
@@ -9348,11 +9417,12 @@ function RecordsAdminShellInner(props) {
9348
9417
  from: e.from,
9349
9418
  to: e.to
9350
9419
  }),
9351
- onChanged: () => {
9352
- void refetchAll();
9353
- if (cardinality === "singleton") {
9354
- ruleScopedList.refetch();
9355
- globalScopedList.refetch();
9420
+ onChanged: (_next, updated) => {
9421
+ if (updated) {
9422
+ patchRecordIntoCaches(queryClient, ctx, updated);
9423
+ queryClient.invalidateQueries({
9424
+ queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
9425
+ });
9356
9426
  }
9357
9427
  }
9358
9428
  }
@@ -9524,7 +9594,7 @@ function RecordsAdminShellInner(props) {
9524
9594
  }];
9525
9595
  }
9526
9596
  const configured = scopeCounts.productIds;
9527
- const all = productBrowse.items.map(productItemToSummary);
9597
+ const all = productBrowse.items.map((p) => productItemToSummary(p, configured));
9528
9598
  const isConfigured = (s) => {
9529
9599
  const pid = s.scope.productId;
9530
9600
  return !!pid && configured.has(pid);
@@ -10123,6 +10193,8 @@ function RecordsAdminShellInner(props) {
10123
10193
  onLoadMore: () => {
10124
10194
  void collectionItems.fetchNextPage();
10125
10195
  },
10196
+ rowClipboard,
10197
+ rowActions: wrappedRecordActions,
10126
10198
  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,
10127
10199
  contextSummary: isLifecycleRailEarly && lifecycleBucketLabel ? `${scopedCollectionItemsList.length} ${itemNounLabel}${scopedCollectionItemsList.length === 1 ? "" : "s"}` : activeScope === "rule" ? activeRuleSummary : activeScope === "product" ? editorHeaderLabel ?? null : null,
10128
10200
  i18n
@@ -10274,7 +10346,8 @@ function RecordsAdminShellInner(props) {
10274
10346
  const ids = singletonConflicts.flatMap((c) => c.duplicates.map((d) => d.id)).filter((id) => !!id);
10275
10347
  for (const id of ids) {
10276
10348
  try {
10277
- await SL.app.records.update(collectionId, appId, id, { status: archivedStatusValue }, true);
10349
+ const updated = await SL.app.records.update(collectionId, appId, id, { status: archivedStatusValue }, true);
10350
+ if (updated) patchRecordIntoCaches(queryClient, ctx, updated);
10278
10351
  onTelemetry?.({
10279
10352
  type: "recordAction.invoke",
10280
10353
  recordType,
@@ -10285,17 +10358,16 @@ function RecordsAdminShellInner(props) {
10285
10358
  console.warn("[RecordsAdminShell] archive-duplicate failed", id, err);
10286
10359
  }
10287
10360
  }
10288
- if (cardinality === "singleton") {
10289
- ruleScopedList.refetch();
10290
- globalScopedList.refetch();
10291
- }
10292
- await refetchAll();
10361
+ queryClient.invalidateQueries({
10362
+ queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
10363
+ });
10293
10364
  } : void 0,
10294
10365
  onDeleteDuplicates: enableDeleteDuplicates ? async () => {
10295
10366
  const ids = singletonConflicts.flatMap((c) => c.duplicates.map((d) => d.id)).filter((id) => !!id);
10296
10367
  for (const id of ids) {
10297
10368
  try {
10298
10369
  await SL.app.records.remove(collectionId, appId, id, true);
10370
+ removeRecordFromCaches(queryClient, ctx, id);
10299
10371
  onTelemetry?.({
10300
10372
  type: "recordAction.invoke",
10301
10373
  recordType,
@@ -10306,11 +10378,9 @@ function RecordsAdminShellInner(props) {
10306
10378
  console.warn("[RecordsAdminShell] delete-duplicate failed", id, err);
10307
10379
  }
10308
10380
  }
10309
- if (cardinality === "singleton") {
10310
- ruleScopedList.refetch();
10311
- globalScopedList.refetch();
10312
- }
10313
- await refetchAll();
10381
+ queryClient.invalidateQueries({
10382
+ queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
10383
+ });
10314
10384
  } : void 0,
10315
10385
  i18n: {
10316
10386
  title: i18n.conflictBannerTitle,