@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.
Files changed (89) hide show
  1. package/dist/components/admin/AddonEditor.d.ts +3 -2
  2. package/dist/components/admin/AddonEditor.d.ts.map +1 -1
  3. package/dist/components/admin/AddonEditor.js +14 -10
  4. package/dist/components/admin/AdminContent.d.ts +4 -1
  5. package/dist/components/admin/AdminContent.d.ts.map +1 -1
  6. package/dist/components/admin/AdminContent.js +74 -12
  7. package/dist/components/admin/AdminSaveBar.d.ts.map +1 -1
  8. package/dist/components/admin/AdminSaveBar.js +6 -1
  9. package/dist/components/admin/AdminSyncPage.d.ts.map +1 -1
  10. package/dist/components/admin/AdminSyncPage.js +50 -6
  11. package/dist/components/admin/AdminTopBar.d.ts +6 -2
  12. package/dist/components/admin/AdminTopBar.d.ts.map +1 -1
  13. package/dist/components/admin/AdminTopBar.js +27 -4
  14. package/dist/components/admin/AuditBoard.d.ts +7 -0
  15. package/dist/components/admin/AuditBoard.d.ts.map +1 -0
  16. package/dist/components/admin/AuditBoard.js +43 -0
  17. package/dist/components/admin/HealthModal.d.ts +9 -0
  18. package/dist/components/admin/HealthModal.d.ts.map +1 -0
  19. package/dist/components/admin/HealthModal.js +59 -0
  20. package/dist/components/admin/PagePreviewPane.d.ts +3 -1
  21. package/dist/components/admin/PagePreviewPane.d.ts.map +1 -1
  22. package/dist/components/admin/PagePreviewPane.js +41 -9
  23. package/dist/components/admin/SyncPreviewFrame.d.ts +22 -0
  24. package/dist/components/admin/SyncPreviewFrame.d.ts.map +1 -0
  25. package/dist/components/admin/SyncPreviewFrame.js +135 -0
  26. package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
  27. package/dist/components/admin/VertebrAdmin.js +88 -22
  28. package/dist/components/admin/adminSearch.d.ts.map +1 -1
  29. package/dist/components/admin/adminSearch.js +9 -0
  30. package/dist/components/admin/breadcrumbs.d.ts.map +1 -1
  31. package/dist/components/admin/breadcrumbs.js +2 -0
  32. package/dist/components/admin/routes.d.ts.map +1 -1
  33. package/dist/components/admin/routes.js +13 -1
  34. package/dist/components/admin/types.d.ts +2 -0
  35. package/dist/components/admin/types.d.ts.map +1 -1
  36. package/dist/components/config/DatetimeInput.d.ts +15 -0
  37. package/dist/components/config/DatetimeInput.d.ts.map +1 -0
  38. package/dist/components/config/DatetimeInput.js +287 -0
  39. package/dist/components/config/EnumInput.d.ts.map +1 -1
  40. package/dist/components/config/EnumInput.js +4 -4
  41. package/dist/components/config/EnumListInput.d.ts.map +1 -1
  42. package/dist/components/config/EnumListInput.js +13 -3
  43. package/dist/components/config/ReferenceInput.d.ts +11 -0
  44. package/dist/components/config/ReferenceInput.d.ts.map +1 -0
  45. package/dist/components/config/ReferenceInput.js +99 -0
  46. package/dist/components/config/RevisionHistory.d.ts +12 -0
  47. package/dist/components/config/RevisionHistory.d.ts.map +1 -0
  48. package/dist/components/config/RevisionHistory.js +139 -0
  49. package/dist/components/config/form/ConfigField.d.ts +3 -2
  50. package/dist/components/config/form/ConfigField.d.ts.map +1 -1
  51. package/dist/components/config/form/ConfigField.js +4 -2
  52. package/dist/components/config/form/ConfigForm.d.ts +1 -1
  53. package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
  54. package/dist/components/config/form/ConfigForm.js +66 -14
  55. package/dist/components/config/form/RecordConfigForm.d.ts +3 -2
  56. package/dist/components/config/form/RecordConfigForm.d.ts.map +1 -1
  57. package/dist/components/config/form/RecordConfigForm.js +2 -1
  58. package/dist/components/config/form/SettingsConfigForm.d.ts +3 -2
  59. package/dist/components/config/form/SettingsConfigForm.d.ts.map +1 -1
  60. package/dist/components/config/form/SettingsConfigForm.js +2 -2
  61. package/dist/components/config/form/types.d.ts +14 -1
  62. package/dist/components/config/form/types.d.ts.map +1 -1
  63. package/dist/components/resources/ResourceTable.d.ts +7 -1
  64. package/dist/components/resources/ResourceTable.d.ts.map +1 -1
  65. package/dist/components/resources/ResourceTable.js +56 -6
  66. package/dist/components/theme/ThemeToggle.d.ts +3 -1
  67. package/dist/components/theme/ThemeToggle.d.ts.map +1 -1
  68. package/dist/components/theme/ThemeToggle.js +2 -2
  69. package/dist/components/ui/icons.d.ts +1 -0
  70. package/dist/components/ui/icons.d.ts.map +1 -1
  71. package/dist/components/ui/icons.js +6 -1
  72. package/dist/hooks/useSaveBar.d.ts +6 -0
  73. package/dist/hooks/useSaveBar.d.ts.map +1 -1
  74. package/dist/index.d.ts +2 -1
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +1 -0
  77. package/dist/lib/capabilities.d.ts +4 -0
  78. package/dist/lib/capabilities.d.ts.map +1 -0
  79. package/dist/lib/capabilities.js +9 -0
  80. package/dist/styles/audit.css +34 -0
  81. package/dist/styles/form.css +587 -9
  82. package/dist/styles/index.css +1 -0
  83. package/dist/styles/modal.css +111 -0
  84. package/dist/styles/page-preview.css +84 -0
  85. package/dist/styles/select.css +23 -0
  86. package/dist/styles/shell.css +234 -18
  87. package/dist/styles/sync.css +388 -4
  88. package/dist/themes/light.css +27 -0
  89. 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":"AAOA,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,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC,CAAC;AAWF;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,SAAS,EACT,KAAK,EACL,WAAoB,GACrB,EAAE,oBAAoB,+BAmMtB"}
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 src = useMemo(() => buildVertebrPreviewUrl(previewPath, {
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
- }), [previewPath, controlId]);
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: previewPath })] }), _jsxs("p", { children: [_jsx("span", { children: "Status" }), _jsx("strong", { children: frameError
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
- ? "Live"
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 ", previewPath, "."] })) : (_jsx("iframe", { ref: iframeRef, title: `Preview ${controlId}`, src: src, width: isMobile ? MOBILE_WIDTH : undefined, height: isMobile ? MOBILE_HEIGHT : undefined, style: isMobile
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":"AAsCA,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,+BAopBnB"}
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" || selection.kind === "addons") {
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 === "addons"
524
- ? "Add-ons"
525
- : selection?.kind === "gallery"
526
- ? (catalog?.gallery?.label ?? "Gallery")
527
- : selection?.kind === "fontLibrary"
528
- ? (catalog?.fontLibrary?.label ?? "Font Library")
529
- : selection?.kind === "emails"
530
- ? (catalog?.emails?.label ?? "Emails")
531
- : selection?.kind === "resource"
532
- ? catalog?.resources.find((r) => r.id === selection.id)?.label
533
- : selection?.kind === "config"
534
- ? catalog?.configs.find((c) => c.id === selection.id)?.label
535
- : selection?.kind === "addon"
536
- ? catalog?.addons?.find((a) => a.id === selection.id)?.label
537
- : null;
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: Boolean(catalog?.sync?.enabled), onOpenSync: () => setSelection({ kind: "sync" }), onOpenSearch: () => setSearchOpen(true) }), 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, onRefreshResource: refreshResource, configData: configData, onConfigSaved: setConfigData, galleryPicker: galleryPicker, fontLibraryPicker: fontLibraryPicker }), _jsx(AdminSaveBar, {})] }), _jsx(AdminToastStack, {})] }) }) }));
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,CA6G5E;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,eAAe,EAAE,EACxB,KAAK,EAAE,MAAM,GACZ,eAAe,EAAE,CASnB"}
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,CAyDd"}
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,CAgCR;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,CAgDlB;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,CAoCX;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAgBnE"}
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"}