@vertebr/react 0.4.0 → 0.5.1

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 (82) hide show
  1. package/dist/components/admin/AdminContent.d.ts +36 -0
  2. package/dist/components/admin/AdminContent.d.ts.map +1 -0
  3. package/dist/components/admin/AdminContent.js +107 -0
  4. package/dist/components/admin/AdminHeader.d.ts +13 -0
  5. package/dist/components/admin/AdminHeader.d.ts.map +1 -0
  6. package/dist/components/admin/AdminHeader.js +32 -0
  7. package/dist/components/admin/AdminSidebar.d.ts +25 -0
  8. package/dist/components/admin/AdminSidebar.d.ts.map +1 -0
  9. package/dist/components/admin/AdminSidebar.js +31 -0
  10. package/dist/components/admin/VertebrAdmin.d.ts +3 -18
  11. package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
  12. package/dist/components/admin/VertebrAdmin.js +130 -120
  13. package/dist/components/admin/sidebar.d.ts +3 -0
  14. package/dist/components/admin/sidebar.d.ts.map +1 -0
  15. package/dist/components/admin/sidebar.js +13 -0
  16. package/dist/components/admin/storage.d.ts +4 -0
  17. package/dist/components/admin/storage.d.ts.map +1 -0
  18. package/dist/components/admin/storage.js +24 -0
  19. package/dist/components/admin/types.d.ts +53 -0
  20. package/dist/components/admin/types.d.ts.map +1 -0
  21. package/dist/components/admin/types.js +1 -0
  22. package/dist/components/config/form/ConfigForm.d.ts +1 -1
  23. package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
  24. package/dist/components/config/form/ConfigForm.js +5 -1
  25. package/dist/components/config/form/RecordConfigForm.d.ts +1 -0
  26. package/dist/components/config/form/RecordConfigForm.d.ts.map +1 -1
  27. package/dist/components/config/form/SettingsConfigForm.d.ts +2 -1
  28. package/dist/components/config/form/SettingsConfigForm.d.ts.map +1 -1
  29. package/dist/components/config/form/SettingsConfigForm.js +2 -2
  30. package/dist/components/config/form/helpers/fields.js +1 -1
  31. package/dist/components/config/form/types.d.ts +4 -0
  32. package/dist/components/config/form/types.d.ts.map +1 -1
  33. package/dist/components/emails/EmailRecipientSelect.d.ts +16 -0
  34. package/dist/components/emails/EmailRecipientSelect.d.ts.map +1 -0
  35. package/dist/components/emails/EmailRecipientSelect.js +10 -0
  36. package/dist/components/emails/EmailsActivityPanel.d.ts +11 -0
  37. package/dist/components/emails/EmailsActivityPanel.d.ts.map +1 -0
  38. package/dist/components/emails/EmailsActivityPanel.js +139 -0
  39. package/dist/components/emails/EmailsBoard.d.ts +47 -0
  40. package/dist/components/emails/EmailsBoard.d.ts.map +1 -0
  41. package/dist/components/emails/EmailsBoard.js +594 -0
  42. package/dist/components/emails/EmailsLibraryPanel.d.ts +25 -0
  43. package/dist/components/emails/EmailsLibraryPanel.d.ts.map +1 -0
  44. package/dist/components/emails/EmailsLibraryPanel.js +379 -0
  45. package/dist/components/emails/EmailsNavSelect.d.ts +18 -0
  46. package/dist/components/emails/EmailsNavSelect.d.ts.map +1 -0
  47. package/dist/components/emails/EmailsNavSelect.js +37 -0
  48. package/dist/components/emails/MultiCheckSelect.d.ts +18 -0
  49. package/dist/components/emails/MultiCheckSelect.d.ts.map +1 -0
  50. package/dist/components/emails/MultiCheckSelect.js +74 -0
  51. package/dist/components/metrics/MetricsBoard.d.ts +8 -2
  52. package/dist/components/metrics/MetricsBoard.d.ts.map +1 -1
  53. package/dist/components/metrics/MetricsBoard.js +55 -2
  54. package/dist/components/metrics/charts.d.ts +14 -0
  55. package/dist/components/metrics/charts.d.ts.map +1 -0
  56. package/dist/components/metrics/charts.js +212 -0
  57. package/dist/components/resources/ResourceActionModal.d.ts +14 -0
  58. package/dist/components/resources/ResourceActionModal.d.ts.map +1 -0
  59. package/dist/components/resources/ResourceActionModal.js +64 -0
  60. package/dist/components/resources/ResourceCell.d.ts.map +1 -1
  61. package/dist/components/resources/ResourceCell.js +25 -9
  62. package/dist/components/resources/ResourceMailModal.d.ts +12 -0
  63. package/dist/components/resources/ResourceMailModal.d.ts.map +1 -0
  64. package/dist/components/resources/ResourceMailModal.js +196 -0
  65. package/dist/components/resources/ResourceTable.d.ts +7 -3
  66. package/dist/components/resources/ResourceTable.d.ts.map +1 -1
  67. package/dist/components/resources/ResourceTable.js +204 -34
  68. package/dist/index.d.ts +7 -1
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +1 -0
  71. package/dist/lib/metricsRanges.d.ts +20 -0
  72. package/dist/lib/metricsRanges.d.ts.map +1 -0
  73. package/dist/lib/metricsRanges.js +14 -0
  74. package/dist/styles/cells.css +42 -0
  75. package/dist/styles/emails.css +632 -0
  76. package/dist/styles/index.css +3 -0
  77. package/dist/styles/mail.css +240 -0
  78. package/dist/styles/metrics.css +633 -0
  79. package/dist/styles/nav.css +103 -1
  80. package/dist/styles/panel.css +2 -68
  81. package/dist/styles/table.css +26 -3
  82. package/package.json +1 -1
