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

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