@vertebr/react 0.6.0 → 0.8.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/AdminContent.d.ts +2 -1
- package/dist/components/admin/AdminContent.d.ts.map +1 -1
- package/dist/components/admin/AdminContent.js +10 -2
- package/dist/components/admin/AdminHeader.d.ts +3 -2
- package/dist/components/admin/AdminHeader.d.ts.map +1 -1
- package/dist/components/admin/AdminHeader.js +5 -3
- package/dist/components/admin/AdminSaveBar.d.ts +2 -0
- package/dist/components/admin/AdminSaveBar.d.ts.map +1 -0
- package/dist/components/admin/AdminSaveBar.js +10 -0
- package/dist/components/admin/AdminSidebar.d.ts +2 -20
- package/dist/components/admin/AdminSidebar.d.ts.map +1 -1
- package/dist/components/admin/AdminSidebar.js +8 -12
- package/dist/components/admin/AdminToastStack.d.ts +2 -0
- package/dist/components/admin/AdminToastStack.d.ts.map +1 -0
- package/dist/components/admin/AdminToastStack.js +8 -0
- package/dist/components/admin/AdminTopBar.d.ts +24 -0
- package/dist/components/admin/AdminTopBar.d.ts.map +1 -0
- package/dist/components/admin/AdminTopBar.js +7 -0
- package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
- package/dist/components/admin/VertebrAdmin.js +15 -1
- package/dist/components/admin/breadcrumbs.d.ts +8 -0
- package/dist/components/admin/breadcrumbs.d.ts.map +1 -0
- package/dist/components/admin/breadcrumbs.js +44 -0
- package/dist/components/admin/routes.d.ts.map +1 -1
- package/dist/components/admin/routes.js +9 -2
- package/dist/components/admin/types.d.ts +6 -0
- package/dist/components/admin/types.d.ts.map +1 -1
- package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/ConfigForm.js +44 -7
- package/dist/components/fonts/FontPickerField.d.ts.map +1 -1
- package/dist/components/fonts/FontPickerField.js +12 -1
- package/dist/components/resources/ResourceTable.d.ts +3 -1
- package/dist/components/resources/ResourceTable.d.ts.map +1 -1
- package/dist/components/resources/ResourceTable.js +20 -5
- package/dist/components/ui/icons.d.ts +15 -0
- package/dist/components/ui/icons.d.ts.map +1 -0
- package/dist/components/ui/icons.js +60 -0
- package/dist/components/ui/primitives.d.ts +27 -0
- package/dist/components/ui/primitives.d.ts.map +1 -0
- package/dist/components/ui/primitives.js +16 -0
- package/dist/hooks/useSaveBar.d.ts +21 -0
- package/dist/hooks/useSaveBar.d.ts.map +1 -0
- package/dist/hooks/useSaveBar.js +30 -0
- package/dist/hooks/useToast.d.ts +18 -0
- package/dist/hooks/useToast.d.ts.map +1 -0
- package/dist/hooks/useToast.js +23 -0
- package/dist/styles/base.css +8 -14
- package/dist/styles/buttons.css +3 -12
- package/dist/styles/index.css +1 -0
- package/dist/styles/main.css +3 -7
- package/dist/styles/nav.css +3 -1
- package/dist/styles/shell.css +314 -0
- package/dist/styles/sidebar.css +26 -5
- package/dist/themes/dark.css +48 -46
- package/dist/themes/index.css +1 -0
- package/dist/themes/light.css +91 -85
- package/dist/themes/tokens.css +52 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ export type AdminContentProps = {
|
|
|
5
5
|
client: VertebrClient;
|
|
6
6
|
catalog: CatalogResponse | null;
|
|
7
7
|
selection: Selection;
|
|
8
|
+
onSelectionChange?: (next: Selection) => void;
|
|
8
9
|
authRequired: boolean;
|
|
9
10
|
loading: boolean;
|
|
10
11
|
isPageSelection: boolean;
|
|
@@ -41,5 +42,5 @@ export type AdminContentProps = {
|
|
|
41
42
|
onRefreshFonts?: () => Promise<void>;
|
|
42
43
|
};
|
|
43
44
|
};
|
|
44
|
-
export declare function AdminContent({ client, catalog, selection, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, onRefreshResource, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }: AdminContentProps): import("react").JSX.Element;
|
|
45
|
+
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, onRefreshResource, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }: AdminContentProps): import("react").JSX.Element;
|
|
45
46
|
//# sourceMappingURL=AdminContent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminContent.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminContent.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,aAAa,EACd,MAAM,iBAAiB,CAAC;AAOzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;IAC7B,oBAAoB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACrD,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,WAAW,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,eAAe,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC5C,kBAAkB,EAAE,OAAO,CAAC;IAC5B,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACvC,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC1C,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,UAAU,EAAE,cAAc,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,aAAa,EAAE;QACb,aAAa,CAAC,EAAE;YACd,MAAM,EAAE,MAAM,EAAE,CAAC;YACjB,QAAQ,EAAE,MAAM,CAAC;SAClB,CAAC;QACF,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;QACxD,eAAe,CAAC,EAAE,CAChB,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;KACxC,CAAC;IACF,iBAAiB,EAAE;QACjB,KAAK,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;QACrC,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1D,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;KACtC,CAAC;CACH,CAAC;AA6EF,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,OAAO,EACP,SAAS,EACT,YAAY,EACZ,OAAO,EACP,eAAe,EACf,WAAW,EACX,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,aAAa,EACb,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,aAAa,EACb,iBAAiB,GAClB,EAAE,iBAAiB,+
|
|
1
|
+
{"version":3,"file":"AdminContent.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminContent.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,aAAa,EACd,MAAM,iBAAiB,CAAC;AAOzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,SAAS,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAC9C,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;IAC7B,oBAAoB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACrD,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,WAAW,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,eAAe,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC5C,kBAAkB,EAAE,OAAO,CAAC;IAC5B,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACvC,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC1C,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,UAAU,EAAE,cAAc,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,aAAa,EAAE;QACb,aAAa,CAAC,EAAE;YACd,MAAM,EAAE,MAAM,EAAE,CAAC;YACjB,QAAQ,EAAE,MAAM,CAAC;SAClB,CAAC;QACF,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;QACxD,eAAe,CAAC,EAAE,CAChB,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;KACxC,CAAC;IACF,iBAAiB,EAAE;QACjB,KAAK,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;QACrC,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1D,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;KACtC,CAAC;CACH,CAAC;AA6EF,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,OAAO,EACP,eAAe,EACf,WAAW,EACX,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,aAAa,EACb,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,aAAa,EACb,iBAAiB,GAClB,EAAE,iBAAiB,+BAmRnB"}
|
|
@@ -19,7 +19,7 @@ function ConfigEditor({ client, configData, isPageSelection, previewPath, onConf
|
|
|
19
19
|
}, ...galleryPicker, ...fontLibraryPicker }));
|
|
20
20
|
return (_jsxs("div", { className: "vertebr-config-editor", children: [showPreview ? (_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: isPageSelection ? "Page content" : configData.label }), _jsxs("span", { className: "vertebr-count", children: [Object.keys(configData.schema).length, " fields"] })] })) : isPageSelection ? (_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: "Page content" }), _jsxs("span", { className: "vertebr-count", children: [Object.keys(configData.schema).length, " fields"] })] })) : 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 })] })) : (form)] }));
|
|
21
21
|
}
|
|
22
|
-
export function AdminContent({ client, catalog, selection, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, onRefreshResource, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }) {
|
|
22
|
+
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, onRefreshResource, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }) {
|
|
23
23
|
if (!catalog) {
|
|
24
24
|
return (_jsx("div", { className: "vertebr-content", children: _jsx("section", { className: "vertebr-panel", children: _jsx("div", { className: "vertebr-empty", children: authRequired
|
|
25
25
|
? "Authenticated access required for this environment."
|
|
@@ -97,7 +97,15 @@ export function AdminContent({ client, catalog, selection, authRequired, loading
|
|
|
97
97
|
? async (id) => {
|
|
98
98
|
await client.deleteEmailLibraryTemplate(id);
|
|
99
99
|
}
|
|
100
|
-
: undefined })) : null, selection?.kind === "resource" && resourceData ? (_jsx(ResourcePanel, { schema: resourceData.schema, items: resourceData.items, label: resourceData.label,
|
|
100
|
+
: undefined })) : null, selection?.kind === "resource" && resourceData ? (_jsx(ResourcePanel, { schema: resourceData.schema, items: resourceData.items, label: resourceData.label, initialEditRowId: selection.rowId && selection.id === resourceData.id
|
|
101
|
+
? selection.rowId
|
|
102
|
+
: undefined, onEditRowIdChange: (rowId) => {
|
|
103
|
+
onSelectionChange?.({
|
|
104
|
+
kind: "resource",
|
|
105
|
+
id: resourceData.id,
|
|
106
|
+
rowId: rowId ?? undefined,
|
|
107
|
+
});
|
|
108
|
+
}, permissions: resourceData.permissions, actions: resourceData.actions, collectionActions: resourceData.collectionActions, uploads: resourceData.uploads, storageKey: resourceData.id, onUpdate: resourceData.permissions.update
|
|
101
109
|
? async (rowId, value) => {
|
|
102
110
|
await client.updateResource(resourceData.id, rowId, value);
|
|
103
111
|
await onRefreshResource();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AdminEnvironment } from "@vertebr/client";
|
|
2
|
+
import type { Breadcrumb } from "./breadcrumbs";
|
|
2
3
|
import type { Selection } from "./types";
|
|
3
4
|
export type AdminHeaderProps = {
|
|
4
5
|
activeLabel: string;
|
|
@@ -7,9 +8,9 @@ export type AdminHeaderProps = {
|
|
|
7
8
|
authRequired: boolean;
|
|
8
9
|
selection: Selection;
|
|
9
10
|
isPageSelection: boolean;
|
|
10
|
-
/** True when the active page/layout has a host previewPath. */
|
|
11
11
|
hasPagePreview?: boolean;
|
|
12
12
|
error: string | null;
|
|
13
|
+
breadcrumbs: Breadcrumb[];
|
|
13
14
|
};
|
|
14
|
-
export declare function AdminHeader({ activeLabel, activeEnv, catalogLoaded, authRequired, selection, isPageSelection, hasPagePreview, error, }: AdminHeaderProps): import("react").JSX.Element;
|
|
15
|
+
export declare function AdminHeader({ activeLabel, activeEnv, catalogLoaded, authRequired, selection, isPageSelection, hasPagePreview, error, breadcrumbs, }: AdminHeaderProps): import("react").JSX.Element;
|
|
15
16
|
//# sourceMappingURL=AdminHeader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminHeader.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;IACzB
|
|
1
|
+
{"version":3,"file":"AdminHeader.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,CAAC;AAiDF,wBAAgB,WAAW,CAAC,EAC1B,WAAW,EACX,SAAS,EACT,aAAa,EACb,YAAY,EACZ,SAAS,EACT,eAAe,EACf,cAAc,EACd,KAAK,EACL,WAAW,GACZ,EAAE,gBAAgB,+BA6ClB"}
|
|
@@ -16,7 +16,9 @@ function subtitle({ catalogLoaded, authRequired, selection, isPageSelection, has
|
|
|
16
16
|
return "Send or schedule branded email to a list or specific people.";
|
|
17
17
|
}
|
|
18
18
|
if (selection?.kind === "resource") {
|
|
19
|
-
return
|
|
19
|
+
return selection.rowId
|
|
20
|
+
? "Edit this record — changes apply to the selected environment."
|
|
21
|
+
: "Manage records scoped to the selected environment.";
|
|
20
22
|
}
|
|
21
23
|
if (selection?.kind === "config") {
|
|
22
24
|
if (hasPagePreview) {
|
|
@@ -30,8 +32,8 @@ function subtitle({ catalogLoaded, authRequired, selection, isPageSelection, has
|
|
|
30
32
|
}
|
|
31
33
|
return "Select a panel.";
|
|
32
34
|
}
|
|
33
|
-
export function AdminHeader({ activeLabel, activeEnv, catalogLoaded, authRequired, selection, isPageSelection, hasPagePreview, error, }) {
|
|
34
|
-
return (_jsxs("header", { className: "vertebr-header", children: [_jsxs("div", { children: [_jsxs("div", { className: "vertebr-heading-row", children: [_jsx("h1", { children: activeLabel }), activeEnv ? (_jsx("span", { className: "vertebr-env-badge", "data-tone": activeEnv.tone ?? "neutral", children: activeEnv.label })) : null] }), _jsx("p", { children: subtitle({
|
|
35
|
+
export function AdminHeader({ activeLabel, activeEnv, catalogLoaded, authRequired, selection, isPageSelection, hasPagePreview, error, breadcrumbs, }) {
|
|
36
|
+
return (_jsxs("header", { className: "vertebr-header", children: [_jsxs("div", { children: [breadcrumbs.length > 1 ? (_jsx("ol", { className: "vertebr-breadcrumbs", children: breadcrumbs.map((crumb, index) => (_jsx("li", { children: index === breadcrumbs.length - 1 ? (_jsx("strong", { children: crumb.label })) : (crumb.label) }, `${crumb.label}-${index}`))) })) : null, _jsxs("div", { className: "vertebr-heading-row", children: [_jsx("h1", { children: activeLabel }), activeEnv ? (_jsx("span", { className: "vertebr-env-badge", "data-tone": activeEnv.tone ?? "neutral", children: activeEnv.label })) : null] }), _jsx("p", { children: subtitle({
|
|
35
37
|
catalogLoaded,
|
|
36
38
|
authRequired,
|
|
37
39
|
selection,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminSaveBar.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminSaveBar.tsx"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,uCAmC3B"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useSaveBar } from "../../hooks/useSaveBar";
|
|
3
|
+
export function AdminSaveBar() {
|
|
4
|
+
const { state } = useSaveBar();
|
|
5
|
+
if (!state.visible)
|
|
6
|
+
return null;
|
|
7
|
+
return (_jsxs("div", { className: "vertebr-savebar", "data-tone": state.tone, children: [_jsx("div", { className: "vertebr-savebar-message", children: state.saving
|
|
8
|
+
? "Saving…"
|
|
9
|
+
: state.message ?? (state.isDirty ? "Unsaved changes" : "Saved") }), _jsxs("div", { className: "vertebr-savebar-actions", children: [state.onDiscard && state.isDirty ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: state.saving, onClick: state.onDiscard, children: "Discard" })) : null, state.onSave ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", disabled: state.saving || !state.isDirty, onClick: () => void state.onSave?.(), children: "Save" })) : null] })] }));
|
|
10
|
+
}
|
|
@@ -1,25 +1,7 @@
|
|
|
1
|
-
import type { AdminEnvironment } from "@vertebr/client";
|
|
2
|
-
import type { VbTheme } from "../../hooks/useVertebrTheme";
|
|
3
1
|
import type { SidebarNavGroup } from "./types";
|
|
4
2
|
export type AdminSidebarProps = {
|
|
5
|
-
brand: string;
|
|
6
|
-
title: string;
|
|
7
|
-
envList: AdminEnvironment[];
|
|
8
|
-
environment: string;
|
|
9
|
-
onEnvironmentChange: (nextEnv: string) => void;
|
|
10
3
|
navGroups: SidebarNavGroup[];
|
|
11
|
-
|
|
12
|
-
authUserLabel?: string;
|
|
13
|
-
onSignOut?: () => void | Promise<void>;
|
|
14
|
-
authRequired: boolean;
|
|
15
|
-
activeEnv?: AdminEnvironment;
|
|
16
|
-
token: string;
|
|
17
|
-
tokenDraft: string;
|
|
18
|
-
onTokenDraftChange: (value: string) => void;
|
|
19
|
-
onSaveToken: () => void;
|
|
20
|
-
onClearToken: () => void;
|
|
21
|
-
theme: VbTheme;
|
|
22
|
-
onThemeChange: (theme: VbTheme) => void;
|
|
4
|
+
collapsed: boolean;
|
|
23
5
|
};
|
|
24
|
-
export declare function AdminSidebar({
|
|
6
|
+
export declare function AdminSidebar({ navGroups, collapsed }: AdminSidebarProps): import("react").JSX.Element;
|
|
25
7
|
//# sourceMappingURL=AdminSidebar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminSidebar.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminSidebar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"AdminSidebar.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminSidebar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,SAAS,CAAC;AAG7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAwFF,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,iBAAiB,+BAuBvE"}
|
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
function SidebarNavItem({ item }) {
|
|
3
|
+
import { iconForNavKey } from "../ui/icons";
|
|
4
|
+
function SidebarNavItem({ item, collapsed, }) {
|
|
6
5
|
const hasChildren = Boolean(item.children?.length);
|
|
7
6
|
const childActive = item.children?.some((child) => child.active) ?? false;
|
|
8
7
|
const sectionActive = item.active || childActive;
|
|
9
8
|
const [open, setOpen] = useState(sectionActive);
|
|
9
|
+
const Icon = item.icon ?? iconForNavKey(item.key);
|
|
10
10
|
useEffect(() => {
|
|
11
11
|
if (sectionActive)
|
|
12
12
|
setOpen(true);
|
|
13
13
|
}, [sectionActive]);
|
|
14
14
|
if (!hasChildren) {
|
|
15
|
-
return (
|
|
15
|
+
return (_jsxs("button", { type: "button", "data-active": item.active, title: collapsed ? item.label : undefined, onClick: item.onSelect, children: [_jsx(Icon, { className: "vertebr-nav-icon" }), _jsx("span", { className: "vertebr-nav-label-text", children: item.label })] }));
|
|
16
16
|
}
|
|
17
|
-
return (_jsxs("div", { className: "vertebr-nav-item", "data-has-children": "true", "data-open": open || undefined, children: [_jsxs("div", { className: "vertebr-nav-parent", children: [
|
|
17
|
+
return (_jsxs("div", { className: "vertebr-nav-item", "data-has-children": "true", "data-open": open || undefined, children: [_jsxs("div", { className: "vertebr-nav-parent", children: [_jsxs("button", { type: "button", className: "vertebr-nav-parent-label", "data-active": sectionActive && !childActive, "data-section": sectionActive || undefined, title: collapsed ? item.label : undefined, onClick: () => {
|
|
18
18
|
setOpen(true);
|
|
19
19
|
item.onSelect();
|
|
20
|
-
}, children: item.label }), _jsx("button", { type: "button", className: "vertebr-nav-parent-toggle", "aria-label": open ? `Collapse ${item.label}` : `Expand ${item.label}`, "aria-expanded": open, onClick: () => setOpen((prev) => !prev), children: _jsx("span", { "aria-hidden": "true", children: open ? "▾" : "▸" }) })] }), open ? (_jsx("div", { className: "vertebr-nav-sub", role: "group", children: item.children.map((child) => (_jsx("button", { type: "button", className: "vertebr-nav-subitem", "data-active": child.active, onClick: () => {
|
|
20
|
+
}, children: [_jsx(Icon, { className: "vertebr-nav-icon" }), _jsx("span", { className: "vertebr-nav-label-text", children: item.label })] }), !collapsed ? (_jsx("button", { type: "button", className: "vertebr-nav-parent-toggle", "aria-label": open ? `Collapse ${item.label}` : `Expand ${item.label}`, "aria-expanded": open, onClick: () => setOpen((prev) => !prev), children: _jsx("span", { "aria-hidden": "true", children: open ? "▾" : "▸" }) })) : null] }), open && !collapsed ? (_jsx("div", { className: "vertebr-nav-sub", role: "group", children: item.children.map((child) => (_jsx("button", { type: "button", className: "vertebr-nav-subitem", "data-active": child.active, onClick: () => {
|
|
21
21
|
setOpen(true);
|
|
22
22
|
child.onSelect();
|
|
23
23
|
}, children: child.label }, child.key))) })) : null] }));
|
|
24
24
|
}
|
|
25
|
-
export function AdminSidebar({
|
|
26
|
-
return (
|
|
27
|
-
? `This environment requires auth${activeEnv?.auth?.claimKey
|
|
28
|
-
? ` (${activeEnv.auth.claimKey})`
|
|
29
|
-
: ""}.`
|
|
30
|
-
: "Optional access token for protected environments." }), _jsx("input", { className: "vertebr-auth-input", type: "password", placeholder: "Bearer token", value: tokenDraft, onChange: (event) => onTokenDraftChange(event.target.value), autoComplete: "off" }), _jsxs("div", { className: "vertebr-auth-actions", children: [_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", onClick: onSaveToken, children: "Use token" }), token ? (_jsx("button", { type: "button", className: "vertebr-btn", onClick: onClearToken, children: "Clear" })) : null] })] })) : null, _jsx(ThemeToggle, { theme: theme, onChange: onThemeChange })] })] }));
|
|
25
|
+
export function AdminSidebar({ navGroups, collapsed }) {
|
|
26
|
+
return (_jsx("aside", { className: "vertebr-sidebar", "data-collapsed": collapsed || undefined, children: _jsx("div", { className: "vertebr-sidebar-top", children: _jsx("nav", { className: "vertebr-nav", children: navGroups.map((group) => (_jsxs("div", { className: "vertebr-nav-group", children: [!collapsed ? (_jsx("div", { className: "vertebr-nav-label", children: group.label })) : null, group.items.map((item) => (_jsx(SidebarNavItem, { item: item, collapsed: collapsed }, item.key)))] }, group.label))) }) }) }));
|
|
31
27
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminToastStack.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminToastStack.tsx"],"names":[],"mappings":"AAEA,wBAAgB,eAAe,uCA0B9B"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useToast } from "../../hooks/useToast";
|
|
3
|
+
export function AdminToastStack() {
|
|
4
|
+
const { toasts, dismiss } = useToast();
|
|
5
|
+
if (!toasts.length)
|
|
6
|
+
return null;
|
|
7
|
+
return (_jsx("div", { className: "vertebr-toast-stack", "aria-live": "polite", children: toasts.map((toast) => (_jsxs("div", { className: "vertebr-toast", "data-tone": toast.tone, role: "status", children: [_jsx("span", { children: toast.message }), _jsx("button", { type: "button", className: "vertebr-toast-dismiss", "aria-label": "Dismiss", onClick: () => dismiss(toast.id), children: "\u00D7" })] }, toast.id))) }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AdminEnvironment } from "@vertebr/client";
|
|
2
|
+
import type { VbTheme } from "../../hooks/useVertebrTheme";
|
|
3
|
+
export type AdminTopBarProps = {
|
|
4
|
+
brand: string;
|
|
5
|
+
envList: AdminEnvironment[];
|
|
6
|
+
environment: string;
|
|
7
|
+
onEnvironmentChange: (nextEnv: string) => void;
|
|
8
|
+
activeEnv?: AdminEnvironment;
|
|
9
|
+
authUserLabel?: string;
|
|
10
|
+
onSignOut?: () => void | Promise<void>;
|
|
11
|
+
getAccessToken?: () => string | null | undefined | Promise<string | null | undefined>;
|
|
12
|
+
authRequired: boolean;
|
|
13
|
+
token: string;
|
|
14
|
+
tokenDraft: string;
|
|
15
|
+
onTokenDraftChange: (value: string) => void;
|
|
16
|
+
onSaveToken: () => void;
|
|
17
|
+
onClearToken: () => void;
|
|
18
|
+
theme: VbTheme;
|
|
19
|
+
onThemeChange: (theme: VbTheme) => void;
|
|
20
|
+
sidebarCollapsed: boolean;
|
|
21
|
+
onToggleSidebar: () => void;
|
|
22
|
+
};
|
|
23
|
+
export declare function AdminTopBar({ brand, envList, environment, onEnvironmentChange, activeEnv, authUserLabel, onSignOut, getAccessToken, authRequired, token, tokenDraft, onTokenDraftChange, onSaveToken, onClearToken, theme, onThemeChange, sidebarCollapsed, onToggleSidebar, }: AdminTopBarProps): import("react").JSX.Element;
|
|
24
|
+
//# sourceMappingURL=AdminTopBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminTopBar.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminTopBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAG3D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,cAAc,CAAC,EAAE,MACb,MAAM,GACN,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACvC,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,MAAM,IAAI,CAAC;CAC7B,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,WAAW,EACX,mBAAmB,EACnB,SAAS,EACT,aAAa,EACb,SAAS,EACT,cAAc,EACd,YAAY,EACZ,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,KAAK,EACL,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,EAAE,gBAAgB,+BAsFlB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { EnvSelect } from "../env/EnvSelect";
|
|
3
|
+
import { ThemeToggle } from "../theme/ThemeToggle";
|
|
4
|
+
import { IconSearch } from "../ui/icons";
|
|
5
|
+
export function AdminTopBar({ brand, envList, environment, onEnvironmentChange, activeEnv, authUserLabel, onSignOut, getAccessToken, authRequired, token, tokenDraft, onTokenDraftChange, onSaveToken, onClearToken, theme, onThemeChange, sidebarCollapsed, onToggleSidebar, }) {
|
|
6
|
+
return (_jsxs("header", { className: "vertebr-topbar", children: [_jsxs("div", { className: "vertebr-topbar-start", children: [_jsx("button", { type: "button", className: "vertebr-topbar-toggle", "aria-label": sidebarCollapsed ? "Expand sidebar" : "Collapse sidebar", onClick: onToggleSidebar, children: "\u2630" }), _jsx("strong", { className: "vertebr-topbar-brand", children: brand }), envList.length > 0 ? (_jsxs("div", { className: "vertebr-topbar-env", children: [_jsx(EnvSelect, { environments: envList, value: environment, onChange: onEnvironmentChange }), activeEnv ? (_jsx("span", { className: "vertebr-env-badge", "data-tone": activeEnv.tone ?? "neutral", children: activeEnv.label })) : null] })) : null] }), _jsxs("div", { className: "vertebr-topbar-search", "aria-hidden": "true", children: [_jsx(IconSearch, {}), _jsx("span", { children: "Search admin" }), _jsx("kbd", { children: "\u2318K" })] }), _jsxs("div", { className: "vertebr-topbar-end", children: [!getAccessToken && (authRequired || token) ? (_jsxs("div", { className: "vertebr-topbar-token", children: [_jsx("input", { className: "vertebr-auth-input", type: "password", placeholder: "Access token", value: tokenDraft, onChange: (event) => onTokenDraftChange(event.target.value), autoComplete: "off" }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: onSaveToken, children: "Use" }), token ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: onClearToken, children: "Clear" })) : null] })) : null, getAccessToken && authUserLabel ? (_jsx("span", { className: "vertebr-topbar-user", children: authUserLabel })) : null, onSignOut ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => void onSignOut(), children: "Sign out" })) : null, _jsx(ThemeToggle, { theme: theme, onChange: onThemeChange })] })] }));
|
|
7
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VertebrAdmin.d.ts","sourceRoot":"","sources":["../../../src/components/admin/VertebrAdmin.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VertebrAdmin.d.ts","sourceRoot":"","sources":["../../../src/components/admin/VertebrAdmin.tsx"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAGV,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,wBAAgB,YAAY,CAAC,EAC3B,OAAsB,EACtB,QAAQ,EAAE,YAAY,EACtB,KAAuB,EACvB,aAA6B,EAC7B,eAAiC,EACjC,eAAiC,EACjC,sBAAgD,EAChD,cAAc,EACd,aAAa,EACb,SAAS,GACV,EAAE,iBAAiB,+BAskBnB"}
|
|
@@ -5,10 +5,17 @@ import { useVertebrTheme } from "../../hooks/useVertebrTheme";
|
|
|
5
5
|
import { parseMetricsRangeId, } from "../../lib/metricsRanges";
|
|
6
6
|
import { AdminContent } from "./AdminContent";
|
|
7
7
|
import { AdminHeader } from "./AdminHeader";
|
|
8
|
+
import { AdminSaveBar } from "./AdminSaveBar";
|
|
8
9
|
import { AdminSidebar } from "./AdminSidebar";
|
|
10
|
+
import { AdminToastStack } from "./AdminToastStack";
|
|
11
|
+
import { AdminTopBar } from "./AdminTopBar";
|
|
12
|
+
import { breadcrumbsForSelection } from "./breadcrumbs";
|
|
9
13
|
import { adminBaseFromApiBase, coerceSelection, defaultSelection, hrefForSelection, selectionFromPath, selectionsEqual, } from "./routes";
|
|
10
14
|
import { sidebarGroups } from "./sidebar";
|
|
11
15
|
import { clearStored, readStored, writeStored } from "./storage";
|
|
16
|
+
import { SaveBarProvider } from "../../hooks/useSaveBar";
|
|
17
|
+
import { ToastProvider } from "../../hooks/useToast";
|
|
18
|
+
import { iconForNavKey } from "../ui/icons";
|
|
12
19
|
export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, title = "Control plane", envStorageKey = "vertebr:env", tokenStorageKey = "vertebr:token", themeStorageKey = "vertebr:theme", metricsRangeStorageKey = "vertebr:metrics-range", getAccessToken, authUserLabel, onSignOut, }) {
|
|
13
20
|
const basePath = basePathProp ?? adminBaseFromApiBase(apiBase);
|
|
14
21
|
const { theme, setTheme } = useVertebrTheme(themeStorageKey);
|
|
@@ -43,6 +50,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
43
50
|
const [emailsLoading, setEmailsLoading] = useState(false);
|
|
44
51
|
const [error, setError] = useState(null);
|
|
45
52
|
const [loading, setLoading] = useState(true);
|
|
53
|
+
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
|
46
54
|
/** Skip pushState when selection was applied from the URL (catalog/popstate). */
|
|
47
55
|
const skipNextUrlPush = useRef(true);
|
|
48
56
|
const syncUrl = useCallback((next, catalogSnapshot, replace = false) => {
|
|
@@ -371,6 +379,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
371
379
|
key: "overview",
|
|
372
380
|
group: catalog.metrics.group ?? "Analytics",
|
|
373
381
|
label: catalog.metrics.label,
|
|
382
|
+
icon: iconForNavKey("overview"),
|
|
374
383
|
active: selection?.kind === "overview",
|
|
375
384
|
onSelect: () => setSelection({ kind: "overview" }),
|
|
376
385
|
});
|
|
@@ -380,6 +389,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
380
389
|
key: `resource:${resource.id}`,
|
|
381
390
|
group: resource.group ?? "Resources",
|
|
382
391
|
label: resource.label,
|
|
392
|
+
icon: iconForNavKey(`resource:${resource.id}`),
|
|
383
393
|
active: selection?.kind === "resource" && selection.id === resource.id,
|
|
384
394
|
onSelect: () => setSelection({ kind: "resource", id: resource.id }),
|
|
385
395
|
});
|
|
@@ -389,6 +399,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
389
399
|
key: "gallery",
|
|
390
400
|
group: catalog.gallery.group ?? "Resources",
|
|
391
401
|
label: catalog.gallery.label,
|
|
402
|
+
icon: iconForNavKey("gallery"),
|
|
392
403
|
active: selection?.kind === "gallery",
|
|
393
404
|
onSelect: () => setSelection({ kind: "gallery" }),
|
|
394
405
|
});
|
|
@@ -398,6 +409,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
398
409
|
key: "fonts",
|
|
399
410
|
group: catalog.fontLibrary.group ?? "Resources",
|
|
400
411
|
label: catalog.fontLibrary.label,
|
|
412
|
+
icon: iconForNavKey("fonts"),
|
|
401
413
|
active: selection?.kind === "fontLibrary",
|
|
402
414
|
onSelect: () => setSelection({ kind: "fontLibrary" }),
|
|
403
415
|
});
|
|
@@ -408,6 +420,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
408
420
|
key: "emails",
|
|
409
421
|
group: catalog.emails.group ?? "Growth",
|
|
410
422
|
label: catalog.emails.label,
|
|
423
|
+
icon: iconForNavKey("emails"),
|
|
411
424
|
active: selection?.kind === "emails",
|
|
412
425
|
onSelect: () => setSelection({ kind: "emails", tab: "compose" }),
|
|
413
426
|
children: [
|
|
@@ -438,6 +451,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
438
451
|
key: `config:${config.id}`,
|
|
439
452
|
group: config.group ?? fallback,
|
|
440
453
|
label: config.label,
|
|
454
|
+
icon: iconForNavKey(`config:${config.id}`),
|
|
441
455
|
active: selection?.kind === "config" && selection.id === config.id,
|
|
442
456
|
onSelect: () => setSelection({ kind: "config", id: config.id }),
|
|
443
457
|
});
|
|
@@ -463,5 +477,5 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
|
|
|
463
477
|
: selection?.kind === "config"
|
|
464
478
|
? catalog?.configs.find((c) => c.id === selection.id)?.label
|
|
465
479
|
: null;
|
|
466
|
-
return (_jsxs("div", { className: "vertebr-root", "data-env": environment, children: [_jsx(
|
|
480
|
+
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", envList: envList, environment: environment, onEnvironmentChange: switchEnvironment, activeEnv: activeEnv, 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) }), _jsx(AdminSidebar, { navGroups: navGroups, collapsed: sidebarCollapsed }), _jsxs("main", { className: "vertebr-main", children: [_jsx(AdminHeader, { activeLabel: activeLabel ?? (catalog ? "Dashboard" : "Sign in"), activeEnv: activeEnv, catalogLoaded: Boolean(catalog), authRequired: authRequired, selection: selection, isPageSelection: isPageSelection, hasPagePreview: hasPagePreview, 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, onRefreshResource: refreshResource, configData: configData, onConfigSaved: setConfigData, galleryPicker: galleryPicker, fontLibraryPicker: fontLibraryPicker }), _jsx(AdminSaveBar, {})] }), _jsx(AdminToastStack, {})] }) }) }));
|
|
467
481
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CatalogResponse } from "@vertebr/client";
|
|
2
|
+
import type { Selection } from "./types";
|
|
3
|
+
export type Breadcrumb = {
|
|
4
|
+
label: string;
|
|
5
|
+
href?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function breadcrumbsForSelection(selection: Selection, catalog: CatalogResponse | null): Breadcrumb[];
|
|
8
|
+
//# sourceMappingURL=breadcrumbs.d.ts.map
|
|
@@ -0,0 +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,CA6Cd"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function breadcrumbsForSelection(selection, catalog) {
|
|
2
|
+
const crumbs = [{ label: "Home", href: "/overview" }];
|
|
3
|
+
if (!selection || !catalog)
|
|
4
|
+
return crumbs;
|
|
5
|
+
switch (selection.kind) {
|
|
6
|
+
case "overview":
|
|
7
|
+
return [...crumbs, { label: catalog.metrics?.label ?? "Overview" }];
|
|
8
|
+
case "gallery":
|
|
9
|
+
return [...crumbs, { label: catalog.gallery?.label ?? "Gallery" }];
|
|
10
|
+
case "fontLibrary":
|
|
11
|
+
return [...crumbs, { label: catalog.fontLibrary?.label ?? "Fonts" }];
|
|
12
|
+
case "emails":
|
|
13
|
+
return [
|
|
14
|
+
...crumbs,
|
|
15
|
+
{ label: catalog.emails?.label ?? "Email" },
|
|
16
|
+
{
|
|
17
|
+
label: selection.tab === "templates"
|
|
18
|
+
? "Templates"
|
|
19
|
+
: selection.tab === "activity"
|
|
20
|
+
? "Activity"
|
|
21
|
+
: "Compose",
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
case "resource": {
|
|
25
|
+
const resource = catalog.resources.find((item) => item.id === selection.id);
|
|
26
|
+
const items = [
|
|
27
|
+
...crumbs,
|
|
28
|
+
{ label: resource?.group ?? "Products", href: undefined },
|
|
29
|
+
{ label: resource?.label ?? selection.id },
|
|
30
|
+
];
|
|
31
|
+
if (selection.rowId) {
|
|
32
|
+
items.push({ label: selection.rowId });
|
|
33
|
+
}
|
|
34
|
+
return items;
|
|
35
|
+
}
|
|
36
|
+
case "config": {
|
|
37
|
+
const config = catalog.configs.find((item) => item.id === selection.id);
|
|
38
|
+
const group = config?.group ?? (config?.section === "page" ? "Pages" : "Settings");
|
|
39
|
+
return [...crumbs, { label: group }, { label: config?.label ?? selection.id }];
|
|
40
|
+
}
|
|
41
|
+
default:
|
|
42
|
+
return crumbs;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -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,CA0BR;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,CAuClB;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,CAyBX;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAenE"}
|
|
@@ -32,6 +32,9 @@ export function pathForSelection(selection, catalog) {
|
|
|
32
32
|
return `/emails/${tab}`;
|
|
33
33
|
}
|
|
34
34
|
case "resource":
|
|
35
|
+
if (selection.rowId) {
|
|
36
|
+
return `/resources/${encodeURIComponent(selection.id)}/${encodeURIComponent(selection.rowId)}`;
|
|
37
|
+
}
|
|
35
38
|
return `/resources/${encodeURIComponent(selection.id)}`;
|
|
36
39
|
case "config": {
|
|
37
40
|
const config = catalog.configs.find((item) => item.id === selection.id);
|
|
@@ -86,6 +89,9 @@ export function selectionFromPath(pathname, basePath, catalog) {
|
|
|
86
89
|
if (catalog && !catalog.resources.some((item) => item.id === id)) {
|
|
87
90
|
return null;
|
|
88
91
|
}
|
|
92
|
+
if (tail[1]) {
|
|
93
|
+
return { kind: "resource", id, rowId: decodeURIComponent(tail[1]) };
|
|
94
|
+
}
|
|
89
95
|
return { kind: "resource", id };
|
|
90
96
|
}
|
|
91
97
|
if ((head === "pages" || head === "config") && tail[0]) {
|
|
@@ -162,8 +168,9 @@ export function selectionsEqual(a, b) {
|
|
|
162
168
|
if (a.kind === "emails" && b.kind === "emails") {
|
|
163
169
|
return (a.tab ?? "compose") === (b.tab ?? "compose");
|
|
164
170
|
}
|
|
165
|
-
if (a.kind === "resource" && b.kind === "resource")
|
|
166
|
-
return a.id === b.id;
|
|
171
|
+
if (a.kind === "resource" && b.kind === "resource") {
|
|
172
|
+
return a.id === b.id && (a.rowId ?? "") === (b.rowId ?? "");
|
|
173
|
+
}
|
|
167
174
|
if (a.kind === "config" && b.kind === "config")
|
|
168
175
|
return a.id === b.id;
|
|
169
176
|
return false;
|
|
@@ -22,12 +22,17 @@ export type VertebrAdminProps = {
|
|
|
22
22
|
/** Host-provided sign-out action. */
|
|
23
23
|
onSignOut?: () => void | Promise<void>;
|
|
24
24
|
};
|
|
25
|
+
import type { ReactElement } from "react";
|
|
25
26
|
export type EmailsTab = "compose" | "templates" | "activity";
|
|
27
|
+
export type NavIcon = (props: {
|
|
28
|
+
className?: string;
|
|
29
|
+
}) => ReactElement;
|
|
26
30
|
export type Selection = {
|
|
27
31
|
kind: "overview";
|
|
28
32
|
} | {
|
|
29
33
|
kind: "resource";
|
|
30
34
|
id: string;
|
|
35
|
+
rowId?: string;
|
|
31
36
|
} | {
|
|
32
37
|
kind: "gallery";
|
|
33
38
|
} | {
|
|
@@ -52,6 +57,7 @@ export type SidebarEntry = {
|
|
|
52
57
|
active: boolean;
|
|
53
58
|
onSelect: () => void;
|
|
54
59
|
}>;
|
|
60
|
+
icon?: NavIcon;
|
|
55
61
|
};
|
|
56
62
|
export type SidebarNavGroup = {
|
|
57
63
|
label: string;
|
|
@@ -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,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAE7D,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,
|
|
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,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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigForm.tsx"],"names":[],"mappings":"
|
|
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,GACZ,EAAE,eAAe,+BAqIjB"}
|
|
@@ -2,10 +2,14 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { useForm, useWatch } from "react-hook-form";
|
|
4
4
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
5
|
+
import { useSaveBar } from "../../../hooks/useSaveBar";
|
|
6
|
+
import { useToast } from "../../../hooks/useToast";
|
|
5
7
|
import { buildZodSchema, groupFields, withSpecVisibility, withSpecVisibilityValues, } from "./helpers";
|
|
6
8
|
import { RecordConfigForm } from "./RecordConfigForm";
|
|
7
9
|
import { SettingsConfigForm } from "./SettingsConfigForm";
|
|
8
10
|
export function ConfigForm({ schema, value, onSave, submitLabel, variant = "config", uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, onChange, hideActions, }) {
|
|
11
|
+
const { register, clear } = useSaveBar();
|
|
12
|
+
const { push: pushToast } = useToast();
|
|
9
13
|
const editorSchema = useMemo(() => (variant === "record" ? withSpecVisibility(schema) : schema), [schema, variant]);
|
|
10
14
|
const editorValue = useMemo(() => variant === "record"
|
|
11
15
|
? withSpecVisibilityValues(editorSchema, value)
|
|
@@ -25,20 +29,58 @@ export function ConfigForm({ schema, value, onSave, submitLabel, variant = "conf
|
|
|
25
29
|
useEffect(() => {
|
|
26
30
|
form.reset(editorValue);
|
|
27
31
|
}, [editorValue, form]);
|
|
32
|
+
const onDiscard = () => {
|
|
33
|
+
form.reset(editorValue);
|
|
34
|
+
setStatus(null);
|
|
35
|
+
setTone(undefined);
|
|
36
|
+
};
|
|
28
37
|
const onSubmit = form.handleSubmit(async (data) => {
|
|
29
38
|
setStatus("Saving…");
|
|
30
39
|
setTone(undefined);
|
|
40
|
+
register({ saving: true, isDirty: true, visible: true });
|
|
31
41
|
try {
|
|
32
42
|
await onSave(data);
|
|
33
43
|
setStatus("Saved");
|
|
34
44
|
setTone("ok");
|
|
45
|
+
form.reset(data);
|
|
46
|
+
pushToast("Changes saved", "ok");
|
|
35
47
|
}
|
|
36
48
|
catch (error) {
|
|
37
|
-
|
|
49
|
+
const message = error instanceof Error ? error.message : "Save failed";
|
|
50
|
+
setStatus(message);
|
|
38
51
|
setTone("danger");
|
|
52
|
+
pushToast(message, "danger");
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
register({ saving: false });
|
|
39
56
|
}
|
|
40
57
|
});
|
|
41
58
|
const isDirty = form.formState.isDirty;
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (hideActions) {
|
|
61
|
+
clear();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
register({
|
|
65
|
+
visible: isDirty,
|
|
66
|
+
isDirty,
|
|
67
|
+
saving: status === "Saving…",
|
|
68
|
+
message: status ?? undefined,
|
|
69
|
+
tone,
|
|
70
|
+
onSave: () => void onSubmit(),
|
|
71
|
+
onDiscard,
|
|
72
|
+
});
|
|
73
|
+
return () => clear();
|
|
74
|
+
}, [
|
|
75
|
+
hideActions,
|
|
76
|
+
isDirty,
|
|
77
|
+
status,
|
|
78
|
+
tone,
|
|
79
|
+
onSubmit,
|
|
80
|
+
onDiscard,
|
|
81
|
+
register,
|
|
82
|
+
clear,
|
|
83
|
+
]);
|
|
42
84
|
useEffect(() => {
|
|
43
85
|
const onKeyDown = (event) => {
|
|
44
86
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "s") {
|
|
@@ -49,11 +91,6 @@ export function ConfigForm({ schema, value, onSave, submitLabel, variant = "conf
|
|
|
49
91
|
window.addEventListener("keydown", onKeyDown);
|
|
50
92
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
51
93
|
}, [onSubmit]);
|
|
52
|
-
const onDiscard = () => {
|
|
53
|
-
form.reset(editorValue);
|
|
54
|
-
setStatus(null);
|
|
55
|
-
setTone(undefined);
|
|
56
|
-
};
|
|
57
94
|
const shared = {
|
|
58
95
|
schema: editorSchema,
|
|
59
96
|
groups,
|
|
@@ -74,7 +111,7 @@ export function ConfigForm({ schema, value, onSave, submitLabel, variant = "conf
|
|
|
74
111
|
tone,
|
|
75
112
|
onDiscard,
|
|
76
113
|
submitLabel,
|
|
77
|
-
hideActions,
|
|
114
|
+
hideActions: true,
|
|
78
115
|
};
|
|
79
116
|
if (variant === "record") {
|
|
80
117
|
return _jsx(RecordConfigForm, { ...shared });
|