@@ -1,38 +1,14 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useState } from "react";
3
3
  import { createVertebrClient, } from "@vertebr/client";
4
- import { ConfigForm } from "../config/ConfigForm";
5
- import { EnvSelect } from "../env/EnvSelect";
6
- import { GalleryBoard } from "../gallery/GalleryBoard";
7
- import { MetricsBoard } from "../metrics/MetricsBoard";
8
- import { ResourcePanel } from "../resources/ResourceTable";
9
- import { ThemeToggle } from "../theme/ThemeToggle";
10
4
  import { useVertebrTheme } from "../../hooks/useVertebrTheme";
11
- function readStored(key) {
12
- try {
13
- return localStorage.getItem(key);
14
- }
15
- catch {
16
- return null;
17
- }
18
- }
19
- function writeStored(key, value) {
20
- try {
21
- localStorage.setItem(key, value);
22
- }
23
- catch {
24
- // ignore
25
- }
26
- }
27
- function clearStored(key) {
28
- try {
29
- localStorage.removeItem(key);
30
- }
31
- catch {
32
- // ignore
33
- }
34
- }
35
- export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane", envStorageKey = "vertebr:env", tokenStorageKey = "vertebr:token", themeStorageKey = "vertebr:theme", getAccessToken, authUserLabel, onSignOut, }) {
5
+ import { parseMetricsRangeId, } from "../../lib/metricsRanges";
6
+ import { AdminContent } from "./AdminContent";
7
+ import { AdminHeader } from "./AdminHeader";
8
+ import { AdminSidebar } from "./AdminSidebar";
9
+ import { sidebarGroups } from "./sidebar";
10
+ import { clearStored, readStored, writeStored } from "./storage";
11
+ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane", envStorageKey = "vertebr:env", tokenStorageKey = "vertebr:token", themeStorageKey = "vertebr:theme", metricsRangeStorageKey = "vertebr:metrics-range", getAccessToken, authUserLabel, onSignOut, }) {
36
12
  const { theme, setTheme } = useVertebrTheme(themeStorageKey);
37
13
  const [environment, setEnvironment] = useState(() => readStored(envStorageKey) ?? "");
38
14
  const [token, setToken] = useState(() => readStored(tokenStorageKey) ?? "");
@@ -50,8 +26,11 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
50
26
  const [configData, setConfigData] = useState(null);
51
27
  const [metricsData, setMetricsData] = useState(null);
52
28
  const [metricsLoading, setMetricsLoading] = useState(false);
29
+ const [metricsRange, setMetricsRange] = useState(() => parseMetricsRangeId(readStored(metricsRangeStorageKey)));
53
30
  const [galleryData, setGalleryData] = useState(null);
54
31
  const [galleryLoading, setGalleryLoading] = useState(false);
32
+ const [emailsData, setEmailsData] = useState(null);
33
+ const [emailsLoading, setEmailsLoading] = useState(false);
55
34
  const [error, setError] = useState(null);
56
35
  const [loading, setLoading] = useState(true);
57
36
  useEffect(() => {
@@ -97,6 +76,8 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
97
76
  return current;
98
77
  if (current?.kind === "gallery" && next.gallery)
99
78
  return current;
79
+ if (current?.kind === "emails" && next.emails)
80
+ return current;
100
81
  if (current?.kind === "resource") {
101
82
  if (next.resources.some((r) => r.id === current.id))
102
83
  return current;
@@ -112,6 +93,8 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
112
93
  return { kind: "resource", id: firstResource.id };
113
94
  if (next.gallery)
114
95
  return { kind: "gallery" };
96
+ if (next.emails)
97
+ return { kind: "emails", tab: "compose" };
115
98
  const firstConfig = next.configs[0];
116
99
  if (firstConfig)
117
100
  return { kind: "config", id: firstConfig.id };
@@ -142,12 +125,13 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
142
125
  setError(null);
143
126
  if (selection.kind === "overview") {
144
127
  setMetricsLoading(true);
145
- const data = await client.getMetrics();
128
+ const data = await client.getMetrics({ range: metricsRange });
146
129
  if (!cancelled) {
147
130
  setMetricsData(data);
148
131
  setResourceData(null);
149
132
  setConfigData(null);
150
133
  setGalleryData(null);
134
+ setEmailsData(null);
151
135
  }
152
136
  }
153
137
  else if (selection.kind === "gallery") {
@@ -158,6 +142,18 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
158
142
  setResourceData(null);
159
143
  setConfigData(null);
160
144
  setMetricsData(null);
145
+ setEmailsData(null);
146
+ }
147
+ }
148
+ else if (selection.kind === "emails") {
149
+ setEmailsLoading(true);
150
+ const data = await client.getEmails();
151
+ if (!cancelled) {
152
+ setEmailsData(data);
153
+ setResourceData(null);
154
+ setConfigData(null);
155
+ setMetricsData(null);
156
+ setGalleryData(null);
161
157
  }
162
158
  }
163
159
  else if (selection.kind === "resource") {
@@ -167,6 +163,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
167
163
  setConfigData(null);
168
164
  setMetricsData(null);
169
165
  setGalleryData(null);
166
+ setEmailsData(null);
170
167
  }
171
168
  }
172
169
  else {
@@ -176,6 +173,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
176
173
  setResourceData(null);
177
174
  setMetricsData(null);
178
175
  setGalleryData(null);
176
+ setEmailsData(null);
179
177
  }
180
178
  }
