@proveanything/smartlinks-utils-ui 0.1.13 → 0.3.0
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/README.md +100 -64
- package/dist/{chunk-ECAGO3HU.js → chunk-XA5J6CZL.js} +672 -25
- package/dist/chunk-XA5J6CZL.js.map +1 -0
- package/dist/components/AssetPicker/index.d.ts +1 -1
- package/dist/components/AssetPicker/index.js +1 -1
- package/dist/components/RecordsAdmin/index.css +40 -0
- package/dist/components/RecordsAdmin/index.css.map +1 -0
- package/dist/components/RecordsAdmin/index.d.ts +1083 -0
- package/dist/components/RecordsAdmin/index.js +2833 -0
- package/dist/components/RecordsAdmin/index.js.map +1 -0
- package/dist/{index-Ba3K3Ehs.d.ts → index-BmKyfKiK.d.ts} +19 -0
- package/dist/index.css +249 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +21 -10
- package/dist/chunk-ECAGO3HU.js.map +0 -1
|
@@ -0,0 +1,1083 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import * as _tanstack_query_core from '@tanstack/query-core';
|
|
4
|
+
import { RecordScope, AppRecord, RecordTarget, MatchResult } from '@proveanything/smartlinks/dist/types/appObjects';
|
|
5
|
+
|
|
6
|
+
type ScopeKind = 'product' | 'facet' | 'variant' | 'batch';
|
|
7
|
+
/** Parsed `ref` string — see `data/refs.ts`. Format: `kind:id` or chain. */
|
|
8
|
+
interface ParsedRef {
|
|
9
|
+
/** Most-specific scope this ref points at. */
|
|
10
|
+
kind: ScopeKind | 'collection';
|
|
11
|
+
/** Raw ref string, e.g. `product:abc123` or `facet:gluten-free/variant:500ml`. */
|
|
12
|
+
raw: string;
|
|
13
|
+
collectionId?: string;
|
|
14
|
+
productId?: string;
|
|
15
|
+
facetId?: string;
|
|
16
|
+
facetValue?: string;
|
|
17
|
+
variantId?: string;
|
|
18
|
+
batchId?: string;
|
|
19
|
+
proofId?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type RecordSource = 'self' | 'inherited' | 'empty';
|
|
23
|
+
type RecordStatus = 'configured' | 'partial' | 'empty';
|
|
24
|
+
/** Optional badge displayed on a record card or row. */
|
|
25
|
+
interface RecordBadge {
|
|
26
|
+
label: string;
|
|
27
|
+
/** Semantic tone — apps may map to their own colour vocabulary. */
|
|
28
|
+
tone?: 'neutral' | 'info' | 'success' | 'warning' | 'danger';
|
|
29
|
+
}
|
|
30
|
+
interface RecordSummary<TData = unknown> {
|
|
31
|
+
id: string | null;
|
|
32
|
+
ref: string;
|
|
33
|
+
scope: ParsedRef;
|
|
34
|
+
data: TData | null;
|
|
35
|
+
status: RecordStatus;
|
|
36
|
+
/** Display label for the list row. */
|
|
37
|
+
label: string;
|
|
38
|
+
/** Optional subtitle (e.g. SKU, GTIN). */
|
|
39
|
+
subtitle?: string;
|
|
40
|
+
updatedAt?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Optional thumbnail URL used by card / gallery presentations. Apps that
|
|
43
|
+
* don't supply one fall back to a coloured tile with the label initials.
|
|
44
|
+
*/
|
|
45
|
+
thumbnail?: string;
|
|
46
|
+
/** Optional small badges (e.g. "Draft", "12 items"). */
|
|
47
|
+
badges?: RecordBadge[];
|
|
48
|
+
/**
|
|
49
|
+
* For `collection` cardinality record types: the per-item identifier within
|
|
50
|
+
* a scope. The full ref is `{scopeRef}/item:{itemId}`. Singleton records
|
|
51
|
+
* leave this undefined.
|
|
52
|
+
*/
|
|
53
|
+
itemId?: string;
|
|
54
|
+
}
|
|
55
|
+
interface ResolvedRecord<TData = unknown> {
|
|
56
|
+
data: TData | null;
|
|
57
|
+
source: RecordSource;
|
|
58
|
+
/** Where the resolved value came from (own scope or parent ref). */
|
|
59
|
+
sourceRef?: string;
|
|
60
|
+
/** The parent value that would be inherited if this scope cleared its override. */
|
|
61
|
+
parentValue?: TData | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface EditorContext<TData = unknown> {
|
|
65
|
+
value: TData;
|
|
66
|
+
onChange: (next: TData) => void;
|
|
67
|
+
source: RecordSource;
|
|
68
|
+
parentValue?: TData | null;
|
|
69
|
+
scope: ParsedRef;
|
|
70
|
+
isDirty: boolean;
|
|
71
|
+
save: () => Promise<void>;
|
|
72
|
+
/** Discard local changes (reverts to last saved/inherited value). */
|
|
73
|
+
reset: () => void;
|
|
74
|
+
/** Permanently delete this record's own override (when `source === 'self'`). */
|
|
75
|
+
remove: () => Promise<void>;
|
|
76
|
+
/** True when a saved-own record exists that can be removed. */
|
|
77
|
+
canRemove: boolean;
|
|
78
|
+
/** True while an optimistic save is in flight (request not yet acknowledged). */
|
|
79
|
+
isSaving?: boolean;
|
|
80
|
+
/** Last save error, if any. Cleared when the next save attempt starts. */
|
|
81
|
+
saveError?: unknown | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface CsvSchemaColumn<TData> {
|
|
85
|
+
key: string;
|
|
86
|
+
header: string;
|
|
87
|
+
toCell: (v: TData) => string;
|
|
88
|
+
fromCell: (s: string) => unknown;
|
|
89
|
+
}
|
|
90
|
+
interface CsvSchema<TData> {
|
|
91
|
+
columns: Array<CsvSchemaColumn<TData>>;
|
|
92
|
+
validate?: (row: Record<string, unknown>) => string | null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* How records are laid out in the browse rail / panel.
|
|
97
|
+
* - `list` → dense rows with status dot + label + subtitle (default).
|
|
98
|
+
* - `grid` → square cards with thumbnail + label.
|
|
99
|
+
* - `gallery` → larger 16:9 cards with thumbnail + label + subtitle.
|
|
100
|
+
* - `compact` → minimal one-line rows (no subtitle, no status dot).
|
|
101
|
+
*/
|
|
102
|
+
type RecordPresentation = 'list' | 'grid' | 'gallery' | 'compact';
|
|
103
|
+
declare const ALL_PRESENTATIONS: RecordPresentation[];
|
|
104
|
+
/**
|
|
105
|
+
* Whether each scope holds at most one record of this type, or many.
|
|
106
|
+
* - `singleton` → one record per scope (e.g. washing instructions). Default.
|
|
107
|
+
* - `collection` → many records per scope (e.g. FAQs, recipes, SOPs).
|
|
108
|
+
*/
|
|
109
|
+
type RecordCardinality = 'singleton' | 'collection';
|
|
110
|
+
/**
|
|
111
|
+
* How `useCollectedRecords` orders the aggregated chain.
|
|
112
|
+
* - `specificity` → most-specific scope first (default).
|
|
113
|
+
* - `field` → use a numeric/string `data.<field>` to sort.
|
|
114
|
+
*/
|
|
115
|
+
type CollectedSort = {
|
|
116
|
+
kind: 'specificity';
|
|
117
|
+
direction?: 'asc' | 'desc';
|
|
118
|
+
} | {
|
|
119
|
+
kind: 'field';
|
|
120
|
+
field: string;
|
|
121
|
+
direction?: 'asc' | 'desc';
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
type TelemetryEvent = {
|
|
125
|
+
type: 'record.save';
|
|
126
|
+
recordType: string;
|
|
127
|
+
ref: string;
|
|
128
|
+
isCreate: boolean;
|
|
129
|
+
} | {
|
|
130
|
+
type: 'record.delete';
|
|
131
|
+
recordType: string;
|
|
132
|
+
ref: string;
|
|
133
|
+
} | {
|
|
134
|
+
type: 'scope.change';
|
|
135
|
+
recordType: string;
|
|
136
|
+
from: ScopeKind;
|
|
137
|
+
to: ScopeKind;
|
|
138
|
+
} | {
|
|
139
|
+
type: 'intro.dismiss';
|
|
140
|
+
recordType: string;
|
|
141
|
+
} | {
|
|
142
|
+
type: 'csv.import';
|
|
143
|
+
recordType: string;
|
|
144
|
+
rows: number;
|
|
145
|
+
errors: number;
|
|
146
|
+
} | {
|
|
147
|
+
type: 'csv.export';
|
|
148
|
+
recordType: string;
|
|
149
|
+
rows: number;
|
|
150
|
+
} | {
|
|
151
|
+
type: 'bulk.apply';
|
|
152
|
+
recordType: string;
|
|
153
|
+
targetCount: number;
|
|
154
|
+
} | {
|
|
155
|
+
type: 'presentation.change';
|
|
156
|
+
recordType: string;
|
|
157
|
+
from: RecordPresentation;
|
|
158
|
+
to: RecordPresentation;
|
|
159
|
+
} | {
|
|
160
|
+
type: 'item.create';
|
|
161
|
+
recordType: string;
|
|
162
|
+
scopeRef: string;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
interface RecordsAdminI18n {
|
|
166
|
+
emptyTitle: string;
|
|
167
|
+
emptyBody: string;
|
|
168
|
+
noResults: string;
|
|
169
|
+
searchPlaceholder: string;
|
|
170
|
+
filterAll: string;
|
|
171
|
+
filterConfigured: string;
|
|
172
|
+
filterPartial: string;
|
|
173
|
+
filterEmpty: string;
|
|
174
|
+
save: string;
|
|
175
|
+
discard: string;
|
|
176
|
+
delete: string;
|
|
177
|
+
/** Save button label while a save is in flight. */
|
|
178
|
+
saving: string;
|
|
179
|
+
/** Inline error label shown next to the Save button when a save fails. */
|
|
180
|
+
saveError: string;
|
|
181
|
+
inherited: string;
|
|
182
|
+
override: string;
|
|
183
|
+
reset: string;
|
|
184
|
+
bulkActions: string;
|
|
185
|
+
applyToMany: string;
|
|
186
|
+
copyFrom: string;
|
|
187
|
+
clear: string;
|
|
188
|
+
importCsv: string;
|
|
189
|
+
exportCsv: string;
|
|
190
|
+
preview: string;
|
|
191
|
+
editor: string;
|
|
192
|
+
closePreview: string;
|
|
193
|
+
openPreview: string;
|
|
194
|
+
previewAs: string;
|
|
195
|
+
previewAsDefault: string;
|
|
196
|
+
confirmDelete: string;
|
|
197
|
+
unsavedBadge: string;
|
|
198
|
+
unsavedPromptTitle: string;
|
|
199
|
+
unsavedPromptBody: string;
|
|
200
|
+
unsavedPromptDiscard: string;
|
|
201
|
+
unsavedPromptCancel: string;
|
|
202
|
+
unsavedPromptSave: string;
|
|
203
|
+
/** Presentation mode switcher labels. */
|
|
204
|
+
presentationList: string;
|
|
205
|
+
presentationGrid: string;
|
|
206
|
+
presentationGallery: string;
|
|
207
|
+
presentationCompact: string;
|
|
208
|
+
/** Collection cardinality affordances. */
|
|
209
|
+
newItem: string;
|
|
210
|
+
noItemsTitle: string;
|
|
211
|
+
noItemsBody: string;
|
|
212
|
+
}
|
|
213
|
+
declare const DEFAULT_I18N: RecordsAdminI18n;
|
|
214
|
+
|
|
215
|
+
/** Minimal SDK shape consumed by the shell. */
|
|
216
|
+
type SmartLinksSDK = any;
|
|
217
|
+
/** Slot context passed to renderCard / renderListRow. */
|
|
218
|
+
interface RecordSlotContext {
|
|
219
|
+
selected: boolean;
|
|
220
|
+
onSelect: () => void;
|
|
221
|
+
isDirty?: boolean;
|
|
222
|
+
}
|
|
223
|
+
interface RecordsAdminShellProps<TData = unknown> {
|
|
224
|
+
SL: SmartLinksSDK;
|
|
225
|
+
appId: string;
|
|
226
|
+
collectionId: string;
|
|
227
|
+
recordType: string;
|
|
228
|
+
label: string;
|
|
229
|
+
scopes: ScopeKind[];
|
|
230
|
+
defaultScope?: ScopeKind;
|
|
231
|
+
/**
|
|
232
|
+
* Context from the host URL (productId/variantId/batchId).
|
|
233
|
+
* When provided, the browser is constrained to that subtree:
|
|
234
|
+
* — `batchId` set → editing one batch (no list / variants pinned)
|
|
235
|
+
* — `variantId` set → list of batches under that variant
|
|
236
|
+
* — `productId` set → list of variants/batches/the product itself
|
|
237
|
+
* — none set → full collection-level browser (current behaviour)
|
|
238
|
+
*/
|
|
239
|
+
contextScope?: {
|
|
240
|
+
productId?: string;
|
|
241
|
+
variantId?: string;
|
|
242
|
+
batchId?: string;
|
|
243
|
+
};
|
|
244
|
+
renderEditor: (ctx: EditorContext<TData>) => ReactNode;
|
|
245
|
+
/**
|
|
246
|
+
* Render the preview surface. Receives the editor's current value so the
|
|
247
|
+
* preview tracks unsaved edits. When `previewScope` differs from the
|
|
248
|
+
* editing scope (because the user picked something in `<PreviewScopePicker>`),
|
|
249
|
+
* `previewScope` reflects the chosen target — apps can use it to load
|
|
250
|
+
* resolved/collected/merged data for that scope instead of `resolved`.
|
|
251
|
+
*/
|
|
252
|
+
renderPreview?: (ctx: {
|
|
253
|
+
resolved: TData | null;
|
|
254
|
+
previewScope: ParsedRef;
|
|
255
|
+
}) => ReactNode;
|
|
256
|
+
/**
|
|
257
|
+
* How the preview surface attaches to the editor pane.
|
|
258
|
+
* - `inline` (default): rendered below the form (current behaviour).
|
|
259
|
+
* - `side`: resizable right pane next to the editor.
|
|
260
|
+
* - `tab`: Editor / Preview siblings inside the right pane.
|
|
261
|
+
* - `drawer`: slide-out from the right, toggled by a button.
|
|
262
|
+
*/
|
|
263
|
+
previewMode?: 'inline' | 'side' | 'tab' | 'drawer';
|
|
264
|
+
/**
|
|
265
|
+
* When true, render a `<PreviewScopePicker>` in the preview chrome so admins
|
|
266
|
+
* can preview as a different product/variant/batch than they're editing.
|
|
267
|
+
* Default false.
|
|
268
|
+
*/
|
|
269
|
+
previewScopePicker?: boolean;
|
|
270
|
+
/**
|
|
271
|
+
* How to handle unsaved edits when the user navigates to a different
|
|
272
|
+
* record/scope/tab.
|
|
273
|
+
* - `prompt` (default): show a confirm dialog (Discard / Cancel / Save).
|
|
274
|
+
* - `autosave`: silently save before navigating away.
|
|
275
|
+
* - `keep`: hold edits in memory keyed by ref; coming back restores them.
|
|
276
|
+
*/
|
|
277
|
+
dirtyStrategy?: 'prompt' | 'autosave' | 'keep';
|
|
278
|
+
/**
|
|
279
|
+
* Pre-delete hook. Return `false` to abort. Lets apps run app-specific
|
|
280
|
+
* confirms (e.g. "this batch has 12k linked proofs"). When omitted the
|
|
281
|
+
* inline two-step confirm is the only safeguard.
|
|
282
|
+
*/
|
|
283
|
+
onBeforeDelete?: (scope: ParsedRef) => boolean | Promise<boolean>;
|
|
284
|
+
/**
|
|
285
|
+
* Disable the browser-level `beforeunload` prompt. Default false.
|
|
286
|
+
* Set true when the host platform handles unload guarding itself.
|
|
287
|
+
*/
|
|
288
|
+
disableBeforeUnload?: boolean;
|
|
289
|
+
intro?: {
|
|
290
|
+
title: string;
|
|
291
|
+
body: ReactNode;
|
|
292
|
+
};
|
|
293
|
+
csvSchema?: CsvSchema<TData>;
|
|
294
|
+
classify?: (record: RecordSummary<TData>) => RecordStatus;
|
|
295
|
+
defaultData?: () => TData;
|
|
296
|
+
/**
|
|
297
|
+
* Which layouts the rail offers. Default `['list']`. When more than one is
|
|
298
|
+
* supplied, a switcher appears above the list and the choice persists
|
|
299
|
+
* per-app/recordType.
|
|
300
|
+
*/
|
|
301
|
+
presentations?: RecordPresentation[];
|
|
302
|
+
/** Initial presentation when nothing is persisted. Default first of `presentations`. */
|
|
303
|
+
defaultPresentation?: RecordPresentation;
|
|
304
|
+
/**
|
|
305
|
+
* Optional custom card renderer used by `grid` / `gallery` presentations.
|
|
306
|
+
* If omitted the shell uses `<DefaultRecordCard>`.
|
|
307
|
+
*/
|
|
308
|
+
renderCard?: (record: RecordSummary<TData>, ctx: RecordSlotContext) => ReactNode;
|
|
309
|
+
/**
|
|
310
|
+
* Optional custom list-row renderer used by `list` / `compact` presentations.
|
|
311
|
+
* If omitted the shell uses `<DefaultRecordRow>`.
|
|
312
|
+
*/
|
|
313
|
+
renderListRow?: (record: RecordSummary<TData>, ctx: RecordSlotContext) => ReactNode;
|
|
314
|
+
/**
|
|
315
|
+
* Optional custom empty-state renderer for the rail. If omitted the shell
|
|
316
|
+
* uses the default `<EmptyState>` with `i18n.emptyTitle` / `i18n.emptyBody`.
|
|
317
|
+
*/
|
|
318
|
+
renderEmpty?: (ctx: {
|
|
319
|
+
scope: ParsedRef | null;
|
|
320
|
+
}) => ReactNode;
|
|
321
|
+
/**
|
|
322
|
+
* Whether each scope holds at most one record (`singleton`, default) or
|
|
323
|
+
* many (`collection`, e.g. FAQs / recipes / SOPs). In collection mode the
|
|
324
|
+
* shell treats scopes as folders containing items, exposes a "+ New" action,
|
|
325
|
+
* and the `RecordEditor` works on individual items via `itemId`-suffixed refs.
|
|
326
|
+
*/
|
|
327
|
+
cardinality?: RecordCardinality;
|
|
328
|
+
/** Display name for an item in collection mode (defaults to `'item'`). */
|
|
329
|
+
itemNoun?: string;
|
|
330
|
+
/**
|
|
331
|
+
* Generate a per-item id when "+ New" is clicked. Defaults to a ULID-like
|
|
332
|
+
* timestamp+random string.
|
|
333
|
+
*/
|
|
334
|
+
generateItemId?: () => string;
|
|
335
|
+
i18n?: Partial<RecordsAdminI18n>;
|
|
336
|
+
onTelemetry?: (event: TelemetryEvent) => void;
|
|
337
|
+
className?: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
declare function RecordsAdminShell<TData = unknown>(props: RecordsAdminShellProps<TData>): react_jsx_runtime.JSX.Element;
|
|
341
|
+
|
|
342
|
+
interface RecordBrowserProps {
|
|
343
|
+
scopes: ScopeKind[];
|
|
344
|
+
activeScope: ScopeKind;
|
|
345
|
+
onActiveScopeChange: (s: ScopeKind) => void;
|
|
346
|
+
selectedRef?: string;
|
|
347
|
+
onSelectRef: (item: RecordSummary) => void;
|
|
348
|
+
items: RecordSummary[];
|
|
349
|
+
counts: {
|
|
350
|
+
all: number;
|
|
351
|
+
configured: number;
|
|
352
|
+
partial: number;
|
|
353
|
+
empty: number;
|
|
354
|
+
};
|
|
355
|
+
isLoading: boolean;
|
|
356
|
+
error: Error | null;
|
|
357
|
+
filter: 'all' | RecordStatus;
|
|
358
|
+
onFilterChange: (v: 'all' | RecordStatus) => void;
|
|
359
|
+
search: string;
|
|
360
|
+
onSearchChange: (v: string) => void;
|
|
361
|
+
hasNextPage?: boolean;
|
|
362
|
+
isFetchingNextPage?: boolean;
|
|
363
|
+
onLoadMore?: () => void;
|
|
364
|
+
scopesLoading?: boolean;
|
|
365
|
+
i18n: RecordsAdminI18n;
|
|
366
|
+
}
|
|
367
|
+
declare const RecordBrowser: ({ scopes, activeScope, onActiveScopeChange, selectedRef, onSelectRef, items, counts, isLoading, error, filter, onFilterChange, search, onSearchChange, hasNextPage, isFetchingNextPage, onLoadMore, scopesLoading, i18n, }: RecordBrowserProps) => react_jsx_runtime.JSX.Element;
|
|
368
|
+
|
|
369
|
+
interface Props$9 {
|
|
370
|
+
scopes: ScopeKind[];
|
|
371
|
+
active: ScopeKind;
|
|
372
|
+
onChange: (s: ScopeKind) => void;
|
|
373
|
+
/** When true, tabs render disabled (used while collection flags are loading). */
|
|
374
|
+
loading?: boolean;
|
|
375
|
+
}
|
|
376
|
+
declare const ScopeTabs: ({ scopes, active, onChange, loading }: Props$9) => react_jsx_runtime.JSX.Element;
|
|
377
|
+
|
|
378
|
+
interface Props$8 {
|
|
379
|
+
source?: RecordSource;
|
|
380
|
+
status?: RecordStatus;
|
|
381
|
+
className?: string;
|
|
382
|
+
}
|
|
383
|
+
/** Emerald = own data, amber = inherited, muted = empty */
|
|
384
|
+
declare const StatusDot: ({ source, status, className }: Props$8) => react_jsx_runtime.JSX.Element;
|
|
385
|
+
|
|
386
|
+
interface Props$7 {
|
|
387
|
+
value: 'all' | RecordStatus;
|
|
388
|
+
onChange: (v: 'all' | RecordStatus) => void;
|
|
389
|
+
counts: {
|
|
390
|
+
all: number;
|
|
391
|
+
configured: number;
|
|
392
|
+
partial: number;
|
|
393
|
+
empty: number;
|
|
394
|
+
};
|
|
395
|
+
i18n: RecordsAdminI18n;
|
|
396
|
+
}
|
|
397
|
+
declare const StatusFilterPills: ({ value, onChange, counts, i18n }: Props$7) => react_jsx_runtime.JSX.Element;
|
|
398
|
+
|
|
399
|
+
interface Props$6 {
|
|
400
|
+
items: RecordSummary[];
|
|
401
|
+
selectedRef?: string;
|
|
402
|
+
onSelect: (item: RecordSummary) => void;
|
|
403
|
+
/** When set, the matching row gets a small "unsaved" indicator. */
|
|
404
|
+
dirtyRef?: string;
|
|
405
|
+
/** Layout. Defaults to `list` for full back-compat. */
|
|
406
|
+
presentation?: RecordPresentation;
|
|
407
|
+
/** Optional override for `list` / `compact` rows. */
|
|
408
|
+
renderListRow?: (record: RecordSummary, ctx: RecordSlotContext) => ReactNode;
|
|
409
|
+
/** Optional override for `grid` / `gallery` cards. */
|
|
410
|
+
renderCard?: (record: RecordSummary, ctx: RecordSlotContext) => ReactNode;
|
|
411
|
+
}
|
|
412
|
+
declare const RecordList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, }: Props$6) => react_jsx_runtime.JSX.Element;
|
|
413
|
+
declare const ProductList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, }: Props$6) => react_jsx_runtime.JSX.Element;
|
|
414
|
+
declare const FacetList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, }: Props$6) => react_jsx_runtime.JSX.Element;
|
|
415
|
+
declare const VariantList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, }: Props$6) => react_jsx_runtime.JSX.Element;
|
|
416
|
+
declare const BatchList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, }: Props$6) => react_jsx_runtime.JSX.Element;
|
|
417
|
+
|
|
418
|
+
interface DefaultRecordRowProps {
|
|
419
|
+
record: RecordSummary;
|
|
420
|
+
ctx: RecordSlotContext;
|
|
421
|
+
/** Drop the status dot + subtitle (used by the `compact` preset). */
|
|
422
|
+
compact?: boolean;
|
|
423
|
+
}
|
|
424
|
+
declare const DefaultRecordRow: ({ record, ctx, compact }: DefaultRecordRowProps) => react_jsx_runtime.JSX.Element;
|
|
425
|
+
|
|
426
|
+
interface DefaultRecordCardProps {
|
|
427
|
+
record: RecordSummary;
|
|
428
|
+
ctx: RecordSlotContext;
|
|
429
|
+
/** `grid` → 1:1, `gallery` → 16:9 with denser meta. Default `grid`. */
|
|
430
|
+
variant?: 'grid' | 'gallery';
|
|
431
|
+
}
|
|
432
|
+
declare const DefaultRecordCard: ({ record, ctx, variant }: DefaultRecordCardProps) => react_jsx_runtime.JSX.Element;
|
|
433
|
+
|
|
434
|
+
interface PresentationSwitcherProps {
|
|
435
|
+
options: RecordPresentation[];
|
|
436
|
+
value: RecordPresentation;
|
|
437
|
+
onChange: (next: RecordPresentation) => void;
|
|
438
|
+
i18n: RecordsAdminI18n;
|
|
439
|
+
}
|
|
440
|
+
declare const PresentationSwitcher: ({ options, value, onChange, i18n }: PresentationSwitcherProps) => react_jsx_runtime.JSX.Element | null;
|
|
441
|
+
|
|
442
|
+
declare const EmptyState: ({ title, body, action }: {
|
|
443
|
+
title: string;
|
|
444
|
+
body?: ReactNode;
|
|
445
|
+
action?: ReactNode;
|
|
446
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
447
|
+
declare const LoadingState: () => react_jsx_runtime.JSX.Element;
|
|
448
|
+
declare const ErrorState: ({ error }: {
|
|
449
|
+
error: Error;
|
|
450
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
451
|
+
|
|
452
|
+
interface Props$5 {
|
|
453
|
+
i18n: RecordsAdminI18n;
|
|
454
|
+
onApplyToMany?: () => void;
|
|
455
|
+
onCopyFrom?: () => void;
|
|
456
|
+
onClearMany?: () => void;
|
|
457
|
+
onImportCsv?: () => void;
|
|
458
|
+
onExportCsv?: () => void;
|
|
459
|
+
}
|
|
460
|
+
declare const BulkActionsMenu: ({ i18n, onApplyToMany, onCopyFrom, onClearMany, onImportCsv, onExportCsv, }: Props$5) => react_jsx_runtime.JSX.Element | null;
|
|
461
|
+
|
|
462
|
+
interface Props$4<T> {
|
|
463
|
+
ctx: EditorContext<T>;
|
|
464
|
+
i18n: RecordsAdminI18n;
|
|
465
|
+
children: ReactNode;
|
|
466
|
+
preview?: ReactNode;
|
|
467
|
+
bulkActions?: React.ComponentProps<typeof BulkActionsMenu>;
|
|
468
|
+
/** Extra slot rendered in the footer between the danger actions and Save. */
|
|
469
|
+
footerExtra?: ReactNode;
|
|
470
|
+
/** Optional pre-delete hook (see DeleteButton). */
|
|
471
|
+
onBeforeDelete?: () => boolean | Promise<boolean>;
|
|
472
|
+
}
|
|
473
|
+
declare function RecordEditor<T>({ ctx, i18n, children, preview, bulkActions, footerExtra, onBeforeDelete, }: Props$4<T>): react_jsx_runtime.JSX.Element;
|
|
474
|
+
|
|
475
|
+
interface InheritanceCtx {
|
|
476
|
+
parentValue?: Record<string, unknown> | null;
|
|
477
|
+
ownValue?: Record<string, unknown> | null;
|
|
478
|
+
onResetField?: (field: string) => void;
|
|
479
|
+
}
|
|
480
|
+
declare const InheritanceProvider: ({ parentValue, ownValue, onResetField, children, }: InheritanceCtx & {
|
|
481
|
+
children: ReactNode;
|
|
482
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
483
|
+
interface MarkerProps {
|
|
484
|
+
field: string;
|
|
485
|
+
inheritedValue?: unknown;
|
|
486
|
+
value?: unknown;
|
|
487
|
+
children: ReactNode;
|
|
488
|
+
}
|
|
489
|
+
declare const InheritanceMarker: ({ field, inheritedValue, value, children }: MarkerProps) => react_jsx_runtime.JSX.Element;
|
|
490
|
+
|
|
491
|
+
interface Props$3 {
|
|
492
|
+
children: ReactNode;
|
|
493
|
+
}
|
|
494
|
+
declare const ResolvedPreview: ({ children }: Props$3) => react_jsx_runtime.JSX.Element;
|
|
495
|
+
|
|
496
|
+
interface ProductChildItem {
|
|
497
|
+
/** Variant or batch id (the `<id>` part of `variant:<id>` / `batch:<id>`). */
|
|
498
|
+
id: string;
|
|
499
|
+
/** Display name. */
|
|
500
|
+
name: string;
|
|
501
|
+
/** Optional subtitle (sku, expiry, etc.). */
|
|
502
|
+
subtitle?: string;
|
|
503
|
+
}
|
|
504
|
+
interface UseProductChildrenArgs {
|
|
505
|
+
SL: SmartLinksSDK;
|
|
506
|
+
collectionId: string;
|
|
507
|
+
productId: string | undefined;
|
|
508
|
+
/** Which child kind to fetch. Hooks only fire when both productId and kind are set. */
|
|
509
|
+
kind: 'variant' | 'batch' | null;
|
|
510
|
+
enabled?: boolean;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Lazy children loader. When `kind` is `null` or `productId` is missing the
|
|
514
|
+
* underlying query is disabled, so opening the shell at collection level never
|
|
515
|
+
* touches `variant.list` / `batch.list`.
|
|
516
|
+
*/
|
|
517
|
+
declare const useProductChildren: (args: UseProductChildrenArgs) => {
|
|
518
|
+
items: ProductChildItem[];
|
|
519
|
+
isLoading: boolean;
|
|
520
|
+
error: Error | null;
|
|
521
|
+
refetch: () => Promise<void>;
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
type DrillTab = 'product' | 'variant' | 'batch';
|
|
525
|
+
interface Props$2 {
|
|
526
|
+
productLabel: string;
|
|
527
|
+
/** Which child types are available on the collection. */
|
|
528
|
+
showVariants: boolean;
|
|
529
|
+
showBatches: boolean;
|
|
530
|
+
active: DrillTab;
|
|
531
|
+
onChange: (tab: DrillTab) => void;
|
|
532
|
+
/** Currently selected child id (variant or batch), if any. */
|
|
533
|
+
selectedChildId?: string;
|
|
534
|
+
onSelectChild: (id: string) => void;
|
|
535
|
+
variants: ProductChildItem[];
|
|
536
|
+
batches: ProductChildItem[];
|
|
537
|
+
variantsLoading: boolean;
|
|
538
|
+
batchesLoading: boolean;
|
|
539
|
+
/** Editor body rendered to the right of the picker. */
|
|
540
|
+
children: ReactNode;
|
|
541
|
+
}
|
|
542
|
+
declare const ProductDrillDown: ({ productLabel, showVariants, showBatches, active, onChange, selectedChildId, onSelectChild, variants, batches, variantsLoading, batchesLoading, children, }: Props$2) => react_jsx_runtime.JSX.Element;
|
|
543
|
+
|
|
544
|
+
type PreviewMode = 'inline' | 'side' | 'tab' | 'drawer';
|
|
545
|
+
interface CommonProps {
|
|
546
|
+
/** App-rendered preview body. */
|
|
547
|
+
children: ReactNode;
|
|
548
|
+
/** Optional scope picker rendered in the chrome header. */
|
|
549
|
+
scopePicker?: ReactNode;
|
|
550
|
+
/** Header label (defaults to "Preview"). */
|
|
551
|
+
label?: string;
|
|
552
|
+
}
|
|
553
|
+
declare const InlinePreview: ({ children, scopePicker, label }: CommonProps) => react_jsx_runtime.JSX.Element;
|
|
554
|
+
declare const SidePreview: ({ children, scopePicker, label }: CommonProps) => react_jsx_runtime.JSX.Element;
|
|
555
|
+
interface TabbedPreviewProps {
|
|
556
|
+
editor: ReactNode;
|
|
557
|
+
preview: ReactNode;
|
|
558
|
+
scopePicker?: ReactNode;
|
|
559
|
+
/** Initial active tab (default `editor`). */
|
|
560
|
+
defaultTab?: 'editor' | 'preview';
|
|
561
|
+
i18n?: {
|
|
562
|
+
editor?: string;
|
|
563
|
+
preview?: string;
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
declare const TabbedPreview: ({ editor, preview, scopePicker, defaultTab, i18n, }: TabbedPreviewProps) => react_jsx_runtime.JSX.Element;
|
|
567
|
+
interface DrawerPreviewProps extends CommonProps {
|
|
568
|
+
open: boolean;
|
|
569
|
+
onClose: () => void;
|
|
570
|
+
/** Drawer width (default 420). */
|
|
571
|
+
width?: number;
|
|
572
|
+
}
|
|
573
|
+
declare const DrawerPreview: ({ open, onClose, children, scopePicker, label, width, }: DrawerPreviewProps) => react_jsx_runtime.JSX.Element | null;
|
|
574
|
+
declare const PreviewToggleButton: ({ onClick, label, }: {
|
|
575
|
+
onClick: () => void;
|
|
576
|
+
label?: string;
|
|
577
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
578
|
+
|
|
579
|
+
interface PreviewScopePickerProps {
|
|
580
|
+
SL: SmartLinksSDK;
|
|
581
|
+
collectionId: string;
|
|
582
|
+
/** The scope currently being edited — used as the "Same as edited" default. */
|
|
583
|
+
editingScope: ParsedRef;
|
|
584
|
+
/** The current preview scope. */
|
|
585
|
+
value: ParsedRef;
|
|
586
|
+
onChange: (next: ParsedRef) => void;
|
|
587
|
+
/** Are variants/batches available for drill-down. */
|
|
588
|
+
showVariants: boolean;
|
|
589
|
+
showBatches: boolean;
|
|
590
|
+
/** i18n strings (uses defaults if omitted). */
|
|
591
|
+
i18n?: {
|
|
592
|
+
previewAs?: string;
|
|
593
|
+
previewAsDefault?: string;
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
declare const PreviewScopePicker: ({ SL, collectionId, editingScope, value, onChange, showVariants, showBatches, i18n, }: PreviewScopePickerProps) => react_jsx_runtime.JSX.Element;
|
|
597
|
+
|
|
598
|
+
declare const ScopeBreadcrumb: ({ scope }: {
|
|
599
|
+
scope: ParsedRef;
|
|
600
|
+
}) => react_jsx_runtime.JSX.Element | null;
|
|
601
|
+
|
|
602
|
+
interface Props$1 {
|
|
603
|
+
title: string;
|
|
604
|
+
body: ReactNode;
|
|
605
|
+
onDismiss: () => void;
|
|
606
|
+
}
|
|
607
|
+
declare const IntroCard: ({ title, body, onDismiss }: Props$1) => react_jsx_runtime.JSX.Element;
|
|
608
|
+
|
|
609
|
+
interface Props {
|
|
610
|
+
label: string;
|
|
611
|
+
recordType: string;
|
|
612
|
+
introHidden: boolean;
|
|
613
|
+
onShowIntro?: () => void;
|
|
614
|
+
}
|
|
615
|
+
declare const UtilityRow: ({ label, recordType, introHidden, onShowIntro }: Props) => react_jsx_runtime.JSX.Element;
|
|
616
|
+
|
|
617
|
+
interface RecordsCtx {
|
|
618
|
+
SL: SmartLinksSDK;
|
|
619
|
+
collectionId: string;
|
|
620
|
+
appId: string;
|
|
621
|
+
recordType: string;
|
|
622
|
+
}
|
|
623
|
+
/** Shape of a single record write — uses the SDK's structured `scope`. */
|
|
624
|
+
interface RecordWrite<T = unknown> {
|
|
625
|
+
/** Logical key. The server canonicalises this from `scope` if omitted. */
|
|
626
|
+
ref?: string;
|
|
627
|
+
/** Structured scope; required for specificity / `match()` to work. */
|
|
628
|
+
scope: RecordScope;
|
|
629
|
+
data: T;
|
|
630
|
+
visibility?: 'public' | 'owner' | 'admin';
|
|
631
|
+
productId?: string;
|
|
632
|
+
status?: string;
|
|
633
|
+
startsAt?: string | null;
|
|
634
|
+
expiresAt?: string | null;
|
|
635
|
+
customId?: string;
|
|
636
|
+
sourceSystem?: string;
|
|
637
|
+
}
|
|
638
|
+
declare const listRecords: (ctx: RecordsCtx, params?: {
|
|
639
|
+
ref?: string;
|
|
640
|
+
refPrefix?: string;
|
|
641
|
+
q?: string;
|
|
642
|
+
limit?: number;
|
|
643
|
+
offset?: number;
|
|
644
|
+
sort?: string;
|
|
645
|
+
}) => Promise<{
|
|
646
|
+
data: AppRecord[];
|
|
647
|
+
total: number;
|
|
648
|
+
hasMore: boolean;
|
|
649
|
+
}>;
|
|
650
|
+
/** Look up a single record by its canonical ref. */
|
|
651
|
+
declare const getRecordByRef: (ctx: RecordsCtx, ref: string) => Promise<AppRecord | null>;
|
|
652
|
+
/**
|
|
653
|
+
* Atomic upsert via the SDK. Always sends `scope` so the server can compute
|
|
654
|
+
* `specificity` and derive `ref` deterministically.
|
|
655
|
+
*/
|
|
656
|
+
declare const upsertRecord: <T>(ctx: RecordsCtx, write: RecordWrite<T>) => Promise<{
|
|
657
|
+
record: AppRecord;
|
|
658
|
+
isCreate: boolean;
|
|
659
|
+
}>;
|
|
660
|
+
declare const deleteRecord: (ctx: RecordsCtx, ref: string) => Promise<boolean>;
|
|
661
|
+
/** Restore a soft-deleted record by ID (admin only). */
|
|
662
|
+
declare const restoreRecord: (ctx: RecordsCtx, recordId: string) => Promise<AppRecord>;
|
|
663
|
+
/**
|
|
664
|
+
* Server-side match — single round trip that returns every record whose
|
|
665
|
+
* `scope` is satisfied by `target`, ordered by descending `specificity`.
|
|
666
|
+
* Replaces the per-scope N+1 chain walk we used pre-1.9.
|
|
667
|
+
*/
|
|
668
|
+
declare const matchRecords: (ctx: RecordsCtx, target: RecordTarget, opts?: {
|
|
669
|
+
strategy?: "all" | "best";
|
|
670
|
+
at?: string;
|
|
671
|
+
includeScheduled?: boolean;
|
|
672
|
+
includeExpired?: boolean;
|
|
673
|
+
limit?: number;
|
|
674
|
+
}) => Promise<MatchResult>;
|
|
675
|
+
/**
|
|
676
|
+
* Server-side bulk upsert. Up to 500 rows per call; any leftover is chunked.
|
|
677
|
+
* Each row is error-isolated by the server.
|
|
678
|
+
*/
|
|
679
|
+
declare const bulkUpsert: <T>(ctx: RecordsCtx, entries: Array<{
|
|
680
|
+
ref?: string;
|
|
681
|
+
scope: RecordScope;
|
|
682
|
+
data: T;
|
|
683
|
+
status?: string;
|
|
684
|
+
}>) => Promise<{
|
|
685
|
+
saved: number;
|
|
686
|
+
failed: number;
|
|
687
|
+
}>;
|
|
688
|
+
/**
|
|
689
|
+
* Server-side bulk delete. Accepts an explicit ref list (max 1000 per call,
|
|
690
|
+
* chunked here) or a scope anchor (single call).
|
|
691
|
+
*/
|
|
692
|
+
declare const bulkDelete: (ctx: RecordsCtx, input: {
|
|
693
|
+
refs: string[];
|
|
694
|
+
} | {
|
|
695
|
+
scope: Omit<RecordScope, "facets">;
|
|
696
|
+
}) => Promise<{
|
|
697
|
+
removed: number;
|
|
698
|
+
}>;
|
|
699
|
+
|
|
700
|
+
interface UseRecordListArgs {
|
|
701
|
+
ctx: RecordsCtx;
|
|
702
|
+
scopeKind: ScopeKind;
|
|
703
|
+
search?: string;
|
|
704
|
+
filter?: 'all' | RecordStatus;
|
|
705
|
+
classify?: (record: RecordSummary) => RecordStatus;
|
|
706
|
+
enabled?: boolean;
|
|
707
|
+
scaffolder?: (existing: RecordSummary[]) => Promise<RecordSummary[]> | RecordSummary[];
|
|
708
|
+
/**
|
|
709
|
+
* Constrain the list to a subtree (e.g. a specific product when the host URL
|
|
710
|
+
* pins one). Records whose ParsedRef does not match every set field are dropped.
|
|
711
|
+
*/
|
|
712
|
+
contextScope?: {
|
|
713
|
+
productId?: string;
|
|
714
|
+
variantId?: string;
|
|
715
|
+
batchId?: string;
|
|
716
|
+
};
|
|
717
|
+
/** Page size requested from the SDK (default 100). */
|
|
718
|
+
pageSize?: number;
|
|
719
|
+
}
|
|
720
|
+
declare const useRecordList: (args: UseRecordListArgs) => {
|
|
721
|
+
items: RecordSummary<unknown>[];
|
|
722
|
+
total: number;
|
|
723
|
+
counts: {
|
|
724
|
+
all: number;
|
|
725
|
+
configured: number;
|
|
726
|
+
partial: number;
|
|
727
|
+
empty: number;
|
|
728
|
+
};
|
|
729
|
+
isLoading: boolean;
|
|
730
|
+
error: Error | null;
|
|
731
|
+
refetch: () => void;
|
|
732
|
+
hasNextPage: boolean;
|
|
733
|
+
isFetchingNextPage: boolean;
|
|
734
|
+
fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<{
|
|
735
|
+
data: AppRecord[];
|
|
736
|
+
total: number;
|
|
737
|
+
hasMore: boolean;
|
|
738
|
+
nextOffset: number;
|
|
739
|
+
}, unknown>, Error>>;
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
interface UseRecordEditorArgs<T> {
|
|
743
|
+
ctx: RecordsCtx;
|
|
744
|
+
scope: ParsedRef;
|
|
745
|
+
resolved: ResolvedRecord<T>;
|
|
746
|
+
defaultData?: () => T;
|
|
747
|
+
onSaved?: () => void;
|
|
748
|
+
onDeleted?: () => void;
|
|
749
|
+
/** Fired after a save fails and local state has been rolled back. */
|
|
750
|
+
onSaveError?: (err: unknown) => void;
|
|
751
|
+
/**
|
|
752
|
+
* Controls how the editor reseeds its local value when the resolved
|
|
753
|
+
* record changes. Default `'always'` matches legacy behaviour.
|
|
754
|
+
* `'preserve-dirty'` keeps unsaved local edits across resolve refreshes
|
|
755
|
+
* (used by the shell when `dirtyStrategy === 'keep'`).
|
|
756
|
+
*/
|
|
757
|
+
reseed?: 'always' | 'preserve-dirty';
|
|
758
|
+
}
|
|
759
|
+
declare function useRecordEditor<T>(args: UseRecordEditorArgs<T>): EditorContext<T>;
|
|
760
|
+
|
|
761
|
+
interface UseResolvedRecordArgs {
|
|
762
|
+
SL: SmartLinksSDK;
|
|
763
|
+
appId: string;
|
|
764
|
+
recordType: string;
|
|
765
|
+
collectionId: string;
|
|
766
|
+
productId?: string;
|
|
767
|
+
variantId?: string;
|
|
768
|
+
batchId?: string;
|
|
769
|
+
facetId?: string;
|
|
770
|
+
facetValue?: string;
|
|
771
|
+
proofId?: string;
|
|
772
|
+
supportedScopes?: ScopeKind[];
|
|
773
|
+
enabled?: boolean;
|
|
774
|
+
withParent?: boolean;
|
|
775
|
+
}
|
|
776
|
+
declare function useResolvedRecord<T = unknown>(args: UseResolvedRecordArgs): {
|
|
777
|
+
isLoading: boolean;
|
|
778
|
+
error: Error | null;
|
|
779
|
+
data: T | null;
|
|
780
|
+
source: RecordSource;
|
|
781
|
+
sourceRef?: string;
|
|
782
|
+
parentValue?: T | null | undefined;
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
interface UseScopeProbeArgs {
|
|
786
|
+
SL: SmartLinksSDK;
|
|
787
|
+
collectionId: string;
|
|
788
|
+
/** Force admin endpoint when fetching the collection (default true). */
|
|
789
|
+
admin?: boolean;
|
|
790
|
+
enabled?: boolean;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Reports whether a collection has variants/batches enabled.
|
|
794
|
+
*
|
|
795
|
+
* Uses the canonical `Collection.variants` / `Collection.batches` boolean flags
|
|
796
|
+
* from `SL.collection.get(collectionId)` (SDK ≥ 1.9). No more guessing by
|
|
797
|
+
* probing list endpoints.
|
|
798
|
+
*/
|
|
799
|
+
declare const useScopeProbe: ({ SL, collectionId, admin, enabled }: UseScopeProbeArgs) => {
|
|
800
|
+
hasVariants: boolean;
|
|
801
|
+
hasBatches: boolean;
|
|
802
|
+
isLoading: boolean;
|
|
803
|
+
error: Error | null;
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
declare const useIntroDismissed: (SL: SmartLinksSDK, collectionId: string, appId: string, recordType: string) => {
|
|
807
|
+
dismissed: boolean;
|
|
808
|
+
dismiss: () => Promise<void>;
|
|
809
|
+
undismiss: () => void;
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
interface ProductBrowseItem {
|
|
813
|
+
/** Product id (used in refs as `product:<id>`). */
|
|
814
|
+
id: string;
|
|
815
|
+
/** Display name. */
|
|
816
|
+
name: string;
|
|
817
|
+
/** Optional subtitle. */
|
|
818
|
+
sku?: string | null;
|
|
819
|
+
/** Optional ordering hint. */
|
|
820
|
+
sortOrder?: number | null;
|
|
821
|
+
}
|
|
822
|
+
interface UseProductBrowseArgs {
|
|
823
|
+
SL: SmartLinksSDK;
|
|
824
|
+
collectionId: string;
|
|
825
|
+
search?: string;
|
|
826
|
+
/** Soft cap per page (default 50). */
|
|
827
|
+
pageSize?: number;
|
|
828
|
+
enabled?: boolean;
|
|
829
|
+
/** Override admin flag if needed (default true). */
|
|
830
|
+
admin?: boolean;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Cursor-paginated product list backed by `SL.product.query`. When the SDK
|
|
834
|
+
* returns a `nextCursor`, we use it; otherwise we fall back to offset-based
|
|
835
|
+
* paging so the hook works against older SDKs or in-memory adapters too.
|
|
836
|
+
*/
|
|
837
|
+
declare const useProductBrowse: (args: UseProductBrowseArgs) => {
|
|
838
|
+
items: any[];
|
|
839
|
+
total: any;
|
|
840
|
+
isLoading: boolean;
|
|
841
|
+
error: Error | null;
|
|
842
|
+
hasNextPage: boolean;
|
|
843
|
+
isFetchingNextPage: boolean;
|
|
844
|
+
fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<{
|
|
845
|
+
items: any;
|
|
846
|
+
nextOffset: any;
|
|
847
|
+
nextCursor: any;
|
|
848
|
+
hasMore: any;
|
|
849
|
+
total: any;
|
|
850
|
+
}, unknown>, Error>>;
|
|
851
|
+
refetch: () => Promise<void>;
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
interface CollectedRecord<T = unknown> {
|
|
855
|
+
/** The ref this record was loaded from. */
|
|
856
|
+
ref: string;
|
|
857
|
+
/** Parsed scope of the ref (gives you `productId`, `facetId`, etc.). */
|
|
858
|
+
scope: ParsedRef;
|
|
859
|
+
/** Domain payload. */
|
|
860
|
+
data: T;
|
|
861
|
+
/** Index in the resolution chain (0 = most-specific). */
|
|
862
|
+
depth: number;
|
|
863
|
+
}
|
|
864
|
+
interface UseCollectedRecordsArgs {
|
|
865
|
+
SL: SmartLinksSDK;
|
|
866
|
+
appId: string;
|
|
867
|
+
recordType: string;
|
|
868
|
+
collectionId: string;
|
|
869
|
+
productId?: string;
|
|
870
|
+
variantId?: string;
|
|
871
|
+
batchId?: string;
|
|
872
|
+
facetId?: string;
|
|
873
|
+
facetValue?: string;
|
|
874
|
+
/** Scopes to walk. Defaults to all four. */
|
|
875
|
+
supportedScopes?: ScopeKind[];
|
|
876
|
+
/** Drop scopes that don't have a record (default true). */
|
|
877
|
+
filterEmpty?: boolean;
|
|
878
|
+
/**
|
|
879
|
+
* Ordering applied to the result. Defaults to specificity-descending
|
|
880
|
+
* (most-specific scope first), matching the previous behaviour and aligning
|
|
881
|
+
* with the future `match()` API.
|
|
882
|
+
*/
|
|
883
|
+
sort?: CollectedSort;
|
|
884
|
+
enabled?: boolean;
|
|
885
|
+
}
|
|
886
|
+
declare function useCollectedRecords<T = unknown>(args: UseCollectedRecordsArgs): {
|
|
887
|
+
items: CollectedRecord<T>[];
|
|
888
|
+
isLoading: boolean;
|
|
889
|
+
error: Error | null;
|
|
890
|
+
refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<CollectedRecord<T>[], Error>>;
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
type MergeStrategy = 'deep' | 'shallow';
|
|
894
|
+
interface MergedRecord<T = unknown> {
|
|
895
|
+
/** The merged result. */
|
|
896
|
+
data: T | null;
|
|
897
|
+
/**
|
|
898
|
+
* For each top-level key, the ref of the record that contributed its
|
|
899
|
+
* winning value. Useful for "Inherited from facet:gluten-free" hints.
|
|
900
|
+
*/
|
|
901
|
+
provenance: Record<string, string>;
|
|
902
|
+
/** Refs that contributed something to the merge, deepest-first. */
|
|
903
|
+
layers: string[];
|
|
904
|
+
}
|
|
905
|
+
interface UseMergedRecordArgs<T> {
|
|
906
|
+
SL: SmartLinksSDK;
|
|
907
|
+
appId: string;
|
|
908
|
+
recordType: string;
|
|
909
|
+
collectionId: string;
|
|
910
|
+
productId?: string;
|
|
911
|
+
variantId?: string;
|
|
912
|
+
batchId?: string;
|
|
913
|
+
facetId?: string;
|
|
914
|
+
facetValue?: string;
|
|
915
|
+
supportedScopes?: ScopeKind[];
|
|
916
|
+
/**
|
|
917
|
+
* Merge strategy. `deep` recursively merges plain objects; arrays are
|
|
918
|
+
* replaced wholesale. `shallow` does Object.assign at the top level.
|
|
919
|
+
* Default `deep`.
|
|
920
|
+
*/
|
|
921
|
+
strategy?: MergeStrategy;
|
|
922
|
+
/** Custom merge fn. Receives `(parent, child)` and returns the winner. */
|
|
923
|
+
merge?: (parent: T | null, child: T) => T;
|
|
924
|
+
enabled?: boolean;
|
|
925
|
+
}
|
|
926
|
+
declare function useMergedRecord<T = unknown>(args: UseMergedRecordArgs<T>): {
|
|
927
|
+
isLoading: boolean;
|
|
928
|
+
error: Error | null;
|
|
929
|
+
refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<MergedRecord<T>, Error>>;
|
|
930
|
+
/** The merged result. */
|
|
931
|
+
data: T | null;
|
|
932
|
+
/**
|
|
933
|
+
* For each top-level key, the ref of the record that contributed its
|
|
934
|
+
* winning value. Useful for "Inherited from facet:gluten-free" hints.
|
|
935
|
+
*/
|
|
936
|
+
provenance: Record<string, string>;
|
|
937
|
+
/** Refs that contributed something to the merge, deepest-first. */
|
|
938
|
+
layers: string[];
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
interface UseUnsavedGuardArgs {
|
|
942
|
+
/** Whether there are unsaved edits right now. */
|
|
943
|
+
isDirty: boolean;
|
|
944
|
+
/**
|
|
945
|
+
* Optional human-friendly label (e.g. recordType). Posted to the parent
|
|
946
|
+
* frame so the host can describe what's unsaved.
|
|
947
|
+
*/
|
|
948
|
+
label?: string;
|
|
949
|
+
/**
|
|
950
|
+
* Optional async confirm impl. Defaults to `window.confirm`. Apps can pass
|
|
951
|
+
* a custom dialog (e.g. a shadcn AlertDialog promise wrapper).
|
|
952
|
+
*/
|
|
953
|
+
confirm?: (message: string) => boolean | Promise<boolean>;
|
|
954
|
+
/**
|
|
955
|
+
* Disable the browser-level `beforeunload` listener (e.g. when running
|
|
956
|
+
* inside a host that handles it already). Default false.
|
|
957
|
+
*/
|
|
958
|
+
disableBeforeUnload?: boolean;
|
|
959
|
+
/**
|
|
960
|
+
* Disable the parent-frame `postMessage` notification. Default false.
|
|
961
|
+
*/
|
|
962
|
+
disableParentMessage?: boolean;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Tracks unsaved state and wires up the browser/iframe guards. Returns a
|
|
966
|
+
* `confirmDiscard` helper apps can `await` from their own navigation code.
|
|
967
|
+
*/
|
|
968
|
+
declare function useUnsavedGuard({ isDirty, label, confirm, disableBeforeUnload, disableParentMessage, }: UseUnsavedGuardArgs): {
|
|
969
|
+
confirmDiscard: (message?: string) => Promise<boolean>;
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
type DirtyStrategy = 'prompt' | 'autosave' | 'keep';
|
|
973
|
+
interface UseDirtyNavigationArgs {
|
|
974
|
+
strategy: DirtyStrategy;
|
|
975
|
+
isDirty: boolean;
|
|
976
|
+
/** Persist current edits. Resolves when the SDK call completes. */
|
|
977
|
+
save: () => void | Promise<void>;
|
|
978
|
+
/** Discard local edits to the saved snapshot. */
|
|
979
|
+
reset: () => void;
|
|
980
|
+
/**
|
|
981
|
+
* Confirm impl: returns `'save' | 'discard' | 'cancel'`. Defaults to a
|
|
982
|
+
* `window.confirm` fallback (Save = OK, Discard = Cancel, no Cancel option).
|
|
983
|
+
*/
|
|
984
|
+
confirm?: (i18n?: NavConfirmI18n) => Promise<'save' | 'discard' | 'cancel'>;
|
|
985
|
+
i18n?: NavConfirmI18n;
|
|
986
|
+
}
|
|
987
|
+
interface NavConfirmI18n {
|
|
988
|
+
title?: string;
|
|
989
|
+
body?: string;
|
|
990
|
+
save?: string;
|
|
991
|
+
discard?: string;
|
|
992
|
+
cancel?: string;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Returns `runWithGuard(action)` — call it instead of executing the
|
|
996
|
+
* navigation directly. The guard handles dirty-state policy and only calls
|
|
997
|
+
* `action()` when it's safe to proceed.
|
|
998
|
+
*/
|
|
999
|
+
declare const useDirtyNavigation: ({ strategy, isDirty, save, reset, confirm, i18n, }: UseDirtyNavigationArgs) => {
|
|
1000
|
+
runWithGuard: (action: () => void) => Promise<boolean>;
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
declare function usePresentationPref(args: {
|
|
1004
|
+
appId: string;
|
|
1005
|
+
recordType: string;
|
|
1006
|
+
options: RecordPresentation[];
|
|
1007
|
+
defaultValue: RecordPresentation;
|
|
1008
|
+
}): [RecordPresentation, (next: RecordPresentation) => void];
|
|
1009
|
+
|
|
1010
|
+
interface DeleteButtonProps {
|
|
1011
|
+
/** Performs the delete. Should resolve when the SDK call completes. */
|
|
1012
|
+
onConfirm: () => void | Promise<void>;
|
|
1013
|
+
/**
|
|
1014
|
+
* Optional async pre-check — return false to abort BEFORE the inline
|
|
1015
|
+
* confirm flips. Lets apps run their own modal for high-stakes deletes.
|
|
1016
|
+
*/
|
|
1017
|
+
onBeforeDelete?: () => boolean | Promise<boolean>;
|
|
1018
|
+
/** Default label (idle state). */
|
|
1019
|
+
label?: string;
|
|
1020
|
+
/** Confirm-state label. */
|
|
1021
|
+
confirmLabel?: string;
|
|
1022
|
+
/** Auto-revert timeout in ms (default 3000). */
|
|
1023
|
+
revertMs?: number;
|
|
1024
|
+
disabled?: boolean;
|
|
1025
|
+
}
|
|
1026
|
+
declare const DeleteButton: ({ onConfirm, onBeforeDelete, label, confirmLabel, revertMs, disabled, }: DeleteButtonProps) => react_jsx_runtime.JSX.Element;
|
|
1027
|
+
|
|
1028
|
+
declare const parseRef: (raw: string) => ParsedRef;
|
|
1029
|
+
interface BuildRefArgs {
|
|
1030
|
+
collectionId?: string;
|
|
1031
|
+
productId?: string;
|
|
1032
|
+
facetId?: string;
|
|
1033
|
+
facetValue?: string;
|
|
1034
|
+
variantId?: string;
|
|
1035
|
+
batchId?: string;
|
|
1036
|
+
proofId?: string;
|
|
1037
|
+
}
|
|
1038
|
+
declare const buildRef: (a: BuildRefArgs) => string;
|
|
1039
|
+
/**
|
|
1040
|
+
* Build the resolution chain for a target ref, from most specific → least specific.
|
|
1041
|
+
* `supportedScopes` prunes scopes the app doesn't care about.
|
|
1042
|
+
*/
|
|
1043
|
+
declare const resolutionChain: (target: ParsedRef, supportedScopes: ScopeKind[]) => string[];
|
|
1044
|
+
|
|
1045
|
+
interface ResolveArgs {
|
|
1046
|
+
ctx: RecordsCtx;
|
|
1047
|
+
target: ParsedRef;
|
|
1048
|
+
/**
|
|
1049
|
+
* Ignored — kept for API compatibility. The server determines the
|
|
1050
|
+
* resolution chain from the structured scope.
|
|
1051
|
+
*/
|
|
1052
|
+
supportedScopes?: ScopeKind[];
|
|
1053
|
+
/** When true, also returns the parent value (next-best after self). */
|
|
1054
|
+
withParent?: boolean;
|
|
1055
|
+
admin?: boolean;
|
|
1056
|
+
}
|
|
1057
|
+
declare const resolveRecord: <T>(args: ResolveArgs) => Promise<ResolvedRecord<T>>;
|
|
1058
|
+
|
|
1059
|
+
declare const parsedRefToScope: (ref: ParsedRef) => RecordScope;
|
|
1060
|
+
declare const parsedRefToTarget: (ref: ParsedRef) => RecordTarget;
|
|
1061
|
+
/**
|
|
1062
|
+
* Compare a winning record's scope against the editing scope.
|
|
1063
|
+
* Returns true when they refer to the same node — used to decide
|
|
1064
|
+
* "self" vs "inherited" when consuming `match()` results.
|
|
1065
|
+
*/
|
|
1066
|
+
declare const scopesEqual: (a: RecordScope, b: RecordScope) => boolean;
|
|
1067
|
+
|
|
1068
|
+
interface ImportReport {
|
|
1069
|
+
total: number;
|
|
1070
|
+
saved: number;
|
|
1071
|
+
failed: number;
|
|
1072
|
+
errorRows: Array<{
|
|
1073
|
+
row: number;
|
|
1074
|
+
error: string;
|
|
1075
|
+
}>;
|
|
1076
|
+
/** Annotated CSV ready for download (input + `error` column). */
|
|
1077
|
+
annotatedCsv: string;
|
|
1078
|
+
}
|
|
1079
|
+
declare const exportCsv: <T>(records: RecordSummary<T>[], schema: CsvSchema<T>) => Blob;
|
|
1080
|
+
declare const importCsv: <T>(file: File, schema: CsvSchema<T>, ctx: RecordsCtx) => Promise<ImportReport>;
|
|
1081
|
+
declare const downloadBlob: (blob: Blob, filename: string) => void;
|
|
1082
|
+
|
|
1083
|
+
export { ALL_PRESENTATIONS, BatchList, BulkActionsMenu, type CollectedRecord, type CollectedSort, type CsvSchema, type CsvSchemaColumn, DEFAULT_I18N, DefaultRecordCard, DefaultRecordRow, DeleteButton, type DirtyStrategy, DrawerPreview, type EditorContext, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, LoadingState, type MergeStrategy, type MergedRecord, type NavConfirmI18n, type ParsedRef, PresentationSwitcher, type PreviewMode, PreviewScopePicker, PreviewToggleButton, type ProductBrowseItem, type ProductChildItem, ProductDrillDown, ProductList, type RecordBadge, RecordBrowser, type RecordCardinality, RecordEditor, RecordList, type RecordPresentation, type RecordSlotContext, type RecordSource, type RecordStatus, type RecordSummary, type RecordsAdminI18n, RecordsAdminShell, type RecordsAdminShellProps, ResolvedPreview, type ResolvedRecord, ScopeBreadcrumb, type ScopeKind, ScopeTabs, SidePreview, type SmartLinksSDK, StatusDot, StatusFilterPills, TabbedPreview, type TelemetryEvent, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, parseRef, parsedRefToScope, parsedRefToTarget, resolutionChain, resolveRecord, restoreRecord, scopesEqual, upsertRecord, useCollectedRecords, useDirtyNavigation, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordEditor, useRecordList, useResolvedRecord, useScopeProbe, useUnsavedGuard };
|