@vertebr/react 0.18.1 → 0.20.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.
Files changed (67) hide show
  1. package/dist/components/admin/AdminContent.d.ts +5 -1
  2. package/dist/components/admin/AdminContent.d.ts.map +1 -1
  3. package/dist/components/admin/AdminContent.js +75 -50
  4. package/dist/components/admin/AdminSyncPage.d.ts.map +1 -1
  5. package/dist/components/admin/AdminSyncPage.js +202 -3
  6. package/dist/components/admin/AuditBoard.d.ts +6 -2
  7. package/dist/components/admin/AuditBoard.d.ts.map +1 -1
  8. package/dist/components/admin/AuditBoard.js +145 -3
  9. package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
  10. package/dist/components/admin/VertebrAdmin.js +14 -3
  11. package/dist/components/config/EnumInput.d.ts.map +1 -1
  12. package/dist/components/config/EnumInput.js +9 -2
  13. package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
  14. package/dist/components/config/form/ConfigForm.js +14 -2
  15. package/dist/components/dialog/SelectionTableDialog.d.ts +79 -0
  16. package/dist/components/dialog/SelectionTableDialog.d.ts.map +1 -0
  17. package/dist/components/dialog/SelectionTableDialog.js +177 -0
  18. package/dist/components/emails/EmailsActivityPanel.d.ts.map +1 -1
  19. package/dist/components/emails/EmailsActivityPanel.js +80 -2
  20. package/dist/components/metrics/MetricsBoard.d.ts.map +1 -1
  21. package/dist/components/metrics/MetricsBoard.js +7 -1
  22. package/dist/components/resources/ColumnVisibilityMenu.d.ts +4 -0
  23. package/dist/components/resources/ColumnVisibilityMenu.d.ts.map +1 -1
  24. package/dist/components/resources/ColumnVisibilityMenu.js +14 -7
  25. package/dist/components/resources/ResourceTable.d.ts +7 -1
  26. package/dist/components/resources/ResourceTable.d.ts.map +1 -1
  27. package/dist/components/resources/ResourceTable.js +84 -64
  28. package/dist/components/resources/SortableHeader.d.ts +9 -0
  29. package/dist/components/resources/SortableHeader.d.ts.map +1 -0
  30. package/dist/components/resources/SortableHeader.js +9 -0
  31. package/dist/components/table/TableFilters.d.ts +20 -0
  32. package/dist/components/table/TableFilters.d.ts.map +1 -0
  33. package/dist/components/table/TableFilters.js +55 -0
  34. package/dist/components/table/TableSearch.d.ts +11 -0
  35. package/dist/components/table/TableSearch.d.ts.map +1 -0
  36. package/dist/components/table/TableSearch.js +23 -0
  37. package/dist/components/table/TableSkeleton.d.ts +16 -0
  38. package/dist/components/table/TableSkeleton.d.ts.map +1 -0
  39. package/dist/components/table/TableSkeleton.js +29 -0
  40. package/dist/components/table/TableWrap.d.ts +23 -0
  41. package/dist/components/table/TableWrap.d.ts.map +1 -0
  42. package/dist/components/table/TableWrap.js +35 -0
  43. package/dist/components/table/filterModel.d.ts +35 -0
  44. package/dist/components/table/filterModel.d.ts.map +1 -0
  45. package/dist/components/table/filterModel.js +150 -0
  46. package/dist/components/table/index.d.ts +13 -0
  47. package/dist/components/table/index.d.ts.map +1 -0
  48. package/dist/components/table/index.js +6 -0
  49. package/dist/components/table/useTableOverflow.d.ts +17 -0
  50. package/dist/components/table/useTableOverflow.d.ts.map +1 -0
  51. package/dist/components/table/useTableOverflow.js +54 -0
  52. package/dist/index.d.ts +4 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +3 -0
  55. package/dist/lib/sort.d.ts +10 -0
  56. package/dist/lib/sort.d.ts.map +1 -0
  57. package/dist/lib/sort.js +59 -0
  58. package/dist/styles/audit.css +11 -1
  59. package/dist/styles/columns.css +1 -84
  60. package/dist/styles/index.css +1 -0
  61. package/dist/styles/metrics.css +27 -4
  62. package/dist/styles/panel.css +143 -0
  63. package/dist/styles/select.css +16 -0
  64. package/dist/styles/selection-dialog.css +170 -0
  65. package/dist/styles/sync.css +21 -1
  66. package/dist/styles/table.css +115 -2
  67. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import type { CatalogResponse, ConfigResponse, EmailsBoardResponse, FontLibraryResponse, GalleryResponse, MetricsResponse, ResourceListResponse, VertebrClient } from "@vertebr/client";
2
2
  import type { MetricsRangeId } from "../../lib/metricsRanges";
3
+ import type { TableSortState } from "../../lib/sort";
3
4
  import type { Selection } from "./types";