181
179
  }
@@ -188,13 +186,14 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
188
186
  if (!cancelled) {
189
187
  setMetricsLoading(false);
190
188
  setGalleryLoading(false);
189
+ setEmailsLoading(false);
191
190
  }
192
191
  }
193
192
  })();
194
193
  return () => {
195
194
  cancelled = true;
196
195
  };
197
- }, [client, selection, catalog, environment]);
196
+ }, [client, selection, catalog, environment, metricsRange]);
198
197
  const refreshResource = useCallback(async () => {
199
198
  if (selection?.kind !== "resource")
200
199
  return;
@@ -204,13 +203,17 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
204
203
  const refreshMetrics = useCallback(async () => {
205
204
  setMetricsLoading(true);
206
205
  try {
207
- const data = await client.getMetrics();
206
+ const data = await client.getMetrics({ range: metricsRange, fresh: true });
208
207
  setMetricsData(data);
209
208
  }
210
209
  finally {
211
210
  setMetricsLoading(false);
212
211
  }
213
- }, [client]);
212
+ }, [client, metricsRange]);
213
+ const changeMetricsRange = useCallback((next) => {
214
+ setMetricsRange(next);
215
+ writeStored(metricsRangeStorageKey, next);
216
+ }, [metricsRangeStorageKey]);
214
217
  const refreshGallery = useCallback(async () => {
215
218
  setGalleryLoading(true);
216
219
  try {
@@ -221,6 +224,16 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
221
224
  setGalleryLoading(false);
222
225
  }
223
226
  }, [client]);
