@vertebr/react 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/admin/AddonEditor.d.ts +13 -0
- package/dist/components/admin/AddonEditor.d.ts.map +1 -0
- package/dist/components/admin/AddonEditor.js +27 -0
- package/dist/components/admin/AddonOverview.d.ts +8 -0
- package/dist/components/admin/AddonOverview.d.ts.map +1 -0
- package/dist/components/admin/AddonOverview.js +125 -0
- package/dist/components/admin/AddonPreviewThumb.d.ts +11 -0
- package/dist/components/admin/AddonPreviewThumb.d.ts.map +1 -0
- package/dist/components/admin/AddonPreviewThumb.js +52 -0
- package/dist/components/admin/AdminCommandPalette.d.ts +9 -0
- package/dist/components/admin/AdminCommandPalette.d.ts.map +1 -0
- package/dist/components/admin/AdminCommandPalette.js +68 -0
- 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 +32 -10
- package/dist/components/admin/AdminHeader.d.ts +2 -3
- package/dist/components/admin/AdminHeader.d.ts.map +1 -1
- package/dist/components/admin/AdminHeader.js +10 -3
- package/dist/components/admin/AdminPoweredBy.d.ts +5 -0
- package/dist/components/admin/AdminPoweredBy.d.ts.map +1 -0
- package/dist/components/admin/AdminPoweredBy.js +8 -0
- package/dist/components/admin/AdminSidebar.d.ts.map +1 -1
- package/dist/components/admin/AdminSidebar.js +9 -7
- package/dist/components/admin/AdminSyncPage.d.ts +7 -0
- package/dist/components/admin/AdminSyncPage.d.ts.map +1 -0
- package/dist/components/admin/AdminSyncPage.js +169 -0
- package/dist/components/admin/AdminTopBar.d.ts +11 -3
- 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 +2 -2
- package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
- package/dist/components/admin/VertebrAdmin.js +146 -17
- package/dist/components/admin/addonLabels.d.ts +6 -0
- package/dist/components/admin/addonLabels.d.ts.map +1 -0
- package/dist/components/admin/addonLabels.js +20 -0
- package/dist/components/admin/adminSearch.d.ts +12 -0
- package/dist/components/admin/adminSearch.d.ts.map +1 -0
- package/dist/components/admin/adminSearch.js +120 -0
- package/dist/components/admin/breadcrumbs.d.ts.map +1 -1
- package/dist/components/admin/breadcrumbs.js +14 -0
- package/dist/components/admin/routes.d.ts.map +1 -1
- package/dist/components/admin/routes.js +43 -1
- package/dist/components/admin/types.d.ts +9 -0
- package/dist/components/admin/types.d.ts.map +1 -1
- package/dist/components/config/ReferenceInput.d.ts +11 -0
- package/dist/components/config/ReferenceInput.d.ts.map +1 -0
- package/dist/components/config/ReferenceInput.js +99 -0
- package/dist/components/config/RevisionHistory.d.ts +8 -0
- package/dist/components/config/RevisionHistory.d.ts.map +1 -0
- package/dist/components/config/RevisionHistory.js +84 -0
- package/dist/components/config/form/ConfigField.d.ts +3 -2
- package/dist/components/config/form/ConfigField.d.ts.map +1 -1
- package/dist/components/config/form/ConfigField.js +3 -2
- package/dist/components/config/form/ConfigForm.d.ts +1 -1
- package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/ConfigForm.js +13 -6
- package/dist/components/config/form/RecordConfigForm.d.ts +3 -2
- package/dist/components/config/form/RecordConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/RecordConfigForm.js +2 -1
- package/dist/components/config/form/SettingsConfigForm.d.ts +3 -2
- package/dist/components/config/form/SettingsConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/SettingsConfigForm.js +2 -2
- package/dist/components/config/form/types.d.ts +5 -1
- package/dist/components/config/form/types.d.ts.map +1 -1
- package/dist/components/env/EnvMultiSelect.d.ts +20 -0
- package/dist/components/env/EnvMultiSelect.d.ts.map +1 -0
- package/dist/components/env/EnvMultiSelect.js +38 -0
- 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 +3 -0
- package/dist/components/ui/icons.d.ts.map +1 -1
- package/dist/components/ui/icons.js +17 -0
- 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/addon.css +540 -0
- package/dist/styles/audit.css +34 -0
- package/dist/styles/base.css +1 -0
- package/dist/styles/font-library.css +2 -0
- package/dist/styles/form.css +143 -0
- package/dist/styles/index.css +3 -0
- package/dist/styles/main.css +0 -38
- package/dist/styles/modal.css +113 -0
- package/dist/styles/motion.css +55 -0
- package/dist/styles/nav.css +39 -4
- package/dist/styles/select.css +6 -0
- package/dist/styles/shell.css +417 -14
- package/dist/styles/sidebar.css +63 -3
- package/dist/styles/sync.css +540 -0
- package/dist/styles/table.css +15 -5
- package/dist/themes/dark.css +11 -11
- package/dist/themes/index.css +1 -0
- package/dist/themes/light.css +19 -19
- package/package.json +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CatalogAddon, CatalogResponse, ConfigResponse, VertebrClient } from "@vertebr/client";
|
|
2
|
+
import type { AdminContentProps } from "./AdminContent";
|
|
3
|
+
export type AddonEditorProps = {
|
|
4
|
+
client: VertebrClient;
|
|
5
|
+
catalog: CatalogResponse;
|
|
6
|
+
addon: CatalogAddon;
|
|
7
|
+
configData: ConfigResponse;
|
|
8
|
+
onConfigSaved: (next: ConfigResponse) => void;
|
|
9
|
+
galleryPicker: AdminContentProps["galleryPicker"];
|
|
10
|
+
fontLibraryPicker: AdminContentProps["fontLibraryPicker"];
|
|
11
|
+
};
|
|
12
|
+
export declare function AddonEditor({ client, catalog, addon, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }: AddonEditorProps): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=AddonEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddonEditor.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AddonEditor.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,aAAa,EACd,MAAM,iBAAiB,CAAC;AAKzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,aAAa,EAAE,iBAAiB,CAAC,eAAe,CAAC,CAAC;IAClD,iBAAiB,EAAE,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;CAC3D,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,OAAO,EACP,KAAK,EACL,UAAU,EACV,aAAa,EACb,aAAa,EACb,iBAAiB,GAClB,EAAE,gBAAgB,+BAyFlB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo, useState } from "react";
|
|
3
|
+
import { ConfigForm } from "../config/ConfigForm";
|
|
4
|
+
import { hasCap } from "../../lib/capabilities";
|
|
5
|
+
import { addonCategoryLabel } from "./addonLabels";
|
|
6
|
+
import { PagePreviewPane } from "./PagePreviewPane";
|
|
7
|
+
export function AddonEditor({ client, catalog, addon, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }) {
|
|
8
|
+
const [draft, setDraft] = useState(configData.value);
|
|
9
|
+
const onChange = useCallback((value) => {
|
|
10
|
+
setDraft(value);
|
|
11
|
+
}, []);
|
|
12
|
+
const enabled = Boolean(draft?.enabled ?? configData.value.enabled);
|
|
13
|
+
const previewPath = addon.previewPath;
|
|
14
|
+
const showPreview = Boolean(previewPath);
|
|
15
|
+
const canWrite = hasCap(catalog, "addon.write");
|
|
16
|
+
const previewDraft = useMemo(() => {
|
|
17
|
+
const base = draft ?? configData.value;
|
|
18
|
+
if (!showPreview)
|
|
19
|
+
return base;
|
|
20
|
+
return { ...base, enabled: true };
|
|
21
|
+
}, [configData.value, draft, showPreview]);
|
|
22
|
+
const form = (_jsx(ConfigForm, { schema: configData.schema, value: configData.value, client: client, canWrite: canWrite, onChange: showPreview ? onChange : undefined, onSave: async (next) => {
|
|
23
|
+
const saved = await client.setConfig(configData.id, next);
|
|
24
|
+
onConfigSaved(saved);
|
|
25
|
+
}, ...galleryPicker, ...fontLibraryPicker }));
|
|
26
|
+
return (_jsxs("div", { className: "vertebr-addon-editor", children: [_jsxs("div", { className: "vertebr-addon-editor-head", "data-category": addon.category ?? "engagement", children: [_jsxs("div", { className: "vertebr-addon-editor-head-start", children: [_jsx("span", { className: "vertebr-addon-category", "data-category": addon.category ?? "engagement", children: addonCategoryLabel(addon.category) }), _jsxs("span", { className: "vertebr-addon-status", "data-enabled": enabled ? "true" : "false", children: [_jsx("span", { className: "vertebr-addon-status-dot", "aria-hidden": "true" }), enabled ? "Enabled on storefront" : "Disabled"] })] }), previewPath ? (_jsxs("div", { className: "vertebr-addon-editor-head-end", children: [_jsx("span", { className: "vertebr-addon-preview-label", children: "Live preview" }), _jsx("code", { className: "vertebr-addon-preview-path", children: previewPath })] })) : null] }), showPreview && previewPath ? (_jsxs("div", { className: "vertebr-page-editor vertebr-addon-page-editor", children: [_jsx("div", { className: "vertebr-page-editor-form", children: form }), _jsx(PagePreviewPane, { previewPath: previewPath, controlId: configData.id, draft: previewDraft, previewKind: "addon" })] })) : (_jsxs("div", { className: "vertebr-addon-editor-form-only", children: [form, _jsxs("div", { className: "vertebr-addon-preview-missing", children: [_jsx("strong", { children: "Live preview unavailable" }), _jsxs("p", { children: ["Register a ", _jsx("code", { children: "previewPath" }), " on this add-on so Vertebr can iframe your storefront while you edit."] })] })] }))] }));
|
|
27
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CatalogResponse, VertebrClient } from "@vertebr/client";
|
|
2
|
+
export type AddonOverviewProps = {
|
|
3
|
+
client: VertebrClient;
|
|
4
|
+
catalog: CatalogResponse;
|
|
5
|
+
onSelectAddon: (id: string) => void;
|
|
6
|
+
};
|
|
7
|
+
export declare function AddonOverview({ client, catalog, onSelectAddon }: AddonOverviewProps): import("react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=AddonOverview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddonOverview.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AddonOverview.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIpF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,eAAe,CAAC;IACzB,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAyFF,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,kBAAkB,+BAwKnF"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { AddonPreviewThumb } from "./AddonPreviewThumb";
|
|
4
|
+
import { addonCategoryHint, addonCategoryLabel } from "./addonLabels";
|
|
5
|
+
const CATEGORY_ORDER = ["product", "engagement", "sales"];
|
|
6
|
+
function groupAddons(addons) {
|
|
7
|
+
const buckets = new Map();
|
|
8
|
+
for (const addon of addons) {
|
|
9
|
+
const key = addon.category ?? "other";
|
|
10
|
+
if (!buckets.has(key))
|
|
11
|
+
buckets.set(key, []);
|
|
12
|
+
buckets.get(key).push(addon);
|
|
13
|
+
}
|
|
14
|
+
const groups = [];
|
|
15
|
+
for (const category of CATEGORY_ORDER) {
|
|
16
|
+
const items = buckets.get(category);
|
|
17
|
+
if (!items?.length)
|
|
18
|
+
continue;
|
|
19
|
+
groups.push({
|
|
20
|
+
category,
|
|
21
|
+
label: addonCategoryLabel(category),
|
|
22
|
+
hint: addonCategoryHint(category),
|
|
23
|
+
items,
|
|
24
|
+
});
|
|
25
|
+
buckets.delete(category);
|
|
26
|
+
}
|
|
27
|
+
const other = buckets.get("other");
|
|
28
|
+
if (other?.length) {
|
|
29
|
+
groups.push({
|
|
30
|
+
category: "other",
|
|
31
|
+
label: "Add-ons",
|
|
32
|
+
hint: "Optional storefront features",
|
|
33
|
+
items: other,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return groups;
|
|
37
|
+
}
|
|
38
|
+
function AddonScrollRow({ children }) {
|
|
39
|
+
const wrapRef = useRef(null);
|
|
40
|
+
const scrollRef = useRef(null);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const el = scrollRef.current;
|
|
43
|
+
const wrap = wrapRef.current;
|
|
44
|
+
if (!el || !wrap)
|
|
45
|
+
return;
|
|
46
|
+
const update = () => {
|
|
47
|
+
const canLeft = el.scrollLeft > 4;
|
|
48
|
+
const canRight = el.scrollLeft + el.clientWidth < el.scrollWidth - 4;
|
|
49
|
+
wrap.classList.toggle("can-scroll-left", canLeft);
|
|
50
|
+
wrap.classList.toggle("can-scroll-right", canRight);
|
|
51
|
+
};
|
|
52
|
+
update();
|
|
53
|
+
el.addEventListener("scroll", update, { passive: true });
|
|
54
|
+
const observer = new ResizeObserver(update);
|
|
55
|
+
observer.observe(el);
|
|
56
|
+
return () => {
|
|
57
|
+
el.removeEventListener("scroll", update);
|
|
58
|
+
observer.disconnect();
|
|
59
|
+
};
|
|
60
|
+
}, [children]);
|
|
61
|
+
return (_jsx("div", { ref: wrapRef, className: "vertebr-addon-scroll-wrap", children: _jsx("div", { ref: scrollRef, className: "vertebr-addon-scroll", tabIndex: 0, children: children }) }));
|
|
62
|
+
}
|
|
63
|
+
export function AddonOverview({ client, catalog, onSelectAddon }) {
|
|
64
|
+
const addons = catalog.addons ?? [];
|
|
65
|
+
const [states, setStates] = useState({});
|
|
66
|
+
const [loading, setLoading] = useState(true);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
let cancelled = false;
|
|
69
|
+
void (async () => {
|
|
70
|
+
setLoading(true);
|
|
71
|
+
const next = {};
|
|
72
|
+
await Promise.all(addons.map(async (addon) => {
|
|
73
|
+
try {
|
|
74
|
+
const config = await client.getConfig(addon.id);
|
|
75
|
+
next[addon.id] = {
|
|
76
|
+
enabled: Boolean(config.value.enabled),
|
|
77
|
+
value: config.value,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
next[addon.id] = { enabled: false, value: {} };
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
if (!cancelled) {
|
|
85
|
+
setStates(next);
|
|
86
|
+
setLoading(false);
|
|
87
|
+
}
|
|
88
|
+
})();
|
|
89
|
+
return () => {
|
|
90
|
+
cancelled = true;
|
|
91
|
+
};
|
|
92
|
+
}, [addons, client]);
|
|
93
|
+
const groups = useMemo(() => groupAddons(addons), [addons]);
|
|
94
|
+
const stats = useMemo(() => {
|
|
95
|
+
let enabled = 0;
|
|
96
|
+
const byCategory = {};
|
|
97
|
+
for (const addon of addons) {
|
|
98
|
+
const category = addon.category ?? "other";
|
|
99
|
+
byCategory[category] = (byCategory[category] ?? 0) + 1;
|
|
100
|
+
if (states[addon.id]?.enabled)
|
|
101
|
+
enabled += 1;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
total: addons.length,
|
|
105
|
+
enabled,
|
|
106
|
+
products: byCategory.product ?? 0,
|
|
107
|
+
engagement: byCategory.engagement ?? 0,
|
|
108
|
+
sales: byCategory.sales ?? 0,
|
|
109
|
+
};
|
|
110
|
+
}, [addons, states]);
|
|
111
|
+
if (!addons.length) {
|
|
112
|
+
return (_jsx("div", { className: "vertebr-addon-overview", children: _jsx("div", { className: "vertebr-empty", children: "No add-ons registered for this host." }) }));
|
|
113
|
+
}
|
|
114
|
+
return (_jsxs("div", { className: "vertebr-addon-overview", children: [_jsxs("header", { className: "vertebr-addon-overview-toolbar", children: [_jsx("div", { className: "vertebr-addon-overview-intro", children: _jsx("p", { children: "Optional storefront features grouped by purpose. Scroll each row to browse, then configure one to edit with a live shop preview." }) }), _jsxs("div", { className: "vertebr-addon-overview-stats", "aria-label": "Add-on summary", children: [_jsxs("div", { className: "vertebr-addon-stat", children: [_jsx("strong", { children: loading ? "—" : stats.enabled }), _jsx("span", { children: "Enabled" })] }), _jsxs("div", { className: "vertebr-addon-stat", children: [_jsx("strong", { children: stats.total }), _jsx("span", { children: "Total" })] }), _jsxs("div", { className: "vertebr-addon-stat", "data-category": "product", children: [_jsx("strong", { children: stats.products }), _jsx("span", { children: "Products" })] }), _jsxs("div", { className: "vertebr-addon-stat", "data-category": "engagement", children: [_jsx("strong", { children: stats.engagement }), _jsx("span", { children: "Engagement" })] }), _jsxs("div", { className: "vertebr-addon-stat", "data-category": "sales", children: [_jsx("strong", { children: stats.sales }), _jsx("span", { children: "Sales" })] })] })] }), groups.map((group) => (_jsxs("section", { className: "vertebr-addon-overview-section", "data-category": group.category, children: [_jsxs("div", { className: "vertebr-addon-section-head", children: [_jsxs("div", { className: "vertebr-addon-section-title", children: [_jsx("h3", { children: group.label }), _jsx("p", { children: group.hint })] }), _jsxs("span", { className: "vertebr-addon-section-count", children: [group.items.length, " add-on", group.items.length === 1 ? "" : "s"] })] }), _jsx(AddonScrollRow, { children: group.items.map((addon) => {
|
|
115
|
+
const state = states[addon.id];
|
|
116
|
+
const previewDraft = state
|
|
117
|
+
? { ...state.value, enabled: true }
|
|
118
|
+
: { enabled: true };
|
|
119
|
+
return (_jsxs("article", { className: "vertebr-addon-card", "data-category": addon.category ?? "engagement", children: [_jsx(AddonPreviewThumb, { addonId: addon.id, previewPath: addon.previewPath, draft: previewDraft, label: addon.label }), _jsxs("div", { className: "vertebr-addon-card-body", children: [_jsxs("div", { className: "vertebr-addon-card-head", children: [_jsx("h4", { children: addon.label }), _jsx("span", { className: "vertebr-addon-status", "data-enabled": loading
|
|
120
|
+
? undefined
|
|
121
|
+
: state?.enabled
|
|
122
|
+
? "true"
|
|
123
|
+
: "false", children: loading ? "…" : state?.enabled ? "On" : "Off" })] }), addon.description ? _jsx("p", { children: addon.description }) : null, _jsxs("div", { className: "vertebr-addon-card-foot", children: [addon.previewPath ? (_jsx("span", { className: "vertebr-addon-route", children: addon.previewPath })) : null, _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", onClick: () => onSelectAddon(addon.id), children: "Configure" })] })] })] }, addon.id));
|
|
124
|
+
}) })] }, group.category)))] }));
|
|
125
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type AddonPreviewThumbProps = {
|
|
2
|
+
addonId: string;
|
|
3
|
+
previewPath?: string;
|
|
4
|
+
draft?: Record<string, unknown> | null;
|
|
5
|
+
label?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Small live host iframe for add-on overview cards.
|
|
9
|
+
*/
|
|
10
|
+
export declare function AddonPreviewThumb({ addonId, previewPath, draft, label, }: AddonPreviewThumbProps): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=AddonPreviewThumb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddonPreviewThumb.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AddonPreviewThumb.tsx"],"names":[],"mappings":"AAOA,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,WAAW,EACX,KAAK,EACL,KAAK,GACN,EAAE,sBAAsB,+BA6ExB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { buildVertebrPreviewUrl, postVertebrPreviewDraft, subscribeVertebrPreviewReady, } from "@vertebr/client";
|
|
4
|
+
/**
|
|
5
|
+
* Small live host iframe for add-on overview cards.
|
|
6
|
+
*/
|
|
7
|
+
export function AddonPreviewThumb({ addonId, previewPath, draft, label, }) {
|
|
8
|
+
const iframeRef = useRef(null);
|
|
9
|
+
const draftRef = useRef(draft);
|
|
10
|
+
const [ready, setReady] = useState(false);
|
|
11
|
+
const [error, setError] = useState(false);
|
|
12
|
+
draftRef.current = draft;
|
|
13
|
+
const src = useMemo(() => {
|
|
14
|
+
if (!previewPath)
|
|
15
|
+
return null;
|
|
16
|
+
return buildVertebrPreviewUrl(previewPath, { controlId: addonId });
|
|
17
|
+
}, [addonId, previewPath]);
|
|
18
|
+
const pushDraft = () => {
|
|
19
|
+
const win = iframeRef.current?.contentWindow;
|
|
20
|
+
const value = draftRef.current;
|
|
21
|
+
if (!win || !value)
|
|
22
|
+
return;
|
|
23
|
+
postVertebrPreviewDraft(win, addonId, value);
|
|
24
|
+
};
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
setReady(false);
|
|
27
|
+
setError(false);
|
|
28
|
+
}, [src]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!src)
|
|
31
|
+
return;
|
|
32
|
+
return subscribeVertebrPreviewReady((source) => {
|
|
33
|
+
const win = iframeRef.current?.contentWindow;
|
|
34
|
+
if (!win || source !== win)
|
|
35
|
+
return;
|
|
36
|
+
setReady(true);
|
|
37
|
+
pushDraft();
|
|
38
|
+
});
|
|
39
|
+
}, [src]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!ready || !draft)
|
|
42
|
+
return;
|
|
43
|
+
pushDraft();
|
|
44
|
+
}, [draft, ready]);
|
|
45
|
+
if (!previewPath) {
|
|
46
|
+
return (_jsx("div", { className: "vertebr-addon-thumb vertebr-addon-thumb-empty", children: _jsx("span", { children: "No preview route" }) }));
|
|
47
|
+
}
|
|
48
|
+
return (_jsxs("div", { className: "vertebr-addon-thumb", "data-ready": ready ? "true" : undefined, "aria-label": label ? `Preview ${label}` : `Preview ${addonId}`, children: [_jsx("div", { className: "vertebr-addon-thumb-scale", children: error ? (_jsx("div", { className: "vertebr-addon-thumb-fallback", children: "Preview unavailable" })) : (_jsx("iframe", { ref: iframeRef, title: label ? `Preview ${label}` : `Preview ${addonId}`, src: src ?? undefined, tabIndex: -1, loading: "lazy", onLoad: () => {
|
|
49
|
+
setReady(true);
|
|
50
|
+
pushDraft();
|
|
51
|
+
}, onError: () => setError(true) })) }), !ready && !error ? (_jsx("div", { className: "vertebr-addon-thumb-loading", children: "Loading preview\u2026" })) : null] }));
|
|
52
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CatalogResponse } from "@vertebr/client";
|
|
2
|
+
import type { Selection } from "./types";
|
|
3
|
+
export type AdminCommandPaletteProps = {
|
|
4
|
+
catalog: CatalogResponse;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
onSelect: (selection: Selection) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function AdminCommandPalette({ catalog, onClose, onSelect, }: AdminCommandPaletteProps): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=AdminCommandPalette.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminCommandPalette.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminCommandPalette.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAOvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;CAC1C,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,OAAO,EACP,QAAQ,GACT,EAAE,wBAAwB,+BAqI1B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { IconSearch } from "../ui/icons";
|
|
4
|
+
import { adminSearchItems, filterAdminSearchItems, } from "./adminSearch";
|
|
5
|
+
export function AdminCommandPalette({ catalog, onClose, onSelect, }) {
|
|
6
|
+
const inputRef = useRef(null);
|
|
7
|
+
const [query, setQuery] = useState("");
|
|
8
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
9
|
+
const items = useMemo(() => adminSearchItems(catalog), [catalog]);
|
|
10
|
+
const results = useMemo(() => filterAdminSearchItems(items, query), [items, query]);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
inputRef.current?.focus();
|
|
13
|
+
}, []);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
setActiveIndex(0);
|
|
16
|
+
}, [query]);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const onKeyDown = (event) => {
|
|
19
|
+
if (event.key === "Escape") {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
onClose();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (results.length === 0)
|
|
25
|
+
return;
|
|
26
|
+
if (event.key === "ArrowDown") {
|
|
27
|
+
event.preventDefault();
|
|
28
|
+
setActiveIndex((current) => (current + 1) % results.length);
|
|
29
|
+
}
|
|
30
|
+
else if (event.key === "ArrowUp") {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
setActiveIndex((current) => (current - 1 + results.length) % results.length);
|
|
33
|
+
}
|
|
34
|
+
else if (event.key === "Enter") {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
const item = results[activeIndex];
|
|
37
|
+
if (item) {
|
|
38
|
+
onSelect(item.selection);
|
|
39
|
+
onClose();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
window.addEventListener("keydown", onKeyDown);
|
|
44
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
45
|
+
}, [activeIndex, onClose, onSelect, results]);
|
|
46
|
+
const pick = (item) => {
|
|
47
|
+
onSelect(item.selection);
|
|
48
|
+
onClose();
|
|
49
|
+
};
|
|
50
|
+
const grouped = useMemo(() => {
|
|
51
|
+
const map = new Map();
|
|
52
|
+
for (const item of results) {
|
|
53
|
+
const list = map.get(item.group) ?? [];
|
|
54
|
+
list.push(item);
|
|
55
|
+
map.set(item.group, list);
|
|
56
|
+
}
|
|
57
|
+
return map;
|
|
58
|
+
}, [results]);
|
|
59
|
+
let resultIndex = -1;
|
|
60
|
+
return (_jsx("div", { className: "vertebr-command-backdrop", role: "presentation", onMouseDown: (event) => {
|
|
61
|
+
if (event.target === event.currentTarget)
|
|
62
|
+
onClose();
|
|
63
|
+
}, children: _jsxs("div", { className: "vertebr-command", role: "dialog", "aria-modal": "true", "aria-label": "Search admin", children: [_jsxs("div", { className: "vertebr-command-input-wrap", children: [_jsx(IconSearch, {}), _jsx("input", { ref: inputRef, className: "vertebr-command-input", type: "search", placeholder: "Search pages, resources, settings\u2026", value: query, onChange: (event) => setQuery(event.target.value), autoComplete: "off", spellCheck: false }), _jsx("kbd", { children: "esc" })] }), _jsx("div", { className: "vertebr-command-results", role: "listbox", children: results.length === 0 ? (_jsx("div", { className: "vertebr-command-empty", children: "No matches found." })) : ([...grouped.entries()].map(([group, groupItems]) => (_jsxs("div", { className: "vertebr-command-group", children: [_jsx("div", { className: "vertebr-command-group-label", children: group }), groupItems.map((item) => {
|
|
64
|
+
resultIndex += 1;
|
|
65
|
+
const index = resultIndex;
|
|
66
|
+
return (_jsxs("button", { type: "button", className: "vertebr-command-item", role: "option", "aria-selected": index === activeIndex, "data-active": index === activeIndex || undefined, onMouseEnter: () => setActiveIndex(index), onClick: () => pick(item), children: [_jsx("span", { className: "vertebr-command-item-label", children: item.label }), item.hint ? (_jsx("span", { className: "vertebr-command-item-hint", children: item.hint })) : null] }, item.id));
|
|
67
|
+
})] }, group)))) })] }) }));
|
|
68
|
+
}
|
|
@@ -24,7 +24,10 @@ export type AdminContentProps = {
|
|
|
24
24
|
emailsLoading: boolean;
|
|
25
25
|
onRefreshEmails: () => Promise<void>;
|
|
26
26
|
resourceData: ResourceListResponse | null;
|
|
27
|
+
resourceSearch?: string;
|
|
28
|
+
onResourceSearchChange?: (q: string) => void;
|
|
27
29
|
onRefreshResource: () => Promise<void>;
|
|
30
|
+
onRefreshConfig?: () => Promise<void>;
|
|
28
31
|
configData: ConfigResponse | null;
|
|
29
32
|
onConfigSaved: (next: ConfigResponse) => void;
|
|
30
33
|
galleryPicker: {
|
|
@@ -42,5 +45,5 @@ export type AdminContentProps = {
|
|
|
42
45
|
onRefreshFonts?: () => Promise<void>;
|
|
43
46
|
};
|
|
44
47
|
};
|
|
45
|
-
export declare function AdminContent({ client, catalog, selection, onSelectionChange, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, onRefreshResource, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }: AdminContentProps): import("react").JSX.Element;
|
|
48
|
+
export declare function AdminContent({ client, catalog, selection, onSelectionChange, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, resourceSearch, onResourceSearchChange, onRefreshResource, onRefreshConfig, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }: AdminContentProps): import("react").JSX.Element;
|
|
46
49
|
//# sourceMappingURL=AdminContent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminContent.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminContent.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,aAAa,EACd,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"AdminContent.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminContent.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useState } from "react";
|
|
3
3
|
import { ConfigForm } from "../config/ConfigForm";
|
|
4
|
+
import { RevisionHistory } from "../config/RevisionHistory";
|
|
4
5
|
import { EmailsBoard } from "../emails/EmailsBoard";
|
|
5
6
|
import { FontLibraryBoard } from "../fonts/FontLibraryBoard";
|
|
6
7
|
import { GalleryBoard } from "../gallery/GalleryBoard";
|
|
7
8
|
import { MetricsBoard } from "../metrics/MetricsBoard";
|
|
8
9
|
import { ResourcePanel } from "../resources/ResourceTable";
|
|
10
|
+
import { hasCap } from "../../lib/capabilities";
|
|
11
|
+
import { AddonEditor } from "./AddonEditor";
|
|
12
|
+
import { AddonOverview } from "./AddonOverview";
|
|
13
|
+
import { AuditBoard } from "./AuditBoard";
|
|
9
14
|
import { PagePreviewPane } from "./PagePreviewPane";
|
|
10
|
-
|
|
15
|
+
import { AdminSyncPage } from "./AdminSyncPage";
|
|
16
|
+
function ConfigEditor({ client, catalog, configData, isPageSelection, previewPath, onConfigSaved, onRefreshConfig, galleryPicker, fontLibraryPicker, }) {
|
|
11
17
|
const [draft, setDraft] = useState(configData.value);
|
|
12
18
|
const showPreview = Boolean(previewPath);
|
|
19
|
+
const canWrite = hasCap(catalog, "config.write");
|
|
20
|
+
const showRevisions = Boolean(catalog.revisionsEnabled) || Boolean(configData.revisionsEnabled);
|
|
13
21
|
const onChange = useCallback((value) => {
|
|
14
22
|
setDraft(value);
|
|
15
23
|
}, []);
|
|
16
|
-
const form = (_jsx(ConfigForm, { schema: configData.schema, value: configData.value, onChange: showPreview ? onChange : undefined, onSave: async (next) => {
|
|
24
|
+
const form = (_jsx(ConfigForm, { schema: configData.schema, value: configData.value, client: client, canWrite: canWrite, onChange: showPreview ? onChange : undefined, onSave: async (next) => {
|
|
17
25
|
const saved = await client.setConfig(configData.id, next);
|
|
18
26
|
onConfigSaved(saved);
|
|
19
27
|
}, ...galleryPicker, ...fontLibraryPicker }));
|
|
20
|
-
return (_jsxs("div", { className: "vertebr-config-editor", children: [showPreview ? (_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: isPageSelection ? "Page content" : configData.label }), _jsxs("span", { className: "vertebr-count", children: [Object.keys(configData.schema).length, " fields"] })] })) : isPageSelection ? (_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: "Page content" }), _jsxs("span", { className: "vertebr-count", children: [Object.keys(configData.schema).length, " fields"] })] })) : null, showPreview && previewPath ? (_jsxs("div", { className: "vertebr-page-editor", children: [_jsx("div", { className: "vertebr-page-editor-form", children: form }), _jsx(PagePreviewPane, { previewPath: previewPath, controlId: configData.id, draft: draft ?? configData.value })] })) : (form)
|
|
28
|
+
return (_jsxs("div", { className: "vertebr-config-editor", children: [showPreview ? (_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: isPageSelection ? "Page content" : configData.label }), _jsxs("span", { className: "vertebr-count", children: [Object.keys(configData.schema).length, " fields"] })] })) : isPageSelection ? (_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: "Page content" }), _jsxs("span", { className: "vertebr-count", children: [Object.keys(configData.schema).length, " fields"] })] })) : null, showPreview && previewPath ? (_jsxs("div", { className: "vertebr-page-editor", children: [_jsx("div", { className: "vertebr-page-editor-form", children: form }), _jsx(PagePreviewPane, { previewPath: previewPath, controlId: configData.id, draft: draft ?? configData.value })] })) : (form), showRevisions ? (_jsx(RevisionHistory, { client: client, configId: configData.id, onPublished: async () => {
|
|
29
|
+
await onRefreshConfig?.();
|
|
30
|
+
} })) : null] }));
|
|
21
31
|
}
|
|
22
|
-
export function AdminContent({ client, catalog, selection, onSelectionChange, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, onRefreshResource, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }) {
|
|
32
|
+
export function AdminContent({ client, catalog, selection, onSelectionChange, authRequired, loading, isPageSelection, metricsData, metricsLoading, metricsRange, onMetricsRangeChange, onRefreshMetrics, galleryData, galleryLoading, onRefreshGallery, fontLibraryData, fontLibraryLoading, onRefreshFonts, emailsData, emailsLoading, onRefreshEmails, resourceData, resourceSearch, onResourceSearchChange, onRefreshResource, onRefreshConfig, configData, onConfigSaved, galleryPicker, fontLibraryPicker, }) {
|
|
23
33
|
if (!catalog) {
|
|
24
34
|
return (_jsx("div", { className: "vertebr-content", children: _jsx("section", { className: "vertebr-panel", children: _jsx("div", { className: "vertebr-empty", children: authRequired
|
|
25
35
|
? "Authenticated access required for this environment."
|
|
@@ -27,7 +37,7 @@ export function AdminContent({ client, catalog, selection, onSelectionChange, au
|
|
|
27
37
|
? "Loading…"
|
|
28
38
|
: "Unable to load catalog." }) }) }));
|
|
29
39
|
}
|
|
30
|
-
return (_jsx("div", { className: "vertebr-content", children: _jsxs("section", { className: "vertebr-panel", children: [selection?.kind === "overview" ? (_jsx(MetricsBoard, { label: metricsData?.label ?? catalog.metrics?.label, windowLabel: metricsData?.windowLabel, compareLabel: metricsData?.compareLabel, metrics: metricsData?.metrics ?? [], charts: metricsData?.charts ?? [], range: metricsRange, onRangeChange: onMetricsRangeChange, loading: metricsLoading, onRefresh: () => void onRefreshMetrics() })) : null, selection?.kind === "gallery" && galleryData ? (_jsx(GalleryBoard, { label: galleryData.label, items: galleryData.items, accept: galleryData.accept, maxBytes: galleryData.maxBytes, canDelete: galleryData.canDelete, loading: galleryLoading, onRefresh: () => void onRefreshGallery(), onUpload: async (files) => {
|
|
40
|
+
return (_jsx("div", { className: "vertebr-content", children: _jsxs("section", { className: "vertebr-panel", children: [selection?.kind === "overview" ? (_jsx(MetricsBoard, { label: metricsData?.label ?? catalog.metrics?.label, windowLabel: metricsData?.windowLabel, compareLabel: metricsData?.compareLabel, metrics: metricsData?.metrics ?? [], charts: metricsData?.charts ?? [], range: metricsRange, onRangeChange: onMetricsRangeChange, loading: metricsLoading, onRefresh: () => void onRefreshMetrics() })) : null, selection?.kind === "audit" ? (_jsx(AuditBoard, { client: client, label: catalog.audit?.label })) : null, selection?.kind === "sync" ? (_jsx(AdminSyncPage, { client: client, catalog: catalog })) : null, selection?.kind === "gallery" && galleryData ? (_jsx(GalleryBoard, { label: galleryData.label, items: galleryData.items, accept: galleryData.accept, maxBytes: galleryData.maxBytes, canDelete: galleryData.canDelete, loading: galleryLoading, onRefresh: () => void onRefreshGallery(), onUpload: async (files) => {
|
|
31
41
|
if (files.length === 1 && files[0]) {
|
|
32
42
|
await client.uploadGalleryFile(files[0]);
|
|
33
43
|
}
|
|
@@ -97,7 +107,7 @@ export function AdminContent({ client, catalog, selection, onSelectionChange, au
|
|
|
97
107
|
? async (id) => {
|
|
98
108
|
await client.deleteEmailLibraryTemplate(id);
|
|
99
109
|
}
|
|
100
|
-
: undefined })) : null, selection?.kind === "resource" && resourceData ? (_jsx(ResourcePanel, { schema: resourceData.schema, items: resourceData.items, label: resourceData.label, initialEditRowId: selection.rowId && selection.id === resourceData.id
|
|
110
|
+
: undefined })) : null, selection?.kind === "resource" && resourceData ? (_jsx(ResourcePanel, { schema: resourceData.schema, items: resourceData.items, label: resourceData.label, listQuery: resourceData.listQuery, total: resourceData.total, searchQuery: resourceSearch, onSearchChange: onResourceSearchChange, initialEditRowId: selection.rowId && selection.id === resourceData.id
|
|
101
111
|
? selection.rowId
|
|
102
112
|
: undefined, onEditRowIdChange: (rowId) => {
|
|
103
113
|
onSelectionChange?.({
|
|
@@ -105,7 +115,7 @@ export function AdminContent({ client, catalog, selection, onSelectionChange, au
|
|
|
105
115
|
id: resourceData.id,
|
|
106
116
|
rowId: rowId ?? undefined,
|
|
107
117
|
});
|
|
108
|
-
}, permissions: resourceData.permissions, actions: resourceData.actions, collectionActions: resourceData.collectionActions, uploads: resourceData.uploads, storageKey: resourceData.id, onUpdate: resourceData.permissions.update
|
|
118
|
+
}, permissions: resourceData.permissions, actions: resourceData.actions, collectionActions: resourceData.collectionActions, bulkActions: resourceData.bulkActions, uploads: resourceData.uploads, storageKey: resourceData.id, onUpdate: resourceData.permissions.update
|
|
109
119
|
? async (rowId, value) => {
|
|
110
120
|
await client.updateResource(resourceData.id, rowId, value);
|
|
111
121
|
await onRefreshResource();
|
|
@@ -126,7 +136,14 @@ export function AdminContent({ client, catalog, selection, onSelectionChange, au
|
|
|
126
136
|
return client.previewResourceMail(resourceData.id, rowId, actionId, input);
|
|
127
137
|
}, onCollectionAction: async (actionId, input) => {
|
|
128
138
|
return client.runCollectionAction(resourceData.id, actionId, input);
|
|
129
|
-
},
|
|
139
|
+
}, onBulkAction: resourceData.bulkActions
|
|
140
|
+
? async (actionId, rowIds, input) => {
|
|
141
|
+
await client.runBulkAction(resourceData.id, actionId, {
|
|
142
|
+
rowIds,
|
|
143
|
+
input,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
: undefined, onUpload: resourceData.uploads
|
|
130
147
|
? async (field, files) => {
|
|
131
148
|
if (files.length === 1 && files[0]) {
|
|
132
149
|
const result = await client.uploadResourceFile(resourceData.id, field, files[0]);
|
|
@@ -135,6 +152,11 @@ export function AdminContent({ client, catalog, selection, onSelectionChange, au
|
|
|
135
152
|
const result = await client.uploadResourceFiles(resourceData.id, field, files);
|
|
136
153
|
return result.files;
|
|
137
154
|
}
|
|
138
|
-
: undefined, ...galleryPicker })) : null, selection?.kind === "config" && configData ? (_jsx(ConfigEditor, { client: client, configData: configData, isPageSelection: isPageSelection, previewPath: catalog.configs.find((item) => item.id === configData.id)
|
|
139
|
-
?.previewPath, onConfigSaved: onConfigSaved, galleryPicker: galleryPicker, fontLibraryPicker: fontLibraryPicker }, configData.id)) : null
|
|
155
|
+
: undefined, ...galleryPicker })) : null, selection?.kind === "addons" ? (_jsx(AddonOverview, { client: client, catalog: catalog, onSelectAddon: (id) => onSelectionChange?.({ kind: "addon", id }) })) : null, selection?.kind === "config" && configData ? (_jsx(ConfigEditor, { client: client, catalog: catalog, configData: configData, isPageSelection: isPageSelection, previewPath: catalog.configs.find((item) => item.id === configData.id)
|
|
156
|
+
?.previewPath, onConfigSaved: onConfigSaved, onRefreshConfig: onRefreshConfig, galleryPicker: galleryPicker, fontLibraryPicker: fontLibraryPicker }, configData.id)) : null, selection?.kind === "addon" && configData ? ((() => {
|
|
157
|
+
const addon = catalog.addons?.find((item) => item.id === configData.id);
|
|
158
|
+
if (!addon)
|
|
159
|
+
return null;
|
|
160
|
+
return (_jsx(AddonEditor, { client: client, catalog: catalog, addon: addon, configData: configData, onConfigSaved: onConfigSaved, galleryPicker: galleryPicker, fontLibraryPicker: fontLibraryPicker }, configData.id));
|
|
161
|
+
})()) : null] }) }));
|
|
140
162
|
}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import type { AdminEnvironment } from "@vertebr/client";
|
|
2
1
|
import type { Breadcrumb } from "./breadcrumbs";
|
|
3
2
|
import type { Selection } from "./types";
|
|
4
3
|
export type AdminHeaderProps = {
|
|
5
4
|
activeLabel: string;
|
|
6
|
-
activeEnv?: AdminEnvironment;
|
|
7
5
|
catalogLoaded: boolean;
|
|
8
6
|
authRequired: boolean;
|
|
9
7
|
selection: Selection;
|
|
10
8
|
isPageSelection: boolean;
|
|
11
9
|
hasPagePreview?: boolean;
|
|
10
|
+
selectionDescription?: string;
|
|
12
11
|
error: string | null;
|
|
13
12
|
breadcrumbs: Breadcrumb[];
|
|
14
13
|
};
|
|
15
|
-
export declare function AdminHeader({ activeLabel,
|
|
14
|
+
export declare function AdminHeader({ activeLabel, catalogLoaded, authRequired, selection, isPageSelection, hasPagePreview, selectionDescription, error, breadcrumbs, }: AdminHeaderProps): import("react").JSX.Element;
|
|
16
15
|
//# sourceMappingURL=AdminHeader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminHeader.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"AdminHeader.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,CAAC;AAyDF,wBAAgB,WAAW,CAAC,EAC1B,WAAW,EACX,aAAa,EACb,YAAY,EACZ,SAAS,EACT,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,KAAK,EACL,WAAW,GACZ,EAAE,gBAAgB,+BAoClB"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
function subtitle({ catalogLoaded, authRequired, selection, isPageSelection, hasPagePreview, }) {
|
|
2
|
+
function subtitle({ catalogLoaded, authRequired, selection, isPageSelection, hasPagePreview, selectionDescription, }) {
|
|
3
3
|
if (!catalogLoaded && authRequired) {
|
|
4
4
|
return "Provide an access token to load this environment.";
|
|
5
5
|
}
|
|
6
|
+
if (selectionDescription) {
|
|
7
|
+
return selectionDescription;
|
|
8
|
+
}
|
|
6
9
|
if (selection?.kind === "overview") {
|
|
7
10
|
return "Commerce, growth, and catalog for the selected environment.";
|
|
8
11
|
}
|
|
12
|
+
if (selection?.kind === "sync") {
|
|
13
|
+
return "Copy selected content between environments.";
|
|
14
|
+
}
|
|
9
15
|
if (selection?.kind === "gallery") {
|
|
10
16
|
return "Browse and upload shared media assets.";
|
|
11
17
|
}
|
|
@@ -32,12 +38,13 @@ function subtitle({ catalogLoaded, authRequired, selection, isPageSelection, has
|
|
|
32
38
|
}
|
|
33
39
|
return "Select a panel.";
|
|
34
40
|
}
|
|
35
|
-
export function AdminHeader({ activeLabel,
|
|
36
|
-
return (_jsxs("header", { className: "vertebr-header", children: [_jsxs("div", { children: [breadcrumbs.length > 1 ? (_jsx("ol", { className: "vertebr-breadcrumbs", children: breadcrumbs.map((crumb, index) => (_jsx("li", { children: index === breadcrumbs.length - 1 ? (_jsx("strong", { children: crumb.label })) : (crumb.label) }, `${crumb.label}-${index}`))) })) : null,
|
|
41
|
+
export function AdminHeader({ activeLabel, catalogLoaded, authRequired, selection, isPageSelection, hasPagePreview, selectionDescription, error, breadcrumbs, }) {
|
|
42
|
+
return (_jsxs("header", { className: "vertebr-header", children: [_jsxs("div", { children: [breadcrumbs.length > 1 ? (_jsx("ol", { className: "vertebr-breadcrumbs", children: breadcrumbs.map((crumb, index) => (_jsx("li", { children: index === breadcrumbs.length - 1 ? (_jsx("strong", { children: crumb.label })) : (crumb.label) }, `${crumb.label}-${index}`))) })) : null, _jsx("h1", { children: activeLabel }), _jsx("p", { children: subtitle({
|
|
37
43
|
catalogLoaded,
|
|
38
44
|
authRequired,
|
|
39
45
|
selection,
|
|
40
46
|
isPageSelection,
|
|
41
47
|
hasPagePreview,
|
|
48
|
+
selectionDescription,
|
|
42
49
|
}) })] }), error ? (_jsx("span", { className: "vertebr-status", "data-tone": "danger", children: error })) : null] }));
|
|
43
50
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminPoweredBy.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminPoweredBy.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE,mBAAmB,+BAwBhE"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const VERTEBR_URL = "https://github.com/yaboijams/vertebr";
|
|
3
|
+
export function AdminPoweredBy({ collapsed }) {
|
|
4
|
+
if (collapsed) {
|
|
5
|
+
return (_jsx("div", { className: "vertebr-powered-by", "data-collapsed": "true", children: _jsx("a", { href: VERTEBR_URL, target: "_blank", rel: "noopener noreferrer", title: "Powered by Vertebr", "aria-label": "Powered by Vertebr", children: "V" }) }));
|
|
6
|
+
}
|
|
7
|
+
return (_jsx("div", { className: "vertebr-powered-by", children: _jsxs("a", { href: VERTEBR_URL, target: "_blank", rel: "noopener noreferrer", children: ["Powered by ", _jsx("strong", { children: "Vertebr" })] }) }));
|
|
8
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminSidebar.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminSidebar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminSidebar.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminSidebar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,SAAS,CAAC;AAG7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AA6FF,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,iBAAiB,+BAwBvE"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
+
import { AdminPoweredBy } from "./AdminPoweredBy";
|
|
3
4
|
import { iconForNavKey } from "../ui/icons";
|
|
4
5
|
function SidebarNavItem({ item, collapsed, }) {
|
|
5
6
|
const hasChildren = Boolean(item.children?.length);
|
|
@@ -11,17 +12,18 @@ function SidebarNavItem({ item, collapsed, }) {
|
|
|
11
12
|
if (sectionActive)
|
|
12
13
|
setOpen(true);
|
|
13
14
|
}, [sectionActive]);
|
|
14
|
-
if (!hasChildren) {
|
|
15
|
-
|
|
15
|
+
if (!hasChildren || collapsed) {
|
|
16
|
+
const active = hasChildren ? sectionActive : item.active;
|
|
17
|
+
return (_jsxs("button", { type: "button", "data-active": active || undefined, title: collapsed ? item.label : undefined, onClick: item.onSelect, children: [_jsx(Icon, { className: "vertebr-nav-icon" }), _jsx("span", { className: "vertebr-nav-label-text", children: item.label })] }));
|
|
16
18
|
}
|
|
17
19
|
return (_jsxs("div", { className: "vertebr-nav-item", "data-has-children": "true", "data-open": open || undefined, children: [_jsxs("div", { className: "vertebr-nav-parent", children: [_jsxs("button", { type: "button", className: "vertebr-nav-parent-label", "data-active": sectionActive && !childActive, "data-section": sectionActive || undefined, title: collapsed ? item.label : undefined, onClick: () => {
|
|
18
20
|
setOpen(true);
|
|
19
21
|
item.onSelect();
|
|
20
|
-
}, children: [_jsx(Icon, { className: "vertebr-nav-icon" }), _jsx("span", { className: "vertebr-nav-label-text", children: item.label })] }), !collapsed ? (_jsx("button", { type: "button", className: "vertebr-nav-parent-toggle", "aria-label": open ? `Collapse ${item.label}` : `Expand ${item.label}`, "aria-expanded": open, onClick: () => setOpen((prev) => !prev), children: _jsx("span", { "aria-hidden": "true", children:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
}, children: [_jsx(Icon, { className: "vertebr-nav-icon" }), _jsx("span", { className: "vertebr-nav-label-text", children: item.label })] }), !collapsed ? (_jsx("button", { type: "button", className: "vertebr-nav-parent-toggle", "aria-label": open ? `Collapse ${item.label}` : `Expand ${item.label}`, "aria-expanded": open, onClick: () => setOpen((prev) => !prev), children: _jsx("span", { className: "vertebr-nav-chevron", "aria-hidden": "true", children: "\u25B8" }) })) : null] }), _jsx("div", { className: "vertebr-nav-sub-wrap", "data-open": open ? "true" : undefined, children: _jsx("div", { className: "vertebr-nav-sub-inner", children: _jsx("div", { className: "vertebr-nav-sub", role: "group", children: item.children.map((child) => (_jsx("button", { type: "button", className: "vertebr-nav-subitem", "data-active": child.active, onClick: () => {
|
|
23
|
+
setOpen(true);
|
|
24
|
+
child.onSelect();
|
|
25
|
+
}, children: child.label }, child.key))) }) }) })] }));
|
|
24
26
|
}
|
|
25
27
|
export function AdminSidebar({ navGroups, collapsed }) {
|
|
26
|
-
return (
|
|
28
|
+
return (_jsxs("aside", { className: "vertebr-sidebar", "data-collapsed": collapsed || undefined, children: [_jsx("div", { className: "vertebr-sidebar-top", children: _jsx("nav", { className: "vertebr-nav", children: navGroups.map((group) => (_jsxs("div", { className: "vertebr-nav-group", children: [_jsx("div", { className: "vertebr-nav-label", children: group.label }), group.items.map((item) => (_jsx(SidebarNavItem, { item: item, collapsed: collapsed }, item.key)))] }, group.label))) }) }), _jsx("div", { className: "vertebr-sidebar-bottom", children: _jsx(AdminPoweredBy, { collapsed: collapsed }) })] }));
|
|
27
29
|
}
|