@orion-studios/payload-admin-components 0.1.0 → 0.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20,9 +20,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ BlockPicker: () => BlockPicker,
23
24
  Dashboard: () => Dashboard,
25
+ EmptyState: () => EmptyState,
26
+ HelpTooltip: () => HelpTooltip,
24
27
  Icon: () => Icon,
25
- Logo: () => Logo
28
+ Logo: () => Logo,
29
+ SectionTabs: () => SectionTabs,
30
+ StatusBadge: () => StatusBadge,
31
+ ThemeProvider: () => ThemeProvider,
32
+ ThemeSwitcher: () => ThemeSwitcher,
33
+ WelcomeHeader: () => WelcomeHeader,
34
+ configureAdmin: () => configureAdmin,
35
+ themePreferenceField: () => themePreferenceField,
36
+ useTheme: () => useTheme,
37
+ withTooltips: () => withTooltips
26
38
  });
27
39
  module.exports = __toCommonJS(index_exports);
28
40
 
@@ -33,37 +45,46 @@ function Logo() {
33
45
  "div",
34
46
  {
35
47
  style: {
36
- alignItems: "center",
37
48
  display: "flex",
38
- fontWeight: 600,
39
- gap: 8,
40
- padding: "12px 0"
49
+ alignItems: "center",
50
+ gap: 10,
51
+ padding: "4px 0",
52
+ textDecoration: "none",
53
+ transition: "opacity 0.2s ease"
41
54
  },
42
55
  children: [
43
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
44
- "svg",
56
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
57
+ "div",
45
58
  {
46
- width: "32",
47
- height: "32",
48
- viewBox: "0 0 32 32",
49
- fill: "none",
50
- xmlns: "http://www.w3.org/2000/svg",
51
- children: [
52
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "32", height: "32", rx: "8", fill: "currentColor" }),
53
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
54
- "path",
55
- {
56
- d: "M16 8L8 16L16 24L24 16L16 8Z",
57
- fill: "white",
58
- stroke: "white",
59
- strokeWidth: "2",
60
- strokeLinejoin: "round"
61
- }
62
- )
63
- ]
59
+ style: {
60
+ width: 32,
61
+ height: 32,
62
+ borderRadius: "var(--admin-radius-sm, 6px)",
63
+ background: "var(--admin-accent, #3b82f6)",
64
+ display: "flex",
65
+ alignItems: "center",
66
+ justifyContent: "center",
67
+ flexShrink: 0
68
+ },
69
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: "white", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polygon", { points: "12 2 22 8.5 22 15.5 12 22 2 15.5 2 8.5 12 2" }),
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "22", x2: "12", y2: "15.5" }),
72
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "22 8.5 12 15.5 2 8.5" })
73
+ ] })
64
74
  }
65
75
  ),
66
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 18 }, children: "Your Brand" })
76
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
77
+ "span",
78
+ {
79
+ style: {
80
+ fontSize: 15,
81
+ fontWeight: 700,
82
+ color: "var(--admin-text, #111827)",
83
+ letterSpacing: "-0.01em"
84
+ },
85
+ children: "Orion CMS"
86
+ }
87
+ )
67
88
  ]
68
89
  }
69
90
  );