227
+ const refreshEmails = useCallback(async () => {
228
+ setEmailsLoading(true);
229
+ try {
230
+ const data = await client.getEmails({ fresh: true });
231
+ setEmailsData(data);
232
+ }
233
+ finally {
234
+ setEmailsLoading(false);
235
+ }
236
+ }, [client]);
224
237
  const listGallery = useCallback(async () => {
225
238
  const data = await client.getGallery();
226
239
  return data.items;
@@ -278,6 +291,79 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
278
291
  setTokenDraft("");
279
292
  clearStored(tokenStorageKey);
280
293
  };
294
+ const navGroups = useMemo(() => {
295
+ if (!catalog)
296
+ return [];
297
+ const entries = [];
298
+ if (catalog.metrics) {
299
+ entries.push({
300
+ key: "overview",
301
+ group: catalog.metrics.group ?? "Analytics",
302
+ label: catalog.metrics.label,
303
+ active: selection?.kind === "overview",
304
+ onSelect: () => setSelection({ kind: "overview" }),
305
+ });
306
+ }
307
+ for (const resource of catalog.resources) {
308
+ entries.push({
309
+ key: `resource:${resource.id}`,
310
+ group: resource.group ?? "Resources",
311
+ label: resource.label,
312
+ active: selection?.kind === "resource" && selection.id === resource.id,
313
+ onSelect: () => setSelection({ kind: "resource", id: resource.id }),
314
+ });
315
+ }
316
+ if (catalog.gallery) {
317
+ entries.push({
318
+ key: "gallery",
319
+ group: catalog.gallery.group ?? "Resources",
320
+ label: catalog.gallery.label,
321
+ active: selection?.kind === "gallery",
322
+ onSelect: () => setSelection({ kind: "gallery" }),
323
+ });
324
+ }
325
+ if (catalog.emails) {
326
+ const emailsTab = selection?.kind === "emails" ? (selection.tab ?? "compose") : null;
327
+ entries.push({
328
+ key: "emails",
329
+ group: catalog.emails.group ?? "Growth",
330
+ label: catalog.emails.label,
331
+ active: selection?.kind === "emails",
332
+ onSelect: () => setSelection({ kind: "emails", tab: "compose" }),
333
+ children: [
334
+ {
335
+ key: "emails:compose",
336
+ label: "Compose",
337
+ active: emailsTab === "compose",
338
+ onSelect: () => setSelection({ kind: "emails", tab: "compose" }),
339
+ },
340
+ {
341
+ key: "emails:templates",
342
+ label: "Templates",
343
+ active: emailsTab === "templates",
344
+ onSelect: () => setSelection({ kind: "emails", tab: "templates" }),
345
+ },
346
+ {
347
+ key: "emails:activity",
348
+ label: "Activity",
349
+ active: emailsTab === "activity",
350
+ onSelect: () => setSelection({ kind: "emails", tab: "activity" }),
351
+ },
352
+ ],
353
+ });
354
+ }
355
+ for (const config of catalog.configs) {
356
+ const fallback = config.section === "page" ? "Pages" : "Config";
357
+ entries.push({
358
+ key: `config:${config.id}`,
359
+ group: config.group ?? fallback,
360
+ label: config.label,
361
+ active: selection?.kind === "config" && selection.id === config.id,
362
+ onSelect: () => setSelection({ kind: "config", id: config.id }),
363
+ });
364
+ }
365
+ return sidebarGroups(entries);
366
+ }, [catalog, selection]);
281
367
  if (!bootstrap && loading) {
282
368
  return _jsx("div", { className: "vertebr-loading", children: "Loading Vertebr\u2026" });
283
369
  }
@@ -288,88 +374,12 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
288
374
  ? (catalog?.metrics?.label ?? "Overview")
289
375
  : selection?.kind === "gallery"
290
376
  ? (catalog?.gallery?.label ?? "Gallery")
291
- : selection?.kind === "resource"
292
- ? catalog?.resources.find((r) => r.id === selection.id)?.label
293
- : selection?.kind === "config"
294
- ? catalog?.configs.find((c) => c.id === selection.id)?.label
295
- : null;
296
- return (_jsxs("div", { className: "vertebr-root", "data-env": environment, children: [_jsxs("aside", { className: "vertebr-sidebar", children: [_jsxs("div", { className: "vertebr-sidebar-top", children: [_jsxs("div", { className: "vertebr-brand", children: [_jsx("strong", { children: bootstrap?.brand ?? catalog?.brand ?? "Vertebr" }), _jsx("span", { children: title })] }), envList.length > 0 ? (_jsxs("div", { className: "vertebr-env", children: [_jsx("div", { className: "vertebr-nav-label", children: "Environment" }), _jsx(EnvSelect, { environments: envList, value: environment, onChange: switchEnvironment })] })) : null, _jsxs("nav", { className: "vertebr-nav", children: [catalog?.metrics ? (_jsxs("div", { className: "vertebr-nav-group", children: [_jsx("div", { className: "vertebr-nav-label", children: "Analytics" }), _jsx("button", { type: "button", "data-active": selection?.kind === "overview", onClick: () => setSelection({ kind: "overview" }), children: catalog.metrics.label })] })) : null, catalog &&
297
- (catalog.resources.length > 0 || catalog.gallery) ? (_jsxs("div", { className: "vertebr-nav-group", children: [_jsx("div", { className: "vertebr-nav-label", children: "Resources" }), catalog.resources.map((resource) => (_jsx("button", { type: "button", "data-active": selection?.kind === "resource" &&
298
- selection.id === resource.id, onClick: () => setSelection({ kind: "resource", id: resource.id }), children: resource.label }, resource.id))), catalog.gallery ? (_jsx("button", { type: "button", "data-active": selection?.kind === "gallery", onClick: () => setSelection({ kind: "gallery" }), children: catalog.gallery.label })) : null] })) : null, catalog &&
299
- catalog.configs.some((c) => (c.section ?? "config") !== "page") ? (_jsxs("div", { className: "vertebr-nav-group", children: [_jsx("div", { className: "vertebr-nav-label", children: "Config" }), catalog.configs
300
- .filter((c) => (c.section ?? "config") !== "page")
301
- .map((config) => (_jsx("button", { type: "button", "data-active": selection?.kind === "config" &&
302
- selection.id === config.id, onClick: () => setSelection({ kind: "config", id: config.id }), children: config.label }, config.id)))] })) : null, catalog &&
303
- catalog.configs.some((c) => c.section === "page") ? (_jsxs("div", { className: "vertebr-nav-group", children: [_jsx("div", { className: "vertebr-nav-label", children: "Pages" }), catalog.configs
304
- .filter((c) => c.section === "page")
305
- .map((config) => (_jsx("button", { type: "button", "data-active": selection?.kind === "config" &&
306
- selection.id === config.id, onClick: () => setSelection({ kind: "config", id: config.id }), children: config.label }, config.id)))] })) : null] })] }), _jsxs("div", { className: "vertebr-sidebar-bottom", children: [getAccessToken && authUserLabel ? (_jsxs("div", { className: "vertebr-auth", children: [_jsx("div", { className: "vertebr-nav-label", children: "Signed in" }), _jsx("p", { className: "vertebr-auth-hint", children: authUserLabel }), onSignOut ? (_jsx("button", { type: "button", className: "vertebr-btn", onClick: () => void onSignOut(), children: "Sign out" })) : null] })) : null, !getAccessToken && (authRequired || token) ? (_jsxs("div", { className: "vertebr-auth", children: [_jsx("div", { className: "vertebr-nav-label", children: "Access" }), _jsx("p", { className: "vertebr-auth-hint", children: authRequired
307
- ? `This environment requires auth${activeEnv?.auth?.claimKey
308
- ? ` (${activeEnv.auth.claimKey})`
309
- : ""}.`
310
- : "Optional access token for protected environments." }), _jsx("input", { className: "vertebr-auth-input", type: "password", placeholder: "Bearer token", value: tokenDraft, onChange: (event) => setTokenDraft(event.target.value), autoComplete: "off" }), _jsxs("div", { className: "vertebr-auth-actions", children: [_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", onClick: saveToken, children: "Use token" }), token ? (_jsx("button", { type: "button", className: "vertebr-btn", onClick: clearToken, children: "Clear" })) : null] })] })) : null, _jsx(ThemeToggle, { theme: theme, onChange: setTheme })] })] }), _jsxs("main", { className: "vertebr-main", children: [_jsxs("header", { className: "vertebr-header", children: [_jsxs("div", { children: [_jsxs("div", { className: "vertebr-heading-row", children: [_jsx("h1", { children: activeLabel ?? (catalog ? "Dashboard" : "Sign in") }), activeEnv ? (_jsx("span", { className: "vertebr-env-badge", "data-tone": activeEnv.tone ?? "neutral", children: activeEnv.label })) : null] }), _jsx("p", { children: !catalog && authRequired
311
- ? "Provide an access token to load this environment."
312
- : selection?.kind === "overview"
313
- ? "Live metrics for the selected environment."
314
- : selection?.kind === "gallery"
315
- ? "Browse and upload shared media assets."
316
- : selection?.kind === "resource"
317
- ? "Manage records scoped to the selected environment."
318
- : selection?.kind === "config"
319
- ? isPageSelection
320
- ? "Edit content for this page."
321
- : "Owner-managed config for this environment."
322
- : "Select a panel." })] }), error ? (_jsx("span", { className: "vertebr-status", "data-tone": "danger", children: error })) : null] }), _jsx("div", { className: "vertebr-content", children: catalog ? (_jsxs("section", { className: "vertebr-panel", children: [selection?.kind === "overview" ? (_jsx(MetricsBoard, { label: metricsData?.label ?? catalog.metrics?.label, metrics: metricsData?.metrics ?? [], loading: metricsLoading, onRefresh: () => void refreshMetrics() })) : 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 refreshGallery(), onUpload: async (files) => {
323
- if (files.length === 1 && files[0]) {
324
- await client.uploadGalleryFile(files[0]);
325
- }
326
- else {
327
- await client.uploadGalleryFiles(files);
328
- }
329
- await refreshGallery();
330
- }, onDelete: galleryData.canDelete
331
- ? async (id) => {
332
- await client.deleteGalleryItem(id);
333
- await refreshGallery();
334
- }
335
- : undefined })) : null, selection?.kind === "resource" && resourceData ? (_jsx(ResourcePanel, { schema: resourceData.schema, items: resourceData.items, label: resourceData.label, permissions: resourceData.permissions, actions: resourceData.actions, uploads: resourceData.uploads, storageKey: resourceData.id, onUpdate: resourceData.permissions.update
336
- ? async (rowId, value) => {
337
- await client.updateResource(resourceData.id, rowId, value);
338
- await refreshResource();
339
- }
340
- : undefined, onCreate: resourceData.permissions.create
341
- ? async (value) => {
342
- await client.createResource(resourceData.id, value);
343
- await refreshResource();
344
- }
345
- : undefined, onDelete: resourceData.permissions.delete
346
- ? async (rowId) => {
347
- await client.deleteResource(resourceData.id, rowId);
348
- await refreshResource();
349
- }
350
- : undefined, onAction: async (rowId, actionId) => {
351
- await client.runResourceAction(resourceData.id, rowId, actionId);
352
- await refreshResource();
353
- }, onUpload: resourceData.uploads
354
- ? async (field, files) => {
355
- if (files.length === 1 && files[0]) {
356
- const result = await client.uploadResourceFile(resourceData.id, field, files[0]);
357
- return [
358
- { url: result.url, name: result.name },
359
- ];
360
- }
361
- const result = await client.uploadResourceFiles(resourceData.id, field, files);
362
- return result.files;
363
- }
364
- : undefined, ...galleryPicker })) : null, selection?.kind === "config" && configData ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: isPageSelection ? "Page content" : "Settings" }), _jsxs("span", { className: "vertebr-count", children: [Object.keys(configData.schema).length, " fields"] })] }), _jsx(ConfigForm, { schema: configData.schema, value: configData.value, onSave: async (next) => {
365
- const saved = await client.setConfig(configData.id, next);
366
- setConfigData({
367
- ...configData,
368
- value: saved.value,
369
- });
370
- }, ...galleryPicker })] })) : null] })) : (_jsx("section", { className: "vertebr-panel", children: _jsx("div", { className: "vertebr-empty", children: authRequired
371
- ? "Authenticated access required for this environment."
372
- : loading
373
- ? "Loading…"
374
- : "Unable to load catalog." }) })) })] })] }));
377
+ : selection?.kind === "emails"
378
+ ? (catalog?.emails?.label ?? "Emails")
379
+ : selection?.kind === "resource"
380
+ ? catalog?.resources.find((r) => r.id === selection.id)?.label
381
+ : selection?.kind === "config"
382
+ ? catalog?.configs.find((c) => c.id === selection.id)?.label
383
+ : null;
384
+ return (_jsxs("div", { className: "vertebr-root", "data-env": environment, children: [_jsx(AdminSidebar, { brand: bootstrap?.brand ?? catalog?.brand ?? "Vertebr", title: title, envList: envList, environment: environment, onEnvironmentChange: switchEnvironment, navGroups: navGroups, getAccessToken: getAccessToken, authUserLabel: authUserLabel, onSignOut: onSignOut, authRequired: authRequired, activeEnv: activeEnv, token: token, tokenDraft: tokenDraft, onTokenDraftChange: setTokenDraft, onSaveToken: saveToken, onClearToken: clearToken, theme: theme, onThemeChange: setTheme }), _jsxs("main", { className: "vertebr-main", children: [_jsx(AdminHeader, { activeLabel: activeLabel ?? (catalog ? "Dashboard" : "Sign in"), activeEnv: activeEnv, catalogLoaded: Boolean(catalog), authRequired: authRequired, selection: selection, isPageSelection: isPageSelection, error: error }), _jsx(AdminContent, { client: client, catalog: catalog, selection: selection, authRequired: authRequired, loading: loading, isPageSelection: isPageSelection, metricsData: metricsData, metricsLoading: metricsLoading, metricsRange: metricsRange, onMetricsRangeChange: changeMetricsRange, onRefreshMetrics: refreshMetrics, galleryData: galleryData, galleryLoading: galleryLoading, onRefreshGallery: refreshGallery, emailsData: emailsData, emailsLoading: emailsLoading, onRefreshEmails: refreshEmails, resourceData: resourceData, onRefreshResource: refreshResource, configData: configData, onConfigSaved: setConfigData, galleryPicker: galleryPicker })] })] }));
375
385
  }
