@vertebr/react 0.9.0 → 0.10.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/dist/components/admin/AddonEditor.d.ts +3 -2
- package/dist/components/admin/AddonEditor.d.ts.map +1 -1
- package/dist/components/admin/AddonEditor.js +4 -2
- package/dist/components/admin/AdminContent.d.ts +4 -1
- package/dist/components/admin/AdminContent.d.ts.map +1 -1
- package/dist/components/admin/AdminContent.js +25 -11
- package/dist/components/admin/AdminTopBar.d.ts +6 -2
- package/dist/components/admin/AdminTopBar.d.ts.map +1 -1
- package/dist/components/admin/AdminTopBar.js +27 -4
- package/dist/components/admin/AuditBoard.d.ts +7 -0
- package/dist/components/admin/AuditBoard.d.ts.map +1 -0
- package/dist/components/admin/AuditBoard.js +43 -0
- package/dist/components/admin/HealthModal.d.ts +9 -0
- package/dist/components/admin/HealthModal.d.ts.map +1 -0
- package/dist/components/admin/HealthModal.js +59 -0
- package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
- package/dist/components/admin/VertebrAdmin.js +88 -22
- package/dist/components/admin/adminSearch.d.ts.map +1 -1
- package/dist/components/admin/adminSearch.js +9 -0
- package/dist/components/admin/breadcrumbs.d.ts.map +1 -1
- package/dist/components/admin/breadcrumbs.js +2 -0
- package/dist/components/admin/routes.d.ts.map +1 -1
- package/dist/components/admin/routes.js +13 -1
- package/dist/components/admin/types.d.ts +2 -0
- package/dist/components/admin/types.d.ts.map +1 -1
- package/dist/components/config/ReferenceInput.d.ts +11 -0
- package/dist/components/config/ReferenceInput.d.ts.map +1 -0
- package/dist/components/config/ReferenceInput.js +99 -0
- package/dist/components/config/RevisionHistory.d.ts +8 -0
- package/dist/components/config/RevisionHistory.d.ts.map +1 -0
- package/dist/components/config/RevisionHistory.js +84 -0
- package/dist/components/config/form/ConfigField.d.ts +3 -2
- package/dist/components/config/form/ConfigField.d.ts.map +1 -1
- package/dist/components/config/form/ConfigField.js +3 -2
- package/dist/components/config/form/ConfigForm.d.ts +1 -1
- package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/ConfigForm.js +13 -6
- package/dist/components/config/form/RecordConfigForm.d.ts +3 -2
- package/dist/components/config/form/RecordConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/RecordConfigForm.js +2 -1
- package/dist/components/config/form/SettingsConfigForm.d.ts +3 -2
- package/dist/components/config/form/SettingsConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/SettingsConfigForm.js +2 -2
- package/dist/components/config/form/types.d.ts +5 -1
- package/dist/components/config/form/types.d.ts.map +1 -1
- package/dist/components/resources/ResourceTable.d.ts +7 -1
- package/dist/components/resources/ResourceTable.d.ts.map +1 -1
- package/dist/components/resources/ResourceTable.js +56 -6
- package/dist/components/theme/ThemeToggle.d.ts +3 -1
- package/dist/components/theme/ThemeToggle.d.ts.map +1 -1
- package/dist/components/theme/ThemeToggle.js +2 -2
- package/dist/components/ui/icons.d.ts +1 -0
- package/dist/components/ui/icons.d.ts.map +1 -1
- package/dist/components/ui/icons.js +6 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/lib/capabilities.d.ts +4 -0
- package/dist/lib/capabilities.d.ts.map +1 -0
- package/dist/lib/capabilities.js +9 -0
- package/dist/styles/audit.css +34 -0
- package/dist/styles/form.css +143 -0
- package/dist/styles/index.css +1 -0
- package/dist/styles/modal.css +111 -0
- package/dist/styles/shell.css +234 -18
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
2
|
import { createVertebrClient, } from "@vertebr/client";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { useVertebrTheme } from "../../hooks/useVertebrTheme";
|
|
5
|
+
import { hasCap } from "../../lib/capabilities";
|
|
5
6
|
import { parseMetricsRangeId, } from "../../lib/metricsRanges";
|
|
6
7
|
import { AdminCommandPalette } from "./AdminCommandPalette";
|
|
7
8
|
import { AdminContent } from "./AdminContent";
|
|
@@ -39,6 +40,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
39
40
|
});
|
|
40
41
|
});
|
|
41
42
|
const [resourceData, setResourceData] = useState(null);
|
|
43
|
+
const [resourceSearch, setResourceSearch] = useState("");
|
|
42
44
|
const [configData, setConfigData] = useState(null);
|
|
43
45
|
const [metricsData, setMetricsData] = useState(null);
|
|
44
46
|
const [metricsLoading, setMetricsLoading] = useState(false);
|
|
@@ -49,6 +51,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
49
51
|
const [fontLibraryLoading, setFontLibraryLoading] = useState(false);
|
|
50
52
|
const [emailsData, setEmailsData] = useState(null);
|
|
51
53
|
const [emailsLoading, setEmailsLoading] = useState(false);
|
|
54
|
+
const [healthChecks, setHealthChecks] = useState(null);
|
|
52
55
|
const [error, setError] = useState(null);
|
|
53
56
|
const [loading, setLoading] = useState(true);
|
|
54
57
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
|
@@ -210,7 +213,10 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
210
213
|
}
|
|
211
214
|
}
|
|
212
215
|
else if (selection.kind === "resource") {
|
|
213
|
-
const data = await client.listResource(selection.id
|
|
216
|
+
const data = await client.listResource(selection.id, {
|
|
217
|
+
...(resourceSearch.trim() ? { q: resourceSearch.trim() } : {}),
|
|
218
|
+
fresh: true,
|
|
219
|
+
});
|
|
214
220
|
if (!cancelled) {
|
|
215
221
|
setResourceData(data);
|
|
216
222
|
setConfigData(null);
|
|
@@ -220,7 +226,9 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
220
226
|
setEmailsData(null);
|
|
221
227
|
}
|
|
222
228
|
}
|
|
223
|
-
else if (selection.kind === "sync" ||
|
|
229
|
+
else if (selection.kind === "sync" ||
|
|
230
|
+
selection.kind === "addons" ||
|
|
231
|
+
selection.kind === "audit") {
|
|
224
232
|
if (!cancelled) {
|
|
225
233
|
setResourceData(null);
|
|
226
234
|
setConfigData(null);
|
|
@@ -270,12 +278,54 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
270
278
|
return () => {
|
|
271
279
|
cancelled = true;
|
|
272
280
|
};
|
|
273
|
-
}, [client, selection, catalog, environment, metricsRange]);
|
|
281
|
+
}, [client, selection, catalog, environment, metricsRange, resourceSearch]);
|
|
282
|
+
const resourceSelectionId = selection?.kind === "resource" ? selection.id : null;
|
|
283
|
+
useEffect(() => {
|
|
284
|
+
setResourceSearch("");
|
|
285
|
+
}, [resourceSelectionId]);
|
|
286
|
+
const refreshHealth = useCallback(async () => {
|
|
287
|
+
if (!catalog?.health) {
|
|
288
|
+
setHealthChecks(null);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const data = await client.getHealth({ fresh: true });
|
|
292
|
+
setHealthChecks(data.checks);
|
|
293
|
+
}, [client, catalog]);
|
|
294
|
+
useEffect(() => {
|
|
295
|
+
if (!catalog?.health) {
|
|
296
|
+
setHealthChecks(null);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
let cancelled = false;
|
|
300
|
+
void (async () => {
|
|
301
|
+
try {
|
|
302
|
+
const data = await client.getHealth({ fresh: true });
|
|
303
|
+
if (!cancelled)
|
|
304
|
+
setHealthChecks(data.checks);
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
if (!cancelled)
|
|
308
|
+
setHealthChecks(null);
|
|
309
|
+
}
|
|
310
|
+
})();
|
|
311
|
+
return () => {
|
|
312
|
+
cancelled = true;
|
|
313
|
+
};
|
|
314
|
+
}, [client, catalog?.health, environment]);
|
|
274
315
|
const refreshResource = useCallback(async () => {
|
|
275
316
|
if (selection?.kind !== "resource")
|
|
276
317
|
return;
|
|
277
|
-
const data = await client.listResource(selection.id
|
|
318
|
+
const data = await client.listResource(selection.id, {
|
|
319
|
+
...(resourceSearch.trim() ? { q: resourceSearch.trim() } : {}),
|
|
320
|
+
fresh: true,
|
|
321
|
+
});
|
|
278
322
|
setResourceData(data);
|
|
323
|
+
}, [client, selection, resourceSearch]);
|
|
324
|
+
const refreshConfig = useCallback(async () => {
|
|
325
|
+
if (selection?.kind !== "config" && selection?.kind !== "addon")
|
|
326
|
+
return;
|
|
327
|
+
const data = await client.getConfig(selection.id, { fresh: true });
|
|
328
|
+
setConfigData(data);
|
|
279
329
|
}, [client, selection]);
|
|
280
330
|
const refreshMetrics = useCallback(async () => {
|
|
281
331
|
setMetricsLoading(true);
|
|
@@ -358,11 +408,14 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
358
408
|
const envList = catalog?.environments ?? bootstrap?.environments ?? [];
|
|
359
409
|
const activeEnv = envList.find((env) => env.id === environment);
|
|
360
410
|
const authRequired = Boolean(activeEnv?.auth?.required);
|
|
411
|
+
const authProvider = activeEnv?.auth?.client?.provider;
|
|
412
|
+
const authViaTokenOnly = authProvider === "custom" && Boolean(getAccessToken);
|
|
361
413
|
const activeConfig = selection?.kind === "config"
|
|
362
414
|
? catalog?.configs.find((c) => c.id === selection.id)
|
|
363
415
|
: undefined;
|
|
364
416
|
const isPageSelection = activeConfig?.section === "page";
|
|
365
417
|
const hasPagePreview = Boolean(activeConfig?.previewPath);
|
|
418
|
+
const syncEnabled = Boolean(catalog?.sync?.enabled) && hasCap(catalog, "sync.run");
|
|
366
419
|
const switchEnvironment = (nextEnv) => {
|
|
367
420
|
if (nextEnv === environment)
|
|
368
421
|
return;
|
|
@@ -375,6 +428,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
375
428
|
writeStored(envStorageKey, nextEnv);
|
|
376
429
|
setEnvironment(nextEnv);
|
|
377
430
|
setResourceData(null);
|
|
431
|
+
setResourceSearch("");
|
|
378
432
|
setConfigData(null);
|
|
379
433
|
setGalleryData(null);
|
|
380
434
|
setFontLibraryData(null);
|
|
@@ -406,6 +460,16 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
406
460
|
onSelect: () => setSelection({ kind: "overview" }),
|
|
407
461
|
});
|
|
408
462
|
}
|
|
463
|
+
if (catalog.audit) {
|
|
464
|
+
entries.push({
|
|
465
|
+
key: "audit",
|
|
466
|
+
group: catalog.audit.group ?? "Analytics",
|
|
467
|
+
label: catalog.audit.label,
|
|
468
|
+
icon: iconForNavKey("audit"),
|
|
469
|
+
active: selection?.kind === "audit",
|
|
470
|
+
onSelect: () => setSelection({ kind: "audit" }),
|
|
471
|
+
});
|
|
472
|
+
}
|
|
409
473
|
for (const resource of catalog.resources) {
|
|
410
474
|
entries.push({
|
|
411
475
|
key: `resource:${resource.id}`,
|
|
@@ -498,7 +562,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
498
562
|
onSelect: () => setSelection({ kind: "config", id: config.id }),
|
|
499
563
|
});
|
|
500
564
|
}
|
|
501
|
-
if (catalog.sync?.enabled) {
|
|
565
|
+
if (catalog.sync?.enabled && hasCap(catalog, "sync.run")) {
|
|
502
566
|
entries.push({
|
|
503
567
|
key: "sync",
|
|
504
568
|
group: "Settings",
|
|
@@ -520,25 +584,27 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
520
584
|
? (catalog?.metrics?.label ?? "Overview")
|
|
521
585
|
: selection?.kind === "sync"
|
|
522
586
|
? "Sync environments"
|
|
523
|
-
: selection?.kind === "
|
|
524
|
-
? "
|
|
525
|
-
: selection?.kind === "
|
|
526
|
-
?
|
|
527
|
-
: selection?.kind === "
|
|
528
|
-
? (catalog?.
|
|
529
|
-
: selection?.kind === "
|
|
530
|
-
? (catalog?.
|
|
531
|
-
: selection?.kind === "
|
|
532
|
-
? catalog?.
|
|
533
|
-
: selection?.kind === "
|
|
534
|
-
? catalog?.
|
|
535
|
-
: selection?.kind === "
|
|
536
|
-
? catalog?.
|
|
537
|
-
:
|
|
587
|
+
: selection?.kind === "audit"
|
|
588
|
+
? (catalog?.audit?.label ?? "Audit")
|
|
589
|
+
: selection?.kind === "addons"
|
|
590
|
+
? "Add-ons"
|
|
591
|
+
: selection?.kind === "gallery"
|
|
592
|
+
? (catalog?.gallery?.label ?? "Gallery")
|
|
593
|
+
: selection?.kind === "fontLibrary"
|
|
594
|
+
? (catalog?.fontLibrary?.label ?? "Font Library")
|
|
595
|
+
: selection?.kind === "emails"
|
|
596
|
+
? (catalog?.emails?.label ?? "Emails")
|
|
597
|
+
: selection?.kind === "resource"
|
|
598
|
+
? catalog?.resources.find((r) => r.id === selection.id)?.label
|
|
599
|
+
: selection?.kind === "config"
|
|
600
|
+
? catalog?.configs.find((c) => c.id === selection.id)?.label
|
|
601
|
+
: selection?.kind === "addon"
|
|
602
|
+
? catalog?.addons?.find((a) => a.id === selection.id)?.label
|
|
603
|
+
: null;
|
|
538
604
|
const selectionDescription = selection?.kind === "addons"
|
|
539
605
|
? "Preview how optional storefront features look in your shop, then configure each one."
|
|
540
606
|
: selection?.kind === "addon"
|
|
541
607
|
? catalog?.addons?.find((item) => item.id === selection.id)?.description
|
|
542
608
|
: undefined;
|
|
543
|
-
return (_jsx(SaveBarProvider, { children: _jsx(ToastProvider, { children: _jsxs("div", { className: "vertebr-root", "data-env": environment, "data-sidebar-collapsed": sidebarCollapsed || undefined, children: [_jsx(AdminTopBar, { brand: bootstrap?.brand ?? catalog?.brand ?? "Vertebr", hostName: bootstrap?.appearance?.hostName ?? catalog?.appearance?.hostName, hostLogoUrl: bootstrap?.appearance?.hostLogoUrl ?? catalog?.appearance?.hostLogoUrl, envList: envList, environment: environment, onEnvironmentChange: switchEnvironment, authUserLabel: authUserLabel, onSignOut: onSignOut, getAccessToken: getAccessToken, authRequired: authRequired, token: token, tokenDraft: tokenDraft, onTokenDraftChange: setTokenDraft, onSaveToken: saveToken, onClearToken: clearToken, theme: theme, onThemeChange: setTheme, sidebarCollapsed: sidebarCollapsed, onToggleSidebar: () => setSidebarCollapsed((prev) => !prev), syncEnabled:
|
|
609
|
+
return (_jsx(SaveBarProvider, { children: _jsx(ToastProvider, { children: _jsxs("div", { className: "vertebr-root", "data-env": environment, "data-sidebar-collapsed": sidebarCollapsed || undefined, children: [_jsx(AdminTopBar, { brand: bootstrap?.brand ?? catalog?.brand ?? "Vertebr", hostName: bootstrap?.appearance?.hostName ?? catalog?.appearance?.hostName, hostLogoUrl: bootstrap?.appearance?.hostLogoUrl ?? catalog?.appearance?.hostLogoUrl, envList: envList, environment: environment, onEnvironmentChange: switchEnvironment, authUserLabel: authUserLabel, onSignOut: onSignOut, getAccessToken: getAccessToken, authViaTokenOnly: authViaTokenOnly, authRequired: authRequired, token: token, tokenDraft: tokenDraft, onTokenDraftChange: setTokenDraft, onSaveToken: saveToken, onClearToken: clearToken, theme: theme, onThemeChange: setTheme, sidebarCollapsed: sidebarCollapsed, onToggleSidebar: () => setSidebarCollapsed((prev) => !prev), syncEnabled: syncEnabled, onOpenSync: () => setSelection({ kind: "sync" }), onOpenSearch: () => setSearchOpen(true), healthChecks: healthChecks, onRefreshHealth: catalog?.health ? refreshHealth : undefined }), catalog && searchOpen ? (_jsx(AdminCommandPalette, { catalog: catalog, onClose: () => setSearchOpen(false), onSelect: setSelection })) : null, _jsx(AdminSidebar, { navGroups: navGroups, collapsed: sidebarCollapsed }), _jsxs("main", { className: "vertebr-main", children: [_jsx(AdminHeader, { activeLabel: activeLabel ?? (catalog ? "Dashboard" : "Sign in"), catalogLoaded: Boolean(catalog), authRequired: authRequired, selection: selection, isPageSelection: isPageSelection, hasPagePreview: hasPagePreview, selectionDescription: selectionDescription, error: error, breadcrumbs: breadcrumbsForSelection(selection, catalog) }), _jsx(AdminContent, { client: client, catalog: catalog, selection: selection, onSelectionChange: setSelection, authRequired: authRequired, loading: loading, isPageSelection: isPageSelection, metricsData: metricsData, metricsLoading: metricsLoading, metricsRange: metricsRange, onMetricsRangeChange: changeMetricsRange, onRefreshMetrics: refreshMetrics, galleryData: galleryData, galleryLoading: galleryLoading, onRefreshGallery: refreshGallery, fontLibraryData: fontLibraryData, fontLibraryLoading: fontLibraryLoading, onRefreshFonts: refreshFonts, emailsData: emailsData, emailsLoading: emailsLoading, onRefreshEmails: refreshEmails, resourceData: resourceData, resourceSearch: resourceSearch, onResourceSearchChange: setResourceSearch, onRefreshResource: refreshResource, configData: configData, onConfigSaved: setConfigData, onRefreshConfig: refreshConfig, galleryPicker: galleryPicker, fontLibraryPicker: fontLibraryPicker }), _jsx(AdminSaveBar, {})] }), _jsx(AdminToastStack, {})] }) }) }));
|
|
544
610
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adminSearch.d.ts","sourceRoot":"","sources":["../../../src/components/admin/adminSearch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"adminSearch.d.ts","sourceRoot":"","sources":["../../../src/components/admin/adminSearch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,EAAE,CAuH5E;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,eAAe,EAAE,EACxB,KAAK,EAAE,MAAM,GACZ,eAAe,EAAE,CASnB"}
|
|
@@ -9,6 +9,15 @@ export function adminSearchItems(catalog) {
|
|
|
9
9
|
selection: { kind: "overview" },
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
|
+
if (catalog.audit) {
|
|
13
|
+
items.push({
|
|
14
|
+
id: "audit",
|
|
15
|
+
label: catalog.audit.label,
|
|
16
|
+
group: catalog.audit.group ?? "Analytics",
|
|
17
|
+
hint: "Audit log",
|
|
18
|
+
selection: { kind: "audit" },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
12
21
|
for (const resource of catalog.resources) {
|
|
13
22
|
items.push({
|
|
14
23
|
id: `resource:${resource.id}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../../../src/components/admin/breadcrumbs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GAAG,IAAI,GAC9B,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../../../src/components/admin/breadcrumbs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GAAG,IAAI,GAC9B,UAAU,EAAE,CA2Dd"}
|
|
@@ -7,6 +7,8 @@ export function breadcrumbsForSelection(selection, catalog) {
|
|
|
7
7
|
return [...crumbs, { label: catalog.metrics?.label ?? "Overview" }];
|
|
8
8
|
case "sync":
|
|
9
9
|
return [...crumbs, { label: "Sync environments" }];
|
|
10
|
+
case "audit":
|
|
11
|
+
return [...crumbs, { label: catalog.audit?.label ?? "Audit" }];
|
|
10
12
|
case "addons":
|
|
11
13
|
return [...crumbs, { label: "Add-ons" }];
|
|
12
14
|
case "addon": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/components/admin/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,SAAS,CAAC;AAIpD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAO5D;AAOD,iEAAiE;AACjE,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GACvB,MAAM,
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/components/admin/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,SAAS,CAAC;AAIpD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAO5D;AAOD,iEAAiE;AACjE,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GACvB,MAAM,CAkCR;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GACvB,MAAM,CAIR;AASD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,GAAG,IAAI,GAC9B,SAAS,GAAG,IAAI,CAiDlB;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CAUpE;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GACvB,SAAS,CAuCX;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAuBnE"}
|
|
@@ -25,6 +25,8 @@ export function pathForSelection(selection, catalog) {
|
|
|
25
25
|
return "/overview";
|
|
26
26
|
case "sync":
|
|
27
27
|
return "/sync";
|
|
28
|
+
case "audit":
|
|
29
|
+
return "/audit";
|
|
28
30
|
case "addons":
|
|
29
31
|
return "/addons";
|
|
30
32
|
case "addon":
|
|
@@ -85,6 +87,8 @@ export function selectionFromPath(pathname, basePath, catalog) {
|
|
|
85
87
|
return { kind: "overview" };
|
|
86
88
|
if (head === "sync")
|
|
87
89
|
return { kind: "sync" };
|
|
90
|
+
if (head === "audit")
|
|
91
|
+
return { kind: "audit" };
|
|
88
92
|
if (head === "addons") {
|
|
89
93
|
if (!tail[0])
|
|
90
94
|
return { kind: "addons" };
|
|
@@ -154,6 +158,9 @@ export function coerceSelection(selection, catalog) {
|
|
|
154
158
|
if (selection.kind === "sync") {
|
|
155
159
|
return catalog.sync?.enabled ? selection : defaultSelection(catalog);
|
|
156
160
|
}
|
|
161
|
+
if (selection.kind === "audit") {
|
|
162
|
+
return catalog.audit ? selection : defaultSelection(catalog);
|
|
163
|
+
}
|
|
157
164
|
if (selection.kind === "addons") {
|
|
158
165
|
return catalog.addons?.length ? selection : defaultSelection(catalog);
|
|
159
166
|
}
|
|
@@ -190,7 +197,12 @@ export function selectionsEqual(a, b) {
|
|
|
190
197
|
return a === b;
|
|
191
198
|
if (a.kind !== b.kind)
|
|
192
199
|
return false;
|
|
193
|
-
if (a.kind === "overview" ||
|
|
200
|
+
if (a.kind === "overview" ||
|
|
201
|
+
a.kind === "gallery" ||
|
|
202
|
+
a.kind === "fontLibrary" ||
|
|
203
|
+
a.kind === "sync" ||
|
|
204
|
+
a.kind === "audit" ||
|
|
205
|
+
a.kind === "addons") {
|
|
194
206
|
return true;
|
|
195
207
|
}
|
|
196
208
|
if (a.kind === "addon" && b.kind === "addon")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/admin/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,oEAAoE;IACpE,cAAc,CAAC,EAAE,MACb,MAAM,GACN,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACvC,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAE1C,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAE7D,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,YAAY,CAAC;AAEtE,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B,IAAI,CAAC;AAET,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;KACtB,CAAC,CAAC;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/admin/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,oEAAoE;IACpE,cAAc,CAAC,EAAE,MACb,MAAM,GACN,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACvC,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAE1C,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAE7D,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,YAAY,CAAC;AAEtE,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B,IAAI,CAAC;AAET,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;KACtB,CAAC,CAAC;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FieldSchema, VertebrClient } from "@vertebr/client";
|
|
2
|
+
export type ReferenceInputProps = {
|
|
3
|
+
id?: string;
|
|
4
|
+
value: unknown;
|
|
5
|
+
field: FieldSchema;
|
|
6
|
+
client: VertebrClient;
|
|
7
|
+
readOnly?: boolean;
|
|
8
|
+
onChange: (value: string) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function ReferenceInput({ id: idProp, value, field, client, readOnly, onChange, }: ReferenceInputProps): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=ReferenceInput.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReferenceInput.d.ts","sourceRoot":"","sources":["../../../src/components/config/ReferenceInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAElE,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAuBF,wBAAgB,cAAc,CAAC,EAC7B,EAAE,EAAE,MAAM,EACV,KAAK,EACL,KAAK,EACL,MAAM,EACN,QAAQ,EACR,QAAQ,GACT,EAAE,mBAAmB,+BA0IrB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
3
|
+
function rowValue(row, valueField) {
|
|
4
|
+
const raw = row[valueField] ?? row.id ?? row.slug ?? row.uid;
|
|
5
|
+
if (typeof raw === "string" && raw.trim())
|
|
6
|
+
return raw;
|
|
7
|
+
if (typeof raw === "number")
|
|
8
|
+
return String(raw);
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
function rowLabel(row, labelField, value) {
|
|
12
|
+
const raw = row[labelField] ?? row.name ?? row.title ?? row.label ?? value;
|
|
13
|
+
return typeof raw === "string" && raw.trim() ? raw.trim() : value;
|
|
14
|
+
}
|
|
15
|
+
export function ReferenceInput({ id: idProp, value, field, client, readOnly, onChange, }) {
|
|
16
|
+
const autoId = useId();
|
|
17
|
+
const id = idProp ?? autoId;
|
|
18
|
+
const resource = field.reference?.resource ?? "";
|
|
19
|
+
const valueField = field.reference?.valueField ?? "id";
|
|
20
|
+
const labelField = field.reference?.labelField ?? "name";
|
|
21
|
+
const current = typeof value === "string" ? value : "";
|
|
22
|
+
const [query, setQuery] = useState("");
|
|
23
|
+
const [open, setOpen] = useState(false);
|
|
24
|
+
const [options, setOptions] = useState([]);
|
|
25
|
+
const [loading, setLoading] = useState(false);
|
|
26
|
+
const [displayLabel, setDisplayLabel] = useState(current);
|
|
27
|
+
const rootRef = useRef(null);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
setDisplayLabel(current);
|
|
30
|
+
}, [current]);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!open || !resource || readOnly)
|
|
33
|
+
return;
|
|
34
|
+
let cancelled = false;
|
|
35
|
+
const handle = window.setTimeout(() => {
|
|
36
|
+
void (async () => {
|
|
37
|
+
setLoading(true);
|
|
38
|
+
try {
|
|
39
|
+
const listed = await client.listResource(resource, {
|
|
40
|
+
q: query.trim() || undefined,
|
|
41
|
+
limit: 20,
|
|
42
|
+
fresh: true,
|
|
43
|
+
});
|
|
44
|
+
if (cancelled)
|
|
45
|
+
return;
|
|
46
|
+
const next = [];
|
|
47
|
+
for (const row of listed.items) {
|
|
48
|
+
const v = rowValue(row, valueField);
|
|
49
|
+
if (!v)
|
|
50
|
+
continue;
|
|
51
|
+
next.push({ value: v, label: rowLabel(row, labelField, v) });
|
|
52
|
+
}
|
|
53
|
+
setOptions(next);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
if (!cancelled)
|
|
57
|
+
setOptions([]);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
if (!cancelled)
|
|
61
|
+
setLoading(false);
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
64
|
+
}, 180);
|
|
65
|
+
return () => {
|
|
66
|
+
cancelled = true;
|
|
67
|
+
window.clearTimeout(handle);
|
|
68
|
+
};
|
|
69
|
+
}, [open, query, resource, client, valueField, labelField, readOnly]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const onPointer = (event) => {
|
|
72
|
+
if (!rootRef.current?.contains(event.target)) {
|
|
73
|
+
setOpen(false);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
document.addEventListener("mousedown", onPointer);
|
|
77
|
+
return () => document.removeEventListener("mousedown", onPointer);
|
|
78
|
+
}, []);
|
|
79
|
+
if (!resource) {
|
|
80
|
+
return (_jsx("input", { id: id, type: "text", readOnly: readOnly, value: current, onChange: (event) => onChange(event.target.value), placeholder: "Missing reference.resource" }));
|
|
81
|
+
}
|
|
82
|
+
return (_jsxs("div", { className: "vertebr-reference", ref: rootRef, "data-open": open || undefined, children: [_jsx("input", { id: id, type: "text", className: "vertebr-reference-input", readOnly: readOnly, value: open ? query : displayLabel || current, placeholder: "Search\u2026", autoComplete: "off", onFocus: () => {
|
|
83
|
+
if (readOnly)
|
|
84
|
+
return;
|
|
85
|
+
setQuery("");
|
|
86
|
+
setOpen(true);
|
|
87
|
+
}, onChange: (event) => {
|
|
88
|
+
setQuery(event.target.value);
|
|
89
|
+
setOpen(true);
|
|
90
|
+
} }), current && !readOnly ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-reference-clear", onClick: () => {
|
|
91
|
+
onChange("");
|
|
92
|
+
setDisplayLabel("");
|
|
93
|
+
setQuery("");
|
|
94
|
+
}, children: "Clear" })) : null, open && !readOnly ? (_jsx("ul", { className: "vertebr-reference-menu", role: "listbox", children: loading ? (_jsx("li", { className: "vertebr-reference-empty", children: "Searching\u2026" })) : options.length === 0 ? (_jsx("li", { className: "vertebr-reference-empty", children: "No matches" })) : (options.map((option) => (_jsx("li", { children: _jsxs("button", { type: "button", role: "option", "aria-selected": option.value === current, className: "vertebr-reference-option", onClick: () => {
|
|
95
|
+
onChange(option.value);
|
|
96
|
+
setDisplayLabel(option.label);
|
|
97
|
+
setOpen(false);
|
|
98
|
+
}, children: [_jsx("span", { children: option.label }), _jsx("code", { children: option.value })] }) }, option.value)))) })) : null] }));
|
|
99
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { VertebrClient } from "@vertebr/client";
|
|
2
|
+
export type RevisionHistoryProps = {
|
|
3
|
+
client: VertebrClient;
|
|
4
|
+
configId: string;
|
|
5
|
+
onPublished?: () => void | Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
export declare function RevisionHistory({ client, configId, onPublished, }: RevisionHistoryProps): import("react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=RevisionHistory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RevisionHistory.d.ts","sourceRoot":"","sources":["../../../src/components/config/RevisionHistory.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGnE,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC;AAaF,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,QAAQ,EACR,WAAW,GACZ,EAAE,oBAAoB,+BAmHtB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { useToast } from "../../hooks/useToast";
|
|
4
|
+
function formatWhen(at) {
|
|
5
|
+
const date = new Date(at);
|
|
6
|
+
if (Number.isNaN(date.getTime()))
|
|
7
|
+
return at;
|
|
8
|
+
return date.toLocaleString(undefined, {
|
|
9
|
+
month: "short",
|
|
10
|
+
day: "numeric",
|
|
11
|
+
hour: "numeric",
|
|
12
|
+
minute: "2-digit",
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function RevisionHistory({ client, configId, onPublished, }) {
|
|
16
|
+
const { push } = useToast();
|
|
17
|
+
const [items, setItems] = useState([]);
|
|
18
|
+
const [loading, setLoading] = useState(true);
|
|
19
|
+
const [busyId, setBusyId] = useState(null);
|
|
20
|
+
const refresh = useCallback(async () => {
|
|
21
|
+
setLoading(true);
|
|
22
|
+
try {
|
|
23
|
+
const data = await client.listRevisions(configId, { fresh: true });
|
|
24
|
+
setItems(data.items);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
setLoading(false);
|
|
28
|
+
}
|
|
29
|
+
}, [client, configId]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
let cancelled = false;
|
|
32
|
+
void (async () => {
|
|
33
|
+
try {
|
|
34
|
+
setLoading(true);
|
|
35
|
+
const data = await client.listRevisions(configId, { fresh: true });
|
|
36
|
+
if (!cancelled)
|
|
37
|
+
setItems(data.items);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (!cancelled) {
|
|
41
|
+
push(err instanceof Error ? err.message : "Failed to load revisions", "danger");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
if (!cancelled)
|
|
46
|
+
setLoading(false);
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
return () => {
|
|
50
|
+
cancelled = true;
|
|
51
|
+
};
|
|
52
|
+
}, [client, configId, push]);
|
|
53
|
+
const publish = async (revId) => {
|
|
54
|
+
setBusyId(revId);
|
|
55
|
+
try {
|
|
56
|
+
await client.publishRevision(configId, revId);
|
|
57
|
+
push("Revision published", "ok");
|
|
58
|
+
await refresh();
|
|
59
|
+
await onPublished?.();
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
push(err instanceof Error ? err.message : "Publish failed", "danger");
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
setBusyId(null);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const restore = async (revId) => {
|
|
69
|
+
setBusyId(revId);
|
|
70
|
+
try {
|
|
71
|
+
await client.restoreRevision(configId, revId);
|
|
72
|
+
push("Draft restored from revision", "ok");
|
|
73
|
+
await refresh();
|
|
74
|
+
await onPublished?.();
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
push(err instanceof Error ? err.message : "Restore failed", "danger");
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
setBusyId(null);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return (_jsxs("div", { className: "vertebr-revisions", children: [_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h3", { children: "Revisions" }), _jsx("span", { className: "vertebr-count", children: loading ? "…" : `${items.length}` })] }), items.length === 0 && !loading ? (_jsx("p", { className: "vertebr-revisions-empty", children: "No revisions yet." })) : (_jsx("ul", { className: "vertebr-revisions-list", children: items.map((rev) => (_jsxs("li", { className: "vertebr-revisions-item", children: [_jsxs("div", { className: "vertebr-revisions-meta", children: [_jsx("strong", { children: rev.label?.trim() || rev.id }), _jsx("span", { "data-status": rev.status, children: rev.status }), _jsxs("span", { className: "vertebr-revisions-when", children: [formatWhen(rev.createdAt), rev.createdBy ? ` · ${rev.createdBy}` : ""] })] }), _jsxs("div", { className: "vertebr-revisions-actions", children: [rev.status !== "published" ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyId != null, onClick: () => void publish(rev.id), children: busyId === rev.id ? "…" : "Publish" })) : null, _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyId != null, onClick: () => void restore(rev.id), children: "Restore" })] })] }, rev.id))) }))] }));
|
|
84
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type UseFormReturn } from "react-hook-form";
|
|
2
|
-
import type { FieldSchema, ResourceUploadMeta } from "@vertebr/client";
|
|
2
|
+
import type { FieldSchema, ResourceUploadMeta, VertebrClient } from "@vertebr/client";
|
|
3
3
|
import type { ConfigFormVariant, FontLibraryHandlers, GalleryHandlers } from "./types";
|
|
4
4
|
export type ConfigFieldProps = GalleryHandlers & FontLibraryHandlers & {
|
|
5
5
|
fieldKey: string;
|
|
@@ -19,6 +19,7 @@ export type ConfigFieldProps = GalleryHandlers & FontLibraryHandlers & {
|
|
|
19
19
|
visibilityGroup?: string;
|
|
20
20
|
/** Render boolean fields as rows in a settings list (config forms). */
|
|
21
21
|
settingsRow?: boolean;
|
|
22
|
+
client?: VertebrClient;
|
|
22
23
|
};
|
|
23
|
-
export declare function ConfigField({ fieldKey, field, groupLabel, variant, schema, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, hideImagePreview, hideUpload, visibilityGroup, settingsRow, }: ConfigFieldProps): import("react").JSX.Element;
|
|
24
|
+
export declare function ConfigField({ fieldKey, field, groupLabel, variant, schema, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, hideImagePreview, hideUpload, visibilityGroup, settingsRow, client, }: ConfigFieldProps): import("react").JSX.Element;
|
|
24
25
|
//# sourceMappingURL=ConfigField.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigField.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigField.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"ConfigField.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigField.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAsBtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEvF,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAC5C,mBAAmB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,KAAK,EACL,UAAU,EACV,OAAO,EACP,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,KAAK,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,WAAW,EACX,MAAM,GACP,EAAE,gBAAgB,+BAkTlB"}
|
|
@@ -12,8 +12,9 @@ import { DescriptionInput } from "../DescriptionInput";
|
|
|
12
12
|
import { EnumInput } from "../EnumInput";
|
|
13
13
|
import { EnumListInput } from "../EnumListInput";
|
|
14
14
|
import { PriceInput } from "../PriceInput";
|
|
15
|
+
import { ReferenceInput } from "../ReferenceInput";
|
|
15
16
|
import { displayLabel, firstImageUrl, isMultilineField, fieldVisibilityKey, spanFor, } from "./helpers";
|
|
16
|
-
export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, hideImagePreview, hideUpload, visibilityGroup, settingsRow, }) {
|
|
17
|
+
export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, hideImagePreview, hideUpload, visibilityGroup, settingsRow, client, }) {
|
|
17
18
|
const label = displayLabel(fieldKey, field, groupLabel);
|
|
18
19
|
const visibilityKey = fieldVisibilityKey(fieldKey, schema, visibilityGroup ?? groupLabel);
|
|
19
20
|
const visibilityOn = !visibilityKey || watched?.[visibilityKey] !== false;
|
|
@@ -38,7 +39,7 @@ export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watc
|
|
|
38
39
|
: "", onBlur: controllerField.onBlur, onChange: (event) => {
|
|
39
40
|
const next = event.target.value;
|
|
40
41
|
controllerField.onChange(next === "" ? undefined : Number(next));
|
|
41
|
-
}, name: controllerField.name, ref: controllerField.ref })) })) : field.type === "enum" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "enum[]" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumListInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "font" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(FontPickerField, { id: fieldKey, value: controllerField.value, label: field.label, description: field.description, readOnly: field.readOnly, fonts: fonts ?? [], loading: fontsLoading, onChange: controllerField.onChange, onRefreshFonts: onRefreshFonts })) })) : isMultilineField(fieldKey, field) ? (_jsx("textarea", { id: fieldKey, rows: 5, readOnly: field.readOnly, ...form.register(fieldKey) })) : (_jsx("input", { id: fieldKey, type: "text", readOnly: field.readOnly, ...form.register(fieldKey) })), !hideUpload && uploads?.[fieldKey] && onUpload && !field.readOnly ? (_jsx(ImageUploadControl, { meta: uploads[fieldKey], multiple: field.type === "file[]", onUpload: async (files) => {
|
|
42
|
+
}, name: controllerField.name, ref: controllerField.ref })) })) : field.type === "enum" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "enum[]" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumListInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "font" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(FontPickerField, { id: fieldKey, value: controllerField.value, label: field.label, description: field.description, readOnly: field.readOnly, fonts: fonts ?? [], loading: fontsLoading, onChange: controllerField.onChange, onRefreshFonts: onRefreshFonts })) })) : field.type === "reference" && client ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(ReferenceInput, { id: fieldKey, value: controllerField.value, field: field, client: client, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : isMultilineField(fieldKey, field) ? (_jsx("textarea", { id: fieldKey, rows: 5, readOnly: field.readOnly, ...form.register(fieldKey) })) : (_jsx("input", { id: fieldKey, type: "text", readOnly: field.readOnly, ...form.register(fieldKey) })), !hideUpload && uploads?.[fieldKey] && onUpload && !field.readOnly ? (_jsx(ImageUploadControl, { meta: uploads[fieldKey], multiple: field.type === "file[]", onUpload: async (files) => {
|
|
42
43
|
const uploaded = await onUpload(fieldKey, files);
|
|
43
44
|
const urls = uploaded
|
|
44
45
|
.map((item) => item.url)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ConfigFormProps } from "./types";
|
|
2
2
|
export type { ConfigFormProps, ConfigFormVariant } from "./types";
|
|
3
|
-
export declare function ConfigForm({ schema, value, onSave, submitLabel, variant, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, onChange, hideActions, }: ConfigFormProps): import("react").JSX.Element;
|
|
3
|
+
export declare function ConfigForm({ schema, value, onSave, submitLabel, variant, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, onChange, hideActions, client, canWrite, }: ConfigFormProps): import("react").JSX.Element;
|
|
4
4
|
//# sourceMappingURL=ConfigForm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigForm.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAElE,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAkB,EAClB,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,KAAK,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,QAAQ,EACR,WAAW,
|
|
1
|
+
{"version":3,"file":"ConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigForm.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAElE,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAkB,EAClB,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,KAAK,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,QAAQ,EACR,WAAW,EACX,MAAM,EACN,QAAe,GAChB,EAAE,eAAe,+BA2IjB"}
|