@@ -72,103 +93,1239 @@ function Logo() {
72
93
  // src/components/Icon.tsx
73
94
  var import_jsx_runtime2 = require("react/jsx-runtime");
74
95
  function Icon() {
75
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
76
- "svg",
96
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "32", height: "32", viewBox: "0 0 32 32", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
97
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { width: "32", height: "32", rx: "8", fill: "var(--admin-accent, #3b82f6)" }),
98
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: "translate(4, 4)", children: [
99
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: "12 2 22 8.5 22 15.5 12 22 2 15.5 2 8.5 12 2", fill: "none", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
100
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "22", x2: "12", y2: "15.5", stroke: "white", strokeWidth: "1.5" }),
101
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "22 8.5 12 15.5 2 8.5", fill: "none", stroke: "white", strokeWidth: "1.5" })
102
+ ] })
103
+ ] });
104
+ }
105
+
106
+ // src/components/Dashboard.tsx
107
+ var import_react4 = require("react");
108
+
109
+ // src/components/ThemeSwitcher.tsx
110
+ var import_react2 = require("react");
111
+
112
+ // src/hooks/useTheme.ts
113
+ var import_react = require("react");
114
+ var STORAGE_KEY = "orion-admin-theme";
115
+ var DEFAULT_THEME = "light";
116
+ function applyTheme(theme) {
117
+ document.documentElement.setAttribute("data-theme", theme);
118
+ }
119
+ function getCachedTheme() {
120
+ try {
121
+ const stored = localStorage.getItem(STORAGE_KEY);
122
+ if (stored && ["light", "dark", "brand-light", "brand-dark"].includes(stored)) {
123
+ return stored;
124
+ }
125
+ } catch {
126
+ }
127
+ return null;
128
+ }
129
+ function cacheTheme(theme) {
130
+ try {
131
+ localStorage.setItem(STORAGE_KEY, theme);
132
+ } catch {
133
+ }
134
+ }
135
+ function useTheme() {
136
+ const [theme, setThemeState] = (0, import_react.useState)(() => {
137
+ if (typeof window === "undefined") return DEFAULT_THEME;
138
+ return getCachedTheme() || DEFAULT_THEME;
139
+ });
140
+ const [isLoading, setIsLoading] = (0, import_react.useState)(true);
141
+ const debounceRef = (0, import_react.useRef)(null);
142
+ const userIdRef = (0, import_react.useRef)(null);
143
+ (0, import_react.useEffect)(() => {
144
+ const cached = getCachedTheme();
145
+ if (cached) {
146
+ applyTheme(cached);
147
+ setThemeState(cached);
148
+ } else {
149
+ applyTheme(DEFAULT_THEME);
150
+ }
151
+ fetch("/api/users/me", { credentials: "include" }).then((res) => res.json()).then((data) => {
152
+ const user = data?.user || data;
153
+ if (user?.id) {
154
+ userIdRef.current = user.id;
155
+ }
156
+ if (user?.themePreference) {
157
+ const dbTheme = user.themePreference;
158
+ setThemeState(dbTheme);
159
+ applyTheme(dbTheme);
160
+ cacheTheme(dbTheme);
161
+ }
162
+ }).catch(() => {
163
+ }).finally(() => {
164
+ setIsLoading(false);
165
+ });
166
+ }, []);
167
+ const setTheme = (0, import_react.useCallback)((newTheme) => {
168
+ setThemeState(newTheme);
169
+ applyTheme(newTheme);
170
+ cacheTheme(newTheme);
171
+ if (debounceRef.current) {
172
+ clearTimeout(debounceRef.current);
173
+ }
174
+ debounceRef.current = setTimeout(() => {
175
+ const userId = userIdRef.current;
176
+ if (!userId) return;
177
+ fetch(`/api/users/${userId}`, {
178
+ method: "PATCH",
179
+ credentials: "include",
180
+ headers: { "Content-Type": "application/json" },
181
+ body: JSON.stringify({ themePreference: newTheme })
182
+ }).catch(() => {
183
+ });
184
+ }, 300);
185
+ }, []);
186
+ const isDark = theme === "dark" || theme === "brand-dark";
187
+ const isBrand = theme === "brand-light" || theme === "brand-dark";
188
+ const toggleDarkMode = (0, import_react.useCallback)(() => {
189
+ if (isBrand) {
190
+ setTheme(isDark ? "brand-light" : "brand-dark");
191
+ } else {
192
+ setTheme(isDark ? "light" : "dark");
193
+ }
194
+ }, [isDark, isBrand, setTheme]);
195
+ const toggleBrandMode = (0, import_react.useCallback)(() => {
196
+ if (isBrand) {
197
+ setTheme(isDark ? "dark" : "light");
198
+ } else {
199
+ setTheme(isDark ? "brand-dark" : "brand-light");
200
+ }
201
+ }, [isDark, isBrand, setTheme]);
202
+ return {
203
+ theme,
204
+ setTheme,
205
+ isDark,
206
+ isBrand,
207
+ isLoading,
208
+ toggleDarkMode,
209
+ toggleBrandMode
210
+ };
211
+ }
212
+
213
+ // src/components/ThemeSwitcher.tsx
214
+ var import_jsx_runtime3 = require("react/jsx-runtime");
215
+ var iconSize = 16;
216
+ function SunIcon() {
217
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: iconSize, height: iconSize, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
218
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "5" }),
219
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", y1: "1", x2: "12", y2: "3" }),
220
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", y1: "21", x2: "12", y2: "23" }),
221
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }),
222
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }),
223
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "1", y1: "12", x2: "3", y2: "12" }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "21", y1: "12", x2: "23", y2: "12" }),
225
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }),
226
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })
227
+ ] });
228
+ }
229
+ function MoonIcon() {
230
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: iconSize, height: iconSize, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) });
231
+ }
232
+ function PaletteIcon() {
233
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: iconSize, height: iconSize, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
234
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "13.5", cy: "6.5", r: "0.5", fill: "currentColor" }),
235
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "17.5", cy: "10.5", r: "0.5", fill: "currentColor" }),
236
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "8.5", cy: "7.5", r: "0.5", fill: "currentColor" }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "6.5", cy: "12", r: "0.5", fill: "currentColor" }),
238
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z" })
239
+ ] });
240
+ }
241
+ var buttonBase = {
242
+ display: "flex",
243
+ alignItems: "center",
244
+ justifyContent: "center",
245
+ width: 32,
246
+ height: 32,
247
+ borderRadius: "var(--admin-radius-sm)",
248
+ border: "1px solid var(--admin-border)",
249
+ background: "var(--admin-surface)",
250
+ color: "var(--admin-text-secondary)",
251
+ cursor: "pointer",
252
+ transition: "all 0.2s ease",
253
+ padding: 0
254
+ };
255
+ var buttonActive = {
256
+ ...buttonBase,
257
+ background: "var(--admin-accent-subtle)",
258
+ borderColor: "var(--admin-accent)",
259
+ color: "var(--admin-accent)"
260
+ };
261
+ function ThemeSwitcher() {
262
+ const { isDark, isBrand, toggleDarkMode, toggleBrandMode } = useTheme();
263
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
264
+ "div",
77
265
  {
78
- width: "32",
79
- height: "32",
80
- viewBox: "0 0 32 32",
81
- fill: "none",
82
- xmlns: "http://www.w3.org/2000/svg",
266
+ style: {
267
+ display: "flex",
268
+ alignItems: "center",
269
+ gap: 6,
270
+ padding: "8px 12px"
271
+ },
83
272
  children: [
84
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { width: "32", height: "32", rx: "8", fill: "currentColor" }),
85
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
86
- "path",
273
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
274
+ "button",
87
275
  {
88
- d: "M16 8L8 16L16 24L24 16L16 8Z",
89
- fill: "white",
90
- stroke: "white",
91
- strokeWidth: "2",
92
- strokeLinejoin: "round"
276
+ type: "button",
277
+ onClick: toggleDarkMode,
278
+ style: isDark ? buttonActive : buttonBase,
279
+ title: isDark ? "Switch to light mode" : "Switch to dark mode",
280
+ "aria-label": isDark ? "Switch to light mode" : "Switch to dark mode",
281
+ children: isDark ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MoonIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SunIcon, {})
282
+ }
283
+ ),
284
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
285
+ "button",
286
+ {
287
+ type: "button",
288
+ onClick: toggleBrandMode,
289
+ style: isBrand ? buttonActive : buttonBase,
290
+ title: isBrand ? "Switch to standard colors" : "Switch to brand colors",
291
+ "aria-label": isBrand ? "Switch to standard colors" : "Switch to brand colors",
292
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaletteIcon, {})
93
293
  }
94
294
  )
95
295
  ]
96
296
  }
97
297
  );
98
298
  }