@@ -0,0 +1,3 @@
1
+ import type { SidebarEntry, SidebarNavGroup } from "./types";
2
+ export declare function sidebarGroups(entries: SidebarEntry[]): SidebarNavGroup[];
3
+ //# sourceMappingURL=sidebar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["../../../src/components/admin/sidebar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE7D,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,eAAe,EAAE,CAYxE"}
@@ -0,0 +1,13 @@
1
+ export function sidebarGroups(entries) {
2
+ const order = [];
3
+ const map = new Map();
4
+ for (const entry of entries) {
5
+ const group = entry.group.trim() || "Resources";
6
+ if (!map.has(group)) {
7
+ map.set(group, []);
8
+ order.push(group);
9
+ }
10
+ map.get(group).push(entry);
11
+ }
12
+ return order.map((label) => ({ label, items: map.get(label) }));
13
+ }
@@ -0,0 +1,4 @@
1
+ export declare function readStored(key: string): string | null;
2
+ export declare function writeStored(key: string, value: string): void;
3
+ export declare function clearStored(key: string): void;
4
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/components/admin/storage.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMrD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAM5D;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAM7C"}
@@ -0,0 +1,24 @@
1
+ export function readStored(key) {
2
+ try {
3
+ return localStorage.getItem(key);
4
+ }
5
+ catch {
6
+ return null;
7
+ }
8
+ }
9
+ export function writeStored(key, value) {
10
+ try {
11
+ localStorage.setItem(key, value);
12
+ }
13
+ catch {
14
+ // ignore
15
+ }
16
+ }
17
+ export function clearStored(key) {
18
+ try {
19
+ localStorage.removeItem(key);
20
+ }
21
+ catch {
22
+ // ignore
23
+ }
24
+ }
@@ -0,0 +1,53 @@
1
+ export type VertebrAdminProps = {
2
+ /** Base URL for Vertebr API, e.g. `/admin/api` */
3
+ apiBase?: string;
4
+ title?: string;
5
+ /** localStorage key for remembering the selected environment */
6
+ envStorageKey?: string;
7
+ /** localStorage key for the access token */
8
+ tokenStorageKey?: string;
9
+ /** localStorage key for light/dark theme preference */
10
+ themeStorageKey?: string;
11
+ /** localStorage key for the overview metrics range */
12
+ metricsRangeStorageKey?: string;
13
+ /** Optional host-provided token getter (Firebase ID token, etc.) */
14
+ getAccessToken?: () => string | null | undefined | Promise<string | null | undefined>;
15
+ /** Signed-in identity shown in the dashboard. */
16
+ authUserLabel?: string;
17
+ /** Host-provided sign-out action. */
18
+ onSignOut?: () => void | Promise<void>;
19
+ };
20
+ export type EmailsTab = "compose" | "templates" | "activity";
21
+ export type Selection = {
22
+ kind: "overview";
23
+ } | {
24
+ kind: "resource";
25
+ id: string;
26
+ } | {
27
+ kind: "gallery";
28
+ } | {
29
+ kind: "emails";
30
+ tab?: EmailsTab;
31
+ } | {
32
+ kind: "config";
33
+ id: string;
34
+ } | null;
35
+ export type SidebarEntry = {
36
+ key: string;
37
+ group: string;
38
+ label: string;
39
+ active: boolean;
40
+ onSelect: () => void;
41
+ /** Nested items shown under this entry (e.g. Emails sections). */
42
+ children?: Array<{
43
+ key: string;
44
+ label: string;
45
+ active: boolean;
46
+ onSelect: () => void;
47
+ }>;
48
+ };
49
+ export type SidebarNavGroup = {
50
+ label: string;
51
+ items: SidebarEntry[];
52
+ };
53
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/admin/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,oEAAoE;IACpE,cAAc,CAAC,EAAE,MACb,MAAM,GACN,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACvC,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAE7D,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B,IAAI,CAAC;AAET,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;KACtB,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
1
  import type { ConfigFormProps } from "./types";
