@proveanything/smartlinks-utils-ui 0.10.7 → 0.10.9
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/{IconPicker-BMMQLR5I.d.ts → IconPicker-DKpL5Hcc.d.ts} +1 -1
- package/dist/chunk-2MW54ZVG.js +92 -0
- package/dist/chunk-2MW54ZVG.js.map +1 -0
- package/dist/components/AssetPicker/index.css +15 -0
- package/dist/components/AssetPicker/index.css.map +1 -1
- package/dist/components/AssetPicker/index.d.ts +1 -1
- package/dist/components/ConditionsEditor/index.css +15 -0
- package/dist/components/ConditionsEditor/index.css.map +1 -1
- package/dist/components/FontPicker/index.css +15 -0
- package/dist/components/FontPicker/index.css.map +1 -1
- package/dist/components/IconPicker/index.css +15 -0
- package/dist/components/IconPicker/index.css.map +1 -1
- package/dist/components/IconPicker/index.d.ts +2 -2
- package/dist/components/RecordsAdmin/index.css +15 -0
- package/dist/components/RecordsAdmin/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.d.ts +77 -5
- package/dist/components/RecordsAdmin/index.js +166 -109
- package/dist/components/RecordsAdmin/index.js.map +1 -1
- package/dist/index.css +197 -182
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +62 -4
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/{useAssets-BM1bzzkq.d.ts → useAssets-BAtXv6D5.d.ts} +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { FacetRule, MatchedAt, AppRecord, RecordTarget, MatchResult, ResolveAllEntry } from '@proveanything/smartlinks/dist/types/appObjects';
|
|
3
|
+
import { ReactNode, ComponentType } from 'react';
|
|
4
4
|
import { LucideIcon } from 'lucide-react';
|
|
5
5
|
import * as _tanstack_query_core from '@tanstack/query-core';
|
|
6
6
|
import * as _proveanything_smartlinks from '@proveanything/smartlinks';
|
|
@@ -93,6 +93,21 @@ interface RecordSummary<TData = unknown> {
|
|
|
93
93
|
* browser uses this to render a friendly rule summary as the row subtitle.
|
|
94
94
|
*/
|
|
95
95
|
facetRule?: FacetRule | null;
|
|
96
|
+
/**
|
|
97
|
+
* Optional visual hint for the leading status icon on the default row
|
|
98
|
+
* renderer. When set, takes precedence over the status-derived icon
|
|
99
|
+
* (configured / inherited / empty). Used by lifecycle rail buckets
|
|
100
|
+
* (success/warning/etc) and any host that wants per-row iconography
|
|
101
|
+
* without supplying a full `renderListRow`.
|
|
102
|
+
*/
|
|
103
|
+
iconHint?: ReactNode;
|
|
104
|
+
/**
|
|
105
|
+
* Optional semantic tone for the leading status icon. Drives the icon
|
|
106
|
+
* colour via `ra-status-icon--<tone>` CSS classes. Has no effect unless
|
|
107
|
+
* `iconHint` is also set (or the tone is one that maps to a built-in
|
|
108
|
+
* default icon).
|
|
109
|
+
*/
|
|
110
|
+
toneHint?: 'success' | 'warning' | 'danger' | 'muted' | 'info' | 'default';
|
|
96
111
|
}
|
|
97
112
|
interface ResolvedRecord<TData = unknown> {
|
|
98
113
|
data: TData | null;
|
|
@@ -962,7 +977,35 @@ interface RailConfig<TData = unknown> {
|
|
|
962
977
|
key: string;
|
|
963
978
|
label: string;
|
|
964
979
|
icon?: ReactNode;
|
|
980
|
+
/**
|
|
981
|
+
* Whether this bucket should be expanded by default (records-driven
|
|
982
|
+
* accordion mode). Last-write-wins per key. Has no effect in the
|
|
983
|
+
* collection-cardinality lifecycle-rail mode where buckets are flat
|
|
984
|
+
* selectable rows rather than accordion sections.
|
|
985
|
+
*/
|
|
986
|
+
defaultOpen?: boolean;
|
|
987
|
+
/**
|
|
988
|
+
* Semantic tone for the bucket — drives a CSS data attribute on the
|
|
989
|
+
* lifecycle bucket row (`data-tone="success" | "warning" | "muted"`).
|
|
990
|
+
* Apps style via `[data-tone="…"]` selectors against existing
|
|
991
|
+
* semantic tokens.
|
|
992
|
+
*/
|
|
993
|
+
tone?: 'default' | 'success' | 'warning' | 'muted' | 'danger';
|
|
965
994
|
} | null;
|
|
995
|
+
/**
|
|
996
|
+
* Initial bucket key to select on first mount of the lifecycle rail
|
|
997
|
+
* (collection cardinality + `'all'` / `'global'` scope + `groupBy`).
|
|
998
|
+
* Ignored when a deep-link state restores a different selection. Pass
|
|
999
|
+
* the same key shape your `groupBy` returns (e.g. `"1-open"`).
|
|
1000
|
+
*/
|
|
1001
|
+
defaultGroupKey?: string;
|
|
1002
|
+
/**
|
|
1003
|
+
* EXPERIMENTAL — invoked the first time a lifecycle bucket is selected
|
|
1004
|
+
* (collection-cardinality lifecycle rail). Hosts can use this to trigger
|
|
1005
|
+
* supplemental fetches before the bucket-filtered table renders. May be
|
|
1006
|
+
* called more than once per key. API may change before stabilising.
|
|
1007
|
+
*/
|
|
1008
|
+
onGroupExpanded?: (key: string) => void;
|
|
966
1009
|
/** Visual density. Default `comfortable`. */
|
|
967
1010
|
density?: 'comfortable' | 'compact';
|
|
968
1011
|
}
|
|
@@ -1020,6 +1063,20 @@ interface ItemsConfig<TData = unknown> {
|
|
|
1020
1063
|
cardSize?: 'sm' | 'md' | 'lg';
|
|
1021
1064
|
/** What the rail shows once an item is open. Default `'siblings'`. */
|
|
1022
1065
|
railMode?: CollectionRailMode;
|
|
1066
|
+
/**
|
|
1067
|
+
* Optional projector that derives `label` / `subtitle` / `thumbnail`
|
|
1068
|
+
* (and any other `RecordSummary` fields) from a raw record. Runs for
|
|
1069
|
+
* every item in the collection-cardinality right-pane list. The
|
|
1070
|
+
* shell's default tries common keys (`title`, `name`, `label`,
|
|
1071
|
+
* `question`, …) — supply this when your record shape doesn't match
|
|
1072
|
+
* those, or when you want the global / all-items list to read from a
|
|
1073
|
+
* different field.
|
|
1074
|
+
*
|
|
1075
|
+
* `base` already carries the framework-derived fields (id, scope,
|
|
1076
|
+
* status, etc.); return a merged `RecordSummary` (e.g.
|
|
1077
|
+
* `{ ...base, label: rec.data?.headline ?? base.label }`).
|
|
1078
|
+
*/
|
|
1079
|
+
toSummary?: (rec: AppRecord, base: RecordSummary<TData>) => RecordSummary<TData>;
|
|
1023
1080
|
}
|
|
1024
1081
|
interface UnsavedConfig<TData = unknown> {
|
|
1025
1082
|
/**
|
|
@@ -1135,6 +1192,8 @@ interface Props$f {
|
|
|
1135
1192
|
declare const StatusDot: ({ source, status, className }: Props$f) => react_jsx_runtime.JSX.Element;
|
|
1136
1193
|
|
|
1137
1194
|
type StatusTone = 'own' | 'shared' | 'missing';
|
|
1195
|
+
/** Semantic tones used by host-driven iconography (e.g. lifecycle buckets). */
|
|
1196
|
+
type SemanticTone = 'success' | 'warning' | 'danger' | 'muted' | 'info' | 'default';
|
|
1138
1197
|
interface Props$e {
|
|
1139
1198
|
source?: RecordSource;
|
|
1140
1199
|
status?: RecordStatus;
|
|
@@ -1143,8 +1202,21 @@ interface Props$e {
|
|
|
1143
1202
|
size?: string;
|
|
1144
1203
|
/** Optional accessible label — defaults to the tone name. */
|
|
1145
1204
|
label?: string;
|
|
1205
|
+
/**
|
|
1206
|
+
* Host-supplied icon override. Either a React element (rendered as-is)
|
|
1207
|
+
* or any ReactNode. When set, takes precedence over the status-derived
|
|
1208
|
+
* icon. Pair with `semanticTone` to drive colour.
|
|
1209
|
+
*/
|
|
1210
|
+
iconHint?: ReactNode;
|
|
1211
|
+
/**
|
|
1212
|
+
* Host-supplied semantic tone (success/warning/danger/muted/info). When
|
|
1213
|
+
* set, drives the icon colour AND — if no `iconHint` is supplied —
|
|
1214
|
+
* picks a sensible default lucide icon per tone (CheckCircle2 / Alert /
|
|
1215
|
+
* XCircle / MinusCircle / Info).
|
|
1216
|
+
*/
|
|
1217
|
+
semanticTone?: SemanticTone;
|
|
1146
1218
|
}
|
|
1147
|
-
declare const StatusIcon: ({ source, status, className, size, label }: Props$e) => react_jsx_runtime.JSX.Element;
|
|
1219
|
+
declare const StatusIcon: ({ source, status, className, size, label, iconHint, semanticTone, }: Props$e) => react_jsx_runtime.JSX.Element;
|
|
1148
1220
|
/** Short label rendered next to / under the row title. */
|
|
1149
1221
|
declare const statusToneLabel: (tone: StatusTone) => string;
|
|
1150
1222
|
|
|
@@ -1696,7 +1768,7 @@ declare const useRecordList: (args: UseRecordListArgs) => {
|
|
|
1696
1768
|
};
|
|
1697
1769
|
isLoading: boolean;
|
|
1698
1770
|
error: Error | null;
|
|
1699
|
-
refetch: () => void
|
|
1771
|
+
refetch: () => Promise<void>;
|
|
1700
1772
|
hasNextPage: boolean;
|
|
1701
1773
|
isFetchingNextPage: boolean;
|
|
1702
1774
|
fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<{
|
|
@@ -1841,7 +1913,7 @@ declare function useCollectionItems<T = unknown>(args: UseCollectionItemsArgs):
|
|
|
1841
1913
|
hasMore: boolean;
|
|
1842
1914
|
nextOffset: number;
|
|
1843
1915
|
}, unknown>, Error>>;
|
|
1844
|
-
refetch: () => void
|
|
1916
|
+
refetch: () => Promise<void>;
|
|
1845
1917
|
};
|
|
1846
1918
|
|
|
1847
1919
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AdminPageHeader } from '../../chunk-2MW54ZVG.js';
|
|
1
2
|
import { assertComponentStylesLoaded } from '../../chunk-OLYC54YT.js';
|
|
2
3
|
import '../../chunk-5UQQYXCX.js';
|
|
3
4
|
import { FacetRuleEditor } from '../../chunk-JMCV6FOW.js';
|
|
@@ -5,8 +6,8 @@ import { useFacets } from '../../chunk-4LHF5JB7.js';
|
|
|
5
6
|
import { cn } from '../../chunk-L7FQ52F5.js';
|
|
6
7
|
import { parsedRefToTarget, parsedRefToScope, matchRecords, scopesEqual, getRecordById, listRecords, upsertRecord, updateRecord, createRecord, removeRecord } from '../../chunk-KA4MKRHL.js';
|
|
7
8
|
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
|
|
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, Target, Rows3, ChevronRight, Eraser, ClipboardPaste, Box, X, Search, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, CornerDownLeft, Circle, CopyPlus, AlertCircle, Undo2, Save, Loader2,
|
|
9
|
+
import { createContext, useState, useEffect, useCallback, useMemo, useRef, isValidElement, useContext, useSyncExternalStore, useLayoutEffect, createElement } from 'react';
|
|
10
|
+
import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, List, SlidersHorizontal, Globe, Tag, Boxes, Layers, Package, Target, Rows3, ChevronRight, Eraser, ClipboardPaste, Box, X, Search, Image, Table, ArrowLeft, ChevronLeft, AlertTriangle, Info, HelpCircle, CornerDownLeft, Circle, MinusCircle, XCircle, CopyPlus, AlertCircle, Undo2, Save, Loader2, ArrowRight, Globe2, Check, Settings2 } from 'lucide-react';
|
|
10
11
|
import { useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
|
|
11
12
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
12
13
|
import { createPortal } from 'react-dom';
|
|
@@ -690,9 +691,9 @@ var useRecordList = (args) => {
|
|
|
690
691
|
partial: items.filter((r) => r.status === "partial").length,
|
|
691
692
|
empty: items.filter((r) => r.status === "empty").length
|
|
692
693
|
}), [items]);
|
|
693
|
-
const refetch = useCallback(() => {
|
|
694
|
-
|
|
695
|
-
}, [queryClient, ctx.collectionId, ctx.appId, ctx.recordType]);
|
|
694
|
+
const refetch = useCallback(() => queryClient.refetchQueries({
|
|
695
|
+
queryKey: [...QK_BASE2, ctx.collectionId, ctx.appId, ctx.recordType]
|
|
696
|
+
}), [queryClient, ctx.collectionId, ctx.appId, ctx.recordType]);
|
|
696
697
|
const total = query.data?.pages[query.data.pages.length - 1]?.total ?? items.length;
|
|
697
698
|
return {
|
|
698
699
|
allItems: items,
|
|
@@ -3125,9 +3126,11 @@ var useShellSelection = (args) => {
|
|
|
3125
3126
|
}, [contextScope?.batchId]);
|
|
3126
3127
|
const [selectedItemId, setSelectedItemId] = useState(null);
|
|
3127
3128
|
const skipNextItemResetRef = useRef(false);
|
|
3129
|
+
const [selectedLifecycleKey, setSelectedLifecycleKey] = useState(null);
|
|
3128
3130
|
useEffect(() => {
|
|
3129
3131
|
setSelectedRecordId(null);
|
|
3130
3132
|
setDraftKind(null);
|
|
3133
|
+
setSelectedLifecycleKey(null);
|
|
3131
3134
|
}, [activeScope]);
|
|
3132
3135
|
return {
|
|
3133
3136
|
activeScope,
|
|
@@ -3150,6 +3153,8 @@ var useShellSelection = (args) => {
|
|
|
3150
3153
|
setSelectedBatchId,
|
|
3151
3154
|
selectedItemId,
|
|
3152
3155
|
setSelectedItemId,
|
|
3156
|
+
selectedLifecycleKey,
|
|
3157
|
+
setSelectedLifecycleKey,
|
|
3153
3158
|
skipNextItemResetRef
|
|
3154
3159
|
};
|
|
3155
3160
|
};
|
|
@@ -3245,7 +3250,43 @@ var resolveTone = (source, status) => {
|
|
|
3245
3250
|
if (source === "inherited" || status === "partial") return "shared";
|
|
3246
3251
|
return "missing";
|
|
3247
3252
|
};
|
|
3248
|
-
var
|
|
3253
|
+
var SEMANTIC_DEFAULT_ICONS = {
|
|
3254
|
+
success: CheckCircle2,
|
|
3255
|
+
warning: AlertTriangle,
|
|
3256
|
+
danger: XCircle,
|
|
3257
|
+
muted: MinusCircle,
|
|
3258
|
+
info: Info
|
|
3259
|
+
};
|
|
3260
|
+
var StatusIcon = ({
|
|
3261
|
+
source,
|
|
3262
|
+
status,
|
|
3263
|
+
className,
|
|
3264
|
+
size = "1.05rem",
|
|
3265
|
+
label,
|
|
3266
|
+
iconHint,
|
|
3267
|
+
semanticTone
|
|
3268
|
+
}) => {
|
|
3269
|
+
if (semanticTone || iconHint) {
|
|
3270
|
+
const toneClass = semanticTone && semanticTone !== "default" ? `ra-status-icon--${semanticTone}` : "ra-status-icon--muted";
|
|
3271
|
+
let content;
|
|
3272
|
+
if (iconHint) {
|
|
3273
|
+
content = isValidElement(iconHint) ? iconHint : /* @__PURE__ */ jsx(Fragment, { children: iconHint });
|
|
3274
|
+
} else {
|
|
3275
|
+
const Icon2 = semanticTone && semanticTone !== "default" ? SEMANTIC_DEFAULT_ICONS[semanticTone] : MinusCircle;
|
|
3276
|
+
content = /* @__PURE__ */ jsx(Icon2, { className: "w-full h-full" });
|
|
3277
|
+
}
|
|
3278
|
+
return /* @__PURE__ */ jsx(
|
|
3279
|
+
"span",
|
|
3280
|
+
{
|
|
3281
|
+
className: cn("ra-status-icon", toneClass, className),
|
|
3282
|
+
style: { width: size, height: size },
|
|
3283
|
+
role: label ? "img" : void 0,
|
|
3284
|
+
"aria-label": label,
|
|
3285
|
+
"aria-hidden": label ? void 0 : "true",
|
|
3286
|
+
children: content
|
|
3287
|
+
}
|
|
3288
|
+
);
|
|
3289
|
+
}
|
|
3249
3290
|
const tone = resolveTone(source, status);
|
|
3250
3291
|
const Icon = DEFAULT_ICONS.status[tone === "own" ? "own" : tone === "shared" ? "inherited" : "missing"];
|
|
3251
3292
|
return /* @__PURE__ */ jsx(
|
|
@@ -3478,7 +3519,9 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
|
|
|
3478
3519
|
StatusIcon,
|
|
3479
3520
|
{
|
|
3480
3521
|
status: record.status,
|
|
3481
|
-
label: statusToneLabel(tone)
|
|
3522
|
+
label: statusToneLabel(tone),
|
|
3523
|
+
iconHint: record.iconHint,
|
|
3524
|
+
semanticTone: record.toneHint
|
|
3482
3525
|
}
|
|
3483
3526
|
) }),
|
|
3484
3527
|
/* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
|
|
@@ -4086,9 +4129,7 @@ function useCollectionItems(args) {
|
|
|
4086
4129
|
return toSummary2(rec, base);
|
|
4087
4130
|
}).filter((x) => x !== null);
|
|
4088
4131
|
}, [query.data, toSummary2, scope, ruleSignature, includeAll]);
|
|
4089
|
-
const refetch = useCallback(() => {
|
|
4090
|
-
queryClient.invalidateQueries({ queryKey });
|
|
4091
|
-
}, [queryClient, queryKey]);
|
|
4132
|
+
const refetch = useCallback(() => queryClient.refetchQueries({ queryKey }), [queryClient, queryKey]);
|
|
4092
4133
|
return {
|
|
4093
4134
|
items,
|
|
4094
4135
|
total: query.data?.pages[query.data.pages.length - 1]?.total ?? items.length,
|
|
@@ -6193,89 +6234,6 @@ var UtilityRow = ({ label, customLabel, introHidden, onShowIntro }) => {
|
|
|
6193
6234
|
}
|
|
6194
6235
|
);
|
|
6195
6236
|
};
|
|
6196
|
-
var TONE_ICON2 = {
|
|
6197
|
-
info: Lightbulb,
|
|
6198
|
-
success: CheckCircle2,
|
|
6199
|
-
warning: AlertTriangle
|
|
6200
|
-
};
|
|
6201
|
-
function AdminPageHeader({
|
|
6202
|
-
title,
|
|
6203
|
-
subtitle,
|
|
6204
|
-
icon,
|
|
6205
|
-
helpUrl,
|
|
6206
|
-
helpLabel,
|
|
6207
|
-
actions,
|
|
6208
|
-
aside,
|
|
6209
|
-
intro,
|
|
6210
|
-
className
|
|
6211
|
-
}) {
|
|
6212
|
-
const titleId = useId();
|
|
6213
|
-
const resolvedHelpLabel = helpLabel ?? "Help & documentation";
|
|
6214
|
-
const resolvedReopenLabel = intro?.reopenLabel ?? "How it works";
|
|
6215
|
-
const showReopen = !!intro && intro.dismissed && !!intro.onReopen;
|
|
6216
|
-
const showIntro = !!intro && !intro.dismissed;
|
|
6217
|
-
return /* @__PURE__ */ jsxs("header", { className: `sl-aph${className ? ` ${className}` : ""}`, "aria-labelledby": titleId, children: [
|
|
6218
|
-
/* @__PURE__ */ jsxs("div", { className: "sl-aph__row", children: [
|
|
6219
|
-
/* @__PURE__ */ jsx("div", { className: "sl-aph__main", children: /* @__PURE__ */ jsxs("div", { className: "sl-aph__text", children: [
|
|
6220
|
-
/* @__PURE__ */ jsxs("h1", { className: "sl-aph__title", id: titleId, children: [
|
|
6221
|
-
icon ? /* @__PURE__ */ jsx("span", { className: "sl-aph__icon", "aria-hidden": "true", children: icon }) : null,
|
|
6222
|
-
/* @__PURE__ */ jsx("span", { children: title })
|
|
6223
|
-
] }),
|
|
6224
|
-
subtitle ? /* @__PURE__ */ jsx("p", { className: "sl-aph__subtitle", children: subtitle }) : null
|
|
6225
|
-
] }) }),
|
|
6226
|
-
actions || aside || helpUrl || showReopen ? /* @__PURE__ */ jsxs("div", { className: "sl-aph__aside", children: [
|
|
6227
|
-
actions,
|
|
6228
|
-
aside,
|
|
6229
|
-
helpUrl ? /* @__PURE__ */ jsx(
|
|
6230
|
-
"a",
|
|
6231
|
-
{
|
|
6232
|
-
href: helpUrl,
|
|
6233
|
-
target: "_blank",
|
|
6234
|
-
rel: "noopener noreferrer",
|
|
6235
|
-
className: "sl-aph__icon-btn",
|
|
6236
|
-
"aria-label": resolvedHelpLabel,
|
|
6237
|
-
title: resolvedHelpLabel,
|
|
6238
|
-
children: /* @__PURE__ */ jsx(BookOpen, { "aria-hidden": "true" })
|
|
6239
|
-
}
|
|
6240
|
-
) : null,
|
|
6241
|
-
showReopen ? /* @__PURE__ */ jsx(
|
|
6242
|
-
"button",
|
|
6243
|
-
{
|
|
6244
|
-
type: "button",
|
|
6245
|
-
onClick: intro.onReopen,
|
|
6246
|
-
className: "sl-aph__icon-btn",
|
|
6247
|
-
"aria-label": resolvedReopenLabel,
|
|
6248
|
-
title: resolvedReopenLabel,
|
|
6249
|
-
children: /* @__PURE__ */ jsx(HelpCircle, { "aria-hidden": "true" })
|
|
6250
|
-
}
|
|
6251
|
-
) : null
|
|
6252
|
-
] }) : null
|
|
6253
|
-
] }),
|
|
6254
|
-
showIntro ? /* @__PURE__ */ jsx(AdminPageHeaderIntroCard, { intro }) : null
|
|
6255
|
-
] });
|
|
6256
|
-
}
|
|
6257
|
-
function AdminPageHeaderIntroCard({ intro }) {
|
|
6258
|
-
const tone = intro.tone ?? "info";
|
|
6259
|
-
const Icon = TONE_ICON2[tone] ?? Info;
|
|
6260
|
-
return /* @__PURE__ */ jsxs("div", { className: "sl-aph__intro", "data-tone": tone, role: "note", children: [
|
|
6261
|
-
/* @__PURE__ */ jsx("div", { className: "sl-aph__intro-icon", children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true", style: { width: "0.95rem", height: "0.95rem" } }) }),
|
|
6262
|
-
/* @__PURE__ */ jsxs("div", { className: "sl-aph__intro-body", children: [
|
|
6263
|
-
/* @__PURE__ */ jsx("h4", { className: "sl-aph__intro-title", children: intro.title }),
|
|
6264
|
-
/* @__PURE__ */ jsx("span", { className: "sl-aph__intro-text", children: intro.body }),
|
|
6265
|
-
intro.action ? /* @__PURE__ */ jsx("span", { className: "sl-aph__intro-action", children: intro.action }) : null
|
|
6266
|
-
] }),
|
|
6267
|
-
/* @__PURE__ */ jsx(
|
|
6268
|
-
"button",
|
|
6269
|
-
{
|
|
6270
|
-
type: "button",
|
|
6271
|
-
onClick: intro.onDismiss,
|
|
6272
|
-
"aria-label": "Dismiss",
|
|
6273
|
-
className: "sl-aph__intro-dismiss",
|
|
6274
|
-
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", style: { width: "0.875rem", height: "0.875rem" } })
|
|
6275
|
-
}
|
|
6276
|
-
)
|
|
6277
|
-
] });
|
|
6278
|
-
}
|
|
6279
6237
|
function ShellHeader({
|
|
6280
6238
|
title,
|
|
6281
6239
|
subtitle,
|
|
@@ -7452,6 +7410,8 @@ function RecordsAdminShellInner(props) {
|
|
|
7452
7410
|
renderListRow,
|
|
7453
7411
|
renderEmpty: renderEmptyState,
|
|
7454
7412
|
groupBy,
|
|
7413
|
+
defaultGroupKey,
|
|
7414
|
+
onGroupExpanded,
|
|
7455
7415
|
density = "comfortable"
|
|
7456
7416
|
} = rail ?? {};
|
|
7457
7417
|
const {
|
|
@@ -7474,7 +7434,8 @@ function RecordsAdminShellInner(props) {
|
|
|
7474
7434
|
renderCard: renderItemCard,
|
|
7475
7435
|
renderEmpty: renderItemEmpty,
|
|
7476
7436
|
cardSize: itemCardSize = "md",
|
|
7477
|
-
railMode: collectionRailMode = "siblings"
|
|
7437
|
+
railMode: collectionRailMode = "siblings",
|
|
7438
|
+
toSummary: itemToSummary
|
|
7478
7439
|
} = items ?? {};
|
|
7479
7440
|
const {
|
|
7480
7441
|
strategy: dirtyStrategy = "keep",
|
|
@@ -7592,8 +7553,11 @@ function RecordsAdminShellInner(props) {
|
|
|
7592
7553
|
setSelectedBatchId,
|
|
7593
7554
|
selectedItemId,
|
|
7594
7555
|
setSelectedItemId,
|
|
7556
|
+
selectedLifecycleKey,
|
|
7557
|
+
setSelectedLifecycleKey,
|
|
7595
7558
|
skipNextItemResetRef
|
|
7596
7559
|
} = selection;
|
|
7560
|
+
const [isReconcilingRecordSelection, setIsReconcilingRecordSelection] = useState(false);
|
|
7597
7561
|
const { dismissed, dismiss, undismiss } = useIntroDismissed(SL, collectionId, appId, recordType);
|
|
7598
7562
|
const headerWillRender = useMemo(() => {
|
|
7599
7563
|
const headerCustomised = !!title || !!subtitle || !!headerIcon || !!headerActions || showStats || !!statsItems || !!statsTitle || !!statsIcon || !!helpUrl;
|
|
@@ -7654,6 +7618,7 @@ function RecordsAdminShellInner(props) {
|
|
|
7654
7618
|
useEffect(() => {
|
|
7655
7619
|
if (activeScope !== "rule" && activeScope !== "collection" && activeScope !== "all") return;
|
|
7656
7620
|
if (selectedRecordId !== null) return;
|
|
7621
|
+
if (isReconcilingRecordSelection) return;
|
|
7657
7622
|
if (ruleWizardStep !== null) return;
|
|
7658
7623
|
if (draftKind !== null) return;
|
|
7659
7624
|
if (activeScope === "collection" && cardinality === "collection") {
|
|
@@ -7664,7 +7629,7 @@ function RecordsAdminShellInner(props) {
|
|
|
7664
7629
|
}
|
|
7665
7630
|
const first = recordList.items[0];
|
|
7666
7631
|
if (first?.id) setSelectedRecordId(first.id);
|
|
7667
|
-
}, [activeScope, selectedRecordId, recordList.items, cardinality, ruleWizardStep, draftKind]);
|
|
7632
|
+
}, [activeScope, selectedRecordId, recordList.items, cardinality, ruleWizardStep, draftKind, isReconcilingRecordSelection]);
|
|
7668
7633
|
const editingScopes = useEditingScope({
|
|
7669
7634
|
activeScope,
|
|
7670
7635
|
cardinality,
|
|
@@ -7692,7 +7657,8 @@ function RecordsAdminShellInner(props) {
|
|
|
7692
7657
|
// record across the whole collection (anchored, ruled, global) so
|
|
7693
7658
|
// host-supplied lifecycle grouping has the full picture.
|
|
7694
7659
|
includeAll: isCollection && activeScope === "all",
|
|
7695
|
-
enabled: isCollection
|
|
7660
|
+
enabled: isCollection,
|
|
7661
|
+
toSummary: itemToSummary
|
|
7696
7662
|
});
|
|
7697
7663
|
useEffect(() => {
|
|
7698
7664
|
if (skipNextItemResetRef.current) {
|
|
@@ -7761,10 +7727,15 @@ function RecordsAdminShellInner(props) {
|
|
|
7761
7727
|
supportedScopes: supportedForResolution,
|
|
7762
7728
|
enabled: !!editingTargetScope
|
|
7763
7729
|
});
|
|
7764
|
-
const refetchAll = useCallback(() => {
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7730
|
+
const refetchAll = useCallback(async () => {
|
|
7731
|
+
await Promise.all([
|
|
7732
|
+
recordList.refetch(),
|
|
7733
|
+
isCollection ? collectionItems.refetch() : Promise.resolve(),
|
|
7734
|
+
queryClient.refetchQueries({
|
|
7735
|
+
queryKey: scopeCountsQueryKey(ctx.collectionId, ctx.appId, ctx.recordType)
|
|
7736
|
+
})
|
|
7737
|
+
]);
|
|
7738
|
+
}, [recordList, isCollection, collectionItems, queryClient, ctx.collectionId, ctx.appId, ctx.recordType]);
|
|
7768
7739
|
const { editorCtx } = useShellEditorTarget({
|
|
7769
7740
|
editingTargetScope,
|
|
7770
7741
|
isCollection,
|
|
@@ -7783,7 +7754,7 @@ function RecordsAdminShellInner(props) {
|
|
|
7783
7754
|
},
|
|
7784
7755
|
defaultData,
|
|
7785
7756
|
deriveDraftLabel,
|
|
7786
|
-
onSaved: (isCreate, savedRecordId) => {
|
|
7757
|
+
onSaved: async (isCreate, savedRecordId) => {
|
|
7787
7758
|
onTelemetry?.({ type: "record.save", recordType, ref: editingTargetScope?.raw ?? "", isCreate });
|
|
7788
7759
|
if (ruleWizardStep !== null) {
|
|
7789
7760
|
setRuleWizardStep(null);
|
|
@@ -7791,15 +7762,20 @@ function RecordsAdminShellInner(props) {
|
|
|
7791
7762
|
setDraftKind(null);
|
|
7792
7763
|
}
|
|
7793
7764
|
if (isCreate && selectedRecordId === DRAFT_ID3) {
|
|
7794
|
-
|
|
7765
|
+
setIsReconcilingRecordSelection(true);
|
|
7766
|
+
setSelectedRecordId(null);
|
|
7795
7767
|
setDraftKind(null);
|
|
7796
7768
|
}
|
|
7797
7769
|
if (isCreate && isCollection && savedRecordId && isDraftId3(selectedItemId)) {
|
|
7798
7770
|
setSelectedItemId(savedRecordId);
|
|
7799
7771
|
}
|
|
7800
|
-
refetchAll();
|
|
7772
|
+
await refetchAll();
|
|
7773
|
+
if (!isCollection && isCreate && activeScope === "collection") {
|
|
7774
|
+
setSelectedRecordId(savedRecordId ?? null);
|
|
7775
|
+
}
|
|
7776
|
+
setIsReconcilingRecordSelection(false);
|
|
7801
7777
|
},
|
|
7802
|
-
onDeleted: () => {
|
|
7778
|
+
onDeleted: async () => {
|
|
7803
7779
|
onTelemetry?.({ type: "record.delete", recordType, ref: editingTargetScope?.raw ?? "" });
|
|
7804
7780
|
if (isCollection && selectedItemId) {
|
|
7805
7781
|
setSelectedItemId(null);
|
|
@@ -7808,10 +7784,12 @@ function RecordsAdminShellInner(props) {
|
|
|
7808
7784
|
} else if (drillTab === "batch") {
|
|
7809
7785
|
setSelectedBatchId(void 0);
|
|
7810
7786
|
} else if (selectedRecordId) {
|
|
7787
|
+
setIsReconcilingRecordSelection(true);
|
|
7811
7788
|
setSelectedRecordId(null);
|
|
7812
7789
|
setDraftKind(null);
|
|
7813
7790
|
}
|
|
7814
|
-
refetchAll();
|
|
7791
|
+
await refetchAll();
|
|
7792
|
+
setIsReconcilingRecordSelection(false);
|
|
7815
7793
|
}
|
|
7816
7794
|
});
|
|
7817
7795
|
useUnsavedGuard({
|
|
@@ -8426,17 +8404,96 @@ function RecordsAdminShellInner(props) {
|
|
|
8426
8404
|
}),
|
|
8427
8405
|
[i18n.itemsAllLabel, collectionItems.items.length, itemNoun]
|
|
8428
8406
|
);
|
|
8407
|
+
const isLifecycleRail = (isAllTab || isGlobalTab) && isCollection && !!groupBy;
|
|
8408
|
+
const lifecycleBuckets = useMemo(() => {
|
|
8409
|
+
if (!isLifecycleRail || !groupBy) return [];
|
|
8410
|
+
const map = /* @__PURE__ */ new Map();
|
|
8411
|
+
const order = [];
|
|
8412
|
+
for (const item of collectionItems.items) {
|
|
8413
|
+
const g = groupBy(item) ?? { key: "__other", label: "Other" };
|
|
8414
|
+
let bucket = map.get(g.key);
|
|
8415
|
+
if (!bucket) {
|
|
8416
|
+
bucket = { key: g.key, label: g.label, icon: g.icon, tone: g.tone, items: [] };
|
|
8417
|
+
map.set(g.key, bucket);
|
|
8418
|
+
order.push(g.key);
|
|
8419
|
+
}
|
|
8420
|
+
bucket.items.push(item);
|
|
8421
|
+
}
|
|
8422
|
+
return order.sort().map((k) => map.get(k));
|
|
8423
|
+
}, [isLifecycleRail, groupBy, collectionItems.items]);
|
|
8424
|
+
const LIFECYCLE_PREFIX = "lifecycle:";
|
|
8425
|
+
const lifecycleRows = useMemo(() => {
|
|
8426
|
+
if (!isLifecycleRail) return [];
|
|
8427
|
+
const rows = [
|
|
8428
|
+
{
|
|
8429
|
+
id: `${LIFECYCLE_PREFIX}__all`,
|
|
8430
|
+
ref: "",
|
|
8431
|
+
scope: parseRef(""),
|
|
8432
|
+
data: null,
|
|
8433
|
+
status: collectionItems.items.length ? "configured" : "empty",
|
|
8434
|
+
label: i18n.itemsAllLabel,
|
|
8435
|
+
subtitle: collectionItems.items.length ? `${collectionItems.items.length} ${itemNoun}${collectionItems.items.length === 1 ? "" : "s"}` : void 0
|
|
8436
|
+
}
|
|
8437
|
+
];
|
|
8438
|
+
for (const b of lifecycleBuckets) {
|
|
8439
|
+
rows.push({
|
|
8440
|
+
id: `${LIFECYCLE_PREFIX}${b.key}`,
|
|
8441
|
+
ref: "",
|
|
8442
|
+
scope: parseRef(""),
|
|
8443
|
+
data: null,
|
|
8444
|
+
status: b.items.length ? "configured" : "empty",
|
|
8445
|
+
label: b.label,
|
|
8446
|
+
subtitle: `${b.items.length} ${itemNoun}${b.items.length === 1 ? "" : "s"}`,
|
|
8447
|
+
// Tone + icon drive the row's leading status icon (replacing the
|
|
8448
|
+
// generic green tick / dotted circle for these synthetic rows).
|
|
8449
|
+
// Hosts can override the icon entirely via `groupBy(...).icon`.
|
|
8450
|
+
iconHint: b.icon,
|
|
8451
|
+
toneHint: b.tone
|
|
8452
|
+
});
|
|
8453
|
+
}
|
|
8454
|
+
return rows;
|
|
8455
|
+
}, [isLifecycleRail, lifecycleBuckets, collectionItems.items.length, i18n.itemsAllLabel, itemNoun]);
|
|
8456
|
+
const lifecycleSeededRef = useRef(false);
|
|
8457
|
+
useEffect(() => {
|
|
8458
|
+
if (!isLifecycleRail) {
|
|
8459
|
+
lifecycleSeededRef.current = false;
|
|
8460
|
+
return;
|
|
8461
|
+
}
|
|
8462
|
+
if (lifecycleSeededRef.current) return;
|
|
8463
|
+
if (selectedLifecycleKey !== null) {
|
|
8464
|
+
lifecycleSeededRef.current = true;
|
|
8465
|
+
return;
|
|
8466
|
+
}
|
|
8467
|
+
if (!defaultGroupKey) return;
|
|
8468
|
+
if (!lifecycleBuckets.some((b) => b.key === defaultGroupKey)) return;
|
|
8469
|
+
setSelectedLifecycleKey(defaultGroupKey);
|
|
8470
|
+
lifecycleSeededRef.current = true;
|
|
8471
|
+
}, [isLifecycleRail, defaultGroupKey, lifecycleBuckets, selectedLifecycleKey, setSelectedLifecycleKey]);
|
|
8472
|
+
const filteredCollectionItems = useMemo(() => {
|
|
8473
|
+
if (!isLifecycleRail || !selectedLifecycleKey || !groupBy) return collectionItems.items;
|
|
8474
|
+
return collectionItems.items.filter((it) => {
|
|
8475
|
+
const g = groupBy(it) ?? { key: "__other" };
|
|
8476
|
+
return g.key === selectedLifecycleKey;
|
|
8477
|
+
});
|
|
8478
|
+
}, [isLifecycleRail, selectedLifecycleKey, groupBy, collectionItems.items]);
|
|
8429
8479
|
const leftItems = isProductTab ? productListItems : isRuleTab ? applyFacetBrowseFilter(
|
|
8430
8480
|
isCollection ? collectionRuleRailItems : filteredRuleItems
|
|
8431
|
-
) : (isGlobalTab || isAllTab) && isCollection ? [collectionGlobalAllRow] : isRecordsTab ? applyFacetBrowseFilter(recordList.items) : [];
|
|
8481
|
+
) : isLifecycleRail ? lifecycleRows : (isGlobalTab || isAllTab) && isCollection ? [collectionGlobalAllRow] : isRecordsTab ? applyFacetBrowseFilter(recordList.items) : [];
|
|
8432
8482
|
const leftLoading = isProductTab ? !productPinned && productBrowse.isLoading : isRecordsTab ? recordList.isLoading || probe.isLoading : false;
|
|
8433
8483
|
const leftError = isProductTab ? productBrowse.error : isRecordsTab ? recordList.error : null;
|
|
8434
|
-
const leftSelectedId = isProductTab ? void 0 : selectedRecordId && selectedRecordId !== DRAFT_ID3 ? selectedRecordId : void 0;
|
|
8484
|
+
const leftSelectedId = isProductTab ? void 0 : isLifecycleRail ? `${LIFECYCLE_PREFIX}${selectedLifecycleKey ?? "__all"}` : selectedRecordId && selectedRecordId !== DRAFT_ID3 ? selectedRecordId : void 0;
|
|
8435
8485
|
const leftSelectedAnchorKey = isProductTab ? selectedProductId ? buildRef({ productId: selectedProductId }) : void 0 : void 0;
|
|
8436
8486
|
const dirtyId = !editorCtx.isDirty ? void 0 : selectedRecordId && selectedRecordId !== DRAFT_ID3 ? selectedRecordId : void 0;
|
|
8437
8487
|
const dirtyAnchorKey = !editorCtx.isDirty ? void 0 : isProductTab ? editingScope?.raw : void 0;
|
|
8438
8488
|
const onLeftSelect = (item) => {
|
|
8439
8489
|
void runWithGuard(() => {
|
|
8490
|
+
if (isLifecycleRail && typeof item.id === "string" && item.id.startsWith(LIFECYCLE_PREFIX)) {
|
|
8491
|
+
const key = item.id.slice(LIFECYCLE_PREFIX.length);
|
|
8492
|
+
const next = key === "__all" ? null : key;
|
|
8493
|
+
setSelectedLifecycleKey(next);
|
|
8494
|
+
if (next && onGroupExpanded) onGroupExpanded(next);
|
|
8495
|
+
return;
|
|
8496
|
+
}
|
|
8440
8497
|
if (isProductTab) {
|
|
8441
8498
|
setSelectedProductId(item.scope.productId);
|
|
8442
8499
|
setSelectedVariantId(void 0);
|
|
@@ -8794,7 +8851,7 @@ function RecordsAdminShellInner(props) {
|
|
|
8794
8851
|
// navigational anchor, not a real record — applying the
|
|
8795
8852
|
// host's groupBy bucketed it under "Other" (its
|
|
8796
8853
|
// data is null). Skip grouping for that single-row rail.
|
|
8797
|
-
(isGlobalTab || isAllTab) && isCollection ? void 0 : effectiveGroupBy
|
|
8854
|
+
(isGlobalTab || isAllTab) && isCollection || isLifecycleRail ? void 0 : effectiveGroupBy
|
|
8798
8855
|
),
|
|
8799
8856
|
renderGroupActions: renderRuleGroupActions,
|
|
8800
8857
|
rowClipboard,
|
|
@@ -8883,7 +8940,7 @@ function RecordsAdminShellInner(props) {
|
|
|
8883
8940
|
ruleWizardStep === null && isCollection && editingScope && !selectedItemId && /* @__PURE__ */ jsx(
|
|
8884
8941
|
ItemListView,
|
|
8885
8942
|
{
|
|
8886
|
-
items:
|
|
8943
|
+
items: filteredCollectionItems,
|
|
8887
8944
|
isLoading: collectionItems.isLoading,
|
|
8888
8945
|
error: collectionItems.error,
|
|
8889
8946
|
ctx: itemViewCtx,
|