299
+ function ThemeProvider({ children }) {
300
+ (0, import_react2.useEffect)(() => {
301
+ try {
302
+ const stored = localStorage.getItem("orion-admin-theme");
303
+ if (stored && ["light", "dark", "brand-light", "brand-dark"].includes(stored)) {
304
+ document.documentElement.setAttribute("data-theme", stored);
305
+ } else {
306
+ document.documentElement.setAttribute("data-theme", "light");
307
+ }
308
+ } catch {
309
+ document.documentElement.setAttribute("data-theme", "light");
310
+ }
311
+ }, []);
312
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children });
313
+ }
314
+
315
+ // src/components/HelpTooltip.tsx
316
+ var import_react3 = require("react");
317
+ var import_jsx_runtime4 = require("react/jsx-runtime");
318
+ function HelpTooltip({
319
+ content,
320
+ position = "top"
321
+ }) {
322
+ const [isVisible, setIsVisible] = (0, import_react3.useState)(false);
323
+ const triggerRef = (0, import_react3.useRef)(null);
324
+ const tooltipRef = (0, import_react3.useRef)(null);
325
+ const tooltipId = (0, import_react3.useRef)(`tooltip-${Math.random().toString(36).slice(2, 9)}`);
326
+ const show = (0, import_react3.useCallback)(() => setIsVisible(true), []);
327
+ const hide = (0, import_react3.useCallback)(() => setIsVisible(false), []);
328
+ const toggle = (0, import_react3.useCallback)(() => setIsVisible((v) => !v), []);
329
+ (0, import_react3.useEffect)(() => {
330
+ if (!isVisible) return;
331
+ const handleKeyDown = (e) => {
332
+ if (e.key === "Escape") setIsVisible(false);
333
+ };
334
+ document.addEventListener("keydown", handleKeyDown);
335
+ return () => document.removeEventListener("keydown", handleKeyDown);
336
+ }, [isVisible]);
337
+ (0, import_react3.useEffect)(() => {
338
+ if (!isVisible) return;
339
+ const handleClick = (e) => {
340
+ if (triggerRef.current && !triggerRef.current.contains(e.target) && tooltipRef.current && !tooltipRef.current.contains(e.target)) {
341
+ setIsVisible(false);
342
+ }
343
+ };
344
+ document.addEventListener("mousedown", handleClick);
345
+ return () => document.removeEventListener("mousedown", handleClick);
346
+ }, [isVisible]);
347
+ const positionStyles = {
348
+ top: { bottom: "100%", left: "50%", transform: "translateX(-50%)", marginBottom: 8 },
349
+ bottom: { top: "100%", left: "50%", transform: "translateX(-50%)", marginTop: 8 },
350
+ left: { right: "100%", top: "50%", transform: "translateY(-50%)", marginRight: 8 },
351
+ right: { left: "100%", top: "50%", transform: "translateY(-50%)", marginLeft: 8 }
352
+ };
353
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { position: "relative", display: "inline-flex", verticalAlign: "middle", marginLeft: 6 }, children: [
354
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
355
+ "button",
356
+ {
357
+ ref: triggerRef,
358
+ type: "button",
359
+ onClick: toggle,
360
+ onMouseEnter: show,
361
+ onMouseLeave: hide,
362
+ onFocus: show,
363
+ onBlur: hide,
364
+ "aria-describedby": isVisible ? tooltipId.current : void 0,
365
+ style: {
366
+ display: "inline-flex",
367
+ alignItems: "center",
368
+ justifyContent: "center",
369
+ width: 18,
370
+ height: 18,
371
+ borderRadius: "50%",
372
+ border: "1.5px solid var(--admin-text-muted)",
373
+ background: "transparent",
374
+ color: "var(--admin-text-muted)",
375
+ fontSize: 11,
376
+ fontWeight: 700,
377
+ cursor: "help",
378
+ padding: 0,
379
+ lineHeight: 1,
380
+ transition: "all 0.15s ease",
381
+ ...isVisible ? {
382
+ borderColor: "var(--admin-accent)",
383
+ color: "var(--admin-accent)",
384
+ background: "var(--admin-accent-subtle)"
385
+ } : {}
386
+ },
387
+ children: "?"
388
+ }
389
+ ),
390
+ isVisible && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
391
+ "div",
392
+ {
393
+ ref: tooltipRef,
394
+ id: tooltipId.current,
395
+ role: "tooltip",
396
+ style: {
397
+ position: "absolute",
398
+ ...positionStyles[position],
399
+ background: "var(--admin-tooltip-bg)",
400
+ color: "var(--admin-tooltip-text)",
401
+ padding: "8px 12px",
402
+ borderRadius: "var(--admin-radius-sm)",
403
+ fontSize: 12,
404
+ lineHeight: 1.5,
405
+ maxWidth: 260,
406
+ minWidth: 160,
407
+ whiteSpace: "normal",
408
+ zIndex: 9999,
409
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
410
+ pointerEvents: "auto",
411
+ animation: "tooltipFadeIn 0.15s ease"
412
+ },
413
+ children: content
414
+ }
415
+ )
416
+ ] });
417
+ }
418
+
419
+ // src/components/StatusBadge.tsx
420
+ var import_jsx_runtime5 = require("react/jsx-runtime");
421
+ var statusConfig = {
422
+ draft: { label: "Draft" },
423
+ published: { label: "Published" },
424
+ changed: { label: "Changed" }
425
+ };
426
+ function StatusBadge({
427
+ status,
428
+ size = "md"
429
+ }) {
430
+ const config = statusConfig[status];
431
+ const isSm = size === "sm";
432
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
433
+ "span",
434
+ {
435
+ style: {
436
+ display: "inline-flex",
437
+ alignItems: "center",
438
+ gap: 4,
439
+ padding: isSm ? "2px 8px" : "3px 10px",
440
+ borderRadius: 20,
441
+ fontSize: isSm ? 11 : 12,
442
+ fontWeight: 600,
443
+ lineHeight: 1.4,
444
+ background: `var(--admin-badge-${status}-bg)`,
445
+ color: `var(--admin-badge-${status}-text)`,
446
+ whiteSpace: "nowrap"
447
+ },
448
+ children: [
449
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
450
+ "span",
451
+ {
452
+ style: {
453
+ width: isSm ? 5 : 6,
454
+ height: isSm ? 5 : 6,
455
+ borderRadius: "50%",
456
+ background: "currentColor",
457
+ flexShrink: 0
458
+ }
459
+ }
460
+ ),
461
+ config.label
462
+ ]
463
+ }
464
+ );
465
+ }
99
466
 
100
467
  // src/components/Dashboard.tsx
