@proveanything/smartlinks-utils-ui 0.11.11 → 0.12.2
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 +35 -0
- package/dist/components/AssetPicker/index.css.map +1 -1
- package/dist/components/ConditionsEditor/index.css +35 -0
- package/dist/components/ConditionsEditor/index.css.map +1 -1
- package/dist/components/FontPicker/index.css +35 -0
- package/dist/components/FontPicker/index.css.map +1 -1
- package/dist/components/IconPicker/index.css +35 -0
- package/dist/components/IconPicker/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.css +35 -0
- package/dist/components/RecordsAdmin/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.d.ts +310 -41
- package/dist/components/RecordsAdmin/index.js +1024 -122
- package/dist/components/RecordsAdmin/index.js.map +1 -1
- package/dist/index.css +35 -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, 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, ArrowRight, Globe2,
|
|
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';
|
|
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';
|
|
@@ -250,7 +250,26 @@ var DEFAULT_I18N = {
|
|
|
250
250
|
hookBeforeSaveFailed: "Couldn't save: {message}",
|
|
251
251
|
hookAfterSaveFailed: "Saved, but a follow-up step failed: {message}",
|
|
252
252
|
hookBeforeDeleteFailed: "Couldn't delete: {message}",
|
|
253
|
-
hookAfterDeleteFailed: "Deleted, but a follow-up step failed: {message}"
|
|
253
|
+
hookAfterDeleteFailed: "Deleted, but a follow-up step failed: {message}",
|
|
254
|
+
historyDisclosureShow: "Show {n} archived",
|
|
255
|
+
historyDisclosureHide: "Hide {n} archived",
|
|
256
|
+
lifecycleStatusLabel: "Status",
|
|
257
|
+
lifecycleStatusActive: "Active",
|
|
258
|
+
lifecycleStatusArchived: "Archived",
|
|
259
|
+
lifecycleStatusDraft: "Draft",
|
|
260
|
+
lifecycleStatusHint: "Archived records aren't returned to public consumers.",
|
|
261
|
+
actionArchive: "Archive",
|
|
262
|
+
actionRestore: "Restore",
|
|
263
|
+
lifecycleMenuLabel: "Status",
|
|
264
|
+
lifecycleChangeTo: "Change status to {label}",
|
|
265
|
+
lifecycleCurrentBadge: "Current",
|
|
266
|
+
conflictBannerTitle: "Duplicate records detected",
|
|
267
|
+
conflictBannerBodyOne: "{n} extra active record shares this slot. Only the oldest is used.",
|
|
268
|
+
conflictBannerBodyMany: "{slots} slots have duplicates ({dups} extra records). Only the oldest in each is used.",
|
|
269
|
+
conflictArchiveDuplicates: "Archive duplicates",
|
|
270
|
+
conflictDeleteDuplicates: "Delete duplicates",
|
|
271
|
+
conflictDeleteConfirm: "Permanently delete {n} duplicate record(s)? This cannot be undone.",
|
|
272
|
+
conflictResolveLabel: "Resolve"
|
|
254
273
|
};
|
|
255
274
|
|
|
256
275
|
// src/components/RecordsAdmin/types/presentation.ts
|
|
@@ -603,6 +622,72 @@ var useScopeCounts = (args) => {
|
|
|
603
622
|
return result;
|
|
604
623
|
};
|
|
605
624
|
var scopeCountsQueryKey = (collectionId, appId, recordType) => [...QK_BASE, collectionId, appId, recordType ?? null];
|
|
625
|
+
|
|
626
|
+
// src/components/RecordsAdmin/data/singletonConflicts.ts
|
|
627
|
+
var NO_RULE = "__no_rule__";
|
|
628
|
+
var DEFAULT_ACTIVE_STATUSES = ["active"];
|
|
629
|
+
var isActive = (record, activeStatuses = DEFAULT_ACTIVE_STATUSES) => {
|
|
630
|
+
const s = record.lifecycleStatus;
|
|
631
|
+
if (s == null || s === "") return true;
|
|
632
|
+
return activeStatuses.includes(s);
|
|
633
|
+
};
|
|
634
|
+
var slotKey = (record) => {
|
|
635
|
+
const anchor = anchorKey(record.scope);
|
|
636
|
+
const rule = ruleHash(record.facetRule) ?? NO_RULE;
|
|
637
|
+
return `${anchor}::${rule}`;
|
|
638
|
+
};
|
|
639
|
+
var pickActiveRecord = (records) => {
|
|
640
|
+
if (records.length === 0) return null;
|
|
641
|
+
if (records.length === 1) return records[0];
|
|
642
|
+
const sorted = [...records].sort((a, b) => {
|
|
643
|
+
const aT = a.updatedAt;
|
|
644
|
+
const bT = b.updatedAt;
|
|
645
|
+
if (aT && bT) {
|
|
646
|
+
if (aT < bT) return -1;
|
|
647
|
+
if (aT > bT) return 1;
|
|
648
|
+
} else if (aT && !bT) {
|
|
649
|
+
return -1;
|
|
650
|
+
} else if (!aT && bT) {
|
|
651
|
+
return 1;
|
|
652
|
+
}
|
|
653
|
+
const aId = a.id ?? "";
|
|
654
|
+
const bId = b.id ?? "";
|
|
655
|
+
if (aId < bId) return -1;
|
|
656
|
+
if (aId > bId) return 1;
|
|
657
|
+
return 0;
|
|
658
|
+
});
|
|
659
|
+
return sorted[0];
|
|
660
|
+
};
|
|
661
|
+
var groupSingletonConflicts = (items, activeStatuses = DEFAULT_ACTIVE_STATUSES) => {
|
|
662
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
663
|
+
for (const item of items) {
|
|
664
|
+
if (!isActive(item, activeStatuses)) continue;
|
|
665
|
+
const k = slotKey(item);
|
|
666
|
+
const existing = buckets.get(k);
|
|
667
|
+
if (existing) existing.push(item);
|
|
668
|
+
else buckets.set(k, [item]);
|
|
669
|
+
}
|
|
670
|
+
const conflicts = [];
|
|
671
|
+
for (const [key, records] of buckets) {
|
|
672
|
+
if (records.length < 2) continue;
|
|
673
|
+
const active = pickActiveRecord(records);
|
|
674
|
+
conflicts.push({
|
|
675
|
+
key,
|
|
676
|
+
records,
|
|
677
|
+
active,
|
|
678
|
+
duplicates: records.filter((r) => r !== active)
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
return conflicts;
|
|
682
|
+
};
|
|
683
|
+
var findConflictForRecord = (recordId, conflicts) => {
|
|
684
|
+
for (const c of conflicts) {
|
|
685
|
+
if (c.records.some((r) => r.id === recordId)) return c;
|
|
686
|
+
}
|
|
687
|
+
return null;
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
// src/components/RecordsAdmin/hooks/useRecordList.ts
|
|
606
691
|
var defaultClassify = (r) => {
|
|
607
692
|
if (!r.data) return "empty";
|
|
608
693
|
const keys = Object.keys(r.data);
|
|
@@ -662,7 +747,8 @@ var toSummary = (rec) => {
|
|
|
662
747
|
status: "configured",
|
|
663
748
|
label: fallbackLabel,
|
|
664
749
|
updatedAt: rec.updatedAt,
|
|
665
|
-
facetRule
|
|
750
|
+
facetRule,
|
|
751
|
+
lifecycleStatus: rec.status ?? void 0
|
|
666
752
|
};
|
|
667
753
|
};
|
|
668
754
|
var QK_BASE2 = ["records-admin", "list"];
|
|
@@ -676,7 +762,8 @@ var useRecordList = (args) => {
|
|
|
676
762
|
enabled = true,
|
|
677
763
|
scaffolder,
|
|
678
764
|
contextScope,
|
|
679
|
-
pageSize = 100
|
|
765
|
+
pageSize = 100,
|
|
766
|
+
activeStatuses = DEFAULT_ACTIVE_STATUSES
|
|
680
767
|
} = args;
|
|
681
768
|
const queryClient = useQueryClient();
|
|
682
769
|
const queryKey = useMemo(
|
|
@@ -739,6 +826,24 @@ var useRecordList = (args) => {
|
|
|
739
826
|
partial: items.filter((r) => r.status === "partial").length,
|
|
740
827
|
empty: items.filter((r) => r.status === "empty").length
|
|
741
828
|
}), [items]);
|
|
829
|
+
const activeItems = useMemo(
|
|
830
|
+
() => filtered.filter((r) => isActive(r, activeStatuses)),
|
|
831
|
+
[filtered, activeStatuses]
|
|
832
|
+
);
|
|
833
|
+
const historyItems = useMemo(
|
|
834
|
+
() => filtered.filter((r) => !isActive(r, activeStatuses)),
|
|
835
|
+
[filtered, activeStatuses]
|
|
836
|
+
);
|
|
837
|
+
const historyBySlot = useMemo(() => {
|
|
838
|
+
const map = /* @__PURE__ */ new Map();
|
|
839
|
+
for (const r of historyItems) {
|
|
840
|
+
const k = slotKey(r);
|
|
841
|
+
const list = map.get(k);
|
|
842
|
+
if (list) list.push(r);
|
|
843
|
+
else map.set(k, [r]);
|
|
844
|
+
}
|
|
845
|
+
return map;
|
|
846
|
+
}, [historyItems]);
|
|
742
847
|
const refetch = useCallback(() => queryClient.refetchQueries({
|
|
743
848
|
queryKey: [...QK_BASE2, ctx.collectionId, ctx.appId, ctx.recordType]
|
|
744
849
|
}), [queryClient, ctx.collectionId, ctx.appId, ctx.recordType]);
|
|
@@ -746,6 +851,9 @@ var useRecordList = (args) => {
|
|
|
746
851
|
return {
|
|
747
852
|
allItems: items,
|
|
748
853
|
items: filtered,
|
|
854
|
+
activeItems,
|
|
855
|
+
historyItems,
|
|
856
|
+
historyBySlot,
|
|
749
857
|
total,
|
|
750
858
|
counts,
|
|
751
859
|
isLoading: query.isLoading,
|
|
@@ -1021,7 +1129,8 @@ function useShellBrowser(opts) {
|
|
|
1021
1129
|
selectedProductId,
|
|
1022
1130
|
drillTab,
|
|
1023
1131
|
classify: classify3,
|
|
1024
|
-
pageSize
|
|
1132
|
+
pageSize,
|
|
1133
|
+
activeStatuses
|
|
1025
1134
|
} = opts;
|
|
1026
1135
|
const [search, setSearch] = useState("");
|
|
1027
1136
|
const [filter, setFilter] = useState("all");
|
|
@@ -1048,7 +1157,8 @@ function useShellBrowser(opts) {
|
|
|
1048
1157
|
classify: classify3,
|
|
1049
1158
|
contextScope,
|
|
1050
1159
|
enabled: recordListEnabled,
|
|
1051
|
-
pageSize
|
|
1160
|
+
pageSize,
|
|
1161
|
+
activeStatuses
|
|
1052
1162
|
});
|
|
1053
1163
|
const facetBrowse = useFacetBrowse({
|
|
1054
1164
|
SL,
|
|
@@ -2569,7 +2679,11 @@ var createEditorStore = () => {
|
|
|
2569
2679
|
ref: spec.ref,
|
|
2570
2680
|
scope: anchors,
|
|
2571
2681
|
data: persistedValue,
|
|
2572
|
-
facetRule: persistedFacetRule
|
|
2682
|
+
facetRule: persistedFacetRule,
|
|
2683
|
+
// Seed the configured lifecycle default (e.g. 'draft') so
|
|
2684
|
+
// brand-new records land in the right bucket without the host
|
|
2685
|
+
// wiring it into every editor's beforeSave.
|
|
2686
|
+
status: spec.defaultStatus
|
|
2573
2687
|
});
|
|
2574
2688
|
nextRecordId = created?.id ?? nextRecordId;
|
|
2575
2689
|
savedRecord = created;
|
|
@@ -2578,7 +2692,10 @@ var createEditorStore = () => {
|
|
|
2578
2692
|
ref: spec.ref,
|
|
2579
2693
|
scope: anchors,
|
|
2580
2694
|
data: persistedValue,
|
|
2581
|
-
facetRule: persistedFacetRule
|
|
2695
|
+
facetRule: persistedFacetRule,
|
|
2696
|
+
// Same seeding rule applies on the upsert path used by
|
|
2697
|
+
// singleton-cardinality first-time saves.
|
|
2698
|
+
status: !entry.recordId ? spec.defaultStatus : void 0
|
|
2582
2699
|
});
|
|
2583
2700
|
nextRecordId = upserted.record?.id ?? nextRecordId;
|
|
2584
2701
|
savedRecord = upserted.record;
|
|
@@ -3044,7 +3161,8 @@ function useShellEditorTarget(args) {
|
|
|
3044
3161
|
defaultData,
|
|
3045
3162
|
deriveDraftLabel,
|
|
3046
3163
|
onSaved,
|
|
3047
|
-
onDeleted
|
|
3164
|
+
onDeleted,
|
|
3165
|
+
defaultStatus
|
|
3048
3166
|
} = args;
|
|
3049
3167
|
const editorTargetSpec = useMemo(() => {
|
|
3050
3168
|
if (!editingTargetScope) return null;
|
|
@@ -3076,7 +3194,8 @@ function useShellEditorTarget(args) {
|
|
|
3076
3194
|
label,
|
|
3077
3195
|
draftKey,
|
|
3078
3196
|
isDraftScope,
|
|
3079
|
-
initialData: createMode ? ruleWizardInitialData : explicitSingletonInitialData ?? void 0
|
|
3197
|
+
initialData: createMode ? ruleWizardInitialData : explicitSingletonInitialData ?? void 0,
|
|
3198
|
+
defaultStatus
|
|
3080
3199
|
};
|
|
3081
3200
|
}, [
|
|
3082
3201
|
editingTargetScope?.raw,
|
|
@@ -3091,7 +3210,8 @@ function useShellEditorTarget(args) {
|
|
|
3091
3210
|
ruleWizardDraftKey,
|
|
3092
3211
|
ruleWizardInitialData,
|
|
3093
3212
|
explicitSingletonInitialData,
|
|
3094
|
-
ruleWizardRule
|
|
3213
|
+
ruleWizardRule,
|
|
3214
|
+
defaultStatus
|
|
3095
3215
|
]);
|
|
3096
3216
|
const editorCtx = useEditorBridge({
|
|
3097
3217
|
target: editorTargetSpec,
|
|
@@ -3207,7 +3327,7 @@ var ScopeTabs = ({
|
|
|
3207
3327
|
const iconMap = icons ?? DEFAULT_ICONS.scope;
|
|
3208
3328
|
return /* @__PURE__ */ jsx("div", { role: "tablist", className: "ra-tabs", "aria-label": "Record scope", children: scopes.map((s) => {
|
|
3209
3329
|
const Icon = iconMap[s] ?? DEFAULT_ICONS.scope[s];
|
|
3210
|
-
const
|
|
3330
|
+
const isActive2 = active === s;
|
|
3211
3331
|
const count = counts?.[s];
|
|
3212
3332
|
const tooltip = tooltips?.[s];
|
|
3213
3333
|
return /* @__PURE__ */ jsxs(
|
|
@@ -3215,7 +3335,7 @@ var ScopeTabs = ({
|
|
|
3215
3335
|
{
|
|
3216
3336
|
type: "button",
|
|
3217
3337
|
role: "tab",
|
|
3218
|
-
"aria-selected":
|
|
3338
|
+
"aria-selected": isActive2,
|
|
3219
3339
|
onClick: () => onChange(s),
|
|
3220
3340
|
disabled: loading,
|
|
3221
3341
|
className: "ra-tab",
|
|
@@ -3650,7 +3770,8 @@ var RecordList = ({
|
|
|
3650
3770
|
renderGroupActions,
|
|
3651
3771
|
rowClipboard,
|
|
3652
3772
|
rowActions,
|
|
3653
|
-
i18n
|
|
3773
|
+
i18n,
|
|
3774
|
+
historyBySlot
|
|
3654
3775
|
}) => {
|
|
3655
3776
|
const containerRef = useRef(null);
|
|
3656
3777
|
const onKeyDown = useCallback((e) => {
|
|
@@ -3712,12 +3833,53 @@ var RecordList = ({
|
|
|
3712
3833
|
}
|
|
3713
3834
|
return orderedKeys.map((k) => buckets.get(k));
|
|
3714
3835
|
}, [items, groupBy]);
|
|
3836
|
+
const [expandedSlots, setExpandedSlots] = useState(() => /* @__PURE__ */ new Set());
|
|
3837
|
+
const toggleSlot = useCallback((k) => {
|
|
3838
|
+
setExpandedSlots((prev) => {
|
|
3839
|
+
const next = new Set(prev);
|
|
3840
|
+
if (next.has(k)) next.delete(k);
|
|
3841
|
+
else next.add(k);
|
|
3842
|
+
return next;
|
|
3843
|
+
});
|
|
3844
|
+
}, []);
|
|
3715
3845
|
const renderItems = (rows) => {
|
|
3716
3846
|
const compact = presentation === "compact";
|
|
3717
3847
|
return /* @__PURE__ */ jsx("ul", { children: rows.map((item, idx) => {
|
|
3718
3848
|
const ctx = buildCtx(item);
|
|
3719
3849
|
const key = item.id ?? (anchorKey(item.scope) || `pos:${idx}`);
|
|
3720
|
-
|
|
3850
|
+
const sKey = historyBySlot ? slotKey(item) : null;
|
|
3851
|
+
const history = sKey ? historyBySlot.get(sKey) : void 0;
|
|
3852
|
+
const expanded = sKey ? expandedSlots.has(sKey) : false;
|
|
3853
|
+
const showLabel = i18n?.historyDisclosureShow ?? "Show {n} archived";
|
|
3854
|
+
const hideLabel = i18n?.historyDisclosureHide ?? "Hide {n} archived";
|
|
3855
|
+
const label = (expanded ? hideLabel : showLabel).replace("{n}", String(history?.length ?? 0));
|
|
3856
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
3857
|
+
renderListRow ? renderListRow(item, ctx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: item, ctx, compact }),
|
|
3858
|
+
history && history.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "ra-history-block", children: [
|
|
3859
|
+
expanded ? /* @__PURE__ */ jsx("ul", { className: "ra-history-rows", "aria-label": "Archived records", children: history.map((h, hIdx) => {
|
|
3860
|
+
const hCtx = buildCtx(h);
|
|
3861
|
+
const hKey = h.id ?? `hist:${idx}:${hIdx}`;
|
|
3862
|
+
const badged = {
|
|
3863
|
+
...h,
|
|
3864
|
+
badges: [
|
|
3865
|
+
...h.badges ?? [],
|
|
3866
|
+
{ label: h.lifecycleStatus ?? "archived", tone: "warning" }
|
|
3867
|
+
]
|
|
3868
|
+
};
|
|
3869
|
+
return /* @__PURE__ */ jsx("li", { className: "ra-history-row", "data-history": "true", children: renderListRow ? renderListRow(badged, hCtx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: badged, ctx: hCtx, compact }) }, hKey);
|
|
3870
|
+
}) }) : null,
|
|
3871
|
+
/* @__PURE__ */ jsx(
|
|
3872
|
+
"button",
|
|
3873
|
+
{
|
|
3874
|
+
type: "button",
|
|
3875
|
+
className: "ra-history-disclosure",
|
|
3876
|
+
onClick: () => sKey && toggleSlot(sKey),
|
|
3877
|
+
"aria-expanded": expanded,
|
|
3878
|
+
children: label
|
|
3879
|
+
}
|
|
3880
|
+
)
|
|
3881
|
+
] }) : null
|
|
3882
|
+
] }, key);
|
|
3721
3883
|
}) });
|
|
3722
3884
|
};
|
|
3723
3885
|
if (groups) {
|
|
@@ -3797,6 +3959,306 @@ var ProductList = RecordList;
|
|
|
3797
3959
|
var FacetList = RecordList;
|
|
3798
3960
|
var VariantList = RecordList;
|
|
3799
3961
|
var BatchList = RecordList;
|
|
3962
|
+
var LifecycleStatusControl = ({
|
|
3963
|
+
SL,
|
|
3964
|
+
collectionId,
|
|
3965
|
+
appId,
|
|
3966
|
+
recordId,
|
|
3967
|
+
current,
|
|
3968
|
+
options,
|
|
3969
|
+
i18n,
|
|
3970
|
+
onChanged
|
|
3971
|
+
}) => {
|
|
3972
|
+
const [busy, setBusy] = useState(false);
|
|
3973
|
+
const value = current ?? "active";
|
|
3974
|
+
const opts = options ?? [
|
|
3975
|
+
{ value: "active", label: i18n.lifecycleStatusActive },
|
|
3976
|
+
{ value: "archived", label: i18n.lifecycleStatusArchived },
|
|
3977
|
+
{ value: "draft", label: i18n.lifecycleStatusDraft }
|
|
3978
|
+
];
|
|
3979
|
+
const onChange = useCallback(async (e) => {
|
|
3980
|
+
const next = e.target.value;
|
|
3981
|
+
if (next === value) return;
|
|
3982
|
+
setBusy(true);
|
|
3983
|
+
try {
|
|
3984
|
+
await SL.app.records.update(collectionId, appId, recordId, { status: next }, true);
|
|
3985
|
+
onChanged?.(next);
|
|
3986
|
+
} catch (err) {
|
|
3987
|
+
console.warn("[LifecycleStatusControl] update failed", err);
|
|
3988
|
+
} finally {
|
|
3989
|
+
setBusy(false);
|
|
3990
|
+
}
|
|
3991
|
+
}, [SL, collectionId, appId, recordId, value, onChanged]);
|
|
3992
|
+
return /* @__PURE__ */ jsxs(
|
|
3993
|
+
"label",
|
|
3994
|
+
{
|
|
3995
|
+
className: "inline-flex items-center gap-1.5 text-[11px] mt-0.5",
|
|
3996
|
+
title: i18n.lifecycleStatusHint,
|
|
3997
|
+
style: { color: "hsl(var(--ra-muted-text))" },
|
|
3998
|
+
children: [
|
|
3999
|
+
/* @__PURE__ */ jsx("span", { className: "uppercase tracking-wide", children: i18n.lifecycleStatusLabel }),
|
|
4000
|
+
/* @__PURE__ */ jsx(
|
|
4001
|
+
"select",
|
|
4002
|
+
{
|
|
4003
|
+
value,
|
|
4004
|
+
onChange,
|
|
4005
|
+
disabled: busy,
|
|
4006
|
+
className: "text-xs px-1.5 py-0.5 rounded border bg-transparent",
|
|
4007
|
+
style: {
|
|
4008
|
+
borderColor: "hsl(var(--ra-border))",
|
|
4009
|
+
color: "hsl(var(--ra-text))",
|
|
4010
|
+
background: "hsl(var(--ra-surface))"
|
|
4011
|
+
},
|
|
4012
|
+
children: opts.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
|
|
4013
|
+
}
|
|
4014
|
+
)
|
|
4015
|
+
]
|
|
4016
|
+
}
|
|
4017
|
+
);
|
|
4018
|
+
};
|
|
4019
|
+
|
|
4020
|
+
// src/components/RecordsAdmin/data/lifecycleStatuses.ts
|
|
4021
|
+
var DEFAULT_LIFECYCLE_STATUSES = [
|
|
4022
|
+
{ value: "draft", label: "Draft", tone: "warning" },
|
|
4023
|
+
{ value: "active", label: "Active", tone: "success", isActive: true },
|
|
4024
|
+
{ value: "archived", label: "Archived", tone: "muted" }
|
|
4025
|
+
];
|
|
4026
|
+
var getLifecycleStatuses = (config) => {
|
|
4027
|
+
if (config?.statuses && config.statuses.length > 0) return config.statuses;
|
|
4028
|
+
return DEFAULT_LIFECYCLE_STATUSES;
|
|
4029
|
+
};
|
|
4030
|
+
var getActiveStatusValues = (config, legacyActiveStatuses) => {
|
|
4031
|
+
const fromDefs = getLifecycleStatuses(config).filter((s) => s.isActive).map((s) => s.value);
|
|
4032
|
+
if (legacyActiveStatuses && legacyActiveStatuses.length > 0) {
|
|
4033
|
+
const set = /* @__PURE__ */ new Set([...fromDefs, ...legacyActiveStatuses]);
|
|
4034
|
+
return Array.from(set);
|
|
4035
|
+
}
|
|
4036
|
+
return fromDefs.length > 0 ? fromDefs : ["active"];
|
|
4037
|
+
};
|
|
4038
|
+
var UNKNOWN_DEF = (value) => ({
|
|
4039
|
+
value,
|
|
4040
|
+
label: value,
|
|
4041
|
+
tone: "default"
|
|
4042
|
+
});
|
|
4043
|
+
var resolveLifecycleStatus = (record, config) => {
|
|
4044
|
+
const defs = getLifecycleStatuses(config);
|
|
4045
|
+
const raw = record.lifecycleStatus;
|
|
4046
|
+
if (raw == null || raw === "") {
|
|
4047
|
+
return defs.find((d) => d.value === "active") ?? defs.find((d) => d.isActive) ?? defs[0];
|
|
4048
|
+
}
|
|
4049
|
+
return defs.find((d) => d.value === raw) ?? UNKNOWN_DEF(raw);
|
|
4050
|
+
};
|
|
4051
|
+
var hasMixedLifecycle = (records, activeValues) => {
|
|
4052
|
+
for (const r of records) {
|
|
4053
|
+
const s = r.lifecycleStatus;
|
|
4054
|
+
if (s == null || s === "") continue;
|
|
4055
|
+
if (!activeValues.includes(s)) return true;
|
|
4056
|
+
}
|
|
4057
|
+
return false;
|
|
4058
|
+
};
|
|
4059
|
+
var LIFECYCLE_BUCKET_ORDER = ["draft", "active", "archived"];
|
|
4060
|
+
var compareLifecycleBuckets = (a, b) => {
|
|
4061
|
+
const ai = LIFECYCLE_BUCKET_ORDER.indexOf(a);
|
|
4062
|
+
const bi = LIFECYCLE_BUCKET_ORDER.indexOf(b);
|
|
4063
|
+
if (ai === -1 && bi === -1) return a.localeCompare(b);
|
|
4064
|
+
if (ai === -1) return 1;
|
|
4065
|
+
if (bi === -1) return -1;
|
|
4066
|
+
return ai - bi;
|
|
4067
|
+
};
|
|
4068
|
+
var toneColor = (tone) => {
|
|
4069
|
+
switch (tone) {
|
|
4070
|
+
case "success":
|
|
4071
|
+
return "hsl(var(--ra-success, 142 70% 40%))";
|
|
4072
|
+
case "warning":
|
|
4073
|
+
return "hsl(var(--ra-warning, 38 92% 50%))";
|
|
4074
|
+
case "danger":
|
|
4075
|
+
return "hsl(var(--ra-danger, 0 70% 45%))";
|
|
4076
|
+
case "muted":
|
|
4077
|
+
return "hsl(var(--ra-muted-text))";
|
|
4078
|
+
default:
|
|
4079
|
+
return "hsl(var(--ra-text))";
|
|
4080
|
+
}
|
|
4081
|
+
};
|
|
4082
|
+
var ToneDot = ({ tone, className }) => /* @__PURE__ */ jsx(
|
|
4083
|
+
"span",
|
|
4084
|
+
{
|
|
4085
|
+
"aria-hidden": "true",
|
|
4086
|
+
className: className ?? "inline-block w-2 h-2 rounded-full shrink-0",
|
|
4087
|
+
style: { background: toneColor(tone) }
|
|
4088
|
+
}
|
|
4089
|
+
);
|
|
4090
|
+
var LifecycleStatusMenu = ({
|
|
4091
|
+
SL,
|
|
4092
|
+
collectionId,
|
|
4093
|
+
appId,
|
|
4094
|
+
recordId,
|
|
4095
|
+
scope,
|
|
4096
|
+
current,
|
|
4097
|
+
statuses,
|
|
4098
|
+
i18n,
|
|
4099
|
+
beforeChange,
|
|
4100
|
+
onChanged,
|
|
4101
|
+
onTelemetry,
|
|
4102
|
+
size = "sm"
|
|
4103
|
+
}) => {
|
|
4104
|
+
const [open, setOpen] = useState(false);
|
|
4105
|
+
const [busy, setBusy] = useState(null);
|
|
4106
|
+
const wrapperRef = useRef(null);
|
|
4107
|
+
const triggerRef = useRef(null);
|
|
4108
|
+
const menuRef = useRef(null);
|
|
4109
|
+
const [pos, setPos] = useState(null);
|
|
4110
|
+
const currentDef = resolveLifecycleStatus({ lifecycleStatus: current }, { statuses });
|
|
4111
|
+
useEffect(() => {
|
|
4112
|
+
if (!open) return;
|
|
4113
|
+
const onDoc = (e) => {
|
|
4114
|
+
const t = e.target;
|
|
4115
|
+
if (wrapperRef.current?.contains(t)) return;
|
|
4116
|
+
if (menuRef.current?.contains(t)) return;
|
|
4117
|
+
setOpen(false);
|
|
4118
|
+
};
|
|
4119
|
+
const onKey = (e) => {
|
|
4120
|
+
if (e.key === "Escape") setOpen(false);
|
|
4121
|
+
};
|
|
4122
|
+
document.addEventListener("mousedown", onDoc);
|
|
4123
|
+
document.addEventListener("keydown", onKey);
|
|
4124
|
+
return () => {
|
|
4125
|
+
document.removeEventListener("mousedown", onDoc);
|
|
4126
|
+
document.removeEventListener("keydown", onKey);
|
|
4127
|
+
};
|
|
4128
|
+
}, [open]);
|
|
4129
|
+
useLayoutEffect(() => {
|
|
4130
|
+
if (!open) {
|
|
4131
|
+
setPos(null);
|
|
4132
|
+
return;
|
|
4133
|
+
}
|
|
4134
|
+
const update = () => {
|
|
4135
|
+
const el = triggerRef.current;
|
|
4136
|
+
if (!el) return;
|
|
4137
|
+
const r = el.getBoundingClientRect();
|
|
4138
|
+
const menuHeight = menuRef.current?.offsetHeight ?? 36 * statuses.length + 16;
|
|
4139
|
+
const menuWidth = Math.max(r.width, 180);
|
|
4140
|
+
const margin = 8;
|
|
4141
|
+
const fitsAbove = r.top - menuHeight - margin >= 0;
|
|
4142
|
+
const top = fitsAbove ? r.top - menuHeight - 4 : r.bottom + 4;
|
|
4143
|
+
const left = Math.max(margin, Math.min(window.innerWidth - menuWidth - margin, r.left));
|
|
4144
|
+
setPos({ top, left, width: menuWidth });
|
|
4145
|
+
};
|
|
4146
|
+
update();
|
|
4147
|
+
window.addEventListener("resize", update);
|
|
4148
|
+
window.addEventListener("scroll", update, true);
|
|
4149
|
+
return () => {
|
|
4150
|
+
window.removeEventListener("resize", update);
|
|
4151
|
+
window.removeEventListener("scroll", update, true);
|
|
4152
|
+
};
|
|
4153
|
+
}, [open, statuses.length]);
|
|
4154
|
+
const choose = useCallback(async (next) => {
|
|
4155
|
+
if (busy) return;
|
|
4156
|
+
if (next.value === (current ?? currentDef.value)) {
|
|
4157
|
+
setOpen(false);
|
|
4158
|
+
return;
|
|
4159
|
+
}
|
|
4160
|
+
if (beforeChange) {
|
|
4161
|
+
try {
|
|
4162
|
+
const ok = await beforeChange({
|
|
4163
|
+
recordId,
|
|
4164
|
+
scope,
|
|
4165
|
+
from: current,
|
|
4166
|
+
to: next.value
|
|
4167
|
+
});
|
|
4168
|
+
if (ok === false) {
|
|
4169
|
+
setOpen(false);
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
} catch {
|
|
4173
|
+
setOpen(false);
|
|
4174
|
+
return;
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
setBusy(next.value);
|
|
4178
|
+
try {
|
|
4179
|
+
await SL.app.records.update(collectionId, appId, recordId, { status: next.value }, true);
|
|
4180
|
+
onTelemetry?.({ from: current, to: next.value });
|
|
4181
|
+
onChanged?.(next.value);
|
|
4182
|
+
} catch (err) {
|
|
4183
|
+
console.warn("[LifecycleStatusMenu] update failed", err);
|
|
4184
|
+
} finally {
|
|
4185
|
+
setBusy(null);
|
|
4186
|
+
setOpen(false);
|
|
4187
|
+
}
|
|
4188
|
+
}, [busy, current, currentDef.value, beforeChange, recordId, scope, SL, collectionId, appId, onChanged, onTelemetry]);
|
|
4189
|
+
const padding = size === "xs" ? "px-2 py-1" : "px-3 py-1.5";
|
|
4190
|
+
const fontSize = size === "xs" ? "0.7rem" : "0.75rem";
|
|
4191
|
+
return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: "relative inline-flex", children: [
|
|
4192
|
+
/* @__PURE__ */ jsxs(
|
|
4193
|
+
"button",
|
|
4194
|
+
{
|
|
4195
|
+
ref: triggerRef,
|
|
4196
|
+
type: "button",
|
|
4197
|
+
className: `${padding} rounded-md border transition-colors hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1.5`,
|
|
4198
|
+
"aria-haspopup": "menu",
|
|
4199
|
+
"aria-expanded": open,
|
|
4200
|
+
"aria-label": i18n.lifecycleMenuLabel ?? i18n.lifecycleStatusLabel,
|
|
4201
|
+
title: i18n.lifecycleStatusHint,
|
|
4202
|
+
disabled: !!busy,
|
|
4203
|
+
style: {
|
|
4204
|
+
borderColor: "hsl(var(--ra-border))",
|
|
4205
|
+
color: "hsl(var(--ra-text))",
|
|
4206
|
+
background: "hsl(var(--ra-surface))",
|
|
4207
|
+
fontSize
|
|
4208
|
+
},
|
|
4209
|
+
onClick: (e) => {
|
|
4210
|
+
e.stopPropagation();
|
|
4211
|
+
setOpen((v) => !v);
|
|
4212
|
+
},
|
|
4213
|
+
children: [
|
|
4214
|
+
/* @__PURE__ */ jsx(ToneDot, { tone: currentDef.tone }),
|
|
4215
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: currentDef.label }),
|
|
4216
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3 opacity-60", "aria-hidden": "true" })
|
|
4217
|
+
]
|
|
4218
|
+
}
|
|
4219
|
+
),
|
|
4220
|
+
open && typeof document !== "undefined" && createPortal(
|
|
4221
|
+
/* @__PURE__ */ jsx(
|
|
4222
|
+
"div",
|
|
4223
|
+
{
|
|
4224
|
+
ref: menuRef,
|
|
4225
|
+
role: "menu",
|
|
4226
|
+
className: "ra-row-menu ra-row-menu-portal",
|
|
4227
|
+
style: pos ? { top: pos.top, left: pos.left, minWidth: pos.width } : { visibility: "hidden" },
|
|
4228
|
+
onClick: (e) => e.stopPropagation(),
|
|
4229
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
4230
|
+
children: statuses.map((s) => {
|
|
4231
|
+
const isCurrent = s.value === (current ?? currentDef.value);
|
|
4232
|
+
const Icon = s.icon;
|
|
4233
|
+
return /* @__PURE__ */ jsxs(
|
|
4234
|
+
"button",
|
|
4235
|
+
{
|
|
4236
|
+
type: "button",
|
|
4237
|
+
role: "menuitemradio",
|
|
4238
|
+
"aria-checked": isCurrent,
|
|
4239
|
+
disabled: busy !== null,
|
|
4240
|
+
className: "ra-row-menu-item",
|
|
4241
|
+
onClick: (e) => {
|
|
4242
|
+
e.stopPropagation();
|
|
4243
|
+
void choose(s);
|
|
4244
|
+
},
|
|
4245
|
+
children: [
|
|
4246
|
+
/* @__PURE__ */ jsx(ToneDot, { tone: s.tone }),
|
|
4247
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5 opacity-70" }),
|
|
4248
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: s.label }),
|
|
4249
|
+
isCurrent && /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5 opacity-80", "aria-hidden": "true" }),
|
|
4250
|
+
busy === s.value && /* @__PURE__ */ jsx("span", { className: "opacity-60", children: "\u2026" })
|
|
4251
|
+
]
|
|
4252
|
+
},
|
|
4253
|
+
s.value
|
|
4254
|
+
);
|
|
4255
|
+
})
|
|
4256
|
+
}
|
|
4257
|
+
),
|
|
4258
|
+
document.body
|
|
4259
|
+
)
|
|
4260
|
+
] });
|
|
4261
|
+
};
|
|
3800
4262
|
function LoadMoreFooter({
|
|
3801
4263
|
shown,
|
|
3802
4264
|
total,
|
|
@@ -4514,6 +4976,8 @@ function RecordEditor({
|
|
|
4514
4976
|
preview,
|
|
4515
4977
|
targeting,
|
|
4516
4978
|
targetingControl,
|
|
4979
|
+
lifecycleControl,
|
|
4980
|
+
lifecycleControlFooter,
|
|
4517
4981
|
bulkActions,
|
|
4518
4982
|
footerExtra,
|
|
4519
4983
|
onBeforeDelete,
|
|
@@ -4521,6 +4985,7 @@ function RecordEditor({
|
|
|
4521
4985
|
headerSubtitle,
|
|
4522
4986
|
headerMeta,
|
|
4523
4987
|
headerLeading,
|
|
4988
|
+
headerNotice,
|
|
4524
4989
|
clipboard,
|
|
4525
4990
|
actionLabels,
|
|
4526
4991
|
actionIcons
|
|
@@ -4538,7 +5003,7 @@ function RecordEditor({
|
|
|
4538
5003
|
return Boolean(s?.facetId || s?.productId || s?.variantId || s?.batchId);
|
|
4539
5004
|
})();
|
|
4540
5005
|
const hasLeftContent = Boolean(headerLabel) || hasBreadcrumb || Boolean(headerLeading);
|
|
4541
|
-
const hasRightContent = showInherited || showEmpty || Boolean(headerMeta) || Boolean(bulkActions) || Boolean(targetingControl);
|
|
5006
|
+
const hasRightContent = showInherited || showEmpty || Boolean(headerMeta) || Boolean(bulkActions) || Boolean(targetingControl) || Boolean(lifecycleControl);
|
|
4542
5007
|
const showHeader = hasLeftContent || hasRightContent;
|
|
4543
5008
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
4544
5009
|
showHeader && /* @__PURE__ */ jsxs(
|
|
@@ -4617,12 +5082,14 @@ function RecordEditor({
|
|
|
4617
5082
|
}
|
|
4618
5083
|
),
|
|
4619
5084
|
bulkActions && /* @__PURE__ */ jsx(BulkActionsMenu, { ...bulkActions, i18n }),
|
|
5085
|
+
lifecycleControl,
|
|
4620
5086
|
targetingControl
|
|
4621
5087
|
] })
|
|
4622
5088
|
]
|
|
4623
5089
|
}
|
|
4624
5090
|
),
|
|
4625
5091
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-4", children: [
|
|
5092
|
+
headerNotice,
|
|
4626
5093
|
targeting,
|
|
4627
5094
|
children,
|
|
4628
5095
|
preview
|
|
@@ -4658,6 +5125,7 @@ function RecordEditor({
|
|
|
4658
5125
|
icon: DeleteIcon
|
|
4659
5126
|
}
|
|
4660
5127
|
),
|
|
5128
|
+
lifecycleControlFooter,
|
|
4661
5129
|
clipboard && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4662
5130
|
/* @__PURE__ */ jsxs(
|
|
4663
5131
|
"button",
|
|
@@ -5186,21 +5654,21 @@ var ProductDrillDown = ({
|
|
|
5186
5654
|
children: tabs.map((t) => {
|
|
5187
5655
|
const meta = TAB_META[t];
|
|
5188
5656
|
const Icon = meta.icon;
|
|
5189
|
-
const
|
|
5657
|
+
const isActive2 = active === t;
|
|
5190
5658
|
const label = t === "product" ? productLabel : meta.label;
|
|
5191
5659
|
return /* @__PURE__ */ jsxs(
|
|
5192
5660
|
"button",
|
|
5193
5661
|
{
|
|
5194
5662
|
type: "button",
|
|
5195
5663
|
role: "tab",
|
|
5196
|
-
"aria-selected":
|
|
5664
|
+
"aria-selected": isActive2,
|
|
5197
5665
|
onClick: () => onChange(t),
|
|
5198
5666
|
className: cn(
|
|
5199
5667
|
"flex items-center gap-1.5 px-3 py-2 text-xs border-b-2 -mb-px transition-colors",
|
|
5200
|
-
|
|
5668
|
+
isActive2 ? "font-medium" : "opacity-60 hover:opacity-100"
|
|
5201
5669
|
),
|
|
5202
5670
|
style: {
|
|
5203
|
-
borderColor:
|
|
5671
|
+
borderColor: isActive2 ? "hsl(var(--ra-accent))" : "transparent",
|
|
5204
5672
|
color: "hsl(var(--ra-text))"
|
|
5205
5673
|
},
|
|
5206
5674
|
children: [
|
|
@@ -5225,7 +5693,7 @@ var ProductDrillDown = ({
|
|
|
5225
5693
|
childLoading && /* @__PURE__ */ jsx("div", { className: "p-3 space-y-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { className: "h-9 rounded animate-pulse", style: { background: "hsl(var(--ra-muted))" } }, i)) }),
|
|
5226
5694
|
!childLoading && childList.length === 0 && /* @__PURE__ */ jsx("div", { className: "p-4 text-xs", style: { color: "hsl(var(--ra-muted-text))" }, children: childEmptyLabel }),
|
|
5227
5695
|
!childLoading && childList.length > 0 && /* @__PURE__ */ jsx("ul", { className: "divide-y", style: { borderColor: "hsl(var(--ra-border))" }, children: childList.map((c) => {
|
|
5228
|
-
const
|
|
5696
|
+
const isActive2 = c.id === selectedChildId;
|
|
5229
5697
|
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
5230
5698
|
"button",
|
|
5231
5699
|
{
|
|
@@ -5233,7 +5701,7 @@ var ProductDrillDown = ({
|
|
|
5233
5701
|
onClick: () => onSelectChild(c.id),
|
|
5234
5702
|
className: cn(
|
|
5235
5703
|
"w-full text-left px-3 py-2 transition-colors hover:bg-[hsl(var(--ra-muted))]",
|
|
5236
|
-
|
|
5704
|
+
isActive2 && "ra-row-active"
|
|
5237
5705
|
),
|
|
5238
5706
|
children: [
|
|
5239
5707
|
/* @__PURE__ */ jsx("div", { className: "text-sm truncate", style: { color: "hsl(var(--ra-text))" }, children: c.name }),
|
|
@@ -5344,21 +5812,21 @@ var TabbedPreview = ({
|
|
|
5344
5812
|
style: { borderColor: "hsl(var(--ra-border))" },
|
|
5345
5813
|
children: [
|
|
5346
5814
|
["editor", "preview"].map((t) => {
|
|
5347
|
-
const
|
|
5815
|
+
const isActive2 = tab === t;
|
|
5348
5816
|
const lbl = t === "editor" ? i18n?.editor ?? "Editor" : i18n?.preview ?? "Preview";
|
|
5349
5817
|
return /* @__PURE__ */ jsx(
|
|
5350
5818
|
"button",
|
|
5351
5819
|
{
|
|
5352
5820
|
type: "button",
|
|
5353
5821
|
role: "tab",
|
|
5354
|
-
"aria-selected":
|
|
5822
|
+
"aria-selected": isActive2,
|
|
5355
5823
|
onClick: () => setTab(t),
|
|
5356
5824
|
className: cn(
|
|
5357
5825
|
"px-3 py-2 text-xs border-b-2 -mb-px transition-colors",
|
|
5358
|
-
|
|
5826
|
+
isActive2 ? "font-medium" : "opacity-60 hover:opacity-100"
|
|
5359
5827
|
),
|
|
5360
5828
|
style: {
|
|
5361
|
-
borderColor:
|
|
5829
|
+
borderColor: isActive2 ? "hsl(var(--ra-accent))" : "transparent",
|
|
5362
5830
|
color: "hsl(var(--ra-text))"
|
|
5363
5831
|
},
|
|
5364
5832
|
children: lbl
|
|
@@ -7096,6 +7564,117 @@ var RuleGroupEditDialog = ({
|
|
|
7096
7564
|
document.body
|
|
7097
7565
|
);
|
|
7098
7566
|
};
|
|
7567
|
+
var fmt = (s, vars) => s.replace(/\{(\w+)\}/g, (_, k) => String(vars[k] ?? ""));
|
|
7568
|
+
function SingletonConflictBanner({
|
|
7569
|
+
conflictCount,
|
|
7570
|
+
duplicateCount,
|
|
7571
|
+
onResolve,
|
|
7572
|
+
onArchiveDuplicates,
|
|
7573
|
+
onDeleteDuplicates,
|
|
7574
|
+
i18n
|
|
7575
|
+
}) {
|
|
7576
|
+
const [busy, setBusy] = useState(null);
|
|
7577
|
+
const message = conflictCount === 1 ? fmt(i18n.bodyOne, { n: duplicateCount }) : fmt(i18n.bodyMany, { slots: conflictCount, dups: duplicateCount });
|
|
7578
|
+
const showActions = !!(onArchiveDuplicates || onDeleteDuplicates);
|
|
7579
|
+
const runArchive = async () => {
|
|
7580
|
+
if (!onArchiveDuplicates) return;
|
|
7581
|
+
setBusy("archive");
|
|
7582
|
+
try {
|
|
7583
|
+
await onArchiveDuplicates();
|
|
7584
|
+
} finally {
|
|
7585
|
+
setBusy(null);
|
|
7586
|
+
}
|
|
7587
|
+
};
|
|
7588
|
+
const runDelete = async () => {
|
|
7589
|
+
if (!onDeleteDuplicates) return;
|
|
7590
|
+
if (typeof window !== "undefined" && !window.confirm(fmt(i18n.deleteConfirm, { n: duplicateCount }))) {
|
|
7591
|
+
return;
|
|
7592
|
+
}
|
|
7593
|
+
setBusy("delete");
|
|
7594
|
+
try {
|
|
7595
|
+
await onDeleteDuplicates();
|
|
7596
|
+
} finally {
|
|
7597
|
+
setBusy(null);
|
|
7598
|
+
}
|
|
7599
|
+
};
|
|
7600
|
+
return /* @__PURE__ */ jsxs(
|
|
7601
|
+
"div",
|
|
7602
|
+
{
|
|
7603
|
+
role: "alert",
|
|
7604
|
+
className: "px-3 py-2 border-b text-xs flex items-start gap-2 flex-wrap",
|
|
7605
|
+
style: {
|
|
7606
|
+
background: "hsl(var(--ra-danger, 0 70% 45%) / 0.08)",
|
|
7607
|
+
borderColor: "hsl(var(--ra-danger, 0 70% 45%) / 0.35)",
|
|
7608
|
+
color: "hsl(var(--ra-text))"
|
|
7609
|
+
},
|
|
7610
|
+
children: [
|
|
7611
|
+
/* @__PURE__ */ jsx(
|
|
7612
|
+
AlertTriangle,
|
|
7613
|
+
{
|
|
7614
|
+
"aria-hidden": "true",
|
|
7615
|
+
className: "w-3.5 h-3.5 shrink-0 mt-0.5",
|
|
7616
|
+
style: { color: "hsl(var(--ra-danger, 0 70% 45%))" }
|
|
7617
|
+
}
|
|
7618
|
+
),
|
|
7619
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-[10rem]", children: [
|
|
7620
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium leading-tight", children: i18n.title }),
|
|
7621
|
+
/* @__PURE__ */ jsx("div", { className: "leading-tight", style: { color: "hsl(var(--ra-muted-text))" }, children: message })
|
|
7622
|
+
] }),
|
|
7623
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
|
|
7624
|
+
onArchiveDuplicates && /* @__PURE__ */ jsxs(
|
|
7625
|
+
"button",
|
|
7626
|
+
{
|
|
7627
|
+
type: "button",
|
|
7628
|
+
onClick: runArchive,
|
|
7629
|
+
disabled: busy !== null,
|
|
7630
|
+
className: "ra-btn",
|
|
7631
|
+
"data-variant": "ghost",
|
|
7632
|
+
style: { fontSize: "0.7rem", padding: "0.2rem 0.5rem", display: "inline-flex", alignItems: "center", gap: "0.25rem" },
|
|
7633
|
+
children: [
|
|
7634
|
+
/* @__PURE__ */ jsx(Archive, { className: "w-3 h-3", "aria-hidden": "true" }),
|
|
7635
|
+
busy === "archive" ? "\u2026" : i18n.archiveLabel
|
|
7636
|
+
]
|
|
7637
|
+
}
|
|
7638
|
+
),
|
|
7639
|
+
onDeleteDuplicates && /* @__PURE__ */ jsxs(
|
|
7640
|
+
"button",
|
|
7641
|
+
{
|
|
7642
|
+
type: "button",
|
|
7643
|
+
onClick: runDelete,
|
|
7644
|
+
disabled: busy !== null,
|
|
7645
|
+
className: "ra-btn",
|
|
7646
|
+
"data-variant": "ghost",
|
|
7647
|
+
style: {
|
|
7648
|
+
fontSize: "0.7rem",
|
|
7649
|
+
padding: "0.2rem 0.5rem",
|
|
7650
|
+
display: "inline-flex",
|
|
7651
|
+
alignItems: "center",
|
|
7652
|
+
gap: "0.25rem",
|
|
7653
|
+
color: "hsl(var(--ra-danger, 0 70% 45%))",
|
|
7654
|
+
borderColor: "hsl(var(--ra-danger, 0 70% 45%) / 0.4)"
|
|
7655
|
+
},
|
|
7656
|
+
children: [
|
|
7657
|
+
/* @__PURE__ */ jsx(Trash2, { className: "w-3 h-3", "aria-hidden": "true" }),
|
|
7658
|
+
busy === "delete" ? "\u2026" : i18n.deleteLabel
|
|
7659
|
+
]
|
|
7660
|
+
}
|
|
7661
|
+
),
|
|
7662
|
+
!showActions && onResolve && /* @__PURE__ */ jsx(
|
|
7663
|
+
"button",
|
|
7664
|
+
{
|
|
7665
|
+
type: "button",
|
|
7666
|
+
onClick: onResolve,
|
|
7667
|
+
className: "ra-btn",
|
|
7668
|
+
"data-variant": "ghost",
|
|
7669
|
+
style: { fontSize: "0.7rem", padding: "0.2rem 0.5rem" },
|
|
7670
|
+
children: i18n.resolveLabel
|
|
7671
|
+
}
|
|
7672
|
+
)
|
|
7673
|
+
] })
|
|
7674
|
+
]
|
|
7675
|
+
}
|
|
7676
|
+
);
|
|
7677
|
+
}
|
|
7099
7678
|
var statusDot = (status) => {
|
|
7100
7679
|
switch (status) {
|
|
7101
7680
|
case "saving":
|
|
@@ -7540,20 +8119,20 @@ var EditorMountPool = ({
|
|
|
7540
8119
|
}
|
|
7541
8120
|
const visibleIds = keepMountedHidden ? ids : currentEditorId ? [currentEditorId] : [];
|
|
7542
8121
|
return /* @__PURE__ */ jsx("div", { className, style: { display: "contents" }, children: visibleIds.map((id) => {
|
|
7543
|
-
const
|
|
7544
|
-
return /* @__PURE__ */ jsx(EditorPoolSlot, { editorId: id, isActive, children: renderSlot(id) }, id);
|
|
8122
|
+
const isActive2 = id === currentEditorId;
|
|
8123
|
+
return /* @__PURE__ */ jsx(EditorPoolSlot, { editorId: id, isActive: isActive2, children: renderSlot(id) }, id);
|
|
7545
8124
|
}) });
|
|
7546
8125
|
};
|
|
7547
|
-
var EditorPoolSlot = ({ editorId, isActive, children }) => {
|
|
7548
|
-
const inertProps =
|
|
8126
|
+
var EditorPoolSlot = ({ editorId, isActive: isActive2, children }) => {
|
|
8127
|
+
const inertProps = isActive2 ? {} : { inert: "" };
|
|
7549
8128
|
return /* @__PURE__ */ jsx(
|
|
7550
8129
|
"div",
|
|
7551
8130
|
{
|
|
7552
8131
|
"data-editor-slot": editorId,
|
|
7553
|
-
"data-active":
|
|
7554
|
-
"aria-hidden":
|
|
8132
|
+
"data-active": isActive2 ? "true" : "false",
|
|
8133
|
+
"aria-hidden": isActive2 ? void 0 : true,
|
|
7555
8134
|
style: {
|
|
7556
|
-
display:
|
|
8135
|
+
display: isActive2 ? "contents" : "none"
|
|
7557
8136
|
},
|
|
7558
8137
|
...inertProps,
|
|
7559
8138
|
children
|
|
@@ -7827,8 +8406,28 @@ function RecordsAdminShellInner(props) {
|
|
|
7827
8406
|
icons: iconsOverride,
|
|
7828
8407
|
// Deep linking
|
|
7829
8408
|
deepLink,
|
|
7830
|
-
recordChangeRef
|
|
8409
|
+
recordChangeRef,
|
|
8410
|
+
activeStatuses,
|
|
8411
|
+
conflicts: conflictsConfig,
|
|
8412
|
+
lifecycle: lifecycleConfig
|
|
7831
8413
|
} = props;
|
|
8414
|
+
const lifecycleStatuses = useMemo(
|
|
8415
|
+
() => getLifecycleStatuses(lifecycleConfig),
|
|
8416
|
+
[lifecycleConfig]
|
|
8417
|
+
);
|
|
8418
|
+
const lifecycleSurface = lifecycleConfig?.surface ?? "footer";
|
|
8419
|
+
const lifecycleAutoGroup = lifecycleConfig?.autoGroup ?? true;
|
|
8420
|
+
const lifecycleDefaultStatus = lifecycleConfig?.defaultStatus;
|
|
8421
|
+
const lifecycleBeforeChange = lifecycleConfig?.beforeChange;
|
|
8422
|
+
const resolvedActiveStatuses = useMemo(
|
|
8423
|
+
() => getActiveStatusValues(lifecycleConfig, activeStatuses),
|
|
8424
|
+
[lifecycleConfig, activeStatuses]
|
|
8425
|
+
);
|
|
8426
|
+
const {
|
|
8427
|
+
archiveDuplicates: enableArchiveDuplicates = true,
|
|
8428
|
+
deleteDuplicates: enableDeleteDuplicates = true,
|
|
8429
|
+
archivedStatus: archivedStatusValue = "archived"
|
|
8430
|
+
} = conflictsConfig ?? {};
|
|
7832
8431
|
const {
|
|
7833
8432
|
show: showHeader,
|
|
7834
8433
|
title,
|
|
@@ -8047,7 +8646,8 @@ function RecordsAdminShellInner(props) {
|
|
|
8047
8646
|
selectedProductId,
|
|
8048
8647
|
drillTab,
|
|
8049
8648
|
classify: classify3,
|
|
8050
|
-
pageSize: railPageSize
|
|
8649
|
+
pageSize: railPageSize,
|
|
8650
|
+
activeStatuses
|
|
8051
8651
|
});
|
|
8052
8652
|
const {
|
|
8053
8653
|
search,
|
|
@@ -8066,12 +8666,14 @@ function RecordsAdminShellInner(props) {
|
|
|
8066
8666
|
const ruleScopedList = useRecordList({
|
|
8067
8667
|
ctx,
|
|
8068
8668
|
scopeKind: "rule",
|
|
8069
|
-
enabled: true
|
|
8669
|
+
enabled: true,
|
|
8670
|
+
activeStatuses
|
|
8070
8671
|
});
|
|
8071
8672
|
const globalScopedList = useRecordList({
|
|
8072
8673
|
ctx,
|
|
8073
8674
|
scopeKind: "collection",
|
|
8074
|
-
enabled: cardinality === "singleton"
|
|
8675
|
+
enabled: cardinality === "singleton",
|
|
8676
|
+
activeStatuses
|
|
8075
8677
|
});
|
|
8076
8678
|
const pinnedProduct = useSingleProduct({
|
|
8077
8679
|
SL,
|
|
@@ -8083,6 +8685,32 @@ function RecordsAdminShellInner(props) {
|
|
|
8083
8685
|
if (pinnedProduct.item) return [pinnedProduct.item];
|
|
8084
8686
|
return productBrowse.items;
|
|
8085
8687
|
}, [pinnedProduct.item, productBrowse.items]);
|
|
8688
|
+
const singletonConflicts = useMemo(
|
|
8689
|
+
() => {
|
|
8690
|
+
if (cardinality !== "singleton") return [];
|
|
8691
|
+
if (recordList.isLoading) return [];
|
|
8692
|
+
if (recordList.isFetchingNextPage || recordList.hasNextPage) return [];
|
|
8693
|
+
return groupSingletonConflicts(recordList.items, activeStatuses);
|
|
8694
|
+
},
|
|
8695
|
+
[
|
|
8696
|
+
cardinality,
|
|
8697
|
+
recordList.items,
|
|
8698
|
+
recordList.isLoading,
|
|
8699
|
+
recordList.isFetchingNextPage,
|
|
8700
|
+
recordList.hasNextPage,
|
|
8701
|
+
activeStatuses
|
|
8702
|
+
]
|
|
8703
|
+
);
|
|
8704
|
+
const hasSingletonConflicts = singletonConflicts.length > 0;
|
|
8705
|
+
const totalDuplicateCount = useMemo(
|
|
8706
|
+
() => singletonConflicts.reduce((sum, c) => sum + c.duplicates.length, 0),
|
|
8707
|
+
[singletonConflicts]
|
|
8708
|
+
);
|
|
8709
|
+
const activeRecordIdsBySlot = useMemo(() => {
|
|
8710
|
+
const map = /* @__PURE__ */ new Map();
|
|
8711
|
+
for (const c of singletonConflicts) map.set(c.key, c.active.id);
|
|
8712
|
+
return map;
|
|
8713
|
+
}, [singletonConflicts]);
|
|
8086
8714
|
useEffect(() => {
|
|
8087
8715
|
if (activeScope !== "product") return;
|
|
8088
8716
|
if (selectedProductId) return;
|
|
@@ -8102,8 +8730,16 @@ function RecordsAdminShellInner(props) {
|
|
|
8102
8730
|
return;
|
|
8103
8731
|
}
|
|
8104
8732
|
const first = recordList.items[0];
|
|
8105
|
-
if (first?.id)
|
|
8106
|
-
|
|
8733
|
+
if (!first?.id) return;
|
|
8734
|
+
if (cardinality === "singleton") {
|
|
8735
|
+
const conflict = findConflictForRecord(first.id, singletonConflicts);
|
|
8736
|
+
if (conflict?.active.id) {
|
|
8737
|
+
setSelectedRecordId(conflict.active.id);
|
|
8738
|
+
return;
|
|
8739
|
+
}
|
|
8740
|
+
}
|
|
8741
|
+
setSelectedRecordId(first.id);
|
|
8742
|
+
}, [activeScope, selectedRecordId, recordList.items, cardinality, ruleWizardStep, draftKind, isReconcilingRecordSelection, singletonConflicts]);
|
|
8107
8743
|
const editingScopes = useEditingScope({
|
|
8108
8744
|
activeScope,
|
|
8109
8745
|
cardinality,
|
|
@@ -8135,6 +8771,26 @@ function RecordsAdminShellInner(props) {
|
|
|
8135
8771
|
toSummary: itemToSummary,
|
|
8136
8772
|
pageSize: itemsPageSize
|
|
8137
8773
|
});
|
|
8774
|
+
const autoLifecycleGroupBy = useMemo(() => {
|
|
8775
|
+
if (!isCollection) return void 0;
|
|
8776
|
+
if (!lifecycleAutoGroup) return void 0;
|
|
8777
|
+
if (groupBy) return void 0;
|
|
8778
|
+
if (!hasMixedLifecycle(collectionItems.items, resolvedActiveStatuses)) {
|
|
8779
|
+
return void 0;
|
|
8780
|
+
}
|
|
8781
|
+
return (record) => {
|
|
8782
|
+
const def = resolveLifecycleStatus(record, lifecycleConfig);
|
|
8783
|
+
return { key: def.value, label: def.label, tone: def.tone };
|
|
8784
|
+
};
|
|
8785
|
+
}, [
|
|
8786
|
+
isCollection,
|
|
8787
|
+
lifecycleAutoGroup,
|
|
8788
|
+
groupBy,
|
|
8789
|
+
collectionItems.items,
|
|
8790
|
+
resolvedActiveStatuses,
|
|
8791
|
+
lifecycleConfig
|
|
8792
|
+
]);
|
|
8793
|
+
const lcGroupBy = groupBy ?? autoLifecycleGroupBy;
|
|
8138
8794
|
useEffect(() => {
|
|
8139
8795
|
if (skipNextItemResetRef.current) {
|
|
8140
8796
|
skipNextItemResetRef.current = false;
|
|
@@ -8142,32 +8798,32 @@ function RecordsAdminShellInner(props) {
|
|
|
8142
8798
|
}
|
|
8143
8799
|
setSelectedItemId(null);
|
|
8144
8800
|
}, [editingScope?.raw]);
|
|
8145
|
-
const isLifecycleRailEarly = (activeScope === "all" || activeScope === "collection") && isCollection && !!
|
|
8801
|
+
const isLifecycleRailEarly = (activeScope === "all" || activeScope === "collection") && isCollection && !!lcGroupBy;
|
|
8146
8802
|
const lifecycleBucketLabel = useMemo(() => {
|
|
8147
|
-
if (!isLifecycleRailEarly || !selectedLifecycleKey || !
|
|
8803
|
+
if (!isLifecycleRailEarly || !selectedLifecycleKey || !lcGroupBy) return null;
|
|
8148
8804
|
for (const it of collectionItems.items) {
|
|
8149
|
-
const g =
|
|
8805
|
+
const g = lcGroupBy(it);
|
|
8150
8806
|
if (g && g.key === selectedLifecycleKey) return g.label ?? null;
|
|
8151
8807
|
}
|
|
8152
8808
|
return null;
|
|
8153
|
-
}, [isLifecycleRailEarly, selectedLifecycleKey,
|
|
8809
|
+
}, [isLifecycleRailEarly, selectedLifecycleKey, lcGroupBy, collectionItems.items]);
|
|
8154
8810
|
const scopedCollectionItemsList = useMemo(() => {
|
|
8155
|
-
if (!isLifecycleRailEarly || !selectedLifecycleKey || !
|
|
8811
|
+
if (!isLifecycleRailEarly || !selectedLifecycleKey || !lcGroupBy) return collectionItems.items;
|
|
8156
8812
|
return collectionItems.items.filter((it) => {
|
|
8157
|
-
const g =
|
|
8813
|
+
const g = lcGroupBy(it);
|
|
8158
8814
|
return g?.key === selectedLifecycleKey;
|
|
8159
8815
|
});
|
|
8160
|
-
}, [isLifecycleRailEarly, selectedLifecycleKey,
|
|
8816
|
+
}, [isLifecycleRailEarly, selectedLifecycleKey, lcGroupBy, collectionItems.items]);
|
|
8161
8817
|
useEffect(() => {
|
|
8162
|
-
if (!isLifecycleRailEarly || !
|
|
8818
|
+
if (!isLifecycleRailEarly || !lcGroupBy) return;
|
|
8163
8819
|
if (selectedLifecycleKey || !selectedItemId) return;
|
|
8164
8820
|
const row = collectionItems.items.find(
|
|
8165
8821
|
(it) => it.itemId === selectedItemId || it.id === selectedItemId
|
|
8166
8822
|
);
|
|
8167
8823
|
if (!row) return;
|
|
8168
|
-
const g =
|
|
8824
|
+
const g = lcGroupBy(row);
|
|
8169
8825
|
if (g?.key) setSelectedLifecycleKey(g.key);
|
|
8170
|
-
}, [isLifecycleRailEarly,
|
|
8826
|
+
}, [isLifecycleRailEarly, lcGroupBy, selectedItemId, selectedLifecycleKey, collectionItems.items, setSelectedLifecycleKey]);
|
|
8171
8827
|
const scopedCollectionItems = useMemo(() => ({
|
|
8172
8828
|
...collectionItems,
|
|
8173
8829
|
items: scopedCollectionItemsList
|
|
@@ -8302,6 +8958,15 @@ function RecordsAdminShellInner(props) {
|
|
|
8302
8958
|
return JSON.parse(JSON.stringify(globalSeedSource));
|
|
8303
8959
|
}
|
|
8304
8960
|
}
|
|
8961
|
+
if (draftKind === "paste") {
|
|
8962
|
+
const entry = wizardClipboard.entry;
|
|
8963
|
+
if (!entry) return defaultData?.() ?? {};
|
|
8964
|
+
try {
|
|
8965
|
+
return structuredClone(entry.value);
|
|
8966
|
+
} catch {
|
|
8967
|
+
return JSON.parse(JSON.stringify(entry.value));
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8305
8970
|
return defaultData?.() ?? {};
|
|
8306
8971
|
}, [
|
|
8307
8972
|
isCollection,
|
|
@@ -8312,7 +8977,8 @@ function RecordsAdminShellInner(props) {
|
|
|
8312
8977
|
directGlobalSeedData,
|
|
8313
8978
|
resolvedGlobalSeed.data,
|
|
8314
8979
|
onCopyOverride,
|
|
8315
|
-
defaultData
|
|
8980
|
+
defaultData,
|
|
8981
|
+
wizardClipboard.entry
|
|
8316
8982
|
]);
|
|
8317
8983
|
const refetchAll = useCallback(async () => {
|
|
8318
8984
|
await Promise.all([
|
|
@@ -8344,6 +9010,7 @@ function RecordsAdminShellInner(props) {
|
|
|
8344
9010
|
},
|
|
8345
9011
|
defaultData,
|
|
8346
9012
|
deriveDraftLabel,
|
|
9013
|
+
defaultStatus: lifecycleDefaultStatus,
|
|
8347
9014
|
onSaved: async (isCreate, savedRecordId) => {
|
|
8348
9015
|
onTelemetry?.({ type: "record.save", recordType, ref: editingTargetScope?.raw ?? "", isCreate });
|
|
8349
9016
|
const savedFromRuleWizard = ruleWizardStep !== null;
|
|
@@ -8521,8 +9188,7 @@ function RecordsAdminShellInner(props) {
|
|
|
8521
9188
|
const rowClipboard = shellClipboard.rowClipboard;
|
|
8522
9189
|
const wrappedRecordActions = recordActions ? (record) => {
|
|
8523
9190
|
const list = recordActions(record, record.scope);
|
|
8524
|
-
|
|
8525
|
-
return list.map((a) => ({
|
|
9191
|
+
const baseList = (list ?? []).map((a) => ({
|
|
8526
9192
|
...a,
|
|
8527
9193
|
onAction: () => {
|
|
8528
9194
|
onTelemetry?.({
|
|
@@ -8534,7 +9200,37 @@ function RecordsAdminShellInner(props) {
|
|
|
8534
9200
|
return a.onAction();
|
|
8535
9201
|
}
|
|
8536
9202
|
}));
|
|
8537
|
-
|
|
9203
|
+
const lifecycleAction = buildLifecycleAction(record);
|
|
9204
|
+
const out = lifecycleAction ? [...baseList, lifecycleAction] : baseList;
|
|
9205
|
+
return out.length > 0 ? out : void 0;
|
|
9206
|
+
} : (record) => {
|
|
9207
|
+
const a = buildLifecycleAction(record);
|
|
9208
|
+
return a ? [a] : void 0;
|
|
9209
|
+
};
|
|
9210
|
+
function buildLifecycleAction(record) {
|
|
9211
|
+
if (!record.id) return null;
|
|
9212
|
+
const allow = activeStatuses ?? ["active"];
|
|
9213
|
+
const s = record.lifecycleStatus;
|
|
9214
|
+
const active = s == null || s === "" || allow.includes(s);
|
|
9215
|
+
const next = active ? "archived" : "active";
|
|
9216
|
+
const label2 = active ? i18n.actionArchive : i18n.actionRestore;
|
|
9217
|
+
return {
|
|
9218
|
+
key: active ? "lifecycle.archive" : "lifecycle.restore",
|
|
9219
|
+
label: label2,
|
|
9220
|
+
onAction: async () => {
|
|
9221
|
+
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();
|
|
9227
|
+
}
|
|
9228
|
+
} catch (err) {
|
|
9229
|
+
console.warn("[RecordsAdminShell] lifecycle update failed", err);
|
|
9230
|
+
}
|
|
9231
|
+
}
|
|
9232
|
+
};
|
|
9233
|
+
}
|
|
8538
9234
|
const baseScopeRef = editingScope?.raw ?? "";
|
|
8539
9235
|
const itemNounLabel = itemNoun || "item";
|
|
8540
9236
|
const {
|
|
@@ -8598,6 +9294,70 @@ function RecordsAdminShellInner(props) {
|
|
|
8598
9294
|
i18n
|
|
8599
9295
|
}
|
|
8600
9296
|
) : null;
|
|
9297
|
+
const conflictForCurrent = selectedRecordId && selectedRecordId !== DRAFT_ID3 ? findConflictForRecord(selectedRecordId, singletonConflicts) : null;
|
|
9298
|
+
const editorHeaderNotice = conflictForCurrent ? /* @__PURE__ */ jsxs(
|
|
9299
|
+
"div",
|
|
9300
|
+
{
|
|
9301
|
+
role: "alert",
|
|
9302
|
+
className: "mb-3 px-3 py-2 rounded-md text-xs flex items-start gap-2",
|
|
9303
|
+
style: {
|
|
9304
|
+
background: "hsl(var(--ra-danger, 0 70% 45%) / 0.08)",
|
|
9305
|
+
border: "1px solid hsl(var(--ra-danger, 0 70% 45%) / 0.35)",
|
|
9306
|
+
color: "hsl(var(--ra-text))"
|
|
9307
|
+
},
|
|
9308
|
+
children: [
|
|
9309
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u26A0" }),
|
|
9310
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
9311
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium", children: `This record is 1 of ${conflictForCurrent.records.length} sharing the same slot.` }),
|
|
9312
|
+
/* @__PURE__ */ jsx("div", { style: { color: "hsl(var(--ra-muted-text))" }, children: conflictForCurrent.active.id === selectedRecordId ? "It is the active record \u2014 duplicates below will be ignored at runtime. Delete the duplicates to clean this up." : `Active record: "${conflictForCurrent.active.label}". This duplicate is ignored at runtime \u2014 delete it (or the active one) to resolve.` })
|
|
9313
|
+
] })
|
|
9314
|
+
]
|
|
9315
|
+
}
|
|
9316
|
+
) : 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;
|
|
9318
|
+
const editorLifecycleControl = selectedSummary?.id ? /* @__PURE__ */ jsx(
|
|
9319
|
+
LifecycleStatusControl,
|
|
9320
|
+
{
|
|
9321
|
+
SL,
|
|
9322
|
+
collectionId,
|
|
9323
|
+
appId,
|
|
9324
|
+
recordId: selectedSummary.id,
|
|
9325
|
+
current: selectedSummary.lifecycleStatus,
|
|
9326
|
+
i18n,
|
|
9327
|
+
onChanged: () => {
|
|
9328
|
+
void recordList.refetch();
|
|
9329
|
+
}
|
|
9330
|
+
}
|
|
9331
|
+
) : null;
|
|
9332
|
+
const editorLifecycleFooter = selectedSummary?.id && (lifecycleSurface === "footer" || lifecycleSurface === "both") ? /* @__PURE__ */ jsx(
|
|
9333
|
+
LifecycleStatusMenu,
|
|
9334
|
+
{
|
|
9335
|
+
SL,
|
|
9336
|
+
collectionId,
|
|
9337
|
+
appId,
|
|
9338
|
+
recordId: selectedSummary.id,
|
|
9339
|
+
scope: selectedSummary.scope,
|
|
9340
|
+
current: selectedSummary.lifecycleStatus,
|
|
9341
|
+
statuses: lifecycleStatuses,
|
|
9342
|
+
i18n,
|
|
9343
|
+
beforeChange: lifecycleBeforeChange,
|
|
9344
|
+
onTelemetry: (e) => onTelemetry?.({
|
|
9345
|
+
type: "lifecycle.change",
|
|
9346
|
+
recordType,
|
|
9347
|
+
ref: selectedSummary.id,
|
|
9348
|
+
from: e.from,
|
|
9349
|
+
to: e.to
|
|
9350
|
+
}),
|
|
9351
|
+
onChanged: () => {
|
|
9352
|
+
void refetchAll();
|
|
9353
|
+
if (cardinality === "singleton") {
|
|
9354
|
+
ruleScopedList.refetch();
|
|
9355
|
+
globalScopedList.refetch();
|
|
9356
|
+
}
|
|
9357
|
+
}
|
|
9358
|
+
}
|
|
9359
|
+
) : null;
|
|
9360
|
+
const headerLifecycleControl = lifecycleSurface === "header" || lifecycleSurface === "both" ? editorLifecycleControl : null;
|
|
8601
9361
|
const baseEditor = (extraFooter, inlinePreviewBody) => /* @__PURE__ */ jsx(
|
|
8602
9362
|
RecordEditor,
|
|
8603
9363
|
{
|
|
@@ -8635,11 +9395,14 @@ function RecordsAdminShellInner(props) {
|
|
|
8635
9395
|
onCustomise: () => setTargetingExpandNonce((n) => n + 1)
|
|
8636
9396
|
}
|
|
8637
9397
|
) : void 0,
|
|
9398
|
+
lifecycleControl: headerLifecycleControl,
|
|
9399
|
+
lifecycleControlFooter: editorLifecycleFooter,
|
|
8638
9400
|
onBeforeDelete: onBeforeDelete && editingTargetScope ? () => onBeforeDelete(editingTargetScope) : void 0,
|
|
8639
9401
|
headerLabel: editorHeaderLabel,
|
|
8640
9402
|
headerSubtitle: editorHeaderSubtitle,
|
|
8641
9403
|
headerMeta: editorHeaderMeta,
|
|
8642
9404
|
headerLeading: itemNav,
|
|
9405
|
+
headerNotice: editorHeaderNotice,
|
|
8643
9406
|
clipboard: editorClipboard,
|
|
8644
9407
|
actionLabels,
|
|
8645
9408
|
actionIcons,
|
|
@@ -8905,8 +9668,12 @@ function RecordsAdminShellInner(props) {
|
|
|
8905
9668
|
setRuleWizardStep(2);
|
|
8906
9669
|
} else {
|
|
8907
9670
|
setRuleWizardStep(2);
|
|
9671
|
+
if (ruleWizardSeedMode) {
|
|
9672
|
+
setRuleWizardDraftKey(mintRuleWizardDraftKey());
|
|
9673
|
+
setSelectedRecordId(DRAFT_ID3);
|
|
9674
|
+
}
|
|
8908
9675
|
}
|
|
8909
|
-
}, [cardinality]);
|
|
9676
|
+
}, [cardinality, ruleWizardSeedMode, mintRuleWizardDraftKey]);
|
|
8910
9677
|
const startRuleWizardDraft = useCallback((seed) => {
|
|
8911
9678
|
setRuleWizardSeedMode(seed);
|
|
8912
9679
|
setRuleWizardDraftKey(mintRuleWizardDraftKey());
|
|
@@ -8940,7 +9707,7 @@ function RecordsAdminShellInner(props) {
|
|
|
8940
9707
|
void runWithGuard(() => {
|
|
8941
9708
|
if (activeScope !== "product") setActiveScope("product");
|
|
8942
9709
|
setSelectedRecordId(DRAFT_ID3);
|
|
8943
|
-
setDraftKind(seed === "global" ? "global" : "item");
|
|
9710
|
+
setDraftKind(seed === "global" ? "global" : seed === "paste" ? "paste" : "item");
|
|
8944
9711
|
});
|
|
8945
9712
|
}, [runWithGuard, activeScope]);
|
|
8946
9713
|
const filteredRuleItems = useMemo(
|
|
@@ -9035,13 +9802,13 @@ function RecordsAdminShellInner(props) {
|
|
|
9035
9802
|
}),
|
|
9036
9803
|
[i18n.itemsAllLabel, collectionItems.items.length, itemNoun]
|
|
9037
9804
|
);
|
|
9038
|
-
const isLifecycleRail = (isAllTab || isGlobalTab) && isCollection && !!
|
|
9805
|
+
const isLifecycleRail = (isAllTab || isGlobalTab) && isCollection && !!lcGroupBy;
|
|
9039
9806
|
const lifecycleBuckets = useMemo(() => {
|
|
9040
|
-
if (!isLifecycleRail || !
|
|
9807
|
+
if (!isLifecycleRail || !lcGroupBy) return [];
|
|
9041
9808
|
const map = /* @__PURE__ */ new Map();
|
|
9042
9809
|
const order = [];
|
|
9043
9810
|
for (const item of collectionItems.items) {
|
|
9044
|
-
const g =
|
|
9811
|
+
const g = lcGroupBy(item) ?? { key: "__other", label: "Other" };
|
|
9045
9812
|
let bucket = map.get(g.key);
|
|
9046
9813
|
if (!bucket) {
|
|
9047
9814
|
bucket = { key: g.key, label: g.label, icon: g.icon, tone: g.tone, items: [] };
|
|
@@ -9050,8 +9817,9 @@ function RecordsAdminShellInner(props) {
|
|
|
9050
9817
|
}
|
|
9051
9818
|
bucket.items.push(item);
|
|
9052
9819
|
}
|
|
9053
|
-
|
|
9054
|
-
|
|
9820
|
+
const sortFn = !groupBy ? compareLifecycleBuckets : (a, b) => a.localeCompare(b);
|
|
9821
|
+
return order.sort(sortFn).map((k) => map.get(k));
|
|
9822
|
+
}, [isLifecycleRail, lcGroupBy, groupBy, collectionItems.items]);
|
|
9055
9823
|
const LIFECYCLE_PREFIX = "lifecycle:";
|
|
9056
9824
|
const lifecycleRows = useMemo(() => {
|
|
9057
9825
|
if (!isLifecycleRail) return [];
|
|
@@ -9101,15 +9869,47 @@ function RecordsAdminShellInner(props) {
|
|
|
9101
9869
|
lifecycleSeededRef.current = true;
|
|
9102
9870
|
}, [isLifecycleRail, defaultGroupKey, lifecycleBuckets, selectedLifecycleKey, setSelectedLifecycleKey]);
|
|
9103
9871
|
const filteredCollectionItems = useMemo(() => {
|
|
9104
|
-
if (!isLifecycleRail || !selectedLifecycleKey || !
|
|
9872
|
+
if (!isLifecycleRail || !selectedLifecycleKey || !lcGroupBy) return collectionItems.items;
|
|
9105
9873
|
return collectionItems.items.filter((it) => {
|
|
9106
|
-
const g =
|
|
9874
|
+
const g = lcGroupBy(it) ?? { key: "__other" };
|
|
9107
9875
|
return g.key === selectedLifecycleKey;
|
|
9108
9876
|
});
|
|
9109
|
-
}, [isLifecycleRail, selectedLifecycleKey,
|
|
9877
|
+
}, [isLifecycleRail, selectedLifecycleKey, lcGroupBy, collectionItems.items]);
|
|
9110
9878
|
const leftItems = isProductTab ? productListItems : isRuleTab ? applyFacetBrowseFilter(
|
|
9111
9879
|
isCollection ? collectionRuleRailItems : filteredRuleItems
|
|
9112
9880
|
) : isLifecycleRail ? lifecycleRows : (isGlobalTab || isAllTab) && isCollection ? [collectionGlobalAllRow] : isRecordsTab ? applyFacetBrowseFilter(recordList.items) : [];
|
|
9881
|
+
const railShowsHistoryDisclosure = isRecordsTab && !isLifecycleRail && !((isGlobalTab || isAllTab) && isCollection);
|
|
9882
|
+
const filteredLeftItems = useMemo(() => {
|
|
9883
|
+
if (!railShowsHistoryDisclosure) return leftItems;
|
|
9884
|
+
return leftItems.filter((r) => {
|
|
9885
|
+
const s = r.lifecycleStatus;
|
|
9886
|
+
if (s == null || s === "") return true;
|
|
9887
|
+
const allow = activeStatuses ?? ["active"];
|
|
9888
|
+
return allow.includes(s);
|
|
9889
|
+
});
|
|
9890
|
+
}, [leftItems, railShowsHistoryDisclosure, activeStatuses]);
|
|
9891
|
+
const railHistoryBySlot = useMemo(() => {
|
|
9892
|
+
if (!railShowsHistoryDisclosure) return void 0;
|
|
9893
|
+
return recordList.historyBySlot;
|
|
9894
|
+
}, [railShowsHistoryDisclosure, recordList.historyBySlot]);
|
|
9895
|
+
const decoratedLeftItems = useMemo(() => {
|
|
9896
|
+
if (!hasSingletonConflicts) return filteredLeftItems;
|
|
9897
|
+
return filteredLeftItems.map((row) => {
|
|
9898
|
+
if (!row.id) return row;
|
|
9899
|
+
const key = slotKey(row);
|
|
9900
|
+
const activeId = activeRecordIdsBySlot.get(key);
|
|
9901
|
+
if (activeId === void 0) return row;
|
|
9902
|
+
const isActive2 = row.id === activeId;
|
|
9903
|
+
const conflictBadge = [{
|
|
9904
|
+
label: isActive2 ? "Active" : "Duplicate",
|
|
9905
|
+
tone: isActive2 ? "success" : "warning"
|
|
9906
|
+
}];
|
|
9907
|
+
return {
|
|
9908
|
+
...row,
|
|
9909
|
+
badges: [...conflictBadge, ...row.badges ?? []]
|
|
9910
|
+
};
|
|
9911
|
+
});
|
|
9912
|
+
}, [filteredLeftItems, hasSingletonConflicts, activeRecordIdsBySlot]);
|
|
9113
9913
|
const leftLoading = isProductTab ? !productPinned && productBrowse.isLoading : isRecordsTab ? recordList.isLoading || probe.isLoading : false;
|
|
9114
9914
|
const leftError = isProductTab ? productBrowse.error : isRecordsTab ? recordList.error : null;
|
|
9115
9915
|
const leftSelectedId = isProductTab ? void 0 : isLifecycleRail ? `${LIFECYCLE_PREFIX}${selectedLifecycleKey ?? "__all"}` : selectedRecordId && selectedRecordId !== DRAFT_ID3 ? selectedRecordId : void 0;
|
|
@@ -9456,69 +10256,138 @@ function RecordsAdminShellInner(props) {
|
|
|
9456
10256
|
)
|
|
9457
10257
|
] })
|
|
9458
10258
|
] }),
|
|
9459
|
-
/* @__PURE__ */
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
!leftLoading && !leftError && leftItems.length === 0 && (renderEmptyState ? renderEmptyState({ scope: activeScope }) : /* @__PURE__ */ jsx(
|
|
9463
|
-
EmptyState,
|
|
10259
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
10260
|
+
hasSingletonConflicts && /* @__PURE__ */ jsx(
|
|
10261
|
+
SingletonConflictBanner,
|
|
9464
10262
|
{
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
10263
|
+
conflictCount: singletonConflicts.length,
|
|
10264
|
+
duplicateCount: totalDuplicateCount,
|
|
10265
|
+
onResolve: () => {
|
|
10266
|
+
const first = singletonConflicts[0]?.duplicates[0] ?? singletonConflicts[0]?.active;
|
|
10267
|
+
if (first?.id) {
|
|
10268
|
+
void runWithGuard(() => {
|
|
10269
|
+
setSelectedRecordId(first.id);
|
|
10270
|
+
});
|
|
10271
|
+
}
|
|
10272
|
+
},
|
|
10273
|
+
onArchiveDuplicates: enableArchiveDuplicates ? async () => {
|
|
10274
|
+
const ids = singletonConflicts.flatMap((c) => c.duplicates.map((d) => d.id)).filter((id) => !!id);
|
|
10275
|
+
for (const id of ids) {
|
|
10276
|
+
try {
|
|
10277
|
+
await SL.app.records.update(collectionId, appId, id, { status: archivedStatusValue }, true);
|
|
10278
|
+
onTelemetry?.({
|
|
10279
|
+
type: "recordAction.invoke",
|
|
10280
|
+
recordType,
|
|
10281
|
+
key: "conflict.archiveDuplicates",
|
|
10282
|
+
ref: id
|
|
10283
|
+
});
|
|
10284
|
+
} catch (err) {
|
|
10285
|
+
console.warn("[RecordsAdminShell] archive-duplicate failed", id, err);
|
|
10286
|
+
}
|
|
10287
|
+
}
|
|
10288
|
+
if (cardinality === "singleton") {
|
|
10289
|
+
ruleScopedList.refetch();
|
|
10290
|
+
globalScopedList.refetch();
|
|
10291
|
+
}
|
|
10292
|
+
await refetchAll();
|
|
10293
|
+
} : void 0,
|
|
10294
|
+
onDeleteDuplicates: enableDeleteDuplicates ? async () => {
|
|
10295
|
+
const ids = singletonConflicts.flatMap((c) => c.duplicates.map((d) => d.id)).filter((id) => !!id);
|
|
10296
|
+
for (const id of ids) {
|
|
10297
|
+
try {
|
|
10298
|
+
await SL.app.records.remove(collectionId, appId, id, true);
|
|
10299
|
+
onTelemetry?.({
|
|
10300
|
+
type: "recordAction.invoke",
|
|
10301
|
+
recordType,
|
|
10302
|
+
key: "conflict.deleteDuplicates",
|
|
10303
|
+
ref: id
|
|
10304
|
+
});
|
|
10305
|
+
} catch (err) {
|
|
10306
|
+
console.warn("[RecordsAdminShell] delete-duplicate failed", id, err);
|
|
10307
|
+
}
|
|
10308
|
+
}
|
|
10309
|
+
if (cardinality === "singleton") {
|
|
10310
|
+
ruleScopedList.refetch();
|
|
10311
|
+
globalScopedList.refetch();
|
|
10312
|
+
}
|
|
10313
|
+
await refetchAll();
|
|
10314
|
+
} : void 0,
|
|
10315
|
+
i18n: {
|
|
10316
|
+
title: i18n.conflictBannerTitle,
|
|
10317
|
+
bodyOne: i18n.conflictBannerBodyOne,
|
|
10318
|
+
bodyMany: i18n.conflictBannerBodyMany,
|
|
10319
|
+
archiveLabel: i18n.conflictArchiveDuplicates,
|
|
10320
|
+
deleteLabel: i18n.conflictDeleteDuplicates,
|
|
10321
|
+
deleteConfirm: i18n.conflictDeleteConfirm,
|
|
10322
|
+
resolveLabel: i18n.conflictResolveLabel
|
|
10323
|
+
}
|
|
9468
10324
|
}
|
|
9469
|
-
)
|
|
9470
|
-
|
|
9471
|
-
/* @__PURE__ */ jsx(
|
|
9472
|
-
|
|
10325
|
+
),
|
|
10326
|
+
isGlobalTab && !isCollection && !hasSingletonConflicts ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10327
|
+
leftLoading && /* @__PURE__ */ jsx(LoadingState, {}),
|
|
10328
|
+
!leftLoading && leftError && /* @__PURE__ */ jsx(ErrorState, { error: leftError }),
|
|
10329
|
+
!leftLoading && !leftError && decoratedLeftItems.length === 0 && (renderEmptyState ? renderEmptyState({ scope: activeScope }) : /* @__PURE__ */ jsx(
|
|
10330
|
+
EmptyState,
|
|
9473
10331
|
{
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
onSelect: onLeftSelect,
|
|
9478
|
-
dirtyId,
|
|
9479
|
-
dirtyAnchorKey,
|
|
9480
|
-
dirtyKeys,
|
|
9481
|
-
errorKeys,
|
|
9482
|
-
presentation: effectivePresentation,
|
|
9483
|
-
renderListRow,
|
|
9484
|
-
groupBy: (
|
|
9485
|
-
// The synthetic "All items" row in collection mode is a
|
|
9486
|
-
// navigational anchor, not a real record — applying the
|
|
9487
|
-
// host's groupBy bucketed it under "Other" (its
|
|
9488
|
-
// data is null). Skip grouping for that single-row rail.
|
|
9489
|
-
(isGlobalTab || isAllTab) && isCollection || isLifecycleRail ? void 0 : effectiveGroupBy
|
|
9490
|
-
),
|
|
9491
|
-
renderGroupActions: renderRuleGroupActions,
|
|
9492
|
-
rowClipboard,
|
|
9493
|
-
rowActions: wrappedRecordActions,
|
|
9494
|
-
i18n
|
|
10332
|
+
icon: search ? icons.empty.search : icons.empty.default,
|
|
10333
|
+
title: search ? i18n.noResults : i18n.railEmptyTitle,
|
|
10334
|
+
body: search ? void 0 : isRuleTab ? i18n.rulesEmptyBody : i18n.railEmptyBody
|
|
9495
10335
|
}
|
|
9496
|
-
),
|
|
9497
|
-
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
|
|
10336
|
+
)),
|
|
10337
|
+
!leftLoading && !leftError && decoratedLeftItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10338
|
+
/* @__PURE__ */ jsx(
|
|
10339
|
+
RecordList,
|
|
10340
|
+
{
|
|
10341
|
+
items: decoratedLeftItems,
|
|
10342
|
+
selectedId: leftSelectedId,
|
|
10343
|
+
selectedAnchorKey: leftSelectedAnchorKey,
|
|
10344
|
+
onSelect: onLeftSelect,
|
|
10345
|
+
dirtyId,
|
|
10346
|
+
dirtyAnchorKey,
|
|
10347
|
+
dirtyKeys,
|
|
10348
|
+
errorKeys,
|
|
10349
|
+
presentation: effectivePresentation,
|
|
10350
|
+
renderListRow,
|
|
10351
|
+
groupBy: (
|
|
10352
|
+
// The synthetic "All items" row in collection mode is a
|
|
10353
|
+
// navigational anchor, not a real record — applying the
|
|
10354
|
+
// host's groupBy bucketed it under "Other" (its
|
|
10355
|
+
// data is null). Skip grouping for that single-row rail.
|
|
10356
|
+
(isGlobalTab || isAllTab) && isCollection || isLifecycleRail ? void 0 : effectiveGroupBy
|
|
10357
|
+
),
|
|
10358
|
+
renderGroupActions: renderRuleGroupActions,
|
|
10359
|
+
rowClipboard,
|
|
10360
|
+
rowActions: wrappedRecordActions,
|
|
10361
|
+
i18n,
|
|
10362
|
+
historyBySlot: railHistoryBySlot
|
|
9505
10363
|
}
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
void recordList.fetchNextPage();
|
|
10364
|
+
),
|
|
10365
|
+
isProductTab && !productPinned && /* @__PURE__ */ jsx(
|
|
10366
|
+
LoadMoreFooter,
|
|
10367
|
+
{
|
|
10368
|
+
shown: leftItems.length,
|
|
10369
|
+
hasNextPage: !!productBrowse.hasNextPage,
|
|
10370
|
+
isFetchingNextPage: !!productBrowse.isFetchingNextPage,
|
|
10371
|
+
onLoadMore: () => {
|
|
10372
|
+
void productBrowse.fetchNextPage();
|
|
10373
|
+
}
|
|
9517
10374
|
}
|
|
9518
|
-
|
|
9519
|
-
|
|
10375
|
+
),
|
|
10376
|
+
isRecordsTab && (!isCollection || !(isAllTab || isGlobalTab)) && /* @__PURE__ */ jsx(
|
|
10377
|
+
LoadMoreFooter,
|
|
10378
|
+
{
|
|
10379
|
+
shown: recordList.items.length,
|
|
10380
|
+
total: recordList.total,
|
|
10381
|
+
hasNextPage: !!recordList.hasNextPage,
|
|
10382
|
+
isFetchingNextPage: !!recordList.isFetchingNextPage,
|
|
10383
|
+
onLoadMore: () => {
|
|
10384
|
+
void recordList.fetchNextPage();
|
|
10385
|
+
}
|
|
10386
|
+
}
|
|
10387
|
+
)
|
|
10388
|
+
] })
|
|
9520
10389
|
] })
|
|
9521
|
-
] })
|
|
10390
|
+
] })
|
|
9522
10391
|
] }) }),
|
|
9523
10392
|
/* @__PURE__ */ jsxs("main", { className: "overflow-hidden", children: [
|
|
9524
10393
|
ruleWizardStep !== null && /* @__PURE__ */ jsxs(
|
|
@@ -9666,6 +10535,8 @@ function RecordsAdminShellInner(props) {
|
|
|
9666
10535
|
),
|
|
9667
10536
|
ruleWizardStep === null && isProductTab && selectedProductId && !isCollection && editingTargetScope && resolved.source !== "self" && selectedRecordId !== DRAFT_ID3 ? (() => {
|
|
9668
10537
|
const productName = productLookupItems.find((p) => p.id === selectedProductId)?.name ?? selectedProductId;
|
|
10538
|
+
const pasteEntry = wizardClipboard.entry;
|
|
10539
|
+
const pasteSourceLabel = pasteEntry?.sourceLabel ?? pasteEntry?.sourceScope.raw;
|
|
9669
10540
|
return /* @__PURE__ */ jsx(
|
|
9670
10541
|
CreateRecordChooser,
|
|
9671
10542
|
{
|
|
@@ -9674,7 +10545,9 @@ function RecordsAdminShellInner(props) {
|
|
|
9674
10545
|
primaryLabel: "Start blank",
|
|
9675
10546
|
onPrimary: () => onCreateProductRecord("blank"),
|
|
9676
10547
|
secondaryLabel: singletonGlobalSeedAvailable ? "Copy from global" : void 0,
|
|
9677
|
-
onSecondary: singletonGlobalSeedAvailable ? () => onCreateProductRecord("global") : void 0
|
|
10548
|
+
onSecondary: singletonGlobalSeedAvailable ? () => onCreateProductRecord("global") : void 0,
|
|
10549
|
+
tertiaryLabel: pasteEntry ? pasteSourceLabel ? `Paste from ${pasteSourceLabel}` : "Paste from clipboard" : void 0,
|
|
10550
|
+
onTertiary: pasteEntry ? () => onCreateProductRecord("paste") : void 0
|
|
9678
10551
|
}
|
|
9679
10552
|
);
|
|
9680
10553
|
})() : null,
|
|
@@ -10247,11 +11120,40 @@ function useRecordEditor(args) {
|
|
|
10247
11120
|
if (!resolved.recordId) return;
|
|
10248
11121
|
await removeRecord(ctx, resolved.recordId);
|
|
10249
11122
|
draftStore.clearDraft(draftKey);
|
|
11123
|
+
removeRecordFromCaches(queryClient, ctx, resolved.recordId);
|
|
11124
|
+
const cacheKey = resolvedRecordQueryKey({
|
|
11125
|
+
collectionId: ctx.collectionId,
|
|
11126
|
+
appId: ctx.appId,
|
|
11127
|
+
recordType: ctx.recordType,
|
|
11128
|
+
productId: scope.productId,
|
|
11129
|
+
variantId: scope.variantId,
|
|
11130
|
+
batchId: scope.batchId,
|
|
11131
|
+
facetId: scope.facetId,
|
|
11132
|
+
facetValue: scope.facetValue,
|
|
11133
|
+
proofId: scope.proofId,
|
|
11134
|
+
recordId: resolved.recordId,
|
|
11135
|
+
withParent: true
|
|
11136
|
+
});
|
|
11137
|
+
queryClient.removeQueries({ queryKey: cacheKey });
|
|
11138
|
+
const anchorCacheKey = resolvedRecordQueryKey({
|
|
11139
|
+
collectionId: ctx.collectionId,
|
|
11140
|
+
appId: ctx.appId,
|
|
11141
|
+
recordType: ctx.recordType,
|
|
11142
|
+
productId: scope.productId,
|
|
11143
|
+
variantId: scope.variantId,
|
|
11144
|
+
batchId: scope.batchId,
|
|
11145
|
+
facetId: scope.facetId,
|
|
11146
|
+
facetValue: scope.facetValue,
|
|
11147
|
+
proofId: scope.proofId,
|
|
11148
|
+
recordId: void 0,
|
|
11149
|
+
withParent: true
|
|
11150
|
+
});
|
|
11151
|
+
queryClient.removeQueries({ queryKey: anchorCacheKey });
|
|
10250
11152
|
queryClient.invalidateQueries({
|
|
10251
11153
|
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
10252
11154
|
});
|
|
10253
11155
|
onDeleted?.();
|
|
10254
|
-
}, [resolved.source, resolved.recordId, draftKey]);
|
|
11156
|
+
}, [resolved.source, resolved.recordId, draftKey, scope.raw]);
|
|
10255
11157
|
const prevDraftKeyRef = useRef(draftKey);
|
|
10256
11158
|
const prevScopeRawRef = useRef(scope.raw);
|
|
10257
11159
|
useEffect(() => {
|
|
@@ -10728,6 +11630,6 @@ function useMergedRecord(args) {
|
|
|
10728
11630
|
// src/components/RecordsAdmin/index.ts
|
|
10729
11631
|
assertComponentStylesLoaded("records-admin");
|
|
10730
11632
|
|
|
10731
|
-
export { ALL_ITEM_VIEWS, ALL_PRESENTATIONS, BatchList, BulkActionsMenu, DEFAULT_DEEP_LINK_PARAM_NAMES, DEFAULT_I18N, DEFAULT_ICONS, DefaultItemCards, DefaultItemTable, DefaultRecordCard, DefaultRecordRow, DeleteButton, DirtyDraftProvider, DrawerPreview, EditorItemNav, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, ItemListView, ItemViewSwitcher, LoadingState, PresentationSwitcher, PreviewReopenPill, PreviewScopePicker, PreviewToggleButton, ProductDrillDown, ProductList, RecordBrowser, RecordEditor, RecordList, RecordsAdminShell, ResolvedPreview, ScopeBreadcrumb, ScopeTabs, SiblingRail, SidePreview, StatusDot, StatusFilterPills, StatusIcon, TabbedPreview, UtilityRow, VariantList, buildDraftKey, buildRef, checkPasteCompatibility, cloneValue, createDefaultDeepLinkAdapter, createPostMessageDeepLinkAdapter, createRouterDeepLinkAdapter, downloadBlob, exportCsv, importCsv, isInSmartLinksIframe, mergeIcons, normaliseRule, parseRef, pickHeaderIcon, resolutionChain, resolveRecord, ruleHash, rulesEqual, scopeCountsQueryKey, statusToneLabel, summariseRule, useCollectedRecords, useCollectionItems, useDeepLinkState, useDirtyDraft, useDirtyDraftActions, useDirtyDraftStore, useDirtyDrafts, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useItemViewPref, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordClipboard, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeCounts, useScopeProbe, useUnsavedGuard };
|
|
11633
|
+
export { ALL_ITEM_VIEWS, ALL_PRESENTATIONS, BatchList, BulkActionsMenu, DEFAULT_DEEP_LINK_PARAM_NAMES, DEFAULT_I18N, DEFAULT_ICONS, DEFAULT_LIFECYCLE_STATUSES, DefaultItemCards, DefaultItemTable, DefaultRecordCard, DefaultRecordRow, DeleteButton, DirtyDraftProvider, DrawerPreview, EditorItemNav, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, ItemListView, ItemViewSwitcher, LifecycleStatusMenu, LoadingState, PresentationSwitcher, PreviewReopenPill, PreviewScopePicker, PreviewToggleButton, ProductDrillDown, ProductList, RecordBrowser, RecordEditor, RecordList, RecordsAdminShell, ResolvedPreview, ScopeBreadcrumb, ScopeTabs, SiblingRail, SidePreview, StatusDot, StatusFilterPills, StatusIcon, TabbedPreview, UtilityRow, VariantList, buildDraftKey, buildRef, checkPasteCompatibility, cloneValue, compareLifecycleBuckets, createDefaultDeepLinkAdapter, createPostMessageDeepLinkAdapter, createRouterDeepLinkAdapter, downloadBlob, exportCsv, getActiveStatusValues, getLifecycleStatuses, hasMixedLifecycle, importCsv, isInSmartLinksIframe, mergeIcons, normaliseRule, parseRef, pickHeaderIcon, resolutionChain, resolveLifecycleStatus, resolveRecord, ruleHash, rulesEqual, scopeCountsQueryKey, statusToneLabel, summariseRule, useCollectedRecords, useCollectionItems, useDeepLinkState, useDirtyDraft, useDirtyDraftActions, useDirtyDraftStore, useDirtyDrafts, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useItemViewPref, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordClipboard, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeCounts, useScopeProbe, useUnsavedGuard };
|
|
10732
11634
|
//# sourceMappingURL=index.js.map
|
|
10733
11635
|
//# sourceMappingURL=index.js.map
|