@vertebr/react 0.9.0 → 0.11.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 +14 -10
- 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 +74 -12
- package/dist/components/admin/AdminSaveBar.d.ts.map +1 -1
- package/dist/components/admin/AdminSaveBar.js +6 -1
- package/dist/components/admin/AdminSyncPage.d.ts.map +1 -1
- package/dist/components/admin/AdminSyncPage.js +50 -6
- 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/PagePreviewPane.d.ts +3 -1
- package/dist/components/admin/PagePreviewPane.d.ts.map +1 -1
- package/dist/components/admin/PagePreviewPane.js +41 -9
- package/dist/components/admin/SyncPreviewFrame.d.ts +22 -0
- package/dist/components/admin/SyncPreviewFrame.d.ts.map +1 -0
- package/dist/components/admin/SyncPreviewFrame.js +135 -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/DatetimeInput.d.ts +15 -0
- package/dist/components/config/DatetimeInput.d.ts.map +1 -0
- package/dist/components/config/DatetimeInput.js +287 -0
- package/dist/components/config/EnumInput.d.ts.map +1 -1
- package/dist/components/config/EnumInput.js +4 -4
- package/dist/components/config/EnumListInput.d.ts.map +1 -1
- package/dist/components/config/EnumListInput.js +13 -3
- 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 +12 -0
- package/dist/components/config/RevisionHistory.d.ts.map +1 -0
- package/dist/components/config/RevisionHistory.js +139 -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 +4 -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 +66 -14
- 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 +14 -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/hooks/useSaveBar.d.ts +6 -0
- package/dist/hooks/useSaveBar.d.ts.map +1 -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 +587 -9
- package/dist/styles/index.css +1 -0
- package/dist/styles/modal.css +111 -0
- package/dist/styles/page-preview.css +84 -0
- package/dist/styles/select.css +23 -0
- package/dist/styles/shell.css +234 -18
- package/dist/styles/sync.css +388 -4
- package/dist/themes/light.css +27 -0
- package/package.json +3 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
function statusLabel(status) {
|
|
4
|
+
if (status === "ok")
|
|
5
|
+
return "OK";
|
|
6
|
+
if (status === "warn")
|
|
7
|
+
return "Warn";
|
|
8
|
+
return "Error";
|
|
9
|
+
}
|
|
10
|
+
function formatCheckedAt(value) {
|
|
11
|
+
if (!value)
|
|
12
|
+
return null;
|
|
13
|
+
const date = new Date(value);
|
|
14
|
+
if (Number.isNaN(date.getTime()))
|
|
15
|
+
return value;
|
|
16
|
+
return date.toLocaleString(undefined, {
|
|
17
|
+
dateStyle: "medium",
|
|
18
|
+
timeStyle: "short",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export function HealthModal({ checks, environment, onClose, onRefresh, }) {
|
|
22
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
23
|
+
const [refreshError, setRefreshError] = useState(null);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const onKeyDown = (event) => {
|
|
26
|
+
if (event.key === "Escape")
|
|
27
|
+
onClose();
|
|
28
|
+
};
|
|
29
|
+
window.addEventListener("keydown", onKeyDown);
|
|
30
|
+
const previous = document.body.style.overflow;
|
|
31
|
+
document.body.style.overflow = "hidden";
|
|
32
|
+
return () => {
|
|
33
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
34
|
+
document.body.style.overflow = previous;
|
|
35
|
+
};
|
|
36
|
+
}, [onClose]);
|
|
37
|
+
const handleRefresh = async () => {
|
|
38
|
+
if (!onRefresh || refreshing)
|
|
39
|
+
return;
|
|
40
|
+
setRefreshing(true);
|
|
41
|
+
setRefreshError(null);
|
|
42
|
+
try {
|
|
43
|
+
await onRefresh();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
setRefreshError(error instanceof Error ? error.message : "Failed to refresh health");
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
setRefreshing(false);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const latestCheckedAt = formatCheckedAt(checks.map((c) => c.checkedAt).find(Boolean));
|
|
53
|
+
return (_jsx("div", { className: "vertebr-modal-backdrop", role: "presentation", onClick: onClose, children: _jsxs("div", { className: "vertebr-modal vertebr-modal-sm vertebr-health-modal", role: "dialog", "aria-modal": "true", "aria-label": "System health", onClick: (event) => event.stopPropagation(), children: [_jsxs("header", { className: "vertebr-modal-head", children: [_jsxs("div", { className: "vertebr-modal-heading", children: [_jsx("p", { className: "vertebr-modal-eyebrow", children: "Operations" }), _jsx("h2", { children: "System health" }), environment || latestCheckedAt ? (_jsx("p", { className: "vertebr-health-modal-meta", children: [
|
|
54
|
+
environment ? `Env: ${environment}` : null,
|
|
55
|
+
latestCheckedAt ? `Checked ${latestCheckedAt}` : null,
|
|
56
|
+
]
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
.join(" · ") })) : null] }), _jsxs("div", { className: "vertebr-modal-actions", children: [onRefresh ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: refreshing, onClick: () => void handleRefresh(), children: refreshing ? "Refreshing…" : "Refresh" })) : null, _jsx("button", { type: "button", className: "vertebr-btn", onClick: onClose, children: "Close" })] })] }), _jsxs("div", { className: "vertebr-modal-body", children: [refreshError ? (_jsx("p", { className: "vertebr-health-modal-error", children: refreshError })) : null, checks.length === 0 ? (_jsx("p", { className: "vertebr-health-modal-empty", children: "No health checks reported." })) : (_jsx("ul", { className: "vertebr-health-list", children: checks.map((check) => (_jsxs("li", { className: "vertebr-health-item", "data-status": check.status, children: [_jsxs("div", { className: "vertebr-health-item-top", children: [_jsxs("span", { className: "vertebr-health-badge", "data-status": check.status, children: [_jsx("span", { className: "vertebr-health-badge-dot", "aria-hidden": true }), statusLabel(check.status)] }), _jsx("strong", { className: "vertebr-health-item-label", children: check.label })] }), check.detail ? (_jsx("p", { className: "vertebr-health-item-detail", children: check.detail })) : null] }, check.id))) }))] })] }) }));
|
|
59
|
+
}
|
|
@@ -6,11 +6,13 @@ export type PagePreviewPaneProps = {
|
|
|
6
6
|
controlId: string;
|
|
7
7
|
/** Latest unsaved form values. */
|
|
8
8
|
draft: Record<string, unknown> | null;
|
|
9
|
+
/** Extra add-on drafts owned by this page (e.g. countdown on coming soon). */
|
|
10
|
+
relatedDrafts?: Record<string, Record<string, unknown>>;
|
|
9
11
|
/** Label for the preview chrome. */
|
|
10
12
|
previewKind?: "page" | "addon";
|
|
11
13
|
};
|
|
12
14
|
/**
|
|
13
15
|
* Live host iframe for Pages. Vertebr posts drafts; the host renders.
|
|
14
16
|
*/
|
|
15
|
-
export declare function PagePreviewPane({ previewPath, controlId, draft, previewKind, }: PagePreviewPaneProps): import("react").JSX.Element;
|
|
17
|
+
export declare function PagePreviewPane({ previewPath, controlId, draft, relatedDrafts, previewKind, }: PagePreviewPaneProps): import("react").JSX.Element;
|
|
16
18
|
//# sourceMappingURL=PagePreviewPane.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PagePreviewPane.d.ts","sourceRoot":"","sources":["../../../src/components/admin/PagePreviewPane.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PagePreviewPane.d.ts","sourceRoot":"","sources":["../../../src/components/admin/PagePreviewPane.tsx"],"names":[],"mappings":"AAQA,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEvD,MAAM,MAAM,oBAAoB,GAAG;IACjC,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC,CAAC;AAqBF;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,SAAS,EACT,KAAK,EACL,aAAa,EACb,WAAoB,GACrB,EAAE,oBAAoB,+BAoOtB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { buildVertebrPreviewUrl, postVertebrPreviewDraft, subscribeVertebrPreviewReady, } from "@vertebr/client";
|
|
3
|
+
import { buildVertebrPreviewUrl, postVertebrPreviewDraft, subscribeVertebrPreviewNavigate, subscribeVertebrPreviewReady, } from "@vertebr/client";
|
|
4
4
|
const VIEWPORTS = [
|
|
5
5
|
{ id: "desktop", label: "Desktop" },
|
|
6
6
|
{ id: "mobile", label: "Mobile" },
|
|
@@ -8,28 +8,52 @@ const VIEWPORTS = [
|
|
|
8
8
|
/** Logical CSS viewport for mobile preview (host media queries use this). */
|
|
9
9
|
const MOBILE_WIDTH = 390;
|
|
10
10
|
const MOBILE_HEIGHT = 844;
|
|
11
|
+
function normalizePreviewPath(path) {
|
|
12
|
+
const trimmed = path.trim() || "/";
|
|
13
|
+
try {
|
|
14
|
+
const url = new URL(trimmed, "http://vertebr.local");
|
|
15
|
+
return `${url.pathname}${url.search}${url.hash}` || "/";
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
11
21
|
/**
|
|
12
22
|
* Live host iframe for Pages. Vertebr posts drafts; the host renders.
|
|
13
23
|
*/
|
|
14
|
-
export function PagePreviewPane({ previewPath, controlId, draft, previewKind = "page", }) {
|
|
24
|
+
export function PagePreviewPane({ previewPath, controlId, draft, relatedDrafts, previewKind = "page", }) {
|
|
15
25
|
const iframeRef = useRef(null);
|
|
16
26
|
const stageRef = useRef(null);
|
|
17
27
|
const draftRef = useRef(draft);
|
|
28
|
+
const relatedRef = useRef(relatedDrafts);
|
|
18
29
|
const [frameReady, setFrameReady] = useState(false);
|
|
19
30
|
const [frameError, setFrameError] = useState(false);
|
|
20
31
|
const [viewport, setViewport] = useState("desktop");
|
|
21
32
|
const [mobileScale, setMobileScale] = useState(1);
|
|
33
|
+
const [activePath, setActivePath] = useState(() => normalizePreviewPath(previewPath));
|
|
22
34
|
draftRef.current = draft;
|
|
35
|
+
relatedRef.current = relatedDrafts;
|
|
23
36
|
const isMobile = viewport === "mobile";
|
|
24
|
-
const
|
|
37
|
+
const basePath = normalizePreviewPath(previewPath);
|
|
38
|
+
const navigatedAway = activePath !== basePath;
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
setActivePath(normalizePreviewPath(previewPath));
|
|
41
|
+
}, [previewPath]);
|
|
42
|
+
const src = useMemo(() => buildVertebrPreviewUrl(activePath, {
|
|
25
43
|
controlId,
|
|
26
|
-
}), [
|
|
44
|
+
}), [activePath, controlId]);
|
|
27
45
|
const pushDraft = useCallback(() => {
|
|
28
46
|
const win = iframeRef.current?.contentWindow;
|
|
29
47
|
const value = draftRef.current;
|
|
30
48
|
if (!win || !value)
|
|
31
49
|
return;
|
|
32
50
|
postVertebrPreviewDraft(win, controlId, value);
|
|
51
|
+
const related = relatedRef.current;
|
|
52
|
+
if (!related)
|
|
53
|
+
return;
|
|
54
|
+
for (const [id, relatedValue] of Object.entries(related)) {
|
|
55
|
+
postVertebrPreviewDraft(win, id, relatedValue);
|
|
56
|
+
}
|
|
33
57
|
}, [controlId]);
|
|
34
58
|
const notifyHostResize = useCallback(() => {
|
|
35
59
|
const win = iframeRef.current?.contentWindow;
|
|
@@ -55,11 +79,19 @@ export function PagePreviewPane({ previewPath, controlId, draft, previewKind = "
|
|
|
55
79
|
pushDraft();
|
|
56
80
|
});
|
|
57
81
|
}, [pushDraft, src]);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
return subscribeVertebrPreviewNavigate((path, source) => {
|
|
84
|
+
const win = iframeRef.current?.contentWindow;
|
|
85
|
+
if (!win || source !== win)
|
|
86
|
+
return;
|
|
87
|
+
setActivePath(normalizePreviewPath(path));
|
|
88
|
+
});
|
|
89
|
+
}, []);
|
|
58
90
|
useEffect(() => {
|
|
59
91
|
if (!frameReady)
|
|
60
92
|
return;
|
|
61
93
|
pushDraft();
|
|
62
|
-
}, [draft, frameReady, pushDraft]);
|
|
94
|
+
}, [draft, relatedDrafts, frameReady, pushDraft]);
|
|
63
95
|
// Fit the fixed mobile viewport into the stage without changing its
|
|
64
96
|
// layout width (transform scale keeps host media queries at 390px).
|
|
65
97
|
useEffect(() => {
|
|
@@ -93,11 +125,11 @@ export function PagePreviewPane({ previewPath, controlId, draft, previewKind = "
|
|
|
93
125
|
const timer = window.setTimeout(notifyHostResize, 50);
|
|
94
126
|
return () => window.clearTimeout(timer);
|
|
95
127
|
}, [viewport, mobileScale, frameReady, notifyHostResize]);
|
|
96
|
-
return (_jsxs("div", { className: "vertebr-page-preview", "data-preview-kind": previewKind, children: [_jsxs("div", { className: "vertebr-page-preview-meta", children: [_jsxs("p", { children: [_jsx("span", { children: previewKind === "addon" ? "Storefront" : "Preview" }), _jsx("strong", { children:
|
|
128
|
+
return (_jsxs("div", { className: "vertebr-page-preview", "data-preview-kind": previewKind, children: [_jsxs("div", { className: "vertebr-page-preview-meta", children: [_jsxs("p", { children: [_jsx("span", { children: previewKind === "addon" ? "Storefront" : "Preview" }), _jsx("strong", { children: activePath })] }), _jsxs("p", { children: [_jsx("span", { children: "Status" }), _jsx("strong", { children: frameError
|
|
97
129
|
? "Unavailable"
|
|
98
130
|
: frameReady
|
|
99
|
-
? "
|
|
100
|
-
: "Loading…" })] }), isMobile ? (_jsxs("p", { children: [_jsx("span", { children: "Viewport" }), _jsxs("strong", { children: [MOBILE_WIDTH, "\u00D7", MOBILE_HEIGHT] })] })) : null, _jsx("div", { className: "vertebr-page-preview-viewports", role: "group", "aria-label": "Preview viewport", children: VIEWPORTS.map((item) => (_jsx("button", { type: "button", "data-active": viewport === item.id ? "true" : undefined, onClick: () => setViewport(item.id), children: item.label }, item.id))) })] }), _jsx("div", { ref: stageRef, className: "vertebr-page-preview-stage", "data-viewport": viewport, children: _jsx("div", { className: "vertebr-page-preview-shell", style: isMobile
|
|
131
|
+
? "Connected"
|
|
132
|
+
: "Loading…" })] }), isMobile ? (_jsxs("p", { children: [_jsx("span", { children: "Viewport" }), _jsxs("strong", { children: [MOBILE_WIDTH, "\u00D7", MOBILE_HEIGHT] })] })) : null, navigatedAway ? (_jsx("button", { type: "button", className: "vertebr-page-preview-back", onClick: () => setActivePath(basePath), children: "Back to page" })) : null, _jsx("div", { className: "vertebr-page-preview-viewports", role: "group", "aria-label": "Preview viewport", children: VIEWPORTS.map((item) => (_jsx("button", { type: "button", "data-active": viewport === item.id ? "true" : undefined, onClick: () => setViewport(item.id), children: item.label }, item.id))) })] }), _jsx("div", { ref: stageRef, className: "vertebr-page-preview-stage", "data-viewport": viewport, children: _jsx("div", { className: "vertebr-page-preview-shell", style: isMobile
|
|
101
133
|
? {
|
|
102
134
|
width: MOBILE_WIDTH * mobileScale,
|
|
103
135
|
height: MOBILE_HEIGHT * mobileScale,
|
|
@@ -109,7 +141,7 @@ export function PagePreviewPane({ previewPath, controlId, draft, previewKind = "
|
|
|
109
141
|
transform: `scale(${mobileScale})`,
|
|
110
142
|
transformOrigin: "top left",
|
|
111
143
|
}
|
|
112
|
-
: undefined, children: frameError ? (_jsxs("div", { className: "vertebr-page-preview-empty", children: ["Could not load host preview at ",
|
|
144
|
+
: undefined, children: frameError ? (_jsxs("div", { className: "vertebr-page-preview-empty", children: ["Could not load host preview at ", activePath, "."] })) : (_jsx("iframe", { ref: iframeRef, title: `Preview ${controlId}`, src: src, width: isMobile ? MOBILE_WIDTH : undefined, height: isMobile ? MOBILE_HEIGHT : undefined, style: isMobile
|
|
113
145
|
? { width: MOBILE_WIDTH, height: MOBILE_HEIGHT }
|
|
114
146
|
: undefined, onLoad: () => {
|
|
115
147
|
// Ready message is preferred; onLoad covers hosts that
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type SyncPreviewAddonChip = {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
/** True when the add-on is enabled in that environment. */
|
|
5
|
+
envEnabled: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type SyncPreviewFrameProps = {
|
|
8
|
+
label: string;
|
|
9
|
+
previewPath: string;
|
|
10
|
+
controlId: string;
|
|
11
|
+
draft: Record<string, unknown>;
|
|
12
|
+
/** Extra controls to inject (e.g. countdown alongside coming soon). */
|
|
13
|
+
relatedDrafts?: Record<string, Record<string, unknown>>;
|
|
14
|
+
/** Add-ons that belong on this page preview (always force-shown in the iframe). */
|
|
15
|
+
addons?: SyncPreviewAddonChip[];
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Compact same-origin preview iframe for Sync visual compare.
|
|
19
|
+
* Fixed mobile-sized frame; posts the given draft after ready.
|
|
20
|
+
*/
|
|
21
|
+
export declare function SyncPreviewFrame({ label, previewPath, controlId, draft, relatedDrafts, addons, }: SyncPreviewFrameProps): import("react").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=SyncPreviewFrame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SyncPreviewFrame.d.ts","sourceRoot":"","sources":["../../../src/components/admin/SyncPreviewFrame.tsx"],"names":[],"mappings":"AAOA,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,mFAAmF;IACnF,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACjC,CAAC;AAqDF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,WAAW,EACX,SAAS,EACT,KAAK,EACL,aAAa,EACb,MAAM,GACP,EAAE,qBAAqB,+BA6JvB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { buildVertebrPreviewUrl, postVertebrPreviewDraft, subscribeVertebrPreviewReady, } from "@vertebr/client";
|
|
4
|
+
const FRAME_WIDTH = 390;
|
|
5
|
+
const FRAME_HEIGHT = 720;
|
|
6
|
+
function normalizePreviewPath(path) {
|
|
7
|
+
const trimmed = path.trim() || "/";
|
|
8
|
+
try {
|
|
9
|
+
const url = new URL(trimmed, "http://vertebr.local");
|
|
10
|
+
return `${url.pathname}${url.search}${url.hash}` || "/";
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build related drafts for the iframe: prefer plan payloads (real enabled /
|
|
18
|
+
* content). Synthesize only when a listed add-on is missing from the plan.
|
|
19
|
+
*/
|
|
20
|
+
function resolveRelatedDrafts(controlId, relatedDrafts, addons) {
|
|
21
|
+
const next = {};
|
|
22
|
+
for (const [id, value] of Object.entries(relatedDrafts ?? {})) {
|
|
23
|
+
next[id] = { ...value };
|
|
24
|
+
}
|
|
25
|
+
for (const addon of addons ?? []) {
|
|
26
|
+
const existing = next[addon.id] ?? {
|
|
27
|
+
enabled: addon.envEnabled,
|
|
28
|
+
};
|
|
29
|
+
const patched = { ...existing };
|
|
30
|
+
if (patched.enabled === undefined) {
|
|
31
|
+
patched.enabled = addon.envEnabled;
|
|
32
|
+
}
|
|
33
|
+
if (addon.id === "countdown" && controlId === "comingSoon") {
|
|
34
|
+
patched.target = "comingSoon";
|
|
35
|
+
}
|
|
36
|
+
if (addon.id === "countdown" && controlId === "product") {
|
|
37
|
+
patched.target = "product";
|
|
38
|
+
}
|
|
39
|
+
if (addon.id === "reviews" && controlId === "home") {
|
|
40
|
+
patched.showOnHome = true;
|
|
41
|
+
}
|
|
42
|
+
if (addon.id === "reviews" && controlId === "product") {
|
|
43
|
+
patched.showOnProducts = true;
|
|
44
|
+
}
|
|
45
|
+
next[addon.id] = patched;
|
|
46
|
+
}
|
|
47
|
+
return next;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Compact same-origin preview iframe for Sync visual compare.
|
|
51
|
+
* Fixed mobile-sized frame; posts the given draft after ready.
|
|
52
|
+
*/
|
|
53
|
+
export function SyncPreviewFrame({ label, previewPath, controlId, draft, relatedDrafts, addons, }) {
|
|
54
|
+
const iframeRef = useRef(null);
|
|
55
|
+
const stageRef = useRef(null);
|
|
56
|
+
const draftRef = useRef(draft);
|
|
57
|
+
const [frameReady, setFrameReady] = useState(false);
|
|
58
|
+
const [frameError, setFrameError] = useState(false);
|
|
59
|
+
const [scale, setScale] = useState(1);
|
|
60
|
+
const resolvedRelated = useMemo(() => resolveRelatedDrafts(controlId, relatedDrafts, addons), [controlId, relatedDrafts, addons]);
|
|
61
|
+
const relatedRef = useRef(resolvedRelated);
|
|
62
|
+
draftRef.current = draft;
|
|
63
|
+
relatedRef.current = resolvedRelated;
|
|
64
|
+
const path = normalizePreviewPath(previewPath);
|
|
65
|
+
const src = useMemo(() => buildVertebrPreviewUrl(path, {
|
|
66
|
+
controlId,
|
|
67
|
+
}), [path, controlId]);
|
|
68
|
+
const pushDraft = useCallback(() => {
|
|
69
|
+
const win = iframeRef.current?.contentWindow;
|
|
70
|
+
const value = draftRef.current;
|
|
71
|
+
if (!win || !value)
|
|
72
|
+
return;
|
|
73
|
+
postVertebrPreviewDraft(win, controlId, value);
|
|
74
|
+
for (const [id, relatedValue] of Object.entries(relatedRef.current)) {
|
|
75
|
+
postVertebrPreviewDraft(win, id, relatedValue);
|
|
76
|
+
}
|
|
77
|
+
}, [controlId]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
setFrameReady(false);
|
|
80
|
+
setFrameError(false);
|
|
81
|
+
}, [src]);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
return subscribeVertebrPreviewReady((source) => {
|
|
84
|
+
const win = iframeRef.current?.contentWindow;
|
|
85
|
+
if (!win || source !== win)
|
|
86
|
+
return;
|
|
87
|
+
setFrameReady(true);
|
|
88
|
+
pushDraft();
|
|
89
|
+
});
|
|
90
|
+
}, [pushDraft, src]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!frameReady)
|
|
93
|
+
return;
|
|
94
|
+
pushDraft();
|
|
95
|
+
}, [draft, resolvedRelated, frameReady, pushDraft]);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
const stage = stageRef.current;
|
|
98
|
+
if (!stage || typeof ResizeObserver === "undefined")
|
|
99
|
+
return;
|
|
100
|
+
const update = () => {
|
|
101
|
+
const style = getComputedStyle(stage);
|
|
102
|
+
const padX = (Number.parseFloat(style.paddingLeft) || 0) +
|
|
103
|
+
(Number.parseFloat(style.paddingRight) || 0);
|
|
104
|
+
const padY = (Number.parseFloat(style.paddingTop) || 0) +
|
|
105
|
+
(Number.parseFloat(style.paddingBottom) || 0);
|
|
106
|
+
const availW = Math.max(0, stage.clientWidth - padX);
|
|
107
|
+
const availH = Math.max(0, stage.clientHeight - padY);
|
|
108
|
+
const next = Math.min(1, availW / FRAME_WIDTH, availH / FRAME_HEIGHT);
|
|
109
|
+
setScale(Number.isFinite(next) && next > 0 ? next : 1);
|
|
110
|
+
};
|
|
111
|
+
update();
|
|
112
|
+
const observer = new ResizeObserver(update);
|
|
113
|
+
observer.observe(stage);
|
|
114
|
+
return () => observer.disconnect();
|
|
115
|
+
}, []);
|
|
116
|
+
const status = frameError
|
|
117
|
+
? "Unavailable"
|
|
118
|
+
: frameReady
|
|
119
|
+
? "Connected"
|
|
120
|
+
: "Loading…";
|
|
121
|
+
return (_jsxs("div", { className: "vertebr-sync-preview-frame", children: [_jsxs("div", { className: "vertebr-sync-preview-frame-meta", children: [_jsx("strong", { children: label }), _jsx("span", { children: path }), _jsx("span", { "data-status": frameError ? "error" : frameReady ? "ok" : "pending", children: status })] }), addons ? (_jsx("div", { className: "vertebr-sync-preview-addons", "aria-label": "Add-ons in preview", children: addons.length === 0 ? (_jsx("span", { className: "vertebr-sync-preview-addons-empty", children: "No add-ons" })) : (addons.map((addon) => (_jsxs("span", { className: "vertebr-sync-preview-addon", "data-enabled": addon.envEnabled ? "true" : "false", title: addon.envEnabled
|
|
122
|
+
? `${addon.label} is enabled in this environment`
|
|
123
|
+
: `${addon.label} is off in this environment`, children: [addon.label, _jsx("em", { children: addon.envEnabled ? "on" : "off" })] }, addon.id)))) })) : null, _jsx("div", { ref: stageRef, className: "vertebr-sync-preview-frame-stage", children: _jsx("div", { className: "vertebr-sync-preview-frame-shell", style: {
|
|
124
|
+
width: FRAME_WIDTH * scale,
|
|
125
|
+
height: FRAME_HEIGHT * scale,
|
|
126
|
+
}, children: _jsx("div", { className: "vertebr-sync-preview-frame-scaler", style: {
|
|
127
|
+
width: FRAME_WIDTH,
|
|
128
|
+
height: FRAME_HEIGHT,
|
|
129
|
+
transform: `scale(${scale})`,
|
|
130
|
+
transformOrigin: "top left",
|
|
131
|
+
}, children: frameError ? (_jsxs("div", { className: "vertebr-sync-preview-frame-empty", children: ["Could not load preview at ", path, "."] })) : (_jsx("iframe", { ref: iframeRef, title: `${label} preview`, src: src, width: FRAME_WIDTH, height: FRAME_HEIGHT, onLoad: () => {
|
|
132
|
+
setFrameReady(true);
|
|
133
|
+
pushDraft();
|
|
134
|
+
}, onError: () => setFrameError(true) })) }) }) })] }));
|
|
135
|
+
}
|
|
@@ -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":"AAwCA,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,+BAguBnB"}
|
|
@@ -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"}
|