101
- var import_jsx_runtime3 = require("react/jsx-runtime");
468
+ var import_jsx_runtime6 = require("react/jsx-runtime");
469
+ function PagesIcon({ size = 24 }) {
470
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
471
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
472
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "14 2 14 8 20 8" }),
473
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
474
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "16", y1: "17", x2: "8", y2: "17" }),
475
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "10 9 9 9 8 9" })
476
+ ] });
477
+ }
478
+ function MediaIcon({ size = 24 }) {
479
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
480
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
481
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
482
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "21 15 16 10 5 21" })
483
+ ] });
484
+ }
485
+ function SettingsIcon({ size = 24 }) {
486
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
487
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "3" }),
488
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" })
489
+ ] });
490
+ }
491
+ function LayoutIcon({ size = 24 }) {
492
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
493
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
494
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
495
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "3", y1: "15", x2: "21", y2: "15" })
496
+ ] });
497
+ }
498
+ function PlusIcon({ size = 16 }) {
499
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
500
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
501
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
502
+ ] });
503
+ }
504
+ function ClockIcon({ size = 14 }) {
505
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
506
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
507
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "12 6 12 12 16 14" })
508
+ ] });
509
+ }
510
+ function getGreeting() {
511
+ const hour = (/* @__PURE__ */ new Date()).getHours();
512
+ if (hour < 12) return "Good morning";
513
+ if (hour < 17) return "Good afternoon";
514
+ return "Good evening";
515
+ }
516
+ function formatRelativeTime(dateStr) {
517
+ const date = new Date(dateStr);
518
+ const now = /* @__PURE__ */ new Date();
519
+ const diffMs = now.getTime() - date.getTime();
520
+ const diffMins = Math.floor(diffMs / 6e4);
521
+ const diffHours = Math.floor(diffMs / 36e5);
522
+ const diffDays = Math.floor(diffMs / 864e5);
523
+ if (diffMins < 1) return "Just now";
524
+ if (diffMins < 60) return `${diffMins}m ago`;
525
+ if (diffHours < 24) return `${diffHours}h ago`;
526
+ if (diffDays < 7) return `${diffDays}d ago`;
527
+ return date.toLocaleDateString();
528
+ }
102
529
  function Dashboard() {
103
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { padding: "24px", maxWidth: 1200, margin: "0 auto" }, children: [
104
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: { fontSize: 32, fontWeight: 700, marginBottom: 8 }, children: "Welcome to Your CMS" }),
105
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { color: "var(--theme-text-muted)", marginBottom: 32 }, children: "Manage your website content from this admin panel." }),
106
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
530
+ const [userName, setUserName] = (0, import_react4.useState)("");
531
+ const [recentPages, setRecentPages] = (0, import_react4.useState)([]);
532
+ const [pageCount, setPageCount] = (0, import_react4.useState)(null);
533
+ const [mediaCount, setMediaCount] = (0, import_react4.useState)(null);
534
+ (0, import_react4.useEffect)(() => {
535
+ fetch("/api/users/me", { credentials: "include" }).then((res) => res.json()).then((data) => {
536
+ const user = data?.user || data;
537
+ setUserName(user?.fullName || user?.email?.split("@")[0] || "");
538
+ }).catch(() => {
539
+ });
540
+ fetch("/api/pages?limit=5&sort=-updatedAt", { credentials: "include" }).then((res) => res.json()).then((data) => {
541
+ setRecentPages(data?.docs || []);
542
+ setPageCount(data?.totalDocs ?? null);
543
+ }).catch(() => {
544
+ });
545
+ fetch("/api/media?limit=0", { credentials: "include" }).then((res) => res.json()).then((data) => {
546
+ setMediaCount(data?.totalDocs ?? null);
547
+ }).catch(() => {
548
+ });
549
+ }, []);
550
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { padding: "32px", maxWidth: 1200, margin: "0 auto" }, children: [
551
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 32 }, children: [
552
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
553
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("h1", { style: { fontSize: 28, fontWeight: 700, color: "var(--admin-text)", margin: "0 0 6px" }, children: [
554
+ getGreeting(),
555
+ userName ? `, ${userName}` : ""
556
+ ] }),
557
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { fontSize: 15, color: "var(--admin-text-muted)", margin: 0 }, children: "Manage your website content and settings from here." })
558
+ ] }),
559
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ThemeSwitcher, {})
560
+ ] }),
561
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 10, marginBottom: 32, flexWrap: "wrap" }, children: [
562
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QuickAction, { href: "/admin/collections/pages/create", icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PlusIcon, {}), label: "New Page" }),
563
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QuickAction, { href: "/admin/collections/media/create", icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PlusIcon, {}), label: "Upload Media" }),
564
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QuickAction, { href: "/admin/globals/header", icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LayoutIcon, { size: 16 }), label: "Edit Navigation" }),
565
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QuickAction, { href: "/admin/globals/site-settings", icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SettingsIcon, { size: 16 }), label: "Website Settings" })
566
+ ] }),
567
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
107
568
  "div",
108
569
  {
109
570
  style: {
110
571
  display: "grid",
111
- gap: 16,
112
- gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))"
572
+ gap: 20,
573
+ gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
574
+ marginBottom: 32
113
575
  },
114
576
  children: [
115
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
116
- DashboardCard,
577
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
578
+ ContentCard,
117
579
  {
580
+ icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PagesIcon, {}),
118
581
  title: "Pages",
119
- description: "Create and manage website pages",
120
- href: "/admin/collections/pages"
582
+ description: "Create and manage your website pages",
583
+ count: pageCount,
584
+ countLabel: "pages",
585
+ tooltip: "Pages are the individual sections of your website. Each page has a URL and contains content sections.",
586
+ actions: [
587
+ { label: "View All", href: "/admin/collections/pages" },
588
+ { label: "Create New", href: "/admin/collections/pages/create", primary: true }
589
+ ]
121
590
  }
122
591
  ),
123
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
124
- DashboardCard,
592
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
593
+ ContentCard,
125
594
  {
126
- title: "Media",
595
+ icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MediaIcon, {}),
596
+ title: "Media Library",
127
597
  description: "Upload and organize images and files",
128
- href: "/admin/collections/media"
598
+ count: mediaCount,
599
+ countLabel: "files",
600
+ tooltip: "Your media library stores all images, documents, and files used across your website.",
601
+ actions: [
602
+ { label: "Browse", href: "/admin/collections/media" },
603
+ { label: "Upload", href: "/admin/collections/media/create", primary: true }
604
+ ]
129
605
  }
130
606
  ),
131
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
132
- DashboardCard,
607
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
608
+ ContentCard,
133
609
  {
134
- title: "Site Settings",
135
- description: "Configure global site settings",
136
- href: "/admin/globals/site-settings"
610
+ icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LayoutIcon, {}),
611
+ title: "Site Design",
612
+ description: "Customize your header, footer, and site-wide settings",
613
+ tooltip: "These settings apply to every page on your website \u2014 your navigation menu, footer information, and global SEO settings.",
614
+ actions: [
615
+ { label: "Header & Nav", href: "/admin/globals/header" },
616
+ { label: "Footer", href: "/admin/globals/footer" },
617
+ { label: "Settings", href: "/admin/globals/site-settings", primary: true }
618
+ ]
137
619
  }
