@proveanything/smartlinks-utils-ui 0.12.2 → 0.12.5
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.
- package/dist/components/AssetPicker/index.css +42 -0
- package/dist/components/AssetPicker/index.css.map +1 -1
- package/dist/components/ConditionsEditor/index.css +42 -0
- package/dist/components/ConditionsEditor/index.css.map +1 -1
- package/dist/components/FontPicker/index.css +42 -0
- package/dist/components/FontPicker/index.css.map +1 -1
- package/dist/components/IconPicker/index.css +42 -0
- package/dist/components/IconPicker/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.css +42 -0
- package/dist/components/RecordsAdmin/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.d.ts +32 -3
- package/dist/components/RecordsAdmin/index.js +503 -95
- package/dist/components/RecordsAdmin/index.js.map +1 -1
- package/dist/index.css +42 -0
- package/dist/index.css.map +1 -1
- package/package.json +1 -1
|
@@ -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,
|
|
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';
|
|
12
12
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
13
13
|
import { createPortal } from 'react-dom';
|
|
@@ -240,6 +240,13 @@ var DEFAULT_I18N = {
|
|
|
240
240
|
pasteWarnContinue: "Continue",
|
|
241
241
|
duplicateAction: "Duplicate",
|
|
242
242
|
duplicateToast: "Duplicated {name}. Review and Save.",
|
|
243
|
+
duplicateDialogTitle: 'Duplicate "{name}" to:',
|
|
244
|
+
duplicateTargetGlobal: "Global",
|
|
245
|
+
duplicateTargetSameRule: "Same rule as source",
|
|
246
|
+
duplicateTargetExistingRules: "Existing rules",
|
|
247
|
+
duplicateTargetNewRule: "New rule\u2026",
|
|
248
|
+
duplicateConfirm: "Duplicate",
|
|
249
|
+
duplicateCancel: "Cancel",
|
|
243
250
|
copyAndNewRuleAction: "Copy and start new rule",
|
|
244
251
|
itemsAllLabel: "All items",
|
|
245
252
|
subtitleEmpty: "Not set",
|
|
@@ -1621,6 +1628,199 @@ var ClipboardToast = ({ message, variant = "copy", onDismiss }) => {
|
|
|
1621
1628
|
}
|
|
1622
1629
|
);
|
|
1623
1630
|
};
|
|
1631
|
+
var DEFAULT_I18N2 = {
|
|
1632
|
+
title: 'Duplicate "{name}" to:',
|
|
1633
|
+
global: "Global",
|
|
1634
|
+
sameRule: "Same rule as source",
|
|
1635
|
+
existingRules: "Existing rules",
|
|
1636
|
+
newRule: "New rule\u2026",
|
|
1637
|
+
confirm: "Duplicate",
|
|
1638
|
+
cancel: "Cancel"
|
|
1639
|
+
};
|
|
1640
|
+
function DuplicateTargetPicker({
|
|
1641
|
+
open,
|
|
1642
|
+
sourceLabel,
|
|
1643
|
+
sourceHasRule,
|
|
1644
|
+
rules,
|
|
1645
|
+
supportsRules,
|
|
1646
|
+
supportsGlobal = true,
|
|
1647
|
+
onCancel,
|
|
1648
|
+
onConfirm,
|
|
1649
|
+
i18n
|
|
1650
|
+
}) {
|
|
1651
|
+
const t = { ...DEFAULT_I18N2, ...i18n ?? {} };
|
|
1652
|
+
const [picked, setPicked] = useState(null);
|
|
1653
|
+
const containerRef = useRef(null);
|
|
1654
|
+
useEffect(() => {
|
|
1655
|
+
if (!open) {
|
|
1656
|
+
setPicked(null);
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
setPicked(sourceHasRule && supportsRules ? { kind: "sameRule" } : supportsGlobal ? { kind: "global" } : null);
|
|
1660
|
+
}, [open, sourceHasRule, supportsRules, supportsGlobal]);
|
|
1661
|
+
useEffect(() => {
|
|
1662
|
+
if (!open) return;
|
|
1663
|
+
const onKey = (e) => {
|
|
1664
|
+
if (e.key === "Escape") {
|
|
1665
|
+
e.preventDefault();
|
|
1666
|
+
onCancel();
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1669
|
+
window.addEventListener("keydown", onKey);
|
|
1670
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1671
|
+
}, [open, onCancel]);
|
|
1672
|
+
const sortedRules = useMemo(() => rules.slice().sort((a, b) => {
|
|
1673
|
+
if (b.count !== a.count) return b.count - a.count;
|
|
1674
|
+
return a.summary.localeCompare(b.summary);
|
|
1675
|
+
}), [rules]);
|
|
1676
|
+
if (!open || typeof document === "undefined") return null;
|
|
1677
|
+
const stop = (e) => {
|
|
1678
|
+
e.stopPropagation();
|
|
1679
|
+
};
|
|
1680
|
+
const isSame = (a, b) => {
|
|
1681
|
+
if (!a || a.kind !== b.kind) return false;
|
|
1682
|
+
if (a.kind === "existingRule" && b.kind === "existingRule") {
|
|
1683
|
+
return a.ruleHash === b.ruleHash;
|
|
1684
|
+
}
|
|
1685
|
+
return true;
|
|
1686
|
+
};
|
|
1687
|
+
const RadioRow = ({
|
|
1688
|
+
target,
|
|
1689
|
+
label,
|
|
1690
|
+
sublabel,
|
|
1691
|
+
icon: Icon
|
|
1692
|
+
}) => {
|
|
1693
|
+
const checked = isSame(picked, target);
|
|
1694
|
+
return /* @__PURE__ */ jsxs(
|
|
1695
|
+
"button",
|
|
1696
|
+
{
|
|
1697
|
+
type: "button",
|
|
1698
|
+
onClick: () => setPicked(target),
|
|
1699
|
+
className: "w-full text-left px-3 py-2 rounded-md border transition-colors flex items-start gap-2.5",
|
|
1700
|
+
style: {
|
|
1701
|
+
borderColor: checked ? "hsl(var(--ra-accent))" : "hsl(var(--ra-border))",
|
|
1702
|
+
background: checked ? "hsl(var(--ra-accent) / 0.08)" : "transparent",
|
|
1703
|
+
color: "hsl(var(--ra-text))"
|
|
1704
|
+
},
|
|
1705
|
+
children: [
|
|
1706
|
+
/* @__PURE__ */ jsx(
|
|
1707
|
+
"span",
|
|
1708
|
+
{
|
|
1709
|
+
className: "mt-0.5 inline-flex items-center justify-center h-4 w-4 rounded-full shrink-0",
|
|
1710
|
+
style: {
|
|
1711
|
+
border: `2px solid ${checked ? "hsl(var(--ra-accent))" : "hsl(var(--ra-border))"}`
|
|
1712
|
+
},
|
|
1713
|
+
children: checked && /* @__PURE__ */ jsx(
|
|
1714
|
+
"span",
|
|
1715
|
+
{
|
|
1716
|
+
className: "h-2 w-2 rounded-full",
|
|
1717
|
+
style: { background: "hsl(var(--ra-accent))" }
|
|
1718
|
+
}
|
|
1719
|
+
)
|
|
1720
|
+
}
|
|
1721
|
+
),
|
|
1722
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 mt-0.5 shrink-0" }),
|
|
1723
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
1724
|
+
/* @__PURE__ */ jsx("span", { className: "block text-sm", children: label }),
|
|
1725
|
+
sublabel && /* @__PURE__ */ jsx("span", { className: "block text-xs truncate", style: { color: "hsl(var(--ra-muted-text))" }, children: sublabel })
|
|
1726
|
+
] })
|
|
1727
|
+
]
|
|
1728
|
+
}
|
|
1729
|
+
);
|
|
1730
|
+
};
|
|
1731
|
+
const showRuleSection = supportsRules && (sortedRules.length > 0 || sourceHasRule);
|
|
1732
|
+
return createPortal(
|
|
1733
|
+
/* @__PURE__ */ jsx(
|
|
1734
|
+
"div",
|
|
1735
|
+
{
|
|
1736
|
+
className: "fixed inset-0 z-[1000] flex items-center justify-center",
|
|
1737
|
+
style: { background: "hsl(0 0% 0% / 0.4)" },
|
|
1738
|
+
onClick: onCancel,
|
|
1739
|
+
onMouseDown: stop,
|
|
1740
|
+
onTouchStart: stop,
|
|
1741
|
+
children: /* @__PURE__ */ jsxs(
|
|
1742
|
+
"div",
|
|
1743
|
+
{
|
|
1744
|
+
ref: containerRef,
|
|
1745
|
+
role: "dialog",
|
|
1746
|
+
"aria-modal": "true",
|
|
1747
|
+
className: "w-[min(28rem,calc(100vw-2rem))] max-h-[min(36rem,calc(100vh-2rem))] flex flex-col rounded-lg shadow-xl",
|
|
1748
|
+
style: { background: "hsl(var(--ra-surface))", border: "1px solid hsl(var(--ra-border))" },
|
|
1749
|
+
onClick: stop,
|
|
1750
|
+
onMouseDown: stop,
|
|
1751
|
+
onTouchStart: stop,
|
|
1752
|
+
children: [
|
|
1753
|
+
/* @__PURE__ */ jsx("header", { className: "px-4 py-3 border-b", style: { borderColor: "hsl(var(--ra-border))" }, children: /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", style: { color: "hsl(var(--ra-text))" }, children: t.title.replace("{name}", sourceLabel) }) }),
|
|
1754
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-h-0 overflow-y-auto px-4 py-3 space-y-3", children: [
|
|
1755
|
+
supportsGlobal && /* @__PURE__ */ jsx(RadioRow, { target: { kind: "global" }, label: t.global, icon: Globe2 }),
|
|
1756
|
+
showRuleSection && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1757
|
+
sourceHasRule && /* @__PURE__ */ jsx(RadioRow, { target: { kind: "sameRule" }, label: t.sameRule, icon: Target }),
|
|
1758
|
+
sortedRules.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
1759
|
+
/* @__PURE__ */ jsx(
|
|
1760
|
+
"div",
|
|
1761
|
+
{
|
|
1762
|
+
className: "text-[10px] uppercase tracking-wide mb-1.5 px-1",
|
|
1763
|
+
style: { color: "hsl(var(--ra-muted-text))" },
|
|
1764
|
+
children: t.existingRules
|
|
1765
|
+
}
|
|
1766
|
+
),
|
|
1767
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: sortedRules.map((r) => /* @__PURE__ */ jsx(
|
|
1768
|
+
RadioRow,
|
|
1769
|
+
{
|
|
1770
|
+
target: { kind: "existingRule", ruleHash: r.hash },
|
|
1771
|
+
label: r.summary,
|
|
1772
|
+
sublabel: `${r.count} record${r.count === 1 ? "" : "s"}`,
|
|
1773
|
+
icon: Target
|
|
1774
|
+
},
|
|
1775
|
+
r.hash
|
|
1776
|
+
)) })
|
|
1777
|
+
] }),
|
|
1778
|
+
/* @__PURE__ */ jsx(RadioRow, { target: { kind: "newRule" }, label: t.newRule, icon: Sparkles })
|
|
1779
|
+
] })
|
|
1780
|
+
] }),
|
|
1781
|
+
/* @__PURE__ */ jsxs(
|
|
1782
|
+
"footer",
|
|
1783
|
+
{
|
|
1784
|
+
className: "px-4 py-3 border-t flex items-center justify-end gap-2",
|
|
1785
|
+
style: { borderColor: "hsl(var(--ra-border))" },
|
|
1786
|
+
children: [
|
|
1787
|
+
/* @__PURE__ */ jsx(
|
|
1788
|
+
"button",
|
|
1789
|
+
{
|
|
1790
|
+
type: "button",
|
|
1791
|
+
onClick: onCancel,
|
|
1792
|
+
className: "text-xs px-3 py-1.5 rounded-md border hover:bg-[hsl(var(--ra-muted))]",
|
|
1793
|
+
style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
|
|
1794
|
+
children: t.cancel
|
|
1795
|
+
}
|
|
1796
|
+
),
|
|
1797
|
+
/* @__PURE__ */ jsxs(
|
|
1798
|
+
"button",
|
|
1799
|
+
{
|
|
1800
|
+
type: "button",
|
|
1801
|
+
disabled: !picked,
|
|
1802
|
+
onClick: () => {
|
|
1803
|
+
if (picked) onConfirm(picked);
|
|
1804
|
+
},
|
|
1805
|
+
className: "text-xs px-3 py-1.5 rounded-md font-medium transition-opacity disabled:opacity-40 inline-flex items-center gap-1.5",
|
|
1806
|
+
style: { background: "hsl(var(--ra-accent))", color: "hsl(var(--ra-surface))" },
|
|
1807
|
+
children: [
|
|
1808
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
|
|
1809
|
+
t.confirm
|
|
1810
|
+
]
|
|
1811
|
+
}
|
|
1812
|
+
)
|
|
1813
|
+
]
|
|
1814
|
+
}
|
|
1815
|
+
)
|
|
1816
|
+
]
|
|
1817
|
+
}
|
|
1818
|
+
)
|
|
1819
|
+
}
|
|
1820
|
+
),
|
|
1821
|
+
document.body
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1624
1824
|
function useShellClipboard(args) {
|
|
1625
1825
|
const {
|
|
1626
1826
|
enabled,
|
|
@@ -1640,7 +1840,11 @@ function useShellClipboard(args) {
|
|
|
1640
1840
|
onLeftSelectRef,
|
|
1641
1841
|
onCreateItemDraftRef,
|
|
1642
1842
|
onCreateRuleFromClipboardRef,
|
|
1643
|
-
isRuleTab
|
|
1843
|
+
isRuleTab,
|
|
1844
|
+
ruleCatalogue,
|
|
1845
|
+
ruleCatalogueRef,
|
|
1846
|
+
supportsRules = true,
|
|
1847
|
+
onCreateItemDraftAtScopeRef
|
|
1644
1848
|
} = args;
|
|
1645
1849
|
const clipboard = useRecordClipboard({
|
|
1646
1850
|
appId,
|
|
@@ -1765,9 +1969,103 @@ function useShellClipboard(args) {
|
|
|
1765
1969
|
window.setTimeout(() => {
|
|
1766
1970
|
onCreateRuleFromClipboardRef?.current?.();
|
|
1767
1971
|
}, 0);
|
|
1972
|
+
} : void 0,
|
|
1973
|
+
// Collection-cardinality clone affordance — only shown when the host
|
|
1974
|
+
// wired item-draft creation. Pushes the editor value to the clipboard
|
|
1975
|
+
// (so the existing pendingPasteTarget effect lands it on the new
|
|
1976
|
+
// draft) and mints a fresh draft.
|
|
1977
|
+
// Collection-cardinality clone affordance — opens the Duplicate-To
|
|
1978
|
+
// picker so the admin chooses the destination scope (Global, an
|
|
1979
|
+
// existing rule, a new rule, or the source's own rule). We only show
|
|
1980
|
+
// this when the host wired the at-scope draft creator, since without
|
|
1981
|
+
// it we have nowhere to mint the duplicate.
|
|
1982
|
+
onDuplicate: isCollection && !!onCreateItemDraftAtScopeRef ? () => {
|
|
1983
|
+
if (!editingScope) return;
|
|
1984
|
+
openDuplicatePicker({
|
|
1985
|
+
value: editorCtx.value,
|
|
1986
|
+
scope: editingScope,
|
|
1987
|
+
label: editorHeaderLabel ?? editingScope.raw,
|
|
1988
|
+
facetRule: resolved.facetRule ?? null,
|
|
1989
|
+
recordId: resolved.recordId
|
|
1990
|
+
});
|
|
1768
1991
|
} : void 0
|
|
1769
1992
|
} : void 0;
|
|
1770
1993
|
const [pendingPasteTarget, setPendingPasteTarget] = useState(null);
|
|
1994
|
+
const [pendingSeed, setPendingSeed] = useState(null);
|
|
1995
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
1996
|
+
const pickerSourceRef = useRef(null);
|
|
1997
|
+
const openDuplicatePicker = useCallback((source) => {
|
|
1998
|
+
pickerSourceRef.current = {
|
|
1999
|
+
value: source.value,
|
|
2000
|
+
scope: source.scope,
|
|
2001
|
+
label: source.label,
|
|
2002
|
+
facetRule: source.facetRule ?? null,
|
|
2003
|
+
recordId: source.recordId
|
|
2004
|
+
};
|
|
2005
|
+
setPickerOpen(true);
|
|
2006
|
+
}, []);
|
|
2007
|
+
const onDuplicatePickerCancel = useCallback(() => {
|
|
2008
|
+
setPickerOpen(false);
|
|
2009
|
+
pickerSourceRef.current = null;
|
|
2010
|
+
}, []);
|
|
2011
|
+
const onDuplicatePickerConfirm = useCallback((target) => {
|
|
2012
|
+
const source = pickerSourceRef.current;
|
|
2013
|
+
setPickerOpen(false);
|
|
2014
|
+
pickerSourceRef.current = null;
|
|
2015
|
+
if (!source) return;
|
|
2016
|
+
const transformed = onCopyOverride ? onCopyOverride({ value: source.value, scope: source.scope }) : cloneValue(source.value);
|
|
2017
|
+
if (target.kind === "newRule") {
|
|
2018
|
+
clipboard.set({
|
|
2019
|
+
value: transformed,
|
|
2020
|
+
sourceScope: source.scope,
|
|
2021
|
+
sourceRecordId: source.recordId,
|
|
2022
|
+
sourceLabel: source.label
|
|
2023
|
+
});
|
|
2024
|
+
onTelemetry?.({
|
|
2025
|
+
type: "clipboard.copy",
|
|
2026
|
+
recordType,
|
|
2027
|
+
sourceRef: anchorKey(source.scope)
|
|
2028
|
+
});
|
|
2029
|
+
window.setTimeout(() => onCreateRuleFromClipboardRef?.current?.(), 0);
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
2032
|
+
const create = onCreateItemDraftAtScopeRef?.current;
|
|
2033
|
+
if (!create) return;
|
|
2034
|
+
let scopeArg;
|
|
2035
|
+
if (target.kind === "global") {
|
|
2036
|
+
scopeArg = { kind: "global" };
|
|
2037
|
+
} else if (target.kind === "sameRule") {
|
|
2038
|
+
if (!source.facetRule) return;
|
|
2039
|
+
scopeArg = { kind: "rule", rule: source.facetRule };
|
|
2040
|
+
} else {
|
|
2041
|
+
const list = ruleCatalogueRef?.current ?? ruleCatalogue ?? [];
|
|
2042
|
+
const hit = list.find((r) => r.hash === target.ruleHash);
|
|
2043
|
+
if (!hit) return;
|
|
2044
|
+
scopeArg = { kind: "rule", rule: hit.rule };
|
|
2045
|
+
}
|
|
2046
|
+
const newId = create(scopeArg);
|
|
2047
|
+
if (!newId) return;
|
|
2048
|
+
setPendingPasteTarget({ kind: "record", recordId: newId });
|
|
2049
|
+
setPendingSeed({ value: transformed, sourceLabel: source.label });
|
|
2050
|
+
onTelemetry?.({
|
|
2051
|
+
type: "clipboard.copy",
|
|
2052
|
+
recordType,
|
|
2053
|
+
sourceRef: anchorKey(source.scope)
|
|
2054
|
+
});
|
|
2055
|
+
setNotice({
|
|
2056
|
+
message: i18n.duplicateToast.replace("{name}", source.label),
|
|
2057
|
+
variant: "copy"
|
|
2058
|
+
});
|
|
2059
|
+
}, [
|
|
2060
|
+
onCopyOverride,
|
|
2061
|
+
clipboard,
|
|
2062
|
+
onTelemetry,
|
|
2063
|
+
recordType,
|
|
2064
|
+
onCreateRuleFromClipboardRef,
|
|
2065
|
+
onCreateItemDraftAtScopeRef,
|
|
2066
|
+
ruleCatalogue,
|
|
2067
|
+
i18n.duplicateToast
|
|
2068
|
+
]);
|
|
1771
2069
|
useEffect(() => {
|
|
1772
2070
|
if (!pendingPasteTarget) return;
|
|
1773
2071
|
if (!editingScope) return;
|
|
@@ -1775,14 +2073,27 @@ function useShellClipboard(args) {
|
|
|
1775
2073
|
if (!matched) return;
|
|
1776
2074
|
const t = window.setTimeout(() => {
|
|
1777
2075
|
setPendingPasteTarget(null);
|
|
2076
|
+
if (pendingSeed) {
|
|
2077
|
+
const seed = pendingSeed;
|
|
2078
|
+
setPendingSeed(null);
|
|
2079
|
+
editorCtx.onChange(seed.value);
|
|
2080
|
+
onTelemetry?.({
|
|
2081
|
+
type: "clipboard.paste",
|
|
2082
|
+
recordType,
|
|
2083
|
+
sourceRef: anchorKey(editingScope),
|
|
2084
|
+
destinationRef: editingScope.raw,
|
|
2085
|
+
replaced: false
|
|
2086
|
+
});
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
1778
2089
|
void pasteCurrent();
|
|
1779
2090
|
}, 0);
|
|
1780
2091
|
return () => window.clearTimeout(t);
|
|
1781
|
-
}, [pendingPasteTarget, editingScope, isCollection, selectedItemId, selectedRecordId, pasteCurrent]);
|
|
2092
|
+
}, [pendingPasteTarget, editingScope, isCollection, selectedItemId, selectedRecordId, pasteCurrent, pendingSeed, editorCtx, onTelemetry, recordType]);
|
|
1782
2093
|
const rowClipboard = enabled ? (record) => {
|
|
1783
2094
|
const summaryHasData = record.data != null;
|
|
1784
2095
|
const sourceParsed = record.scope;
|
|
1785
|
-
const canDuplicate = summaryHasData && isCollection;
|
|
2096
|
+
const canDuplicate = summaryHasData && isCollection && !!onCreateItemDraftAtScopeRef;
|
|
1786
2097
|
const canCopyAndNewRule = summaryHasData && !isCollection && !!isRuleTab && !!onCreateRuleFromClipboardRef;
|
|
1787
2098
|
return {
|
|
1788
2099
|
onCopy: summaryHasData ? () => {
|
|
@@ -1813,24 +2124,12 @@ function useShellClipboard(args) {
|
|
|
1813
2124
|
}, 0);
|
|
1814
2125
|
} : void 0,
|
|
1815
2126
|
onDuplicate: canDuplicate ? () => {
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
});
|
|
1823
|
-
onTelemetry?.({ type: "clipboard.copy", recordType, sourceRef: anchorKey(record.scope) });
|
|
1824
|
-
onLeftSelectRef.current?.(record);
|
|
1825
|
-
const create = onCreateItemDraftRef?.current;
|
|
1826
|
-
if (!create) return;
|
|
1827
|
-
window.setTimeout(() => {
|
|
1828
|
-
const newId = create();
|
|
1829
|
-
if (newId) setPendingPasteTarget({ kind: "record", recordId: newId });
|
|
1830
|
-
}, 0);
|
|
1831
|
-
setNotice({
|
|
1832
|
-
message: i18n.duplicateToast.replace("{name}", record.label),
|
|
1833
|
-
variant: "copy"
|
|
2127
|
+
openDuplicatePicker({
|
|
2128
|
+
value: record.data,
|
|
2129
|
+
scope: sourceParsed,
|
|
2130
|
+
label: record.label,
|
|
2131
|
+
facetRule: record.facetRule ?? null,
|
|
2132
|
+
recordId: record.id ?? void 0
|
|
1834
2133
|
});
|
|
1835
2134
|
} : void 0
|
|
1836
2135
|
};
|
|
@@ -1843,10 +2142,36 @@ function useShellClipboard(args) {
|
|
|
1843
2142
|
onDismiss: () => setNotice(null)
|
|
1844
2143
|
}
|
|
1845
2144
|
) : null;
|
|
2145
|
+
const duplicatePicker = enabled ? /* @__PURE__ */ jsx(
|
|
2146
|
+
DuplicateTargetPicker,
|
|
2147
|
+
{
|
|
2148
|
+
open: pickerOpen,
|
|
2149
|
+
sourceLabel: pickerSourceRef.current?.label ?? "",
|
|
2150
|
+
sourceHasRule: !!pickerSourceRef.current?.facetRule,
|
|
2151
|
+
rules: (ruleCatalogueRef?.current ?? ruleCatalogue ?? []).map((r) => ({
|
|
2152
|
+
hash: r.hash,
|
|
2153
|
+
summary: r.summary,
|
|
2154
|
+
count: r.count
|
|
2155
|
+
})),
|
|
2156
|
+
supportsRules,
|
|
2157
|
+
onCancel: onDuplicatePickerCancel,
|
|
2158
|
+
onConfirm: onDuplicatePickerConfirm,
|
|
2159
|
+
i18n: {
|
|
2160
|
+
title: i18n.duplicateDialogTitle,
|
|
2161
|
+
global: i18n.duplicateTargetGlobal,
|
|
2162
|
+
sameRule: i18n.duplicateTargetSameRule,
|
|
2163
|
+
existingRules: i18n.duplicateTargetExistingRules,
|
|
2164
|
+
newRule: i18n.duplicateTargetNewRule,
|
|
2165
|
+
confirm: i18n.duplicateConfirm,
|
|
2166
|
+
cancel: i18n.duplicateCancel
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
) : null;
|
|
1846
2170
|
return {
|
|
1847
2171
|
editorClipboard,
|
|
1848
2172
|
rowClipboard,
|
|
1849
2173
|
confirmDialog: pasteConfirm.dialog,
|
|
2174
|
+
duplicatePicker,
|
|
1850
2175
|
toast,
|
|
1851
2176
|
store: clipboard
|
|
1852
2177
|
};
|
|
@@ -3981,8 +4306,8 @@ var LifecycleStatusControl = ({
|
|
|
3981
4306
|
if (next === value) return;
|
|
3982
4307
|
setBusy(true);
|
|
3983
4308
|
try {
|
|
3984
|
-
await SL.app.records.update(collectionId, appId, recordId, { status: next }, true);
|
|
3985
|
-
onChanged?.(next);
|
|
4309
|
+
const updated = await SL.app.records.update(collectionId, appId, recordId, { status: next }, true);
|
|
4310
|
+
onChanged?.(next, updated);
|
|
3986
4311
|
} catch (err) {
|
|
3987
4312
|
console.warn("[LifecycleStatusControl] update failed", err);
|
|
3988
4313
|
} finally {
|
|
@@ -4176,9 +4501,9 @@ var LifecycleStatusMenu = ({
|
|
|
4176
4501
|
}
|
|
4177
4502
|
setBusy(next.value);
|
|
4178
4503
|
try {
|
|
4179
|
-
await SL.app.records.update(collectionId, appId, recordId, { status: next.value }, true);
|
|
4504
|
+
const updated = await SL.app.records.update(collectionId, appId, recordId, { status: next.value }, true);
|
|
4180
4505
|
onTelemetry?.({ from: current, to: next.value });
|
|
4181
|
-
onChanged?.(next.value);
|
|
4506
|
+
onChanged?.(next.value, updated);
|
|
4182
4507
|
} catch (err) {
|
|
4183
4508
|
console.warn("[LifecycleStatusMenu] update failed", err);
|
|
4184
4509
|
} finally {
|
|
@@ -4684,7 +5009,8 @@ function useCollectionItems(args) {
|
|
|
4684
5009
|
label: stableItemId,
|
|
4685
5010
|
updatedAt: rec.updatedAt,
|
|
4686
5011
|
itemId: stableItemId,
|
|
4687
|
-
facetRule: recFacetRule
|
|
5012
|
+
facetRule: recFacetRule,
|
|
5013
|
+
lifecycleStatus: rec.status ?? void 0
|
|
4688
5014
|
};
|
|
4689
5015
|
return toSummary2(rec, base);
|
|
4690
5016
|
}).filter((x) => x !== null);
|
|
@@ -5159,6 +5485,22 @@ function RecordEditor({
|
|
|
5159
5485
|
]
|
|
5160
5486
|
}
|
|
5161
5487
|
),
|
|
5488
|
+
clipboard.onDuplicate && /* @__PURE__ */ jsxs(
|
|
5489
|
+
"button",
|
|
5490
|
+
{
|
|
5491
|
+
type: "button",
|
|
5492
|
+
onClick: clipboard.onDuplicate,
|
|
5493
|
+
disabled: !clipboard.canCopy || !!ctx.isSaving,
|
|
5494
|
+
title: clipboard.duplicateLabel ?? i18n.duplicateAction ?? "Duplicate",
|
|
5495
|
+
"aria-label": clipboard.duplicateLabel ?? i18n.duplicateAction ?? "Duplicate",
|
|
5496
|
+
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",
|
|
5497
|
+
style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
|
|
5498
|
+
children: [
|
|
5499
|
+
/* @__PURE__ */ jsx(CopyPlus, { className: "w-3 h-3" }),
|
|
5500
|
+
clipboard.duplicateLabel ?? i18n.duplicateAction ?? "Duplicate"
|
|
5501
|
+
]
|
|
5502
|
+
}
|
|
5503
|
+
),
|
|
5162
5504
|
/* @__PURE__ */ jsxs(
|
|
5163
5505
|
"button",
|
|
5164
5506
|
{
|
|
@@ -6895,7 +7237,9 @@ function SiblingRail({
|
|
|
6895
7237
|
total,
|
|
6896
7238
|
hasNextPage,
|
|
6897
7239
|
isFetchingNextPage,
|
|
6898
|
-
onLoadMore
|
|
7240
|
+
onLoadMore,
|
|
7241
|
+
rowClipboard,
|
|
7242
|
+
rowActions
|
|
6899
7243
|
}) {
|
|
6900
7244
|
const ruleLabelLookup = useRuleLabelLookup();
|
|
6901
7245
|
const newLabel = i18n.newItem.includes("{noun}") ? i18n.newItem.replace("{noun}", itemNoun ?? "item") : i18n.newItem;
|
|
@@ -6934,45 +7278,68 @@ function SiblingRail({
|
|
|
6934
7278
|
const ruleClauses = item.facetRule ? summarizeFacetRule(item.facetRule, ruleLabelLookup) : [];
|
|
6935
7279
|
const isTargeted = ruleClauses.length > 0;
|
|
6936
7280
|
const ruleSummary = isTargeted ? ruleClauses.map((c) => c.label).join(" \xB7 ") : null;
|
|
6937
|
-
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
/* @__PURE__ */
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
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,
|
|
7329
|
+
{
|
|
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
|
|
6971
7338
|
}
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
}
|
|
6975
|
-
) }, key);
|
|
7339
|
+
}
|
|
7340
|
+
);
|
|
7341
|
+
})()
|
|
7342
|
+
] }) }, key);
|
|
6976
7343
|
}) })
|
|
6977
7344
|
] }),
|
|
6978
7345
|
onLoadMore && /* @__PURE__ */ jsx(
|
|
@@ -8327,14 +8694,14 @@ var coerceDraftItemId2 = (generateItemId) => {
|
|
|
8327
8694
|
const candidate = generateItemId ? generateItemId() : mintDraftItemId2();
|
|
8328
8695
|
return isDraftId3(candidate) ? candidate : `draft:${candidate}`;
|
|
8329
8696
|
};
|
|
8330
|
-
var productItemToSummary = (p) => {
|
|
8697
|
+
var productItemToSummary = (p, configured) => {
|
|
8331
8698
|
const ref = buildRef({ productId: p.id });
|
|
8332
8699
|
return {
|
|
8333
8700
|
id: null,
|
|
8334
8701
|
ref,
|
|
8335
8702
|
scope: parseRef(ref),
|
|
8336
8703
|
data: null,
|
|
8337
|
-
status: "empty",
|
|
8704
|
+
status: configured?.has(p.id) ? "configured" : "empty",
|
|
8338
8705
|
label: p.name,
|
|
8339
8706
|
subtitle: p.sku ?? void 0
|
|
8340
8707
|
};
|
|
@@ -9123,6 +9490,8 @@ function RecordsAdminShellInner(props) {
|
|
|
9123
9490
|
]);
|
|
9124
9491
|
const onLeftSelectRef = useRef(null);
|
|
9125
9492
|
const onCreateItemDraftRef = useRef(null);
|
|
9493
|
+
const onCreateItemDraftAtScopeRef = useRef(null);
|
|
9494
|
+
const ruleCatalogueRef = useRef([]);
|
|
9126
9495
|
const onCreateRuleFromClipboardRef = useRef(null);
|
|
9127
9496
|
const previewReopenAnchorRef = useRef(null);
|
|
9128
9497
|
const { runWithGuard } = useDirtyNavigation({
|
|
@@ -9175,14 +9544,17 @@ function RecordsAdminShellInner(props) {
|
|
|
9175
9544
|
isCollection,
|
|
9176
9545
|
selectedItemId,
|
|
9177
9546
|
selectedRecordId,
|
|
9178
|
-
resolved: { source: resolved.source, recordId: resolved.recordId },
|
|
9547
|
+
resolved: { source: resolved.source, recordId: resolved.recordId, facetRule: resolved.facetRule ?? null },
|
|
9179
9548
|
onTelemetry,
|
|
9180
9549
|
onCopyOverride,
|
|
9181
9550
|
onPasteOverride,
|
|
9182
9551
|
onLeftSelectRef,
|
|
9183
9552
|
onCreateItemDraftRef,
|
|
9184
9553
|
onCreateRuleFromClipboardRef,
|
|
9185
|
-
isRuleTab: activeScope === "rule" || activeScope === "collection"
|
|
9554
|
+
isRuleTab: activeScope === "rule" || activeScope === "collection",
|
|
9555
|
+
ruleCatalogueRef,
|
|
9556
|
+
supportsRules: effectiveTopLevelScopes.includes("rule"),
|
|
9557
|
+
onCreateItemDraftAtScopeRef
|
|
9186
9558
|
});
|
|
9187
9559
|
const editorClipboard = shellClipboard.editorClipboard;
|
|
9188
9560
|
const rowClipboard = shellClipboard.rowClipboard;
|
|
@@ -9219,11 +9591,12 @@ function RecordsAdminShellInner(props) {
|
|
|
9219
9591
|
label: label2,
|
|
9220
9592
|
onAction: async () => {
|
|
9221
9593
|
try {
|
|
9222
|
-
await SL.app.records.update(collectionId, appId, record.id, { status: next }, true);
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9594
|
+
const updated = await SL.app.records.update(collectionId, appId, record.id, { status: next }, true);
|
|
9595
|
+
if (updated) {
|
|
9596
|
+
patchRecordIntoCaches(queryClient, ctx, updated);
|
|
9597
|
+
queryClient.invalidateQueries({
|
|
9598
|
+
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
9599
|
+
});
|
|
9227
9600
|
}
|
|
9228
9601
|
} catch (err) {
|
|
9229
9602
|
console.warn("[RecordsAdminShell] lifecycle update failed", err);
|
|
@@ -9263,6 +9636,14 @@ function RecordsAdminShellInner(props) {
|
|
|
9263
9636
|
const itemViewCtx = baseItemViewCtx;
|
|
9264
9637
|
const renderEditorWithPreview = () => {
|
|
9265
9638
|
if (!editingTargetScope) return null;
|
|
9639
|
+
const targetingSavedRecord = isCollection && selectedItemId && !isDraftId3(selectedItemId) || !!selectedRecordId && !isDraftId3(selectedRecordId);
|
|
9640
|
+
if (resolved.isLoading && targetingSavedRecord && resolved.source !== "self") {
|
|
9641
|
+
return /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-3", "aria-busy": "true", "aria-live": "polite", children: [
|
|
9642
|
+
/* @__PURE__ */ jsx("div", { className: "h-6 rounded-md animate-pulse", style: { background: "hsl(var(--ra-muted))", width: "40%" } }),
|
|
9643
|
+
/* @__PURE__ */ jsx("div", { className: "h-24 rounded-md animate-pulse", style: { background: "hsl(var(--ra-muted))" } }),
|
|
9644
|
+
/* @__PURE__ */ jsx("div", { className: "h-24 rounded-md animate-pulse", style: { background: "hsl(var(--ra-muted))" } })
|
|
9645
|
+
] });
|
|
9646
|
+
}
|
|
9266
9647
|
const previewAnchorRef = previewReopenAnchorRef;
|
|
9267
9648
|
const previewBody = renderPreview && effectivePreviewScope ? renderPreview({ resolved: editorCtx.value, previewScope: effectivePreviewScope }) : null;
|
|
9268
9649
|
const scopePicker = previewScopePicker && effectivePreviewScope ? /* @__PURE__ */ jsx(
|
|
@@ -9314,7 +9695,7 @@ function RecordsAdminShellInner(props) {
|
|
|
9314
9695
|
]
|
|
9315
9696
|
}
|
|
9316
9697
|
) : 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;
|
|
9698
|
+
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
9699
|
const editorLifecycleControl = selectedSummary?.id ? /* @__PURE__ */ jsx(
|
|
9319
9700
|
LifecycleStatusControl,
|
|
9320
9701
|
{
|
|
@@ -9324,8 +9705,13 @@ function RecordsAdminShellInner(props) {
|
|
|
9324
9705
|
recordId: selectedSummary.id,
|
|
9325
9706
|
current: selectedSummary.lifecycleStatus,
|
|
9326
9707
|
i18n,
|
|
9327
|
-
onChanged: () => {
|
|
9328
|
-
|
|
9708
|
+
onChanged: (_next, updated) => {
|
|
9709
|
+
if (updated) {
|
|
9710
|
+
patchRecordIntoCaches(queryClient, ctx, updated);
|
|
9711
|
+
queryClient.invalidateQueries({
|
|
9712
|
+
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
9713
|
+
});
|
|
9714
|
+
}
|
|
9329
9715
|
}
|
|
9330
9716
|
}
|
|
9331
9717
|
) : null;
|
|
@@ -9348,11 +9734,12 @@ function RecordsAdminShellInner(props) {
|
|
|
9348
9734
|
from: e.from,
|
|
9349
9735
|
to: e.to
|
|
9350
9736
|
}),
|
|
9351
|
-
onChanged: () => {
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9737
|
+
onChanged: (_next, updated) => {
|
|
9738
|
+
if (updated) {
|
|
9739
|
+
patchRecordIntoCaches(queryClient, ctx, updated);
|
|
9740
|
+
queryClient.invalidateQueries({
|
|
9741
|
+
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
9742
|
+
});
|
|
9356
9743
|
}
|
|
9357
9744
|
}
|
|
9358
9745
|
}
|
|
@@ -9524,7 +9911,7 @@ function RecordsAdminShellInner(props) {
|
|
|
9524
9911
|
}];
|
|
9525
9912
|
}
|
|
9526
9913
|
const configured = scopeCounts.productIds;
|
|
9527
|
-
const all = productBrowse.items.map(productItemToSummary);
|
|
9914
|
+
const all = productBrowse.items.map((p) => productItemToSummary(p, configured));
|
|
9528
9915
|
const isConfigured = (s) => {
|
|
9529
9916
|
const pid = s.scope.productId;
|
|
9530
9917
|
return !!pid && configured.has(pid);
|
|
@@ -9949,6 +10336,26 @@ function RecordsAdminShellInner(props) {
|
|
|
9949
10336
|
onLeftSelectRef.current = onLeftSelect;
|
|
9950
10337
|
onCreateItemDraftRef.current = onItemCreate;
|
|
9951
10338
|
onCreateRuleFromClipboardRef.current = () => onCreateRule("paste");
|
|
10339
|
+
ruleCatalogueRef.current = ruleCatalogue;
|
|
10340
|
+
onCreateItemDraftAtScopeRef.current = (target) => {
|
|
10341
|
+
if (!isCollection) return null;
|
|
10342
|
+
if (target.kind === "global") {
|
|
10343
|
+
if (activeScope !== "collection") setActiveScope("collection");
|
|
10344
|
+
setSelectedRecordId(null);
|
|
10345
|
+
setDraftKind(null);
|
|
10346
|
+
const id2 = onItemCreate();
|
|
10347
|
+
return id2 ?? null;
|
|
10348
|
+
}
|
|
10349
|
+
if (activeScope !== "rule") setActiveScope("rule");
|
|
10350
|
+
setRuleWizardRule(target.rule);
|
|
10351
|
+
setRuleWizardSeedMode(null);
|
|
10352
|
+
setRuleWizardDraftKey(null);
|
|
10353
|
+
setRuleWizardStep(2);
|
|
10354
|
+
setDraftKind("rule");
|
|
10355
|
+
setSelectedRecordId(null);
|
|
10356
|
+
const id = onItemCreate();
|
|
10357
|
+
return id ?? null;
|
|
10358
|
+
};
|
|
9952
10359
|
return /* @__PURE__ */ jsx(RuleLabelLookupProvider, { value: ruleLabelLookup, children: /* @__PURE__ */ jsxs(
|
|
9953
10360
|
"div",
|
|
9954
10361
|
{
|
|
@@ -9957,6 +10364,7 @@ function RecordsAdminShellInner(props) {
|
|
|
9957
10364
|
children: [
|
|
9958
10365
|
dirtyConfirm.dialog,
|
|
9959
10366
|
shellClipboard.confirmDialog,
|
|
10367
|
+
shellClipboard.duplicatePicker,
|
|
9960
10368
|
shellClipboard.toast,
|
|
9961
10369
|
(() => {
|
|
9962
10370
|
const showFloatHelp = !!intro && dismissed && resolvedReopenAffordance === "footer" && !headerWillRender;
|
|
@@ -10123,6 +10531,8 @@ function RecordsAdminShellInner(props) {
|
|
|
10123
10531
|
onLoadMore: () => {
|
|
10124
10532
|
void collectionItems.fetchNextPage();
|
|
10125
10533
|
},
|
|
10534
|
+
rowClipboard,
|
|
10535
|
+
rowActions: wrappedRecordActions,
|
|
10126
10536
|
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
10537
|
contextSummary: isLifecycleRailEarly && lifecycleBucketLabel ? `${scopedCollectionItemsList.length} ${itemNounLabel}${scopedCollectionItemsList.length === 1 ? "" : "s"}` : activeScope === "rule" ? activeRuleSummary : activeScope === "product" ? editorHeaderLabel ?? null : null,
|
|
10128
10538
|
i18n
|
|
@@ -10274,7 +10684,8 @@ function RecordsAdminShellInner(props) {
|
|
|
10274
10684
|
const ids = singletonConflicts.flatMap((c) => c.duplicates.map((d) => d.id)).filter((id) => !!id);
|
|
10275
10685
|
for (const id of ids) {
|
|
10276
10686
|
try {
|
|
10277
|
-
await SL.app.records.update(collectionId, appId, id, { status: archivedStatusValue }, true);
|
|
10687
|
+
const updated = await SL.app.records.update(collectionId, appId, id, { status: archivedStatusValue }, true);
|
|
10688
|
+
if (updated) patchRecordIntoCaches(queryClient, ctx, updated);
|
|
10278
10689
|
onTelemetry?.({
|
|
10279
10690
|
type: "recordAction.invoke",
|
|
10280
10691
|
recordType,
|
|
@@ -10285,17 +10696,16 @@ function RecordsAdminShellInner(props) {
|
|
|
10285
10696
|
console.warn("[RecordsAdminShell] archive-duplicate failed", id, err);
|
|
10286
10697
|
}
|
|
10287
10698
|
}
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
}
|
|
10292
|
-
await refetchAll();
|
|
10699
|
+
queryClient.invalidateQueries({
|
|
10700
|
+
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
10701
|
+
});
|
|
10293
10702
|
} : void 0,
|
|
10294
10703
|
onDeleteDuplicates: enableDeleteDuplicates ? async () => {
|
|
10295
10704
|
const ids = singletonConflicts.flatMap((c) => c.duplicates.map((d) => d.id)).filter((id) => !!id);
|
|
10296
10705
|
for (const id of ids) {
|
|
10297
10706
|
try {
|
|
10298
10707
|
await SL.app.records.remove(collectionId, appId, id, true);
|
|
10708
|
+
removeRecordFromCaches(queryClient, ctx, id);
|
|
10299
10709
|
onTelemetry?.({
|
|
10300
10710
|
type: "recordAction.invoke",
|
|
10301
10711
|
recordType,
|
|
@@ -10306,11 +10716,9 @@ function RecordsAdminShellInner(props) {
|
|
|
10306
10716
|
console.warn("[RecordsAdminShell] delete-duplicate failed", id, err);
|
|
10307
10717
|
}
|
|
10308
10718
|
}
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
}
|
|
10313
|
-
await refetchAll();
|
|
10719
|
+
queryClient.invalidateQueries({
|
|
10720
|
+
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
10721
|
+
});
|
|
10314
10722
|
} : void 0,
|
|
10315
10723
|
i18n: {
|
|
10316
10724
|
title: i18n.conflictBannerTitle,
|