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