2
2
  export type { ConfigFormProps, ConfigFormVariant } from "./types";
3
- export declare function ConfigForm({ schema, value, onSave, submitLabel, variant, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, }: ConfigFormProps): import("react").JSX.Element;
3
+ export declare function ConfigForm({ schema, value, onSave, submitLabel, variant, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onChange, hideActions, }: ConfigFormProps): import("react").JSX.Element;
4
4
  //# sourceMappingURL=ConfigForm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigForm.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAElE,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAkB,EAClB,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,GAChB,EAAE,eAAe,+BAyFjB"}
1
+ {"version":3,"file":"ConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigForm.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAElE,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAkB,EAClB,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,QAAQ,EACR,WAAW,GACZ,EAAE,eAAe,+BA8FjB"}
@@ -5,7 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
5
5
  import { buildZodSchema, groupFields, withSpecVisibility, withSpecVisibilityValues, } from "./helpers";
6
6
  import { RecordConfigForm } from "./RecordConfigForm";
7
7
  import { SettingsConfigForm } from "./SettingsConfigForm";
8
- export function ConfigForm({ schema, value, onSave, submitLabel, variant = "config", uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, }) {
8
+ export function ConfigForm({ schema, value, onSave, submitLabel, variant = "config", uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onChange, hideActions, }) {
9
9
  const editorSchema = useMemo(() => (variant === "record" ? withSpecVisibility(schema) : schema), [schema, variant]);
10
10
  const editorValue = useMemo(() => variant === "record"
11
11
  ? withSpecVisibilityValues(editorSchema, value)
@@ -19,6 +19,9 @@ export function ConfigForm({ schema, value, onSave, submitLabel, variant = "conf
19
19
  defaultValues: editorValue,
20
20
  });
21
21
  const watched = useWatch({ control: form.control });
22
+ useEffect(() => {
23
+ onChange?.((watched ?? {}));
24
+ }, [watched, onChange]);
22
25
  useEffect(() => {
23
26
  form.reset(editorValue);
24
27
  }, [editorValue, form]);
@@ -67,6 +70,7 @@ export function ConfigForm({ schema, value, onSave, submitLabel, variant = "conf
67
70
  tone,
68
71
  onDiscard,
69
72
  submitLabel,
73
+ hideActions,
70
74
  };
71
75
  if (variant === "record") {
72
76
  return _jsx(RecordConfigForm, { ...shared });
@@ -17,6 +17,7 @@ export type RecordConfigFormProps = GalleryHandlers & {
17
17
  tone?: "ok" | "danger";
18
18
  onDiscard: () => void;
19
19
  submitLabel?: string;
20
+ hideActions?: boolean;
20
21
  };
21
22
  export declare function RecordConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, }: RecordConfigFormProps): import("react").JSX.Element;
22
23
  //# sourceMappingURL=RecordConfigForm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RecordConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/RecordConfigForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAYvE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG;IACpD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,QAAQ,EACR,OAAO,EACP,MAAM,EACN,IAAI,EACJ,SAAS,EACT,WAAW,GACZ,EAAE,qBAAqB,+BAoJvB"}
1
+ {"version":3,"file":"RecordConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/RecordConfigForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAYvE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG;IACpD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,QAAQ,EACR,OAAO,EACP,MAAM,EACN,IAAI,EACJ,SAAS,EACT,WAAW,GACZ,EAAE,qBAAqB,+BAoJvB"}
@@ -17,6 +17,7 @@ export type SettingsConfigFormProps = GalleryHandlers & {
17
17
  tone?: "ok" | "danger";
18
18
  onDiscard: () => void;
19
19
  submitLabel?: string;
20
+ hideActions?: boolean;
20
21
  };
21
- export declare function SettingsConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, }: SettingsConfigFormProps): import("react").JSX.Element;
22
+ export declare function SettingsConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, hideActions, }: SettingsConfigFormProps): import("react").JSX.Element;
22
23
  //# sourceMappingURL=SettingsConfigForm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/SettingsConfigForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGvE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,MAAM,uBAAuB,GAAG,eAAe,GAAG;IACtD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,QAAQ,EACR,OAAO,EACP,MAAM,EACN,IAAI,EACJ,SAAS,EACT,WAAW,GACZ,EAAE,uBAAuB,+BAoDzB"}
