@proveanything/smartlinks-utils-ui 0.9.0 → 0.9.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/chunk-5UQQYXCX.js +3 -0
- package/dist/chunk-5UQQYXCX.js.map +1 -0
- package/dist/{chunk-UDYVH7QF.js → chunk-CBIKDA3S.js} +34 -3
- package/dist/chunk-CBIKDA3S.js.map +1 -0
- package/dist/components/AssetPicker/index.css +1505 -0
- package/dist/components/AssetPicker/index.css.map +1 -0
- package/dist/components/AssetPicker/index.js +2 -2
- package/dist/components/ConditionsEditor/index.css +1505 -0
- package/dist/components/ConditionsEditor/index.css.map +1 -0
- package/dist/components/ConditionsEditor/index.js +1 -1
- package/dist/components/FontPicker/index.css +1505 -0
- package/dist/components/FontPicker/index.css.map +1 -0
- package/dist/components/FontPicker/index.js +1 -1
- package/dist/components/IconPicker/index.css +1505 -0
- package/dist/components/IconPicker/index.css.map +1 -0
- package/dist/components/IconPicker/index.js +1 -1
- package/dist/components/RecordsAdmin/index.css +3646 -0
- package/dist/components/RecordsAdmin/index.css.map +1 -0
- package/dist/components/RecordsAdmin/index.d.ts +185 -10
- package/dist/components/RecordsAdmin/index.js +1093 -136
- package/dist/components/RecordsAdmin/index.js.map +1 -1
- package/dist/index.css +1505 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-NSQRNFT2.js +0 -28
- package/dist/chunk-NSQRNFT2.js.map +0 -1
- package/dist/chunk-UDYVH7QF.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import '../../chunk-5UQQYXCX.js';
|
|
2
2
|
import { FacetRuleEditor } from '../../chunk-JMCV6FOW.js';
|
|
3
3
|
import { useFacets } from '../../chunk-4LHF5JB7.js';
|
|
4
4
|
import { cn } from '../../chunk-L7FQ52F5.js';
|
|
5
5
|
import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-KFKVGUUP.js';
|
|
6
6
|
export { bulkDelete, bulkUpsert, createRecord, getRecordById, listRecords, matchRecords, parsedRefToScope, parsedRefToTarget, removeRecord, restoreRecord, scopesEqual, upsertRecord } from '../../chunk-KFKVGUUP.js';
|
|
7
7
|
import { createContext, useState, useEffect, useCallback, useMemo, useRef, useContext, useSyncExternalStore, createElement, useId } from 'react';
|
|
8
|
-
import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, SlidersHorizontal, Globe, Tag, Boxes, Layers, Package, Rows3,
|
|
8
|
+
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, Rows3, ChevronRight, Eraser, ClipboardPaste, Box, X, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, Search, CornerDownLeft, Circle, AlertCircle, Undo2, Save, Loader2, XCircle, ArrowRight, BookOpen, Globe2, Target, Check, Settings2 } from 'lucide-react';
|
|
9
9
|
import { useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
|
|
10
10
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
11
11
|
import { createPortal } from 'react-dom';
|
|
@@ -18,7 +18,8 @@ var DEFAULT_ICONS = {
|
|
|
18
18
|
batch: Boxes,
|
|
19
19
|
facet: Tag,
|
|
20
20
|
universal: Globe,
|
|
21
|
-
rule: SlidersHorizontal
|
|
21
|
+
rule: SlidersHorizontal,
|
|
22
|
+
all: List
|
|
22
23
|
},
|
|
23
24
|
status: { own: CheckCircle2, inherited: ArrowDownLeft, missing: CircleDashed },
|
|
24
25
|
action: {
|
|
@@ -143,7 +144,13 @@ var DEFAULT_I18N = {
|
|
|
143
144
|
itemsAllLabel: "All items",
|
|
144
145
|
subtitleEmpty: "Not set",
|
|
145
146
|
subtitleConfigured: "Configured",
|
|
146
|
-
subtitleInherited: "Inherited"
|
|
147
|
+
subtitleInherited: "Inherited",
|
|
148
|
+
rulesTabTooltip: "Use rules to scope records to a targeted audience or a subset of products.",
|
|
149
|
+
rulesEmptyBody: "Create a rule to target a subset of products or a specific audience \u2014 for example, \u201Conly premium tier customers in Germany\u201D.",
|
|
150
|
+
hookBeforeSaveFailed: "Couldn't save: {message}",
|
|
151
|
+
hookAfterSaveFailed: "Saved, but a follow-up step failed: {message}",
|
|
152
|
+
hookBeforeDeleteFailed: "Couldn't delete: {message}",
|
|
153
|
+
hookAfterDeleteFailed: "Deleted, but a follow-up step failed: {message}"
|
|
147
154
|
};
|
|
148
155
|
|
|
149
156
|
// src/components/RecordsAdmin/types/presentation.ts
|
|
@@ -204,6 +211,17 @@ var buildRef = (a) => {
|
|
|
204
211
|
if (a.proofId) parts.push(`proof:${a.proofId}`);
|
|
205
212
|
return parts.join("/");
|
|
206
213
|
};
|
|
214
|
+
var anchorKey = (scope) => {
|
|
215
|
+
if (!scope) return "";
|
|
216
|
+
return buildRef({
|
|
217
|
+
productId: scope.productId,
|
|
218
|
+
variantId: scope.variantId,
|
|
219
|
+
batchId: scope.batchId,
|
|
220
|
+
facetId: scope.facetId,
|
|
221
|
+
facetValue: scope.facetValue,
|
|
222
|
+
proofId: scope.proofId
|
|
223
|
+
});
|
|
224
|
+
};
|
|
207
225
|
var resolutionChain = (target, supportedScopes) => {
|
|
208
226
|
const chain = [];
|
|
209
227
|
if (supportedScopes.includes("batch") && target.batchId && target.productId) {
|
|
@@ -224,6 +242,38 @@ var resolutionChain = (target, supportedScopes) => {
|
|
|
224
242
|
return Array.from(new Set(chain));
|
|
225
243
|
};
|
|
226
244
|
|
|
245
|
+
// src/components/RecordsAdmin/data/ruleHash.ts
|
|
246
|
+
var normaliseRule = (rule) => {
|
|
247
|
+
if (!rule || !Array.isArray(rule.all) || rule.all.length === 0) return null;
|
|
248
|
+
const clauses = rule.all.map((clause) => {
|
|
249
|
+
const facetKey = String(clause.facetKey ?? "").trim();
|
|
250
|
+
const anyOfRaw = clause.anyOf ?? [];
|
|
251
|
+
const anyOf = Array.isArray(anyOfRaw) ? Array.from(new Set(anyOfRaw.map((v) => String(v)))).sort() : [];
|
|
252
|
+
return { facetKey, anyOf };
|
|
253
|
+
}).filter((c) => c.facetKey && c.anyOf.length > 0).sort((a, b) => a.facetKey.localeCompare(b.facetKey));
|
|
254
|
+
if (clauses.length === 0) return null;
|
|
255
|
+
return { all: clauses };
|
|
256
|
+
};
|
|
257
|
+
var fnv1a = (s) => {
|
|
258
|
+
let h = 2166136261;
|
|
259
|
+
for (let i = 0; i < s.length; i += 1) {
|
|
260
|
+
h ^= s.charCodeAt(i);
|
|
261
|
+
h = Math.imul(h, 16777619);
|
|
262
|
+
}
|
|
263
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
264
|
+
};
|
|
265
|
+
var ruleHash = (rule) => {
|
|
266
|
+
const norm = normaliseRule(rule);
|
|
267
|
+
if (!norm) return null;
|
|
268
|
+
return fnv1a(JSON.stringify(norm));
|
|
269
|
+
};
|
|
270
|
+
var summariseRule = (rule) => {
|
|
271
|
+
const norm = normaliseRule(rule);
|
|
272
|
+
if (!norm) return "No rule";
|
|
273
|
+
return norm.all.map((c) => `${c.facetKey}=${c.anyOf.join(",")}`).join(" \xB7 ");
|
|
274
|
+
};
|
|
275
|
+
var rulesEqual = (a, b) => ruleHash(a) === ruleHash(b);
|
|
276
|
+
|
|
227
277
|
// src/components/RecordsAdmin/data/resolveRecord.ts
|
|
228
278
|
var resolveRecord = async (args) => {
|
|
229
279
|
const target = parsedRefToTarget(args.target);
|
|
@@ -449,6 +499,70 @@ var useScopeProbe = ({ SL, collectionId, admin = true, enabled = true }) => {
|
|
|
449
499
|
error: query.error ?? null
|
|
450
500
|
};
|
|
451
501
|
};
|
|
502
|
+
var QK_BASE = ["records-admin", "scope-counts"];
|
|
503
|
+
var classify = (rec) => {
|
|
504
|
+
const hasRule = !!rec.facetRule;
|
|
505
|
+
if (hasRule) return "rule";
|
|
506
|
+
const productId = rec.productId ?? void 0;
|
|
507
|
+
const variantId = rec.variantId ?? void 0;
|
|
508
|
+
const batchId = rec.batchId ?? void 0;
|
|
509
|
+
if (batchId) return "batch";
|
|
510
|
+
if (variantId) return "variant";
|
|
511
|
+
if (productId) return "product";
|
|
512
|
+
return "collection";
|
|
513
|
+
};
|
|
514
|
+
var useScopeCounts = (args) => {
|
|
515
|
+
const { ctx, enabled = true, maxRecords = 500, pageSize = 100 } = args;
|
|
516
|
+
const queryKey = useMemo(
|
|
517
|
+
() => [...QK_BASE, ctx.collectionId, ctx.appId, ctx.recordType ?? null],
|
|
518
|
+
[ctx.collectionId, ctx.appId, ctx.recordType]
|
|
519
|
+
);
|
|
520
|
+
const query = useQuery({
|
|
521
|
+
queryKey,
|
|
522
|
+
enabled: enabled && !!ctx.collectionId && !!ctx.appId,
|
|
523
|
+
staleTime: 3e4,
|
|
524
|
+
queryFn: async () => {
|
|
525
|
+
const all = [];
|
|
526
|
+
let offset = 0;
|
|
527
|
+
let truncated = false;
|
|
528
|
+
for (let page = 0; page < Math.ceil(maxRecords / pageSize); page += 1) {
|
|
529
|
+
const res = await listRecords(ctx, { limit: pageSize, offset });
|
|
530
|
+
all.push(...res.data);
|
|
531
|
+
if (!res.hasMore) break;
|
|
532
|
+
offset += res.data.length;
|
|
533
|
+
if (all.length >= maxRecords) {
|
|
534
|
+
truncated = true;
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return { records: all, truncated };
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
const result = useMemo(() => {
|
|
542
|
+
const records = query.data?.records ?? [];
|
|
543
|
+
const truncated = query.data?.truncated ?? false;
|
|
544
|
+
const counts = {
|
|
545
|
+
collection: 0,
|
|
546
|
+
product: 0,
|
|
547
|
+
variant: 0,
|
|
548
|
+
batch: 0,
|
|
549
|
+
facet: 0,
|
|
550
|
+
rule: 0,
|
|
551
|
+
all: 0
|
|
552
|
+
};
|
|
553
|
+
for (const rec of records) counts[classify(rec)] += 1;
|
|
554
|
+
counts.all = records.length;
|
|
555
|
+
return {
|
|
556
|
+
counts,
|
|
557
|
+
total: records.length,
|
|
558
|
+
isLoading: query.isLoading,
|
|
559
|
+
error: query.error ?? null,
|
|
560
|
+
truncated
|
|
561
|
+
};
|
|
562
|
+
}, [query.data, query.isLoading, query.error]);
|
|
563
|
+
return result;
|
|
564
|
+
};
|
|
565
|
+
var scopeCountsQueryKey = (collectionId, appId, recordType) => [...QK_BASE, collectionId, appId, recordType ?? null];
|
|
452
566
|
var defaultClassify = (r) => {
|
|
453
567
|
if (!r.data) return "empty";
|
|
454
568
|
const keys = Object.keys(r.data);
|
|
@@ -457,6 +571,7 @@ var defaultClassify = (r) => {
|
|
|
457
571
|
};
|
|
458
572
|
var matchesScope = (kind, rec, _p) => {
|
|
459
573
|
const hasRule = !!rec.facetRule;
|
|
574
|
+
if (kind === "all") return true;
|
|
460
575
|
if (kind === "rule") return hasRule;
|
|
461
576
|
if (hasRule) return false;
|
|
462
577
|
const productId = rec.productId ?? void 0;
|
|
@@ -479,14 +594,14 @@ var matchesContext = (p, ctx) => {
|
|
|
479
594
|
return true;
|
|
480
595
|
};
|
|
481
596
|
var toSummary = (rec) => {
|
|
482
|
-
const ref = rec.ref ?? "";
|
|
483
597
|
const productId = rec.productId ?? void 0;
|
|
484
598
|
const variantId = rec.variantId ?? void 0;
|
|
485
599
|
const batchId = rec.batchId ?? void 0;
|
|
486
600
|
const proofId = rec.proofId ?? void 0;
|
|
601
|
+
const synthRaw = batchId ? `product:${productId ?? ""}/batch:${batchId}` : variantId ? `product:${productId ?? ""}/variant:${variantId}` : productId ? `product:${productId}` : "";
|
|
487
602
|
const scope = {
|
|
488
603
|
kind: batchId ? "batch" : variantId ? "variant" : productId ? "product" : "collection",
|
|
489
|
-
raw:
|
|
604
|
+
raw: synthRaw,
|
|
490
605
|
productId,
|
|
491
606
|
variantId,
|
|
492
607
|
batchId,
|
|
@@ -495,10 +610,13 @@ var toSummary = (rec) => {
|
|
|
495
610
|
const facetRule = rec.facetRule ?? null;
|
|
496
611
|
if (facetRule) scope.kind = "rule";
|
|
497
612
|
const ruleLabel = facetRule && facetRule.all && facetRule.all.length > 0 ? facetRule.all.length === 1 ? "Rule \xB7 1 facet" : `Rule \xB7 ${facetRule.all.length} facets` : null;
|
|
498
|
-
const fallbackLabel = scope.batchId ?? scope.variantId ?? scope.productId ?? ruleLabel ??
|
|
613
|
+
const fallbackLabel = scope.batchId ?? scope.variantId ?? scope.productId ?? ruleLabel ?? "Global record";
|
|
499
614
|
return {
|
|
500
615
|
id: rec.id,
|
|
501
|
-
ref
|
|
616
|
+
// `RecordSummary.ref` is preserved on the type for host display only
|
|
617
|
+
// (e.g. row renderers that want to show whatever the host put in
|
|
618
|
+
// `record.ref`). Framework code never uses it.
|
|
619
|
+
ref: rec.ref ?? "",
|
|
502
620
|
scope,
|
|
503
621
|
data: rec.data ?? null,
|
|
504
622
|
status: "configured",
|
|
@@ -507,14 +625,14 @@ var toSummary = (rec) => {
|
|
|
507
625
|
facetRule
|
|
508
626
|
};
|
|
509
627
|
};
|
|
510
|
-
var
|
|
628
|
+
var QK_BASE2 = ["records-admin", "list"];
|
|
511
629
|
var useRecordList = (args) => {
|
|
512
630
|
const {
|
|
513
631
|
ctx,
|
|
514
632
|
scopeKind,
|
|
515
633
|
search = "",
|
|
516
634
|
filter = "all",
|
|
517
|
-
classify:
|
|
635
|
+
classify: classify3,
|
|
518
636
|
enabled = true,
|
|
519
637
|
scaffolder,
|
|
520
638
|
contextScope,
|
|
@@ -523,7 +641,7 @@ var useRecordList = (args) => {
|
|
|
523
641
|
const queryClient = useQueryClient();
|
|
524
642
|
const queryKey = useMemo(
|
|
525
643
|
() => [
|
|
526
|
-
...
|
|
644
|
+
...QK_BASE2,
|
|
527
645
|
ctx.collectionId,
|
|
528
646
|
ctx.appId,
|
|
529
647
|
ctx.recordType,
|
|
@@ -549,9 +667,9 @@ var useRecordList = (args) => {
|
|
|
549
667
|
const [scaffolded, setScaffolded] = useState(null);
|
|
550
668
|
const rawItems = useMemo(() => {
|
|
551
669
|
const all = query.data?.pages.flatMap((p) => p.data) ?? [];
|
|
552
|
-
const cls =
|
|
670
|
+
const cls = classify3 ?? defaultClassify;
|
|
553
671
|
return all.map((rec) => ({ rec, summary: toSummary(rec) })).filter(({ rec, summary }) => matchesScope(scopeKind, rec, summary.scope)).filter(({ summary }) => matchesContext(summary.scope, contextScope)).map(({ summary }) => ({ ...summary, status: cls(summary) }));
|
|
554
|
-
}, [query.data, scopeKind,
|
|
672
|
+
}, [query.data, scopeKind, classify3, contextScope]);
|
|
555
673
|
useEffect(() => {
|
|
556
674
|
if (!scaffolder) {
|
|
557
675
|
setScaffolded(null);
|
|
@@ -571,7 +689,7 @@ var useRecordList = (args) => {
|
|
|
571
689
|
if (filter !== "all") out = out.filter((r) => r.status === filter);
|
|
572
690
|
if (search.trim()) {
|
|
573
691
|
const q = search.trim().toLowerCase();
|
|
574
|
-
out = out.filter((r) => `${r.label} ${r.subtitle ?? ""}
|
|
692
|
+
out = out.filter((r) => `${r.label} ${r.subtitle ?? ""}`.toLowerCase().includes(q));
|
|
575
693
|
}
|
|
576
694
|
return out;
|
|
577
695
|
}, [items, filter, search]);
|
|
@@ -582,7 +700,7 @@ var useRecordList = (args) => {
|
|
|
582
700
|
empty: items.filter((r) => r.status === "empty").length
|
|
583
701
|
}), [items]);
|
|
584
702
|
const refetch = useCallback(() => {
|
|
585
|
-
queryClient.invalidateQueries({ queryKey: [...
|
|
703
|
+
queryClient.invalidateQueries({ queryKey: [...QK_BASE2, ctx.collectionId, ctx.appId, ctx.recordType] });
|
|
586
704
|
}, [queryClient, ctx.collectionId, ctx.appId, ctx.recordType]);
|
|
587
705
|
const total = query.data?.pages[query.data.pages.length - 1]?.total ?? items.length;
|
|
588
706
|
return {
|
|
@@ -603,11 +721,11 @@ var QK = ["records-admin", "facet-browse"];
|
|
|
603
721
|
var toScaffoldSummary = (facet, value) => {
|
|
604
722
|
const facetKey = facet.key ?? "";
|
|
605
723
|
const valueKey = value.key ?? "";
|
|
606
|
-
const
|
|
724
|
+
const synthScopeRaw = buildRef({ facetId: facetKey, facetValue: valueKey });
|
|
607
725
|
return {
|
|
608
726
|
id: null,
|
|
609
|
-
ref,
|
|
610
|
-
scope: parseRef(
|
|
727
|
+
ref: "",
|
|
728
|
+
scope: parseRef(synthScopeRaw),
|
|
611
729
|
data: null,
|
|
612
730
|
status: "empty",
|
|
613
731
|
label: value.name ?? valueKey ?? "Untitled value",
|
|
@@ -687,14 +805,18 @@ var useFacetBrowse = ({
|
|
|
687
805
|
);
|
|
688
806
|
}, [enabled, collectionId, hasAdminList, hasPublicList, hasAnyList, SL]);
|
|
689
807
|
const mergedItems = useMemo(() => {
|
|
690
|
-
const
|
|
808
|
+
const existingByAnchor = new Map(
|
|
809
|
+
existing.map((item) => [anchorKey(item.scope), item]).filter(([k]) => !!k)
|
|
810
|
+
);
|
|
691
811
|
const scaffolded = (query.data ?? []).flatMap(
|
|
692
812
|
(facet) => (facet.values ?? []).filter((value) => !!facet.key && !!value.key).map((value) => {
|
|
693
813
|
const scaffold = toScaffoldSummary(facet, value);
|
|
694
|
-
|
|
814
|
+
const k = anchorKey(scaffold.scope);
|
|
815
|
+
return existingByAnchor.get(k) ?? scaffold;
|
|
695
816
|
})
|
|
696
817
|
);
|
|
697
|
-
const
|
|
818
|
+
const scaffoldedKeys = new Set(scaffolded.map((s) => anchorKey(s.scope)));
|
|
819
|
+
const extras = existing.filter((item) => !scaffoldedKeys.has(anchorKey(item.scope)));
|
|
698
820
|
return [...scaffolded, ...extras];
|
|
699
821
|
}, [existing, query.data]);
|
|
700
822
|
const filteredItems = useMemo(() => {
|
|
@@ -702,7 +824,7 @@ var useFacetBrowse = ({
|
|
|
702
824
|
if (filter !== "all") next = next.filter((item) => item.status === filter);
|
|
703
825
|
if (search.trim()) {
|
|
704
826
|
const q = search.trim().toLowerCase();
|
|
705
|
-
next = next.filter((item) => `${item.label} ${item.subtitle ?? ""}
|
|
827
|
+
next = next.filter((item) => `${item.label} ${item.subtitle ?? ""}`.toLowerCase().includes(q));
|
|
706
828
|
}
|
|
707
829
|
return next;
|
|
708
830
|
}, [mergedItems, filter, search]);
|
|
@@ -950,7 +1072,7 @@ function useShellBrowser(opts) {
|
|
|
950
1072
|
probeIsLoading,
|
|
951
1073
|
selectedProductId,
|
|
952
1074
|
drillTab,
|
|
953
|
-
classify:
|
|
1075
|
+
classify: classify3
|
|
954
1076
|
} = opts;
|
|
955
1077
|
const [search, setSearch] = useState("");
|
|
956
1078
|
const [filter, setFilter] = useState("all");
|
|
@@ -968,13 +1090,13 @@ function useShellBrowser(opts) {
|
|
|
968
1090
|
search: activeScope === "product" ? search : "",
|
|
969
1091
|
enabled: activeScope === "product" && !contextScope?.productId
|
|
970
1092
|
});
|
|
971
|
-
const recordListEnabled = (activeScope === "rule" || activeScope === "collection") && !probeIsLoading;
|
|
1093
|
+
const recordListEnabled = (activeScope === "rule" || activeScope === "collection" || activeScope === "all") && !probeIsLoading;
|
|
972
1094
|
const recordList = useRecordList({
|
|
973
1095
|
ctx,
|
|
974
1096
|
scopeKind: activeScope,
|
|
975
1097
|
search,
|
|
976
1098
|
filter,
|
|
977
|
-
classify:
|
|
1099
|
+
classify: classify3,
|
|
978
1100
|
contextScope,
|
|
979
1101
|
enabled: recordListEnabled
|
|
980
1102
|
});
|
|
@@ -982,7 +1104,7 @@ function useShellBrowser(opts) {
|
|
|
982
1104
|
SL,
|
|
983
1105
|
collectionId,
|
|
984
1106
|
existing: [],
|
|
985
|
-
enabled: (activeScope === "rule" || activeScope === "collection") && !probeIsLoading
|
|
1107
|
+
enabled: (activeScope === "rule" || activeScope === "collection" || activeScope === "all") && !probeIsLoading
|
|
986
1108
|
});
|
|
987
1109
|
const variantChildren = useProductChildren({
|
|
988
1110
|
SL,
|
|
@@ -1588,7 +1710,7 @@ function useShellClipboard(args) {
|
|
|
1588
1710
|
const summaryHasData = record.data != null;
|
|
1589
1711
|
const sourceParsed = record.scope;
|
|
1590
1712
|
const compat = clipboard.entry ? checkPasteCompatibility(clipboard.entry.sourceScope, sourceParsed) : null;
|
|
1591
|
-
const sameTarget = clipboard.entry ? clipboard.entry.sourceRecordId && record.id ? clipboard.entry.sourceRecordId === record.id : clipboard.entry.sourceScope.raw === record.
|
|
1713
|
+
const sameTarget = clipboard.entry ? clipboard.entry.sourceRecordId && record.id ? clipboard.entry.sourceRecordId === record.id : clipboard.entry.sourceScope.raw === anchorKey(record.scope) : false;
|
|
1592
1714
|
return {
|
|
1593
1715
|
onCopy: summaryHasData ? () => {
|
|
1594
1716
|
const value = onCopyOverride ? onCopyOverride({ value: record.data, scope: sourceParsed }) : cloneValue(record.data);
|
|
@@ -1598,7 +1720,7 @@ function useShellClipboard(args) {
|
|
|
1598
1720
|
sourceRecordId: record.id ?? void 0,
|
|
1599
1721
|
sourceLabel: record.label
|
|
1600
1722
|
});
|
|
1601
|
-
onTelemetry?.({ type: "clipboard.copy", recordType, sourceRef: record.
|
|
1723
|
+
onTelemetry?.({ type: "clipboard.copy", recordType, sourceRef: anchorKey(record.scope) });
|
|
1602
1724
|
setNotice({
|
|
1603
1725
|
message: i18n.copyToast.replace("{name}", record.label),
|
|
1604
1726
|
variant: "copy"
|
|
@@ -1607,7 +1729,7 @@ function useShellClipboard(args) {
|
|
|
1607
1729
|
onPaste: () => {
|
|
1608
1730
|
onLeftSelectRef.current?.(record);
|
|
1609
1731
|
setPendingPasteTarget(
|
|
1610
|
-
record.id ? { kind: "record", recordId: record.id } : { kind: "anchor", ref: record.
|
|
1732
|
+
record.id ? { kind: "record", recordId: record.id } : { kind: "anchor", ref: anchorKey(record.scope) }
|
|
1611
1733
|
);
|
|
1612
1734
|
},
|
|
1613
1735
|
canPaste: !!clipboard.entry && !sameTarget && compat?.status !== "denied",
|
|
@@ -1701,7 +1823,8 @@ function useShellNavigation(args) {
|
|
|
1701
1823
|
deepLinkState,
|
|
1702
1824
|
onTelemetry,
|
|
1703
1825
|
onBeforeDelete,
|
|
1704
|
-
generateItemId
|
|
1826
|
+
generateItemId,
|
|
1827
|
+
hooks
|
|
1705
1828
|
} = args;
|
|
1706
1829
|
const buildItemUrlValue = useCallback((id) => {
|
|
1707
1830
|
if (!baseScopeRef && id.startsWith("item:")) return id;
|
|
@@ -1786,6 +1909,34 @@ function useShellNavigation(args) {
|
|
|
1786
1909
|
const ok = await onBeforeDelete(editingScope ?? parseRef(""));
|
|
1787
1910
|
if (!ok) return;
|
|
1788
1911
|
}
|
|
1912
|
+
const row = collectionItems.items.find(
|
|
1913
|
+
(it) => it.itemId === itemId || it.id === itemId
|
|
1914
|
+
);
|
|
1915
|
+
const hookCtxBase = {
|
|
1916
|
+
collectionId: ctx.collectionId,
|
|
1917
|
+
appId: ctx.appId,
|
|
1918
|
+
recordType: ctx.recordType,
|
|
1919
|
+
scope: editingScope?.kind ?? "collection",
|
|
1920
|
+
targetRef: editingScope?.raw || void 0,
|
|
1921
|
+
admin: true
|
|
1922
|
+
};
|
|
1923
|
+
const recordSummary = row ?? {
|
|
1924
|
+
id: itemId,
|
|
1925
|
+
ref: editingScope?.raw ?? "",
|
|
1926
|
+
scope: editingScope ?? parseRef(""),
|
|
1927
|
+
data: null,
|
|
1928
|
+
status: "configured",
|
|
1929
|
+
label: itemId
|
|
1930
|
+
};
|
|
1931
|
+
if (hooks?.beforeDelete) {
|
|
1932
|
+
try {
|
|
1933
|
+
const result = await hooks.beforeDelete({ ...hookCtxBase, record: recordSummary });
|
|
1934
|
+
if (result === false) return;
|
|
1935
|
+
} catch (err) {
|
|
1936
|
+
console.warn("[RecordsAdmin] item beforeDelete hook cancelled delete", err);
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1789
1940
|
try {
|
|
1790
1941
|
const { removeRecord: removeRecord2 } = await import('../../records-AYYQSP7E.js');
|
|
1791
1942
|
await removeRecord2(ctx, itemId);
|
|
@@ -1793,6 +1944,13 @@ function useShellNavigation(args) {
|
|
|
1793
1944
|
if (selectedItemId === itemId) setSelectedItemId(null);
|
|
1794
1945
|
if (selectedItemId === itemId) deepLinkState.emit({ recordId: null }, "record.close");
|
|
1795
1946
|
collectionItems.refetch();
|
|
1947
|
+
if (hooks?.afterDelete) {
|
|
1948
|
+
try {
|
|
1949
|
+
await hooks.afterDelete({ ...hookCtxBase, record: recordSummary });
|
|
1950
|
+
} catch (err) {
|
|
1951
|
+
console.warn("[RecordsAdmin] item afterDelete hook failed", err);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1796
1954
|
} catch (err) {
|
|
1797
1955
|
console.error("[RecordsAdminShell] item delete failed", err);
|
|
1798
1956
|
}
|
|
@@ -1807,7 +1965,8 @@ function useShellNavigation(args) {
|
|
|
1807
1965
|
collectionItems,
|
|
1808
1966
|
deepLinkState,
|
|
1809
1967
|
editingScope,
|
|
1810
|
-
setSelectedItemId
|
|
1968
|
+
setSelectedItemId,
|
|
1969
|
+
hooks
|
|
1811
1970
|
]);
|
|
1812
1971
|
const itemViewCtx = useMemo(() => ({
|
|
1813
1972
|
onOpen: onItemOpen,
|
|
@@ -1879,12 +2038,12 @@ var useEditingScope = (args) => {
|
|
|
1879
2038
|
const isCollection = cardinality === "collection";
|
|
1880
2039
|
const editingItemRecordId = isCollection ? selectedItemId : null;
|
|
1881
2040
|
const editingScope = useMemo(() => {
|
|
1882
|
-
if (activeScope === "rule" || activeScope === "collection") {
|
|
2041
|
+
if (activeScope === "rule" || activeScope === "collection" || activeScope === "all") {
|
|
1883
2042
|
if (activeScope === "rule" && ruleWizardStep === 2 && selectedRecordId === null) {
|
|
1884
2043
|
return { ...parseRef(""), kind: "rule", raw: "rule:__draft__" };
|
|
1885
2044
|
}
|
|
1886
2045
|
if (selectedRecordId === null) {
|
|
1887
|
-
if (activeScope === "collection" && cardinality === "collection") {
|
|
2046
|
+
if ((activeScope === "collection" || activeScope === "all") && cardinality === "collection") {
|
|
1888
2047
|
return parseRef("");
|
|
1889
2048
|
}
|
|
1890
2049
|
return null;
|
|
@@ -2113,6 +2272,25 @@ var createEditorStore = () => {
|
|
|
2113
2272
|
const listeners = /* @__PURE__ */ new Set();
|
|
2114
2273
|
let cachedList = [];
|
|
2115
2274
|
let nextOrder = 0;
|
|
2275
|
+
let hooksBundle = null;
|
|
2276
|
+
let notifier = null;
|
|
2277
|
+
const buildRecordSummary = (entry, value) => ({
|
|
2278
|
+
id: entry.recordId ?? null,
|
|
2279
|
+
ref: entry.spec.scope.raw,
|
|
2280
|
+
scope: entry.spec.scope,
|
|
2281
|
+
label: entry.label,
|
|
2282
|
+
status: "configured",
|
|
2283
|
+
data: value,
|
|
2284
|
+
facetRule: entry.facetRule
|
|
2285
|
+
});
|
|
2286
|
+
const buildHookCtxBase = (entry) => ({
|
|
2287
|
+
collectionId: entry.saveSpec.ctx.collectionId,
|
|
2288
|
+
appId: entry.saveSpec.ctx.appId,
|
|
2289
|
+
recordType: entry.saveSpec.ctx.recordType,
|
|
2290
|
+
scope: entry.spec.scope.kind,
|
|
2291
|
+
targetRef: entry.spec.scope.raw || void 0,
|
|
2292
|
+
admin: true
|
|
2293
|
+
});
|
|
2116
2294
|
const recompute = () => {
|
|
2117
2295
|
cachedList = Array.from(map.values()).sort((a, b) => a.order - b.order);
|
|
2118
2296
|
};
|
|
@@ -2266,10 +2444,35 @@ var createEditorStore = () => {
|
|
|
2266
2444
|
const entry = map.get(editorId);
|
|
2267
2445
|
if (!entry) return;
|
|
2268
2446
|
if (entry.status !== "dirty" && entry.status !== "error") return;
|
|
2269
|
-
|
|
2447
|
+
let persistedValue = entry.value;
|
|
2270
2448
|
const persistedFacetRule = entry.facetRule;
|
|
2271
2449
|
const { ctx, anchors } = entry.saveSpec;
|
|
2272
2450
|
const spec = entry.spec;
|
|
2451
|
+
const isCreate = !(entry.recordId && entry.source === "self") || !!spec.createMode;
|
|
2452
|
+
if (hooksBundle?.beforeSave) {
|
|
2453
|
+
try {
|
|
2454
|
+
const hookCtx = {
|
|
2455
|
+
...buildHookCtxBase(entry),
|
|
2456
|
+
isCreate,
|
|
2457
|
+
record: buildRecordSummary(entry, persistedValue),
|
|
2458
|
+
before: !isCreate ? buildRecordSummary(entry, entry.baseline) : void 0
|
|
2459
|
+
};
|
|
2460
|
+
const result = await hooksBundle.beforeSave(hookCtx);
|
|
2461
|
+
if (result === false) {
|
|
2462
|
+
return;
|
|
2463
|
+
}
|
|
2464
|
+
if (result && typeof result === "object") {
|
|
2465
|
+
persistedValue = persistedValue && typeof persistedValue === "object" ? { ...persistedValue, ...result } : result;
|
|
2466
|
+
}
|
|
2467
|
+
} catch (err) {
|
|
2468
|
+
notifier?.({ kind: "hook-before-save", error: err, recordLabel: entry.label });
|
|
2469
|
+
update(editorId, (e) => {
|
|
2470
|
+
e.status = "error";
|
|
2471
|
+
e.error = err;
|
|
2472
|
+
});
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2273
2476
|
update(editorId, (e) => {
|
|
2274
2477
|
e.status = "saving";
|
|
2275
2478
|
e.error = void 0;
|
|
@@ -2283,11 +2486,14 @@ var createEditorStore = () => {
|
|
|
2283
2486
|
});
|
|
2284
2487
|
} else if (spec.createMode) {
|
|
2285
2488
|
const created = await createRecord(ctx, {
|
|
2286
|
-
//
|
|
2287
|
-
//
|
|
2288
|
-
//
|
|
2289
|
-
//
|
|
2290
|
-
|
|
2489
|
+
// `record.ref` is a host-owned external handle. The framework
|
|
2490
|
+
// never writes to it — rule identity is content-addressed via
|
|
2491
|
+
// `facetRule`, record identity via `record.id` (UUID).
|
|
2492
|
+
// `spec.ref` is always `undefined` from `useShellEditorTarget`
|
|
2493
|
+
// for the same reason; we keep the prop on the spec so hosts
|
|
2494
|
+
// that wire the editor session directly can still pass an
|
|
2495
|
+
// explicit external ref if they choose.
|
|
2496
|
+
ref: spec.ref,
|
|
2291
2497
|
scope: anchors,
|
|
2292
2498
|
data: persistedValue,
|
|
2293
2499
|
facetRule: persistedFacetRule
|
|
@@ -2295,7 +2501,7 @@ var createEditorStore = () => {
|
|
|
2295
2501
|
nextRecordId = created?.id ?? nextRecordId;
|
|
2296
2502
|
} else {
|
|
2297
2503
|
const upserted = await upsertRecord(ctx, {
|
|
2298
|
-
ref: spec.
|
|
2504
|
+
ref: spec.ref,
|
|
2299
2505
|
scope: anchors,
|
|
2300
2506
|
data: persistedValue,
|
|
2301
2507
|
facetRule: persistedFacetRule
|
|
@@ -2303,6 +2509,9 @@ var createEditorStore = () => {
|
|
|
2303
2509
|
nextRecordId = upserted.record?.id ?? nextRecordId;
|
|
2304
2510
|
}
|
|
2305
2511
|
update(editorId, (e) => {
|
|
2512
|
+
if (persistedValue !== entry.value) {
|
|
2513
|
+
e.value = cloneDeep(persistedValue);
|
|
2514
|
+
}
|
|
2306
2515
|
e.baseline = cloneDeep(e.value);
|
|
2307
2516
|
e.baselineFacetRule = e.facetRule;
|
|
2308
2517
|
e.recordId = nextRecordId;
|
|
@@ -2310,6 +2519,21 @@ var createEditorStore = () => {
|
|
|
2310
2519
|
e.status = "saved";
|
|
2311
2520
|
e.error = void 0;
|
|
2312
2521
|
});
|
|
2522
|
+
if (hooksBundle?.afterSave) {
|
|
2523
|
+
const refreshed = map.get(editorId);
|
|
2524
|
+
const hookCtx = {
|
|
2525
|
+
...buildHookCtxBase(refreshed ?? entry),
|
|
2526
|
+
isCreate,
|
|
2527
|
+
record: buildRecordSummary(refreshed ?? entry, persistedValue),
|
|
2528
|
+
before: !isCreate ? buildRecordSummary(entry, entry.baseline) : void 0
|
|
2529
|
+
};
|
|
2530
|
+
try {
|
|
2531
|
+
await hooksBundle.afterSave(hookCtx);
|
|
2532
|
+
} catch (err) {
|
|
2533
|
+
notifier?.({ kind: "hook-after-save", error: err, recordLabel: entry.label });
|
|
2534
|
+
console.warn("[RecordsAdmin] afterSave hook failed", err);
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2313
2537
|
} catch (err) {
|
|
2314
2538
|
update(editorId, (e) => {
|
|
2315
2539
|
e.status = "error";
|
|
@@ -2331,9 +2555,35 @@ var createEditorStore = () => {
|
|
|
2331
2555
|
if (!entry) return;
|
|
2332
2556
|
if (entry.source !== "self") return;
|
|
2333
2557
|
if (!entry.recordId) return;
|
|
2558
|
+
if (hooksBundle?.beforeDelete) {
|
|
2559
|
+
try {
|
|
2560
|
+
const hookCtx = {
|
|
2561
|
+
...buildHookCtxBase(entry),
|
|
2562
|
+
record: buildRecordSummary(entry, entry.value)
|
|
2563
|
+
};
|
|
2564
|
+
const result = await hooksBundle.beforeDelete(hookCtx);
|
|
2565
|
+
if (result === false) return;
|
|
2566
|
+
} catch (err) {
|
|
2567
|
+
notifier?.({ kind: "hook-before-delete", error: err, recordLabel: entry.label });
|
|
2568
|
+
console.warn("[RecordsAdmin] beforeDelete hook cancelled delete", err);
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
const deletedSnapshot = hooksBundle?.afterDelete ? {
|
|
2573
|
+
...buildHookCtxBase(entry),
|
|
2574
|
+
record: buildRecordSummary(entry, entry.value)
|
|
2575
|
+
} : null;
|
|
2334
2576
|
await removeRecord(entry.saveSpec.ctx, entry.recordId);
|
|
2335
2577
|
map.delete(editorId);
|
|
2336
2578
|
emit();
|
|
2579
|
+
if (hooksBundle?.afterDelete && deletedSnapshot) {
|
|
2580
|
+
try {
|
|
2581
|
+
await hooksBundle.afterDelete(deletedSnapshot);
|
|
2582
|
+
} catch (err) {
|
|
2583
|
+
notifier?.({ kind: "hook-after-delete", error: err, recordLabel: entry.label });
|
|
2584
|
+
console.warn("[RecordsAdmin] afterDelete hook failed", err);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2337
2587
|
},
|
|
2338
2588
|
enforcePoolLimit(max, exceptEditorId) {
|
|
2339
2589
|
const alive = Array.from(map.values());
|
|
@@ -2354,6 +2604,12 @@ var createEditorStore = () => {
|
|
|
2354
2604
|
return () => {
|
|
2355
2605
|
listeners.delete(listener);
|
|
2356
2606
|
};
|
|
2607
|
+
},
|
|
2608
|
+
setHooks(hooks) {
|
|
2609
|
+
hooksBundle = hooks ?? null;
|
|
2610
|
+
},
|
|
2611
|
+
setNotifier(notify2) {
|
|
2612
|
+
notifier = notify2 ?? null;
|
|
2357
2613
|
}
|
|
2358
2614
|
};
|
|
2359
2615
|
};
|
|
@@ -2375,10 +2631,14 @@ var EditorSessionProvider = ({
|
|
|
2375
2631
|
ctx,
|
|
2376
2632
|
children,
|
|
2377
2633
|
maxOpenEditors = 8,
|
|
2378
|
-
defaultValueFactory
|
|
2634
|
+
defaultValueFactory,
|
|
2635
|
+
hooks,
|
|
2636
|
+
onHookNotice
|
|
2379
2637
|
}) => {
|
|
2380
2638
|
const storeRef = useRef(null);
|
|
2381
2639
|
if (!storeRef.current) storeRef.current = createEditorStore();
|
|
2640
|
+
storeRef.current.setHooks(hooks ?? null);
|
|
2641
|
+
storeRef.current.setNotifier(onHookNotice ?? null);
|
|
2382
2642
|
const currentRef = useRef(void 0);
|
|
2383
2643
|
const listenersRef = useRef(/* @__PURE__ */ new Set());
|
|
2384
2644
|
const subscribeCurrent = useCallback((cb) => {
|
|
@@ -2706,7 +2966,14 @@ function useShellEditorTarget(args) {
|
|
|
2706
2966
|
// would update the rule record in place.
|
|
2707
2967
|
recordId: createMode ? void 0 : resolved.recordId,
|
|
2708
2968
|
createMode,
|
|
2709
|
-
|
|
2969
|
+
// We deliberately do NOT propagate `editingTargetScope.raw` (e.g.
|
|
2970
|
+
// `rule:<ulid>`) into `spec.ref`. `record.ref` is a host-owned
|
|
2971
|
+
// EXTERNAL HANDLE — this framework must remain agnostic about how
|
|
2972
|
+
// hosts use it. Rule identity is content-addressed via `facetRule`
|
|
2973
|
+
// (see `data/ruleHash`); record identity is the UUID `record.id`.
|
|
2974
|
+
// Whatever the SDK chooses to back-fill in `ref` is the SDK's
|
|
2975
|
+
// problem, not ours, and we never write to it.
|
|
2976
|
+
ref: void 0,
|
|
2710
2977
|
initialFacetRule,
|
|
2711
2978
|
label,
|
|
2712
2979
|
draftKey,
|
|
@@ -2813,7 +3080,8 @@ var LABELS = {
|
|
|
2813
3080
|
facet: "Shared",
|
|
2814
3081
|
variant: "Variants",
|
|
2815
3082
|
batch: "Batches",
|
|
2816
|
-
rule: "Rules"
|
|
3083
|
+
rule: "Rules",
|
|
3084
|
+
all: "All"
|
|
2817
3085
|
};
|
|
2818
3086
|
var ScopeTabs = ({
|
|
2819
3087
|
scopes,
|
|
@@ -2821,6 +3089,7 @@ var ScopeTabs = ({
|
|
|
2821
3089
|
onChange,
|
|
2822
3090
|
loading = false,
|
|
2823
3091
|
counts,
|
|
3092
|
+
tooltips,
|
|
2824
3093
|
icons
|
|
2825
3094
|
}) => {
|
|
2826
3095
|
const iconMap = icons ?? DEFAULT_ICONS.scope;
|
|
@@ -2828,6 +3097,7 @@ var ScopeTabs = ({
|
|
|
2828
3097
|
const Icon = iconMap[s] ?? DEFAULT_ICONS.scope[s];
|
|
2829
3098
|
const isActive = active === s;
|
|
2830
3099
|
const count = counts?.[s];
|
|
3100
|
+
const tooltip = tooltips?.[s];
|
|
2831
3101
|
return /* @__PURE__ */ jsxs(
|
|
2832
3102
|
"button",
|
|
2833
3103
|
{
|
|
@@ -2837,6 +3107,7 @@ var ScopeTabs = ({
|
|
|
2837
3107
|
onClick: () => onChange(s),
|
|
2838
3108
|
disabled: loading,
|
|
2839
3109
|
className: "ra-tab",
|
|
3110
|
+
title: tooltip,
|
|
2840
3111
|
children: [
|
|
2841
3112
|
/* @__PURE__ */ jsx(Icon, { className: cn("ra-tab-icon w-3.5 h-3.5", loading && "animate-pulse") }),
|
|
2842
3113
|
/* @__PURE__ */ jsx("span", { children: LABELS[s] }),
|
|
@@ -3069,6 +3340,15 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
|
|
|
3069
3340
|
compact && /* @__PURE__ */ jsx(StatusIcon, { status: record.status, size: "0.85rem" }),
|
|
3070
3341
|
!compact && record.scope.kind && record.scope.kind !== "collection" && /* @__PURE__ */ jsx("span", { className: "ra-row-scope", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ScopeIcon, { className: "w-3 h-3" }) }),
|
|
3071
3342
|
record.badges?.slice(0, 1).map((b, i) => /* @__PURE__ */ jsx("span", { className: "ra-chip", "data-tone": "muted", children: b.label }, `${b.label}-${i}`)),
|
|
3343
|
+
record.facetRule && !compact && /* @__PURE__ */ jsx(
|
|
3344
|
+
"span",
|
|
3345
|
+
{
|
|
3346
|
+
className: "ra-row-rule-pip",
|
|
3347
|
+
title: ruleSummary ? `Targeted: ${ruleSummary}` : "This record has targeting rules",
|
|
3348
|
+
"aria-label": ruleSummary ? `Targeted: ${ruleSummary}` : "This record has targeting rules",
|
|
3349
|
+
children: /* @__PURE__ */ jsx(SlidersHorizontal, { className: "w-3 h-3", "aria-hidden": "true" })
|
|
3350
|
+
}
|
|
3351
|
+
),
|
|
3072
3352
|
hasError ? /* @__PURE__ */ jsx(
|
|
3073
3353
|
"span",
|
|
3074
3354
|
{
|
|
@@ -3117,22 +3397,24 @@ var RecordList = ({
|
|
|
3117
3397
|
presentation = "list",
|
|
3118
3398
|
renderListRow,
|
|
3119
3399
|
groupBy,
|
|
3400
|
+
renderGroupActions,
|
|
3120
3401
|
rowClipboard,
|
|
3121
3402
|
i18n
|
|
3122
3403
|
}) => {
|
|
3123
3404
|
const buildCtx = (item) => {
|
|
3124
3405
|
const cb = rowClipboard ? rowClipboard(item) : null;
|
|
3406
|
+
const itemAnchorKey = anchorKey(item.scope);
|
|
3125
3407
|
const idMatch = selectedId != null && item.id != null && item.id === selectedId;
|
|
3126
|
-
const anchorMatch = selectedAnchorKey != null &&
|
|
3408
|
+
const anchorMatch = selectedAnchorKey != null && !!itemAnchorKey && itemAnchorKey === selectedAnchorKey;
|
|
3127
3409
|
const dirtyIdMatch = dirtyId != null && item.id != null && item.id === dirtyId;
|
|
3128
|
-
const dirtyAnchorMatch = dirtyAnchorKey != null &&
|
|
3410
|
+
const dirtyAnchorMatch = dirtyAnchorKey != null && !!itemAnchorKey && itemAnchorKey === dirtyAnchorKey;
|
|
3129
3411
|
const idInStore = !!(item.id && dirtyKeys?.has(item.id));
|
|
3130
|
-
const
|
|
3131
|
-
const errorInStore = !!(item.id && errorKeys?.has(item.id) ||
|
|
3412
|
+
const anchorInStore = !!(itemAnchorKey && dirtyKeys?.has(itemAnchorKey));
|
|
3413
|
+
const errorInStore = !!(item.id && errorKeys?.has(item.id) || itemAnchorKey && errorKeys?.has(itemAnchorKey));
|
|
3132
3414
|
return {
|
|
3133
3415
|
selected: idMatch || anchorMatch,
|
|
3134
3416
|
onSelect: () => onSelect(item),
|
|
3135
|
-
isDirty: dirtyIdMatch || dirtyAnchorMatch || idInStore ||
|
|
3417
|
+
isDirty: dirtyIdMatch || dirtyAnchorMatch || idInStore || anchorInStore,
|
|
3136
3418
|
hasError: errorInStore,
|
|
3137
3419
|
i18n,
|
|
3138
3420
|
...cb ?? {}
|
|
@@ -3154,19 +3436,28 @@ var RecordList = ({
|
|
|
3154
3436
|
}, [items, groupBy]);
|
|
3155
3437
|
const renderItems = (rows) => {
|
|
3156
3438
|
const compact = presentation === "compact";
|
|
3157
|
-
return /* @__PURE__ */ jsx("ul", { children: rows.map((item) => {
|
|
3439
|
+
return /* @__PURE__ */ jsx("ul", { children: rows.map((item, idx) => {
|
|
3158
3440
|
const ctx = buildCtx(item);
|
|
3159
|
-
|
|
3441
|
+
const key = item.id ?? (anchorKey(item.scope) || `pos:${idx}`);
|
|
3442
|
+
return /* @__PURE__ */ jsx("li", { children: renderListRow ? renderListRow(item, ctx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: item, ctx, compact }) }, key);
|
|
3160
3443
|
}) });
|
|
3161
3444
|
};
|
|
3162
3445
|
if (groups) {
|
|
3163
|
-
return /* @__PURE__ */ jsx(
|
|
3446
|
+
return /* @__PURE__ */ jsx(
|
|
3447
|
+
GroupedList,
|
|
3448
|
+
{
|
|
3449
|
+
groups,
|
|
3450
|
+
renderItems,
|
|
3451
|
+
renderGroupActions
|
|
3452
|
+
}
|
|
3453
|
+
);
|
|
3164
3454
|
}
|
|
3165
3455
|
return renderItems(items);
|
|
3166
3456
|
};
|
|
3167
3457
|
var GroupedList = ({
|
|
3168
3458
|
groups,
|
|
3169
|
-
renderItems
|
|
3459
|
+
renderItems,
|
|
3460
|
+
renderGroupActions
|
|
3170
3461
|
}) => {
|
|
3171
3462
|
const Chevron = DEFAULT_ICONS.group.chevron;
|
|
3172
3463
|
const [open, setOpen] = useState(
|
|
@@ -3174,22 +3465,34 @@ var GroupedList = ({
|
|
|
3174
3465
|
);
|
|
3175
3466
|
return /* @__PURE__ */ jsx(Fragment, { children: groups.map((g) => {
|
|
3176
3467
|
const isOpen = open[g.key] !== false;
|
|
3468
|
+
const actions = renderGroupActions?.({ key: g.key, label: g.label, items: g.items });
|
|
3177
3469
|
return /* @__PURE__ */ jsxs("section", { className: "ra-group", "data-open": isOpen ? "true" : "false", children: [
|
|
3178
|
-
/* @__PURE__ */ jsxs(
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3470
|
+
/* @__PURE__ */ jsxs("div", { className: "ra-group-summary-row flex items-center", children: [
|
|
3471
|
+
/* @__PURE__ */ jsxs(
|
|
3472
|
+
"button",
|
|
3473
|
+
{
|
|
3474
|
+
type: "button",
|
|
3475
|
+
className: "ra-group-summary flex-1 min-w-0",
|
|
3476
|
+
onClick: () => setOpen((s) => ({ ...s, [g.key]: !isOpen })),
|
|
3477
|
+
"aria-expanded": isOpen,
|
|
3478
|
+
children: [
|
|
3479
|
+
/* @__PURE__ */ jsx(Chevron, { className: "ra-group-chevron w-3 h-3" }),
|
|
3480
|
+
g.icon,
|
|
3481
|
+
/* @__PURE__ */ jsx("span", { className: "ra-group-name", children: g.label }),
|
|
3482
|
+
/* @__PURE__ */ jsx("span", { className: "ra-group-count", children: g.items.length })
|
|
3483
|
+
]
|
|
3484
|
+
}
|
|
3485
|
+
),
|
|
3486
|
+
actions ? /* @__PURE__ */ jsx(
|
|
3487
|
+
"div",
|
|
3488
|
+
{
|
|
3489
|
+
className: "ra-group-actions flex-shrink-0 pr-2",
|
|
3490
|
+
onClick: (e) => e.stopPropagation(),
|
|
3491
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
3492
|
+
children: actions
|
|
3493
|
+
}
|
|
3494
|
+
) : null
|
|
3495
|
+
] }),
|
|
3193
3496
|
/* @__PURE__ */ jsx("div", { className: "ra-group-body", children: renderItems(g.items) })
|
|
3194
3497
|
] }, g.key);
|
|
3195
3498
|
}) });
|
|
@@ -3442,7 +3745,7 @@ function useItemViewPref(args) {
|
|
|
3442
3745
|
}, [key]);
|
|
3443
3746
|
return [value, set];
|
|
3444
3747
|
}
|
|
3445
|
-
var
|
|
3748
|
+
var QK_BASE3 = ["records-admin", "collection-items"];
|
|
3446
3749
|
var canonicalFacetRule = (rule) => {
|
|
3447
3750
|
if (!rule || !Array.isArray(rule.all)) return "";
|
|
3448
3751
|
const clauses = rule.all.map((c) => ({
|
|
@@ -3475,23 +3778,24 @@ function useCollectionItems(args) {
|
|
|
3475
3778
|
ctx,
|
|
3476
3779
|
scope,
|
|
3477
3780
|
facetRule,
|
|
3781
|
+
includeAll = false,
|
|
3478
3782
|
pageSize = 100,
|
|
3479
3783
|
toSummary: toSummary2 = defaultToSummary,
|
|
3480
3784
|
enabled = true
|
|
3481
3785
|
} = args;
|
|
3482
3786
|
const queryClient = useQueryClient();
|
|
3483
|
-
const scopeRef = scope?.raw ?? "";
|
|
3787
|
+
const scopeRef = includeAll ? "__all__" : scope?.raw ?? "";
|
|
3484
3788
|
const ruleSignature = useMemo(
|
|
3485
|
-
() => scope?.kind === "rule" ? canonicalFacetRule(facetRule) : "",
|
|
3486
|
-
[scope?.kind, facetRule]
|
|
3789
|
+
() => !includeAll && scope?.kind === "rule" ? canonicalFacetRule(facetRule) : "",
|
|
3790
|
+
[includeAll, scope?.kind, facetRule]
|
|
3487
3791
|
);
|
|
3488
3792
|
const queryKey = useMemo(
|
|
3489
|
-
() => [...
|
|
3793
|
+
() => [...QK_BASE3, ctx.collectionId, ctx.appId, ctx.recordType, scopeRef, ruleSignature],
|
|
3490
3794
|
[ctx.collectionId, ctx.appId, ctx.recordType, scopeRef, ruleSignature]
|
|
3491
3795
|
);
|
|
3492
3796
|
const query = useInfiniteQuery({
|
|
3493
3797
|
queryKey,
|
|
3494
|
-
enabled: enabled && !!scope,
|
|
3798
|
+
enabled: enabled && (includeAll || !!scope),
|
|
3495
3799
|
initialPageParam: 0,
|
|
3496
3800
|
queryFn: async ({ pageParam }) => {
|
|
3497
3801
|
const offset = pageParam;
|
|
@@ -3505,12 +3809,13 @@ function useCollectionItems(args) {
|
|
|
3505
3809
|
staleTime: 15e3
|
|
3506
3810
|
});
|
|
3507
3811
|
const items = useMemo(() => {
|
|
3508
|
-
if (!scope) return [];
|
|
3812
|
+
if (!includeAll && !scope) return [];
|
|
3509
3813
|
const all = query.data?.pages.flatMap((p) => p.data) ?? [];
|
|
3510
|
-
const relevant = all.filter((rec) => recordMatchesScope(rec, scope, ruleSignature));
|
|
3814
|
+
const relevant = includeAll ? all : all.filter((rec) => recordMatchesScope(rec, scope, ruleSignature));
|
|
3511
3815
|
return relevant.map((rec) => {
|
|
3512
3816
|
const ref = rec.ref ?? "";
|
|
3513
3817
|
const parsed = parseRef(ref);
|
|
3818
|
+
const recFacetRule = rec.facetRule ?? null;
|
|
3514
3819
|
const stableItemId = rec.id;
|
|
3515
3820
|
const base = {
|
|
3516
3821
|
id: rec.id,
|
|
@@ -3520,11 +3825,12 @@ function useCollectionItems(args) {
|
|
|
3520
3825
|
status: rec.data ? "configured" : "empty",
|
|
3521
3826
|
label: stableItemId,
|
|
3522
3827
|
updatedAt: rec.updatedAt,
|
|
3523
|
-
itemId: stableItemId
|
|
3828
|
+
itemId: stableItemId,
|
|
3829
|
+
facetRule: recFacetRule
|
|
3524
3830
|
};
|
|
3525
3831
|
return toSummary2(rec, base);
|
|
3526
3832
|
}).filter((x) => x !== null);
|
|
3527
|
-
}, [query.data, toSummary2, scope, ruleSignature]);
|
|
3833
|
+
}, [query.data, toSummary2, scope, ruleSignature, includeAll]);
|
|
3528
3834
|
const refetch = useCallback(() => {
|
|
3529
3835
|
queryClient.invalidateQueries({ queryKey });
|
|
3530
3836
|
}, [queryClient, queryKey]);
|
|
@@ -3700,7 +4006,7 @@ var createPostMessageDeepLinkAdapter = (paramNames) => {
|
|
|
3700
4006
|
|
|
3701
4007
|
// src/components/RecordsAdmin/hooks/useDeepLinkState.ts
|
|
3702
4008
|
var SMART_PUSH = ["record.open", "record.close", "scope"];
|
|
3703
|
-
var
|
|
4009
|
+
var classify2 = (mode, kind) => {
|
|
3704
4010
|
if (mode === "push") return "push";
|
|
3705
4011
|
if (mode === "replace") return "replace";
|
|
3706
4012
|
return SMART_PUSH.includes(kind) ? "push" : "replace";
|
|
@@ -3737,7 +4043,7 @@ function useDeepLinkState(options) {
|
|
|
3737
4043
|
}, [adapter]);
|
|
3738
4044
|
const emit = useCallback((partial, kind) => {
|
|
3739
4045
|
if (!adapter) return;
|
|
3740
|
-
const mode =
|
|
4046
|
+
const mode = classify2(history, kind);
|
|
3741
4047
|
adapter.write(partial, mode);
|
|
3742
4048
|
setUrlState((prev) => ({ ...prev, ...partial }));
|
|
3743
4049
|
}, [adapter, history]);
|
|
@@ -3914,6 +4220,7 @@ function RecordEditor({
|
|
|
3914
4220
|
children,
|
|
3915
4221
|
preview,
|
|
3916
4222
|
targeting,
|
|
4223
|
+
targetingControl,
|
|
3917
4224
|
bulkActions,
|
|
3918
4225
|
footerExtra,
|
|
3919
4226
|
onBeforeDelete,
|
|
@@ -3932,8 +4239,15 @@ function RecordEditor({
|
|
|
3932
4239
|
const DeleteIcon = actionIcons?.delete;
|
|
3933
4240
|
const showInherited = ctx.source === "inherited";
|
|
3934
4241
|
const showEmpty = ctx.source === "empty";
|
|
4242
|
+
const hasBreadcrumb = (() => {
|
|
4243
|
+
const s = ctx.scope;
|
|
4244
|
+
return Boolean(s?.facetId || s?.productId || s?.variantId || s?.batchId);
|
|
4245
|
+
})();
|
|
4246
|
+
const hasLeftContent = Boolean(headerLabel) || hasBreadcrumb;
|
|
4247
|
+
const hasRightContent = showInherited || showEmpty || Boolean(headerMeta) || Boolean(bulkActions) || Boolean(targetingControl);
|
|
4248
|
+
const showHeader = hasLeftContent || hasRightContent;
|
|
3935
4249
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
3936
|
-
/* @__PURE__ */ jsxs(
|
|
4250
|
+
showHeader && /* @__PURE__ */ jsxs(
|
|
3937
4251
|
"header",
|
|
3938
4252
|
{
|
|
3939
4253
|
className: "sticky top-0 z-40 px-5 py-3 border-b flex items-start justify-between gap-3",
|
|
@@ -4005,7 +4319,8 @@ function RecordEditor({
|
|
|
4005
4319
|
children: headerMeta
|
|
4006
4320
|
}
|
|
4007
4321
|
),
|
|
4008
|
-
bulkActions && /* @__PURE__ */ jsx(BulkActionsMenu, { ...bulkActions, i18n })
|
|
4322
|
+
bulkActions && /* @__PURE__ */ jsx(BulkActionsMenu, { ...bulkActions, i18n }),
|
|
4323
|
+
targetingControl
|
|
4009
4324
|
] })
|
|
4010
4325
|
]
|
|
4011
4326
|
}
|
|
@@ -4114,7 +4429,7 @@ function RecordEditor({
|
|
|
4114
4429
|
)
|
|
4115
4430
|
] });
|
|
4116
4431
|
}
|
|
4117
|
-
function
|
|
4432
|
+
function summariseRule2(clauses, facets) {
|
|
4118
4433
|
let valueCount = 0;
|
|
4119
4434
|
const parts = clauses.map((c) => {
|
|
4120
4435
|
const facet = facets.find((f) => f.key === c.facetKey);
|
|
@@ -4127,20 +4442,36 @@ function summariseRule(clauses, facets) {
|
|
|
4127
4442
|
});
|
|
4128
4443
|
return { text: parts.join(" \xB7 "), clauseCount: clauses.length, valueCount };
|
|
4129
4444
|
}
|
|
4130
|
-
function RecordTargeting({
|
|
4445
|
+
function RecordTargeting({
|
|
4446
|
+
SL,
|
|
4447
|
+
collectionId,
|
|
4448
|
+
appId,
|
|
4449
|
+
ctx,
|
|
4450
|
+
forceOpen,
|
|
4451
|
+
onOpenChange
|
|
4452
|
+
}) {
|
|
4131
4453
|
const facets = useFacets(collectionId, void 0);
|
|
4132
4454
|
const preview = useRulePreview({ SL, collectionId, appId, rule: ctx.facetRule ?? null });
|
|
4133
4455
|
const clauses = ctx.facetRule?.all ?? [];
|
|
4134
4456
|
const hasAnyClause = clauses.length > 0;
|
|
4457
|
+
const hasInflightRule = ctx.facetRule != null;
|
|
4135
4458
|
const isInvalid = ctx.canSave === false;
|
|
4136
|
-
const shouldAutoOpen = !hasAnyClause || isInvalid;
|
|
4459
|
+
const shouldAutoOpen = hasInflightRule && (!hasAnyClause || isInvalid);
|
|
4137
4460
|
const [open, setOpen] = useState(shouldAutoOpen);
|
|
4138
4461
|
useEffect(() => {
|
|
4139
4462
|
if (shouldAutoOpen) setOpen(true);
|
|
4140
4463
|
}, [shouldAutoOpen]);
|
|
4141
|
-
|
|
4464
|
+
useEffect(() => {
|
|
4465
|
+
if (forceOpen) setOpen(true);
|
|
4466
|
+
}, [forceOpen]);
|
|
4467
|
+
const summary = useMemo(() => summariseRule2(clauses, facets), [clauses, facets]);
|
|
4142
4468
|
if (!ctx.onFacetRuleChange) return null;
|
|
4469
|
+
if (!hasInflightRule) return null;
|
|
4143
4470
|
const headerTone = isInvalid ? { color: "hsl(var(--ra-danger, 0 70% 45%))" } : { color: "hsl(var(--ra-muted-text))" };
|
|
4471
|
+
const setOpenAndNotify = (next) => {
|
|
4472
|
+
setOpen(next);
|
|
4473
|
+
onOpenChange?.(next);
|
|
4474
|
+
};
|
|
4144
4475
|
return /* @__PURE__ */ jsxs(
|
|
4145
4476
|
"div",
|
|
4146
4477
|
{
|
|
@@ -4150,12 +4481,12 @@ function RecordTargeting({ SL, collectionId, appId, ctx }) {
|
|
|
4150
4481
|
background: "hsl(var(--ra-muted) / 0.3)"
|
|
4151
4482
|
},
|
|
4152
4483
|
children: [
|
|
4153
|
-
/* @__PURE__ */ jsxs(
|
|
4484
|
+
/* @__PURE__ */ jsx("div", { className: "w-full flex items-center gap-2 px-3 py-2", children: /* @__PURE__ */ jsxs(
|
|
4154
4485
|
"button",
|
|
4155
4486
|
{
|
|
4156
4487
|
type: "button",
|
|
4157
|
-
onClick: () =>
|
|
4158
|
-
className: "
|
|
4488
|
+
onClick: () => setOpenAndNotify(!open),
|
|
4489
|
+
className: "flex items-center gap-2 flex-1 min-w-0 text-left rounded transition-colors hover:bg-[hsl(var(--ra-muted)/0.5)] -mx-1 px-1 py-0.5",
|
|
4159
4490
|
"aria-expanded": open,
|
|
4160
4491
|
children: [
|
|
4161
4492
|
open ? /* @__PURE__ */ jsx(ChevronDown, { className: "w-3.5 h-3.5 shrink-0", style: headerTone }) : /* @__PURE__ */ jsx(ChevronRight, { className: "w-3.5 h-3.5 shrink-0", style: headerTone }),
|
|
@@ -4173,7 +4504,7 @@ function RecordTargeting({ SL, collectionId, appId, ctx }) {
|
|
|
4173
4504
|
{
|
|
4174
4505
|
className: "text-xs truncate flex-1 min-w-0",
|
|
4175
4506
|
style: { color: "hsl(var(--ra-text))" },
|
|
4176
|
-
title: summary.text
|
|
4507
|
+
title: hasAnyClause ? summary.text : void 0,
|
|
4177
4508
|
children: hasAnyClause ? summary.text || `${summary.clauseCount} facet${summary.clauseCount === 1 ? "" : "s"}` : /* @__PURE__ */ jsx("span", { style: { color: "hsl(var(--ra-muted-text))", fontStyle: "italic" }, children: "No rule yet \u2014 pick a facet to begin" })
|
|
4178
4509
|
}
|
|
4179
4510
|
),
|
|
@@ -4206,7 +4537,7 @@ function RecordTargeting({ SL, collectionId, appId, ctx }) {
|
|
|
4206
4537
|
/* @__PURE__ */ jsx(Settings2, { className: "w-3.5 h-3.5 shrink-0", style: headerTone })
|
|
4207
4538
|
]
|
|
4208
4539
|
}
|
|
4209
|
-
),
|
|
4540
|
+
) }),
|
|
4210
4541
|
open && /* @__PURE__ */ jsx(
|
|
4211
4542
|
"div",
|
|
4212
4543
|
{
|
|
@@ -4228,6 +4559,262 @@ function RecordTargeting({ SL, collectionId, appId, ctx }) {
|
|
|
4228
4559
|
}
|
|
4229
4560
|
);
|
|
4230
4561
|
}
|
|
4562
|
+
function deepCloneRule(rule) {
|
|
4563
|
+
if (typeof structuredClone === "function") return structuredClone(rule);
|
|
4564
|
+
return JSON.parse(JSON.stringify(rule));
|
|
4565
|
+
}
|
|
4566
|
+
function TargetingPopover({
|
|
4567
|
+
ctx,
|
|
4568
|
+
catalogue,
|
|
4569
|
+
onCustomise,
|
|
4570
|
+
disabled
|
|
4571
|
+
}) {
|
|
4572
|
+
const [open, setOpen] = useState(false);
|
|
4573
|
+
const triggerRef = useRef(null);
|
|
4574
|
+
const [anchor, setAnchor] = useState(null);
|
|
4575
|
+
const currentHash = useMemo(() => ruleHash(ctx.facetRule), [ctx.facetRule]);
|
|
4576
|
+
const currentMatchesPreset = !!currentHash && catalogue.some((e) => e.hash === currentHash);
|
|
4577
|
+
const sharedCount = useMemo(() => {
|
|
4578
|
+
if (!currentHash) return 0;
|
|
4579
|
+
const entry = catalogue.find((e) => e.hash === currentHash);
|
|
4580
|
+
if (!entry) return 0;
|
|
4581
|
+
return Math.max(0, entry.count - 1);
|
|
4582
|
+
}, [catalogue, currentHash]);
|
|
4583
|
+
useEffect(() => {
|
|
4584
|
+
if (!open || !triggerRef.current) return;
|
|
4585
|
+
const r = triggerRef.current.getBoundingClientRect();
|
|
4586
|
+
setAnchor({ top: r.bottom + 6, right: window.innerWidth - r.right });
|
|
4587
|
+
}, [open]);
|
|
4588
|
+
useEffect(() => {
|
|
4589
|
+
if (!open) return;
|
|
4590
|
+
const onKey = (e) => {
|
|
4591
|
+
if (e.key === "Escape") {
|
|
4592
|
+
e.stopPropagation();
|
|
4593
|
+
setOpen(false);
|
|
4594
|
+
}
|
|
4595
|
+
};
|
|
4596
|
+
const onDown = (e) => {
|
|
4597
|
+
const t = e.target;
|
|
4598
|
+
if (!t) return;
|
|
4599
|
+
if (triggerRef.current?.contains(t)) return;
|
|
4600
|
+
const card = document.getElementById("ra-targeting-popover-card");
|
|
4601
|
+
if (card?.contains(t)) return;
|
|
4602
|
+
setOpen(false);
|
|
4603
|
+
};
|
|
4604
|
+
window.addEventListener("keydown", onKey, true);
|
|
4605
|
+
window.addEventListener("mousedown", onDown, true);
|
|
4606
|
+
return () => {
|
|
4607
|
+
window.removeEventListener("keydown", onKey, true);
|
|
4608
|
+
window.removeEventListener("mousedown", onDown, true);
|
|
4609
|
+
};
|
|
4610
|
+
}, [open]);
|
|
4611
|
+
if (disabled || !ctx.onFacetRuleChange) return null;
|
|
4612
|
+
const mode = !ctx.facetRule ? "global" : currentMatchesPreset ? "preset" : "custom";
|
|
4613
|
+
const onPickGlobal = () => {
|
|
4614
|
+
ctx.onFacetRuleChange?.(null);
|
|
4615
|
+
setOpen(false);
|
|
4616
|
+
};
|
|
4617
|
+
const onPickPreset = (entry) => {
|
|
4618
|
+
ctx.onFacetRuleChange?.(deepCloneRule(entry.rule));
|
|
4619
|
+
setOpen(false);
|
|
4620
|
+
};
|
|
4621
|
+
const onPickCustom = () => {
|
|
4622
|
+
if (!ctx.facetRule) ctx.onFacetRuleChange?.({ all: [] });
|
|
4623
|
+
onCustomise();
|
|
4624
|
+
setOpen(false);
|
|
4625
|
+
};
|
|
4626
|
+
const triggerLabel = mode === "global" ? "Global" : "Targeted";
|
|
4627
|
+
const TriggerIcon = mode === "global" ? Globe2 : Target;
|
|
4628
|
+
const popover = open && anchor ? createPortal(
|
|
4629
|
+
/* @__PURE__ */ jsxs(
|
|
4630
|
+
"div",
|
|
4631
|
+
{
|
|
4632
|
+
id: "ra-targeting-popover-card",
|
|
4633
|
+
role: "dialog",
|
|
4634
|
+
"aria-label": "Targeting",
|
|
4635
|
+
className: "ra-shell",
|
|
4636
|
+
style: {
|
|
4637
|
+
position: "fixed",
|
|
4638
|
+
top: anchor.top,
|
|
4639
|
+
right: anchor.right,
|
|
4640
|
+
zIndex: 60,
|
|
4641
|
+
width: 320,
|
|
4642
|
+
maxHeight: "min(70vh, 480px)",
|
|
4643
|
+
overflowY: "auto",
|
|
4644
|
+
background: "hsl(var(--ra-surface))",
|
|
4645
|
+
border: "1px solid hsl(var(--ra-border))",
|
|
4646
|
+
borderRadius: 8,
|
|
4647
|
+
boxShadow: "0 8px 24px hsl(0 0% 0% / 0.12)"
|
|
4648
|
+
},
|
|
4649
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
4650
|
+
onClick: (e) => e.stopPropagation(),
|
|
4651
|
+
children: [
|
|
4652
|
+
/* @__PURE__ */ jsx(
|
|
4653
|
+
"div",
|
|
4654
|
+
{
|
|
4655
|
+
className: "px-3 py-2 border-b text-[11px] uppercase tracking-wide font-medium",
|
|
4656
|
+
style: {
|
|
4657
|
+
borderColor: "hsl(var(--ra-border))",
|
|
4658
|
+
color: "hsl(var(--ra-muted-text))"
|
|
4659
|
+
},
|
|
4660
|
+
children: "Targeting"
|
|
4661
|
+
}
|
|
4662
|
+
),
|
|
4663
|
+
sharedCount > 0 && /* @__PURE__ */ jsxs(
|
|
4664
|
+
"div",
|
|
4665
|
+
{
|
|
4666
|
+
className: "mx-3 mt-2 px-2 py-1.5 text-[11px] rounded flex items-start gap-1.5",
|
|
4667
|
+
style: {
|
|
4668
|
+
background: "hsl(var(--ra-muted) / 0.5)",
|
|
4669
|
+
color: "hsl(var(--ra-muted-text))"
|
|
4670
|
+
},
|
|
4671
|
+
children: [
|
|
4672
|
+
/* @__PURE__ */ jsx(Info, { className: "w-3 h-3 mt-0.5 shrink-0" }),
|
|
4673
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
4674
|
+
/* @__PURE__ */ jsx("strong", { style: { color: "hsl(var(--ra-text))" }, children: sharedCount }),
|
|
4675
|
+
" ",
|
|
4676
|
+
"other record",
|
|
4677
|
+
sharedCount === 1 ? "" : "s",
|
|
4678
|
+
" share this rule. Changing it here only affects this record. To retarget all of them, use ",
|
|
4679
|
+
/* @__PURE__ */ jsx("em", { children: "Edit rule" }),
|
|
4680
|
+
" on the rail group."
|
|
4681
|
+
] })
|
|
4682
|
+
]
|
|
4683
|
+
}
|
|
4684
|
+
),
|
|
4685
|
+
/* @__PURE__ */ jsx(
|
|
4686
|
+
Option,
|
|
4687
|
+
{
|
|
4688
|
+
active: mode === "global",
|
|
4689
|
+
onClick: onPickGlobal,
|
|
4690
|
+
icon: /* @__PURE__ */ jsx(Globe2, { className: "w-3.5 h-3.5" }),
|
|
4691
|
+
title: "Apply globally",
|
|
4692
|
+
subtitle: "Applies to every product in the collection."
|
|
4693
|
+
}
|
|
4694
|
+
),
|
|
4695
|
+
catalogue.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4696
|
+
/* @__PURE__ */ jsx(
|
|
4697
|
+
"div",
|
|
4698
|
+
{
|
|
4699
|
+
className: "px-3 pt-2 pb-1 text-[10px] uppercase tracking-wide",
|
|
4700
|
+
style: { color: "hsl(var(--ra-muted-text))" },
|
|
4701
|
+
children: "Existing rule sets"
|
|
4702
|
+
}
|
|
4703
|
+
),
|
|
4704
|
+
catalogue.map((entry) => /* @__PURE__ */ jsx(
|
|
4705
|
+
Option,
|
|
4706
|
+
{
|
|
4707
|
+
active: mode === "preset" && currentHash === entry.hash,
|
|
4708
|
+
onClick: () => onPickPreset(entry),
|
|
4709
|
+
icon: /* @__PURE__ */ jsx(Target, { className: "w-3.5 h-3.5" }),
|
|
4710
|
+
title: entry.summary || "Untitled rule",
|
|
4711
|
+
subtitle: `${entry.count} record${entry.count === 1 ? "" : "s"}`
|
|
4712
|
+
},
|
|
4713
|
+
entry.hash
|
|
4714
|
+
))
|
|
4715
|
+
] }),
|
|
4716
|
+
/* @__PURE__ */ jsx(
|
|
4717
|
+
"div",
|
|
4718
|
+
{
|
|
4719
|
+
className: "border-t mt-1",
|
|
4720
|
+
style: { borderColor: "hsl(var(--ra-border) / 0.6)" }
|
|
4721
|
+
}
|
|
4722
|
+
),
|
|
4723
|
+
/* @__PURE__ */ jsx(
|
|
4724
|
+
Option,
|
|
4725
|
+
{
|
|
4726
|
+
active: mode === "custom",
|
|
4727
|
+
onClick: onPickCustom,
|
|
4728
|
+
icon: /* @__PURE__ */ jsx(Pencil, { className: "w-3.5 h-3.5" }),
|
|
4729
|
+
title: mode === "custom" ? "Customise rule" : "Create a new rule",
|
|
4730
|
+
subtitle: "Open the rule editor below to author a bespoke rule."
|
|
4731
|
+
}
|
|
4732
|
+
)
|
|
4733
|
+
]
|
|
4734
|
+
}
|
|
4735
|
+
),
|
|
4736
|
+
document.body
|
|
4737
|
+
) : null;
|
|
4738
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4739
|
+
/* @__PURE__ */ jsxs(
|
|
4740
|
+
"button",
|
|
4741
|
+
{
|
|
4742
|
+
ref: triggerRef,
|
|
4743
|
+
type: "button",
|
|
4744
|
+
onClick: () => setOpen((o) => !o),
|
|
4745
|
+
className: "text-[11px] inline-flex items-center gap-1 px-2 py-1 rounded-md border transition-colors hover:bg-[hsl(var(--ra-muted))]",
|
|
4746
|
+
style: {
|
|
4747
|
+
borderColor: "hsl(var(--ra-border))",
|
|
4748
|
+
color: "hsl(var(--ra-text))"
|
|
4749
|
+
},
|
|
4750
|
+
"aria-haspopup": "dialog",
|
|
4751
|
+
"aria-expanded": open,
|
|
4752
|
+
title: "Use rules to scope this record to a targeted audience",
|
|
4753
|
+
children: [
|
|
4754
|
+
/* @__PURE__ */ jsx(TriggerIcon, { className: "w-3 h-3" }),
|
|
4755
|
+
triggerLabel,
|
|
4756
|
+
mode === "preset" && /* @__PURE__ */ jsx(Check, { className: "w-3 h-3", style: { color: "hsl(var(--ra-muted-text))" } })
|
|
4757
|
+
]
|
|
4758
|
+
}
|
|
4759
|
+
),
|
|
4760
|
+
popover
|
|
4761
|
+
] });
|
|
4762
|
+
}
|
|
4763
|
+
function Option({
|
|
4764
|
+
active,
|
|
4765
|
+
onClick,
|
|
4766
|
+
icon,
|
|
4767
|
+
title,
|
|
4768
|
+
subtitle
|
|
4769
|
+
}) {
|
|
4770
|
+
return /* @__PURE__ */ jsxs(
|
|
4771
|
+
"button",
|
|
4772
|
+
{
|
|
4773
|
+
type: "button",
|
|
4774
|
+
onClick,
|
|
4775
|
+
className: "w-full text-left px-3 py-2 flex items-start gap-2 transition-colors hover:bg-[hsl(var(--ra-muted)/0.6)]",
|
|
4776
|
+
style: {
|
|
4777
|
+
background: active ? "hsl(var(--ra-accent) / 0.08)" : void 0
|
|
4778
|
+
},
|
|
4779
|
+
children: [
|
|
4780
|
+
/* @__PURE__ */ jsx(
|
|
4781
|
+
"span",
|
|
4782
|
+
{
|
|
4783
|
+
className: "mt-0.5 shrink-0",
|
|
4784
|
+
style: { color: active ? "hsl(var(--ra-accent))" : "hsl(var(--ra-muted-text))" },
|
|
4785
|
+
children: icon
|
|
4786
|
+
}
|
|
4787
|
+
),
|
|
4788
|
+
/* @__PURE__ */ jsxs("span", { className: "flex-1 min-w-0", children: [
|
|
4789
|
+
/* @__PURE__ */ jsx(
|
|
4790
|
+
"span",
|
|
4791
|
+
{
|
|
4792
|
+
className: "block text-xs font-medium truncate",
|
|
4793
|
+
style: { color: "hsl(var(--ra-text))" },
|
|
4794
|
+
title,
|
|
4795
|
+
children: title
|
|
4796
|
+
}
|
|
4797
|
+
),
|
|
4798
|
+
subtitle && /* @__PURE__ */ jsx(
|
|
4799
|
+
"span",
|
|
4800
|
+
{
|
|
4801
|
+
className: "block text-[11px] truncate",
|
|
4802
|
+
style: { color: "hsl(var(--ra-muted-text))" },
|
|
4803
|
+
children: subtitle
|
|
4804
|
+
}
|
|
4805
|
+
)
|
|
4806
|
+
] }),
|
|
4807
|
+
active && /* @__PURE__ */ jsx(
|
|
4808
|
+
Check,
|
|
4809
|
+
{
|
|
4810
|
+
className: "w-3.5 h-3.5 mt-0.5 shrink-0",
|
|
4811
|
+
style: { color: "hsl(var(--ra-accent))" }
|
|
4812
|
+
}
|
|
4813
|
+
)
|
|
4814
|
+
]
|
|
4815
|
+
}
|
|
4816
|
+
);
|
|
4817
|
+
}
|
|
4231
4818
|
var TAB_META = {
|
|
4232
4819
|
product: { icon: Box, label: "Product" },
|
|
4233
4820
|
variant: { icon: Layers, label: "Variants" },
|
|
@@ -4736,9 +5323,10 @@ function DefaultItemTable({
|
|
|
4736
5323
|
}
|
|
4737
5324
|
)
|
|
4738
5325
|
] }) }),
|
|
4739
|
-
/* @__PURE__ */ jsx("tbody", { children: items.map((item) => {
|
|
5326
|
+
/* @__PURE__ */ jsx("tbody", { children: items.map((item, idx) => {
|
|
4740
5327
|
const id = item.itemId ?? "";
|
|
4741
5328
|
const isSelected = !!selectedId && selectedId === id;
|
|
5329
|
+
const key = item.id ?? (id || anchorKey(item.scope) || `pos:${idx}`);
|
|
4742
5330
|
return /* @__PURE__ */ jsxs(
|
|
4743
5331
|
"tr",
|
|
4744
5332
|
{
|
|
@@ -4784,7 +5372,7 @@ function DefaultItemTable({
|
|
|
4784
5372
|
] })
|
|
4785
5373
|
]
|
|
4786
5374
|
},
|
|
4787
|
-
|
|
5375
|
+
key
|
|
4788
5376
|
);
|
|
4789
5377
|
}) })
|
|
4790
5378
|
] }) });
|
|
@@ -4805,8 +5393,9 @@ function DefaultItemCards({
|
|
|
4805
5393
|
{
|
|
4806
5394
|
className: isGallery ? "ra-item-gallery" : "ra-item-cards",
|
|
4807
5395
|
"data-card-size": cardSize,
|
|
4808
|
-
children: items.map((item) => {
|
|
5396
|
+
children: items.map((item, idx) => {
|
|
4809
5397
|
const id = item.itemId ?? "";
|
|
5398
|
+
const key = item.id ?? (id || anchorKey(item.scope) || `pos:${idx}`);
|
|
4810
5399
|
const open = () => ctx.onOpen(id);
|
|
4811
5400
|
const slotCtx = {
|
|
4812
5401
|
...ctx,
|
|
@@ -4822,7 +5411,7 @@ function DefaultItemCards({
|
|
|
4822
5411
|
"data-selected": slotCtx.selected,
|
|
4823
5412
|
children: renderCard(item, slotCtx)
|
|
4824
5413
|
},
|
|
4825
|
-
|
|
5414
|
+
key
|
|
4826
5415
|
);
|
|
4827
5416
|
}
|
|
4828
5417
|
return /* @__PURE__ */ jsxs(
|
|
@@ -4854,7 +5443,7 @@ function DefaultItemCards({
|
|
|
4854
5443
|
)
|
|
4855
5444
|
]
|
|
4856
5445
|
},
|
|
4857
|
-
|
|
5446
|
+
key
|
|
4858
5447
|
);
|
|
4859
5448
|
})
|
|
4860
5449
|
}
|
|
@@ -4866,6 +5455,7 @@ function ItemListView({
|
|
|
4866
5455
|
error,
|
|
4867
5456
|
ctx,
|
|
4868
5457
|
itemNoun,
|
|
5458
|
+
ruleSummary,
|
|
4869
5459
|
view,
|
|
4870
5460
|
views,
|
|
4871
5461
|
onViewChange,
|
|
@@ -4963,6 +5553,27 @@ function ItemListView({
|
|
|
4963
5553
|
}
|
|
4964
5554
|
return /* @__PURE__ */ jsxs("div", { className: "ra-item-list", children: [
|
|
4965
5555
|
toolbar,
|
|
5556
|
+
ruleSummary ? /* @__PURE__ */ jsxs(
|
|
5557
|
+
"div",
|
|
5558
|
+
{
|
|
5559
|
+
className: "ra-item-rule-summary",
|
|
5560
|
+
style: {
|
|
5561
|
+
padding: "6px 12px",
|
|
5562
|
+
fontSize: "11px",
|
|
5563
|
+
color: "hsl(var(--ra-muted-text))",
|
|
5564
|
+
borderBottom: "1px solid hsl(var(--ra-border))",
|
|
5565
|
+
background: "hsl(var(--ra-muted) / 0.4)",
|
|
5566
|
+
display: "flex",
|
|
5567
|
+
alignItems: "center",
|
|
5568
|
+
gap: "6px"
|
|
5569
|
+
},
|
|
5570
|
+
children: [
|
|
5571
|
+
/* @__PURE__ */ jsx("span", { style: { fontWeight: 500, color: "hsl(var(--ra-text))" }, children: "Rule" }),
|
|
5572
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
5573
|
+
/* @__PURE__ */ jsx("span", { children: ruleSummary })
|
|
5574
|
+
]
|
|
5575
|
+
}
|
|
5576
|
+
) : null,
|
|
4966
5577
|
/* @__PURE__ */ jsx("div", { className: "ra-item-list-body", children: body })
|
|
4967
5578
|
] });
|
|
4968
5579
|
}
|
|
@@ -5051,11 +5662,13 @@ function SiblingRail({
|
|
|
5051
5662
|
isLoading && /* @__PURE__ */ jsx(LoadingState, {}),
|
|
5052
5663
|
!isLoading && error && /* @__PURE__ */ jsx(ErrorState, { error }),
|
|
5053
5664
|
!isLoading && !error && items.length === 0 && /* @__PURE__ */ jsx(EmptyState, { title: i18n.noItemsTitle, body: i18n.noItemsBody }),
|
|
5054
|
-
!isLoading && !error && items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "ra-sibling-list", children: items.map((item) => {
|
|
5665
|
+
!isLoading && !error && items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "ra-sibling-list", children: items.map((item, idx) => {
|
|
5055
5666
|
const id = item.itemId ?? "";
|
|
5667
|
+
const key = item.id ?? (id || anchorKey(item.scope) || `pos:${idx}`);
|
|
5668
|
+
const akey = anchorKey(item.scope);
|
|
5056
5669
|
const selected = selectedItemId === id;
|
|
5057
|
-
const isDirty = !!(id && dirtyKeys?.has(id) ||
|
|
5058
|
-
const hasError = !!(id && errorKeys?.has(id) ||
|
|
5670
|
+
const isDirty = !!(id && dirtyKeys?.has(id) || akey && dirtyKeys?.has(akey));
|
|
5671
|
+
const hasError = !!(id && errorKeys?.has(id) || akey && errorKeys?.has(akey));
|
|
5059
5672
|
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
5060
5673
|
"button",
|
|
5061
5674
|
{
|
|
@@ -5085,7 +5698,7 @@ function SiblingRail({
|
|
|
5085
5698
|
) : null
|
|
5086
5699
|
]
|
|
5087
5700
|
}
|
|
5088
|
-
) },
|
|
5701
|
+
) }, key);
|
|
5089
5702
|
}) })
|
|
5090
5703
|
] })
|
|
5091
5704
|
] });
|
|
@@ -5143,9 +5756,6 @@ var UtilityRow = ({ label, customLabel, introHidden, onShowIntro }) => {
|
|
|
5143
5756
|
}
|
|
5144
5757
|
);
|
|
5145
5758
|
};
|
|
5146
|
-
|
|
5147
|
-
// src/components/AdminPageHeader/admin-page-header.css
|
|
5148
|
-
styleInject('.sl-aph {\n width: 100%;\n font-family: var(--ra-font-ui, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);\n color: hsl(var(--ra-text, 222 47% 11%));\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n.sl-aph *,\n.sl-aph *::before,\n.sl-aph *::after {\n box-sizing: border-box;\n}\n.sl-aph__row {\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: 0.6rem;\n padding: 0.25rem 0.25rem 0.5rem;\n}\n.sl-aph__main {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: flex-start;\n gap: 0.55rem;\n}\n.sl-aph__aside {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n.sl-aph__text {\n flex: 1;\n min-width: 0;\n}\n.sl-aph__title {\n font-family: var(--ra-font-display, var(--ra-font-ui, ui-sans-serif, system-ui, sans-serif));\n font-weight: 700;\n font-size: 1.2rem;\n line-height: 1.2;\n color: hsl(var(--ra-text, 222 47% 11%));\n letter-spacing: -0.015em;\n margin: 0;\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n}\n.sl-aph__icon {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: flex-start;\n background: transparent;\n color: hsl(var(--ra-text, 222 47% 11%));\n border: 0;\n padding: 0;\n}\n.sl-aph__icon > svg {\n width: 1.05rem;\n height: 1.05rem;\n}\n.sl-aph__subtitle {\n font-size: 0.78rem;\n color: hsl(var(--ra-muted-text, 220 9% 46%));\n margin: 0.1rem 0 0;\n line-height: 1.3;\n}\n.sl-aph__icon-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n padding: 0;\n border-radius: 999px;\n background: transparent;\n border: 1px solid transparent;\n color: hsl(var(--ra-muted-text, 220 9% 46%));\n cursor: pointer;\n transition:\n background .15s ease,\n color .15s ease,\n border-color .15s ease;\n text-decoration: none;\n}\n.sl-aph__icon-btn:hover {\n background: hsl(var(--ra-text, 222 47% 11%) / 0.06);\n color: hsl(var(--ra-text, 222 47% 11%));\n}\n.sl-aph__icon-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring, hsl(222 47% 11% / 0.35));\n}\n.sl-aph__icon-btn > svg {\n width: 1rem;\n height: 1rem;\n}\n.sl-aph__intro {\n position: relative;\n display: flex;\n align-items: center;\n gap: 0.55rem;\n padding: 0.4rem 2rem 0.4rem 0.5rem;\n border-radius: var(--ra-radius, 0.625rem);\n border: 1px solid hsl(var(--ra-info, 214 95% 55%) / 0.30);\n background: hsl(var(--ra-info, 214 95% 55%) / 0.08);\n}\n.sl-aph__intro[data-tone=success] {\n border-color: hsl(var(--ra-success, 142 71% 45%) / 0.30);\n background: hsl(var(--ra-success, 142 71% 45%) / 0.08);\n}\n.sl-aph__intro[data-tone=warning] {\n border-color: hsl(var(--ra-warning, 38 92% 50%) / 0.35);\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.10);\n}\n.sl-aph__intro-icon {\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-info, 214 95% 55%) / 0.18);\n color: hsl(var(--ra-info, 214 95% 55%));\n}\n.sl-aph__intro[data-tone=success] .sl-aph__intro-icon {\n background: hsl(var(--ra-success, 142 71% 45%) / 0.18);\n color: hsl(var(--ra-success, 142 71% 45%));\n}\n.sl-aph__intro[data-tone=warning] .sl-aph__intro-icon {\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.20);\n color: hsl(var(--ra-warning, 38 92% 50%));\n}\n.sl-aph__intro-body {\n flex: 1;\n min-width: 0;\n}\n.sl-aph__intro-title {\n font-family: var(--ra-font-display, var(--ra-font-ui, ui-sans-serif, system-ui, sans-serif));\n font-weight: var(--ra-title-weight, 600);\n font-size: 0.8rem;\n color: hsl(var(--ra-text, 222 47% 11%));\n margin: 0;\n line-height: 1.2;\n display: inline;\n}\n.sl-aph__intro-text {\n font-size: 0.78rem;\n color: hsl(var(--ra-text, 222 47% 11%) / 0.85);\n line-height: 1.35;\n display: inline;\n margin-left: 0.4rem;\n}\n.sl-aph__intro-action {\n margin-left: 0.375rem;\n}\n.sl-aph__intro-dismiss {\n position: absolute;\n top: 50%;\n right: 0.35rem;\n transform: translateY(-50%);\n width: 1.4rem;\n height: 1.4rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n color: hsl(var(--ra-muted-text, 220 9% 46%));\n cursor: pointer;\n padding: 0;\n}\n.sl-aph__intro-dismiss:hover {\n background: hsl(var(--ra-text, 222 47% 11%) / 0.06);\n color: hsl(var(--ra-text, 222 47% 11%));\n}\n');
|
|
5149
5759
|
var TONE_ICON2 = {
|
|
5150
5760
|
info: Lightbulb,
|
|
5151
5761
|
success: CheckCircle2,
|
|
@@ -5496,6 +6106,183 @@ function WizardFooter({
|
|
|
5496
6106
|
}
|
|
5497
6107
|
);
|
|
5498
6108
|
}
|
|
6109
|
+
var RuleGroupEditDialog = ({
|
|
6110
|
+
open,
|
|
6111
|
+
ctx,
|
|
6112
|
+
currentRule,
|
|
6113
|
+
recordIds,
|
|
6114
|
+
onClose,
|
|
6115
|
+
onApplied
|
|
6116
|
+
}) => {
|
|
6117
|
+
const [draftRule, setDraftRule] = useState(currentRule);
|
|
6118
|
+
const [isApplying, setIsApplying] = useState(false);
|
|
6119
|
+
const [error, setError] = useState(null);
|
|
6120
|
+
const [completed, setCompleted] = useState(() => /* @__PURE__ */ new Set());
|
|
6121
|
+
const cancelledRef = useRef(false);
|
|
6122
|
+
const facets = useFacets(ctx.collectionId, void 0);
|
|
6123
|
+
useEffect(() => {
|
|
6124
|
+
if (!open) return;
|
|
6125
|
+
setDraftRule(currentRule);
|
|
6126
|
+
setError(null);
|
|
6127
|
+
setCompleted(/* @__PURE__ */ new Set());
|
|
6128
|
+
cancelledRef.current = false;
|
|
6129
|
+
}, [open, currentRule]);
|
|
6130
|
+
useEffect(() => {
|
|
6131
|
+
if (!open) return;
|
|
6132
|
+
const prev = document.body.style.overflow;
|
|
6133
|
+
document.body.style.overflow = "hidden";
|
|
6134
|
+
const onKey = (e) => {
|
|
6135
|
+
if (e.key === "Escape" && !isApplying) {
|
|
6136
|
+
e.stopPropagation();
|
|
6137
|
+
onClose();
|
|
6138
|
+
}
|
|
6139
|
+
};
|
|
6140
|
+
window.addEventListener("keydown", onKey, true);
|
|
6141
|
+
return () => {
|
|
6142
|
+
window.removeEventListener("keydown", onKey, true);
|
|
6143
|
+
document.body.style.overflow = prev;
|
|
6144
|
+
};
|
|
6145
|
+
}, [open, isApplying, onClose]);
|
|
6146
|
+
const isUnchanged = useMemo(
|
|
6147
|
+
() => rulesEqual(draftRule, currentRule),
|
|
6148
|
+
[draftRule, currentRule]
|
|
6149
|
+
);
|
|
6150
|
+
const remaining = useMemo(
|
|
6151
|
+
() => recordIds.filter((id) => !completed.has(id)),
|
|
6152
|
+
[recordIds, completed]
|
|
6153
|
+
);
|
|
6154
|
+
const apply = async () => {
|
|
6155
|
+
if (isUnchanged) {
|
|
6156
|
+
onClose();
|
|
6157
|
+
return;
|
|
6158
|
+
}
|
|
6159
|
+
setIsApplying(true);
|
|
6160
|
+
setError(null);
|
|
6161
|
+
cancelledRef.current = false;
|
|
6162
|
+
try {
|
|
6163
|
+
for (const id of remaining) {
|
|
6164
|
+
if (cancelledRef.current) break;
|
|
6165
|
+
await updateRecord(ctx, id, { facetRule: draftRule });
|
|
6166
|
+
setCompleted((prev) => {
|
|
6167
|
+
const next = new Set(prev);
|
|
6168
|
+
next.add(id);
|
|
6169
|
+
return next;
|
|
6170
|
+
});
|
|
6171
|
+
}
|
|
6172
|
+
if (!cancelledRef.current) {
|
|
6173
|
+
onApplied?.(draftRule);
|
|
6174
|
+
onClose();
|
|
6175
|
+
}
|
|
6176
|
+
} catch (err) {
|
|
6177
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
6178
|
+
} finally {
|
|
6179
|
+
setIsApplying(false);
|
|
6180
|
+
}
|
|
6181
|
+
};
|
|
6182
|
+
if (!open || typeof document === "undefined") return null;
|
|
6183
|
+
const total = recordIds.length;
|
|
6184
|
+
const done = completed.size;
|
|
6185
|
+
return createPortal(
|
|
6186
|
+
/* @__PURE__ */ jsxs(
|
|
6187
|
+
"div",
|
|
6188
|
+
{
|
|
6189
|
+
className: "ra-shell ra-confirm-root",
|
|
6190
|
+
role: "presentation",
|
|
6191
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
6192
|
+
onClick: (e) => e.stopPropagation(),
|
|
6193
|
+
children: [
|
|
6194
|
+
/* @__PURE__ */ jsx("div", { className: "ra-confirm-backdrop", onClick: () => !isApplying && onClose() }),
|
|
6195
|
+
/* @__PURE__ */ jsxs(
|
|
6196
|
+
"div",
|
|
6197
|
+
{
|
|
6198
|
+
role: "dialog",
|
|
6199
|
+
"aria-modal": "true",
|
|
6200
|
+
"aria-labelledby": "ra-rule-bulk-title",
|
|
6201
|
+
className: "ra-confirm-card",
|
|
6202
|
+
style: { maxWidth: 560, width: "92vw" },
|
|
6203
|
+
children: [
|
|
6204
|
+
/* @__PURE__ */ jsxs("div", { className: "ra-confirm-header", style: { justifyContent: "space-between" }, children: [
|
|
6205
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
6206
|
+
/* @__PURE__ */ jsx("span", { className: "ra-confirm-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AlertTriangle, { className: "w-4 h-4" }) }),
|
|
6207
|
+
/* @__PURE__ */ jsxs("h2", { id: "ra-rule-bulk-title", className: "ra-confirm-title", children: [
|
|
6208
|
+
"Edit rule for ",
|
|
6209
|
+
total,
|
|
6210
|
+
" record",
|
|
6211
|
+
total === 1 ? "" : "s"
|
|
6212
|
+
] })
|
|
6213
|
+
] }),
|
|
6214
|
+
/* @__PURE__ */ jsx(
|
|
6215
|
+
"button",
|
|
6216
|
+
{
|
|
6217
|
+
type: "button",
|
|
6218
|
+
className: "ra-confirm-btn ra-confirm-btn-ghost",
|
|
6219
|
+
style: { padding: 4 },
|
|
6220
|
+
onClick: () => !isApplying && onClose(),
|
|
6221
|
+
"aria-label": "Close",
|
|
6222
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
|
|
6223
|
+
}
|
|
6224
|
+
)
|
|
6225
|
+
] }),
|
|
6226
|
+
/* @__PURE__ */ jsxs("div", { className: "px-4 pb-2 text-xs", style: { color: "hsl(var(--ra-muted-text))" }, children: [
|
|
6227
|
+
"Currently targeting:",
|
|
6228
|
+
" ",
|
|
6229
|
+
/* @__PURE__ */ jsx("span", { style: { color: "hsl(var(--ra-text))" }, children: summariseRule(currentRule) })
|
|
6230
|
+
] }),
|
|
6231
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: /* @__PURE__ */ jsx(
|
|
6232
|
+
FacetRuleEditor,
|
|
6233
|
+
{
|
|
6234
|
+
value: draftRule,
|
|
6235
|
+
onChange: setDraftRule,
|
|
6236
|
+
collectionId: ctx.collectionId,
|
|
6237
|
+
description: "The new rule will be applied to every record in this group."
|
|
6238
|
+
}
|
|
6239
|
+
) }),
|
|
6240
|
+
error && /* @__PURE__ */ jsxs(
|
|
6241
|
+
"div",
|
|
6242
|
+
{
|
|
6243
|
+
className: "mx-4 mb-2 px-3 py-2 text-xs rounded",
|
|
6244
|
+
style: {
|
|
6245
|
+
background: "hsl(var(--ra-danger-soft, var(--destructive) / 0.12))",
|
|
6246
|
+
color: "hsl(var(--ra-danger, var(--destructive)))"
|
|
6247
|
+
},
|
|
6248
|
+
role: "alert",
|
|
6249
|
+
children: [
|
|
6250
|
+
error,
|
|
6251
|
+
done > 0 && ` Applied to ${done} of ${total} so far.`
|
|
6252
|
+
]
|
|
6253
|
+
}
|
|
6254
|
+
),
|
|
6255
|
+
/* @__PURE__ */ jsxs("div", { className: "ra-confirm-actions", children: [
|
|
6256
|
+
/* @__PURE__ */ jsx(
|
|
6257
|
+
"button",
|
|
6258
|
+
{
|
|
6259
|
+
type: "button",
|
|
6260
|
+
className: "ra-confirm-btn ra-confirm-btn-ghost",
|
|
6261
|
+
onClick: onClose,
|
|
6262
|
+
disabled: isApplying,
|
|
6263
|
+
children: "Cancel"
|
|
6264
|
+
}
|
|
6265
|
+
),
|
|
6266
|
+
/* @__PURE__ */ jsx(
|
|
6267
|
+
"button",
|
|
6268
|
+
{
|
|
6269
|
+
type: "button",
|
|
6270
|
+
className: "ra-confirm-btn ra-confirm-btn-primary",
|
|
6271
|
+
onClick: apply,
|
|
6272
|
+
disabled: isApplying || isUnchanged || !facets.length,
|
|
6273
|
+
children: isApplying ? `Applying ${done + 1} of ${total}\u2026` : error ? `Retry (${remaining.length} left)` : `Apply to all ${total}`
|
|
6274
|
+
}
|
|
6275
|
+
)
|
|
6276
|
+
] })
|
|
6277
|
+
]
|
|
6278
|
+
}
|
|
6279
|
+
)
|
|
6280
|
+
]
|
|
6281
|
+
}
|
|
6282
|
+
),
|
|
6283
|
+
document.body
|
|
6284
|
+
);
|
|
6285
|
+
};
|
|
5499
6286
|
var statusDot = (status) => {
|
|
5500
6287
|
switch (status) {
|
|
5501
6288
|
case "saving":
|
|
@@ -6063,12 +6850,6 @@ function useShellCsvBulk(args) {
|
|
|
6063
6850
|
[csvSchema, handleImport, handleExport]
|
|
6064
6851
|
);
|
|
6065
6852
|
}
|
|
6066
|
-
|
|
6067
|
-
// src/components/RecordsAdmin/shell/tokens.css
|
|
6068
|
-
styleInject(':root {\n --ra-status-own: var(--ra-emerald, 142 71% 45%);\n --ra-status-shared: var(--ra-amber, 38 92% 50%);\n --ra-status-missing: var(--muted-foreground, 220 9% 46%);\n --ra-accent: var(--primary, 222 47% 11%);\n --ra-surface: var(--card, 0 0% 100%);\n --ra-border: var(--border, 220 13% 91%);\n --ra-text: var(--foreground, 222 47% 11%);\n --ra-muted: var(--muted, 220 14% 96%);\n --ra-muted-text: var(--muted-foreground, 220 9% 46%);\n --ra-radius: var(--radius, 0.625rem);\n --ra-dot-size: 0.5rem;\n --ra-page-bg: var(--background, 220 14% 98%);\n --ra-card-shadow: 0 1px 2px hsl(var(--ra-accent) / 0.04), 0 4px 12px hsl(var(--ra-accent) / 0.05);\n --ra-card-shadow-hover: 0 2px 4px hsl(var(--ra-accent) / 0.06), 0 8px 24px hsl(var(--ra-accent) / 0.08);\n --ra-row-hover: hsl(var(--ra-accent) / 0.05);\n --ra-row-active-bg: hsl(var(--ra-accent) / 0.10);\n --ra-row-active-bd: hsl(var(--ra-accent) / 0.45);\n --ra-focus-ring: hsl(var(--ra-accent) / 0.35);\n --ra-font-display: var(--font-display, var(--font-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif));\n --ra-font-ui: var(--font-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);\n --ra-title-weight: 600;\n --ra-display-weight: 700;\n --ra-info: var(--ra-blue, 214 95% 55%);\n --ra-success: var(--ra-emerald, 142 71% 45%);\n --ra-warning: var(--ra-amber, 38 92% 50%);\n --ra-danger: var(--destructive, 0 72% 51%);\n}\n:root {\n --sl-control-bg: var(--muted, 220 14% 96%);\n --sl-control-fg: var(--muted-foreground, 220 9% 40%);\n --sl-control-border: var(--border, 220 13% 88%);\n --sl-control-active-bg: var(--primary, 222 47% 11%);\n --sl-control-active-fg: var(--primary-foreground, 0 0% 100%);\n --sl-control-active-bd: var(--primary, 222 47% 11%);\n --sl-control-hover-bg: var(--sl-control-active-bg, 222 47% 11%);\n --sl-control-hover-fg: var(--foreground, 222 47% 11%);\n --sl-control-focus-ring: var(--sl-control-active-bg, 222 47% 11%);\n --sl-control-radius: var(--radius, 0.5rem);\n --sl-control-weight: 500;\n --sl-control-active-weight: 600;\n}\n.ra-status-dot {\n display: inline-block;\n width: var(--ra-dot-size);\n height: var(--ra-dot-size);\n border-radius: 9999px;\n flex-shrink: 0;\n}\n.ra-status-own {\n background: hsl(var(--ra-status-own));\n}\n.ra-status-shared {\n background: hsl(var(--ra-status-shared));\n}\n.ra-status-missing {\n background: hsl(var(--ra-status-missing) / 0.4);\n border: 1px solid hsl(var(--ra-status-missing) / 0.6);\n}\n.ra-row-active {\n background: var(--ra-row-active-bg);\n border-color: var(--ra-row-active-bd) !important;\n}\n');
|
|
6069
|
-
|
|
6070
|
-
// src/components/RecordsAdmin/shell/shell.css
|
|
6071
|
-
styleInject(".ra-shell {\n color: hsl(var(--ra-text));\n background: hsl(var(--ra-page-bg));\n font-family: var(--ra-font-ui);\n}\n.ra-shell *,\n.ra-shell *::before,\n.ra-shell *::after {\n box-sizing: border-box;\n}\n.ra-shell .ra-card {\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-card-hover {\n transition:\n box-shadow .18s ease,\n transform .18s ease,\n border-color .18s ease;\n}\n.ra-shell .ra-card-hover:hover {\n box-shadow: var(--ra-card-shadow-hover);\n}\n.ra-shell .ra-display {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-title {\n font-weight: var(--ra-title-weight);\n}\n.ra-shell :where(button, [role=button], input, select, textarea, a):focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n border-radius: calc(var(--ra-radius) * 0.6);\n}\n.ra-shell .ra-header {\n display: block;\n width: 100%;\n}\n.ra-shell .ra-header__main {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: flex-start;\n gap: 0.55rem;\n}\n.ra-shell .ra-header-aside {\n display: flex;\n align-items: flex-start;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-header-icon {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: flex-start;\n background: transparent;\n color: hsl(var(--ra-text));\n border: 0;\n padding: 0;\n margin-top: 0.1rem;\n}\n.ra-shell .ra-header-icon > svg {\n width: 1.05rem;\n height: 1.05rem;\n}\n.ra-shell .ra-header-text {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-header-title {\n font-family: var(--ra-font-display);\n font-weight: 700;\n font-size: 1.2rem;\n line-height: 1.2;\n color: hsl(var(--ra-text));\n letter-spacing: -0.015em;\n margin: 0;\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n}\n.ra-shell .ra-header-subtitle {\n font-size: 0.78rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.1rem;\n line-height: 1.3;\n}\n.ra-shell .ra-header-stats {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n padding: 0.15rem 0.4rem;\n border-radius: calc(var(--ra-radius) * 0.75);\n background: hsl(var(--ra-surface) / 0.7);\n border: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-header-stats--titled {\n flex-direction: column;\n align-items: stretch;\n padding: 0.4rem 0.55rem;\n gap: 0.3rem;\n}\n.ra-shell .ra-header-stats .ra-stats-items {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n}\n.ra-shell .ra-header-stats .ra-stats-heading {\n display: flex;\n align-items: center;\n gap: 0.35rem;\n color: hsl(var(--ra-muted-text));\n font-size: 0.65rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon {\n display: inline-flex;\n align-items: center;\n color: hsl(var(--ra-text));\n opacity: 0.75;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon > svg {\n width: 0.85rem;\n height: 0.85rem;\n}\n.ra-shell .ra-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 0.15rem 0.45rem;\n min-width: 2.5rem;\n}\n.ra-shell .ra-stat-value {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 0.85rem;\n color: hsl(var(--ra-text));\n line-height: 1;\n}\n.ra-shell .ra-stat-label {\n font-size: 0.6rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n}\n.ra-shell .ra-stat-divider {\n width: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-header-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.ra-shell .ra-tabs {\n display: flex;\n gap: 0.25rem;\n padding: 0.25rem;\n background: hsl(var(--sl-control-bg));\n border-radius: var(--sl-control-radius);\n border: 1px solid hsl(var(--sl-control-border));\n}\n.ra-shell .ra-tab {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.4rem 0.7rem;\n border-radius: calc(var(--sl-control-radius) - 2px);\n font-size: 0.78rem;\n font-weight: var(--sl-control-weight);\n color: hsl(var(--sl-control-fg));\n background: transparent;\n border: 0;\n cursor: pointer;\n transition:\n background .15s ease,\n color .15s ease,\n transform .15s ease;\n white-space: nowrap;\n}\n.ra-shell .ra-tab:hover {\n background: hsl(var(--sl-control-hover-bg) / 0.10);\n color: hsl(var(--sl-control-hover-fg));\n}\n.ra-shell .ra-tab:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px hsl(var(--sl-control-focus-ring) / 0.45);\n}\n.ra-shell .ra-tab[aria-selected=true] {\n background: hsl(var(--sl-control-active-bg));\n color: hsl(var(--sl-control-active-fg));\n border-color: hsl(var(--sl-control-active-bd));\n font-weight: var(--sl-control-active-weight);\n box-shadow: 0 1px 2px hsl(var(--sl-control-active-bg) / 0.25);\n}\n.ra-shell .ra-tab[aria-selected=true]:hover {\n background: hsl(var(--sl-control-active-bg) / 0.92);\n color: hsl(var(--sl-control-active-fg));\n}\n.ra-shell .ra-tab[aria-selected=true] .ra-tab-icon {\n color: hsl(var(--sl-control-active-fg));\n}\n.ra-shell .ra-tab[disabled] {\n opacity: .5;\n cursor: not-allowed;\n}\n.ra-shell .ra-tab-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.25rem;\n padding: 0 0.35rem;\n height: 1.1rem;\n border-radius: 999px;\n background: hsl(var(--sl-control-active-fg) / 0.20);\n color: hsl(var(--sl-control-active-fg));\n font-size: 0.625rem;\n font-weight: 600;\n line-height: 1;\n}\n.ra-shell .ra-tab[aria-selected=false] .ra-tab-count {\n background: hsl(var(--sl-control-fg) / 0.15);\n color: hsl(var(--sl-control-fg));\n}\n.ra-shell[data-density=compact] .ra-row {\n padding-block: 0.4rem;\n}\n.ra-shell .ra-row {\n display: flex;\n align-items: center;\n gap: 0.55rem;\n width: 100%;\n text-align: left;\n padding: 0.45rem 0.75rem;\n border-left: 3px solid transparent;\n background: transparent;\n border-bottom: 1px solid transparent;\n transition: background .12s ease, border-color .12s ease;\n cursor: pointer;\n color: hsl(var(--ra-text));\n font-family: inherit;\n}\n.ra-shell .ra-row + .ra-row {\n border-top: 1px solid hsl(var(--ra-border) / 0.6);\n}\n.ra-shell .ra-row:hover {\n background: var(--ra-row-hover);\n}\n.ra-shell .ra-row[data-selected=true] {\n background: var(--ra-row-active-bg);\n border-left-color: var(--ra-row-active-bd);\n}\n.ra-shell .ra-row-compact {\n padding-block: 0.3rem;\n}\n.ra-shell .ra-row-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n flex-shrink: 0;\n}\n.ra-shell .ra-row[data-selected=true] .ra-row-icon {\n background: hsl(var(--ra-accent) / 0.15);\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-row-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-row-title {\n font-weight: var(--ra-title-weight);\n font-size: 0.8125rem;\n line-height: 1.2;\n color: hsl(var(--ra-text));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-sub {\n font-size: 0.6875rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.05rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-rule-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 0.2rem;\n margin-top: 0.2rem;\n}\n.ra-shell .ra-rule-chip {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n padding: 0.05rem 0.4rem;\n border-radius: 999px;\n font-size: 0.625rem;\n font-weight: 500;\n line-height: 1.4;\n background: hsl(var(--ra-accent) / 0.10);\n color: hsl(var(--ra-accent));\n border: 1px solid hsl(var(--ra-accent) / 0.20);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-rule-chip-more {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.ra-shell[data-density=compact] .ra-row-rule-chips {\n margin-top: 0.15rem;\n gap: 0.15rem;\n}\n.ra-shell[data-density=compact] .ra-rule-chip {\n font-size: 0.6rem;\n padding: 0.02rem 0.35rem;\n}\n.ra-shell .ra-rule-filters {\n display: flex;\n flex-direction: column;\n gap: 0.3rem;\n}\n.ra-shell .ra-rule-filters-row {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n}\n.ra-shell .ra-rule-filter-chip {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n padding: 0.15rem 0.5rem;\n border-radius: 999px;\n font-size: 0.65rem;\n font-weight: 500;\n line-height: 1.4;\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n border: 1px solid hsl(var(--ra-border));\n cursor: pointer;\n transition:\n background .12s ease,\n color .12s ease,\n border-color .12s ease;\n max-width: 100%;\n}\n.ra-shell .ra-rule-filter-chip:hover {\n background: hsl(var(--ra-accent) / 0.10);\n color: hsl(var(--ra-text));\n border-color: hsl(var(--ra-accent) / 0.25);\n}\n.ra-shell .ra-rule-filter-chip[data-active=true] {\n background: hsl(var(--ra-accent) / 0.15);\n color: hsl(var(--ra-accent));\n border-color: hsl(var(--ra-accent) / 0.40);\n}\n.ra-shell .ra-rule-filter-chip[data-tone=complexity][data-active=true] {\n background: hsl(var(--ra-info) / 0.15);\n color: hsl(var(--ra-info));\n border-color: hsl(var(--ra-info) / 0.40);\n}\n.ra-shell .ra-rule-filter-chip-label {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 9rem;\n}\n.ra-shell .ra-rule-filter-chip-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.1rem;\n height: 1rem;\n padding: 0 0.3rem;\n border-radius: 999px;\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-muted-text));\n font-size: 0.6rem;\n font-weight: 600;\n}\n.ra-shell .ra-rule-filter-chip[data-active=true] .ra-rule-filter-chip-count {\n background: hsl(var(--ra-accent) / 0.18);\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-rule-filter-clear {\n align-self: flex-start;\n background: transparent;\n border: 0;\n padding: 0;\n color: hsl(var(--ra-muted-text));\n font-size: 0.65rem;\n cursor: pointer;\n text-decoration: underline;\n text-decoration-style: dotted;\n}\n.ra-shell .ra-rule-filter-clear:hover {\n color: hsl(var(--ra-text));\n}\n.ra-shell[data-density=compact] .ra-row {\n padding-block: 0.3rem;\n gap: 0.45rem;\n}\n.ra-shell[data-density=compact] .ra-row-title {\n font-size: 0.78125rem;\n}\n.ra-shell .ra-row-actions {\n display: inline-flex;\n align-items: center;\n gap: 0.15rem;\n margin-left: auto;\n opacity: 0;\n transition: opacity .15s ease;\n}\n.ra-shell .ra-row:hover .ra-row-actions,\n.ra-shell .ra-row:focus-within .ra-row-actions {\n opacity: 1;\n}\n.ra-shell .ra-row-action {\n width: 1.6rem;\n height: 1.6rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .15s ease, color .15s ease;\n}\n.ra-shell .ra-row-action:hover {\n background: hsl(var(--ra-accent) / 0.10);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-row-action[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.12);\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-chip {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n padding: 0.15rem 0.5rem;\n border-radius: 999px;\n font-size: 0.6875rem;\n font-weight: 500;\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n border: 1px solid hsl(var(--ra-border));\n white-space: nowrap;\n max-width: 14rem;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-chip[data-tone=success] {\n background: hsl(var(--ra-success) / 0.12);\n color: hsl(var(--ra-success));\n border-color: hsl(var(--ra-success) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=warning] {\n background: hsl(var(--ra-warning) / 0.14);\n color: hsl(var(--ra-warning));\n border-color: hsl(var(--ra-warning) / 0.35);\n}\n.ra-shell .ra-chip[data-tone=info] {\n background: hsl(var(--ra-info) / 0.10);\n color: hsl(var(--ra-info));\n border-color: hsl(var(--ra-info) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=danger] {\n background: hsl(var(--ra-danger) / 0.10);\n color: hsl(var(--ra-danger));\n border-color: hsl(var(--ra-danger) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=muted] {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-style: dashed;\n}\n.ra-shell .ra-group {\n border-bottom: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-group:last-child {\n border-bottom: 0;\n}\n.ra-shell .ra-group-summary {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 0.85rem;\n background: hsl(var(--ra-muted) / 0.6);\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .12s ease;\n}\n.ra-shell .ra-group-summary:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-group-summary .ra-group-chevron {\n transition: transform .15s ease;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-chevron {\n transform: rotate(-90deg);\n}\n.ra-shell .ra-group-name {\n flex: 1;\n text-align: left;\n}\n.ra-shell .ra-group-count {\n font-size: 0.65rem;\n font-weight: 600;\n color: hsl(var(--ra-muted-text));\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: 999px;\n padding: 0.05rem 0.4rem;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-body {\n display: none;\n}\n.ra-shell .ra-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n padding: 2.5rem 1.5rem;\n gap: 0.75rem;\n}\n.ra-shell .ra-empty-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 3.25rem;\n height: 3.25rem;\n border-radius: 999px;\n background: hsl(var(--ra-accent) / 0.08);\n color: hsl(var(--ra-accent));\n margin-bottom: 0.25rem;\n}\n.ra-shell .ra-empty-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1rem;\n color: hsl(var(--ra-text));\n margin: 0;\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-empty-body {\n font-size: 0.8125rem;\n color: hsl(var(--ra-muted-text));\n max-width: 22rem;\n line-height: 1.45;\n}\n.ra-shell .ra-empty-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n flex-wrap: wrap;\n justify-content: center;\n}\n.ra-shell .ra-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.45rem 0.85rem;\n border-radius: calc(var(--ra-radius) * 0.7);\n font-size: 0.8125rem;\n font-weight: 500;\n border: 1px solid hsl(var(--ra-border));\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n cursor: pointer;\n transition:\n background .15s ease,\n border-color .15s ease,\n box-shadow .15s ease,\n transform .1s ease;\n}\n.ra-shell .ra-btn:hover {\n background: hsl(var(--ra-muted));\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-btn:active {\n transform: translateY(1px);\n}\n.ra-shell .ra-btn[data-variant=primary] {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-surface));\n border-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-btn[data-variant=primary]:hover {\n background: hsl(var(--ra-accent) / 0.92);\n}\n.ra-shell .ra-btn[data-variant=ghost] {\n background: transparent;\n border-color: transparent;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-btn[data-variant=ghost]:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-btn[data-variant=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-btn[data-variant=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n border-color: hsl(var(--ra-danger) / 0.40);\n}\n.ra-shell .ra-intro {\n position: relative;\n display: flex;\n align-items: center;\n gap: 0.55rem;\n padding: 0.4rem 2rem 0.4rem 0.5rem;\n border-radius: var(--ra-radius);\n border: 1px solid hsl(var(--ra-info) / 0.30);\n background: hsl(var(--ra-info) / 0.08);\n}\n.ra-shell .ra-intro[data-tone=success] {\n border-color: hsl(var(--ra-success) / 0.30);\n background: hsl(var(--ra-success) / 0.08);\n}\n.ra-shell .ra-intro[data-tone=warning] {\n border-color: hsl(var(--ra-warning) / 0.35);\n background: hsl(var(--ra-warning) / 0.10);\n}\n.ra-shell .ra-intro-icon {\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-info) / 0.18);\n color: hsl(var(--ra-info));\n}\n.ra-shell .ra-intro[data-tone=success] .ra-intro-icon {\n background: hsl(var(--ra-success) / 0.18);\n color: hsl(var(--ra-success));\n}\n.ra-shell .ra-intro[data-tone=warning] .ra-intro-icon {\n background: hsl(var(--ra-warning) / 0.20);\n color: hsl(var(--ra-warning));\n}\n.ra-shell .ra-intro-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-intro-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-title-weight);\n font-size: 0.8rem;\n color: hsl(var(--ra-text));\n margin: 0;\n line-height: 1.2;\n display: inline;\n}\n.ra-shell .ra-intro-text {\n font-size: 0.78rem;\n color: hsl(var(--ra-text) / 0.85);\n line-height: 1.35;\n display: inline;\n margin-left: 0.4rem;\n}\n.ra-shell .ra-intro-dismiss {\n position: absolute;\n top: 50%;\n right: 0.35rem;\n transform: translateY(-50%);\n width: 1.4rem;\n height: 1.4rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n color: hsl(var(--ra-muted-text));\n cursor: pointer;\n padding: 0;\n flex-shrink: 0;\n}\n.ra-shell .ra-intro-dismiss:hover {\n background: hsl(var(--ra-text) / 0.06);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-bulk-menu {\n min-width: 12rem;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: calc(var(--ra-radius) * 0.85);\n box-shadow: var(--ra-card-shadow-hover);\n padding: 0.3rem;\n z-index: 60;\n}\n.ra-shell .ra-bulk-item {\n display: flex;\n align-items: center;\n gap: 0.55rem;\n width: 100%;\n padding: 0.45rem 0.6rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n background: transparent;\n border: 0;\n cursor: pointer;\n text-align: left;\n transition: background .12s ease, color .12s ease;\n}\n.ra-shell .ra-bulk-item:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-bulk-item[data-tone=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-bulk-item[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n}\n.ra-shell .ra-bulk-divider {\n height: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-preview-rail {\n background: hsl(var(--ra-surface));\n border-left: 1px solid hsl(var(--ra-border));\n box-shadow: -4px 0 16px hsl(var(--ra-accent) / 0.04);\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n.ra-shell .ra-preview-rail-header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background:\n linear-gradient(\n 180deg,\n hsl(var(--ra-surface)) 0%,\n hsl(var(--ra-surface) / 0.92) 100%);\n border-bottom: 1px solid hsl(var(--ra-border));\n backdrop-filter: blur(6px);\n}\n.ra-shell .ra-preview-rail-title {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-preview-rail-body {\n flex: 1;\n overflow-y: auto;\n padding: 1rem;\n}\n.ra-confirm-root {\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem;\n background: transparent;\n}\n.ra-confirm-root .ra-confirm-backdrop {\n position: absolute;\n inset: 0;\n background: hsl(0 0% 0% / 0.45);\n backdrop-filter: blur(2px);\n animation: ra-confirm-fade .12s ease-out;\n}\n.ra-confirm-root .ra-confirm-card {\n position: relative;\n width: min(440px, 100%);\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: 0 1px 2px hsl(0 0% 0% / 0.08), 0 24px 48px -12px hsl(0 0% 0% / 0.32);\n padding: 1.25rem;\n animation: ra-confirm-pop .14s ease-out;\n}\n.ra-confirm-root .ra-confirm-header {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n margin-bottom: 0.5rem;\n}\n.ra-confirm-root .ra-confirm-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.75rem;\n height: 1.75rem;\n border-radius: 999px;\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.12);\n color: hsl(var(--ra-warning, 38 92% 50%));\n}\n.ra-confirm-root .ra-confirm-title {\n font-family: var(--ra-font-display);\n font-weight: 600;\n font-size: 1rem;\n margin: 0;\n}\n.ra-confirm-root .ra-confirm-body {\n font-size: 0.875rem;\n color: hsl(var(--ra-muted-text));\n margin: 0 0 1.1rem;\n line-height: 1.45;\n}\n.ra-confirm-root .ra-confirm-actions {\n display: flex;\n justify-content: flex-end;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n.ra-confirm-root .ra-confirm-btn {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid transparent;\n border-radius: calc(var(--ra-radius) - 2px);\n padding: 0.45rem 0.85rem;\n font-size: 0.8125rem;\n font-weight: 500;\n cursor: pointer;\n transition:\n background-color .12s ease,\n border-color .12s ease,\n color .12s ease;\n}\n.ra-confirm-root .ra-confirm-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n}\n.ra-confirm-root .ra-confirm-btn-ghost {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.ra-confirm-root .ra-confirm-btn-ghost:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-confirm-root .ra-confirm-btn-danger {\n background: transparent;\n color: hsl(var(--ra-danger, 0 72% 51%));\n border-color: hsl(var(--ra-danger, 0 72% 51%) / 0.45);\n}\n.ra-confirm-root .ra-confirm-btn-danger:hover {\n background: hsl(var(--ra-danger, 0 72% 51%) / 0.08);\n border-color: hsl(var(--ra-danger, 0 72% 51%));\n}\n.ra-confirm-root .ra-confirm-btn-primary {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-accent-fg, 0 0% 100%));\n border-color: hsl(var(--ra-accent));\n}\n.ra-confirm-root .ra-confirm-btn-primary:hover {\n filter: brightness(0.95);\n}\n@keyframes ra-confirm-fade {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes ra-confirm-pop {\n from {\n opacity: 0;\n transform: translateY(4px) scale(.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n.ra-shell .ra-unsaved-banner {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.5rem 0.75rem;\n border: 1px solid hsl(var(--ra-warning, 38 92% 50%) / 0.35);\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.08);\n border-radius: var(--ra-radius);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n animation: ra-unsaved-slide .14s ease-out;\n}\n.ra-shell .ra-unsaved-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: hsl(var(--ra-warning, 38 92% 50%));\n flex-shrink: 0;\n}\n.ra-shell .ra-unsaved-text {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ra-shell .ra-unsaved-context {\n color: hsl(var(--ra-muted-text));\n font-weight: 400;\n}\n.ra-shell .ra-unsaved-error {\n color: hsl(var(--ra-danger, 0 72% 51%));\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 500;\n}\n.ra-shell .ra-unsaved-actions {\n display: inline-flex;\n gap: 0.4rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-unsaved-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid transparent;\n border-radius: calc(var(--ra-radius) - 2px);\n padding: 0.3rem 0.6rem;\n font-size: 0.75rem;\n font-weight: 500;\n cursor: pointer;\n transition:\n background-color .12s ease,\n border-color .12s ease,\n color .12s ease,\n opacity .12s ease;\n}\n.ra-shell .ra-unsaved-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.ra-shell .ra-unsaved-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n}\n.ra-shell .ra-unsaved-btn-ghost {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.ra-shell .ra-unsaved-btn-ghost:hover:not(:disabled) {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-unsaved-btn-primary {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-accent-fg, 0 0% 100%));\n border-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-unsaved-btn-primary:hover:not(:disabled) {\n filter: brightness(0.95);\n}\n.sl-aph .ra-unsaved-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid transparent;\n border-radius: calc(var(--ra-radius, 8px) - 2px);\n padding: 0.3rem 0.6rem;\n font-size: 0.75rem;\n font-weight: 500;\n cursor: pointer;\n transition:\n background-color .12s ease,\n border-color .12s ease,\n color .12s ease,\n opacity .12s ease;\n}\n.sl-aph .ra-unsaved-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.sl-aph .ra-unsaved-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring, 0 0 0 3px hsl(var(--ra-accent) / 0.35));\n}\n.sl-aph .ra-unsaved-btn-ghost {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.sl-aph .ra-unsaved-btn-ghost:hover:not(:disabled) {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.sl-aph .ra-unsaved-btn-primary {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-accent-fg, 0 0% 100%));\n border-color: hsl(var(--ra-accent));\n}\n.sl-aph .ra-unsaved-btn-primary:hover:not(:disabled) {\n filter: brightness(0.95);\n}\n.sl-aph .ra-unsaved-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: hsl(var(--ra-warning, 38 92% 50%));\n flex-shrink: 0;\n}\n@keyframes ra-unsaved-slide {\n from {\n opacity: 0;\n transform: translateY(-3px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ra-shell .ra-unsaved-pill,\n.sl-aph .ra-unsaved-pill {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.25rem 0.5rem 0.25rem 0.6rem;\n border: 1px solid hsl(var(--ra-warning, 38 92% 50%) / 0.35);\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.08);\n border-radius: 999px;\n animation: ra-unsaved-slide .14s ease-out;\n}\n.ra-shell .ra-unsaved-pill .ra-unsaved-pill-text,\n.sl-aph .ra-unsaved-pill .ra-unsaved-pill-text {\n font-size: 0.75rem;\n font-weight: 500;\n color: hsl(var(--ra-text));\n white-space: nowrap;\n margin-right: 0.15rem;\n}\n.ra-shell .ra-clipboard-toast {\n position: fixed;\n bottom: 1.25rem;\n left: 50%;\n transform: translateX(-50%);\n z-index: 90;\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n max-width: min(28rem, calc(100vw - 2rem));\n padding: 0.55rem 0.85rem;\n border-radius: 999px;\n background: hsl(var(--ra-text));\n color: hsl(var(--ra-surface));\n font-size: 0.75rem;\n line-height: 1;\n box-shadow: 0 8px 24px -10px hsl(0 0% 0% / 0.45);\n animation: ra-clipboard-pop 0.18s ease-out both;\n pointer-events: none;\n}\n@keyframes ra-clipboard-pop {\n from {\n opacity: 0;\n transform: translate(-50%, 6px);\n }\n to {\n opacity: 1;\n transform: translate(-50%, 0);\n }\n}\n.ra-shell .ra-row-menu-wrap {\n display: inline-flex;\n align-items: center;\n margin-left: 0.25rem;\n}\n.ra-shell .ra-row-menu-trigger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n border-radius: 0.35rem;\n background: transparent;\n color: hsl(var(--ra-muted-text));\n opacity: 0;\n transition:\n opacity .15s ease,\n background .15s ease,\n color .15s ease;\n border: 1px solid transparent;\n}\n.ra-shell .ra-row:hover .ra-row-menu-trigger,\n.ra-shell .ra-card-hover:hover .ra-row-menu-trigger,\n.ra-shell .ra-row-menu-trigger:focus-visible,\n.ra-shell .ra-row-menu-trigger[aria-expanded=true] {\n opacity: 1;\n}\n.ra-shell .ra-row-menu-trigger:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-row-menu {\n position: absolute;\n right: 0;\n top: calc(100% + 4px);\n z-index: 50;\n min-width: 11rem;\n padding: 0.25rem;\n border-radius: 0.5rem;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n box-shadow: 0 12px 28px -10px hsl(0 0% 0% / 0.25);\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n}\n.ra-shell .ra-row-menu-item {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4rem 0.55rem;\n border-radius: 0.35rem;\n font-size: 0.75rem;\n color: hsl(var(--ra-text));\n background: transparent;\n border: 0;\n text-align: left;\n width: 100%;\n cursor: pointer;\n}\n.ra-shell .ra-row-menu-item:hover:not(:disabled) {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-row-menu-item:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ra-shell .ra-item-list {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n}\n.ra-shell .ra-item-list-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 1rem 1.25rem 1.5rem;\n}\n.ra-shell .ra-item-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 0.75rem 1.25rem;\n border-bottom: 1px solid hsl(var(--ra-border));\n background: hsl(var(--ra-surface));\n}\n.ra-shell .ra-item-toolbar-title {\n display: flex;\n align-items: baseline;\n gap: 0.5rem;\n min-width: 0;\n}\n.ra-shell .ra-item-toolbar-count {\n font-size: 0.7rem;\n font-weight: 600;\n color: hsl(var(--ra-muted-text));\n background: hsl(var(--ra-muted));\n border: 1px solid hsl(var(--ra-border));\n border-radius: 999px;\n padding: 0.05rem 0.45rem;\n}\n.ra-shell .ra-item-toolbar-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-item-table-wrap {\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n background: hsl(var(--ra-surface));\n overflow: hidden;\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-item-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.85rem;\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-item-table thead th {\n text-align: left;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n padding: 0.65rem 0.85rem;\n background: hsl(var(--ra-muted) / 0.55);\n border-bottom: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-item-table tbody td {\n padding: 0.65rem 0.85rem;\n border-bottom: 1px solid hsl(var(--ra-border) / 0.7);\n vertical-align: middle;\n}\n.ra-shell .ra-item-table tbody tr:last-child td {\n border-bottom: 0;\n}\n.ra-shell .ra-item-row {\n cursor: pointer;\n transition: background .12s ease;\n}\n.ra-shell .ra-item-row:hover {\n background: var(--ra-row-hover);\n}\n.ra-shell .ra-item-row[data-selected=true] {\n background: var(--ra-row-active-bg);\n}\n.ra-shell .ra-item-row-title {\n font-weight: var(--ra-title-weight);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-item-row-sub {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n}\n.ra-shell .ra-item-row-meta {\n font-size: 0.78rem;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-item-row-actions {\n text-align: right;\n white-space: nowrap;\n}\n.ra-shell .ra-item-row-actions .ra-row-action + .ra-row-action {\n margin-left: 0.15rem;\n}\n.ra-shell .ra-item-cards {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(var(--ra-item-card-min, 240px), 1fr));\n gap: 0.85rem;\n}\n.ra-shell .ra-item-gallery {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(var(--ra-item-gallery-min, 320px), 1fr));\n gap: 1rem;\n}\n.ra-shell .ra-item-cards[data-card-size=sm] {\n --ra-item-card-min: 180px;\n}\n.ra-shell .ra-item-cards[data-card-size=md] {\n --ra-item-card-min: 240px;\n}\n.ra-shell .ra-item-cards[data-card-size=lg] {\n --ra-item-card-min: 320px;\n gap: 1rem;\n}\n.ra-shell .ra-item-gallery[data-card-size=sm] {\n --ra-item-gallery-min: 240px;\n}\n.ra-shell .ra-item-gallery[data-card-size=md] {\n --ra-item-gallery-min: 320px;\n}\n.ra-shell .ra-item-gallery[data-card-size=lg] {\n --ra-item-gallery-min: 420px;\n gap: 1.25rem;\n}\n.ra-shell .ra-item-cards[data-card-size=lg] .ra-item-card-title,\n.ra-shell .ra-item-gallery[data-card-size=lg] .ra-item-card-title {\n font-size: 0.95rem;\n white-space: normal;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n.ra-shell .ra-item-cards[data-card-size=lg] .ra-item-card-body,\n.ra-shell .ra-item-gallery[data-card-size=lg] .ra-item-card-body {\n padding: 0.85rem 1rem 1rem;\n}\n.ra-shell .ra-item-card {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n text-align: left;\n padding: 0;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n overflow: hidden;\n cursor: pointer;\n transition:\n box-shadow .18s ease,\n transform .12s ease,\n border-color .15s ease;\n box-shadow: var(--ra-card-shadow);\n font-family: inherit;\n color: inherit;\n}\n.ra-shell .ra-item-card:hover {\n box-shadow: var(--ra-card-shadow-hover);\n border-color: hsl(var(--ra-accent) / 0.30);\n}\n.ra-shell .ra-item-card[data-selected=true] {\n border-color: hsl(var(--ra-accent) / 0.55);\n box-shadow: var(--ra-card-shadow-hover);\n}\n.ra-shell .ra-item-card-thumb {\n width: 100%;\n aspect-ratio: 1 / 1;\n background:\n linear-gradient(\n 135deg,\n hsl(var(--ra-accent) / 0.12),\n hsl(var(--ra-accent) / 0.04));\n display: flex;\n align-items: center;\n justify-content: center;\n color: hsl(var(--ra-accent));\n overflow: hidden;\n}\n.ra-shell .ra-item-card-thumb--gallery {\n aspect-ratio: 16 / 9;\n}\n.ra-shell .ra-item-card-thumb img {\n width: 100%;\n height: 100%;\n -o-object-fit: cover;\n object-fit: cover;\n}\n.ra-shell .ra-item-card-initials {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1.5rem;\n letter-spacing: 0.02em;\n}\n.ra-shell .ra-item-card-body {\n padding: 0.65rem 0.8rem 0.85rem;\n min-width: 0;\n}\n.ra-shell .ra-item-card-title {\n font-weight: var(--ra-title-weight);\n font-size: 0.85rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-item-card-sub {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-item-card-delete {\n position: absolute;\n top: 0.4rem;\n right: 0.4rem;\n background: hsl(var(--ra-surface) / 0.85);\n backdrop-filter: blur(4px);\n opacity: 0;\n transition: opacity .15s ease;\n}\n.ra-shell .ra-item-card:hover .ra-item-card-delete,\n.ra-shell .ra-item-card:focus-within .ra-item-card-delete {\n opacity: 1;\n}\n.ra-shell .ra-item-nav {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.5rem 1.25rem;\n border-bottom: 1px solid hsl(var(--ra-border));\n background: hsl(var(--ra-surface));\n}\n.ra-shell .ra-item-nav-position {\n font-size: 0.72rem;\n color: hsl(var(--ra-muted-text));\n font-variant-numeric: tabular-nums;\n}\n.ra-shell .ra-item-nav-arrows {\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 0.15rem;\n}\n.ra-shell .ra-item-nav-arrows .ra-row-action[disabled] {\n opacity: 0.35;\n cursor: not-allowed;\n}\n.ra-shell .ra-sibling-rail {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n}\n.ra-shell .ra-sibling-back {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.6rem 0.85rem;\n font-size: 0.75rem;\n font-weight: 500;\n color: hsl(var(--ra-muted-text));\n background: hsl(var(--ra-muted) / 0.5);\n border: 0;\n border-bottom: 1px solid hsl(var(--ra-border));\n cursor: pointer;\n text-align: left;\n transition: background .12s ease, color .12s ease;\n}\n.ra-shell .ra-sibling-back:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-sibling-heading {\n padding: 0.6rem 0.85rem 0.4rem;\n font-size: 0.65rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-sibling-body {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n}\n.ra-shell .ra-sibling-list {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n.ra-shell .ra-status-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n border-radius: 9999px;\n}\n.ra-shell .ra-status-icon > svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n.ra-shell .ra-status-icon--own {\n color: hsl(var(--ra-status-own));\n}\n.ra-shell .ra-status-icon--shared {\n color: hsl(var(--ra-status-shared));\n}\n.ra-shell .ra-status-icon--missing {\n color: hsl(var(--ra-status-missing) / 0.7);\n}\n.ra-shell .ra-row-status {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-row-scope {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.25rem;\n height: 1.25rem;\n border-radius: calc(var(--ra-radius) * 0.5);\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n flex-shrink: 0;\n margin-left: auto;\n opacity: 0.55;\n transition:\n opacity .12s ease,\n color .12s ease,\n background .12s ease;\n}\n.ra-shell .ra-row:hover .ra-row-scope {\n opacity: 0.85;\n}\n.ra-shell .ra-row[data-selected=true] .ra-row-scope {\n opacity: 1;\n background: hsl(var(--ra-accent) / 0.12);\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-row[data-tone=own] .ra-row-sub {\n color: hsl(var(--ra-status-own));\n}\n.ra-shell .ra-row[data-tone=shared] .ra-row-sub {\n color: hsl(var(--ra-status-shared));\n}\n.ra-shell .ra-row[data-selected=true] {\n background:\n linear-gradient(\n 90deg,\n hsl(var(--ra-accent) / 0.10) 0%,\n hsl(var(--ra-accent) / 0.04) 100%);\n border-left-width: 3px;\n border-left-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-dirty-pip {\n display: inline-block;\n width: 0.45rem;\n height: 0.45rem;\n border-radius: 9999px;\n background: hsl(var(--ra-warning));\n box-shadow: 0 0 0 2px hsl(var(--ra-warning) / 0.18);\n flex-shrink: 0;\n}\n.ra-shell .ra-error-pip {\n display: inline-block;\n width: 0.45rem;\n height: 0.45rem;\n border-radius: 9999px;\n background: hsl(var(--ra-danger, 0 72% 51%));\n box-shadow: 0 0 0 2px hsl(var(--ra-danger, 0 72% 51%) / 0.22);\n flex-shrink: 0;\n}\n.ra-shell .ra-group-summary {\n background: transparent;\n}\n.ra-shell {\n position: relative;\n}\n.ra-shell .ra-help-float {\n position: absolute;\n top: 0.65rem;\n right: 0.85rem;\n z-index: 5;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.6rem;\n height: 1.6rem;\n padding: 0;\n color: hsl(var(--ra-muted-text));\n background: hsl(var(--ra-surface) / 0.85);\n backdrop-filter: blur(6px);\n border: 1px solid hsl(var(--ra-border));\n border-radius: 999px;\n cursor: pointer;\n transition:\n color .12s ease,\n background .12s ease,\n border-color .12s ease;\n}\n.ra-shell .ra-help-float:hover {\n color: hsl(var(--ra-accent));\n border-color: hsl(var(--ra-accent) / 0.4);\n background: hsl(var(--ra-surface));\n}\n.ra-shell .ra-help-float svg {\n width: 0.95rem;\n height: 0.95rem;\n}\n.ra-shell .ra-help-float > span {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n.ra-shell .ra-preview-reopen {\n position: absolute;\n top: 50%;\n right: 0;\n transform: translateY(-50%);\n z-index: 4;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.4rem;\n padding: 0.65rem 0.45rem;\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-muted-text));\n border: 1px solid hsl(var(--ra-border));\n border-right: 0;\n border-radius: calc(var(--ra-radius) * 0.85) 0 0 calc(var(--ra-radius) * 0.85);\n box-shadow: var(--ra-card-shadow);\n cursor: pointer;\n transition:\n color .12s ease,\n background .12s ease,\n padding-right .15s ease;\n writing-mode: vertical-rl;\n font-size: 0.7rem;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n.ra-shell .ra-preview-reopen:hover {\n color: hsl(var(--ra-accent));\n background: hsl(var(--ra-accent) / 0.04);\n padding-right: 0.6rem;\n}\n.ra-shell .ra-preview-reopen svg {\n width: 0.85rem;\n height: 0.85rem;\n writing-mode: horizontal-tb;\n}\n.ra-shell .ra-unsaved-tray {\n position: relative;\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.5rem 0.75rem;\n border: 1px solid hsl(var(--ra-warning, 38 92% 50%) / 0.35);\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.08);\n border-radius: var(--ra-radius);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n animation: ra-unsaved-slide .14s ease-out;\n}\n.ra-shell .ra-unsaved-count {\n flex: 1;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n padding: 0.15rem 0.4rem;\n margin: -0.15rem -0.4rem;\n background: transparent;\n border: 0;\n color: inherit;\n font: inherit;\n font-weight: 500;\n text-align: left;\n cursor: pointer;\n border-radius: calc(var(--ra-radius) - 4px);\n}\n.ra-shell .ra-unsaved-count:hover {\n background: hsl(var(--ra-muted) / 0.6);\n}\n.ra-shell .ra-unsaved-error-chip {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid hsl(var(--ra-danger, 0 72% 51%) / 0.35);\n background: hsl(var(--ra-danger, 0 72% 51%) / 0.08);\n color: hsl(var(--ra-danger, 0 72% 51%));\n border-radius: 999px;\n padding: 0.15rem 0.55rem;\n font-size: 0.7rem;\n font-weight: 500;\n cursor: pointer;\n}\n.ra-shell .ra-unsaved-error-chip:hover {\n filter: brightness(0.97);\n}\n.ra-shell .ra-unsaved-popover {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n z-index: 60;\n min-width: 18rem;\n max-height: 18rem;\n overflow: auto;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: 0 12px 30px -10px hsl(0 0% 0% / 0.25);\n padding: 0.3rem;\n display: flex;\n flex-direction: column;\n gap: 0.15rem;\n}\n.ra-shell .ra-unsaved-popover-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4rem 0.55rem;\n background: transparent;\n border: 0;\n border-radius: calc(var(--ra-radius) - 2px);\n cursor: pointer;\n text-align: left;\n font: inherit;\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-unsaved-popover-row:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-unsaved-popover-dot {\n width: 0.5rem;\n height: 0.5rem;\n border-radius: 999px;\n flex-shrink: 0;\n}\n.ra-shell .ra-unsaved-popover-label {\n flex: 1;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 0.8125rem;\n}\n.ra-shell .ra-unsaved-popover-ctx {\n color: hsl(var(--ra-muted-text));\n font-size: 0.7rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n.ra-shell .ra-unsaved-popover-err {\n color: hsl(var(--ra-danger, 0 72% 51%));\n font-size: 0.7rem;\n font-weight: 500;\n}\n.ra-saveall-overlay {\n position: fixed;\n inset: 0;\n z-index: 100;\n background: hsl(0 0% 0% / 0.45);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem;\n animation: ra-confirm-fade .12s ease-out;\n}\n.ra-saveall-card {\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: 0 24px 48px -16px hsl(0 0% 0% / 0.45);\n width: min(28rem, 100%);\n max-height: min(80vh, 36rem);\n display: flex;\n flex-direction: column;\n animation: ra-confirm-pop .14s ease-out;\n}\n.ra-saveall-header {\n padding: 1rem 1rem 0.5rem;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n.ra-saveall-title {\n font-weight: 600;\n font-size: 0.95rem;\n}\n.ra-saveall-progress {\n height: 4px;\n background: hsl(var(--ra-muted));\n border-radius: 999px;\n overflow: hidden;\n}\n.ra-saveall-progress-bar {\n height: 100%;\n background: hsl(var(--ra-accent));\n transition: width .2s ease;\n}\n.ra-saveall-counter {\n color: hsl(var(--ra-muted-text));\n font-size: 0.75rem;\n font-variant-numeric: tabular-nums;\n}\n.ra-saveall-list {\n list-style: none;\n margin: 0;\n padding: 0.25rem 0.5rem;\n overflow: auto;\n flex: 1;\n}\n.ra-saveall-row {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.45rem 0.5rem;\n border-radius: calc(var(--ra-radius) - 4px);\n font-size: 0.8125rem;\n}\n.ra-saveall-row[data-status=saving] {\n background: hsl(var(--ra-accent) / 0.06);\n}\n.ra-saveall-row[data-status=saved] {\n color: hsl(var(--ra-muted-text));\n}\n.ra-saveall-row[data-status=error] {\n background: hsl(var(--ra-danger, 0 72% 51%) / 0.06);\n}\n.ra-saveall-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n color: hsl(var(--ra-muted-text));\n}\n.ra-saveall-row[data-status=saved] .ra-saveall-icon {\n color: hsl(var(--ra-success, 142 71% 45%));\n}\n.ra-saveall-row[data-status=saving] .ra-saveall-icon {\n color: hsl(var(--ra-accent));\n}\n.ra-saveall-row[data-status=error] .ra-saveall-icon {\n color: hsl(var(--ra-danger, 0 72% 51%));\n}\n.ra-saveall-label {\n flex: 1;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-saveall-err {\n color: hsl(var(--ra-danger, 0 72% 51%));\n font-size: 0.7rem;\n max-width: 12rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-saveall-actions {\n padding: 0.75rem 1rem 1rem;\n display: flex;\n justify-content: flex-end;\n gap: 0.4rem;\n border-top: 1px solid hsl(var(--ra-border));\n}\n.ra-spin {\n animation: ra-spin 1s linear infinite;\n}\n@keyframes ra-spin {\n to {\n transform: rotate(360deg);\n }\n}\n");
|
|
6072
6853
|
var EditorPoolBody = ({
|
|
6073
6854
|
editorId,
|
|
6074
6855
|
renderEditor
|
|
@@ -6078,6 +6859,7 @@ var EditorPoolBody = ({
|
|
|
6078
6859
|
return renderEditor(ctx);
|
|
6079
6860
|
};
|
|
6080
6861
|
var TOP_LEVEL_SCOPES = ["collection", "rule", "product"];
|
|
6862
|
+
var OPT_IN_TOP_LEVEL_SCOPES = ["all"];
|
|
6081
6863
|
var WARNED_FACET_DEPRECATED = false;
|
|
6082
6864
|
var DRAFT_ID3 = "__draft__";
|
|
6083
6865
|
var isDraftId3 = (id) => !!id && (id === DRAFT_ID3 || id.startsWith("draft:"));
|
|
@@ -6116,6 +6898,19 @@ function RecordsAdminShell(props) {
|
|
|
6116
6898
|
{
|
|
6117
6899
|
ctx,
|
|
6118
6900
|
defaultValueFactory: props.defaultData,
|
|
6901
|
+
hooks: props.hooks,
|
|
6902
|
+
onHookNotice: (notice) => {
|
|
6903
|
+
console.warn(`[RecordsAdmin] ${notice.kind} hook failed`, notice.error);
|
|
6904
|
+
try {
|
|
6905
|
+
props.onTelemetry?.({
|
|
6906
|
+
type: "record.hook.error",
|
|
6907
|
+
recordType: props.recordType,
|
|
6908
|
+
kind: notice.kind,
|
|
6909
|
+
message: notice.error instanceof Error ? notice.error.message : String(notice.error)
|
|
6910
|
+
});
|
|
6911
|
+
} catch {
|
|
6912
|
+
}
|
|
6913
|
+
},
|
|
6119
6914
|
children: /* @__PURE__ */ jsx(RecordsAdminShellInner, { ...props })
|
|
6120
6915
|
}
|
|
6121
6916
|
);
|
|
@@ -6134,7 +6929,7 @@ function RecordsAdminShellInner(props) {
|
|
|
6134
6929
|
renderEditor,
|
|
6135
6930
|
intro,
|
|
6136
6931
|
csvSchema,
|
|
6137
|
-
classify:
|
|
6932
|
+
classify: classify3,
|
|
6138
6933
|
defaultData,
|
|
6139
6934
|
i18n: i18nOverride,
|
|
6140
6935
|
onTelemetry,
|
|
@@ -6256,13 +7051,15 @@ function RecordsAdminShellInner(props) {
|
|
|
6256
7051
|
);
|
|
6257
7052
|
const queryClient = useQueryClient();
|
|
6258
7053
|
const probe = useScopeProbe({ SL, collectionId });
|
|
7054
|
+
const scopeCounts = useScopeCounts({ ctx });
|
|
6259
7055
|
const topLevelScopes = useMemo(() => {
|
|
6260
7056
|
const requested = requestedScopes ?? [];
|
|
7057
|
+
const allowed = /* @__PURE__ */ new Set([...TOP_LEVEL_SCOPES, ...OPT_IN_TOP_LEVEL_SCOPES]);
|
|
6261
7058
|
if (requested.length > 0) {
|
|
6262
7059
|
const seen = /* @__PURE__ */ new Set();
|
|
6263
7060
|
const ordered = [];
|
|
6264
7061
|
for (const s of requested) {
|
|
6265
|
-
if (!
|
|
7062
|
+
if (!allowed.has(s)) continue;
|
|
6266
7063
|
if (seen.has(s)) continue;
|
|
6267
7064
|
seen.add(s);
|
|
6268
7065
|
ordered.push(s);
|
|
@@ -6274,7 +7071,7 @@ function RecordsAdminShellInner(props) {
|
|
|
6274
7071
|
const productPinnedFromContext = !!contextScope?.productId;
|
|
6275
7072
|
const effectiveTopLevelScopes = useMemo(() => {
|
|
6276
7073
|
if (!productPinnedFromContext || contextScopeMode !== "strict") return topLevelScopes;
|
|
6277
|
-
return topLevelScopes.filter((s) => s !== "collection" && s !== "rule");
|
|
7074
|
+
return topLevelScopes.filter((s) => s !== "collection" && s !== "rule" && s !== "all");
|
|
6278
7075
|
}, [productPinnedFromContext, contextScopeMode, topLevelScopes]);
|
|
6279
7076
|
useEffect(() => {
|
|
6280
7077
|
if (requestedScopes.includes("facet") && !WARNED_FACET_DEPRECATED) {
|
|
@@ -6340,7 +7137,7 @@ function RecordsAdminShellInner(props) {
|
|
|
6340
7137
|
probeIsLoading: probe.isLoading,
|
|
6341
7138
|
selectedProductId,
|
|
6342
7139
|
drillTab,
|
|
6343
|
-
classify:
|
|
7140
|
+
classify: classify3
|
|
6344
7141
|
});
|
|
6345
7142
|
const {
|
|
6346
7143
|
search,
|
|
@@ -6374,13 +7171,16 @@ function RecordsAdminShellInner(props) {
|
|
|
6374
7171
|
if (first) setSelectedProductId(first.id);
|
|
6375
7172
|
}, [activeScope, selectedProductId, productBrowse.items]);
|
|
6376
7173
|
useEffect(() => {
|
|
6377
|
-
if (activeScope !== "rule" && activeScope !== "collection") return;
|
|
7174
|
+
if (activeScope !== "rule" && activeScope !== "collection" && activeScope !== "all") return;
|
|
6378
7175
|
if (selectedRecordId !== null) return;
|
|
6379
7176
|
if (ruleWizardStep !== null) return;
|
|
6380
7177
|
if (draftKind !== null) return;
|
|
6381
7178
|
if (activeScope === "collection" && cardinality === "collection") {
|
|
6382
7179
|
return;
|
|
6383
7180
|
}
|
|
7181
|
+
if (activeScope === "all" && cardinality === "collection") {
|
|
7182
|
+
return;
|
|
7183
|
+
}
|
|
6384
7184
|
const first = recordList.items[0];
|
|
6385
7185
|
if (first?.id) setSelectedRecordId(first.id);
|
|
6386
7186
|
}, [activeScope, selectedRecordId, recordList.items, cardinality, ruleWizardStep, draftKind]);
|
|
@@ -6407,6 +7207,10 @@ function RecordsAdminShellInner(props) {
|
|
|
6407
7207
|
// facetRule from the selected record's summary so `useCollectionItems`
|
|
6408
7208
|
// can match by rule equality instead of by anchors.
|
|
6409
7209
|
facetRule: editingScope?.kind === "rule" ? recordList.items.find((it) => it.id === selectedRecordId)?.facetRule ?? null : null,
|
|
7210
|
+
// 'all' tab + collection cardinality: pull every item-cardinality
|
|
7211
|
+
// record across the whole collection (anchored, ruled, global) so
|
|
7212
|
+
// host-supplied lifecycle grouping has the full picture.
|
|
7213
|
+
includeAll: isCollection && activeScope === "all",
|
|
6410
7214
|
enabled: isCollection
|
|
6411
7215
|
});
|
|
6412
7216
|
useEffect(() => {
|
|
@@ -6498,6 +7302,10 @@ function RecordsAdminShellInner(props) {
|
|
|
6498
7302
|
setRuleWizardRule(null);
|
|
6499
7303
|
setDraftKind(null);
|
|
6500
7304
|
}
|
|
7305
|
+
if (isCreate && selectedRecordId === DRAFT_ID3) {
|
|
7306
|
+
setSelectedRecordId(null);
|
|
7307
|
+
setDraftKind(null);
|
|
7308
|
+
}
|
|
6501
7309
|
refetchAll();
|
|
6502
7310
|
},
|
|
6503
7311
|
onDeleted: () => {
|
|
@@ -6663,7 +7471,8 @@ function RecordsAdminShellInner(props) {
|
|
|
6663
7471
|
deepLinkState,
|
|
6664
7472
|
onTelemetry,
|
|
6665
7473
|
onBeforeDelete,
|
|
6666
|
-
generateItemId
|
|
7474
|
+
generateItemId,
|
|
7475
|
+
hooks: props.hooks
|
|
6667
7476
|
});
|
|
6668
7477
|
const renderEditorWithPreview = () => {
|
|
6669
7478
|
if (!editingTargetScope) return null;
|
|
@@ -6703,13 +7512,33 @@ function RecordsAdminShellInner(props) {
|
|
|
6703
7512
|
bulkActions: { ...csvBulk, i18n },
|
|
6704
7513
|
preview: inlinePreviewBody,
|
|
6705
7514
|
footerExtra: extraFooter,
|
|
6706
|
-
targeting:
|
|
6707
|
-
|
|
7515
|
+
targeting: (
|
|
7516
|
+
// Mount the Targeting section for:
|
|
7517
|
+
// • Native rule scopes (existing behaviour — author the rule).
|
|
7518
|
+
// • Non-pinned Global (collection-root) scopes — admins can grow
|
|
7519
|
+
// a rule on the fly to convert Global → Rule, or stay global.
|
|
7520
|
+
// Pinned scopes (product / variant / batch / proof) are anchored
|
|
7521
|
+
// by ID, not by rule, so the section would be a no-op there.
|
|
7522
|
+
editingTargetScope.kind === "rule" || editingTargetScope.kind === "collection" && !editingTargetScope.productId && !editingTargetScope.variantId && !editingTargetScope.batchId && !editingTargetScope.proofId ? /* @__PURE__ */ jsx(
|
|
7523
|
+
RecordTargeting,
|
|
7524
|
+
{
|
|
7525
|
+
SL,
|
|
7526
|
+
collectionId,
|
|
7527
|
+
appId,
|
|
7528
|
+
ctx: editorCtx,
|
|
7529
|
+
forceOpen: targetingExpandNonce > 0,
|
|
7530
|
+
onOpenChange: (o) => {
|
|
7531
|
+
if (!o) setTargetingExpandNonce(0);
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
) : void 0
|
|
7535
|
+
),
|
|
7536
|
+
targetingControl: editingTargetScope.kind === "rule" || editingTargetScope.kind === "collection" && !editingTargetScope.productId && !editingTargetScope.variantId && !editingTargetScope.batchId && !editingTargetScope.proofId ? /* @__PURE__ */ jsx(
|
|
7537
|
+
TargetingPopover,
|
|
6708
7538
|
{
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
ctx: editorCtx
|
|
7539
|
+
ctx: editorCtx,
|
|
7540
|
+
catalogue: ruleCatalogue,
|
|
7541
|
+
onCustomise: () => setTargetingExpandNonce((n) => n + 1)
|
|
6713
7542
|
}
|
|
6714
7543
|
) : void 0,
|
|
6715
7544
|
onBeforeDelete: onBeforeDelete && editingTargetScope ? () => onBeforeDelete(editingTargetScope) : void 0,
|
|
@@ -6827,7 +7656,6 @@ function RecordsAdminShellInner(props) {
|
|
|
6827
7656
|
]);
|
|
6828
7657
|
const effectivePresentation = isProductTab ? presentation : "list";
|
|
6829
7658
|
const showPresentationSwitcher = isProductTab && presentations.length > 1;
|
|
6830
|
-
const effectiveGroupBy = groupBy;
|
|
6831
7659
|
const productListItems = useMemo(() => {
|
|
6832
7660
|
if (productPinned) {
|
|
6833
7661
|
const pid = contextScope.productId;
|
|
@@ -6845,7 +7673,66 @@ function RecordsAdminShellInner(props) {
|
|
|
6845
7673
|
}, [productPinned, contextScope, productBrowse.items, pinnedProduct.item]);
|
|
6846
7674
|
const isRuleTab = activeScope === "rule";
|
|
6847
7675
|
const isGlobalTab = activeScope === "collection";
|
|
6848
|
-
const
|
|
7676
|
+
const isAllTab = activeScope === "all";
|
|
7677
|
+
const isRecordsTab = isRuleTab || isGlobalTab || isAllTab;
|
|
7678
|
+
const effectiveGroupBy = useMemo(() => {
|
|
7679
|
+
if (groupBy) return groupBy;
|
|
7680
|
+
if (isAllTab) return void 0;
|
|
7681
|
+
if (!isRuleTab) return void 0;
|
|
7682
|
+
if (isCollection) return void 0;
|
|
7683
|
+
return (record) => {
|
|
7684
|
+
const hash = ruleHash(record.facetRule);
|
|
7685
|
+
if (!hash) return null;
|
|
7686
|
+
return { key: `rule:${hash}`, label: summariseRule(record.facetRule) };
|
|
7687
|
+
};
|
|
7688
|
+
}, [groupBy, isRuleTab, isAllTab, isCollection]);
|
|
7689
|
+
const [bulkRuleEditTarget, setBulkRuleEditTarget] = useState(null);
|
|
7690
|
+
const ruleCatalogue = useMemo(() => {
|
|
7691
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
7692
|
+
for (const item of recordList.items) {
|
|
7693
|
+
const hash = ruleHash(item.facetRule);
|
|
7694
|
+
if (!hash || !item.facetRule) continue;
|
|
7695
|
+
const existing = buckets.get(hash);
|
|
7696
|
+
if (existing) {
|
|
7697
|
+
existing.count += 1;
|
|
7698
|
+
} else {
|
|
7699
|
+
buckets.set(hash, {
|
|
7700
|
+
hash,
|
|
7701
|
+
rule: item.facetRule,
|
|
7702
|
+
summary: summariseRule(item.facetRule),
|
|
7703
|
+
count: 1
|
|
7704
|
+
});
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
7707
|
+
return Array.from(buckets.values()).sort((a, b) => {
|
|
7708
|
+
if (b.count !== a.count) return b.count - a.count;
|
|
7709
|
+
return a.summary.localeCompare(b.summary);
|
|
7710
|
+
});
|
|
7711
|
+
}, [recordList.items]);
|
|
7712
|
+
const [targetingExpandNonce, setTargetingExpandNonce] = useState(0);
|
|
7713
|
+
const renderRuleGroupActions = useCallback(
|
|
7714
|
+
(group) => {
|
|
7715
|
+
if (!isRuleTab || groupBy) return null;
|
|
7716
|
+
const ids = group.items.map((it) => it.id).filter((id) => !!id);
|
|
7717
|
+
if (ids.length < 2) return null;
|
|
7718
|
+
const rule = group.items.find((it) => it.facetRule)?.facetRule ?? null;
|
|
7719
|
+
return /* @__PURE__ */ jsx(
|
|
7720
|
+
"button",
|
|
7721
|
+
{
|
|
7722
|
+
type: "button",
|
|
7723
|
+
className: "text-[11px] px-2 py-0.5 rounded border hover:bg-[hsl(var(--ra-muted))] transition-colors",
|
|
7724
|
+
style: {
|
|
7725
|
+
borderColor: "hsl(var(--ra-border))",
|
|
7726
|
+
color: "hsl(var(--ra-muted-text))"
|
|
7727
|
+
},
|
|
7728
|
+
title: `Edit this rule and apply the change to all ${ids.length} records that share it`,
|
|
7729
|
+
onClick: () => setBulkRuleEditTarget({ rule, recordIds: ids }),
|
|
7730
|
+
children: "Edit rule"
|
|
7731
|
+
}
|
|
7732
|
+
);
|
|
7733
|
+
},
|
|
7734
|
+
[isRuleTab, groupBy]
|
|
7735
|
+
);
|
|
6849
7736
|
const onCreateRule = useCallback(() => {
|
|
6850
7737
|
void runWithGuard(() => {
|
|
6851
7738
|
if (activeScope !== "rule") setActiveScope("rule");
|
|
@@ -6897,6 +7784,32 @@ function RecordsAdminShellInner(props) {
|
|
|
6897
7784
|
() => isRuleTab ? applyRuleFilters(recordList.items, ruleFilters) : recordList.items,
|
|
6898
7785
|
[isRuleTab, recordList.items, ruleFilters]
|
|
6899
7786
|
);
|
|
7787
|
+
const collectionRuleRailItems = useMemo(() => {
|
|
7788
|
+
if (!isRuleTab || !isCollection) return filteredRuleItems;
|
|
7789
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
7790
|
+
for (const item of filteredRuleItems) {
|
|
7791
|
+
const hash = ruleHash(item.facetRule);
|
|
7792
|
+
if (!hash) continue;
|
|
7793
|
+
const existing = buckets.get(hash);
|
|
7794
|
+
if (existing) {
|
|
7795
|
+
existing.count += 1;
|
|
7796
|
+
} else {
|
|
7797
|
+
buckets.set(hash, { rep: item, count: 1 });
|
|
7798
|
+
}
|
|
7799
|
+
}
|
|
7800
|
+
return Array.from(buckets.values()).map(({ rep, count }) => ({
|
|
7801
|
+
...rep,
|
|
7802
|
+
label: summariseRule(rep.facetRule),
|
|
7803
|
+
subtitle: `${count} ${itemNoun}${count === 1 ? "" : "s"}`
|
|
7804
|
+
}));
|
|
7805
|
+
}, [isRuleTab, isCollection, filteredRuleItems, itemNoun]);
|
|
7806
|
+
const activeRuleSummary = useMemo(() => {
|
|
7807
|
+
if (!isRuleTab || !isCollection) return null;
|
|
7808
|
+
if (!selectedRecordId || isDraftId3(selectedRecordId)) return null;
|
|
7809
|
+
const hit = recordList.items.find((it) => it.id === selectedRecordId);
|
|
7810
|
+
if (!hit?.facetRule) return null;
|
|
7811
|
+
return summariseRule(hit.facetRule);
|
|
7812
|
+
}, [isRuleTab, isCollection, selectedRecordId, recordList.items]);
|
|
6900
7813
|
const applyFacetBrowseFilter = useCallback(
|
|
6901
7814
|
(items2) => {
|
|
6902
7815
|
if (!facetBrowseFilter) return items2;
|
|
@@ -6944,7 +7857,9 @@ function RecordsAdminShellInner(props) {
|
|
|
6944
7857
|
}),
|
|
6945
7858
|
[i18n.itemsAllLabel, collectionItems.items.length, itemNoun]
|
|
6946
7859
|
);
|
|
6947
|
-
const leftItems = isProductTab ? productListItems : isRuleTab ? applyFacetBrowseFilter(
|
|
7860
|
+
const leftItems = isProductTab ? productListItems : isRuleTab ? applyFacetBrowseFilter(
|
|
7861
|
+
isCollection ? collectionRuleRailItems : filteredRuleItems
|
|
7862
|
+
) : (isGlobalTab || isAllTab) && isCollection ? [collectionGlobalAllRow] : isRecordsTab ? applyFacetBrowseFilter(recordList.items) : [];
|
|
6948
7863
|
const leftLoading = isProductTab ? !productPinned && productBrowse.isLoading : isRecordsTab ? recordList.isLoading || probe.isLoading : false;
|
|
6949
7864
|
const leftError = isProductTab ? productBrowse.error : isRecordsTab ? recordList.error : null;
|
|
6950
7865
|
const leftSelectedId = isProductTab ? void 0 : selectedRecordId && selectedRecordId !== DRAFT_ID3 ? selectedRecordId : void 0;
|
|
@@ -7157,8 +8072,22 @@ function RecordsAdminShellInner(props) {
|
|
|
7157
8072
|
},
|
|
7158
8073
|
loading: probe.isLoading,
|
|
7159
8074
|
counts: {
|
|
7160
|
-
product
|
|
8075
|
+
// `product` continues to show the catalogue size (browse
|
|
8076
|
+
// affordance), not the per-product record count — that's
|
|
8077
|
+
// the long-standing semantics for the Products tab.
|
|
8078
|
+
product: productBrowse.items.length,
|
|
8079
|
+
// The remaining tabs show actual record counts so hidden
|
|
8080
|
+
// state (e.g. a rule-scoped competition) is visible from
|
|
8081
|
+
// any tab. `useScopeCounts` returns 0 while loading, which
|
|
8082
|
+
// is fine — the badges just appear once data lands.
|
|
8083
|
+
collection: scopeCounts.counts.collection,
|
|
8084
|
+
rule: scopeCounts.counts.rule,
|
|
8085
|
+
variant: scopeCounts.counts.variant,
|
|
8086
|
+
batch: scopeCounts.counts.batch,
|
|
8087
|
+
facet: scopeCounts.counts.facet,
|
|
8088
|
+
all: scopeCounts.counts.all
|
|
7161
8089
|
},
|
|
8090
|
+
tooltips: { rule: i18n.rulesTabTooltip },
|
|
7162
8091
|
icons: icons.scope
|
|
7163
8092
|
}
|
|
7164
8093
|
) }),
|
|
@@ -7254,7 +8183,7 @@ function RecordsAdminShellInner(props) {
|
|
|
7254
8183
|
{
|
|
7255
8184
|
icon: search ? icons.empty.search : icons.empty.default,
|
|
7256
8185
|
title: search ? i18n.noResults : i18n.railEmptyTitle,
|
|
7257
|
-
body: search ? void 0 : i18n.railEmptyBody
|
|
8186
|
+
body: search ? void 0 : isRuleTab ? i18n.rulesEmptyBody : i18n.railEmptyBody
|
|
7258
8187
|
}
|
|
7259
8188
|
)),
|
|
7260
8189
|
!leftLoading && !leftError && leftItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -7272,6 +8201,7 @@ function RecordsAdminShellInner(props) {
|
|
|
7272
8201
|
presentation: effectivePresentation,
|
|
7273
8202
|
renderListRow,
|
|
7274
8203
|
groupBy: effectiveGroupBy,
|
|
8204
|
+
renderGroupActions: renderRuleGroupActions,
|
|
7275
8205
|
rowClipboard,
|
|
7276
8206
|
i18n
|
|
7277
8207
|
}
|
|
@@ -7370,6 +8300,7 @@ function RecordsAdminShellInner(props) {
|
|
|
7370
8300
|
renderItemEmpty,
|
|
7371
8301
|
itemColumns,
|
|
7372
8302
|
cardSize: itemCardSize,
|
|
8303
|
+
ruleSummary: activeRuleSummary,
|
|
7373
8304
|
i18n
|
|
7374
8305
|
}
|
|
7375
8306
|
),
|
|
@@ -7415,6 +8346,22 @@ function RecordsAdminShellInner(props) {
|
|
|
7415
8346
|
] })
|
|
7416
8347
|
]
|
|
7417
8348
|
}
|
|
8349
|
+
),
|
|
8350
|
+
/* @__PURE__ */ jsx(
|
|
8351
|
+
RuleGroupEditDialog,
|
|
8352
|
+
{
|
|
8353
|
+
open: !!bulkRuleEditTarget,
|
|
8354
|
+
ctx,
|
|
8355
|
+
currentRule: bulkRuleEditTarget?.rule ?? null,
|
|
8356
|
+
recordIds: bulkRuleEditTarget?.recordIds ?? [],
|
|
8357
|
+
onClose: () => setBulkRuleEditTarget(null),
|
|
8358
|
+
onApplied: () => {
|
|
8359
|
+
recordList.refetch();
|
|
8360
|
+
queryClient.invalidateQueries({
|
|
8361
|
+
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
8362
|
+
});
|
|
8363
|
+
}
|
|
8364
|
+
}
|
|
7418
8365
|
)
|
|
7419
8366
|
]
|
|
7420
8367
|
}
|
|
@@ -7839,8 +8786,8 @@ function useRecordEditor(args) {
|
|
|
7839
8786
|
const anchors = parsedRefToScope(scope);
|
|
7840
8787
|
const hasAnchors = !!(anchors.productId || anchors.variantId || anchors.batchId || anchors.proofId);
|
|
7841
8788
|
const hasRule = !!(facetRule && facetRule.all && facetRule.all.length > 0);
|
|
7842
|
-
const
|
|
7843
|
-
if (
|
|
8789
|
+
const isRuleScope = scope.kind === "rule";
|
|
8790
|
+
if (isRuleScope && !hasAnchors && !hasRule && !resolved.recordId && !createMode) {
|
|
7844
8791
|
console.warn("[useRecordEditor] save() bailed \u2014 rule scope with no clauses, no recordId, not in createMode");
|
|
7845
8792
|
return;
|
|
7846
8793
|
}
|
|
@@ -7881,23 +8828,28 @@ function useRecordEditor(args) {
|
|
|
7881
8828
|
});
|
|
7882
8829
|
} else if (createMode) {
|
|
7883
8830
|
await createRecord(ctx, {
|
|
7884
|
-
|
|
8831
|
+
// The framework does NOT write to `record.ref` — that's a
|
|
8832
|
+
// host-owned field. Rule identity flows through `facetRule`
|
|
8833
|
+
// (content-addressed via `ruleHash`); record identity is the
|
|
8834
|
+
// UUID assigned by the server.
|
|
8835
|
+
ref: void 0,
|
|
7885
8836
|
scope: anchors,
|
|
7886
8837
|
data: value,
|
|
7887
8838
|
facetRule
|
|
7888
8839
|
});
|
|
7889
8840
|
} else {
|
|
7890
8841
|
await upsertRecord(ctx, {
|
|
7891
|
-
//
|
|
7892
|
-
|
|
7893
|
-
// anchor-only writes — server addresses by anchors.
|
|
7894
|
-
ref: scope.kind === "rule" && scope.raw ? scope.raw : void 0,
|
|
8842
|
+
// See note above — never write to `ref`.
|
|
8843
|
+
ref: void 0,
|
|
7895
8844
|
scope: anchors,
|
|
7896
8845
|
data: value,
|
|
7897
8846
|
facetRule
|
|
7898
8847
|
});
|
|
7899
8848
|
}
|
|
7900
8849
|
draftStore.clearDraft(draftKey);
|
|
8850
|
+
queryClient.invalidateQueries({
|
|
8851
|
+
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
8852
|
+
});
|
|
7901
8853
|
onSaved?.();
|
|
7902
8854
|
} catch (err) {
|
|
7903
8855
|
setSavedSnapshot(previousSnapshot);
|
|
@@ -7926,6 +8878,9 @@ function useRecordEditor(args) {
|
|
|
7926
8878
|
if (!resolved.recordId) return;
|
|
7927
8879
|
await removeRecord(ctx, resolved.recordId);
|
|
7928
8880
|
draftStore.clearDraft(draftKey);
|
|
8881
|
+
queryClient.invalidateQueries({
|
|
8882
|
+
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
8883
|
+
});
|
|
7929
8884
|
onDeleted?.();
|
|
7930
8885
|
}, [resolved.source, resolved.recordId, draftKey]);
|
|
7931
8886
|
const prevDraftKeyRef = useRef(draftKey);
|
|
@@ -7991,7 +8946,9 @@ function useRecordEditor(args) {
|
|
|
7991
8946
|
baselineFacetRule: savedFacetRule,
|
|
7992
8947
|
createMode,
|
|
7993
8948
|
scopeAnchors: anchors,
|
|
7994
|
-
|
|
8949
|
+
// Draft-store entry never carries a framework-derived `ref`. The
|
|
8950
|
+
// value belongs to the host; we don't even pass it through here.
|
|
8951
|
+
ref: void 0,
|
|
7995
8952
|
saveKind,
|
|
7996
8953
|
save: async () => {
|
|
7997
8954
|
await save();
|
|
@@ -7999,8 +8956,8 @@ function useRecordEditor(args) {
|
|
|
7999
8956
|
});
|
|
8000
8957
|
}, [isDirty, value, facetRule, savedSnapshot, savedFacetRule, scope.raw, resolved.recordId, resolved.source, createMode, save]);
|
|
8001
8958
|
const effectiveSource = optimisticSource ?? resolved.source;
|
|
8002
|
-
const
|
|
8003
|
-
const ruleValid = !
|
|
8959
|
+
const hasRuleInFlight = !!(facetRule && facetRule.all && facetRule.all.length > 0);
|
|
8960
|
+
const ruleValid = !hasRuleInFlight || isFacetRuleValid(facetRule);
|
|
8004
8961
|
const canSave = ruleValid;
|
|
8005
8962
|
const cannotSaveReason = !ruleValid ? "Pick at least one value for every facet in the rule before saving." : void 0;
|
|
8006
8963
|
return {
|
|
@@ -8263,6 +9220,6 @@ function useMergedRecord(args) {
|
|
|
8263
9220
|
};
|
|
8264
9221
|
}
|
|
8265
9222
|
|
|
8266
|
-
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, 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, downloadBlob, exportCsv, importCsv, isInSmartLinksIframe, mergeIcons, parseRef, pickHeaderIcon, resolutionChain, resolveRecord, statusToneLabel, useCollectedRecords, useCollectionItems, useDeepLinkState, useDirtyDraft, useDirtyDraftActions, useDirtyDraftStore, useDirtyDrafts, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useItemViewPref, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordClipboard, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };
|
|
9223
|
+
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, 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, 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 };
|
|
8267
9224
|
//# sourceMappingURL=index.js.map
|
|
8268
9225
|
//# sourceMappingURL=index.js.map
|