138
620
  )
139
621
  ]
140
622
  }
623
+ ),
624
+ recentPages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
625
+ "div",
626
+ {
627
+ style: {
628
+ background: "var(--admin-card-bg)",
629
+ border: "1px solid var(--admin-card-border)",
630
+ borderRadius: "var(--admin-radius-lg)",
631
+ overflow: "hidden"
632
+ },
633
+ children: [
634
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
635
+ "div",
636
+ {
637
+ style: {
638
+ padding: "16px 20px",
639
+ borderBottom: "1px solid var(--admin-border-subtle)",
640
+ display: "flex",
641
+ alignItems: "center",
642
+ justifyContent: "space-between"
643
+ },
644
+ children: [
645
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("h3", { style: { fontSize: 15, fontWeight: 600, color: "var(--admin-text)", margin: 0, display: "flex", alignItems: "center", gap: 6 }, children: [
646
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ClockIcon, {}),
647
+ " Recently Edited"
648
+ ] }),
649
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
650
+ "a",
651
+ {
652
+ href: "/admin/collections/pages",
653
+ style: { fontSize: 13, color: "var(--admin-accent)", textDecoration: "none", fontWeight: 500 },
654
+ children: "View all"
655
+ }
656
+ )
657
+ ]
658
+ }
659
+ ),
660
+ recentPages.map((page, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
661
+ "a",
662
+ {
663
+ href: `/admin/collections/pages/${page.id}`,
664
+ style: {
665
+ display: "flex",
666
+ alignItems: "center",
667
+ justifyContent: "space-between",
668
+ padding: "12px 20px",
669
+ textDecoration: "none",
670
+ borderBottom: i < recentPages.length - 1 ? "1px solid var(--admin-border-subtle)" : "none",
671
+ transition: "background-color 0.15s ease"
672
+ },
673
+ onMouseEnter: (e) => {
674
+ e.currentTarget.style.backgroundColor = "var(--admin-surface)";
675
+ },
676
+ onMouseLeave: (e) => {
677
+ e.currentTarget.style.backgroundColor = "transparent";
678
+ },
679
+ children: [
680
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12, minWidth: 0 }, children: [
681
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PagesIcon, { size: 16 }),
682
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: 14, fontWeight: 500, color: "var(--admin-text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: page.title || page.slug || "Untitled" })
683
+ ] }),
684
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12, flexShrink: 0 }, children: [
685
+ page._status && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatusBadge, { status: page._status, size: "sm" }),
686
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: 12, color: "var(--admin-text-muted)", whiteSpace: "nowrap" }, children: formatRelativeTime(page.updatedAt) })
687
+ ] })
688
+ ]
689
+ },
690
+ page.id
691
+ ))
692
+ ]
693
+ }
141
694
  )
142
695
  ] });
143
696
  }
144
- function DashboardCard({
145
- title,
146
- description,
147
- href
148
- }) {
149
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
697
+ function QuickAction({ href, icon, label }) {
698
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
150
699
  "a",
151
700
  {
152
701
  href,
153
702
  style: {
154
- background: "var(--theme-elevation-50)",
155
- border: "1px solid var(--theme-elevation-150)",
156
- borderRadius: 12,
157
- display: "block",
158
- padding: 20,
703
+ display: "inline-flex",
704
+ alignItems: "center",
705
+ gap: 6,
706
+ padding: "8px 16px",
707
+ background: "var(--admin-surface)",
708
+ border: "1px solid var(--admin-border)",
709
+ borderRadius: "var(--admin-radius-md)",
710
+ color: "var(--admin-text)",
159
711
  textDecoration: "none",
160
- transition: "border-color 0.2s"
712
+ fontSize: 13,
713
+ fontWeight: 500,
714
+ transition: "all 0.2s ease",
715
+ whiteSpace: "nowrap"
716
+ },
717
+ onMouseEnter: (e) => {
718
+ e.currentTarget.style.borderColor = "var(--admin-accent)";
719
+ e.currentTarget.style.color = "var(--admin-accent)";
720
+ e.currentTarget.style.background = "var(--admin-accent-subtle)";
721
+ },
722
+ onMouseLeave: (e) => {
723
+ e.currentTarget.style.borderColor = "var(--admin-border)";
724
+ e.currentTarget.style.color = "var(--admin-text)";
725
+ e.currentTarget.style.background = "var(--admin-surface)";
161
726
  },
162
727
  children: [
163
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { style: { fontSize: 18, fontWeight: 600, marginBottom: 8 }, children: title }),
164
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { color: "var(--theme-text-muted)", fontSize: 14, margin: 0 }, children: description })
728
+ icon,
729
+ label
165
730
  ]
166
731
  }
167
732
  );
168
733
  }