1
+ {"version":3,"file":"SettingsConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/SettingsConfigForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGvE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,MAAM,uBAAuB,GAAG,eAAe,GAAG;IACtD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,QAAQ,EACR,OAAO,EACP,MAAM,EACN,IAAI,EACJ,SAAS,EACT,WAAW,EACX,WAAW,GACZ,EAAE,uBAAuB,+BAsDzB"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ConfigField } from "./ConfigField";
3
3
  import { ConfigFormActions } from "./ConfigFormActions";
4
- export function SettingsConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, }) {
4
+ export function SettingsConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, hideActions, }) {
5
5
  const singleGroup = groups.length === 1;
6
- return (_jsxs("form", { className: "vertebr-form", "data-variant": "config", onSubmit: onSubmit, children: [_jsx("div", { className: "vertebr-form-scroll", children: groups.map((group) => (_jsxs("div", { className: "vertebr-form-section", "data-plain": singleGroup ? "true" : undefined, children: [!singleGroup ? (_jsxs("div", { className: "vertebr-form-section-head", children: [_jsx("h3", { children: group.label }), _jsx("span", { className: "vertebr-form-section-count", children: group.fields.length })] })) : null, _jsx("div", { className: "vertebr-form-fields", children: group.fields.map(([key, field]) => (_jsx(ConfigField, { fieldKey: key, field: field, groupLabel: group.label, variant: "config", schema: schema, watched: watched, form: form, uploads: uploads, onUpload: onUpload, galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery }, key))) })] }, group.id))) }), _jsx(ConfigFormActions, { form: form, isDirty: isDirty, status: status, tone: tone, onDiscard: onDiscard, submitLabel: submitLabel })] }));
6
+ return (_jsxs("form", { className: "vertebr-form", "data-variant": "config", onSubmit: onSubmit, children: [_jsx("div", { className: "vertebr-form-scroll", children: groups.map((group) => (_jsxs("div", { className: "vertebr-form-section", "data-plain": singleGroup ? "true" : undefined, children: [!singleGroup ? (_jsxs("div", { className: "vertebr-form-section-head", children: [_jsx("h3", { children: group.label }), _jsx("span", { className: "vertebr-form-section-count", children: group.fields.length })] })) : null, _jsx("div", { className: "vertebr-form-fields", children: group.fields.map(([key, field]) => (_jsx(ConfigField, { fieldKey: key, field: field, groupLabel: group.label, variant: "config", schema: schema, watched: watched, form: form, uploads: uploads, onUpload: onUpload, galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery }, key))) })] }, group.id))) }), hideActions ? null : (_jsx(ConfigFormActions, { form: form, isDirty: isDirty, status: status, tone: tone, onDiscard: onDiscard, submitLabel: submitLabel }))] }));
7
7
  }