4
5
  export type AdminContentProps = {
5
6
  client: VertebrClient;
@@ -24,8 +25,11 @@ export type AdminContentProps = {
24
25
  emailsLoading: boolean;
25
26
  onRefreshEmails: () => Promise<void>;
26
27
  resourceData: ResourceListResponse | null;
28
+ resourceLoading?: boolean;
27
29
  resourceSearch?: string;
28
30
  onResourceSearchChange?: (q: string) => void;
31
+ resourceSort?: TableSortState | null;
32
+ onResourceSortChange?: (sort: TableSortState) => void;
29
33
  onRefreshResource: () => Promise<void>;
30
34
  onRefreshConfig?: () => Promise<void>;
31
35
  configData: ConfigResponse | null;
@@ -45,5 +49,5 @@ export type AdminContentProps = {
45
49
  onRefreshFonts?: () => Promise<void>;
46
50
  };
47
51
  };
48
- export declare function AdminContent({ client, catalog, selection, onSelectionChange, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, resourceSearch, onResourceSearchChange, onRefreshResource, onRefreshConfig, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }: AdminContentProps): import("react").JSX.Element;
52
+ export declare function AdminContent({ client, catalog, selection, onSelectionChange, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, resourceLoading, resourceSearch, onResourceSearchChange, resourceSort, onResourceSortChange, onRefreshResource, onRefreshConfig, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }: AdminContentProps): import("react").JSX.Element;
49
53
  //# sourceMappingURL=AdminContent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminContent.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminContent.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AdminContent.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminContent.tsx"],"names":[],"mappings":"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"}
@@ -57,8 +57,9 @@ function ConfigEditor({ client, catalog, configData, isPageSelection, previewPat
57
57
  };
58
58
  }, [client, configData.id, relatedKey, relatedAddons]);
59
59
  const onChange = useCallback((value) => {
60
- setDraft(value);
61
- }, []);
60
+ // Merge — react-hook-form watch can omit unchanged / empty keys.
61
+ setDraft((prev) => ({ ...(prev ?? configData.value), ...value }));
62
+ }, [configData.value]);
62
63
  const form = (_jsx(ConfigForm, { schema: configData.schema, value: configData.value, client: client, canWrite: canWrite, revisionsEnabled: showRevisions, canPublish: canPublish && Boolean(configData.draftRevisionId), onPublish: canPublish && configData.draftRevisionId
63
64
  ? async () => {
64
65
  const published = await client.publishRevision(configData.id, configData.draftRevisionId);
@@ -76,7 +77,7 @@ function ConfigEditor({ client, catalog, configData, isPageSelection, previewPat
76
77
  return (_jsxs("button", { type: "button", className: "vertebr-page-related-addon", "data-enabled": enabled ? "true" : "false", onClick: () => onSelectAddon?.(addon.id), children: [_jsx("span", { children: addon.label }), _jsx("em", { children: enabled ? "on" : "off" })] }, addon.id));
77
78
  }) })] })) : null, showPreview && previewPath ? (_jsxs("div", { className: "vertebr-page-editor", children: [_jsx("div", { className: "vertebr-page-editor-form", children: form }), _jsx(PagePreviewPane, { previewPath: previewPath, controlId: configData.id, draft: draft ?? configData.value, relatedDrafts: Object.keys(relatedDrafts).length ? relatedDrafts : undefined })] })) : (form)] }));
78
79
  }
