@proveanything/smartlinks-utils-ui 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-KFKVGUUP.js → chunk-KA4MKRHL.js} +2 -4
- package/dist/chunk-KA4MKRHL.js.map +1 -0
- package/dist/components/AssetPicker/index.css +53 -17
- package/dist/components/AssetPicker/index.css.map +1 -1
- package/dist/components/ConditionsEditor/index.css +53 -17
- package/dist/components/ConditionsEditor/index.css.map +1 -1
- package/dist/components/FontPicker/index.css +53 -17
- package/dist/components/FontPicker/index.css.map +1 -1
- package/dist/components/IconPicker/index.css +53 -17
- package/dist/components/IconPicker/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.css +53 -17
- package/dist/components/RecordsAdmin/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.d.ts +15 -2
- package/dist/components/RecordsAdmin/index.js +212 -139
- package/dist/components/RecordsAdmin/index.js.map +1 -1
- package/dist/index.css +53 -17
- package/dist/index.css.map +1 -1
- package/dist/records-4NN757SG.js +3 -0
- package/dist/{records-AYYQSP7E.js.map → records-4NN757SG.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-KFKVGUUP.js.map +0 -1
- package/dist/records-AYYQSP7E.js +0 -3
|
@@ -2417,11 +2417,24 @@ declare const normaliseRule: (rule: FacetRule | null | undefined) => NormalisedR
|
|
|
2417
2417
|
* `null` so the caller can route them to a separate "no rule" bucket.
|
|
2418
2418
|
*/
|
|
2419
2419
|
declare const ruleHash: (rule: FacetRule | null | undefined) => string | null;
|
|
2420
|
+
/**
|
|
2421
|
+
* Optional label lookup — turn raw facet/value KEYS (e.g. `cooking_guide`,
|
|
2422
|
+
* `over-cook`) into the human-readable LABELS the host already shows
|
|
2423
|
+
* elsewhere (e.g. "Cooking Guide", "Over-cook"). Pass via `summariseRule`
|
|
2424
|
+
* so rail headers, right-pane summaries and chips never expose raw keys
|
|
2425
|
+
* when a friendly name is available.
|
|
2426
|
+
*/
|
|
2427
|
+
interface RuleLabelLookup {
|
|
2428
|
+
facetLabel?: (facetKey: string) => string | undefined;
|
|
2429
|
+
valueLabel?: (facetKey: string, valueKey: string) => string | undefined;
|
|
2430
|
+
}
|
|
2420
2431
|
/**
|
|
2421
2432
|
* Human-readable single-line summary of a rule, used for group headers.
|
|
2422
|
-
* Compact ("
|
|
2433
|
+
* Compact ("Country: Germany, France · Tier: Premium") so it fits a
|
|
2434
|
+
* rail-width header. Falls back to raw keys when no lookup is supplied
|
|
2435
|
+
* or the lookup returns undefined for a given key.
|
|
2423
2436
|
*/
|
|
2424
|
-
declare const summariseRule: (rule: FacetRule | null | undefined) => string;
|
|
2437
|
+
declare const summariseRule: (rule: FacetRule | null | undefined, lookup?: RuleLabelLookup) => string;
|
|
2425
2438
|
/** Comparator: are two rules semantically identical? */
|
|
2426
2439
|
declare const rulesEqual: (a: FacetRule | null | undefined, b: FacetRule | null | undefined) => boolean;
|
|
2427
2440
|
|
|
@@ -3,9 +3,9 @@ import '../../chunk-5UQQYXCX.js';
|
|
|
3
3
|
import { FacetRuleEditor } from '../../chunk-JMCV6FOW.js';
|
|
4
4
|
import { useFacets } from '../../chunk-4LHF5JB7.js';
|
|
5
5
|
import { cn } from '../../chunk-L7FQ52F5.js';
|
|
6
|
-
import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-
|
|
7
|
-
export { bulkDelete, bulkUpsert, createRecord, getRecordById, listRecords, matchRecords, parsedRefToScope, parsedRefToTarget, removeRecord, restoreRecord, scopesEqual, upsertRecord } from '../../chunk-
|
|
8
|
-
import { createContext, useState, useEffect, useCallback, useMemo, useRef, useContext, useSyncExternalStore, createElement, useId } from 'react';
|
|
6
|
+
import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-KA4MKRHL.js';
|
|
7
|
+
export { bulkDelete, bulkUpsert, createRecord, getRecordById, listRecords, matchRecords, parsedRefToScope, parsedRefToTarget, removeRecord, restoreRecord, scopesEqual, upsertRecord } from '../../chunk-KA4MKRHL.js';
|
|
8
|
+
import { createContext, useState, useEffect, useCallback, useMemo, useRef, useContext, useSyncExternalStore, useLayoutEffect, createElement, useId } from 'react';
|
|
9
9
|
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, Search, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, CornerDownLeft, Circle, AlertCircle, Undo2, Save, Loader2, XCircle, ArrowRight, BookOpen, Globe2, Target, Check, Settings2 } from 'lucide-react';
|
|
10
10
|
import { useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
|
|
11
11
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -268,10 +268,12 @@ var ruleHash = (rule) => {
|
|
|
268
268
|
if (!norm) return null;
|
|
269
269
|
return fnv1a(JSON.stringify(norm));
|
|
270
270
|
};
|
|
271
|
-
var summariseRule = (rule) => {
|
|
271
|
+
var summariseRule = (rule, lookup) => {
|
|
272
272
|
const norm = normaliseRule(rule);
|
|
273
273
|
if (!norm) return "No rule";
|
|
274
|
-
|
|
274
|
+
const fLabel = (k) => lookup?.facetLabel?.(k) ?? k;
|
|
275
|
+
const vLabel = (k, v) => lookup?.valueLabel?.(k, v) ?? v;
|
|
276
|
+
return norm.all.map((c) => `${fLabel(c.facetKey)}: ${c.anyOf.map((v) => vLabel(c.facetKey, v)).join(", ")}`).join(" \xB7 ");
|
|
275
277
|
};
|
|
276
278
|
var rulesEqual = (a, b) => ruleHash(a) === ruleHash(b);
|
|
277
279
|
|
|
@@ -281,18 +283,6 @@ var resolveRecord = async (args) => {
|
|
|
281
283
|
const editingScope = parsedRefToScope(args.target);
|
|
282
284
|
const result = await matchRecords(args.ctx, target, { strategy: "all" }).catch(() => null);
|
|
283
285
|
const entries = result?.data ?? [];
|
|
284
|
-
console.info("[RecordsAdmin/resolveRecord]", {
|
|
285
|
-
editingScope,
|
|
286
|
-
target,
|
|
287
|
-
matchCount: entries.length,
|
|
288
|
-
winnerAnchors: entries[0] && {
|
|
289
|
-
productId: entries[0].productId,
|
|
290
|
-
variantId: entries[0].variantId,
|
|
291
|
-
batchId: entries[0].batchId,
|
|
292
|
-
proofId: entries[0].proofId
|
|
293
|
-
},
|
|
294
|
-
winnerRef: entries[0]?.ref
|
|
295
|
-
});
|
|
296
286
|
if (entries.length === 0) {
|
|
297
287
|
return { data: null, source: "empty" };
|
|
298
288
|
}
|
|
@@ -306,16 +296,6 @@ var resolveRecord = async (args) => {
|
|
|
306
296
|
},
|
|
307
297
|
editingScope
|
|
308
298
|
);
|
|
309
|
-
console.info("[RecordsAdmin/resolveRecord] classification", {
|
|
310
|
-
winnerIsSelf,
|
|
311
|
-
winnerAnchors: {
|
|
312
|
-
productId: winner.productId,
|
|
313
|
-
variantId: winner.variantId,
|
|
314
|
-
batchId: winner.batchId,
|
|
315
|
-
proofId: winner.proofId
|
|
316
|
-
},
|
|
317
|
-
editingScope
|
|
318
|
-
});
|
|
319
299
|
if (winnerIsSelf) {
|
|
320
300
|
const parent = entries[1];
|
|
321
301
|
return {
|
|
@@ -1834,11 +1814,6 @@ function useShellNavigation(args) {
|
|
|
1834
1814
|
const onItemOpen = useCallback((itemId) => {
|
|
1835
1815
|
if (!isCollection) return;
|
|
1836
1816
|
void runWithGuard(() => {
|
|
1837
|
-
console.info("[RecordsAdminShell] item open requested", {
|
|
1838
|
-
itemId,
|
|
1839
|
-
scopeRef: baseScopeRef,
|
|
1840
|
-
previousSelectedItemId: selectedItemId
|
|
1841
|
-
});
|
|
1842
1817
|
if (!isDraftId(itemId)) {
|
|
1843
1818
|
const row = collectionItems.items.find((it) => it.itemId === itemId || it.id === itemId);
|
|
1844
1819
|
if (row && row.data !== void 0) {
|
|
@@ -1939,7 +1914,7 @@ function useShellNavigation(args) {
|
|
|
1939
1914
|
}
|
|
1940
1915
|
}
|
|
1941
1916
|
try {
|
|
1942
|
-
const { removeRecord: removeRecord2 } = await import('../../records-
|
|
1917
|
+
const { removeRecord: removeRecord2 } = await import('../../records-4NN757SG.js');
|
|
1943
1918
|
await removeRecord2(ctx, itemId);
|
|
1944
1919
|
onTelemetry?.({ type: "item.delete", recordType, scopeRef: baseScopeRef, itemId });
|
|
1945
1920
|
if (selectedItemId === itemId) setSelectedItemId(null);
|
|
@@ -2124,17 +2099,20 @@ var useShellDeepLink = (args) => {
|
|
|
2124
2099
|
} = args;
|
|
2125
2100
|
const lastAppliedDLRef = useRef("");
|
|
2126
2101
|
const [pendingDeepLinkRecordId, setPendingDeepLinkRecordId] = useState(null);
|
|
2102
|
+
const preserveInitialRecordIdRef = useRef(!!deepLinkState.urlState.recordId);
|
|
2127
2103
|
const warnedMissingRef = useRef(/* @__PURE__ */ new Set());
|
|
2128
2104
|
useEffect(() => {
|
|
2129
2105
|
if (!deepLinkState.enabled) return;
|
|
2130
2106
|
if (selectedItemId) return;
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2107
|
+
const echoView = deepLinkState.urlState.view ?? "";
|
|
2108
|
+
const currentDeepLinkedRecordId = pendingDeepLinkRecordId ?? deepLinkState.urlState.recordId ?? null;
|
|
2109
|
+
const preservingPendingRecordId = preserveInitialRecordIdRef.current && !!currentDeepLinkedRecordId;
|
|
2110
|
+
deepLinkState.emit(
|
|
2111
|
+
preservingPendingRecordId ? { scope: editingScope?.raw ?? null } : { scope: editingScope?.raw ?? null, recordId: null },
|
|
2112
|
+
"scope"
|
|
2113
|
+
);
|
|
2114
|
+
lastAppliedDLRef.current = `${preservingPendingRecordId ? currentDeepLinkedRecordId ?? "" : ""}|${editingScope?.raw ?? ""}|${echoView}`;
|
|
2115
|
+
}, [deepLinkState.enabled, deepLinkState.urlState.recordId, editingScope?.raw, selectedItemId, itemView, pendingDeepLinkRecordId]);
|
|
2138
2116
|
useEffect(() => {
|
|
2139
2117
|
if (!deepLinkState.enabled) return;
|
|
2140
2118
|
const { recordId, scope, view } = deepLinkState.urlState;
|
|
@@ -2163,6 +2141,7 @@ var useShellDeepLink = (args) => {
|
|
|
2163
2141
|
if (selectedRecordId !== null) setSelectedRecordId(null);
|
|
2164
2142
|
}
|
|
2165
2143
|
setPendingDeepLinkRecordId(recordId ?? null);
|
|
2144
|
+
if (recordId) preserveInitialRecordIdRef.current = true;
|
|
2166
2145
|
if (!recordId && selectedItemId !== null) {
|
|
2167
2146
|
console.info("[RecordsAdminShell] preserving selected item during restore without recordId", {
|
|
2168
2147
|
selectedItemId,
|
|
@@ -2179,6 +2158,7 @@ var useShellDeepLink = (args) => {
|
|
|
2179
2158
|
if (hit2) {
|
|
2180
2159
|
skipNextItemResetRef.current = true;
|
|
2181
2160
|
setSelectedItemId(pending);
|
|
2161
|
+
preserveInitialRecordIdRef.current = false;
|
|
2182
2162
|
setPendingDeepLinkRecordId(null);
|
|
2183
2163
|
return;
|
|
2184
2164
|
}
|
|
@@ -2190,6 +2170,7 @@ var useShellDeepLink = (args) => {
|
|
|
2190
2170
|
scope: editingScope?.raw ?? null
|
|
2191
2171
|
});
|
|
2192
2172
|
}
|
|
2173
|
+
preserveInitialRecordIdRef.current = false;
|
|
2193
2174
|
setPendingDeepLinkRecordId(null);
|
|
2194
2175
|
}
|
|
2195
2176
|
return;
|
|
@@ -2197,6 +2178,7 @@ var useShellDeepLink = (args) => {
|
|
|
2197
2178
|
const hit = recordListItems.find((it) => it.id === pending);
|
|
2198
2179
|
if (hit) {
|
|
2199
2180
|
if (selectedRecordId !== pending) setSelectedRecordId(pending);
|
|
2181
|
+
preserveInitialRecordIdRef.current = false;
|
|
2200
2182
|
setPendingDeepLinkRecordId(null);
|
|
2201
2183
|
return;
|
|
2202
2184
|
}
|
|
@@ -2208,6 +2190,7 @@ var useShellDeepLink = (args) => {
|
|
|
2208
2190
|
scope: editingScope?.raw ?? null
|
|
2209
2191
|
});
|
|
2210
2192
|
}
|
|
2193
|
+
preserveInitialRecordIdRef.current = false;
|
|
2211
2194
|
setPendingDeepLinkRecordId(null);
|
|
2212
2195
|
}
|
|
2213
2196
|
}, [
|
|
@@ -3255,10 +3238,16 @@ var RowContextMenu = ({
|
|
|
3255
3238
|
}) => {
|
|
3256
3239
|
const [open, setOpen] = useState(false);
|
|
3257
3240
|
const wrapperRef = useRef(null);
|
|
3241
|
+
const triggerRef = useRef(null);
|
|
3242
|
+
const menuRef = useRef(null);
|
|
3243
|
+
const [pos, setPos] = useState(null);
|
|
3258
3244
|
useEffect(() => {
|
|
3259
3245
|
if (!open) return;
|
|
3260
3246
|
const onDoc = (e) => {
|
|
3261
|
-
|
|
3247
|
+
const t = e.target;
|
|
3248
|
+
if (wrapperRef.current?.contains(t)) return;
|
|
3249
|
+
if (menuRef.current?.contains(t)) return;
|
|
3250
|
+
setOpen(false);
|
|
3262
3251
|
};
|
|
3263
3252
|
const onKey = (e) => {
|
|
3264
3253
|
if (e.key === "Escape") setOpen(false);
|
|
@@ -3270,12 +3259,36 @@ var RowContextMenu = ({
|
|
|
3270
3259
|
document.removeEventListener("keydown", onKey);
|
|
3271
3260
|
};
|
|
3272
3261
|
}, [open]);
|
|
3262
|
+
useLayoutEffect(() => {
|
|
3263
|
+
if (!open) {
|
|
3264
|
+
setPos(null);
|
|
3265
|
+
return;
|
|
3266
|
+
}
|
|
3267
|
+
const update = () => {
|
|
3268
|
+
const el = triggerRef.current;
|
|
3269
|
+
if (!el) return;
|
|
3270
|
+
const r = el.getBoundingClientRect();
|
|
3271
|
+
const menuWidth = menuRef.current?.offsetWidth ?? 176;
|
|
3272
|
+
const margin = 8;
|
|
3273
|
+
const left = Math.max(margin, Math.min(window.innerWidth - menuWidth - margin, r.right - menuWidth));
|
|
3274
|
+
const top = r.bottom + 4;
|
|
3275
|
+
setPos({ top, left });
|
|
3276
|
+
};
|
|
3277
|
+
update();
|
|
3278
|
+
window.addEventListener("resize", update);
|
|
3279
|
+
window.addEventListener("scroll", update, true);
|
|
3280
|
+
return () => {
|
|
3281
|
+
window.removeEventListener("resize", update);
|
|
3282
|
+
window.removeEventListener("scroll", update, true);
|
|
3283
|
+
};
|
|
3284
|
+
}, [open]);
|
|
3273
3285
|
if (!onCopy && !onPaste) return null;
|
|
3274
3286
|
const pasteLabel = !canPaste ? i18n.clipboardEmpty : pasteSourceLabel ? i18n.pasteFrom.replace("{name}", pasteSourceLabel) : pasteWillReplace ? i18n.pasteReplace : i18n.paste;
|
|
3275
3287
|
return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: "ra-row-menu-wrap relative", children: [
|
|
3276
3288
|
/* @__PURE__ */ jsx(
|
|
3277
3289
|
"button",
|
|
3278
3290
|
{
|
|
3291
|
+
ref: triggerRef,
|
|
3279
3292
|
type: "button",
|
|
3280
3293
|
className: "ra-row-menu-trigger",
|
|
3281
3294
|
"aria-label": "Row actions",
|
|
@@ -3288,75 +3301,106 @@ var RowContextMenu = ({
|
|
|
3288
3301
|
children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "w-3.5 h-3.5", "aria-hidden": "true" })
|
|
3289
3302
|
}
|
|
3290
3303
|
),
|
|
3291
|
-
open &&
|
|
3292
|
-
|
|
3293
|
-
"
|
|
3304
|
+
open && typeof document !== "undefined" && createPortal(
|
|
3305
|
+
/* @__PURE__ */ jsxs(
|
|
3306
|
+
"div",
|
|
3294
3307
|
{
|
|
3295
|
-
|
|
3296
|
-
role: "
|
|
3297
|
-
className: "ra-row-menu-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
onCopy();
|
|
3302
|
-
},
|
|
3308
|
+
ref: menuRef,
|
|
3309
|
+
role: "menu",
|
|
3310
|
+
className: "ra-row-menu ra-row-menu-portal",
|
|
3311
|
+
style: pos ? { top: pos.top, left: pos.left } : { visibility: "hidden" },
|
|
3312
|
+
onClick: (e) => e.stopPropagation(),
|
|
3313
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
3303
3314
|
children: [
|
|
3304
|
-
/* @__PURE__ */
|
|
3305
|
-
|
|
3315
|
+
onCopy && /* @__PURE__ */ jsxs(
|
|
3316
|
+
"button",
|
|
3317
|
+
{
|
|
3318
|
+
type: "button",
|
|
3319
|
+
role: "menuitem",
|
|
3320
|
+
className: "ra-row-menu-item",
|
|
3321
|
+
onClick: (e) => {
|
|
3322
|
+
e.stopPropagation();
|
|
3323
|
+
setOpen(false);
|
|
3324
|
+
onCopy();
|
|
3325
|
+
},
|
|
3326
|
+
children: [
|
|
3327
|
+
/* @__PURE__ */ jsx(Copy, { className: "w-3 h-3", "aria-hidden": "true" }),
|
|
3328
|
+
/* @__PURE__ */ jsx("span", { children: i18n.copy })
|
|
3329
|
+
]
|
|
3330
|
+
}
|
|
3331
|
+
),
|
|
3332
|
+
onPaste && /* @__PURE__ */ jsxs(
|
|
3333
|
+
"button",
|
|
3334
|
+
{
|
|
3335
|
+
type: "button",
|
|
3336
|
+
role: "menuitem",
|
|
3337
|
+
className: "ra-row-menu-item",
|
|
3338
|
+
disabled: !canPaste,
|
|
3339
|
+
onClick: (e) => {
|
|
3340
|
+
e.stopPropagation();
|
|
3341
|
+
setOpen(false);
|
|
3342
|
+
if (canPaste) onPaste();
|
|
3343
|
+
},
|
|
3344
|
+
children: [
|
|
3345
|
+
/* @__PURE__ */ jsx(ClipboardPaste, { className: "w-3 h-3", "aria-hidden": "true" }),
|
|
3346
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: pasteLabel })
|
|
3347
|
+
]
|
|
3348
|
+
}
|
|
3349
|
+
)
|
|
3306
3350
|
]
|
|
3307
3351
|
}
|
|
3308
3352
|
),
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
{
|
|
3312
|
-
type: "button",
|
|
3313
|
-
role: "menuitem",
|
|
3314
|
-
className: "ra-row-menu-item",
|
|
3315
|
-
disabled: !canPaste,
|
|
3316
|
-
onClick: (e) => {
|
|
3317
|
-
e.stopPropagation();
|
|
3318
|
-
setOpen(false);
|
|
3319
|
-
if (canPaste) onPaste();
|
|
3320
|
-
},
|
|
3321
|
-
children: [
|
|
3322
|
-
/* @__PURE__ */ jsx(ClipboardPaste, { className: "w-3 h-3", "aria-hidden": "true" }),
|
|
3323
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: pasteLabel })
|
|
3324
|
-
]
|
|
3325
|
-
}
|
|
3326
|
-
)
|
|
3327
|
-
] })
|
|
3353
|
+
document.body
|
|
3354
|
+
)
|
|
3328
3355
|
] });
|
|
3329
3356
|
};
|
|
3330
3357
|
|
|
3331
3358
|
// src/components/RecordsAdmin/browser/formatFacetRule.ts
|
|
3332
|
-
function formatFacetRule(rule) {
|
|
3359
|
+
function formatFacetRule(rule, lookup) {
|
|
3333
3360
|
if (!rule || !rule.all || rule.all.length === 0) return null;
|
|
3361
|
+
const fLabel = (k) => lookup?.facetLabel?.(k) ?? k;
|
|
3362
|
+
const vLabel = (k, v) => lookup?.valueLabel?.(k, v) ?? v;
|
|
3334
3363
|
const parts = rule.all.map((c) => {
|
|
3335
3364
|
const vals = (c.anyOf ?? []).filter(Boolean);
|
|
3336
|
-
|
|
3337
|
-
if (vals.length ===
|
|
3338
|
-
return `${
|
|
3365
|
+
const fk = fLabel(c.facetKey);
|
|
3366
|
+
if (vals.length === 0) return `${fk} (no values)`;
|
|
3367
|
+
if (vals.length === 1) return `${fk} = ${vLabel(c.facetKey, vals[0])}`;
|
|
3368
|
+
return `${fk} \u2208 {${vals.map((v) => vLabel(c.facetKey, v)).join(", ")}}`;
|
|
3339
3369
|
});
|
|
3340
3370
|
return parts.join(" AND ");
|
|
3341
3371
|
}
|
|
3342
|
-
function summarizeFacetRule(rule) {
|
|
3372
|
+
function summarizeFacetRule(rule, lookup) {
|
|
3343
3373
|
if (!rule || !rule.all || rule.all.length === 0) return [];
|
|
3374
|
+
const fLabel = (k) => lookup?.facetLabel?.(k) ?? k;
|
|
3375
|
+
const vLabel = (k, v) => lookup?.valueLabel?.(k, v) ?? v;
|
|
3344
3376
|
return rule.all.map((c) => {
|
|
3345
|
-
const
|
|
3377
|
+
const rawValues = (c.anyOf ?? []).filter(Boolean);
|
|
3378
|
+
const values = rawValues.map((v) => vLabel(c.facetKey, v));
|
|
3379
|
+
const fk = fLabel(c.facetKey);
|
|
3346
3380
|
let label;
|
|
3347
|
-
if (values.length === 0) label = `${
|
|
3348
|
-
else if (values.length === 1) label = `${
|
|
3349
|
-
else if (values.length <= 3) label = `${
|
|
3350
|
-
else label = `${
|
|
3381
|
+
if (values.length === 0) label = `${fk}: \u2014`;
|
|
3382
|
+
else if (values.length === 1) label = `${fk}: ${values[0]}`;
|
|
3383
|
+
else if (values.length <= 3) label = `${fk}: ${values.join(", ")}`;
|
|
3384
|
+
else label = `${fk}: ${values.slice(0, 2).join(", ")} +${values.length - 2}`;
|
|
3351
3385
|
return { facetKey: c.facetKey, values, label };
|
|
3352
3386
|
});
|
|
3353
3387
|
}
|
|
3388
|
+
var RuleLabelLookupContext = createContext(null);
|
|
3389
|
+
var useRuleLabelLookup = () => useContext(RuleLabelLookupContext) ?? void 0;
|
|
3390
|
+
var RuleLabelLookupProvider = ({
|
|
3391
|
+
value,
|
|
3392
|
+
children
|
|
3393
|
+
}) => {
|
|
3394
|
+
const v = useMemo(() => value, [value]);
|
|
3395
|
+
return /* @__PURE__ */ jsx(RuleLabelLookupContext.Provider, { value: v, children });
|
|
3396
|
+
};
|
|
3354
3397
|
var DefaultRecordRow = ({ record, ctx, compact = false }) => {
|
|
3355
3398
|
const { selected, onSelect, isDirty, hasError, onCopy, onPaste, canPaste, pasteWillReplace, clipboardSourceLabel } = ctx;
|
|
3399
|
+
const ruleLabelLookup = useRuleLabelLookup();
|
|
3356
3400
|
const ScopeIcon = record.scope.kind && record.scope.kind !== "collection" ? DEFAULT_ICONS.scope[record.scope.kind] : DEFAULT_ICONS.scope.product;
|
|
3357
3401
|
const tone = resolveTone(void 0, record.status);
|
|
3358
|
-
const ruleSummary = formatFacetRule(record.facetRule);
|
|
3359
|
-
const ruleClauses = summarizeFacetRule(record.facetRule);
|
|
3402
|
+
const ruleSummary = formatFacetRule(record.facetRule, ruleLabelLookup);
|
|
3403
|
+
const ruleClauses = summarizeFacetRule(record.facetRule, ruleLabelLookup);
|
|
3360
3404
|
const isRuleRecord = ruleClauses.length > 0;
|
|
3361
3405
|
const i18n = ctx.i18n ?? DEFAULT_I18N;
|
|
3362
3406
|
const subtitle = record.subtitle ?? (isRuleRecord ? null : ruleSummary) ?? (tone === "missing" ? i18n.subtitleEmpty : tone === "own" ? i18n.subtitleConfigured : i18n.subtitleInherited);
|
|
@@ -3397,9 +3441,9 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
|
|
|
3397
3441
|
!compact && !isRuleRecord && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: subtitle })
|
|
3398
3442
|
] }),
|
|
3399
3443
|
compact && /* @__PURE__ */ jsx(StatusIcon, { status: record.status, size: "0.85rem" }),
|
|
3400
|
-
!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" }) }),
|
|
3444
|
+
!compact && record.scope.kind && record.scope.kind !== "collection" && record.scope.kind !== "rule" && /* @__PURE__ */ jsx("span", { className: "ra-row-scope", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ScopeIcon, { className: "w-3 h-3" }) }),
|
|
3401
3445
|
record.badges?.slice(0, 1).map((b, i) => /* @__PURE__ */ jsx("span", { className: "ra-chip", "data-tone": "muted", children: b.label }, `${b.label}-${i}`)),
|
|
3402
|
-
record.facetRule && !compact && /* @__PURE__ */ jsx(
|
|
3446
|
+
record.facetRule && !compact && !isRuleRecord && /* @__PURE__ */ jsx(
|
|
3403
3447
|
"span",
|
|
3404
3448
|
{
|
|
3405
3449
|
className: "ra-row-rule-pip",
|
|
@@ -3907,15 +3951,19 @@ function useCollectionItems(args) {
|
|
|
3907
3951
|
|
|
3908
3952
|
// src/components/RecordsAdmin/data/deepLinkAdapter.ts
|
|
3909
3953
|
var findQueryHost = (loc) => {
|
|
3910
|
-
if (loc.
|
|
3954
|
+
if (loc.hash && loc.hash.startsWith("#/")) return "hash";
|
|
3911
3955
|
if (loc.hash && loc.hash.includes("?")) return "hash";
|
|
3956
|
+
if (loc.search && loc.search.length > 1) return "search";
|
|
3912
3957
|
return "search";
|
|
3913
3958
|
};
|
|
3914
|
-
var
|
|
3915
|
-
|
|
3916
|
-
if (host === "search") return loc.search.startsWith("?") ? loc.search.slice(1) : loc.search;
|
|
3959
|
+
var getSearchParams = (loc) => new URLSearchParams(loc.search.startsWith("?") ? loc.search.slice(1) : loc.search);
|
|
3960
|
+
var getHashParams = (loc) => {
|
|
3917
3961
|
const idx = loc.hash.indexOf("?");
|
|
3918
|
-
return idx >= 0 ? loc.hash.slice(idx + 1) : "";
|
|
3962
|
+
return new URLSearchParams(idx >= 0 ? loc.hash.slice(idx + 1) : "");
|
|
3963
|
+
};
|
|
3964
|
+
var getReadParams = (loc) => {
|
|
3965
|
+
if (loc.hash && loc.hash.includes("?")) return getHashParams(loc);
|
|
3966
|
+
return getSearchParams(loc);
|
|
3919
3967
|
};
|
|
3920
3968
|
var buildUrl = (loc, nextQuery) => {
|
|
3921
3969
|
const host = findQueryHost(loc);
|
|
@@ -3929,7 +3977,7 @@ var buildUrl = (loc, nextQuery) => {
|
|
|
3929
3977
|
var createDefaultDeepLinkAdapter = (paramNames) => ({
|
|
3930
3978
|
read() {
|
|
3931
3979
|
if (typeof window === "undefined") return {};
|
|
3932
|
-
const params =
|
|
3980
|
+
const params = getReadParams(window.location);
|
|
3933
3981
|
return {
|
|
3934
3982
|
recordId: params.get(paramNames.recordId),
|
|
3935
3983
|
scope: params.get(paramNames.scope),
|
|
@@ -3938,7 +3986,7 @@ var createDefaultDeepLinkAdapter = (paramNames) => ({
|
|
|
3938
3986
|
},
|
|
3939
3987
|
write(partial, mode) {
|
|
3940
3988
|
if (typeof window === "undefined") return;
|
|
3941
|
-
const params =
|
|
3989
|
+
const params = findQueryHost(window.location) === "hash" ? getHashParams(window.location) : getSearchParams(window.location);
|
|
3942
3990
|
const apply = (key, value) => {
|
|
3943
3991
|
if (value == null || value === "") params.delete(key);
|
|
3944
3992
|
else params.set(key, value);
|
|
@@ -3972,14 +4020,22 @@ var CONTEXT_KEYS = [
|
|
|
3972
4020
|
"lang",
|
|
3973
4021
|
"theme"
|
|
3974
4022
|
];
|
|
3975
|
-
var
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
return
|
|
4023
|
+
var getSearchParams2 = (loc) => new URLSearchParams(loc.search.startsWith("?") ? loc.search.slice(1) : loc.search);
|
|
4024
|
+
var getHashParams2 = (loc) => {
|
|
4025
|
+
const idx = loc.hash.indexOf("?");
|
|
4026
|
+
return new URLSearchParams(idx >= 0 ? loc.hash.slice(idx + 1) : "");
|
|
4027
|
+
};
|
|
4028
|
+
var getReadParams2 = (loc) => {
|
|
4029
|
+
if (loc.hash && loc.hash.includes("?")) return getHashParams2(loc);
|
|
4030
|
+
return getSearchParams2(loc);
|
|
4031
|
+
};
|
|
4032
|
+
var getMergedParams = (loc) => {
|
|
4033
|
+
const merged = getSearchParams2(loc);
|
|
4034
|
+
const hashParams = getHashParams2(loc);
|
|
4035
|
+
hashParams.forEach((value, key) => {
|
|
4036
|
+
merged.set(key, value);
|
|
4037
|
+
});
|
|
4038
|
+
return merged;
|
|
3983
4039
|
};
|
|
3984
4040
|
var findPath = (loc) => {
|
|
3985
4041
|
if (loc.hash && loc.hash.startsWith("#")) {
|
|
@@ -4002,7 +4058,7 @@ var createPostMessageDeepLinkAdapter = (paramNames) => {
|
|
|
4002
4058
|
const lastShellState = {};
|
|
4003
4059
|
const post = (path) => {
|
|
4004
4060
|
if (typeof window === "undefined") return;
|
|
4005
|
-
const params =
|
|
4061
|
+
const params = getMergedParams(window.location);
|
|
4006
4062
|
const context = {};
|
|
4007
4063
|
const state = {};
|
|
4008
4064
|
params.forEach((value, key) => {
|
|
@@ -4026,9 +4082,6 @@ var createPostMessageDeepLinkAdapter = (paramNames) => {
|
|
|
4026
4082
|
state,
|
|
4027
4083
|
appId: context.appId
|
|
4028
4084
|
};
|
|
4029
|
-
console.debug("[smartlinks-ui] postMessage \u2192 parent", message, {
|
|
4030
|
-
sameWindow: window.parent === window
|
|
4031
|
-
});
|
|
4032
4085
|
window.parent.postMessage(message, "*");
|
|
4033
4086
|
} catch {
|
|
4034
4087
|
}
|
|
@@ -4036,7 +4089,7 @@ var createPostMessageDeepLinkAdapter = (paramNames) => {
|
|
|
4036
4089
|
return {
|
|
4037
4090
|
read() {
|
|
4038
4091
|
if (typeof window === "undefined") return {};
|
|
4039
|
-
const params =
|
|
4092
|
+
const params = getReadParams2(window.location);
|
|
4040
4093
|
return {
|
|
4041
4094
|
recordId: params.get(paramNames.recordId),
|
|
4042
4095
|
scope: params.get(paramNames.scope),
|
|
@@ -4083,11 +4136,6 @@ function useDeepLinkState(options) {
|
|
|
4083
4136
|
if (options?.adapter) return options.adapter;
|
|
4084
4137
|
if (!defaultAdapterRef.current) {
|
|
4085
4138
|
const inIframe = isInSmartLinksIframe();
|
|
4086
|
-
console.debug("[smartlinks-ui] deep-link adapter selected", {
|
|
4087
|
-
adapter: inIframe ? "postMessage" : "default",
|
|
4088
|
-
inIframe,
|
|
4089
|
-
href: typeof window !== "undefined" ? window.location.href : "(ssr)"
|
|
4090
|
-
});
|
|
4091
4139
|
defaultAdapterRef.current = inIframe ? createPostMessageDeepLinkAdapter(paramNames) : createDefaultDeepLinkAdapter(paramNames);
|
|
4092
4140
|
}
|
|
4093
4141
|
return defaultAdapterRef.current;
|
|
@@ -4102,14 +4150,9 @@ function useDeepLinkState(options) {
|
|
|
4102
4150
|
}, [adapter]);
|
|
4103
4151
|
const emit = useCallback((partial, kind) => {
|
|
4104
4152
|
if (!adapter) {
|
|
4105
|
-
console.debug("[smartlinks-ui] deep-link emit skipped \u2014 no adapter (deepLink not enabled?)", {
|
|
4106
|
-
partial,
|
|
4107
|
-
kind
|
|
4108
|
-
});
|
|
4109
4153
|
return;
|
|
4110
4154
|
}
|
|
4111
4155
|
const mode = classify2(history, kind);
|
|
4112
|
-
console.debug("[smartlinks-ui] deep-link emit", { partial, kind, mode });
|
|
4113
4156
|
adapter.write(partial, mode);
|
|
4114
4157
|
setUrlState((prev) => ({ ...prev, ...partial }));
|
|
4115
4158
|
}, [adapter, history]);
|
|
@@ -7209,18 +7252,6 @@ function RecordsAdminShellInner(props) {
|
|
|
7209
7252
|
const i18n = { ...DEFAULT_I18N, ...i18nOverride ?? {} };
|
|
7210
7253
|
const icons = useMemo(() => mergeIcons(iconsOverride), [iconsOverride]);
|
|
7211
7254
|
const deepLinkState = useDeepLinkState(deepLink);
|
|
7212
|
-
const deepLinkLoggedRef = useRef(false);
|
|
7213
|
-
if (!deepLinkLoggedRef.current) {
|
|
7214
|
-
deepLinkLoggedRef.current = true;
|
|
7215
|
-
console.debug("[RecordsAdminShell] deep-link config", {
|
|
7216
|
-
enabled: deepLinkState.enabled,
|
|
7217
|
-
hostPassedOption: !!deepLink,
|
|
7218
|
-
// Resolved adapter mode: 'host' = host-supplied, 'default' = built-in
|
|
7219
|
-
// (window.location standalone, postMessage inside the SmartLinks iframe).
|
|
7220
|
-
adapterMode: deepLink?.adapter ? "host" : "default",
|
|
7221
|
-
paramNames: deepLinkState.paramNames
|
|
7222
|
-
});
|
|
7223
|
-
}
|
|
7224
7255
|
const multiOpenWarnedRef = useRef(false);
|
|
7225
7256
|
if (editorTabs === "multi" && !multiOpenWarnedRef.current) {
|
|
7226
7257
|
multiOpenWarnedRef.current = true;
|
|
@@ -7365,6 +7396,11 @@ function RecordsAdminShellInner(props) {
|
|
|
7365
7396
|
variantChildren,
|
|
7366
7397
|
batchChildren
|
|
7367
7398
|
} = browser;
|
|
7399
|
+
const ruleScopedList = useRecordList({
|
|
7400
|
+
ctx,
|
|
7401
|
+
scopeKind: "rule",
|
|
7402
|
+
enabled: true
|
|
7403
|
+
});
|
|
7368
7404
|
const pinnedProduct = useSingleProduct({
|
|
7369
7405
|
SL,
|
|
7370
7406
|
collectionId,
|
|
@@ -7898,6 +7934,26 @@ function RecordsAdminShellInner(props) {
|
|
|
7898
7934
|
const isGlobalTab = activeScope === "collection";
|
|
7899
7935
|
const isAllTab = activeScope === "all";
|
|
7900
7936
|
const isRecordsTab = isRuleTab || isGlobalTab || isAllTab;
|
|
7937
|
+
const ruleLabelLookup = useMemo(() => {
|
|
7938
|
+
const map = /* @__PURE__ */ new Map();
|
|
7939
|
+
for (const it of facetBrowse.items) {
|
|
7940
|
+
const k = it.scope.facetId;
|
|
7941
|
+
const v = it.scope.facetValue;
|
|
7942
|
+
if (!k || !v) continue;
|
|
7943
|
+
const facetLabel = it.subtitle ?? k;
|
|
7944
|
+
const valueLabel = it.label ?? v;
|
|
7945
|
+
let info = map.get(k);
|
|
7946
|
+
if (!info) {
|
|
7947
|
+
info = { label: facetLabel, values: /* @__PURE__ */ new Map() };
|
|
7948
|
+
map.set(k, info);
|
|
7949
|
+
}
|
|
7950
|
+
if (!info.values.has(v)) info.values.set(v, valueLabel);
|
|
7951
|
+
}
|
|
7952
|
+
return {
|
|
7953
|
+
facetLabel: (k) => map.get(k)?.label,
|
|
7954
|
+
valueLabel: (k, v) => map.get(k)?.values.get(v)
|
|
7955
|
+
};
|
|
7956
|
+
}, [facetBrowse.items]);
|
|
7901
7957
|
const effectiveGroupBy = useMemo(() => {
|
|
7902
7958
|
if (groupBy) return groupBy;
|
|
7903
7959
|
if (isAllTab) return void 0;
|
|
@@ -7906,13 +7962,25 @@ function RecordsAdminShellInner(props) {
|
|
|
7906
7962
|
return (record) => {
|
|
7907
7963
|
const hash = ruleHash(record.facetRule);
|
|
7908
7964
|
if (!hash) return null;
|
|
7909
|
-
return { key: `rule:${hash}`, label: summariseRule(record.facetRule) };
|
|
7965
|
+
return { key: `rule:${hash}`, label: summariseRule(record.facetRule, ruleLabelLookup) };
|
|
7910
7966
|
};
|
|
7911
|
-
}, [groupBy, isRuleTab, isAllTab, isCollection]);
|
|
7967
|
+
}, [groupBy, isRuleTab, isAllTab, isCollection, ruleLabelLookup]);
|
|
7912
7968
|
const [bulkRuleEditTarget, setBulkRuleEditTarget] = useState(null);
|
|
7913
7969
|
const ruleCatalogue = useMemo(() => {
|
|
7914
7970
|
const buckets = /* @__PURE__ */ new Map();
|
|
7971
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
7972
|
+
const merged = [];
|
|
7973
|
+
for (const item of ruleScopedList.allItems) {
|
|
7974
|
+
if (item.id && seenIds.has(item.id)) continue;
|
|
7975
|
+
if (item.id) seenIds.add(item.id);
|
|
7976
|
+
merged.push(item);
|
|
7977
|
+
}
|
|
7915
7978
|
for (const item of recordList.items) {
|
|
7979
|
+
if (item.id && seenIds.has(item.id)) continue;
|
|
7980
|
+
if (item.id) seenIds.add(item.id);
|
|
7981
|
+
merged.push(item);
|
|
7982
|
+
}
|
|
7983
|
+
for (const item of merged) {
|
|
7916
7984
|
const hash = ruleHash(item.facetRule);
|
|
7917
7985
|
if (!hash || !item.facetRule) continue;
|
|
7918
7986
|
const existing = buckets.get(hash);
|
|
@@ -7922,7 +7990,7 @@ function RecordsAdminShellInner(props) {
|
|
|
7922
7990
|
buckets.set(hash, {
|
|
7923
7991
|
hash,
|
|
7924
7992
|
rule: item.facetRule,
|
|
7925
|
-
summary: summariseRule(item.facetRule),
|
|
7993
|
+
summary: summariseRule(item.facetRule, ruleLabelLookup),
|
|
7926
7994
|
count: 1
|
|
7927
7995
|
});
|
|
7928
7996
|
}
|
|
@@ -7931,7 +7999,7 @@ function RecordsAdminShellInner(props) {
|
|
|
7931
7999
|
if (b.count !== a.count) return b.count - a.count;
|
|
7932
8000
|
return a.summary.localeCompare(b.summary);
|
|
7933
8001
|
});
|
|
7934
|
-
}, [recordList.items]);
|
|
8002
|
+
}, [recordList.items, ruleScopedList.allItems, ruleLabelLookup]);
|
|
7935
8003
|
const [targetingExpandNonce, setTargetingExpandNonce] = useState(0);
|
|
7936
8004
|
const renderRuleGroupActions = useCallback(
|
|
7937
8005
|
(group) => {
|
|
@@ -8022,8 +8090,13 @@ function RecordsAdminShellInner(props) {
|
|
|
8022
8090
|
}
|
|
8023
8091
|
return Array.from(buckets.values()).map(({ rep, count }) => ({
|
|
8024
8092
|
...rep,
|
|
8025
|
-
|
|
8026
|
-
|
|
8093
|
+
// Title carries the count + identity; the rule chips below render
|
|
8094
|
+
// the friendly facet/value labels (via the lookup) so we don't
|
|
8095
|
+
// repeat the same information in two places. Without this, a row
|
|
8096
|
+
// had three echoes of the same rule (raw-key title, friendly chip,
|
|
8097
|
+
// and the right-pane "Rule · …" header).
|
|
8098
|
+
label: `${count} ${itemNoun}${count === 1 ? "" : "s"}`,
|
|
8099
|
+
subtitle: void 0
|
|
8027
8100
|
}));
|
|
8028
8101
|
}, [isRuleTab, isCollection, filteredRuleItems, itemNoun]);
|
|
8029
8102
|
const activeRuleSummary = useMemo(() => {
|
|
@@ -8031,8 +8104,8 @@ function RecordsAdminShellInner(props) {
|
|
|
8031
8104
|
if (!selectedRecordId || isDraftId3(selectedRecordId)) return null;
|
|
8032
8105
|
const hit = recordList.items.find((it) => it.id === selectedRecordId);
|
|
8033
8106
|
if (!hit?.facetRule) return null;
|
|
8034
|
-
return summariseRule(hit.facetRule);
|
|
8035
|
-
}, [isRuleTab, isCollection, selectedRecordId, recordList.items]);
|
|
8107
|
+
return summariseRule(hit.facetRule, ruleLabelLookup);
|
|
8108
|
+
}, [isRuleTab, isCollection, selectedRecordId, recordList.items, ruleLabelLookup]);
|
|
8036
8109
|
const applyFacetBrowseFilter = useCallback(
|
|
8037
8110
|
(items2) => {
|
|
8038
8111
|
if (!facetBrowseFilter) return items2;
|
|
@@ -8107,7 +8180,7 @@ function RecordsAdminShellInner(props) {
|
|
|
8107
8180
|
});
|
|
8108
8181
|
};
|
|
8109
8182
|
onLeftSelectRef.current = onLeftSelect;
|
|
8110
|
-
return /* @__PURE__ */ jsxs(
|
|
8183
|
+
return /* @__PURE__ */ jsx(RuleLabelLookupProvider, { value: ruleLabelLookup, children: /* @__PURE__ */ jsxs(
|
|
8111
8184
|
"div",
|
|
8112
8185
|
{
|
|
8113
8186
|
className: `ra-shell flex flex-col h-full ${className ?? ""}`,
|
|
@@ -8588,7 +8661,7 @@ function RecordsAdminShellInner(props) {
|
|
|
8588
8661
|
)
|
|
8589
8662
|
]
|
|
8590
8663
|
}
|
|
8591
|
-
);
|
|
8664
|
+
) });
|
|
8592
8665
|
}
|
|
8593
8666
|
var RecordBrowser = ({
|
|
8594
8667
|
scopes,
|