@@ -14,7 +14,7 @@ export function isSpecsField(key, field) {
14
14
  }
15
15
  export function isMultilineField(key, field) {
16
16
  const hay = `${key} ${field?.label ?? ""}`;
17
- return /(detail|notes|bio|bullets)/i.test(hay);
17
+ return /(detail|notes|bio|bullets|body|message|html)/i.test(hay);
18
18
  }
19
19
  export function spanFor(key, field, variant) {
20
20
  if (field.type === "boolean")
@@ -18,6 +18,10 @@ export type ConfigFormProps = {
18
18
  galleryUpload?: Pick<ResourceUploadMeta, "accept" | "maxBytes">;
19
19
  onListGallery?: () => Promise<GalleryItem[]>;
20
20
  onUploadGallery?: (files: File[]) => Promise<GalleryItem[]>;
21
+ /** Live field values (e.g. mail preview). */
22
+ onChange?: (value: Record<string, unknown>) => void;
23
+ /** Hide Save / Discard — parent owns submit. */
24
+ hideActions?: boolean;
21
25
  };
22
26
  export type FieldEntry = [string, FieldSchema];
23
27
  export type FieldGroup = {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpD,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,sDAAsD;IACtD,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;CAC7D,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,eAAe,EACf,eAAe,GAAG,eAAe,GAAG,iBAAiB,CACtD,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpD,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,sDAAsD;IACtD,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACpD,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,eAAe,EACf,eAAe,GAAG,eAAe,GAAG,iBAAiB,CACtD,CAAC"}
@@ -0,0 +1,16 @@
1
+ export type EmailRecipientOption = {
2
+ email: string;
3
+ id?: string;
4
+ label?: string;
5
+ /** Audience resource this address came from. */
6
+ resourceId?: string;
7
+ };
8
+ export type EmailRecipientSelectProps = {
9
+ options: EmailRecipientOption[];
10
+ value: string[];
11
+ loading?: boolean;
12
+ disabled?: boolean;
13
+ onChange: (emails: string[]) => void;
14
+ };
15
+ export declare function EmailRecipientSelect({ options, value, loading, disabled, onChange, }: EmailRecipientSelectProps): import("react").JSX.Element;
16
+ //# sourceMappingURL=EmailRecipientSelect.d.ts.map