79
- export function AdminContent({ client, catalog, selection, onSelectionChange, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, resourceSearch, onResourceSearchChange, onRefreshResource, onRefreshConfig, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }) {
80
+ export function AdminContent({ client, catalog, selection, onSelectionChange, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, resourceLoading = false, resourceSearch, onResourceSearchChange, resourceSort = null, onResourceSortChange, onRefreshResource, onRefreshConfig, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }) {
80
81
  if (!catalog) {
81
82
  return (_jsx("div", { className: "vertebr-content", children: _jsx("section", { className: "vertebr-panel", children: _jsx("div", { className: "vertebr-empty", children: authRequired
82
83
  ? "Authenticated access required for this environment."
@@ -84,7 +85,7 @@ export function AdminContent({ client, catalog, selection, onSelectionChange, au
84
85
  ? "Loading…"
85
86
  : "Unable to load catalog." }) }) }));
86
87
  }
87
- return (_jsx("div", { className: "vertebr-content", children: _jsxs("section", { className: "vertebr-panel", children: [selection?.kind === "overview" ? (_jsx(MetricsBoard, { label: metricsData?.label ?? catalog.metrics?.label, windowLabel: metricsData?.windowLabel, compareLabel: metricsData?.compareLabel, metrics: metricsData?.metrics ?? [], charts: metricsData?.charts ?? [], range: metricsRange, onRangeChange: onMetricsRangeChange, loading: metricsLoading, onRefresh: () => void onRefreshMetrics() })) : null, selection?.kind === "audit" ? (_jsx(AuditBoard, { client: client, label: catalog.audit?.label })) : null, selection?.kind === "sync" ? (_jsx(AdminSyncPage, { client: client, catalog: catalog })) : null, selection?.kind === "gallery" && galleryData ? (_jsx(GalleryBoard, { label: galleryData.label, items: galleryData.items, accept: galleryData.accept, maxBytes: galleryData.maxBytes, canDelete: galleryData.canDelete, loading: galleryLoading, onRefresh: () => void onRefreshGallery(), onUpload: async (files) => {
88
+ return (_jsx("div", { className: "vertebr-content", children: _jsxs("section", { className: "vertebr-panel", children: [selection?.kind === "overview" ? (_jsx(MetricsBoard, { label: metricsData?.label ?? catalog.metrics?.label, windowLabel: metricsData?.windowLabel, compareLabel: metricsData?.compareLabel, metrics: metricsData?.metrics ?? [], charts: metricsData?.charts ?? [], range: metricsRange, onRangeChange: onMetricsRangeChange, loading: metricsLoading, onRefresh: () => void onRefreshMetrics() })) : null, selection?.kind === "audit" ? (_jsx(AuditBoard, { client: client, label: catalog.audit?.label, columns: catalog.audit?.columns })) : null, selection?.kind === "sync" ? (_jsx(AdminSyncPage, { client: client, catalog: catalog })) : null, selection?.kind === "gallery" && galleryData ? (_jsx(GalleryBoard, { label: galleryData.label, items: galleryData.items, accept: galleryData.accept, maxBytes: galleryData.maxBytes, canDelete: galleryData.canDelete, loading: galleryLoading, onRefresh: () => void onRefreshGallery(), onUpload: async (files) => {
88
89
  for (const file of files) {
89
90
  await client.uploadGalleryFile(file);
90
91
  }
@@ -151,52 +152,76 @@ export function AdminContent({ client, catalog, selection, onSelectionChange, au
151
152
  ? async (id) => {
152
153
  await client.deleteEmailLibraryTemplate(id);
153
154
  }
154
- : undefined })) : null, selection?.kind === "resource" && resourceData ? (_jsx(ResourcePanel, { schema: resourceData.schema, items: resourceData.items, label: resourceData.label, listQuery: resourceData.listQuery, total: resourceData.total, searchQuery: resourceSearch, onSearchChange: onResourceSearchChange, initialEditRowId: selection.rowId && selection.id === resourceData.id
155
- ? selection.rowId
156
- : undefined, onEditRowIdChange: (rowId) => {
157
- onSelectionChange?.({
158
- kind: "resource",
159
- id: resourceData.id,
160
- rowId: rowId ?? undefined,
161
- });
162
- }, permissions: resourceData.permissions, actions: resourceData.actions, collectionActions: resourceData.collectionActions, bulkActions: resourceData.bulkActions, uploads: resourceData.uploads, storageKey: resourceData.id, onUpdate: resourceData.permissions.update
163
- ? async (rowId, value) => {
164
- await client.updateResource(resourceData.id, rowId, value);
165
- await onRefreshResource();
166
- }
167
- : undefined, onCreate: resourceData.permissions.create
168
- ? async (value) => {
169
- await client.createResource(resourceData.id, value);
170
- await onRefreshResource();
171
- }
172
- : undefined, onDelete: resourceData.permissions.delete
173
- ? async (rowId) => {
174
- await client.deleteResource(resourceData.id, rowId);
175
- await onRefreshResource();
176
- }
177
- : undefined, onAction: async (rowId, actionId, input) => {
178
- await client.runResourceAction(resourceData.id, rowId, actionId, input);
179
- }, onActionDone: onRefreshResource, onPreviewMail: async (rowId, actionId, input) => {
180
- return client.previewResourceMail(resourceData.id, rowId, actionId, input);
181
- }, onCollectionAction: async (actionId, input) => {
182
- return client.runCollectionAction(resourceData.id, actionId, input);
183
- }, onBulkAction: resourceData.bulkActions
184
- ? async (actionId, rowIds, input) => {
185
- await client.runBulkAction(resourceData.id, actionId, {
186
- rowIds,
187
- input,
188
- });
189
- }
190
- : undefined, onUpload: resourceData.uploads
191
- ? async (field, files) => {
192
- const uploaded = [];
193
- for (const file of files) {
194
- const result = await client.uploadResourceFile(resourceData.id, field, file);
195
- uploaded.push({ url: result.url, name: result.name });
196
- }
197
- return uploaded;
198
- }
199
- : undefined, ...galleryPicker })) : null, selection?.kind === "addons" ? (_jsx(AddonOverview, { client: client, catalog: catalog, onSelectAddon: (id) => onSelectionChange?.({ kind: "addon", id }) })) : null, selection?.kind === "config" && configData ? (_jsx(ConfigEditor, { client: client, catalog: catalog, configData: configData, isPageSelection: isPageSelection, previewPath: catalog.configs.find((item) => item.id === configData.id)
155
+ : undefined })) : null, selection?.kind === "resource"
156
+ ? (() => {
157
+ const catalogResource = catalog.resources.find((item) => item.id === selection.id);
158
+ const ready = resourceData != null && resourceData.id === selection.id;
159
+ const schema = ready
160
+ ? resourceData.schema
161
+ : (catalogResource?.schema ?? {});
162
+ if (!ready && Object.keys(schema).length === 0)
163
+ return null;
164
+ return (_jsx(ResourcePanel, { schema: schema, items: ready ? resourceData.items : [], label: ready
165
+ ? resourceData.label
166
+ : (catalogResource?.label ?? selection.id), listQuery: ready
167
+ ? resourceData.listQuery
168
+ : catalogResource?.listQuery, total: ready ? resourceData.total : undefined, loading: resourceLoading || !ready, searchQuery: resourceSearch, onSearchChange: onResourceSearchChange, sort: resourceSort, onSortChange: onResourceSortChange, initialEditRowId: ready && selection.rowId ? selection.rowId : undefined, onEditRowIdChange: (rowId) => {
169
+ onSelectionChange?.({
170
+ kind: "resource",
171
+ id: selection.id,
172
+ rowId: rowId ?? undefined,
173
+ });
174
+ }, permissions: ready
175
+ ? resourceData.permissions
176
+ : { create: false, update: false, delete: false }, actions: ready ? resourceData.actions : {}, collectionActions: ready ? resourceData.collectionActions : {}, bulkActions: ready ? resourceData.bulkActions : {}, uploads: ready ? resourceData.uploads : undefined, storageKey: selection.id, onUpdate: ready && resourceData.permissions.update
177
+ ? async (rowId, value) => {
178
+ await client.updateResource(resourceData.id, rowId, value);
179
+ await onRefreshResource();
180
+ }
181
+ : undefined, onCreate: ready && resourceData.permissions.create
182
+ ? async (value) => {
183
+ await client.createResource(resourceData.id, value);
184
+ await onRefreshResource();
185
+ }
186
+ : undefined, onDelete: ready && resourceData.permissions.delete
187
+ ? async (rowId) => {
188
+ await client.deleteResource(resourceData.id, rowId);
189
+ await onRefreshResource();
190
+ }
191
+ : undefined, onAction: ready
192
+ ? async (rowId, actionId, input) => {
193
+ await client.runResourceAction(resourceData.id, rowId, actionId, input);
194
+ }
195
+ : undefined, onActionDone: onRefreshResource, onPreviewMail: ready
196
+ ? async (rowId, actionId, input) => {
197
+ return client.previewResourceMail(resourceData.id, rowId, actionId, input);
198
+ }
199
+ : undefined, onCollectionAction: ready
200
+ ? async (actionId, input) => {
201
+ return client.runCollectionAction(resourceData.id, actionId, input);
202
+ }
203
+ : undefined, onBulkAction: ready && resourceData.bulkActions
204
+ ? async (actionId, rowIds, input) => {
205
+ await client.runBulkAction(resourceData.id, actionId, {
206
+ rowIds,
207
+ input,
208
+ });
209
+ }
210
+ : undefined, onUpload: ready && resourceData.uploads
211
+ ? async (field, files) => {
212
+ const uploaded = [];
213
+ for (const file of files) {
214
+ const result = await client.uploadResourceFile(resourceData.id, field, file);
215
+ uploaded.push({
216
+ url: result.url,
217
+ name: result.name,
218
+ });
219
+ }
220
+ return uploaded;
221
+ }
222
+ : undefined, ...galleryPicker }));
223
+ })()
224
+ : null, selection?.kind === "addons" ? (_jsx(AddonOverview, { client: client, catalog: catalog, onSelectAddon: (id) => onSelectionChange?.({ kind: "addon", id }) })) : null, selection?.kind === "config" && configData ? (_jsx(ConfigEditor, { client: client, catalog: catalog, configData: configData, isPageSelection: isPageSelection, previewPath: catalog.configs.find((item) => item.id === configData.id)
200
225
  ?.previewPath, relatedAddons: catalog.configs.find((item) => item.id === configData.id)
201
226
  ?.relatedAddons, onConfigSaved: onConfigSaved, onRefreshConfig: onRefreshConfig, onSelectAddon: (id) => onSelectionChange?.({ kind: "addon", id }), galleryPicker: galleryPicker, fontLibraryPicker: fontLibraryPicker }, configData.id)) : null, selection?.kind === "addon" && configData ? ((() => {
202
227
  const addon = catalog.addons?.find((item) => item.id === configData.id);
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSyncPage.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminSyncPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,eAAe,EAIf,aAAa,EACd,MAAM,iBAAiB,CAAC;AAQzB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,eAAe,CAAC;CAC1B,CAAC;AAuPF,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,+BA0YpE"}
1
+ {"version":3,"file":"AdminSyncPage.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminSyncPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,eAAe,EAKf,aAAa,EACd,MAAM,iBAAiB,CAAC;AAazB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,eAAe,CAAC;CAC1B,CAAC;AAiZF,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,+BAqhBpE"}
@@ -5,6 +5,7 @@ import { EnvSelect } from "../env/EnvSelect";
5
5
  import { useToast } from "../../hooks/useToast";
6
6
  import { Badge, Card } from "../ui/primitives";
7
7
  import { IconSync } from "../ui/icons";
8
+ import { SelectionTableDialog, } from "../dialog/SelectionTableDialog";
8
9
  import { SyncPreviewFrame } from "./SyncPreviewFrame";
9
10
  function envById(environments, id) {
10
11
  return environments.find((env) => env.id === id);
@@ -47,6 +48,132 @@ function statusLabel(status) {
47
48
  return status;
48
49
  }
49
50
  }
51
+ function asStringList(value) {
52
+ if (Array.isArray(value)) {
53
+ return value.map((item) => String(item).trim()).filter(Boolean);
54
+ }
55
+ if (typeof value === "string" && value.trim()) {
56
+ return value
57
+ .split(",")
58
+ .map((item) => item.trim())
59
+ .filter(Boolean);
60
+ }
61
+ return [];
62
+ }
63
+ function compactInputs(values) {
64
+ const out = {};
65
+ for (const [targetId, fields] of Object.entries(values)) {
66
+ const cleaned = {};
67
+ for (const [key, value] of Object.entries(fields)) {
68
+ if (Array.isArray(value)) {
69
+ if (value.length > 0)
70
+ cleaned[key] = value;
71
+ continue;
72
+ }
73
+ if (value !== undefined && value !== null && value !== "") {
74
+ cleaned[key] = value;
75
+ }
76
+ }
77
+ if (Object.keys(cleaned).length > 0)
78
+ out[targetId] = cleaned;
79
+ }
80
+ return Object.keys(out).length > 0 ? out : undefined;
81
+ }
82
+ function firstEnumListField(schema) {
83
+ if (!schema)
84
+ return null;
85
+ for (const [key, field] of Object.entries(schema)) {
86
+ if (field.type === "enum[]")
87
+ return { key, field };
88
+ }
89
+ return null;
90
+ }
91
+ function selectionNoun(field) {
92
+ const label = (field.label ?? "items").trim().toLowerCase();
93
+ if (!label)
94
+ return "items";
95
+ return label;
96
+ }
97
+ function selectionSummary(field, selected) {
98
+ const total = field.options?.length ?? 0;
99
+ const noun = selectionNoun(field);
100
+ if (selected.length === 0) {
101
+ return total > 0 ? `All ${total} ${noun}` : `All ${noun}`;
102
+ }
103
+ if (selected.length === 1) {
104
+ return field.optionLabels?.[selected[0]] ?? selected[0];
105
+ }
106
+ return `${selected.length} ${noun} selected`;
107
+ }
108
+ /**
109
+ * Which envs an option is active in. Falls back to the sync source when the
110
+ * host omitted `optionEnvironments`.
111
+ */
112
+ function activeEnvIdsForOption(optionId, field, fromEnv) {
113
+ const declared = field.optionEnvironments?.[optionId];
114
+ if (Array.isArray(declared) && declared.length > 0) {
115
+ return [...new Set(declared.map(String).filter(Boolean))];
116
+ }
117
+ if ((field.options ?? []).includes(optionId))
118
+ return [fromEnv];
119
+ return [];
120
+ }
121
+ /** Prefer sync direction order for env chips. */
122
+ function orderedSyncEnvironments(environments, fromEnv, toEnv) {
123
+ const byId = new Map(environments.map((env) => [env.id, env]));
124
+ const ordered = [];
125
+ const push = (id) => {
126
+ const env = byId.get(id);
127
+ if (!env || ordered.some((item) => item.id === id))
128
+ return;
129
+ ordered.push(env);
130
+ };
131
+ push(fromEnv);
132
+ for (const id of toEnv)
133
+ push(id);
134
+ for (const env of environments)
135
+ push(env.id);
136
+ return ordered;
137
+ }
138
+ /** Column / copy presets for Sync target pickers (override per field key). */
139
+ function syncSelectionOverrides(fieldKey, field) {
140
+ const noun = selectionNoun(field);
141
+ if (fieldKey === "codes") {
142
+ return {
143
+ eyebrow: "Sync selection",
144
+ columns: { primary: "Code", secondary: "Offer" },
145
+ allSelectedFooter: "Sync all missing codes",
146
+ removeLabel: "Remove from sync",
147
+ emptyLabel: "No discount codes in the source environment.",
148
+ searchPlaceholder: "Search codes…",
149
+ };
150
+ }
151
+ if (fieldKey === "ids") {
152
+ return {
153
+ eyebrow: "Sync selection",
154
+ columns: { primary: "Id", secondary: "Name" },
155
+ allSelectedFooter: `Sync all ${noun}`,
156
+ removeLabel: "Remove from sync",
157
+ emptyLabel: "No products in the source environment.",
158
+ searchPlaceholder: "Search products…",
159
+ };
160
+ }
161
+ return {
162
+ eyebrow: "Sync selection",
163
+ columns: { primary: "Id", secondary: "Name" },
164
+ allSelectedFooter: `Sync all ${noun}`,
165
+ removeLabel: "Remove from sync",
166
+ searchPlaceholder: `Search ${noun}…`,
167
+ };
168
+ }
169
+ function syncSelectionRows(field, fromEnv) {
170
+ return (field.options ?? []).map((id) => ({
171
+ id,
172
+ primary: id,
173
+ secondary: field.optionLabels?.[id] ?? "",
174
+ environments: activeEnvIdsForOption(id, field, fromEnv),
175
+ }));
176
+ }
50
177
  function formatDiffValue(value) {
51
178
  if (value === undefined || value === null)
52
179
  return "—";
@@ -90,6 +217,8 @@ export function AdminSyncPage({ client, catalog }) {
90
217
  const [selectedTargets, setSelectedTargets] = useState(() => (sync?.targets ?? [])
91
218
  .filter((target) => target.defaultSelected)
92
219
  .map((target) => target.id));
220
+ const [targetInputValues, setTargetInputValues] = useState({});
221
+ const [picker, setPicker] = useState(null);
93
222
  const [plan, setPlan] = useState(null);
94
223
  const [planLoading, setPlanLoading] = useState(false);
95
224
  const [running, setRunning] = useState(false);
@@ -102,6 +231,11 @@ export function AdminSyncPage({ client, catalog }) {
102
231
  const targetGroups = useMemo(() => groupTargets(catalog), [catalog]);
103
232
  const hasTargets = (sync?.targets.length ?? 0) > 0;
104
233
  const allTargetIds = useMemo(() => (sync?.targets ?? []).map((target) => target.id), [sync?.targets]);
234
+ const requestInputs = useMemo(() => compactInputs(targetInputValues), [targetInputValues]);
235
+ const inputsForTarget = useCallback((targetId) => {
236
+ return (plan?.targetInputs?.[targetId] ??
237
+ sync?.targets.find((target) => target.id === targetId)?.inputs);
238
+ }, [plan?.targetInputs, sync?.targets]);
105
239
  const refreshPlan = useCallback(async () => {
106
240
  if (!hasTargets || toEnv.length === 0 || allTargetIds.length === 0) {
107
241
  setPlan(null);
@@ -115,6 +249,7 @@ export function AdminSyncPage({ client, catalog }) {
115
249
  from: fromEnv,
116
250
  to: toEnv,
117
251
  targets: allTargetIds,
252
+ ...(requestInputs ? { inputs: requestInputs } : {}),
118
253
  });
119
254
  setPlan(next);
120
255
  }
@@ -125,7 +260,7 @@ export function AdminSyncPage({ client, catalog }) {
125
260
  finally {
126
261
  setPlanLoading(false);
127
262
  }
128
- }, [client, fromEnv, toEnv, allTargetIds, hasTargets]);
263
+ }, [client, fromEnv, toEnv, allTargetIds, hasTargets, requestInputs]);
129
264
  useEffect(() => {
130
265
  void refreshPlan();
131
266
  }, [refreshPlan]);
@@ -134,6 +269,51 @@ export function AdminSyncPage({ client, catalog }) {
134
269
  ? current.filter((id) => id !== targetId)
135
270
  : [...current, targetId]);
136
271
  };
272
+ const openTargetPicker = (targetId, targetLabel) => {
273
+ const schema = inputsForTarget(targetId);
274
+ const listField = firstEnumListField(schema);
275
+ if (!listField) {
276
+ toggleTarget(targetId);
277
+ return;
278
+ }
279
+ setPicker({
280
+ targetId,
281
+ targetLabel,
282
+ fieldKey: listField.key,
283
+ field: listField.field,
284
+ });
285
+ };
286
+ const applyPickerSelection = (ids) => {
287
+ if (!picker)
288
+ return;
289
+ const { targetId, fieldKey } = picker;
290
+ setSelectedTargets((current) => current.includes(targetId) ? current : [...current, targetId]);
291
+ setTargetInputValues((current) => ({
292
+ ...current,
293
+ [targetId]: {
294
+ ...(current[targetId] ?? {}),
295
+ [fieldKey]: ids,
296
+ },
297
+ }));
298
+ setPicker(null);
299
+ };
300
+ const deselectFromPicker = () => {
301
+ if (!picker)
302
+ return;
303
+ const { targetId, fieldKey } = picker;
304
+ setSelectedTargets((current) => current.filter((id) => id !== targetId));
305
+ setTargetInputValues((current) => {
306
+ const next = { ...current };
307
+ const fields = { ...(next[targetId] ?? {}) };
308
+ delete fields[fieldKey];
309
+ if (Object.keys(fields).length === 0)
310
+ delete next[targetId];
311
+ else
312
+ next[targetId] = fields;
313
+ return next;
314
+ });
315
+ setPicker(null);
316
+ };
137
317
  const runSync = async () => {
138
318
  if (!hasTargets || toEnv.length === 0 || selectedTargets.length === 0)
139
319
  return;
@@ -159,10 +339,16 @@ export function AdminSyncPage({ client, catalog }) {
159
339
  setRunning(true);
160
340
  setError(null);
161
341
  try {
342
+ const selectedInputs = requestInputs
343
+ ? Object.fromEntries(Object.entries(requestInputs).filter(([id]) => selectedTargets.includes(id)))
344
+ : undefined;
162
345
  const result = await client.runSync({
163
346
  from: fromEnv,
164
347
  to: toEnv,
165
348
  targets: selectedTargets,
349
+ ...(selectedInputs && Object.keys(selectedInputs).length > 0
350
+ ? { inputs: selectedInputs }
351
+ : {}),
166
352
  });
167
353
  push(result.message ?? (result.ok ? "Sync complete" : "Sync finished with errors"), result.ok ? "ok" : "danger");
168
354
  await refreshPlan();
@@ -189,7 +375,12 @@ export function AdminSyncPage({ client, catalog }) {
189
375
  setToEnv((current) => current.filter((id) => id !== next));
190
376
  } })] }), _jsx("div", { className: "vertebr-sync-connector", "aria-hidden": "true", children: toEnvironments.length > 0 ? (toEnvironments.map((destination) => (_jsx("span", { className: "vertebr-sync-connector-arrow", children: "\u2192" }, destination.id)))) : (_jsx("span", { className: "vertebr-sync-connector-arrow", children: "\u2192" })) }), _jsxs("label", { className: "vertebr-sync-field", children: [_jsx("span", { className: "vertebr-sync-field-label", children: "To" }), _jsx(EnvMultiSelect, { environments: environments, value: toEnv, excludeId: fromEnv, onChange: setToEnv, placeholder: "Select one or more destinations" })] })] }), fromEnvironment && toEnvironments.length > 0 ? (_jsx("div", { className: "vertebr-sync-preview", "aria-label": "Sync direction", children: _jsxs("div", { className: "vertebr-sync-preview-flow", "data-dest-count": toEnvironments.length, children: [_jsxs("div", { className: "vertebr-sync-preview-col vertebr-sync-preview-col-from", children: [_jsx("span", { className: "vertebr-sync-preview-caption", children: "From" }), _jsx(EnvFlowBadge, { environment: fromEnvironment })] }), _jsx("div", { className: "vertebr-sync-preview-col vertebr-sync-preview-col-routes", children: toEnvironments.map((destination, index) => (_jsxs("div", { className: "vertebr-sync-preview-route", children: [_jsx("span", { className: "vertebr-sync-preview-arrow", "aria-hidden": "true", children: "\u2192" }), _jsxs("div", { className: "vertebr-sync-preview-col vertebr-sync-preview-col-to", children: [index === 0 ? (_jsx("span", { className: "vertebr-sync-preview-caption", children: "To" })) : (_jsx("span", { className: "vertebr-sync-preview-caption", "aria-hidden": "true", children: "\u00A0" })), _jsx(EnvFlowBadge, { environment: destination })] })] }, destination.id))) })] }) })) : (_jsx("p", { className: "vertebr-sync-direction-hint", children: "Pick a source environment and one or more destinations to preview changes." }))] }) }), _jsxs(Card, { title: "What to sync", children: [_jsxs("div", { className: "vertebr-sync-targets-head", children: [_jsx("p", { className: "vertebr-sync-targets-copy", children: "Review what will change in each destination before syncing." }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-sync-refresh", disabled: planLoading || toEnv.length === 0, onClick: () => void refreshPlan(), children: planLoading ? "Refreshing…" : "Refresh preview" })] }), [...targetGroups.entries()].map(([group, targets]) => (_jsxs("div", { className: "vertebr-sync-target-group", children: [_jsx("div", { className: "vertebr-sync-target-group-label", children: group }), _jsx("ul", { className: "vertebr-sync-target-list", children: targets.map((target) => {
191
377
  const selected = selectedTargets.includes(target.id);
192
- return (_jsx("li", { children: _jsxs("div", { className: "vertebr-sync-target-row", "data-selected": selected || undefined, children: [_jsxs("button", { type: "button", className: "vertebr-sync-target-toggle", onClick: () => toggleTarget(target.id), "aria-pressed": selected, children: [_jsx("span", { className: "vertebr-sync-target-check", "aria-hidden": "true", children: selected ? "✓" : "" }), _jsxs("span", { className: "vertebr-sync-target-main", children: [_jsx("span", { className: "vertebr-sync-target-title", children: target.label }), target.description ? (_jsx("span", { className: "vertebr-sync-target-desc", children: target.description })) : null] })] }), _jsx("span", { className: "vertebr-sync-target-badges", children: toEnvironments.length === 0 ? (_jsx(Badge, { children: "Choose destination" })) : planLoading ? (_jsx(Badge, { children: "Previewing\u2026" })) : (toEnvironments.map((destination) => {
378
+ const inputSchema = inputsForTarget(target.id);
379
+ const listField = firstEnumListField(inputSchema);
380
+ const pickedCodes = listField
381
+ ? asStringList(targetInputValues[target.id]?.[listField.key])
382
+ : [];
383
+ return (_jsx("li", { children: _jsxs("div", { className: "vertebr-sync-target-row", "data-selected": selected || undefined, "data-has-picker": listField ? "true" : undefined, children: [_jsxs("button", { type: "button", className: "vertebr-sync-target-toggle", onClick: () => openTargetPicker(target.id, target.label), "aria-pressed": selected, children: [_jsx("span", { className: "vertebr-sync-target-check", "aria-hidden": "true", children: selected ? "✓" : "" }), _jsxs("span", { className: "vertebr-sync-target-main", children: [_jsx("span", { className: "vertebr-sync-target-title", children: target.label }), target.description ? (_jsx("span", { className: "vertebr-sync-target-desc", children: target.description })) : null, selected && listField ? (_jsxs("span", { className: "vertebr-sync-target-selection", children: [selectionSummary(listField.field, pickedCodes), _jsx("span", { className: "vertebr-sync-target-selection-hint", children: "Click to change" })] })) : listField ? (_jsxs("span", { className: "vertebr-sync-target-selection-hint", children: ["Click to choose", " ", selectionNoun(listField.field)] })) : null] })] }), _jsx("span", { className: "vertebr-sync-target-badges", children: toEnvironments.length === 0 ? (_jsx(Badge, { children: "Choose destination" })) : planLoading ? (_jsx(Badge, { children: "Previewing\u2026" })) : (toEnvironments.map((destination) => {
193
384
  const item = planForTarget(destination.id, target.id);
194
385
  if (!item) {
195
386
  return (_jsx(Badge, { children: destination.label }, destination.id));
@@ -209,5 +400,13 @@ export function AdminSyncPage({ client, catalog }) {
209
400
  }) })] }, group)))] }), sync?.excluded.length ? (_jsx(Card, { title: "Never synced", subdued: true, children: _jsx("ul", { className: "vertebr-sync-excluded-list", children: sync.excluded.map((item) => (_jsxs("li", { children: [_jsx("span", { className: "vertebr-sync-excluded-name", children: item.label }), _jsx("span", { className: "vertebr-sync-excluded-reason", children: item.reason })] }, item.label))) }) })) : null, error ? _jsx("div", { className: "vertebr-banner", "data-tone": "danger", children: error }) : null] }), _jsxs("div", { className: "vertebr-savebar vertebr-sync-footer", children: [_jsx("div", { className: "vertebr-savebar-message", children: syncSummary }), _jsx("div", { className: "vertebr-savebar-actions", children: _jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-primary vertebr-sync-run-btn", disabled: running ||
210
401
  planLoading ||
211
402
  toEnv.length === 0 ||
212
- selectedTargets.length === 0, onClick: () => void runSync(), children: [_jsx(IconSync, {}), _jsx("span", { children: running ? "Syncing…" : "Sync selected" })] }) })] }), inspect ? (_jsx(SyncDiffModal, { inspect: inspect, sourceLabel: fromEnvironment?.label ?? fromEnv, onClose: () => setInspect(null) })) : null] }));
403
+ selectedTargets.length === 0, onClick: () => void runSync(), children: [_jsx(IconSync, {}), _jsx("span", { children: running ? "Syncing…" : "Sync selected" })] }) })] }), inspect ? (_jsx(SyncDiffModal, { inspect: inspect, sourceLabel: fromEnvironment?.label ?? fromEnv, onClose: () => setInspect(null) })) : null, picker ? (_jsx(SelectionTableDialog, { title: picker.targetLabel, description: (inputsForTarget(picker.targetId)?.[picker.fieldKey] ??
404
+ picker.field).description, sourceLabel: fromEnvironment?.label ?? fromEnv, environments: orderedSyncEnvironments(environments, fromEnv, toEnv), destinationEnvIds: toEnv, rows: syncSelectionRows(inputsForTarget(picker.targetId)?.[picker.fieldKey] ??
405
+ picker.field, fromEnv), selectedIds: asStringList(targetInputValues[picker.targetId]?.[picker.fieldKey]), overrides: {
406
+ ...syncSelectionOverrides(picker.fieldKey, inputsForTarget(picker.targetId)?.[picker.fieldKey] ??
407
+ picker.field),
408
+ showRemove: selectedTargets.includes(picker.targetId),
409
+ }, onClose: () => setPicker(null), onApply: applyPickerSelection, onRemove: selectedTargets.includes(picker.targetId)
410
+ ? deselectFromPicker
411
+ : undefined })) : null] }));
213
412
  }
@@ -1,7 +1,11 @@
1
- import type { VertebrClient } from "@vertebr/client";
1
+ import type { AuditColumn, AuditEntry, VertebrClient } from "@vertebr/client";
2
2
  export type AuditBoardProps = {
3
3
  client: VertebrClient;
4
4
  label?: string;
5
+ /** From `catalog.audit.columns`. Omit for Vertebr defaults. */
6
+ columns?: AuditColumn[];
5
7
  };
6
- export declare function AuditBoard({ client, label }: AuditBoardProps): import("react").JSX.Element;
8
+ /** Resolve a column path against an audit entry (`meta.x`, `target`, or field). */
9
+ export declare function resolveAuditColumnValue(entry: AuditEntry, path: string): unknown;
10
+ export declare function AuditBoard({ client, label, columns: columnsProp, }: AuditBoardProps): import("react").JSX.Element;
7
11
  //# sourceMappingURL=AuditBoard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AuditBoard.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AuditBoard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEjE,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAaF,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,KAAe,EAAE,EAAE,eAAe,+BA4EtE"}
1
+ {"version":3,"file":"AuditBoard.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AuditBoard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,aAAa,EACd,MAAM,iBAAiB,CAAC;AAoBzB,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CACzB,CAAC;AAsEF,mFAAmF;AACnF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,GACX,OAAO,CAST;AA6CD,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAe,EACf,OAAO,EAAE,WAAW,GACrB,EAAE,eAAe,+BA2LjB"}