734
+ function ContentCard({
735
+ icon,
736
+ title,
737
+ description,
738
+ count,
739
+ countLabel,
740
+ tooltip,
741
+ actions
742
+ }) {
743
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
744
+ "div",
745
+ {
746
+ style: {
747
+ background: "var(--admin-card-bg)",
748
+ border: "1px solid var(--admin-card-border)",
749
+ borderRadius: "var(--admin-radius-lg)",
750
+ padding: 24,
751
+ display: "flex",
752
+ flexDirection: "column",
753
+ gap: 16,
754
+ boxShadow: "var(--admin-card-shadow)",
755
+ transition: "all 0.2s ease"
756
+ },
757
+ children: [
758
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
759
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
760
+ "div",
761
+ {
762
+ style: {
763
+ width: 40,
764
+ height: 40,
765
+ borderRadius: "var(--admin-radius-md)",
766
+ background: "var(--admin-accent-subtle)",
767
+ color: "var(--admin-accent)",
768
+ display: "flex",
769
+ alignItems: "center",
770
+ justifyContent: "center"
771
+ },
772
+ children: icon
773
+ }
774
+ ),
775
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
776
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("h3", { style: { fontSize: 16, fontWeight: 600, color: "var(--admin-text)", margin: 0, display: "flex", alignItems: "center" }, children: [
777
+ title,
778
+ tooltip && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HelpTooltip, { content: tooltip, position: "right" })
779
+ ] }),
780
+ count !== void 0 && count !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { fontSize: 12, color: "var(--admin-text-muted)" }, children: [
781
+ count,
782
+ " ",
783
+ countLabel
784
+ ] })
785
+ ] })
786
+ ] }) }),
787
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { fontSize: 13, color: "var(--admin-text-muted)", margin: 0, lineHeight: 1.5 }, children: description }),
788
+ actions && actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 8, marginTop: "auto", flexWrap: "wrap" }, children: actions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
789
+ "a",
790
+ {
791
+ href: action.href,
792
+ style: {
793
+ display: "inline-flex",
794
+ alignItems: "center",
795
+ padding: "7px 14px",
796
+ borderRadius: "var(--admin-radius-sm)",
797
+ fontSize: 13,
798
+ fontWeight: 500,
799
+ textDecoration: "none",
800
+ transition: "all 0.15s ease",
801
+ ...action.primary ? {
802
+ background: "var(--admin-accent)",
803
+ color: "var(--admin-text-inverse)"
804
+ } : {
805
+ background: "var(--admin-surface)",
806
+ color: "var(--admin-text-secondary)",
807
+ border: "1px solid var(--admin-border)"
808
+ }
809
+ },
810
+ children: action.label
811
+ },
812
+ action.href
813
+ )) })
814
+ ]
815
+ }
816
+ );
817
+ }
818
+
819
+ // src/components/EmptyState.tsx
820
+ var import_jsx_runtime7 = require("react/jsx-runtime");
821
+ function EmptyState({
822
+ icon,
823
+ title,
824
+ description,
825
+ actionLabel,
826
+ actionHref
827
+ }) {
828
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
829
+ "div",
830
+ {
831
+ style: {
832
+ display: "flex",
833
+ flexDirection: "column",
834
+ alignItems: "center",
835
+ justifyContent: "center",
836
+ padding: "48px 24px",
837
+ textAlign: "center",
838
+ background: "var(--admin-surface)",
839
+ borderRadius: "var(--admin-radius-lg)",
840
+ border: "1px dashed var(--admin-border)"
841
+ },
842
+ children: [
843
+ icon && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
844
+ "div",
845
+ {
846
+ style: {
847
+ marginBottom: 16,
848
+ color: "var(--admin-text-muted)",
849
+ opacity: 0.6
850
+ },
851
+ children: icon
852
+ }
853
+ ),
854
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
855
+ "h3",
856
+ {
857
+ style: {
858
+ fontSize: 18,
859
+ fontWeight: 600,
860
+ color: "var(--admin-text)",
861
+ margin: "0 0 8px"
862
+ },
863
+ children: title
864
+ }
865
+ ),
866
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
867
+ "p",
868
+ {
869
+ style: {
870
+ fontSize: 14,
871
+ color: "var(--admin-text-muted)",
872
+ margin: "0 0 20px",
873
+ maxWidth: 400,
874
+ lineHeight: 1.5
875
+ },
876
+ children: description
877
+ }
878
+ ),
879
+ actionLabel && actionHref && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
880
+ "a",
881
+ {
882
+ href: actionHref,
883
+ style: {
884
+ display: "inline-flex",
885
+ alignItems: "center",
886
+ gap: 6,
887
+ padding: "10px 20px",
888
+ background: "var(--admin-accent)",
889
+ color: "var(--admin-text-inverse)",
890
+ borderRadius: "var(--admin-radius-md)",
891
+ textDecoration: "none",
892
+ fontWeight: 600,
893
+ fontSize: 14,
894
+ transition: "all 0.2s ease"
895
+ },
896
+ children: actionLabel
897
+ }
898
+ )
899
+ ]
900
+ }
901
+ );
902
+ }
903
+
904
+ // src/components/BlockPicker.tsx
905
+ var import_react5 = require("react");
906
+ var import_jsx_runtime8 = require("react/jsx-runtime");
907
+ function BlockPicker({
908
+ blocks,
909
+ onSelect
910
+ }) {
911
+ const [searchQuery, setSearchQuery] = (0, import_react5.useState)("");
912
+ const filtered = blocks.filter(
913
+ (b) => b.label.toLowerCase().includes(searchQuery.toLowerCase()) || b.slug.toLowerCase().includes(searchQuery.toLowerCase()) || b.description && b.description.toLowerCase().includes(searchQuery.toLowerCase())
914
+ );
915
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
916
+ blocks.length > 4 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
917
+ "input",
918
+ {
919
+ type: "text",
920
+ placeholder: "Search sections...",
921
+ value: searchQuery,
922
+ onChange: (e) => setSearchQuery(e.target.value),
923
+ style: {
924
+ padding: "10px 14px",
925
+ border: "1px solid var(--admin-input-border)",
926
+ borderRadius: "var(--admin-radius-md)",
927
+ background: "var(--admin-input-bg)",
928
+ color: "var(--admin-text)",
929
+ fontSize: 14,
930
+ outline: "none",
931
+ width: "100%",
932
+ boxSizing: "border-box"
933
+ }
934
+ }
935
+ ),
936
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
937
+ "div",
938
+ {
939
+ style: {
940
+ display: "grid",
941
+ gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
942
+ gap: 12
943
+ },
944
+ children: filtered.map((block) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
945
+ "button",
946
+ {
947
+ type: "button",
948
+ onClick: () => onSelect(block.slug),
949
+ style: {
950
+ display: "flex",
951
+ flexDirection: "column",
952
+ alignItems: "center",
953
+ gap: 10,
954
+ padding: 16,
955
+ background: "var(--admin-card-bg)",
956
+ border: "1px solid var(--admin-card-border)",
957
+ borderRadius: "var(--admin-radius-lg)",
958
+ cursor: "pointer",
959
+ transition: "all 0.2s ease",
960
+ textAlign: "center"
961
+ },
962
+ onMouseEnter: (e) => {
963
+ e.currentTarget.style.borderColor = "var(--admin-accent)";
964
+ e.currentTarget.style.boxShadow = "var(--admin-card-shadow-hover)";
965
+ e.currentTarget.style.transform = "translateY(-2px)";
966
+ },
967
+ onMouseLeave: (e) => {
968
+ e.currentTarget.style.borderColor = "var(--admin-card-border)";
969
+ e.currentTarget.style.boxShadow = "none";
970
+ e.currentTarget.style.transform = "translateY(0)";
971
+ },
972
+ children: [
973
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
974
+ "div",
975
+ {
976
+ style: {
977
+ width: 48,
978
+ height: 48,
979
+ borderRadius: "var(--admin-radius-md)",
980
+ background: "var(--admin-accent-subtle)",
981
+ color: "var(--admin-accent)",
982
+ display: "flex",
983
+ alignItems: "center",
984
+ justifyContent: "center",
985
+ fontSize: 24,
986
+ overflow: "hidden"
987
+ },
988
+ children: block.imageURL ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
989
+ "img",
990
+ {
991
+ src: block.imageURL,
992
+ alt: block.label,
993
+ style: { width: "100%", height: "100%", objectFit: "cover" }
994
+ }
995
+ ) : block.icon ? block.icon : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(BlockDefaultIcon, {})
996
+ }
997
+ ),
998
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
999
+ "span",
1000
+ {
1001
+ style: {
1002
+ fontSize: 13,
1003
+ fontWeight: 600,
1004
+ color: "var(--admin-text)",
1005
+ lineHeight: 1.3
1006
+ },
1007
+ children: block.label
1008
+ }
1009
+ ),
1010
+ block.description && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1011
+ "span",
1012
+ {
1013
+ style: {
1014
+ fontSize: 11,
1015
+ color: "var(--admin-text-muted)",
1016
+ lineHeight: 1.4
1017
+ },
1018
+ children: block.description
1019
+ }
1020
+ )
1021
+ ]
1022
+ },
1023
+ block.slug
1024
+ ))
1025
+ }
1026
+ ),
1027
+ filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: { textAlign: "center", color: "var(--admin-text-muted)", fontSize: 14, padding: 20 }, children: "No sections match your search." })
1028
+ ] });
1029
+ }
1030
+ function BlockDefaultIcon() {
1031
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
1032
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { x: "3", y: "3", width: "7", height: "7" }),
1033
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { x: "14", y: "3", width: "7", height: "7" }),
1034
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { x: "14", y: "14", width: "7", height: "7" }),
1035
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { x: "3", y: "14", width: "7", height: "7" })
1036
+ ] });
1037
+ }
1038
+
1039
+ // src/components/SectionTabs.tsx
1040
+ var import_react6 = require("react");
1041
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1042
+ function SectionTabs({
1043
+ tabs,
1044
+ defaultTab = 0
1045
+ }) {
1046
+ const [activeTab, setActiveTab] = (0, import_react6.useState)(defaultTab);
1047
+ if (tabs.length === 0) return null;
1048
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1049
+ "div",
1050
+ {
1051
+ style: {
1052
+ border: "1px solid var(--admin-border)",
1053
+ borderRadius: "var(--admin-radius-lg)",
1054
+ overflow: "hidden",
1055
+ background: "var(--admin-card-bg)"
1056
+ },
1057
+ children: [
1058
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1059
+ "div",
1060
+ {
1061
+ style: {
1062
+ display: "flex",
1063
+ borderBottom: "2px solid var(--admin-border)",
1064
+ background: "var(--admin-surface)",
1065
+ overflowX: "auto",
1066
+ scrollbarWidth: "none"
1067
+ },
1068
+ children: tabs.map((tab, index) => {
1069
+ const isActive = index === activeTab;
1070
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1071
+ "button",
1072
+ {
1073
+ type: "button",
1074
+ onClick: () => setActiveTab(index),
1075
+ style: {
1076
+ display: "flex",
1077
+ alignItems: "center",
1078
+ gap: 6,
1079
+ padding: "12px 18px",
1080
+ border: "none",
1081
+ borderBottom: isActive ? "2px solid var(--admin-accent)" : "2px solid transparent",
1082
+ marginBottom: -2,
1083
+ background: "transparent",
1084
+ color: isActive ? "var(--admin-accent)" : "var(--admin-text-muted)",
1085
+ fontWeight: isActive ? 600 : 500,
1086
+ fontSize: 13,
1087
+ cursor: "pointer",
1088
+ transition: "all 0.15s ease",
1089
+ whiteSpace: "nowrap"
1090
+ },
1091
+ children: [
1092
+ tab.icon && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { display: "flex", opacity: isActive ? 1 : 0.6 }, children: tab.icon }),
1093
+ tab.label
1094
+ ]
1095
+ },
1096
+ index
1097
+ );
1098
+ })
1099
+ }
1100
+ ),
1101
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: 20 }, children: tabs[activeTab]?.content })
1102
+ ]
1103
+ }
1104
+ );
1105
+ }
1106
+
1107
+ // src/components/WelcomeHeader.tsx
1108
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1109
+ function WelcomeHeader({
1110
+ title,
1111
+ description,
1112
+ tooltip,
1113
+ actions
1114
+ }) {
1115
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1116
+ "div",
1117
+ {
1118
+ style: {
1119
+ display: "flex",
1120
+ alignItems: "flex-start",
1121
+ justifyContent: "space-between",
1122
+ padding: "24px 0",
1123
+ borderBottom: "1px solid var(--admin-border-subtle)",
1124
+ marginBottom: 24
1125
+ },
1126
+ children: [
1127
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
1128
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1129
+ "h1",
1130
+ {
1131
+ style: {
1132
+ fontSize: 24,
1133
+ fontWeight: 700,
1134
+ color: "var(--admin-text)",
1135
+ margin: 0,
1136
+ display: "flex",
1137
+ alignItems: "center"
1138
+ },
1139
+ children: [
1140
+ title,
1141
+ tooltip && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HelpTooltip, { content: tooltip, position: "right" })
1142
+ ]
1143
+ }
1144
+ ),
1145
+ description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { fontSize: 14, color: "var(--admin-text-muted)", margin: "6px 0 0" }, children: description })
1146
+ ] }),
1147
+ actions && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { display: "flex", gap: 8 }, children: actions })
1148
+ ]
1149
+ }
1150
+ );
1151
+ }
1152
+
1153
+ // src/fields/themePreference.ts
1154
+ var themePreferenceField = {
1155
+ name: "themePreference",
1156
+ type: "select",
1157
+ defaultValue: "light",
1158
+ options: [
1159
+ { label: "Light", value: "light" },
1160
+ { label: "Dark", value: "dark" },
1161
+ { label: "Brand Light", value: "brand-light" },
1162
+ { label: "Brand Dark", value: "brand-dark" }
1163
+ ],
1164
+ admin: {
1165
+ position: "sidebar",
1166
+ condition: () => false
1167
+ // Hidden from form — managed by ThemeSwitcher
1168
+ }
1169
+ };
1170
+
1171
+ // src/helpers/configureAdmin.ts
1172
+ function configureAdmin(config) {
1173
+ const { brandName, brandPrimary, brandSecondary } = config;
1174
+ const brandCssVars = [
1175
+ brandPrimary ? `--brand-primary: ${brandPrimary};` : "",
1176
+ brandSecondary ? `--brand-secondary: ${brandSecondary};` : ""
1177
+ ].filter(Boolean).join("\n ");
1178
+ return {
1179
+ admin: {
1180
+ components: {
1181
+ graphics: {
1182
+ Logo: {
1183
+ exportName: "Logo",
1184
+ path: "@orion-studios/payload-admin-components"
1185
+ },
1186
+ Icon: {
1187
+ exportName: "Icon",
1188
+ path: "@orion-studios/payload-admin-components"
1189
+ }
1190
+ },
1191
+ views: {
1192
+ dashboard: {
1193
+ Component: {
1194
+ exportName: "Dashboard",
1195
+ path: "@orion-studios/payload-admin-components"
1196
+ }
1197
+ }
1198
+ },
1199
+ providers: [
1200
+ {
1201
+ exportName: "ThemeProvider",
1202
+ path: "@orion-studios/payload-admin-components"
1203
+ }
1204
+ ],
1205
+ afterNavLinks: [
1206
+ {
1207
+ exportName: "ThemeSwitcher",
1208
+ path: "@orion-studios/payload-admin-components"
1209
+ }
1210
+ ]
1211
+ },
1212
+ meta: {
1213
+ titleSuffix: ` \u2014 ${brandName}`
1214
+ }
1215
+ },
1216
+ brandName,
1217
+ brandPrimary: brandPrimary || "#3b82f6",
1218
+ brandSecondary: brandSecondary || "#8b5cf6",
1219
+ brandCssVars,
1220
+ /**
1221
+ * Wraps the Users collection to add theme preference field.
1222
+ */
1223
+ wrapUsers(usersCollection) {
1224
+ return {
1225
+ ...usersCollection,
1226
+ fields: [...usersCollection.fields || [], themePreferenceField]
1227
+ };
1228
+ },
1229
+ /**
1230
+ * Wraps globals with intuitive group labels.
1231
+ * Maps common global slugs to the "Site Design" group with user-friendly labels.
1232
+ */
1233
+ wrapGlobals(globals) {
1234
+ const labelMap = {
1235
+ header: { group: "Site Design", label: "Header & Navigation" },
1236
+ footer: { group: "Site Design", label: "Footer" },
1237
+ "site-settings": { group: "Site Design", label: "Website Settings" }
1238
+ };
1239
+ return globals.map((global) => {
1240
+ const mapping = labelMap[global.slug];
1241
+ if (!mapping) return global;
1242
+ return {
1243
+ ...global,
1244
+ admin: {
1245
+ ...global.admin,
1246
+ group: mapping.group
1247
+ },
1248
+ label: mapping.label
1249
+ };
1250
+ });
1251
+ }
1252
+ };
1253
+ }
1254
+
1255
+ // src/helpers/withTooltips.ts
1256
+ var defaultTooltips = {
1257
+ title: "The main title displayed on this page.",
1258
+ slug: 'The URL-friendly name for this page (e.g., "about-us"). This appears in the web address.',
1259
+ template: "Choose a layout template. This controls the overall structure of the page.",
1260
+ parent: "Select a parent page to nest this page under. This affects the URL path.",
1261
+ path: "The full URL path for this page. This is automatically generated from the slug and parent.",
1262
+ layout: "Add and arrange content sections on your page. Each section is a building block.",
1263
+ metaTitle: "The title shown in search engine results and browser tabs. Keep under 60 characters.",
1264
+ metaDescription: "A brief summary shown in search results. Keep under 160 characters for best results.",
1265
+ canonicalUrl: "The preferred URL for this page. Used to prevent duplicate content in search engines.",
1266
+ ogImage: "The image shown when this page is shared on social media (Facebook, LinkedIn, etc.).",
1267
+ noIndex: "When enabled, search engines will not list this page in results.",
1268
+ noFollow: "When enabled, search engines will not follow links on this page.",
1269
+ publishedAt: "The date and time this page was first published.",
1270
+ alt: "Describe this image for screen readers and search engines. Be specific and concise.",
1271
+ navItems: "The links shown in your website's navigation menu.",
1272
+ copyright: "The copyright text displayed in your website footer.",
1273
+ contactEmail: "The email address displayed in your footer and contact sections.",
1274
+ contactPhone: "The phone number displayed in your footer and contact sections.",
1275
+ siteName: "Your website's name. Appears in browser tabs and search results.",
1276
+ tagline: "A short phrase describing your business. Used in SEO and site metadata.",
1277
+ canonicalBaseUrl: 'The base URL of your website (e.g., "https://example.com"). Used for generating canonical URLs.'
1278
+ };
1279
+ function withTooltips(fields, customTooltips) {
1280
+ const tooltips = { ...defaultTooltips, ...customTooltips };
1281
+ return fields.map((field) => addTooltipToField(field, tooltips));
1282
+ }
1283
+ function addTooltipToField(field, tooltips) {
1284
+ if ("name" in field && field.name && tooltips[field.name]) {
1285
+ const tooltip = tooltips[field.name];
1286
+ const admin = field.admin;
1287
+ if (!admin?.description) {
1288
+ return {
1289
+ ...field,
1290
+ admin: {
1291
+ ...admin,
1292
+ description: tooltip
1293
+ }
1294
+ };
1295
+ }
1296
+ }
1297
+ if ("fields" in field && Array.isArray(field.fields)) {
1298
+ return {
1299
+ ...field,
1300
+ fields: field.fields.map((f) => addTooltipToField(f, tooltips))
1301
+ };
1302
+ }
1303
+ if ("tabs" in field && Array.isArray(field.tabs)) {
1304
+ return {
1305
+ ...field,
1306
+ tabs: field.tabs.map((tab) => ({
1307
+ ...tab,
1308
+ fields: tab.fields ? tab.fields.map((f) => addTooltipToField(f, tooltips)) : tab.fields
1309
+ }))
1310
+ };
1311
+ }
1312
+ return field;
1313
+ }
169
1314
  // Annotate the CommonJS export names for ESM import in node:
170
1315
  0 && (module.exports = {
1316
+ BlockPicker,
171
1317
  Dashboard,
1318
+ EmptyState,
1319
+ HelpTooltip,
172
1320
  Icon,
173
- Logo
1321
+ Logo,
1322
+ SectionTabs,
1323
+ StatusBadge,
1324
+ ThemeProvider,
1325
+ ThemeSwitcher,
1326
+ WelcomeHeader,
1327
+ configureAdmin,
1328
+ themePreferenceField,
1329
+ useTheme,
1330
+ withTooltips
174
1331
  });