@umituz/web-dashboard 1.0.0 → 1.0.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 ADDED
@@ -0,0 +1,768 @@
1
+ "use client";
2
+
3
+ // src/presentation/organisms/DashboardLayout.tsx
4
+ import { useState as useState3, useEffect } from "react";
5
+ import { useLocation as useLocation2, Outlet, Navigate } from "react-router-dom";
6
+ import { Skeleton } from "@umituz/web-design-system/atoms";
7
+
8
+ // src/presentation/molecules/DashboardSidebar.tsx
9
+ import { useState } from "react";
10
+ import { Link, useLocation } from "react-router-dom";
11
+ import { useTranslation } from "react-i18next";
12
+ import { Button } from "@umituz/web-design-system/atoms";
13
+
14
+ // src/presentation/molecules/BrandLogo.tsx
15
+ import { cn } from "@umituz/web-design-system/utils";
16
+ import { jsx, jsxs } from "react/jsx-runtime";
17
+ var BrandLogo = ({ className, size = 32 }) => {
18
+ return /* @__PURE__ */ jsxs(
19
+ "svg",
20
+ {
21
+ width: size,
22
+ height: size,
23
+ viewBox: "0 0 100 100",
24
+ fill: "none",
25
+ xmlns: "http://www.w3.org/2000/svg",
26
+ className: cn("shrink-0", className),
27
+ children: [
28
+ /* @__PURE__ */ jsx(
29
+ "rect",
30
+ {
31
+ x: "15",
32
+ y: "65",
33
+ width: "70",
34
+ height: "15",
35
+ rx: "4",
36
+ fill: "hsl(var(--primary))"
37
+ }
38
+ ),
39
+ /* @__PURE__ */ jsx(
40
+ "rect",
41
+ {
42
+ x: "25",
43
+ y: "25",
44
+ width: "12",
45
+ height: "40",
46
+ rx: "2",
47
+ fill: "hsl(var(--primary))"
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsx(
51
+ "rect",
52
+ {
53
+ x: "44",
54
+ y: "35",
55
+ width: "12",
56
+ height: "30",
57
+ rx: "2",
58
+ fill: "hsl(var(--primary))"
59
+ }
60
+ ),
61
+ /* @__PURE__ */ jsx(
62
+ "rect",
63
+ {
64
+ x: "63",
65
+ y: "20",
66
+ width: "12",
67
+ height: "45",
68
+ rx: "2",
69
+ fill: "hsl(var(--primary))"
70
+ }
71
+ ),
72
+ /* @__PURE__ */ jsx(
73
+ "rect",
74
+ {
75
+ x: "20",
76
+ y: "45",
77
+ width: "60",
78
+ height: "10",
79
+ rx: "2",
80
+ fill: "hsl(var(--secondary))"
81
+ }
82
+ ),
83
+ /* @__PURE__ */ jsx(
84
+ "circle",
85
+ {
86
+ cx: "50",
87
+ cy: "20",
88
+ r: "5",
89
+ fill: "hsl(var(--secondary))"
90
+ }
91
+ )
92
+ ]
93
+ }
94
+ );
95
+ };
96
+
97
+ // src/presentation/molecules/DashboardSidebar.tsx
98
+ import { PenTool, Menu, ChevronLeft, ChevronDown, ChevronRight } from "lucide-react";
99
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
100
+ var DashboardSidebar = ({
101
+ collapsed,
102
+ setCollapsed,
103
+ sidebarGroups,
104
+ brandName = "App",
105
+ brandTagline = "grow smarter",
106
+ createPostRoute = "/dashboard/create",
107
+ user
108
+ }) => {
109
+ const location = useLocation();
110
+ const { t } = useTranslation();
111
+ const [collapsedGroups, setCollapsedGroups] = useState({});
112
+ const toggleGroup = (title) => {
113
+ setCollapsedGroups((prev) => ({
114
+ ...prev,
115
+ [title]: !prev[title]
116
+ }));
117
+ };
118
+ return /* @__PURE__ */ jsxs2("div", { className: "flex h-full flex-col", children: [
119
+ /* @__PURE__ */ jsxs2("div", { className: "flex h-16 items-center gap-3 border-b border-sidebar-border px-4 transition-all duration-300", children: [
120
+ /* @__PURE__ */ jsx2(BrandLogo, { size: 32 }),
121
+ !collapsed && /* @__PURE__ */ jsxs2("div", { className: "flex flex-col -gap-1", children: [
122
+ /* @__PURE__ */ jsx2("span", { className: "text-2xl font-black text-sidebar-foreground tracking-tighter leading-none", children: brandName }),
123
+ /* @__PURE__ */ jsx2("span", { className: "text-[11px] font-bold text-primary/70 lowercase tracking-tight mt-2 ml-1 select-none underline decoration-primary/40 underline-offset-[6px] decoration-2", children: brandTagline })
124
+ ] })
125
+ ] }),
126
+ /* @__PURE__ */ jsx2("div", { className: "px-3 py-4 border-b border-sidebar-border/50", children: /* @__PURE__ */ jsx2(Link, { to: createPostRoute, children: /* @__PURE__ */ jsxs2(
127
+ Button,
128
+ {
129
+ variant: "default",
130
+ className: `w-full gap-3 shadow-glow transition-all active:scale-95 group overflow-hidden rounded-xl ${collapsed ? "px-0 justify-center h-10 w-10 mx-auto" : "justify-start px-4 h-11"}`,
131
+ title: collapsed ? t("sidebar.createPost") : void 0,
132
+ children: [
133
+ /* @__PURE__ */ jsx2(PenTool, { className: `shrink-0 transition-transform duration-300 ${collapsed ? "h-5 w-5" : "h-4 w-4 group-hover:scale-110"}` }),
134
+ !collapsed && /* @__PURE__ */ jsx2("span", { className: "font-bold tracking-tight", children: t("sidebar.createPost") })
135
+ ]
136
+ }
137
+ ) }) }),
138
+ /* @__PURE__ */ jsx2("nav", { className: "flex-1 overflow-y-auto px-2 py-3 scrollbar-hide", children: /* @__PURE__ */ jsx2("div", { className: "space-y-6", children: sidebarGroups.map((group) => {
139
+ const filteredItems = group.items.filter((item) => {
140
+ if (item.enabled === false) return false;
141
+ if (!item.requiredApp) return true;
142
+ if (item.requiredApp === "mobile") return user?.hasMobileApp;
143
+ if (item.requiredApp === "web") return user?.hasWebApp;
144
+ return true;
145
+ });
146
+ if (filteredItems.length === 0) return null;
147
+ const isGroupCollapsed = collapsedGroups[group.title];
148
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
149
+ !collapsed && /* @__PURE__ */ jsxs2(
150
+ "button",
151
+ {
152
+ onClick: () => toggleGroup(group.title),
153
+ className: "w-full flex items-center justify-between px-3 mb-2 group/header",
154
+ children: [
155
+ /* @__PURE__ */ jsx2("span", { className: "text-[10px] font-bold uppercase tracking-widest text-sidebar-foreground/40 group-hover/header:text-sidebar-foreground/70 transition-colors", children: group.title === "sidebar.ai" ? `${brandName} AI` : t(group.title) }),
156
+ isGroupCollapsed ? /* @__PURE__ */ jsx2(ChevronRight, { className: "h-3 w-3 text-sidebar-foreground/30 flex-shrink-0 group-hover/header:text-sidebar-foreground/50 transition-colors" }) : /* @__PURE__ */ jsx2(ChevronDown, { className: "h-3 w-3 text-sidebar-foreground/30 flex-shrink-0 group-hover/header:text-sidebar-foreground/50 transition-colors" })
157
+ ]
158
+ }
159
+ ),
160
+ (!isGroupCollapsed || collapsed) && filteredItems.map((item) => {
161
+ const active = location.pathname === item.path;
162
+ return /* @__PURE__ */ jsxs2(
163
+ Link,
164
+ {
165
+ to: item.path,
166
+ className: `flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-all duration-200 ${active ? "bg-sidebar-accent text-sidebar-accent-foreground shadow-sm" : "text-sidebar-foreground/70 hover:bg-sidebar-accent/40 hover:text-sidebar-foreground"} ${collapsed ? "justify-center" : ""}`,
167
+ title: collapsed ? t(item.label) : void 0,
168
+ children: [
169
+ /* @__PURE__ */ jsx2(item.icon, { className: `h-4 w-4 shrink-0 transition-transform ${active && "scale-110"}` }),
170
+ !collapsed && /* @__PURE__ */ jsx2("span", { children: t(item.label) })
171
+ ]
172
+ },
173
+ item.path
174
+ );
175
+ })
176
+ ] }, group.title);
177
+ }) }) }),
178
+ /* @__PURE__ */ jsx2("div", { className: "border-t border-sidebar-border p-3", children: /* @__PURE__ */ jsxs2("div", { className: `flex items-center ${collapsed ? "justify-center" : "justify-between"}`, children: [
179
+ !collapsed && /* @__PURE__ */ jsx2("p", { className: "text-[10px] uppercase tracking-wider text-sidebar-foreground/40 font-bold px-2", children: t("sidebar.system") }),
180
+ /* @__PURE__ */ jsx2(Button, { variant: "ghost", size: "icon", onClick: () => setCollapsed(!collapsed), className: "text-sidebar-foreground/70", children: collapsed ? /* @__PURE__ */ jsx2(Menu, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx2(ChevronLeft, { className: "h-4 w-4" }) })
181
+ ] }) })
182
+ ] });
183
+ };
184
+
185
+ // src/presentation/organisms/DashboardHeader.tsx
186
+ import React, { useState as useState2, useCallback } from "react";
187
+ import {
188
+ Bell,
189
+ X,
190
+ Sun,
191
+ Moon,
192
+ Menu as Menu2,
193
+ User,
194
+ Settings,
195
+ LogOut,
196
+ ChevronDown as ChevronDown2,
197
+ CreditCard
198
+ } from "lucide-react";
199
+ import { Button as Button2 } from "@umituz/web-design-system/atoms";
200
+ import { useNavigate } from "react-router-dom";
201
+ import { useTranslation as useTranslation2 } from "react-i18next";
202
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
203
+ var DashboardHeader = ({
204
+ collapsed,
205
+ setCollapsed,
206
+ setMobileOpen,
207
+ title,
208
+ user,
209
+ notifications = [],
210
+ onLogout,
211
+ onMarkAllRead,
212
+ onDismissNotification,
213
+ formatDate,
214
+ settingsRoute = "/dashboard/settings",
215
+ profileRoute = "/dashboard/profile",
216
+ billingRoute = "/dashboard/billing"
217
+ }) => {
218
+ const navigate = useNavigate();
219
+ const { t } = useTranslation2();
220
+ const [notifOpen, setNotifOpen] = useState2(false);
221
+ const [profileOpen, setProfileOpen] = useState2(false);
222
+ const unreadCount = notifications.filter((n) => !n.read).length;
223
+ const markAllRead = () => {
224
+ onMarkAllRead?.();
225
+ };
226
+ const formatTimeAgo = useCallback((createdAt) => {
227
+ if (!formatDate) return "";
228
+ const date = new Date(createdAt);
229
+ const secs = Math.floor((Date.now() - date.getTime()) / 1e3);
230
+ if (secs < 60) return t("dashboard.activityFeed.times.justNow");
231
+ if (secs < 3600) return t("dashboard.activityFeed.times.minutesAgo", { val: Math.floor(secs / 60) });
232
+ if (secs < 86400) return t("dashboard.activityFeed.times.hoursAgo", { val: Math.floor(secs / 3600) });
233
+ return t("dashboard.activityFeed.times.daysAgo", { val: Math.floor(secs / 86400) });
234
+ }, [t, formatDate]);
235
+ const handleLogout = async () => {
236
+ try {
237
+ await onLogout?.();
238
+ navigate("/login");
239
+ } catch (error) {
240
+ }
241
+ };
242
+ const ThemeToggle = () => {
243
+ const [resolvedTheme, setResolvedTheme] = React.useState("light");
244
+ return /* @__PURE__ */ jsx3(
245
+ Button2,
246
+ {
247
+ variant: "ghost",
248
+ size: "icon",
249
+ onClick: () => setResolvedTheme(resolvedTheme === "light" ? "dark" : "light"),
250
+ className: "text-muted-foreground h-9 w-9",
251
+ title: resolvedTheme === "dark" ? t("common.tooltips.switchLight") : t("common.tooltips.switchDark"),
252
+ children: resolvedTheme === "dark" ? /* @__PURE__ */ jsx3(Sun, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx3(Moon, { className: "h-4 w-4" })
253
+ }
254
+ );
255
+ };
256
+ return /* @__PURE__ */ jsxs3("header", { className: "flex h-14 items-center justify-between border-b border-border bg-card/50 backdrop-blur-md px-4 shrink-0 z-30", children: [
257
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3", children: [
258
+ /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "icon", className: "md:hidden", onClick: () => setMobileOpen(true), children: /* @__PURE__ */ jsx3(Menu2, { className: "h-5 w-5" }) }),
259
+ collapsed && /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "icon", className: "hidden md:inline-flex", onClick: () => setCollapsed(false), children: /* @__PURE__ */ jsx3(Menu2, { className: "h-5 w-5" }) }),
260
+ /* @__PURE__ */ jsx3("h2", { className: "text-sm font-semibold text-foreground", children: title })
261
+ ] }),
262
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
263
+ /* @__PURE__ */ jsx3(ThemeToggle, {}),
264
+ /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
265
+ /* @__PURE__ */ jsxs3(
266
+ Button2,
267
+ {
268
+ variant: "ghost",
269
+ size: "icon",
270
+ className: "text-muted-foreground relative h-9 w-9",
271
+ onClick: () => {
272
+ setNotifOpen(!notifOpen);
273
+ setProfileOpen(false);
274
+ },
275
+ children: [
276
+ /* @__PURE__ */ jsx3(Bell, { className: "h-4 w-4" }),
277
+ unreadCount > 0 && /* @__PURE__ */ jsxs3("span", { className: "absolute top-2 right-2 flex h-2 w-2", children: [
278
+ /* @__PURE__ */ jsx3("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-destructive opacity-75" }),
279
+ /* @__PURE__ */ jsx3("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-destructive" })
280
+ ] })
281
+ ]
282
+ }
283
+ ),
284
+ notifOpen && /* @__PURE__ */ jsxs3(Fragment, { children: [
285
+ /* @__PURE__ */ jsx3("div", { className: "fixed inset-0 z-40", onClick: () => setNotifOpen(false) }),
286
+ /* @__PURE__ */ jsxs3("div", { className: "absolute top-12 right-0 w-80 bg-popover border border-border rounded-xl shadow-xl z-50 overflow-hidden animate-in fade-in zoom-in-95 duration-200", children: [
287
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border bg-muted/50", children: [
288
+ /* @__PURE__ */ jsx3("h3", { className: "text-xs font-bold uppercase tracking-wider text-foreground", children: t("dashboard.notifications.title") }),
289
+ unreadCount > 0 && /* @__PURE__ */ jsx3("button", { onClick: markAllRead, className: "text-[10px] font-bold text-primary hover:underline uppercase", children: t("dashboard.notifications.markAllRead") })
290
+ ] }),
291
+ /* @__PURE__ */ jsxs3("div", { className: "max-h-[400px] overflow-y-auto", children: [
292
+ notifications.map((n) => /* @__PURE__ */ jsxs3("div", { className: `px-4 py-3 border-b border-border last:border-0 flex items-start gap-3 transition-colors hover:bg-muted/30 ${!n.read ? "bg-primary/5" : ""}`, children: [
293
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [
294
+ /* @__PURE__ */ jsx3("p", { className: "text-sm text-foreground leading-snug", children: n.text }),
295
+ /* @__PURE__ */ jsxs3("p", { className: "text-[10px] text-muted-foreground mt-1 flex items-center gap-1", children: [
296
+ /* @__PURE__ */ jsx3("span", { className: "inline-block w-1 h-1 rounded-full bg-muted-foreground/30" }),
297
+ formatTimeAgo(n.createdAt)
298
+ ] })
299
+ ] }),
300
+ /* @__PURE__ */ jsx3(
301
+ "button",
302
+ {
303
+ onClick: () => onDismissNotification?.(n.id),
304
+ className: "text-muted-foreground/50 hover:text-foreground shrink-0 transition-colors",
305
+ children: /* @__PURE__ */ jsx3(X, { className: "h-3 w-3" })
306
+ }
307
+ )
308
+ ] }, n.id)),
309
+ notifications.length === 0 && /* @__PURE__ */ jsxs3("div", { className: "px-4 py-10 text-center", children: [
310
+ /* @__PURE__ */ jsx3("div", { className: "mx-auto w-10 h-10 rounded-full bg-muted flex items-center justify-center mb-3", children: /* @__PURE__ */ jsx3(Bell, { className: "h-5 w-5 text-muted-foreground/50" }) }),
311
+ /* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground", children: t("dashboard.notifications.none") })
312
+ ] })
313
+ ] })
314
+ ] })
315
+ ] })
316
+ ] }),
317
+ /* @__PURE__ */ jsx3("div", { className: "h-6 w-px bg-border mx-1" }),
318
+ /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
319
+ /* @__PURE__ */ jsxs3(
320
+ "button",
321
+ {
322
+ onClick: () => {
323
+ setProfileOpen(!profileOpen);
324
+ setNotifOpen(false);
325
+ },
326
+ className: "flex items-center gap-2 p-1 pl-1 rounded-full hover:bg-muted transition-colors group",
327
+ children: [
328
+ /* @__PURE__ */ jsx3("div", { className: "w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-[10px] font-bold text-primary overflow-hidden border border-primary/20 ring-primary/20 group-hover:ring-4 transition-all", children: user?.avatar && /* @__PURE__ */ jsx3("img", { src: user.avatar, alt: "User", className: "w-full h-full object-cover" }) }),
329
+ /* @__PURE__ */ jsx3(ChevronDown2, { className: `h-4 w-4 text-muted-foreground transition-transform duration-200 ${profileOpen && "rotate-180"}` })
330
+ ]
331
+ }
332
+ ),
333
+ profileOpen && /* @__PURE__ */ jsxs3(Fragment, { children: [
334
+ /* @__PURE__ */ jsx3("div", { className: "fixed inset-0 z-40", onClick: () => setProfileOpen(false) }),
335
+ /* @__PURE__ */ jsxs3("div", { className: "absolute top-12 right-0 w-56 bg-popover border border-border rounded-xl shadow-xl z-50 overflow-hidden animate-in fade-in zoom-in-95 duration-200 p-1.5", children: [
336
+ /* @__PURE__ */ jsxs3("div", { className: "px-3 py-2 border-b border-border/50 mb-1", children: [
337
+ /* @__PURE__ */ jsx3("p", { className: "text-sm font-bold text-foreground", children: user?.name || t("common.roles.user") }),
338
+ /* @__PURE__ */ jsx3("p", { className: "text-xs text-muted-foreground truncate", children: user?.email })
339
+ ] }),
340
+ /* @__PURE__ */ jsxs3("div", { className: "space-y-0.5", children: [
341
+ /* @__PURE__ */ jsxs3(
342
+ "button",
343
+ {
344
+ onClick: () => {
345
+ navigate(profileRoute);
346
+ setProfileOpen(false);
347
+ },
348
+ className: "flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground hover:bg-muted rounded-lg transition-colors",
349
+ children: [
350
+ /* @__PURE__ */ jsx3(User, { className: "h-4 w-4 text-muted-foreground" }),
351
+ t("common.profile")
352
+ ]
353
+ }
354
+ ),
355
+ /* @__PURE__ */ jsxs3(
356
+ "button",
357
+ {
358
+ onClick: () => {
359
+ navigate(billingRoute);
360
+ setProfileOpen(false);
361
+ },
362
+ className: "flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground hover:bg-muted rounded-lg transition-colors",
363
+ children: [
364
+ /* @__PURE__ */ jsx3(CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
365
+ t("common.billing")
366
+ ]
367
+ }
368
+ ),
369
+ /* @__PURE__ */ jsxs3(
370
+ "button",
371
+ {
372
+ onClick: () => {
373
+ navigate(settingsRoute);
374
+ setProfileOpen(false);
375
+ },
376
+ className: "flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground hover:bg-muted rounded-lg transition-colors",
377
+ children: [
378
+ /* @__PURE__ */ jsx3(Settings, { className: "h-4 w-4 text-muted-foreground" }),
379
+ t("common.settings")
380
+ ]
381
+ }
382
+ )
383
+ ] }),
384
+ /* @__PURE__ */ jsx3("div", { className: "h-px bg-border my-1.5" }),
385
+ /* @__PURE__ */ jsxs3(
386
+ "button",
387
+ {
388
+ onClick: handleLogout,
389
+ className: "flex w-full items-center gap-2.5 px-3 py-2 text-sm text-destructive hover:bg-destructive/10 rounded-lg transition-colors font-medium",
390
+ children: [
391
+ /* @__PURE__ */ jsx3(LogOut, { className: "h-4 w-4" }),
392
+ t("common.logout")
393
+ ]
394
+ }
395
+ )
396
+ ] })
397
+ ] })
398
+ ] })
399
+ ] })
400
+ ] });
401
+ };
402
+
403
+ // src/presentation/organisms/DashboardLayout.tsx
404
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
405
+ var DashboardLayout = ({
406
+ config,
407
+ user,
408
+ authLoading = false,
409
+ isAuthenticated = true,
410
+ notifications = [],
411
+ onLogout,
412
+ onMarkAllRead,
413
+ onDismissNotification,
414
+ loginRoute = "/login"
415
+ }) => {
416
+ const location = useLocation2();
417
+ const [collapsed, setCollapsed] = useState3(false);
418
+ const [mobileOpen, setMobileOpen] = useState3(false);
419
+ const [loading, setLoading] = useState3(true);
420
+ useEffect(() => {
421
+ setLoading(true);
422
+ const timer = setTimeout(() => setLoading(false), 300);
423
+ return () => clearTimeout(timer);
424
+ }, [location.pathname]);
425
+ useEffect(() => {
426
+ setMobileOpen(false);
427
+ }, [location.pathname]);
428
+ if (authLoading) return null;
429
+ if (!isAuthenticated) return /* @__PURE__ */ jsx4(Navigate, { to: loginRoute, replace: true });
430
+ const activeItem = config.sidebarGroups.flatMap((group) => group.items).find((i) => i.path === location.pathname);
431
+ const getTitle = () => {
432
+ if (!activeItem) return config.extraTitleMap?.[location.pathname] || "Dashboard";
433
+ return activeItem.label;
434
+ };
435
+ const currentTitle = getTitle();
436
+ return /* @__PURE__ */ jsxs4("div", { className: "flex h-screen w-full bg-background font-sans", children: [
437
+ /* @__PURE__ */ jsx4(
438
+ "aside",
439
+ {
440
+ className: `hidden md:flex flex-col shrink-0 border-r border-sidebar-border bg-sidebar transition-all duration-300 ${collapsed ? "w-16" : "w-60"}`,
441
+ children: /* @__PURE__ */ jsx4(
442
+ DashboardSidebar,
443
+ {
444
+ collapsed,
445
+ setCollapsed,
446
+ sidebarGroups: config.sidebarGroups,
447
+ brandName: config.brandName,
448
+ brandTagline: config.brandTagline,
449
+ user
450
+ }
451
+ )
452
+ }
453
+ ),
454
+ mobileOpen && /* @__PURE__ */ jsxs4("div", { className: "fixed inset-0 z-50 md:hidden", children: [
455
+ /* @__PURE__ */ jsx4("div", { className: "absolute inset-0 bg-background/80 backdrop-blur-sm", onClick: () => setMobileOpen(false) }),
456
+ /* @__PURE__ */ jsx4("aside", { className: "absolute left-0 top-0 h-full w-60 border-r border-sidebar-border bg-sidebar shadow-xl", children: /* @__PURE__ */ jsx4(
457
+ DashboardSidebar,
458
+ {
459
+ collapsed: false,
460
+ setCollapsed: () => setMobileOpen(false),
461
+ sidebarGroups: config.sidebarGroups,
462
+ brandName: config.brandName,
463
+ brandTagline: config.brandTagline,
464
+ user
465
+ }
466
+ ) })
467
+ ] }),
468
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-1 flex-col overflow-hidden min-w-0", children: [
469
+ /* @__PURE__ */ jsx4(
470
+ DashboardHeader,
471
+ {
472
+ collapsed,
473
+ setCollapsed,
474
+ setMobileOpen,
475
+ title: currentTitle,
476
+ user,
477
+ notifications,
478
+ onLogout,
479
+ onMarkAllRead,
480
+ onDismissNotification
481
+ }
482
+ ),
483
+ /* @__PURE__ */ jsx4("main", { className: "flex-1 overflow-y-auto p-4 md:p-8", children: loading ? /* @__PURE__ */ jsxs4("div", { className: "mx-auto w-full max-w-7xl space-y-6", children: [
484
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-8 w-1/3 rounded-xl" }),
485
+ /* @__PURE__ */ jsx4("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx4(Skeleton, { className: "h-28 rounded-2xl" }, i)) }),
486
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-64 rounded-[32px]" })
487
+ ] }) : /* @__PURE__ */ jsx4(Outlet, {}) })
488
+ ] })
489
+ ] });
490
+ };
491
+ var DashboardLayout_default = DashboardLayout;
492
+
493
+ // src/domain/theme/index.ts
494
+ var DEFAULT_DASHBOARD_THEME = {
495
+ primary: "hsl(222.2 47.4% 11.2%)",
496
+ secondary: "hsl(217.2 32.6% 17.5%)",
497
+ sidebarBackground: "hsl(222.2 47.4% 11.2%)",
498
+ sidebarForeground: "hsl(210 40% 98%)",
499
+ sidebarBorder: "hsl(217.2 32.6% 17.5%)",
500
+ headerBackground: "hsla(0, 0%, 100%, 0.8)",
501
+ background: "hsl(0 0% 100%)",
502
+ foreground: "hsl(222.2 84% 4.9%)",
503
+ border: "hsl(214.3 31.8% 91.4%)",
504
+ accent: "hsl(217.2 91.2% 59.8%)",
505
+ accentForeground: "hsl(0 0% 100%)",
506
+ destructive: "hsl(0 84.2% 60.2%)",
507
+ destructiveForeground: "hsl(0 0% 98%)",
508
+ muted: "hsl(210 40% 96.1%)",
509
+ mutedForeground: "hsl(215.4 16.3% 46.9%)",
510
+ card: "hsl(0 0% 100%)",
511
+ cardForeground: "hsl(222.2 84% 4.9%)",
512
+ popover: "hsl(0 0% 100%)",
513
+ popoverForeground: "hsl(222.2 84% 4.9%)",
514
+ radius: "0.5rem"
515
+ };
516
+ var DEFAULT_DASHBOARD_THEME_DARK = {
517
+ primary: "hsl(217.2 91.2% 59.8%)",
518
+ secondary: "hsl(217.2 32.6% 17.5%)",
519
+ sidebarBackground: "hsl(222.2 47.4% 11.2%)",
520
+ sidebarForeground: "hsl(210 40% 98%)",
521
+ sidebarBorder: "hsl(217.2 32.6% 17.5%)",
522
+ headerBackground: "hsla(222.2 47.4% 11.2%, 0.8)",
523
+ background: "hsl(222.2 84% 4.9%)",
524
+ foreground: "hsl(210 40% 98%)",
525
+ border: "hsl(217.2 32.6% 17.5%)",
526
+ accent: "hsl(217.2 91.2% 59.8%)",
527
+ accentForeground: "hsl(0 0% 100%)",
528
+ destructive: "hsl(0 62.8% 30.6%)",
529
+ destructiveForeground: "hsl(0 0% 98%)",
530
+ muted: "hsl(217.2 32.6% 17.5%)",
531
+ mutedForeground: "hsl(215 20.2% 65.1%)",
532
+ card: "hsl(222.2 84% 4.9%)",
533
+ cardForeground: "hsl(210 40% 98%)",
534
+ popover: "hsl(222.2 84% 4.9%)",
535
+ popoverForeground: "hsl(210 40% 98%)",
536
+ radius: "0.5rem"
537
+ };
538
+ var DASHBOARD_THEME_PRESETS = [
539
+ {
540
+ name: "default",
541
+ theme: DEFAULT_DASHBOARD_THEME,
542
+ dark: false
543
+ },
544
+ {
545
+ name: "default-dark",
546
+ theme: DEFAULT_DASHBOARD_THEME_DARK,
547
+ dark: true
548
+ },
549
+ {
550
+ name: "blue",
551
+ theme: {
552
+ ...DEFAULT_DASHBOARD_THEME,
553
+ primary: "hsl(221.2 83.2% 53.3%)",
554
+ accent: "hsl(221.2 83.2% 53.3%)"
555
+ },
556
+ dark: false
557
+ },
558
+ {
559
+ name: "blue-dark",
560
+ theme: {
561
+ ...DEFAULT_DASHBOARD_THEME_DARK,
562
+ primary: "hsl(221.2 83.2% 53.3%)",
563
+ accent: "hsl(221.2 83.2% 53.3%)"
564
+ },
565
+ dark: true
566
+ },
567
+ {
568
+ name: "purple",
569
+ theme: {
570
+ ...DEFAULT_DASHBOARD_THEME,
571
+ primary: "hsl(271.5 81.3% 55.9%)",
572
+ accent: "hsl(271.5 81.3% 55.9%)"
573
+ },
574
+ dark: false
575
+ },
576
+ {
577
+ name: "purple-dark",
578
+ theme: {
579
+ ...DEFAULT_DASHBOARD_THEME_DARK,
580
+ primary: "hsl(271.5 81.3% 55.9%)",
581
+ accent: "hsl(271.5 81.3% 55.9%)"
582
+ },
583
+ dark: true
584
+ },
585
+ {
586
+ name: "green",
587
+ theme: {
588
+ ...DEFAULT_DASHBOARD_THEME,
589
+ primary: "hsl(142.1 76.2% 36.3%)",
590
+ accent: "hsl(142.1 76.2% 36.3%)"
591
+ },
592
+ dark: false
593
+ },
594
+ {
595
+ name: "green-dark",
596
+ theme: {
597
+ ...DEFAULT_DASHBOARD_THEME_DARK,
598
+ primary: "hsl(142.1 76.2% 36.3%)",
599
+ accent: "hsl(142.1 76.2% 36.3%)"
600
+ },
601
+ dark: true
602
+ },
603
+ {
604
+ name: "orange",
605
+ theme: {
606
+ ...DEFAULT_DASHBOARD_THEME,
607
+ primary: "hsl(24.6 95% 53.1%)",
608
+ accent: "hsl(24.6 95% 53.1%)"
609
+ },
610
+ dark: false
611
+ },
612
+ {
613
+ name: "orange-dark",
614
+ theme: {
615
+ ...DEFAULT_DASHBOARD_THEME_DARK,
616
+ primary: "hsl(24.6 95% 53.1%)",
617
+ accent: "hsl(24.6 95% 53.1%)"
618
+ },
619
+ dark: true
620
+ }
621
+ ];
622
+ function applyDashboardTheme(theme) {
623
+ if (typeof document === "undefined") return;
624
+ const root = document.documentElement;
625
+ const cssVars = {
626
+ "--primary": theme.primary,
627
+ "--secondary": theme.secondary,
628
+ "--sidebar": theme.sidebarBackground,
629
+ "--sidebar-foreground": theme.sidebarForeground,
630
+ "--sidebar-border": theme.sidebarBorder,
631
+ "--background": theme.background,
632
+ "--foreground": theme.foreground,
633
+ "--border": theme.border,
634
+ "--accent": theme.accent,
635
+ "--accent-foreground": theme.accentForeground,
636
+ "--destructive": theme.destructive,
637
+ "--destructive-foreground": theme.destructiveForeground,
638
+ "--muted": theme.muted,
639
+ "--muted-foreground": theme.mutedForeground,
640
+ "--card": theme.card,
641
+ "--card-foreground": theme.cardForeground,
642
+ "--popover": theme.popover,
643
+ "--popover-foreground": theme.popoverForeground,
644
+ "--radius": theme.radius
645
+ };
646
+ Object.entries(cssVars).forEach(([key, value]) => {
647
+ if (value) {
648
+ root.style.setProperty(key, value);
649
+ }
650
+ });
651
+ }
652
+ function getDashboardThemePreset(name) {
653
+ return DASHBOARD_THEME_PRESETS.find((preset) => preset.name === name);
654
+ }
655
+ function mergeDashboardTheme(customTheme, dark = false) {
656
+ const baseTheme = dark ? DEFAULT_DASHBOARD_THEME_DARK : DEFAULT_DASHBOARD_THEME;
657
+ return {
658
+ ...baseTheme,
659
+ ...customTheme
660
+ };
661
+ }
662
+
663
+ // src/infrastructure/hooks/index.ts
664
+ import { useState as useState4, useCallback as useCallback2 } from "react";
665
+ function useNotifications(initialNotifications = []) {
666
+ const [notifications, setNotifications] = useState4(initialNotifications);
667
+ const markAllRead = useCallback2(() => {
668
+ setNotifications(
669
+ (prev) => prev.map((n) => ({ ...n, read: true }))
670
+ );
671
+ }, []);
672
+ const dismiss = useCallback2((id) => {
673
+ setNotifications((prev) => prev.filter((n) => n.id !== id));
674
+ }, []);
675
+ const add = useCallback2((notification) => {
676
+ const newNotification = {
677
+ ...notification,
678
+ id: Math.random().toString(36).substring(7),
679
+ read: false,
680
+ createdAt: /* @__PURE__ */ new Date()
681
+ };
682
+ setNotifications((prev) => [newNotification, ...prev]);
683
+ }, []);
684
+ return {
685
+ notifications,
686
+ markAllRead,
687
+ dismiss,
688
+ add
689
+ };
690
+ }
691
+ function useSidebar(initialCollapsed = false) {
692
+ const [collapsed, setCollapsed] = useState4(initialCollapsed);
693
+ const [mobileOpen, setMobileOpen] = useState4(false);
694
+ const toggle = useCallback2(() => {
695
+ setCollapsed((prev) => !prev);
696
+ }, []);
697
+ const openMobile = useCallback2(() => {
698
+ setMobileOpen(true);
699
+ }, []);
700
+ const closeMobile = useCallback2(() => {
701
+ setMobileOpen(false);
702
+ }, []);
703
+ return {
704
+ collapsed,
705
+ setCollapsed,
706
+ toggle,
707
+ mobileOpen,
708
+ setMobileOpen,
709
+ openMobile,
710
+ closeMobile
711
+ };
712
+ }
713
+
714
+ // src/infrastructure/utils/index.ts
715
+ function formatNotificationTime(createdAt, t) {
716
+ const date = new Date(createdAt);
717
+ const secs = Math.floor((Date.now() - date.getTime()) / 1e3);
718
+ if (secs < 60) return t("dashboard.activityFeed.times.justNow");
719
+ if (secs < 3600) return t("dashboard.activityFeed.times.minutesAgo", { val: Math.floor(secs / 60) });
720
+ if (secs < 86400) return t("dashboard.activityFeed.times.hoursAgo", { val: Math.floor(secs / 3600) });
721
+ return t("dashboard.activityFeed.times.daysAgo", { val: Math.floor(secs / 86400) });
722
+ }
723
+ function isPathActive(currentPath, targetPath) {
724
+ return currentPath === targetPath;
725
+ }
726
+ function getPageTitle(pathname, sidebarGroups, extraTitleMap) {
727
+ for (const group of sidebarGroups) {
728
+ const item = group.items.find((i) => i.path === pathname);
729
+ if (item) return item.label;
730
+ }
731
+ if (extraTitleMap?.[pathname]) {
732
+ return extraTitleMap[pathname];
733
+ }
734
+ return null;
735
+ }
736
+ function filterSidebarItems(items, user) {
737
+ return items.filter((item) => {
738
+ if (item.enabled === false) return false;
739
+ if (!item.requiredApp) return true;
740
+ if (item.requiredApp === "mobile") return user?.hasMobileApp ?? false;
741
+ if (item.requiredApp === "web") return user?.hasWebApp ?? false;
742
+ return true;
743
+ });
744
+ }
745
+ function generateNotificationId() {
746
+ return `${Date.now()}-${Math.random().toString(36).substring(7)}`;
747
+ }
748
+ export {
749
+ BrandLogo,
750
+ DASHBOARD_THEME_PRESETS,
751
+ DEFAULT_DASHBOARD_THEME,
752
+ DEFAULT_DASHBOARD_THEME_DARK,
753
+ DashboardHeader,
754
+ DashboardLayout,
755
+ DashboardLayout_default as DashboardLayoutDefault,
756
+ DashboardSidebar,
757
+ applyDashboardTheme,
758
+ filterSidebarItems,
759
+ formatNotificationTime,
760
+ generateNotificationId,
761
+ getDashboardThemePreset,
762
+ getPageTitle,
763
+ isPathActive,
764
+ mergeDashboardTheme,
765
+ useNotifications,
766
+ useSidebar
767
+ };
768
+ //# sourceMappingURL=index.js.map