@realiizlabs/admin 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -45,4 +45,22 @@ declare function defineContentType<S extends ZodType>(entry: ContentTypeEntry<S>
45
45
 
46
46
  declare function titlecase(name: string): string;
47
47
 
48
- export { ContentTypeEntry, FieldSpec, defineContentType, titlecase, walkSchema };
48
+ /**
49
+ * navFromContentTypes — the sidebar derives from the registry.
50
+ *
51
+ * Adding a content type to a site (one defineContentType entry) puts it in
52
+ * the nav with no shell change. Pure; lives in /forms so the server layout
53
+ * can compute it without importing the client bundle.
54
+ */
55
+
56
+ interface NavEntry {
57
+ id: string;
58
+ label: string;
59
+ href: string;
60
+ icon: string;
61
+ }
62
+ declare function navFromContentTypes(entries: Record<string, ContentTypeEntry> | ContentTypeEntry[], opts?: {
63
+ basePath?: string;
64
+ }): NavEntry[];
65
+
66
+ export { ContentTypeEntry, FieldSpec, type NavEntry, defineContentType, navFromContentTypes, titlecase, walkSchema };
@@ -1,3 +1,3 @@
1
- export { defineContentType, titlecase, walkSchema } from '../chunk-IS52OXZ2.js';
1
+ export { defineContentType, navFromContentTypes, titlecase, walkSchema } from '../chunk-FGKT5FD4.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { walkSchema } from '../chunk-IS52OXZ2.js';
2
+ import { walkSchema } from '../chunk-FGKT5FD4.js';
3
3
  import { FORM_CSS } from '../chunk-4OCBMOEP.js';
4
4
  import { useId, useMemo, useState, useEffect, useCallback } from 'react';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -0,0 +1,436 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/shell/Sidebar.tsx
8
+ var base = {
9
+ width: 18,
10
+ height: 18,
11
+ viewBox: "0 0 24 24",
12
+ fill: "none",
13
+ stroke: "currentColor",
14
+ strokeWidth: 1.7,
15
+ strokeLinecap: "round",
16
+ strokeLinejoin: "round",
17
+ "aria-hidden": true,
18
+ style: { flexShrink: 0 }
19
+ };
20
+ function Icon({ name }) {
21
+ switch (name) {
22
+ case "home":
23
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base, children: [
24
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "7", height: "9", rx: "1" }),
25
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "3", width: "7", height: "5", rx: "1" }),
26
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "12", width: "7", height: "9", rx: "1" }),
27
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "16", width: "7", height: "5", rx: "1" })
28
+ ] });
29
+ case "posts":
30
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base, children: [
31
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 3h9l4 4v14H6z" }),
32
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 3v4h4" }),
33
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 12h7M9 16h7M9 8h3" })
34
+ ] });
35
+ case "events":
36
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base, children: [
37
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "5", width: "18", height: "16", rx: "2" }),
38
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 3v4M16 3v4M3 10h18" })
39
+ ] });
40
+ case "venues":
41
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base, children: [
42
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 21s7-6.2 7-11.5A7 7 0 0 0 5 9.5C5 14.8 12 21 12 21z" }),
43
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "9.5", r: "2.5" })
44
+ ] });
45
+ case "users":
46
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base, children: [
47
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "8", r: "3.5" }),
48
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2.5 20a6.5 6.5 0 0 1 13 0" }),
49
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "17", cy: "9", r: "2.5" }),
50
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15.5 14.5a5 5 0 0 1 6 5" })
51
+ ] });
52
+ case "settings":
53
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base, children: [
54
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "3" }),
55
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z" })
56
+ ] });
57
+ default:
58
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base, children: [
59
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 3h9l4 4v14H6z" }),
60
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 3v4h4" })
61
+ ] });
62
+ }
63
+ }
64
+ function iconFor(id) {
65
+ const known = ["home", "posts", "events", "venues", "users", "settings"];
66
+ return known.includes(id) ? id : "doc";
67
+ }
68
+ var SIDEBAR_KEY = "rz-admin-sidebar";
69
+ function isActive(item, activeHref, basePath) {
70
+ if (item.href === basePath) return activeHref === basePath || activeHref === `${basePath}/`;
71
+ return activeHref === item.href || activeHref.startsWith(`${item.href}/`);
72
+ }
73
+ function Sidebar({ items, activeHref, basePath }) {
74
+ const [collapsed, setCollapsed] = react.useState(false);
75
+ react.useEffect(() => {
76
+ try {
77
+ if (window.localStorage.getItem(SIDEBAR_KEY) === "collapsed") setCollapsed(true);
78
+ } catch {
79
+ }
80
+ }, []);
81
+ const toggle = () => {
82
+ const next = !collapsed;
83
+ setCollapsed(next);
84
+ try {
85
+ window.localStorage.setItem(SIDEBAR_KEY, next ? "collapsed" : "open");
86
+ } catch {
87
+ }
88
+ };
89
+ return /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: ["rz-sidebar", collapsed && "rz-sidebar--collapsed"].filter(Boolean).join(" "), "aria-label": "Admin navigation", children: [
90
+ /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "rz-sidebar__nav", children: items.map((item) => {
91
+ const active = isActive(item, activeHref, basePath);
92
+ return /* @__PURE__ */ jsxRuntime.jsxs(
93
+ "a",
94
+ {
95
+ href: item.href,
96
+ className: ["rz-sidebar__item", active && "rz-sidebar__item--active"].filter(Boolean).join(" "),
97
+ "aria-current": active ? "page" : void 0,
98
+ title: collapsed ? item.label : void 0,
99
+ children: [
100
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: iconFor(item.icon ?? item.id) }),
101
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-sidebar__label", children: item.label })
102
+ ]
103
+ },
104
+ item.id
105
+ );
106
+ }) }),
107
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-sidebar__collapse", children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rz-btn rz-btn--ghost rz-btn--sm", onClick: toggle, "aria-label": collapsed ? "Expand navigation" : "Collapse navigation", children: collapsed ? "\xBB" : "\xAB Collapse" }) })
108
+ ] });
109
+ }
110
+ function buttonClass(variant = "secondary", size = "md", extra) {
111
+ return ["rz-btn", `rz-btn--${variant}`, `rz-btn--${size}`, extra].filter(Boolean).join(" ");
112
+ }
113
+ function buttonStyle(variant = "secondary", size = "md") {
114
+ return { className: buttonClass(variant, size) };
115
+ }
116
+ function Button({ variant = "secondary", size = "md", className, type = "button", ...rest }) {
117
+ return /* @__PURE__ */ jsxRuntime.jsx("button", { type, className: buttonClass(variant, size, className), ...rest });
118
+ }
119
+ function LinkButton({ variant = "secondary", size = "md", className, ...rest }) {
120
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { className: buttonClass(variant, size, className), ...rest });
121
+ }
122
+ var THEME_KEY = "rz-admin-theme";
123
+ function useTheme(defaultTheme = "light") {
124
+ const [theme, setTheme] = react.useState(defaultTheme);
125
+ react.useEffect(() => {
126
+ try {
127
+ const stored = window.localStorage.getItem(THEME_KEY);
128
+ if (stored === "light" || stored === "dark") setTheme(stored);
129
+ } catch {
130
+ }
131
+ }, []);
132
+ const set = (t) => {
133
+ setTheme(t);
134
+ try {
135
+ window.localStorage.setItem(THEME_KEY, t);
136
+ } catch {
137
+ }
138
+ };
139
+ return [theme, set];
140
+ }
141
+ function ThemeToggle({ theme, onChange }) {
142
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rz-seg", role: "group", "aria-label": "Theme", children: [
143
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-pressed": theme === "light", onClick: () => onChange("light"), children: "Light" }),
144
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-pressed": theme === "dark", onClick: () => onChange("dark"), children: "Dark" })
145
+ ] });
146
+ }
147
+ function firstNameOf(user) {
148
+ const fromName = user.name?.trim().split(/\s+/)[0];
149
+ if (fromName) return fromName;
150
+ const local = user.email.split("@")[0] ?? "";
151
+ return local ? local.charAt(0).toUpperCase() + local.slice(1) : "there";
152
+ }
153
+ function initialsOf(user) {
154
+ const parts = user.name?.trim().split(/\s+/).filter(Boolean) ?? [];
155
+ if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
156
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
157
+ return (user.email[0] ?? "?").toUpperCase();
158
+ }
159
+ function AccountMenu({ user, role, theme, onTheme, signOutPath }) {
160
+ const [open, setOpen] = react.useState(false);
161
+ const wrap = react.useRef(null);
162
+ react.useEffect(() => {
163
+ if (!open) return;
164
+ const onClick = (e) => {
165
+ if (!wrap.current?.contains(e.target)) setOpen(false);
166
+ };
167
+ const onKey = (e) => {
168
+ if (e.key === "Escape") setOpen(false);
169
+ };
170
+ document.addEventListener("mousedown", onClick);
171
+ document.addEventListener("keydown", onKey);
172
+ return () => {
173
+ document.removeEventListener("mousedown", onClick);
174
+ document.removeEventListener("keydown", onKey);
175
+ };
176
+ }, [open]);
177
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: wrap, style: { position: "relative" }, children: [
178
+ /* @__PURE__ */ jsxRuntime.jsx(
179
+ "button",
180
+ {
181
+ type: "button",
182
+ className: "rz-avatar",
183
+ "aria-haspopup": "menu",
184
+ "aria-expanded": open,
185
+ "aria-label": "Account menu",
186
+ onClick: () => setOpen((v) => !v),
187
+ children: initialsOf(user)
188
+ }
189
+ ),
190
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-menu", role: "menu", children: [
191
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-menu__who", children: [
192
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-menu__name", children: user.name?.trim() || user.email }),
193
+ user.name?.trim() && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-menu__email", children: user.email })
194
+ ] }),
195
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-menu__row", children: [
196
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Role" }),
197
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-pill rz-pill--plain", children: role })
198
+ ] }),
199
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-menu__sep" }),
200
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-menu__row", children: [
201
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Theme" }),
202
+ /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, { theme, onChange: onTheme })
203
+ ] }),
204
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-menu__sep" }),
205
+ /* @__PURE__ */ jsxRuntime.jsx("form", { method: "post", action: signOutPath, className: "rz-menu__signout", children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: buttonClass("ghost", "sm"), children: "Sign out" }) })
206
+ ] })
207
+ ] });
208
+ }
209
+ function TopBar({ siteName, logoUrl, homeHref, user, role, theme, onTheme, signOutPath }) {
210
+ return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "rz-topbar", children: [
211
+ /* @__PURE__ */ jsxRuntime.jsxs("a", { href: homeHref, className: "rz-topbar__brand", children: [
212
+ logoUrl && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
213
+ /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoUrl, alt: "", className: "rz-topbar__logo" }),
214
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-topbar__divider", "aria-hidden": "true" })
215
+ ] }),
216
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: siteName }),
217
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-muted", style: { fontWeight: 400 }, children: "\xB7 Admin" })
218
+ ] }),
219
+ user && role && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-topbar__right", children: [
220
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rz-topbar__greeting", "data-testid": "rz-greeting", children: [
221
+ "Welcome back, ",
222
+ firstNameOf(user)
223
+ ] }),
224
+ /* @__PURE__ */ jsxRuntime.jsx(AccountMenu, { user, role, theme, onTheme, signOutPath })
225
+ ] })
226
+ ] });
227
+ }
228
+
229
+ // src/shell/tokens.ts
230
+ var SHELL_CSS = `
231
+ .rz-admin, .rz-admin[data-theme="light"] {
232
+ --ink: #1a1916; --bone: #f5f0e8; --signal: #166534;
233
+ --surface-0: #faf7f2; --surface-1: #f5f0e8; --surface-2: #ede8de; --surface-3: #e2dbd0;
234
+ --text-primary: #1a1916; --text-secondary: #4a4640; --text-muted: #8a8480;
235
+ --signal-dim: #15803d; --signal-glow: #14532d; --signal-warn: #d97706; --series-blue: #2563eb;
236
+ --border: #e2dbd0; --separator: #ede8de;
237
+ --status-up: var(--signal); --status-down: #dc2626; --status-neutral: var(--text-muted); --status-blocked: var(--border);
238
+ --signal-fg: #ffffff;
239
+ }
240
+ .rz-admin[data-theme="dark"] {
241
+ --ink: #e8e4d9; --bone: #1a1a18; --signal: #4ade80;
242
+ --surface-0: #111110; --surface-1: #1a1a18; --surface-2: #252522; --surface-3: #2e2e2a;
243
+ --text-primary: #e8e4d9; --text-secondary: #a8a49a; --text-muted: #6b6762;
244
+ --signal-dim: #22c55e; --signal-glow: #86efac; --signal-warn: #f59e0b; --series-blue: #3b82f6;
245
+ --border: #2e2e2a; --separator: #252522;
246
+ --status-up: var(--signal); --status-down: #f87171; --status-neutral: var(--text-muted); --status-blocked: var(--border);
247
+ --signal-fg: #111110;
248
+ }
249
+ .rz-admin {
250
+ --rz-font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
251
+ --rz-font-display: var(--rz-font);
252
+ --rz-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
253
+ --rz-radius: 6px;
254
+ --rz-sidebar: 220px; --rz-sidebar-collapsed: 58px; --rz-topbar: 52px;
255
+
256
+ /* forms-ui speaks these; map them once so both packages agree */
257
+ --realiiz-form-fg: var(--text-primary); --realiiz-form-muted: var(--text-muted);
258
+ --realiiz-form-border: var(--border); --realiiz-form-accent: var(--signal);
259
+ --realiiz-form-accent-fg: var(--signal-fg); --realiiz-form-danger: var(--status-down);
260
+ --realiiz-form-radius: var(--rz-radius); --realiiz-form-font: var(--rz-font);
261
+ --realiiz-form-focus-ring: 0 0 0 3px color-mix(in srgb, var(--signal) 25%, transparent);
262
+
263
+ box-sizing: border-box; min-height: 100vh; display: flex; flex-direction: column;
264
+ background: var(--surface-0); color: var(--text-primary);
265
+ font-family: var(--rz-font); font-size: 14px; line-height: 1.45;
266
+ }
267
+ .rz-admin *, .rz-admin *::before, .rz-admin *::after { box-sizing: inherit; }
268
+ .rz-admin a { color: inherit; }
269
+ .rz-admin h1, .rz-admin h2, .rz-admin h3 { font-family: var(--rz-font-display); color: var(--text-primary); margin: 0; }
270
+ .rz-admin .realiiz-form__control { background: var(--surface-0); }
271
+
272
+ /* ---- frame ---------------------------------------------------------- */
273
+ .rz-topbar { height: var(--rz-topbar); display: flex; align-items: center; gap: 1rem; padding: 0 1.25rem; border-bottom: 1px solid var(--border); background: var(--surface-1); flex-shrink: 0; }
274
+ .rz-topbar__brand { display: flex; align-items: center; gap: .6rem; font-weight: 600; font-size: .9375rem; text-decoration: none; }
275
+ .rz-topbar__logo { height: 22px; width: auto; display: block; }
276
+ .rz-topbar__divider { width: 1px; height: 18px; background: var(--border); }
277
+ .rz-topbar__right { margin-left: auto; display: flex; align-items: center; gap: 1rem; }
278
+ .rz-topbar__greeting { color: var(--text-secondary); font-size: .8rem; }
279
+ .rz-body { display: flex; flex: 1; min-height: 0; }
280
+ .rz-sidebar { width: var(--rz-sidebar); flex-shrink: 0; border-right: 1px solid var(--border); background: var(--surface-1); display: flex; flex-direction: column; transition: width .15s ease; }
281
+ .rz-sidebar--collapsed { width: var(--rz-sidebar-collapsed); }
282
+ .rz-sidebar__nav { padding: .75rem 0; flex: 1; overflow-y: auto; }
283
+ .rz-sidebar__item { display: flex; align-items: center; gap: .65rem; padding: .5rem .9rem; margin: 1px .5rem; border-radius: var(--rz-radius); color: var(--text-secondary); text-decoration: none; font-size: .8125rem; border-left: 2px solid transparent; white-space: nowrap; overflow: hidden; }
284
+ .rz-sidebar__item:hover { background: var(--surface-2); color: var(--text-primary); }
285
+ .rz-sidebar__item--active { background: var(--surface-2); color: var(--text-primary); border-left-color: var(--signal); }
286
+ .rz-sidebar--collapsed .rz-sidebar__label { display: none; }
287
+ .rz-sidebar__collapse { border-top: 1px solid var(--separator); padding: .3rem .5rem; }
288
+ .rz-sidebar__collapse button { width: 100%; }
289
+ .rz-main { flex: 1; min-width: 0; overflow-x: hidden; }
290
+ .rz-content { max-width: 960px; margin: 0 auto; padding: 28px 28px 80px; }
291
+
292
+ /* ---- primitives ----------------------------------------------------- */
293
+ .rz-btn { font-family: inherit; font-weight: 500; border-radius: var(--rz-radius); cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: .4rem; white-space: nowrap; line-height: 1.2; }
294
+ .rz-btn--md { padding: .45rem 1rem; font-size: .8125rem; }
295
+ .rz-btn--sm { padding: .3rem .7rem; font-size: .75rem; }
296
+ .rz-btn--primary { background: var(--signal); color: var(--signal-fg); border: 1px solid transparent; }
297
+ .rz-btn--secondary { background: var(--surface-3); color: var(--text-secondary); border: 1px solid var(--border); }
298
+ .rz-btn--danger { background: transparent; color: var(--status-down); border: 1px solid var(--status-down); }
299
+ .rz-btn--ghost { background: transparent; color: var(--text-secondary); border: 1px solid var(--border); }
300
+ .rz-btn:disabled { opacity: .55; cursor: default; }
301
+ .rz-card { background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; padding: 18px 20px; }
302
+ .rz-card__title { font-size: .9375rem; font-weight: 600; margin-bottom: .35rem; }
303
+ .rz-card__meta { color: var(--text-muted); font-size: .8rem; }
304
+ .rz-card__actions { display: flex; gap: .5rem; margin-top: .9rem; flex-wrap: wrap; }
305
+ .rz-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
306
+ .rz-pagehead { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
307
+ .rz-pagehead__eyebrow { font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); margin-bottom: .3rem; }
308
+ .rz-pagehead h1 { font-size: 1.375rem; font-weight: 600; }
309
+ .rz-pagehead__sub { color: var(--text-secondary); font-size: .85rem; margin-top: .25rem; }
310
+ .rz-pill { display: inline-flex; align-items: center; gap: .4rem; font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; padding: .15rem .5rem; border: 1px solid var(--border); border-radius: 999px; color: var(--text-secondary); }
311
+ .rz-pill::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--status-neutral); }
312
+ .rz-pill--up::before { background: var(--status-up); } .rz-pill--warn::before { background: var(--signal-warn); } .rz-pill--down::before { background: var(--status-down); }
313
+ .rz-pill--plain::before { display: none; }
314
+ .rz-empty { border: 1px dashed var(--border); border-radius: 8px; padding: 32px; text-align: center; color: var(--text-secondary); }
315
+ .rz-empty h3 { font-size: 1rem; margin-bottom: .4rem; }
316
+ .rz-table { width: 100%; border-collapse: collapse; background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
317
+ .rz-table th, .rz-table td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--separator); font-size: .875rem; vertical-align: top; }
318
+ .rz-table th { font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); background: var(--surface-2); }
319
+ .rz-table tr:last-child td { border-bottom: 0; }
320
+ .rz-muted { color: var(--text-muted); }
321
+ .rz-alert { border: 1px solid var(--status-down); color: var(--status-down); border-radius: var(--rz-radius); padding: 12px 14px; margin: 0 0 16px; }
322
+ .rz-ok { border: 1px solid var(--status-up); color: var(--signal-dim); border-radius: var(--rz-radius); padding: 12px 14px; margin: 0 0 16px; }
323
+
324
+ /* ---- account menu --------------------------------------------------- */
325
+ .rz-avatar { width: 2rem; height: 2rem; border-radius: 50%; background: var(--surface-3); border: 1px solid var(--border); color: var(--text-primary); font-size: .6875rem; font-weight: 600; letter-spacing: .04em; display: flex; align-items: center; justify-content: center; cursor: pointer; font-family: inherit; padding: 0; }
326
+ .rz-menu { position: absolute; right: 0; top: calc(100% + 8px); min-width: 240px; background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 10px 30px rgba(0,0,0,.12); padding: 6px; z-index: 50; }
327
+ .rz-menu__who { padding: 10px 10px 8px; }
328
+ .rz-menu__name { font-weight: 600; font-size: .875rem; }
329
+ .rz-menu__email { color: var(--text-muted); font-size: .75rem; word-break: break-all; }
330
+ .rz-menu__sep { height: 1px; background: var(--separator); margin: 4px 0; }
331
+ .rz-menu__row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; font-size: .8125rem; color: var(--text-secondary); }
332
+ .rz-menu__signout { padding: 6px; }
333
+ .rz-menu__signout .rz-btn { width: 100%; }
334
+ .rz-seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--rz-radius); overflow: hidden; }
335
+ .rz-seg button { font: inherit; font-size: .75rem; padding: .25rem .6rem; background: transparent; color: var(--text-secondary); border: 0; cursor: pointer; }
336
+ .rz-seg button[aria-pressed="true"] { background: var(--surface-3); color: var(--text-primary); }
337
+
338
+ /* ---- mobile: sidebar becomes a strip under the top bar --------------- */
339
+ @media (max-width: 720px) {
340
+ .rz-body { flex-direction: column; }
341
+ .rz-sidebar, .rz-sidebar--collapsed { width: 100%; border-right: 0; border-bottom: 1px solid var(--border); flex-direction: row; }
342
+ .rz-sidebar__nav { display: flex; padding: .35rem .25rem; overflow-x: auto; }
343
+ .rz-sidebar__item { flex-direction: column; gap: .2rem; font-size: .6875rem; padding: .4rem .6rem; border-left: 0; border-bottom: 2px solid transparent; }
344
+ .rz-sidebar__item--active { border-bottom-color: var(--signal); }
345
+ .rz-sidebar--collapsed .rz-sidebar__label { display: block; }
346
+ .rz-sidebar__collapse { display: none; }
347
+ .rz-topbar__greeting { display: none; }
348
+ .rz-content { padding: 18px 14px 60px; }
349
+ }
350
+ `;
351
+ function AdminShell({
352
+ siteName,
353
+ logoUrl,
354
+ user,
355
+ role,
356
+ nav,
357
+ activeHref,
358
+ basePath = "/admin",
359
+ signOutPath,
360
+ defaultTheme = "light",
361
+ children
362
+ }) {
363
+ const [theme, setTheme] = useTheme(defaultTheme);
364
+ const showNav = Boolean(user && role);
365
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-admin", "data-theme": theme, children: [
366
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: SHELL_CSS }),
367
+ /* @__PURE__ */ jsxRuntime.jsx(
368
+ TopBar,
369
+ {
370
+ siteName,
371
+ logoUrl,
372
+ homeHref: basePath,
373
+ user,
374
+ role,
375
+ theme,
376
+ onTheme: setTheme,
377
+ signOutPath: signOutPath ?? `${basePath}/auth/signout`
378
+ }
379
+ ),
380
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-body", children: [
381
+ showNav && /* @__PURE__ */ jsxRuntime.jsx(Sidebar, { items: nav, activeHref, basePath }),
382
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: "rz-main", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-content", children }) })
383
+ ] })
384
+ ] });
385
+ }
386
+ function Card({ title, meta, children, actions, className }) {
387
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["rz-card", className].filter(Boolean).join(" "), children: [
388
+ title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-card__title", children: title }),
389
+ meta && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-card__meta", children: meta }),
390
+ children,
391
+ actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-card__actions", children: actions })
392
+ ] });
393
+ }
394
+ function PageHeader({ eyebrow, title, subtitle, actions }) {
395
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-pagehead", children: [
396
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
397
+ eyebrow && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-pagehead__eyebrow", children: eyebrow }),
398
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { children: title }),
399
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-pagehead__sub", children: subtitle })
400
+ ] }),
401
+ actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-card__actions", style: { marginTop: 0 }, children: actions })
402
+ ] });
403
+ }
404
+ function StatusPill({ tone = "neutral", children, title }) {
405
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `rz-pill rz-pill--${tone}`, title, children });
406
+ }
407
+ function EmptyState({ title, children, action }) {
408
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-empty", children: [
409
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: title }),
410
+ children && /* @__PURE__ */ jsxRuntime.jsx("div", { children }),
411
+ action && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 14 }, children: action })
412
+ ] });
413
+ }
414
+
415
+ exports.AccountMenu = AccountMenu;
416
+ exports.AdminShell = AdminShell;
417
+ exports.Button = Button;
418
+ exports.Card = Card;
419
+ exports.EmptyState = EmptyState;
420
+ exports.Icon = Icon;
421
+ exports.LinkButton = LinkButton;
422
+ exports.PageHeader = PageHeader;
423
+ exports.SHELL_CSS = SHELL_CSS;
424
+ exports.Sidebar = Sidebar;
425
+ exports.StatusPill = StatusPill;
426
+ exports.ThemeToggle = ThemeToggle;
427
+ exports.TopBar = TopBar;
428
+ exports.buttonClass = buttonClass;
429
+ exports.buttonStyle = buttonStyle;
430
+ exports.firstNameOf = firstNameOf;
431
+ exports.iconFor = iconFor;
432
+ exports.initialsOf = initialsOf;
433
+ exports.isActive = isActive;
434
+ exports.useTheme = useTheme;
435
+ //# sourceMappingURL=index.cjs.map
436
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/shell/icons.tsx","../../src/shell/Sidebar.tsx","../../src/shell/Button.tsx","../../src/shell/ThemeToggle.tsx","../../src/shell/AccountMenu.tsx","../../src/shell/TopBar.tsx","../../src/shell/tokens.ts","../../src/shell/AdminShell.tsx","../../src/shell/primitives.tsx"],"names":["jsxs","jsx","useState","useEffect","useRef","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["/** Seven stroke icons, 18px, currentColor. Registry ids pick one; unknown ids get `doc`. */\n\nimport type { SVGProps } from \"react\";\n\nexport type IconName = \"home\" | \"posts\" | \"events\" | \"venues\" | \"users\" | \"settings\" | \"doc\";\n\nconst base: SVGProps<SVGSVGElement> = {\n width: 18, height: 18, viewBox: \"0 0 24 24\", fill: \"none\", stroke: \"currentColor\",\n strokeWidth: 1.7, strokeLinecap: \"round\", strokeLinejoin: \"round\", \"aria-hidden\": true,\n style: { flexShrink: 0 },\n};\n\nexport function Icon({ name }: { name: IconName }) {\n switch (name) {\n case \"home\":\n return <svg {...base}><rect x=\"3\" y=\"3\" width=\"7\" height=\"9\" rx=\"1\" /><rect x=\"14\" y=\"3\" width=\"7\" height=\"5\" rx=\"1\" /><rect x=\"14\" y=\"12\" width=\"7\" height=\"9\" rx=\"1\" /><rect x=\"3\" y=\"16\" width=\"7\" height=\"5\" rx=\"1\" /></svg>;\n case \"posts\":\n return <svg {...base}><path d=\"M6 3h9l4 4v14H6z\" /><path d=\"M15 3v4h4\" /><path d=\"M9 12h7M9 16h7M9 8h3\" /></svg>;\n case \"events\":\n return <svg {...base}><rect x=\"3\" y=\"5\" width=\"18\" height=\"16\" rx=\"2\" /><path d=\"M8 3v4M16 3v4M3 10h18\" /></svg>;\n case \"venues\":\n return <svg {...base}><path d=\"M12 21s7-6.2 7-11.5A7 7 0 0 0 5 9.5C5 14.8 12 21 12 21z\" /><circle cx=\"12\" cy=\"9.5\" r=\"2.5\" /></svg>;\n case \"users\":\n return <svg {...base}><circle cx=\"9\" cy=\"8\" r=\"3.5\" /><path d=\"M2.5 20a6.5 6.5 0 0 1 13 0\" /><circle cx=\"17\" cy=\"9\" r=\"2.5\" /><path d=\"M15.5 14.5a5 5 0 0 1 6 5\" /></svg>;\n case \"settings\":\n return <svg {...base}><circle cx=\"12\" cy=\"12\" r=\"3\" /><path d=\"M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z\" /></svg>;\n default:\n return <svg {...base}><path d=\"M6 3h9l4 4v14H6z\" /><path d=\"M15 3v4h4\" /></svg>;\n }\n}\n\nexport function iconFor(id: string): IconName {\n const known: IconName[] = [\"home\", \"posts\", \"events\", \"venues\", \"users\", \"settings\"];\n return (known as string[]).includes(id) ? (id as IconName) : \"doc\";\n}\n","/**\n * Sidebar — items from the content-type registry (via navFromContentTypes),\n * active by href prefix, collapsible with the state remembered per browser.\n * Plain anchors: the package does not know which router the host uses.\n */\n\nimport { useEffect, useState } from \"react\";\nimport { Icon, iconFor } from \"./icons\";\n\nexport interface NavItem {\n id: string;\n label: string;\n href: string;\n /** Icon id; unknown ids fall back to a document icon. Defaults to `id`. */\n icon?: string;\n}\n\nexport const SIDEBAR_KEY = \"rz-admin-sidebar\";\n\nexport function isActive(item: NavItem, activeHref: string, basePath: string): boolean {\n if (item.href === basePath) return activeHref === basePath || activeHref === `${basePath}/`;\n return activeHref === item.href || activeHref.startsWith(`${item.href}/`);\n}\n\nexport function Sidebar({ items, activeHref, basePath }: { items: NavItem[]; activeHref: string; basePath: string }) {\n const [collapsed, setCollapsed] = useState(false);\n\n useEffect(() => {\n try {\n if (window.localStorage.getItem(SIDEBAR_KEY) === \"collapsed\") setCollapsed(true);\n } catch {\n /* storage unavailable */\n }\n }, []);\n\n const toggle = () => {\n const next = !collapsed;\n setCollapsed(next);\n try {\n window.localStorage.setItem(SIDEBAR_KEY, next ? \"collapsed\" : \"open\");\n } catch {\n /* ignore */\n }\n };\n\n return (\n <aside className={[\"rz-sidebar\", collapsed && \"rz-sidebar--collapsed\"].filter(Boolean).join(\" \")} aria-label=\"Admin navigation\">\n <nav className=\"rz-sidebar__nav\">\n {items.map((item) => {\n const active = isActive(item, activeHref, basePath);\n return (\n <a\n key={item.id}\n href={item.href}\n className={[\"rz-sidebar__item\", active && \"rz-sidebar__item--active\"].filter(Boolean).join(\" \")}\n aria-current={active ? \"page\" : undefined}\n title={collapsed ? item.label : undefined}\n >\n <Icon name={iconFor(item.icon ?? item.id)} />\n <span className=\"rz-sidebar__label\">{item.label}</span>\n </a>\n );\n })}\n </nav>\n <div className=\"rz-sidebar__collapse\">\n <button type=\"button\" className=\"rz-btn rz-btn--ghost rz-btn--sm\" onClick={toggle} aria-label={collapsed ? \"Expand navigation\" : \"Collapse navigation\"}>\n {collapsed ? \"»\" : \"« Collapse\"}\n </button>\n </div>\n </aside>\n );\n}\n","/**\n * The one source of button styling (the tracker's rule: hand-rolled buttons\n * drift). Classes only — everything resolves to tokens in SHELL_CSS.\n */\n\nimport type { AnchorHTMLAttributes, ButtonHTMLAttributes } from \"react\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"danger\" | \"ghost\";\nexport type ButtonSize = \"sm\" | \"md\";\n\nexport function buttonClass(variant: ButtonVariant = \"secondary\", size: ButtonSize = \"md\", extra?: string): string {\n return [\"rz-btn\", `rz-btn--${variant}`, `rz-btn--${size}`, extra].filter(Boolean).join(\" \");\n}\n\n/** Kept for parity with the tracker's API; returns the class string to spread onto an <a>. */\nexport function buttonStyle(variant: ButtonVariant = \"secondary\", size: ButtonSize = \"md\") {\n return { className: buttonClass(variant, size) };\n}\n\ninterface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\nexport function Button({ variant = \"secondary\", size = \"md\", className, type = \"button\", ...rest }: ButtonProps) {\n return <button type={type} className={buttonClass(variant, size, className)} {...rest} />;\n}\n\ninterface LinkButtonProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\nexport function LinkButton({ variant = \"secondary\", size = \"md\", className, ...rest }: LinkButtonProps) {\n return <a className={buttonClass(variant, size, className)} {...rest} />;\n}\n","/**\n * Light / dark, remembered per browser. The attribute lives on the .rz-admin\n * root (not <html>) so the host site's own theme is never touched.\n *\n * No flash of the wrong theme: the server renders defaultTheme, and the effect\n * only flips when a stored value differs — a single client-side change, never\n * a mismatch between server HTML and first client render.\n */\n\nimport { useEffect, useState } from \"react\";\n\nexport type Theme = \"light\" | \"dark\";\nexport const THEME_KEY = \"rz-admin-theme\";\n\nexport function useTheme(defaultTheme: Theme = \"light\"): [Theme, (t: Theme) => void] {\n const [theme, setTheme] = useState<Theme>(defaultTheme);\n\n useEffect(() => {\n try {\n const stored = window.localStorage.getItem(THEME_KEY);\n if (stored === \"light\" || stored === \"dark\") setTheme(stored);\n } catch {\n /* storage unavailable */\n }\n }, []);\n\n const set = (t: Theme) => {\n setTheme(t);\n try {\n window.localStorage.setItem(THEME_KEY, t);\n } catch {\n /* ignore */\n }\n };\n return [theme, set];\n}\n\nexport function ThemeToggle({ theme, onChange }: { theme: Theme; onChange: (t: Theme) => void }) {\n return (\n <span className=\"rz-seg\" role=\"group\" aria-label=\"Theme\">\n <button type=\"button\" aria-pressed={theme === \"light\"} onClick={() => onChange(\"light\")}>Light</button>\n <button type=\"button\" aria-pressed={theme === \"dark\"} onClick={() => onChange(\"dark\")}>Dark</button>\n </span>\n );\n}\n","/**\n * Avatar button → menu: who you are, your role, theme, sign out.\n * Sign out is a plain POST form so it works before hydration.\n */\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { buttonClass } from \"./Button\";\nimport { ThemeToggle, type Theme } from \"./ThemeToggle\";\n\nexport interface ShellUser {\n email: string;\n name?: string | null;\n}\n\nexport function firstNameOf(user: ShellUser): string {\n const fromName = user.name?.trim().split(/\\s+/)[0];\n if (fromName) return fromName;\n const local = user.email.split(\"@\")[0] ?? \"\";\n return local ? local.charAt(0).toUpperCase() + local.slice(1) : \"there\";\n}\n\nexport function initialsOf(user: ShellUser): string {\n const parts = user.name?.trim().split(/\\s+/).filter(Boolean) ?? [];\n if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n return (user.email[0] ?? \"?\").toUpperCase();\n}\n\nexport function AccountMenu({ user, role, theme, onTheme, signOutPath }: {\n user: ShellUser;\n role: string;\n theme: Theme;\n onTheme: (t: Theme) => void;\n signOutPath: string;\n}) {\n const [open, setOpen] = useState(false);\n const wrap = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onClick = (e: MouseEvent) => {\n if (!wrap.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"mousedown\", onClick);\n document.addEventListener(\"keydown\", onKey);\n return () => {\n document.removeEventListener(\"mousedown\", onClick);\n document.removeEventListener(\"keydown\", onKey);\n };\n }, [open]);\n\n return (\n <div ref={wrap} style={{ position: \"relative\" }}>\n <button\n type=\"button\"\n className=\"rz-avatar\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-label=\"Account menu\"\n onClick={() => setOpen((v) => !v)}\n >\n {initialsOf(user)}\n </button>\n\n {open && (\n <div className=\"rz-menu\" role=\"menu\">\n <div className=\"rz-menu__who\">\n <div className=\"rz-menu__name\">{user.name?.trim() || user.email}</div>\n {user.name?.trim() && <div className=\"rz-menu__email\">{user.email}</div>}\n </div>\n <div className=\"rz-menu__row\">\n <span>Role</span>\n <span className=\"rz-pill rz-pill--plain\">{role}</span>\n </div>\n <div className=\"rz-menu__sep\" />\n <div className=\"rz-menu__row\">\n <span>Theme</span>\n <ThemeToggle theme={theme} onChange={onTheme} />\n </div>\n <div className=\"rz-menu__sep\" />\n <form method=\"post\" action={signOutPath} className=\"rz-menu__signout\">\n <button type=\"submit\" className={buttonClass(\"ghost\", \"sm\")}>Sign out</button>\n </form>\n </div>\n )}\n </div>\n );\n}\n","/** Top bar: brand on the left, greeting + account menu on the right. */\n\nimport { AccountMenu, firstNameOf, type ShellUser } from \"./AccountMenu\";\nimport type { Theme } from \"./ThemeToggle\";\n\nexport function TopBar({ siteName, logoUrl, homeHref, user, role, theme, onTheme, signOutPath }: {\n siteName: string;\n logoUrl?: string | null;\n homeHref: string;\n user: ShellUser | null;\n role: string | null;\n theme: Theme;\n onTheme: (t: Theme) => void;\n signOutPath: string;\n}) {\n return (\n <header className=\"rz-topbar\">\n <a href={homeHref} className=\"rz-topbar__brand\">\n {logoUrl && (\n <>\n {/* a site-supplied logo URL; next/image is deliberately not used in the package */}\n <img src={logoUrl} alt=\"\" className=\"rz-topbar__logo\" />\n <span className=\"rz-topbar__divider\" aria-hidden=\"true\" />\n </>\n )}\n <span>{siteName}</span>\n <span className=\"rz-muted\" style={{ fontWeight: 400 }}>· Admin</span>\n </a>\n {user && role && (\n <div className=\"rz-topbar__right\">\n <span className=\"rz-topbar__greeting\" data-testid=\"rz-greeting\">Welcome back, {firstNameOf(user)}</span>\n <AccountMenu user={user} role={role} theme={theme} onTheme={onTheme} signOutPath={signOutPath} />\n </div>\n )}\n </header>\n );\n}\n","/**\n * SHELL_CSS — the design system, as one string the shell injects.\n *\n * Palette borrowed from the visibility tracker (Realiiz's back-office look):\n * warm bone/ink with an operative-green signal. Light is the default so every\n * install looks finished; dark is one attribute away. Everything is scoped to\n * `.rz-admin` so a host site's variables never leak in and ours never leak out.\n *\n * A site overrides by redeclaring any variable on `.rz-admin` — e.g.\n * .rz-admin { --signal: #0a58ca; --rz-font: var(--font-inter), sans-serif; }\n *\n * The block at the end maps the forms-ui variables onto these tokens, so\n * ContentForm and MagicLinkForm re-skin without touching their own code.\n */\n\nexport const SHELL_CSS = `\n.rz-admin, .rz-admin[data-theme=\"light\"] {\n --ink: #1a1916; --bone: #f5f0e8; --signal: #166534;\n --surface-0: #faf7f2; --surface-1: #f5f0e8; --surface-2: #ede8de; --surface-3: #e2dbd0;\n --text-primary: #1a1916; --text-secondary: #4a4640; --text-muted: #8a8480;\n --signal-dim: #15803d; --signal-glow: #14532d; --signal-warn: #d97706; --series-blue: #2563eb;\n --border: #e2dbd0; --separator: #ede8de;\n --status-up: var(--signal); --status-down: #dc2626; --status-neutral: var(--text-muted); --status-blocked: var(--border);\n --signal-fg: #ffffff;\n}\n.rz-admin[data-theme=\"dark\"] {\n --ink: #e8e4d9; --bone: #1a1a18; --signal: #4ade80;\n --surface-0: #111110; --surface-1: #1a1a18; --surface-2: #252522; --surface-3: #2e2e2a;\n --text-primary: #e8e4d9; --text-secondary: #a8a49a; --text-muted: #6b6762;\n --signal-dim: #22c55e; --signal-glow: #86efac; --signal-warn: #f59e0b; --series-blue: #3b82f6;\n --border: #2e2e2a; --separator: #252522;\n --status-up: var(--signal); --status-down: #f87171; --status-neutral: var(--text-muted); --status-blocked: var(--border);\n --signal-fg: #111110;\n}\n.rz-admin {\n --rz-font: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n --rz-font-display: var(--rz-font);\n --rz-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n --rz-radius: 6px;\n --rz-sidebar: 220px; --rz-sidebar-collapsed: 58px; --rz-topbar: 52px;\n\n /* forms-ui speaks these; map them once so both packages agree */\n --realiiz-form-fg: var(--text-primary); --realiiz-form-muted: var(--text-muted);\n --realiiz-form-border: var(--border); --realiiz-form-accent: var(--signal);\n --realiiz-form-accent-fg: var(--signal-fg); --realiiz-form-danger: var(--status-down);\n --realiiz-form-radius: var(--rz-radius); --realiiz-form-font: var(--rz-font);\n --realiiz-form-focus-ring: 0 0 0 3px color-mix(in srgb, var(--signal) 25%, transparent);\n\n box-sizing: border-box; min-height: 100vh; display: flex; flex-direction: column;\n background: var(--surface-0); color: var(--text-primary);\n font-family: var(--rz-font); font-size: 14px; line-height: 1.45;\n}\n.rz-admin *, .rz-admin *::before, .rz-admin *::after { box-sizing: inherit; }\n.rz-admin a { color: inherit; }\n.rz-admin h1, .rz-admin h2, .rz-admin h3 { font-family: var(--rz-font-display); color: var(--text-primary); margin: 0; }\n.rz-admin .realiiz-form__control { background: var(--surface-0); }\n\n/* ---- frame ---------------------------------------------------------- */\n.rz-topbar { height: var(--rz-topbar); display: flex; align-items: center; gap: 1rem; padding: 0 1.25rem; border-bottom: 1px solid var(--border); background: var(--surface-1); flex-shrink: 0; }\n.rz-topbar__brand { display: flex; align-items: center; gap: .6rem; font-weight: 600; font-size: .9375rem; text-decoration: none; }\n.rz-topbar__logo { height: 22px; width: auto; display: block; }\n.rz-topbar__divider { width: 1px; height: 18px; background: var(--border); }\n.rz-topbar__right { margin-left: auto; display: flex; align-items: center; gap: 1rem; }\n.rz-topbar__greeting { color: var(--text-secondary); font-size: .8rem; }\n.rz-body { display: flex; flex: 1; min-height: 0; }\n.rz-sidebar { width: var(--rz-sidebar); flex-shrink: 0; border-right: 1px solid var(--border); background: var(--surface-1); display: flex; flex-direction: column; transition: width .15s ease; }\n.rz-sidebar--collapsed { width: var(--rz-sidebar-collapsed); }\n.rz-sidebar__nav { padding: .75rem 0; flex: 1; overflow-y: auto; }\n.rz-sidebar__item { display: flex; align-items: center; gap: .65rem; padding: .5rem .9rem; margin: 1px .5rem; border-radius: var(--rz-radius); color: var(--text-secondary); text-decoration: none; font-size: .8125rem; border-left: 2px solid transparent; white-space: nowrap; overflow: hidden; }\n.rz-sidebar__item:hover { background: var(--surface-2); color: var(--text-primary); }\n.rz-sidebar__item--active { background: var(--surface-2); color: var(--text-primary); border-left-color: var(--signal); }\n.rz-sidebar--collapsed .rz-sidebar__label { display: none; }\n.rz-sidebar__collapse { border-top: 1px solid var(--separator); padding: .3rem .5rem; }\n.rz-sidebar__collapse button { width: 100%; }\n.rz-main { flex: 1; min-width: 0; overflow-x: hidden; }\n.rz-content { max-width: 960px; margin: 0 auto; padding: 28px 28px 80px; }\n\n/* ---- primitives ----------------------------------------------------- */\n.rz-btn { font-family: inherit; font-weight: 500; border-radius: var(--rz-radius); cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: .4rem; white-space: nowrap; line-height: 1.2; }\n.rz-btn--md { padding: .45rem 1rem; font-size: .8125rem; }\n.rz-btn--sm { padding: .3rem .7rem; font-size: .75rem; }\n.rz-btn--primary { background: var(--signal); color: var(--signal-fg); border: 1px solid transparent; }\n.rz-btn--secondary { background: var(--surface-3); color: var(--text-secondary); border: 1px solid var(--border); }\n.rz-btn--danger { background: transparent; color: var(--status-down); border: 1px solid var(--status-down); }\n.rz-btn--ghost { background: transparent; color: var(--text-secondary); border: 1px solid var(--border); }\n.rz-btn:disabled { opacity: .55; cursor: default; }\n.rz-card { background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; padding: 18px 20px; }\n.rz-card__title { font-size: .9375rem; font-weight: 600; margin-bottom: .35rem; }\n.rz-card__meta { color: var(--text-muted); font-size: .8rem; }\n.rz-card__actions { display: flex; gap: .5rem; margin-top: .9rem; flex-wrap: wrap; }\n.rz-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }\n.rz-pagehead { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }\n.rz-pagehead__eyebrow { font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); margin-bottom: .3rem; }\n.rz-pagehead h1 { font-size: 1.375rem; font-weight: 600; }\n.rz-pagehead__sub { color: var(--text-secondary); font-size: .85rem; margin-top: .25rem; }\n.rz-pill { display: inline-flex; align-items: center; gap: .4rem; font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; padding: .15rem .5rem; border: 1px solid var(--border); border-radius: 999px; color: var(--text-secondary); }\n.rz-pill::before { content: \"\"; width: 7px; height: 7px; border-radius: 50%; background: var(--status-neutral); }\n.rz-pill--up::before { background: var(--status-up); } .rz-pill--warn::before { background: var(--signal-warn); } .rz-pill--down::before { background: var(--status-down); }\n.rz-pill--plain::before { display: none; }\n.rz-empty { border: 1px dashed var(--border); border-radius: 8px; padding: 32px; text-align: center; color: var(--text-secondary); }\n.rz-empty h3 { font-size: 1rem; margin-bottom: .4rem; }\n.rz-table { width: 100%; border-collapse: collapse; background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }\n.rz-table th, .rz-table td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--separator); font-size: .875rem; vertical-align: top; }\n.rz-table th { font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); background: var(--surface-2); }\n.rz-table tr:last-child td { border-bottom: 0; }\n.rz-muted { color: var(--text-muted); }\n.rz-alert { border: 1px solid var(--status-down); color: var(--status-down); border-radius: var(--rz-radius); padding: 12px 14px; margin: 0 0 16px; }\n.rz-ok { border: 1px solid var(--status-up); color: var(--signal-dim); border-radius: var(--rz-radius); padding: 12px 14px; margin: 0 0 16px; }\n\n/* ---- account menu --------------------------------------------------- */\n.rz-avatar { width: 2rem; height: 2rem; border-radius: 50%; background: var(--surface-3); border: 1px solid var(--border); color: var(--text-primary); font-size: .6875rem; font-weight: 600; letter-spacing: .04em; display: flex; align-items: center; justify-content: center; cursor: pointer; font-family: inherit; padding: 0; }\n.rz-menu { position: absolute; right: 0; top: calc(100% + 8px); min-width: 240px; background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 10px 30px rgba(0,0,0,.12); padding: 6px; z-index: 50; }\n.rz-menu__who { padding: 10px 10px 8px; }\n.rz-menu__name { font-weight: 600; font-size: .875rem; }\n.rz-menu__email { color: var(--text-muted); font-size: .75rem; word-break: break-all; }\n.rz-menu__sep { height: 1px; background: var(--separator); margin: 4px 0; }\n.rz-menu__row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; font-size: .8125rem; color: var(--text-secondary); }\n.rz-menu__signout { padding: 6px; }\n.rz-menu__signout .rz-btn { width: 100%; }\n.rz-seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--rz-radius); overflow: hidden; }\n.rz-seg button { font: inherit; font-size: .75rem; padding: .25rem .6rem; background: transparent; color: var(--text-secondary); border: 0; cursor: pointer; }\n.rz-seg button[aria-pressed=\"true\"] { background: var(--surface-3); color: var(--text-primary); }\n\n/* ---- mobile: sidebar becomes a strip under the top bar --------------- */\n@media (max-width: 720px) {\n .rz-body { flex-direction: column; }\n .rz-sidebar, .rz-sidebar--collapsed { width: 100%; border-right: 0; border-bottom: 1px solid var(--border); flex-direction: row; }\n .rz-sidebar__nav { display: flex; padding: .35rem .25rem; overflow-x: auto; }\n .rz-sidebar__item { flex-direction: column; gap: .2rem; font-size: .6875rem; padding: .4rem .6rem; border-left: 0; border-bottom: 2px solid transparent; }\n .rz-sidebar__item--active { border-bottom-color: var(--signal); }\n .rz-sidebar--collapsed .rz-sidebar__label { display: block; }\n .rz-sidebar__collapse { display: none; }\n .rz-topbar__greeting { display: none; }\n .rz-content { padding: 18px 14px 60px; }\n}\n`;\n","/**\n * AdminShell — the frame every /admin page renders inside.\n *\n * Pure presentation: the host passes the session, the nav (from the registry)\n * and the current path. No env, no fetch, no router import. Tokens are\n * injected once via <style>; a site overrides them on `.rz-admin`.\n */\n\nimport type { ReactNode } from \"react\";\nimport type { ShellUser } from \"./AccountMenu\";\nimport { Sidebar, type NavItem } from \"./Sidebar\";\nimport { TopBar } from \"./TopBar\";\nimport { useTheme, type Theme } from \"./ThemeToggle\";\nimport { SHELL_CSS } from \"./tokens\";\n\nexport interface AdminShellProps {\n siteName: string;\n logoUrl?: string | null;\n /** null when nobody is signed in (sign-in page, not-configured page). */\n user: ShellUser | null;\n role: string | null;\n nav: NavItem[];\n /** The current pathname, for the active nav item. */\n activeHref: string;\n /** Default \"/admin\". */\n basePath?: string;\n /** Default `${basePath}/auth/signout`. */\n signOutPath?: string;\n defaultTheme?: Theme;\n children: ReactNode;\n}\n\nexport function AdminShell({\n siteName, logoUrl, user, role, nav, activeHref,\n basePath = \"/admin\", signOutPath, defaultTheme = \"light\", children,\n}: AdminShellProps) {\n const [theme, setTheme] = useTheme(defaultTheme);\n const showNav = Boolean(user && role);\n\n return (\n <div className=\"rz-admin\" data-theme={theme}>\n <style>{SHELL_CSS}</style>\n <TopBar\n siteName={siteName}\n logoUrl={logoUrl}\n homeHref={basePath}\n user={user}\n role={role}\n theme={theme}\n onTheme={setTheme}\n signOutPath={signOutPath ?? `${basePath}/auth/signout`}\n />\n <div className=\"rz-body\">\n {showNav && <Sidebar items={nav} activeHref={activeHref} basePath={basePath} />}\n <main className=\"rz-main\">\n <div className=\"rz-content\">{children}</div>\n </main>\n </div>\n </div>\n );\n}\n","/** Card, PageHeader, StatusPill, EmptyState — the page-level vocabulary. */\n\nimport type { ReactNode } from \"react\";\n\nexport function Card({ title, meta, children, actions, className }: {\n title?: ReactNode; meta?: ReactNode; children?: ReactNode; actions?: ReactNode; className?: string;\n}) {\n return (\n <div className={[\"rz-card\", className].filter(Boolean).join(\" \")}>\n {title && <div className=\"rz-card__title\">{title}</div>}\n {meta && <div className=\"rz-card__meta\">{meta}</div>}\n {children}\n {actions && <div className=\"rz-card__actions\">{actions}</div>}\n </div>\n );\n}\n\nexport function PageHeader({ eyebrow, title, subtitle, actions }: {\n eyebrow?: ReactNode; title: ReactNode; subtitle?: ReactNode; actions?: ReactNode;\n}) {\n return (\n <div className=\"rz-pagehead\">\n <div>\n {eyebrow && <div className=\"rz-pagehead__eyebrow\">{eyebrow}</div>}\n <h1>{title}</h1>\n {subtitle && <div className=\"rz-pagehead__sub\">{subtitle}</div>}\n </div>\n {actions && <div className=\"rz-card__actions\" style={{ marginTop: 0 }}>{actions}</div>}\n </div>\n );\n}\n\nexport type PillTone = \"up\" | \"warn\" | \"down\" | \"neutral\" | \"plain\";\n\nexport function StatusPill({ tone = \"neutral\", children, title }: { tone?: PillTone; children: ReactNode; title?: string }) {\n return <span className={`rz-pill rz-pill--${tone}`} title={title}>{children}</span>;\n}\n\nexport function EmptyState({ title, children, action }: { title: ReactNode; children?: ReactNode; action?: ReactNode }) {\n return (\n <div className=\"rz-empty\">\n <h3>{title}</h3>\n {children && <div>{children}</div>}\n {action && <div style={{ marginTop: 14 }}>{action}</div>}\n </div>\n );\n}\n"]}