@umituz/web-dashboard 1.0.4 → 1.0.7
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.d.ts +299 -8
- package/dist/index.js +10 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/domain/theme/index.d.ts +0 -45
- package/dist/domain/theme/index.js +0 -180
- package/dist/domain/theme/index.js.map +0 -1
- package/dist/domain/types/index.d.ts +0 -171
- package/dist/domain/types/index.js +0 -2
- package/dist/domain/types/index.js.map +0 -1
- package/dist/infrastructure/hooks/index.d.ts +0 -36
- package/dist/infrastructure/hooks/index.js +0 -57
- package/dist/infrastructure/hooks/index.js.map +0 -1
- package/dist/infrastructure/utils/index.d.ts +0 -57
- package/dist/infrastructure/utils/index.js +0 -44
- package/dist/infrastructure/utils/index.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,174 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
import { LucideIcon } from 'lucide-react';
|
|
3
|
+
import * as react from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Dashboard Types
|
|
7
|
+
*
|
|
8
|
+
* Core type definitions for the dashboard layout system
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Single sidebar menu item
|
|
13
|
+
*/
|
|
14
|
+
interface SidebarItem {
|
|
15
|
+
/** Display label (can be i18n key) */
|
|
16
|
+
label: string;
|
|
17
|
+
/** Icon from lucide-react */
|
|
18
|
+
icon: LucideIcon;
|
|
19
|
+
/** Route path */
|
|
20
|
+
path: string;
|
|
21
|
+
/** Filter by app type (optional) */
|
|
22
|
+
requiredApp?: 'mobile' | 'web';
|
|
23
|
+
/** Enable/disable this item (default: true) */
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Group of sidebar items with a title
|
|
28
|
+
*/
|
|
29
|
+
interface SidebarGroup {
|
|
30
|
+
/** Group title (can be i18n key) */
|
|
31
|
+
title: string;
|
|
32
|
+
/** Items in this group */
|
|
33
|
+
items: SidebarItem[];
|
|
34
|
+
/** Optional: Route to title mapping for page headers */
|
|
35
|
+
titleMap?: Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Dashboard header props
|
|
39
|
+
*/
|
|
40
|
+
interface DashboardHeaderProps {
|
|
41
|
+
/** Whether sidebar is collapsed */
|
|
42
|
+
collapsed: boolean;
|
|
43
|
+
/** Toggle sidebar collapsed state */
|
|
44
|
+
setCollapsed: (collapsed: boolean) => void;
|
|
45
|
+
/** Toggle mobile menu open state */
|
|
46
|
+
setMobileOpen: (open: boolean) => void;
|
|
47
|
+
/** Current page title */
|
|
48
|
+
title: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Dashboard sidebar props
|
|
52
|
+
*/
|
|
53
|
+
interface DashboardSidebarProps {
|
|
54
|
+
/** Whether sidebar is collapsed */
|
|
55
|
+
collapsed: boolean;
|
|
56
|
+
/** Toggle sidebar collapsed state */
|
|
57
|
+
setCollapsed: (collapsed: boolean) => void;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Dashboard layout configuration
|
|
61
|
+
*/
|
|
62
|
+
interface DashboardLayoutConfig {
|
|
63
|
+
/** Sidebar groups */
|
|
64
|
+
sidebarGroups: SidebarGroup[];
|
|
65
|
+
/** Extra title mappings for routes */
|
|
66
|
+
extraTitleMap?: Record<string, string>;
|
|
67
|
+
/** Brand name */
|
|
68
|
+
brandName?: string;
|
|
69
|
+
/** Brand tagline */
|
|
70
|
+
brandTagline?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Dashboard theme configuration
|
|
74
|
+
* Extends CSS variables for customization
|
|
75
|
+
*/
|
|
76
|
+
interface DashboardTheme {
|
|
77
|
+
/** Primary color (CSS variable compatible) */
|
|
78
|
+
primary?: string;
|
|
79
|
+
/** Secondary color */
|
|
80
|
+
secondary?: string;
|
|
81
|
+
/** Sidebar background */
|
|
82
|
+
sidebarBackground?: string;
|
|
83
|
+
/** Sidebar foreground */
|
|
84
|
+
sidebarForeground?: string;
|
|
85
|
+
/** Sidebar border */
|
|
86
|
+
sidebarBorder?: string;
|
|
87
|
+
/** Header background */
|
|
88
|
+
headerBackground?: string;
|
|
89
|
+
/** Background color */
|
|
90
|
+
background?: string;
|
|
91
|
+
/** Foreground color */
|
|
92
|
+
foreground?: string;
|
|
93
|
+
/** Border color */
|
|
94
|
+
border?: string;
|
|
95
|
+
/** Accent color */
|
|
96
|
+
accent?: string;
|
|
97
|
+
/** Accent foreground */
|
|
98
|
+
accentForeground?: string;
|
|
99
|
+
/** Destructive color */
|
|
100
|
+
destructive?: string;
|
|
101
|
+
/** Destructive foreground */
|
|
102
|
+
destructiveForeground?: string;
|
|
103
|
+
/** Muted background */
|
|
104
|
+
muted?: string;
|
|
105
|
+
/** Muted foreground */
|
|
106
|
+
mutedForeground?: string;
|
|
107
|
+
/** Card background */
|
|
108
|
+
card?: string;
|
|
109
|
+
/** Card foreground */
|
|
110
|
+
cardForeground?: string;
|
|
111
|
+
/** Popover background */
|
|
112
|
+
popover?: string;
|
|
113
|
+
/** Popover foreground */
|
|
114
|
+
popoverForeground?: string;
|
|
115
|
+
/** Radius (border-radius) */
|
|
116
|
+
radius?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Theme preset for quick setup
|
|
120
|
+
*/
|
|
121
|
+
interface DashboardThemePreset {
|
|
122
|
+
/** Preset name */
|
|
123
|
+
name: string;
|
|
124
|
+
/** Theme configuration */
|
|
125
|
+
theme: DashboardTheme;
|
|
126
|
+
/** Whether this is a dark theme */
|
|
127
|
+
dark?: boolean;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Notification item
|
|
131
|
+
*/
|
|
132
|
+
interface DashboardNotification {
|
|
133
|
+
/** Unique ID */
|
|
134
|
+
id: string;
|
|
135
|
+
/** Notification text */
|
|
136
|
+
text: string;
|
|
137
|
+
/** Whether notification is read */
|
|
138
|
+
read: boolean;
|
|
139
|
+
/** Creation timestamp */
|
|
140
|
+
createdAt: Date | string | number;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* User profile info for header
|
|
144
|
+
*/
|
|
145
|
+
interface DashboardUser {
|
|
146
|
+
/** User ID */
|
|
147
|
+
id: string;
|
|
148
|
+
/** Display name */
|
|
149
|
+
name?: string;
|
|
150
|
+
/** Email address */
|
|
151
|
+
email?: string;
|
|
152
|
+
/** Avatar URL */
|
|
153
|
+
avatar?: string;
|
|
154
|
+
/** Whether user has mobile app access */
|
|
155
|
+
hasMobileApp?: boolean;
|
|
156
|
+
/** Whether user has web app access */
|
|
157
|
+
hasWebApp?: boolean;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Navigation item for user menu
|
|
161
|
+
*/
|
|
162
|
+
interface UserNavMenuItem {
|
|
163
|
+
/** Display label */
|
|
164
|
+
label: string;
|
|
165
|
+
/** Icon component */
|
|
166
|
+
icon: React.ComponentType<{
|
|
167
|
+
className?: string;
|
|
168
|
+
}>;
|
|
169
|
+
/** Route path */
|
|
170
|
+
path: string;
|
|
171
|
+
}
|
|
9
172
|
|
|
10
173
|
interface DashboardLayoutProps {
|
|
11
174
|
/** Layout configuration */
|
|
@@ -109,4 +272,132 @@ interface BrandLogoProps {
|
|
|
109
272
|
*/
|
|
110
273
|
declare const BrandLogo: ({ className, size }: BrandLogoProps) => react_jsx_runtime.JSX.Element;
|
|
111
274
|
|
|
112
|
-
|
|
275
|
+
/**
|
|
276
|
+
* Dashboard Theme System
|
|
277
|
+
*
|
|
278
|
+
* Provides theme configuration and presets for the dashboard layout.
|
|
279
|
+
* Themes are applied via CSS variables, allowing runtime customization.
|
|
280
|
+
*/
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Default dashboard theme (light mode)
|
|
284
|
+
*/
|
|
285
|
+
declare const DEFAULT_DASHBOARD_THEME: DashboardTheme;
|
|
286
|
+
/**
|
|
287
|
+
* Default dashboard theme (dark mode)
|
|
288
|
+
*/
|
|
289
|
+
declare const DEFAULT_DASHBOARD_THEME_DARK: DashboardTheme;
|
|
290
|
+
/**
|
|
291
|
+
* Available theme presets
|
|
292
|
+
*/
|
|
293
|
+
declare const DASHBOARD_THEME_PRESETS: DashboardThemePreset[];
|
|
294
|
+
/**
|
|
295
|
+
* Apply theme to document root via CSS variables
|
|
296
|
+
*
|
|
297
|
+
* @param theme - Theme configuration to apply
|
|
298
|
+
*/
|
|
299
|
+
declare function applyDashboardTheme(theme: DashboardTheme): void;
|
|
300
|
+
/**
|
|
301
|
+
* Get theme preset by name
|
|
302
|
+
*
|
|
303
|
+
* @param name - Preset name
|
|
304
|
+
* @returns Theme preset or undefined
|
|
305
|
+
*/
|
|
306
|
+
declare function getDashboardThemePreset(name: string): DashboardThemePreset | undefined;
|
|
307
|
+
/**
|
|
308
|
+
* Merge custom theme with default theme
|
|
309
|
+
*
|
|
310
|
+
* @param customTheme - Custom theme configuration
|
|
311
|
+
* @param dark - Whether to use dark mode base
|
|
312
|
+
* @returns Merged theme configuration
|
|
313
|
+
*/
|
|
314
|
+
declare function mergeDashboardTheme(customTheme: Partial<DashboardTheme>, dark?: boolean): DashboardTheme;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Use Notifications Hook
|
|
318
|
+
*
|
|
319
|
+
* Manages notification state and actions
|
|
320
|
+
*
|
|
321
|
+
* @param initialNotifications - Initial notification list
|
|
322
|
+
* @returns Notification state and actions
|
|
323
|
+
*/
|
|
324
|
+
declare function useNotifications(initialNotifications?: DashboardNotification[]): {
|
|
325
|
+
notifications: DashboardNotification[];
|
|
326
|
+
markAllRead: () => void;
|
|
327
|
+
dismiss: (id: string) => void;
|
|
328
|
+
add: (notification: Omit<DashboardNotification, "id">) => void;
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* Use Sidebar Hook
|
|
332
|
+
*
|
|
333
|
+
* Manages sidebar state
|
|
334
|
+
*
|
|
335
|
+
* @returns Sidebar state and actions
|
|
336
|
+
*/
|
|
337
|
+
declare function useSidebar(initialCollapsed?: boolean): {
|
|
338
|
+
collapsed: boolean;
|
|
339
|
+
setCollapsed: react.Dispatch<react.SetStateAction<boolean>>;
|
|
340
|
+
toggle: () => void;
|
|
341
|
+
mobileOpen: boolean;
|
|
342
|
+
setMobileOpen: react.Dispatch<react.SetStateAction<boolean>>;
|
|
343
|
+
openMobile: () => void;
|
|
344
|
+
closeMobile: () => void;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Dashboard Utilities
|
|
349
|
+
*
|
|
350
|
+
* Utility functions for dashboard operations
|
|
351
|
+
*/
|
|
352
|
+
/**
|
|
353
|
+
* Format notification timestamp to relative time
|
|
354
|
+
*
|
|
355
|
+
* @param createdAt - Notification creation timestamp
|
|
356
|
+
* @param t - i18n translation function
|
|
357
|
+
* @returns Formatted time string
|
|
358
|
+
*/
|
|
359
|
+
declare function formatNotificationTime(createdAt: Date | string | number, t: (key: string, params?: Record<string, unknown>) => string): string;
|
|
360
|
+
/**
|
|
361
|
+
* Check if a path is active
|
|
362
|
+
*
|
|
363
|
+
* @param currentPath - Current location pathname
|
|
364
|
+
* @param targetPath - Target path to check
|
|
365
|
+
* @returns True if paths match
|
|
366
|
+
*/
|
|
367
|
+
declare function isPathActive(currentPath: string, targetPath: string): boolean;
|
|
368
|
+
/**
|
|
369
|
+
* Get page title from sidebar configuration
|
|
370
|
+
*
|
|
371
|
+
* @param pathname - Current pathname
|
|
372
|
+
* @param sidebarGroups - Sidebar groups configuration
|
|
373
|
+
* @param extraTitleMap - Extra title mappings
|
|
374
|
+
* @returns Page title or null
|
|
375
|
+
*/
|
|
376
|
+
declare function getPageTitle(pathname: string, sidebarGroups: Array<{
|
|
377
|
+
items: Array<{
|
|
378
|
+
path: string;
|
|
379
|
+
label: string;
|
|
380
|
+
}>;
|
|
381
|
+
}>, extraTitleMap?: Record<string, string>): string | null;
|
|
382
|
+
/**
|
|
383
|
+
* Filter sidebar items by app type and enabled status
|
|
384
|
+
*
|
|
385
|
+
* @param items - Sidebar items to filter
|
|
386
|
+
* @param user - Current user object
|
|
387
|
+
* @returns Filtered items
|
|
388
|
+
*/
|
|
389
|
+
declare function filterSidebarItems<T extends {
|
|
390
|
+
enabled?: boolean;
|
|
391
|
+
requiredApp?: "mobile" | "web";
|
|
392
|
+
}>(items: T[], user?: {
|
|
393
|
+
hasMobileApp?: boolean;
|
|
394
|
+
hasWebApp?: boolean;
|
|
395
|
+
}): T[];
|
|
396
|
+
/**
|
|
397
|
+
* Generate notification ID
|
|
398
|
+
*
|
|
399
|
+
* @returns Unique notification ID
|
|
400
|
+
*/
|
|
401
|
+
declare function generateNotificationId(): string;
|
|
402
|
+
|
|
403
|
+
export { BrandLogo, DASHBOARD_THEME_PRESETS, DEFAULT_DASHBOARD_THEME, DEFAULT_DASHBOARD_THEME_DARK, DashboardHeader, type DashboardHeaderProps, DashboardLayout, type DashboardLayoutConfig, type DashboardNotification, DashboardSidebar, type DashboardSidebarProps, type DashboardTheme, type DashboardThemePreset, type DashboardUser, type SidebarGroup, type SidebarItem, type UserNavMenuItem, applyDashboardTheme, filterSidebarItems, formatNotificationTime, generateNotificationId, getDashboardThemePreset, getPageTitle, isPathActive, mergeDashboardTheme, useNotifications, useSidebar };
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
// src/
|
|
3
|
+
// src/domains/layouts/components/DashboardLayout.tsx
|
|
4
4
|
import { useState as useState3, useEffect } from "react";
|
|
5
5
|
import { useLocation as useLocation2, Outlet, Navigate } from "react-router-dom";
|
|
6
6
|
import { Skeleton } from "@umituz/web-design-system/atoms";
|
|
7
7
|
|
|
8
|
-
// src/
|
|
8
|
+
// src/domains/layouts/components/DashboardSidebar.tsx
|
|
9
9
|
import { useState } from "react";
|
|
10
10
|
import { Link, useLocation } from "react-router-dom";
|
|
11
11
|
import { useTranslation } from "react-i18next";
|
|
12
12
|
import { Button } from "@umituz/web-design-system/atoms";
|
|
13
13
|
|
|
14
|
-
// src/
|
|
14
|
+
// src/domains/layouts/components/BrandLogo.tsx
|
|
15
15
|
import { cn } from "@umituz/web-design-system/utils";
|
|
16
16
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
17
|
var BrandLogo = ({ className, size = 32 }) => {
|
|
@@ -94,10 +94,10 @@ var BrandLogo = ({ className, size = 32 }) => {
|
|
|
94
94
|
);
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
-
// src/
|
|
97
|
+
// src/domains/layouts/components/DashboardSidebar.tsx
|
|
98
98
|
import { PenTool, Menu, ChevronLeft, ChevronDown, ChevronRight } from "lucide-react";
|
|
99
99
|
|
|
100
|
-
// src/
|
|
100
|
+
// src/domains/layouts/utils/dashboard.ts
|
|
101
101
|
function formatNotificationTime(createdAt, t) {
|
|
102
102
|
const date = new Date(createdAt);
|
|
103
103
|
const secs = Math.floor((Date.now() - date.getTime()) / 1e3);
|
|
@@ -132,7 +132,7 @@ function generateNotificationId() {
|
|
|
132
132
|
return crypto.randomUUID();
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
// src/
|
|
135
|
+
// src/domains/layouts/components/DashboardSidebar.tsx
|
|
136
136
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
137
137
|
var DashboardSidebar = ({
|
|
138
138
|
collapsed,
|
|
@@ -213,7 +213,7 @@ var DashboardSidebar = ({
|
|
|
213
213
|
] });
|
|
214
214
|
};
|
|
215
215
|
|
|
216
|
-
// src/
|
|
216
|
+
// src/domains/layouts/components/DashboardHeader.tsx
|
|
217
217
|
import React, { useState as useState2 } from "react";
|
|
218
218
|
import {
|
|
219
219
|
Bell,
|
|
@@ -421,7 +421,7 @@ var DashboardHeader = ({
|
|
|
421
421
|
] });
|
|
422
422
|
};
|
|
423
423
|
|
|
424
|
-
// src/
|
|
424
|
+
// src/domains/layouts/components/DashboardLayout.tsx
|
|
425
425
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
426
426
|
var DashboardLayout = ({
|
|
427
427
|
config,
|
|
@@ -510,7 +510,7 @@ var DashboardLayout = ({
|
|
|
510
510
|
] });
|
|
511
511
|
};
|
|
512
512
|
|
|
513
|
-
// src/
|
|
513
|
+
// src/domains/layouts/theme/index.ts
|
|
514
514
|
var DEFAULT_DASHBOARD_THEME = {
|
|
515
515
|
primary: "hsl(222.2 47.4% 11.2%)",
|
|
516
516
|
secondary: "hsl(217.2 32.6% 17.5%)",
|
|
@@ -680,7 +680,7 @@ function mergeDashboardTheme(customTheme, dark = false) {
|
|
|
680
680
|
};
|
|
681
681
|
}
|
|
682
682
|
|
|
683
|
-
// src/
|
|
683
|
+
// src/domains/layouts/hooks/dashboard.ts
|
|
684
684
|
import { useState as useState4, useCallback } from "react";
|
|
685
685
|
function useNotifications(initialNotifications = []) {
|
|
686
686
|
const [notifications, setNotifications] = useState4(initialNotifications);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/presentation/organisms/DashboardLayout.tsx","../src/presentation/molecules/DashboardSidebar.tsx","../src/presentation/molecules/BrandLogo.tsx","../src/infrastructure/utils/index.ts","../src/presentation/organisms/DashboardHeader.tsx","../src/domain/theme/index.ts","../src/infrastructure/hooks/index.ts"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useLocation, Outlet, Navigate } from \"react-router-dom\";\nimport { Skeleton } from \"@umituz/web-design-system/atoms\";\nimport { DashboardSidebar } from \"../molecules/DashboardSidebar\";\nimport { DashboardHeader } from \"./DashboardHeader\";\nimport type { DashboardLayoutConfig, DashboardUser, DashboardNotification } from \"../../domain/types\";\n\ninterface DashboardLayoutProps {\n /** Layout configuration */\n config: DashboardLayoutConfig;\n /** Auth user */\n user?: DashboardUser;\n /** Auth loading state */\n authLoading?: boolean;\n /** Authenticated state */\n isAuthenticated?: boolean;\n /** Notifications */\n notifications?: DashboardNotification[];\n /** Logout function */\n onLogout?: () => Promise<void>;\n /** Mark all as read function */\n onMarkAllRead?: () => void;\n /** Dismiss notification function */\n onDismissNotification?: (id: string) => void;\n /** Login route for redirect */\n loginRoute?: string;\n}\n\n/**\n * Dashboard Layout Component\n *\n * Main layout wrapper for dashboard pages.\n * Provides sidebar, header, and content area with responsive design.\n *\n * Features:\n * - Collapsible sidebar\n * - Mobile menu overlay\n * - Breadcrumb page titles\n * - Loading skeletons\n * - Auth protection\n *\n * @param props - Dashboard layout props\n */\nexport const DashboardLayout = ({\n config,\n user,\n authLoading = false,\n isAuthenticated = true,\n notifications = [],\n onLogout,\n onMarkAllRead,\n onDismissNotification,\n loginRoute = \"/login\",\n}: DashboardLayoutProps) => {\n const location = useLocation();\n const [collapsed, setCollapsed] = useState(false);\n const [mobileOpen, setMobileOpen] = useState(false);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n setLoading(true);\n const timer = setTimeout(() => setLoading(false), 300);\n return () => clearTimeout(timer);\n }, [location.pathname]);\n\n useEffect(() => {\n setMobileOpen(false);\n }, [location.pathname]);\n\n if (authLoading) return null;\n if (!isAuthenticated) return <Navigate to={loginRoute} replace />;\n\n const activeItem = config.sidebarGroups\n .flatMap((group) => group.items)\n .find((i) => i.path === location.pathname);\n\n const getTitle = () => {\n if (!activeItem) return config.extraTitleMap?.[location.pathname] || \"Dashboard\";\n return activeItem.label; // Note: In real app, this would be translated\n };\n\n const currentTitle = getTitle();\n\n return (\n <div className=\"flex h-screen w-full bg-background font-sans\">\n {/* Desktop Sidebar */}\n <aside\n className={`hidden md:flex flex-col shrink-0 border-r border-sidebar-border bg-sidebar transition-all duration-300 ${\n collapsed ? \"w-16\" : \"w-60\"\n }`}\n >\n <DashboardSidebar\n collapsed={collapsed}\n setCollapsed={setCollapsed}\n sidebarGroups={config.sidebarGroups}\n brandName={config.brandName}\n brandTagline={config.brandTagline}\n user={user}\n />\n </aside>\n\n {/* Mobile Menu Overlay */}\n {mobileOpen && (\n <div className=\"fixed inset-0 z-50 md:hidden\">\n <div className=\"absolute inset-0 bg-background/80 backdrop-blur-sm\" onClick={() => setMobileOpen(false)} />\n <aside className=\"absolute left-0 top-0 h-full w-60 border-r border-sidebar-border bg-sidebar shadow-xl\">\n <DashboardSidebar\n collapsed={false}\n setCollapsed={() => setMobileOpen(false)}\n sidebarGroups={config.sidebarGroups}\n brandName={config.brandName}\n brandTagline={config.brandTagline}\n user={user}\n />\n </aside>\n </div>\n )}\n\n {/* Main Content Area */}\n <div className=\"flex flex-1 flex-col overflow-hidden min-w-0\">\n <DashboardHeader\n collapsed={collapsed}\n setCollapsed={setCollapsed}\n setMobileOpen={setMobileOpen}\n title={currentTitle}\n user={user}\n notifications={notifications}\n onLogout={onLogout}\n onMarkAllRead={onMarkAllRead}\n onDismissNotification={onDismissNotification}\n />\n\n <main className=\"flex-1 overflow-y-auto p-4 md:p-8\">\n {loading ? (\n <div className=\"mx-auto w-full max-w-7xl space-y-6\">\n <Skeleton className=\"h-8 w-1/3 rounded-xl\" />\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <Skeleton key={i} className=\"h-28 rounded-2xl\" />\n ))}\n </div>\n <Skeleton className=\"h-64 rounded-[32px]\" />\n </div>\n ) : (\n <Outlet />\n )}\n </main>\n </div>\n </div>\n );\n};\n\nexport default DashboardLayout;\n","import { useState } from \"react\";\nimport { Link, useLocation } from \"react-router-dom\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { Button } from \"@umituz/web-design-system/atoms\";\nimport { BrandLogo } from \"./BrandLogo\";\nimport { PenTool, Menu, ChevronLeft, ChevronDown, ChevronRight } from \"lucide-react\";\nimport type { DashboardUser, SidebarGroup } from \"../../domain/types\";\nimport type { DashboardSidebarProps } from \"../../domain/types\";\nimport { filterSidebarItems } from \"../../infrastructure/utils\";\n\ninterface DashboardSidebarPropsExtended extends DashboardSidebarProps {\n /** Sidebar groups configuration */\n sidebarGroups: SidebarGroup[];\n /** Brand name */\n brandName?: string;\n /** Brand tagline */\n brandTagline?: string;\n /** Create post route */\n createPostRoute?: string;\n /** Auth user */\n user?: DashboardUser;\n}\n\n/**\n * Dashboard Sidebar Component\n *\n * Displays collapsible sidebar with navigation menu items.\n * Supports app-based filtering (mobile/web) and collapsible groups.\n *\n * @param props - Dashboard sidebar props\n */\nexport const DashboardSidebar = ({\n collapsed,\n setCollapsed,\n sidebarGroups,\n brandName = \"App\",\n brandTagline = \"grow smarter\",\n createPostRoute = \"/dashboard/create\",\n user,\n}: DashboardSidebarPropsExtended) => {\n const location = useLocation();\n const { t } = useTranslation();\n const [collapsedGroups, setCollapsedGroups] = useState<Record<string, boolean>>({});\n\n const toggleGroup = (title: string) => {\n setCollapsedGroups(prev => ({\n ...prev,\n [title]: !prev[title]\n }));\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Brand Section */}\n <div className=\"flex h-16 items-center gap-3 border-b border-sidebar-border px-4 transition-all duration-300\">\n <BrandLogo size={32} />\n {!collapsed && (\n <div className=\"flex flex-col -gap-1\">\n <span className=\"text-2xl font-black text-sidebar-foreground tracking-tighter leading-none\">{brandName}</span>\n <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\">\n {brandTagline}\n </span>\n </div>\n )}\n </div>\n\n {/* Create Button */}\n <div className=\"px-3 py-4 border-b border-sidebar-border/50\">\n <Link to={createPostRoute}>\n <Button\n variant=\"default\"\n className={`w-full gap-3 shadow-glow transition-all active:scale-95 group overflow-hidden rounded-xl ${\n collapsed ? \"px-0 justify-center h-10 w-10 mx-auto\" : \"justify-start px-4 h-11\"\n }`}\n title={collapsed ? t('sidebar.createPost') : undefined}\n >\n <PenTool className={`shrink-0 transition-transform duration-300 ${collapsed ? \"h-5 w-5\" : \"h-4 w-4 group-hover:scale-110\"}`} />\n {!collapsed && <span className=\"font-bold tracking-tight\">{t('sidebar.createPost')}</span>}\n </Button>\n </Link>\n </div>\n\n {/* Navigation */}\n <nav className=\"flex-1 overflow-y-auto px-2 py-3 scrollbar-hide\">\n <div className=\"space-y-6\">\n {sidebarGroups.map((group) => {\n const filteredItems = filterSidebarItems(group.items, user);\n\n if (filteredItems.length === 0) return null;\n\n const isGroupCollapsed = collapsedGroups[group.title];\n\n return (\n <div key={group.title} className=\"space-y-1\">\n {!collapsed && (\n <button\n onClick={() => toggleGroup(group.title)}\n className=\"w-full flex items-center justify-between px-3 mb-2 group/header\"\n >\n <span className=\"text-[10px] font-bold uppercase tracking-widest text-sidebar-foreground/40 group-hover/header:text-sidebar-foreground/70 transition-colors\">\n {group.title === \"sidebar.ai\" ? `${brandName} AI` : t(group.title)}\n </span>\n {isGroupCollapsed ? (\n <ChevronRight className=\"h-3 w-3 text-sidebar-foreground/30 flex-shrink-0 group-hover/header:text-sidebar-foreground/50 transition-colors\" />\n ) : (\n <ChevronDown className=\"h-3 w-3 text-sidebar-foreground/30 flex-shrink-0 group-hover/header:text-sidebar-foreground/50 transition-colors\" />\n )}\n </button>\n )}\n\n {(!isGroupCollapsed || collapsed) && filteredItems.map((item) => {\n const active = location.pathname === item.path;\n return (\n <Link\n key={item.path}\n to={item.path}\n className={`flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-all duration-200 ${\n active\n ? \"bg-sidebar-accent text-sidebar-accent-foreground shadow-sm\"\n : \"text-sidebar-foreground/70 hover:bg-sidebar-accent/40 hover:text-sidebar-foreground\"\n } ${collapsed ? \"justify-center\" : \"\"}`}\n title={collapsed ? t(item.label) : undefined}\n >\n <item.icon className={`h-4 w-4 shrink-0 transition-transform ${active && \"scale-110\"}`} />\n {!collapsed && <span>{t(item.label)}</span>}\n </Link>\n );\n })}\n </div>\n );\n })}\n </div>\n </nav>\n\n {/* Collapse Toggle */}\n <div className=\"border-t border-sidebar-border p-3\">\n <div className={`flex items-center ${collapsed ? \"justify-center\" : \"justify-between\"}`}>\n {!collapsed && (\n <p className=\"text-[10px] uppercase tracking-wider text-sidebar-foreground/40 font-bold px-2\">\n {t('sidebar.system')}\n </p>\n )}\n <Button variant=\"ghost\" size=\"icon\" onClick={() => setCollapsed(!collapsed)} className=\"text-sidebar-foreground/70\">\n {collapsed ? <Menu className=\"h-4 w-4\" /> : <ChevronLeft className=\"h-4 w-4\" />}\n </Button>\n </div>\n </div>\n </div>\n );\n};\n","import React from \"react\";\nimport { cn } from \"@umituz/web-design-system/utils\";\n\ninterface BrandLogoProps {\n className?: string;\n size?: number;\n}\n\n/**\n * BrandLogo Component\n *\n * Displays the application brand logo as an SVG.\n * Supports custom sizing and styling through className.\n *\n * @param className - Optional CSS classes for styling\n * @param size - Width and height in pixels (default: 32)\n */\nexport const BrandLogo = ({ className, size = 32 }: BrandLogoProps) => {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 100 100\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={cn(\"shrink-0\", className)}\n >\n {/* Solid Foundation / Platform */}\n <rect\n x=\"15\"\n y=\"65\"\n width=\"70\"\n height=\"15\"\n rx=\"4\"\n fill=\"hsl(var(--primary))\"\n />\n\n {/* Assembly Paths / Structure */}\n <rect\n x=\"25\"\n y=\"25\"\n width=\"12\"\n height=\"40\"\n rx=\"2\"\n fill=\"hsl(var(--primary))\"\n />\n <rect\n x=\"44\"\n y=\"35\"\n width=\"12\"\n height=\"30\"\n rx=\"2\"\n fill=\"hsl(var(--primary))\"\n />\n <rect\n x=\"63\"\n y=\"20\"\n width=\"12\"\n height=\"45\"\n rx=\"2\"\n fill=\"hsl(var(--primary))\"\n />\n\n {/* Modern Accent - The 'Assembly' Bridge */}\n <rect\n x=\"20\"\n y=\"45\"\n width=\"60\"\n height=\"10\"\n rx=\"2\"\n fill=\"hsl(var(--secondary))\"\n />\n\n {/* Connection Point / Beacon */}\n <circle\n cx=\"50\"\n cy=\"20\"\n r=\"5\"\n fill=\"hsl(var(--secondary))\"\n />\n </svg>\n );\n};\n","/**\n * Dashboard Utilities\n *\n * Utility functions for dashboard operations\n */\n\n/**\n * Format notification timestamp to relative time\n *\n * @param createdAt - Notification creation timestamp\n * @param t - i18n translation function\n * @returns Formatted time string\n */\nexport function formatNotificationTime(\n createdAt: Date | string | number,\n t: (key: string, params?: Record<string, unknown>) => string\n): string {\n const date = new Date(createdAt);\n const secs = Math.floor((Date.now() - date.getTime()) / 1000);\n\n if (secs < 60) return t(\"dashboard.activityFeed.times.justNow\");\n if (secs < 3600) return t(\"dashboard.activityFeed.times.minutesAgo\", { val: Math.floor(secs / 60) });\n if (secs < 86400) return t(\"dashboard.activityFeed.times.hoursAgo\", { val: Math.floor(secs / 3600) });\n return t(\"dashboard.activityFeed.times.daysAgo\", { val: Math.floor(secs / 86400) });\n}\n\n/**\n * Check if a path is active\n *\n * @param currentPath - Current location pathname\n * @param targetPath - Target path to check\n * @returns True if paths match\n */\nexport function isPathActive(currentPath: string, targetPath: string): boolean {\n return currentPath === targetPath;\n}\n\n/**\n * Get page title from sidebar configuration\n *\n * @param pathname - Current pathname\n * @param sidebarGroups - Sidebar groups configuration\n * @param extraTitleMap - Extra title mappings\n * @returns Page title or null\n */\nexport function getPageTitle(\n pathname: string,\n sidebarGroups: Array<{ items: Array<{ path: string; label: string }> }>,\n extraTitleMap?: Record<string, string>\n): string | null {\n // Search in sidebar groups\n for (const group of sidebarGroups) {\n const item = group.items.find((i) => i.path === pathname);\n if (item) return item.label;\n }\n\n // Search in extra title map\n if (extraTitleMap?.[pathname]) {\n return extraTitleMap[pathname];\n }\n\n return null;\n}\n\n/**\n * Filter sidebar items by app type and enabled status\n *\n * @param items - Sidebar items to filter\n * @param user - Current user object\n * @returns Filtered items\n */\nexport function filterSidebarItems<T extends { enabled?: boolean; requiredApp?: \"mobile\" | \"web\" }>(\n items: T[],\n user?: { hasMobileApp?: boolean; hasWebApp?: boolean }\n): T[] {\n return items.filter((item) => {\n // Skip disabled items\n if (item.enabled === false) return false;\n\n // Skip items that require specific app types\n if (!item.requiredApp) return true;\n if (item.requiredApp === \"mobile\") return user?.hasMobileApp ?? false;\n if (item.requiredApp === \"web\") return user?.hasWebApp ?? false;\n\n return true;\n });\n}\n\n/**\n * Generate notification ID\n *\n * @returns Unique notification ID\n */\nexport function generateNotificationId(): string {\n return crypto.randomUUID();\n}\n","import React, { useState } from \"react\";\nimport {\n Bell, X, Sun, Moon, Menu, User, Settings, LogOut,\n ChevronDown, CreditCard\n} from \"lucide-react\";\nimport { Button } from \"@umituz/web-design-system/atoms\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useTranslation } from \"react-i18next\";\nimport type { DashboardHeaderProps, DashboardUser, DashboardNotification } from \"../../domain/types\";\nimport { formatNotificationTime } from \"../../infrastructure/utils\";\n\ninterface DashboardHeaderPropsExtended extends DashboardHeaderProps {\n /** Auth user */\n user?: DashboardUser;\n /** Notifications */\n notifications?: DashboardNotification[];\n /** Logout function */\n onLogout?: () => Promise<void>;\n /** Mark all as read function */\n onMarkAllRead?: () => void;\n /** Dismiss notification function */\n onDismissNotification?: (id: string) => void;\n /** Settings route */\n settingsRoute?: string;\n /** Profile route */\n profileRoute?: string;\n /** Billing route */\n billingRoute?: string;\n}\n\n/**\n * Dashboard Header Component\n *\n * Displays top navigation bar with theme toggle, notifications,\n * user menu, and organisation selector.\n *\n * @param props - Dashboard header props\n */\nexport const DashboardHeader = ({\n collapsed,\n setCollapsed,\n setMobileOpen,\n title,\n user,\n notifications = [],\n onLogout,\n onMarkAllRead,\n onDismissNotification,\n settingsRoute = \"/dashboard/settings\",\n profileRoute = \"/dashboard/profile\",\n billingRoute = \"/dashboard/billing\",\n}: DashboardHeaderPropsExtended) => {\n const navigate = useNavigate();\n const { t } = useTranslation();\n const [notifOpen, setNotifOpen] = useState(false);\n const [profileOpen, setProfileOpen] = useState(false);\n\n const unreadCount = notifications.filter((n) => !n.read).length;\n\n const markAllRead = () => {\n onMarkAllRead?.();\n };\n\n const handleLogout = async () => {\n try {\n await onLogout?.();\n navigate(\"/login\");\n } catch {\n // Error handling can be added by parent component\n }\n };\n\n // Placeholder components - these should be provided by the consuming app\n const ThemeToggle = () => {\n const [resolvedTheme, setResolvedTheme] = React.useState<\"light\" | \"dark\">(\"light\");\n\n return (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setResolvedTheme(resolvedTheme === \"light\" ? \"dark\" : \"light\")}\n className=\"text-muted-foreground h-9 w-9\"\n title={resolvedTheme === \"dark\" ? t('common.tooltips.switchLight') : t('common.tooltips.switchDark')}\n >\n {resolvedTheme === \"dark\" ? <Sun className=\"h-4 w-4\" /> : <Moon className=\"h-4 w-4\" />}\n </Button>\n );\n };\n\n return (\n <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\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" className=\"md:hidden\" onClick={() => setMobileOpen(true)}>\n <Menu className=\"h-5 w-5\" />\n </Button>\n {collapsed && (\n <Button variant=\"ghost\" size=\"icon\" className=\"hidden md:inline-flex\" onClick={() => setCollapsed(false)}>\n <Menu className=\"h-5 w-5\" />\n </Button>\n )}\n <h2 className=\"text-sm font-semibold text-foreground\">\n {title}\n </h2>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {/* Theme Toggle */}\n <ThemeToggle />\n\n {/* Notifications */}\n <div className=\"relative\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-muted-foreground relative h-9 w-9\"\n onClick={() => {\n setNotifOpen(!notifOpen);\n setProfileOpen(false);\n }}\n >\n <Bell className=\"h-4 w-4\" />\n {unreadCount > 0 && (\n <span className=\"absolute top-2 right-2 flex h-2 w-2\">\n <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-destructive opacity-75\"></span>\n <span className=\"relative inline-flex rounded-full h-2 w-2 bg-destructive\"></span>\n </span>\n )}\n </Button>\n\n {notifOpen && (\n <>\n <div className=\"fixed inset-0 z-40\" onClick={() => setNotifOpen(false)} />\n <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\">\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border bg-muted/50\">\n <h3 className=\"text-xs font-bold uppercase tracking-wider text-foreground\">{t('dashboard.notifications.title')}</h3>\n {unreadCount > 0 && (\n <button onClick={markAllRead} className=\"text-[10px] font-bold text-primary hover:underline uppercase\">{t('dashboard.notifications.markAllRead')}</button>\n )}\n </div>\n\n <div className=\"max-h-[400px] overflow-y-auto\">\n {notifications.map((n) => (\n <div key={n.id} 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\" : \"\"}`}>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm text-foreground leading-snug\">{n.text}</p>\n <p className=\"text-[10px] text-muted-foreground mt-1 flex items-center gap-1\">\n <span className=\"inline-block w-1 h-1 rounded-full bg-muted-foreground/30\" />\n {formatNotificationTime(n.createdAt, t)}\n </p>\n </div>\n <button\n onClick={() => onDismissNotification?.(n.id)}\n className=\"text-muted-foreground/50 hover:text-foreground shrink-0 transition-colors\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </div>\n ))}\n {notifications.length === 0 && (\n <div className=\"px-4 py-10 text-center\">\n <div className=\"mx-auto w-10 h-10 rounded-full bg-muted flex items-center justify-center mb-3\">\n <Bell className=\"h-5 w-5 text-muted-foreground/50\" />\n </div>\n <p className=\"text-sm text-muted-foreground\">{t('dashboard.notifications.none')}</p>\n </div>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n\n <div className=\"h-6 w-px bg-border mx-1\" />\n\n <div className=\"relative\">\n <button\n onClick={() => {\n setProfileOpen(!profileOpen);\n setNotifOpen(false);\n }}\n className=\"flex items-center gap-2 p-1 pl-1 rounded-full hover:bg-muted transition-colors group\"\n >\n <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\">\n {user?.avatar && <img src={user.avatar} alt=\"User\" className=\"w-full h-full object-cover\" />}\n </div>\n <ChevronDown className={`h-4 w-4 text-muted-foreground transition-transform duration-200 ${profileOpen && \"rotate-180\"}`} />\n </button>\n\n {profileOpen && (\n <>\n <div className=\"fixed inset-0 z-40\" onClick={() => setProfileOpen(false)} />\n <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\">\n <div className=\"px-3 py-2 border-b border-border/50 mb-1\">\n <p className=\"text-sm font-bold text-foreground\">{user?.name || t(\"common.roles.user\")}</p>\n <p className=\"text-xs text-muted-foreground truncate\">{user?.email}</p>\n </div>\n\n <div className=\"space-y-0.5\">\n <button\n onClick={() => { navigate(profileRoute); setProfileOpen(false); }}\n className=\"flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <User className=\"h-4 w-4 text-muted-foreground\" />\n {t('common.profile')}\n </button>\n <button\n onClick={() => { navigate(billingRoute); setProfileOpen(false); }}\n className=\"flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <CreditCard className=\"h-4 w-4 text-muted-foreground\" />\n {t('common.billing')}\n </button>\n <button\n onClick={() => { navigate(settingsRoute); setProfileOpen(false); }}\n className=\"flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <Settings className=\"h-4 w-4 text-muted-foreground\" />\n {t('common.settings')}\n </button>\n </div>\n\n <div className=\"h-px bg-border my-1.5\" />\n\n <button\n onClick={handleLogout}\n 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\"\n >\n <LogOut className=\"h-4 w-4\" />\n {t('common.logout')}\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n </header>\n );\n};\n","/**\n * Dashboard Theme System\n *\n * Provides theme configuration and presets for the dashboard layout.\n * Themes are applied via CSS variables, allowing runtime customization.\n */\n\nimport type { DashboardTheme, DashboardThemePreset } from \"../types\";\n\n// =============================================================================\n// Default Theme\n// =============================================================================\n\n/**\n * Default dashboard theme (light mode)\n */\nexport const DEFAULT_DASHBOARD_THEME: DashboardTheme = {\n primary: \"hsl(222.2 47.4% 11.2%)\",\n secondary: \"hsl(217.2 32.6% 17.5%)\",\n sidebarBackground: \"hsl(222.2 47.4% 11.2%)\",\n sidebarForeground: \"hsl(210 40% 98%)\",\n sidebarBorder: \"hsl(217.2 32.6% 17.5%)\",\n headerBackground: \"hsla(0, 0%, 100%, 0.8)\",\n background: \"hsl(0 0% 100%)\",\n foreground: \"hsl(222.2 84% 4.9%)\",\n border: \"hsl(214.3 31.8% 91.4%)\",\n accent: \"hsl(217.2 91.2% 59.8%)\",\n accentForeground: \"hsl(0 0% 100%)\",\n destructive: \"hsl(0 84.2% 60.2%)\",\n destructiveForeground: \"hsl(0 0% 98%)\",\n muted: \"hsl(210 40% 96.1%)\",\n mutedForeground: \"hsl(215.4 16.3% 46.9%)\",\n card: \"hsl(0 0% 100%)\",\n cardForeground: \"hsl(222.2 84% 4.9%)\",\n popover: \"hsl(0 0% 100%)\",\n popoverForeground: \"hsl(222.2 84% 4.9%)\",\n radius: \"0.5rem\",\n};\n\n/**\n * Default dashboard theme (dark mode)\n */\nexport const DEFAULT_DASHBOARD_THEME_DARK: DashboardTheme = {\n primary: \"hsl(217.2 91.2% 59.8%)\",\n secondary: \"hsl(217.2 32.6% 17.5%)\",\n sidebarBackground: \"hsl(222.2 47.4% 11.2%)\",\n sidebarForeground: \"hsl(210 40% 98%)\",\n sidebarBorder: \"hsl(217.2 32.6% 17.5%)\",\n headerBackground: \"hsla(222.2 47.4% 11.2%, 0.8)\",\n background: \"hsl(222.2 84% 4.9%)\",\n foreground: \"hsl(210 40% 98%)\",\n border: \"hsl(217.2 32.6% 17.5%)\",\n accent: \"hsl(217.2 91.2% 59.8%)\",\n accentForeground: \"hsl(0 0% 100%)\",\n destructive: \"hsl(0 62.8% 30.6%)\",\n destructiveForeground: \"hsl(0 0% 98%)\",\n muted: \"hsl(217.2 32.6% 17.5%)\",\n mutedForeground: \"hsl(215 20.2% 65.1%)\",\n card: \"hsl(222.2 84% 4.9%)\",\n cardForeground: \"hsl(210 40% 98%)\",\n popover: \"hsl(222.2 84% 4.9%)\",\n popoverForeground: \"hsl(210 40% 98%)\",\n radius: \"0.5rem\",\n};\n\n// =============================================================================\n// Theme Presets\n// =============================================================================\n\n/**\n * Available theme presets\n */\nexport const DASHBOARD_THEME_PRESETS: DashboardThemePreset[] = [\n {\n name: \"default\",\n theme: DEFAULT_DASHBOARD_THEME,\n dark: false,\n },\n {\n name: \"default-dark\",\n theme: DEFAULT_DASHBOARD_THEME_DARK,\n dark: true,\n },\n {\n name: \"blue\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(221.2 83.2% 53.3%)\",\n accent: \"hsl(221.2 83.2% 53.3%)\",\n },\n dark: false,\n },\n {\n name: \"blue-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(221.2 83.2% 53.3%)\",\n accent: \"hsl(221.2 83.2% 53.3%)\",\n },\n dark: true,\n },\n {\n name: \"purple\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(271.5 81.3% 55.9%)\",\n accent: \"hsl(271.5 81.3% 55.9%)\",\n },\n dark: false,\n },\n {\n name: \"purple-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(271.5 81.3% 55.9%)\",\n accent: \"hsl(271.5 81.3% 55.9%)\",\n },\n dark: true,\n },\n {\n name: \"green\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(142.1 76.2% 36.3%)\",\n accent: \"hsl(142.1 76.2% 36.3%)\",\n },\n dark: false,\n },\n {\n name: \"green-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(142.1 76.2% 36.3%)\",\n accent: \"hsl(142.1 76.2% 36.3%)\",\n },\n dark: true,\n },\n {\n name: \"orange\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(24.6 95% 53.1%)\",\n accent: \"hsl(24.6 95% 53.1%)\",\n },\n dark: false,\n },\n {\n name: \"orange-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(24.6 95% 53.1%)\",\n accent: \"hsl(24.6 95% 53.1%)\",\n },\n dark: true,\n },\n];\n\n// =============================================================================\n// Theme Utilities\n// =============================================================================\n\n/**\n * Apply theme to document root via CSS variables\n *\n * @param theme - Theme configuration to apply\n */\nexport function applyDashboardTheme(theme: DashboardTheme): void {\n if (typeof document === \"undefined\") return;\n\n const root = document.documentElement;\n\n // Map theme keys to CSS variable names\n const cssVars: Record<string, string | undefined> = {\n \"--primary\": theme.primary,\n \"--secondary\": theme.secondary,\n \"--sidebar\": theme.sidebarBackground,\n \"--sidebar-foreground\": theme.sidebarForeground,\n \"--sidebar-border\": theme.sidebarBorder,\n \"--background\": theme.background,\n \"--foreground\": theme.foreground,\n \"--border\": theme.border,\n \"--accent\": theme.accent,\n \"--accent-foreground\": theme.accentForeground,\n \"--destructive\": theme.destructive,\n \"--destructive-foreground\": theme.destructiveForeground,\n \"--muted\": theme.muted,\n \"--muted-foreground\": theme.mutedForeground,\n \"--card\": theme.card,\n \"--card-foreground\": theme.cardForeground,\n \"--popover\": theme.popover,\n \"--popover-foreground\": theme.popoverForeground,\n \"--radius\": theme.radius,\n };\n\n // Apply CSS variables\n Object.entries(cssVars).forEach(([key, value]) => {\n if (value) {\n root.style.setProperty(key, value);\n }\n });\n}\n\n/**\n * Get theme preset by name\n *\n * @param name - Preset name\n * @returns Theme preset or undefined\n */\nexport function getDashboardThemePreset(name: string): DashboardThemePreset | undefined {\n return DASHBOARD_THEME_PRESETS.find((preset) => preset.name === name);\n}\n\n/**\n * Merge custom theme with default theme\n *\n * @param customTheme - Custom theme configuration\n * @param dark - Whether to use dark mode base\n * @returns Merged theme configuration\n */\nexport function mergeDashboardTheme(\n customTheme: Partial<DashboardTheme>,\n dark = false\n): DashboardTheme {\n const baseTheme = dark ? DEFAULT_DASHBOARD_THEME_DARK : DEFAULT_DASHBOARD_THEME;\n return {\n ...baseTheme,\n ...customTheme,\n } as DashboardTheme;\n}\n","/**\n * Dashboard Hooks\n *\n * Custom React hooks for dashboard functionality\n */\n\nimport { useState, useCallback } from \"react\";\nimport type { DashboardNotification } from \"../../domain/types\";\n\n/**\n * Use Notifications Hook\n *\n * Manages notification state and actions\n *\n * @param initialNotifications - Initial notification list\n * @returns Notification state and actions\n */\nexport function useNotifications(initialNotifications: DashboardNotification[] = []) {\n const [notifications, setNotifications] = useState<DashboardNotification[]>(initialNotifications);\n\n const markAllRead = useCallback(() => {\n setNotifications((prev) =>\n prev.map((n) => ({ ...n, read: true }))\n );\n }, []);\n\n const dismiss = useCallback((id: string) => {\n setNotifications((prev) => prev.filter((n) => n.id !== id));\n }, []);\n\n const add = useCallback((notification: Omit<DashboardNotification, \"id\">) => {\n const newNotification: DashboardNotification = {\n ...notification,\n id: crypto.randomUUID(),\n read: false,\n createdAt: new Date(),\n };\n setNotifications((prev) => [newNotification, ...prev]);\n }, []);\n\n return {\n notifications,\n markAllRead,\n dismiss,\n add,\n };\n}\n\n/**\n * Use Sidebar Hook\n *\n * Manages sidebar state\n *\n * @returns Sidebar state and actions\n */\nexport function useSidebar(initialCollapsed = false) {\n const [collapsed, setCollapsed] = useState(initialCollapsed);\n const [mobileOpen, setMobileOpen] = useState(false);\n\n const toggle = useCallback(() => {\n setCollapsed((prev) => !prev);\n }, []);\n\n const openMobile = useCallback(() => {\n setMobileOpen(true);\n }, []);\n\n const closeMobile = useCallback(() => {\n setMobileOpen(false);\n }, []);\n\n return {\n collapsed,\n setCollapsed,\n toggle,\n mobileOpen,\n setMobileOpen: setMobileOpen,\n openMobile,\n closeMobile,\n };\n}\n"],"mappings":"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","names":["useState","useLocation","jsx","jsxs","useState","Menu","ChevronDown","Button","useTranslation","jsx","jsxs","useTranslation","useState","Button","Menu","ChevronDown","jsx","jsxs","useLocation","useState","useState"]}
|
|
1
|
+
{"version":3,"sources":["../src/domains/layouts/components/DashboardLayout.tsx","../src/domains/layouts/components/DashboardSidebar.tsx","../src/domains/layouts/components/BrandLogo.tsx","../src/domains/layouts/utils/dashboard.ts","../src/domains/layouts/components/DashboardHeader.tsx","../src/domains/layouts/theme/index.ts","../src/domains/layouts/hooks/dashboard.ts"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useLocation, Outlet, Navigate } from \"react-router-dom\";\nimport { Skeleton } from \"@umituz/web-design-system/atoms\";\nimport { DashboardSidebar } from \"./DashboardSidebar\";\nimport { DashboardHeader } from \"./DashboardHeader\";\nimport type { DashboardLayoutConfig, DashboardUser, DashboardNotification } from \"../types\";\n\ninterface DashboardLayoutProps {\n /** Layout configuration */\n config: DashboardLayoutConfig;\n /** Auth user */\n user?: DashboardUser;\n /** Auth loading state */\n authLoading?: boolean;\n /** Authenticated state */\n isAuthenticated?: boolean;\n /** Notifications */\n notifications?: DashboardNotification[];\n /** Logout function */\n onLogout?: () => Promise<void>;\n /** Mark all as read function */\n onMarkAllRead?: () => void;\n /** Dismiss notification function */\n onDismissNotification?: (id: string) => void;\n /** Login route for redirect */\n loginRoute?: string;\n}\n\n/**\n * Dashboard Layout Component\n *\n * Main layout wrapper for dashboard pages.\n * Provides sidebar, header, and content area with responsive design.\n *\n * Features:\n * - Collapsible sidebar\n * - Mobile menu overlay\n * - Breadcrumb page titles\n * - Loading skeletons\n * - Auth protection\n *\n * @param props - Dashboard layout props\n */\nexport const DashboardLayout = ({\n config,\n user,\n authLoading = false,\n isAuthenticated = true,\n notifications = [],\n onLogout,\n onMarkAllRead,\n onDismissNotification,\n loginRoute = \"/login\",\n}: DashboardLayoutProps) => {\n const location = useLocation();\n const [collapsed, setCollapsed] = useState(false);\n const [mobileOpen, setMobileOpen] = useState(false);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n setLoading(true);\n const timer = setTimeout(() => setLoading(false), 300);\n return () => clearTimeout(timer);\n }, [location.pathname]);\n\n useEffect(() => {\n setMobileOpen(false);\n }, [location.pathname]);\n\n if (authLoading) return null;\n if (!isAuthenticated) return <Navigate to={loginRoute} replace />;\n\n const activeItem = config.sidebarGroups\n .flatMap((group) => group.items)\n .find((i) => i.path === location.pathname);\n\n const getTitle = () => {\n if (!activeItem) return config.extraTitleMap?.[location.pathname] || \"Dashboard\";\n return activeItem.label; // Note: In real app, this would be translated\n };\n\n const currentTitle = getTitle();\n\n return (\n <div className=\"flex h-screen w-full bg-background font-sans\">\n {/* Desktop Sidebar */}\n <aside\n className={`hidden md:flex flex-col shrink-0 border-r border-sidebar-border bg-sidebar transition-all duration-300 ${\n collapsed ? \"w-16\" : \"w-60\"\n }`}\n >\n <DashboardSidebar\n collapsed={collapsed}\n setCollapsed={setCollapsed}\n sidebarGroups={config.sidebarGroups}\n brandName={config.brandName}\n brandTagline={config.brandTagline}\n user={user}\n />\n </aside>\n\n {/* Mobile Menu Overlay */}\n {mobileOpen && (\n <div className=\"fixed inset-0 z-50 md:hidden\">\n <div className=\"absolute inset-0 bg-background/80 backdrop-blur-sm\" onClick={() => setMobileOpen(false)} />\n <aside className=\"absolute left-0 top-0 h-full w-60 border-r border-sidebar-border bg-sidebar shadow-xl\">\n <DashboardSidebar\n collapsed={false}\n setCollapsed={() => setMobileOpen(false)}\n sidebarGroups={config.sidebarGroups}\n brandName={config.brandName}\n brandTagline={config.brandTagline}\n user={user}\n />\n </aside>\n </div>\n )}\n\n {/* Main Content Area */}\n <div className=\"flex flex-1 flex-col overflow-hidden min-w-0\">\n <DashboardHeader\n collapsed={collapsed}\n setCollapsed={setCollapsed}\n setMobileOpen={setMobileOpen}\n title={currentTitle}\n user={user}\n notifications={notifications}\n onLogout={onLogout}\n onMarkAllRead={onMarkAllRead}\n onDismissNotification={onDismissNotification}\n />\n\n <main className=\"flex-1 overflow-y-auto p-4 md:p-8\">\n {loading ? (\n <div className=\"mx-auto w-full max-w-7xl space-y-6\">\n <Skeleton className=\"h-8 w-1/3 rounded-xl\" />\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <Skeleton key={i} className=\"h-28 rounded-2xl\" />\n ))}\n </div>\n <Skeleton className=\"h-64 rounded-[32px]\" />\n </div>\n ) : (\n <Outlet />\n )}\n </main>\n </div>\n </div>\n );\n};\n\nexport default DashboardLayout;\n","import { useState } from \"react\";\nimport { Link, useLocation } from \"react-router-dom\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { Button } from \"@umituz/web-design-system/atoms\";\nimport { BrandLogo } from \"./BrandLogo\";\nimport { PenTool, Menu, ChevronLeft, ChevronDown, ChevronRight } from \"lucide-react\";\nimport type { DashboardUser, SidebarGroup } from \"../types\";\nimport type { DashboardSidebarProps } from \"../types\";\nimport { filterSidebarItems } from \"../utils\";\n\ninterface DashboardSidebarPropsExtended extends DashboardSidebarProps {\n /** Sidebar groups configuration */\n sidebarGroups: SidebarGroup[];\n /** Brand name */\n brandName?: string;\n /** Brand tagline */\n brandTagline?: string;\n /** Create post route */\n createPostRoute?: string;\n /** Auth user */\n user?: DashboardUser;\n}\n\n/**\n * Dashboard Sidebar Component\n *\n * Displays collapsible sidebar with navigation menu items.\n * Supports app-based filtering (mobile/web) and collapsible groups.\n *\n * @param props - Dashboard sidebar props\n */\nexport const DashboardSidebar = ({\n collapsed,\n setCollapsed,\n sidebarGroups,\n brandName = \"App\",\n brandTagline = \"grow smarter\",\n createPostRoute = \"/dashboard/create\",\n user,\n}: DashboardSidebarPropsExtended) => {\n const location = useLocation();\n const { t } = useTranslation();\n const [collapsedGroups, setCollapsedGroups] = useState<Record<string, boolean>>({});\n\n const toggleGroup = (title: string) => {\n setCollapsedGroups(prev => ({\n ...prev,\n [title]: !prev[title]\n }));\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Brand Section */}\n <div className=\"flex h-16 items-center gap-3 border-b border-sidebar-border px-4 transition-all duration-300\">\n <BrandLogo size={32} />\n {!collapsed && (\n <div className=\"flex flex-col -gap-1\">\n <span className=\"text-2xl font-black text-sidebar-foreground tracking-tighter leading-none\">{brandName}</span>\n <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\">\n {brandTagline}\n </span>\n </div>\n )}\n </div>\n\n {/* Create Button */}\n <div className=\"px-3 py-4 border-b border-sidebar-border/50\">\n <Link to={createPostRoute}>\n <Button\n variant=\"default\"\n className={`w-full gap-3 shadow-glow transition-all active:scale-95 group overflow-hidden rounded-xl ${\n collapsed ? \"px-0 justify-center h-10 w-10 mx-auto\" : \"justify-start px-4 h-11\"\n }`}\n title={collapsed ? t('sidebar.createPost') : undefined}\n >\n <PenTool className={`shrink-0 transition-transform duration-300 ${collapsed ? \"h-5 w-5\" : \"h-4 w-4 group-hover:scale-110\"}`} />\n {!collapsed && <span className=\"font-bold tracking-tight\">{t('sidebar.createPost')}</span>}\n </Button>\n </Link>\n </div>\n\n {/* Navigation */}\n <nav className=\"flex-1 overflow-y-auto px-2 py-3 scrollbar-hide\">\n <div className=\"space-y-6\">\n {sidebarGroups.map((group) => {\n const filteredItems = filterSidebarItems(group.items, user);\n\n if (filteredItems.length === 0) return null;\n\n const isGroupCollapsed = collapsedGroups[group.title];\n\n return (\n <div key={group.title} className=\"space-y-1\">\n {!collapsed && (\n <button\n onClick={() => toggleGroup(group.title)}\n className=\"w-full flex items-center justify-between px-3 mb-2 group/header\"\n >\n <span className=\"text-[10px] font-bold uppercase tracking-widest text-sidebar-foreground/40 group-hover/header:text-sidebar-foreground/70 transition-colors\">\n {group.title === \"sidebar.ai\" ? `${brandName} AI` : t(group.title)}\n </span>\n {isGroupCollapsed ? (\n <ChevronRight className=\"h-3 w-3 text-sidebar-foreground/30 flex-shrink-0 group-hover/header:text-sidebar-foreground/50 transition-colors\" />\n ) : (\n <ChevronDown className=\"h-3 w-3 text-sidebar-foreground/30 flex-shrink-0 group-hover/header:text-sidebar-foreground/50 transition-colors\" />\n )}\n </button>\n )}\n\n {(!isGroupCollapsed || collapsed) && filteredItems.map((item) => {\n const active = location.pathname === item.path;\n return (\n <Link\n key={item.path}\n to={item.path}\n className={`flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-all duration-200 ${\n active\n ? \"bg-sidebar-accent text-sidebar-accent-foreground shadow-sm\"\n : \"text-sidebar-foreground/70 hover:bg-sidebar-accent/40 hover:text-sidebar-foreground\"\n } ${collapsed ? \"justify-center\" : \"\"}`}\n title={collapsed ? t(item.label) : undefined}\n >\n <item.icon className={`h-4 w-4 shrink-0 transition-transform ${active && \"scale-110\"}`} />\n {!collapsed && <span>{t(item.label)}</span>}\n </Link>\n );\n })}\n </div>\n );\n })}\n </div>\n </nav>\n\n {/* Collapse Toggle */}\n <div className=\"border-t border-sidebar-border p-3\">\n <div className={`flex items-center ${collapsed ? \"justify-center\" : \"justify-between\"}`}>\n {!collapsed && (\n <p className=\"text-[10px] uppercase tracking-wider text-sidebar-foreground/40 font-bold px-2\">\n {t('sidebar.system')}\n </p>\n )}\n <Button variant=\"ghost\" size=\"icon\" onClick={() => setCollapsed(!collapsed)} className=\"text-sidebar-foreground/70\">\n {collapsed ? <Menu className=\"h-4 w-4\" /> : <ChevronLeft className=\"h-4 w-4\" />}\n </Button>\n </div>\n </div>\n </div>\n );\n};\n","import React from \"react\";\nimport { cn } from \"@umituz/web-design-system/utils\";\n\ninterface BrandLogoProps {\n className?: string;\n size?: number;\n}\n\n/**\n * BrandLogo Component\n *\n * Displays the application brand logo as an SVG.\n * Supports custom sizing and styling through className.\n *\n * @param className - Optional CSS classes for styling\n * @param size - Width and height in pixels (default: 32)\n */\nexport const BrandLogo = ({ className, size = 32 }: BrandLogoProps) => {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 100 100\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={cn(\"shrink-0\", className)}\n >\n {/* Solid Foundation / Platform */}\n <rect\n x=\"15\"\n y=\"65\"\n width=\"70\"\n height=\"15\"\n rx=\"4\"\n fill=\"hsl(var(--primary))\"\n />\n\n {/* Assembly Paths / Structure */}\n <rect\n x=\"25\"\n y=\"25\"\n width=\"12\"\n height=\"40\"\n rx=\"2\"\n fill=\"hsl(var(--primary))\"\n />\n <rect\n x=\"44\"\n y=\"35\"\n width=\"12\"\n height=\"30\"\n rx=\"2\"\n fill=\"hsl(var(--primary))\"\n />\n <rect\n x=\"63\"\n y=\"20\"\n width=\"12\"\n height=\"45\"\n rx=\"2\"\n fill=\"hsl(var(--primary))\"\n />\n\n {/* Modern Accent - The 'Assembly' Bridge */}\n <rect\n x=\"20\"\n y=\"45\"\n width=\"60\"\n height=\"10\"\n rx=\"2\"\n fill=\"hsl(var(--secondary))\"\n />\n\n {/* Connection Point / Beacon */}\n <circle\n cx=\"50\"\n cy=\"20\"\n r=\"5\"\n fill=\"hsl(var(--secondary))\"\n />\n </svg>\n );\n};\n","/**\n * Dashboard Utilities\n *\n * Utility functions for dashboard operations\n */\n\n/**\n * Format notification timestamp to relative time\n *\n * @param createdAt - Notification creation timestamp\n * @param t - i18n translation function\n * @returns Formatted time string\n */\nexport function formatNotificationTime(\n createdAt: Date | string | number,\n t: (key: string, params?: Record<string, unknown>) => string\n): string {\n const date = new Date(createdAt);\n const secs = Math.floor((Date.now() - date.getTime()) / 1000);\n\n if (secs < 60) return t(\"dashboard.activityFeed.times.justNow\");\n if (secs < 3600) return t(\"dashboard.activityFeed.times.minutesAgo\", { val: Math.floor(secs / 60) });\n if (secs < 86400) return t(\"dashboard.activityFeed.times.hoursAgo\", { val: Math.floor(secs / 3600) });\n return t(\"dashboard.activityFeed.times.daysAgo\", { val: Math.floor(secs / 86400) });\n}\n\n/**\n * Check if a path is active\n *\n * @param currentPath - Current location pathname\n * @param targetPath - Target path to check\n * @returns True if paths match\n */\nexport function isPathActive(currentPath: string, targetPath: string): boolean {\n return currentPath === targetPath;\n}\n\n/**\n * Get page title from sidebar configuration\n *\n * @param pathname - Current pathname\n * @param sidebarGroups - Sidebar groups configuration\n * @param extraTitleMap - Extra title mappings\n * @returns Page title or null\n */\nexport function getPageTitle(\n pathname: string,\n sidebarGroups: Array<{ items: Array<{ path: string; label: string }> }>,\n extraTitleMap?: Record<string, string>\n): string | null {\n // Search in sidebar groups\n for (const group of sidebarGroups) {\n const item = group.items.find((i) => i.path === pathname);\n if (item) return item.label;\n }\n\n // Search in extra title map\n if (extraTitleMap?.[pathname]) {\n return extraTitleMap[pathname];\n }\n\n return null;\n}\n\n/**\n * Filter sidebar items by app type and enabled status\n *\n * @param items - Sidebar items to filter\n * @param user - Current user object\n * @returns Filtered items\n */\nexport function filterSidebarItems<T extends { enabled?: boolean; requiredApp?: \"mobile\" | \"web\" }>(\n items: T[],\n user?: { hasMobileApp?: boolean; hasWebApp?: boolean }\n): T[] {\n return items.filter((item) => {\n // Skip disabled items\n if (item.enabled === false) return false;\n\n // Skip items that require specific app types\n if (!item.requiredApp) return true;\n if (item.requiredApp === \"mobile\") return user?.hasMobileApp ?? false;\n if (item.requiredApp === \"web\") return user?.hasWebApp ?? false;\n\n return true;\n });\n}\n\n/**\n * Generate notification ID\n *\n * @returns Unique notification ID\n */\nexport function generateNotificationId(): string {\n return crypto.randomUUID();\n}\n","import React, { useState } from \"react\";\nimport {\n Bell, X, Sun, Moon, Menu, User, Settings, LogOut,\n ChevronDown, CreditCard\n} from \"lucide-react\";\nimport { Button } from \"@umituz/web-design-system/atoms\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useTranslation } from \"react-i18next\";\nimport type { DashboardHeaderProps, DashboardUser, DashboardNotification } from \"../types\";\nimport { formatNotificationTime } from \"../utils\";\n\ninterface DashboardHeaderPropsExtended extends DashboardHeaderProps {\n /** Auth user */\n user?: DashboardUser;\n /** Notifications */\n notifications?: DashboardNotification[];\n /** Logout function */\n onLogout?: () => Promise<void>;\n /** Mark all as read function */\n onMarkAllRead?: () => void;\n /** Dismiss notification function */\n onDismissNotification?: (id: string) => void;\n /** Settings route */\n settingsRoute?: string;\n /** Profile route */\n profileRoute?: string;\n /** Billing route */\n billingRoute?: string;\n}\n\n/**\n * Dashboard Header Component\n *\n * Displays top navigation bar with theme toggle, notifications,\n * user menu, and organisation selector.\n *\n * @param props - Dashboard header props\n */\nexport const DashboardHeader = ({\n collapsed,\n setCollapsed,\n setMobileOpen,\n title,\n user,\n notifications = [],\n onLogout,\n onMarkAllRead,\n onDismissNotification,\n settingsRoute = \"/dashboard/settings\",\n profileRoute = \"/dashboard/profile\",\n billingRoute = \"/dashboard/billing\",\n}: DashboardHeaderPropsExtended) => {\n const navigate = useNavigate();\n const { t } = useTranslation();\n const [notifOpen, setNotifOpen] = useState(false);\n const [profileOpen, setProfileOpen] = useState(false);\n\n const unreadCount = notifications.filter((n) => !n.read).length;\n\n const markAllRead = () => {\n onMarkAllRead?.();\n };\n\n const handleLogout = async () => {\n try {\n await onLogout?.();\n navigate(\"/login\");\n } catch {\n // Error handling can be added by parent component\n }\n };\n\n // Placeholder components - these should be provided by the consuming app\n const ThemeToggle = () => {\n const [resolvedTheme, setResolvedTheme] = React.useState<\"light\" | \"dark\">(\"light\");\n\n return (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setResolvedTheme(resolvedTheme === \"light\" ? \"dark\" : \"light\")}\n className=\"text-muted-foreground h-9 w-9\"\n title={resolvedTheme === \"dark\" ? t('common.tooltips.switchLight') : t('common.tooltips.switchDark')}\n >\n {resolvedTheme === \"dark\" ? <Sun className=\"h-4 w-4\" /> : <Moon className=\"h-4 w-4\" />}\n </Button>\n );\n };\n\n return (\n <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\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" className=\"md:hidden\" onClick={() => setMobileOpen(true)}>\n <Menu className=\"h-5 w-5\" />\n </Button>\n {collapsed && (\n <Button variant=\"ghost\" size=\"icon\" className=\"hidden md:inline-flex\" onClick={() => setCollapsed(false)}>\n <Menu className=\"h-5 w-5\" />\n </Button>\n )}\n <h2 className=\"text-sm font-semibold text-foreground\">\n {title}\n </h2>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {/* Theme Toggle */}\n <ThemeToggle />\n\n {/* Notifications */}\n <div className=\"relative\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-muted-foreground relative h-9 w-9\"\n onClick={() => {\n setNotifOpen(!notifOpen);\n setProfileOpen(false);\n }}\n >\n <Bell className=\"h-4 w-4\" />\n {unreadCount > 0 && (\n <span className=\"absolute top-2 right-2 flex h-2 w-2\">\n <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-destructive opacity-75\"></span>\n <span className=\"relative inline-flex rounded-full h-2 w-2 bg-destructive\"></span>\n </span>\n )}\n </Button>\n\n {notifOpen && (\n <>\n <div className=\"fixed inset-0 z-40\" onClick={() => setNotifOpen(false)} />\n <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\">\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border bg-muted/50\">\n <h3 className=\"text-xs font-bold uppercase tracking-wider text-foreground\">{t('dashboard.notifications.title')}</h3>\n {unreadCount > 0 && (\n <button onClick={markAllRead} className=\"text-[10px] font-bold text-primary hover:underline uppercase\">{t('dashboard.notifications.markAllRead')}</button>\n )}\n </div>\n\n <div className=\"max-h-[400px] overflow-y-auto\">\n {notifications.map((n) => (\n <div key={n.id} 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\" : \"\"}`}>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm text-foreground leading-snug\">{n.text}</p>\n <p className=\"text-[10px] text-muted-foreground mt-1 flex items-center gap-1\">\n <span className=\"inline-block w-1 h-1 rounded-full bg-muted-foreground/30\" />\n {formatNotificationTime(n.createdAt, t)}\n </p>\n </div>\n <button\n onClick={() => onDismissNotification?.(n.id)}\n className=\"text-muted-foreground/50 hover:text-foreground shrink-0 transition-colors\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </div>\n ))}\n {notifications.length === 0 && (\n <div className=\"px-4 py-10 text-center\">\n <div className=\"mx-auto w-10 h-10 rounded-full bg-muted flex items-center justify-center mb-3\">\n <Bell className=\"h-5 w-5 text-muted-foreground/50\" />\n </div>\n <p className=\"text-sm text-muted-foreground\">{t('dashboard.notifications.none')}</p>\n </div>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n\n <div className=\"h-6 w-px bg-border mx-1\" />\n\n <div className=\"relative\">\n <button\n onClick={() => {\n setProfileOpen(!profileOpen);\n setNotifOpen(false);\n }}\n className=\"flex items-center gap-2 p-1 pl-1 rounded-full hover:bg-muted transition-colors group\"\n >\n <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\">\n {user?.avatar && <img src={user.avatar} alt=\"User\" className=\"w-full h-full object-cover\" />}\n </div>\n <ChevronDown className={`h-4 w-4 text-muted-foreground transition-transform duration-200 ${profileOpen && \"rotate-180\"}`} />\n </button>\n\n {profileOpen && (\n <>\n <div className=\"fixed inset-0 z-40\" onClick={() => setProfileOpen(false)} />\n <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\">\n <div className=\"px-3 py-2 border-b border-border/50 mb-1\">\n <p className=\"text-sm font-bold text-foreground\">{user?.name || t(\"common.roles.user\")}</p>\n <p className=\"text-xs text-muted-foreground truncate\">{user?.email}</p>\n </div>\n\n <div className=\"space-y-0.5\">\n <button\n onClick={() => { navigate(profileRoute); setProfileOpen(false); }}\n className=\"flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <User className=\"h-4 w-4 text-muted-foreground\" />\n {t('common.profile')}\n </button>\n <button\n onClick={() => { navigate(billingRoute); setProfileOpen(false); }}\n className=\"flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <CreditCard className=\"h-4 w-4 text-muted-foreground\" />\n {t('common.billing')}\n </button>\n <button\n onClick={() => { navigate(settingsRoute); setProfileOpen(false); }}\n className=\"flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <Settings className=\"h-4 w-4 text-muted-foreground\" />\n {t('common.settings')}\n </button>\n </div>\n\n <div className=\"h-px bg-border my-1.5\" />\n\n <button\n onClick={handleLogout}\n 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\"\n >\n <LogOut className=\"h-4 w-4\" />\n {t('common.logout')}\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n </header>\n );\n};\n","/**\n * Dashboard Theme System\n *\n * Provides theme configuration and presets for the dashboard layout.\n * Themes are applied via CSS variables, allowing runtime customization.\n */\n\nimport type { DashboardTheme, DashboardThemePreset } from \"../types\";\n\n// =============================================================================\n// Default Theme\n// =============================================================================\n\n/**\n * Default dashboard theme (light mode)\n */\nexport const DEFAULT_DASHBOARD_THEME: DashboardTheme = {\n primary: \"hsl(222.2 47.4% 11.2%)\",\n secondary: \"hsl(217.2 32.6% 17.5%)\",\n sidebarBackground: \"hsl(222.2 47.4% 11.2%)\",\n sidebarForeground: \"hsl(210 40% 98%)\",\n sidebarBorder: \"hsl(217.2 32.6% 17.5%)\",\n headerBackground: \"hsla(0, 0%, 100%, 0.8)\",\n background: \"hsl(0 0% 100%)\",\n foreground: \"hsl(222.2 84% 4.9%)\",\n border: \"hsl(214.3 31.8% 91.4%)\",\n accent: \"hsl(217.2 91.2% 59.8%)\",\n accentForeground: \"hsl(0 0% 100%)\",\n destructive: \"hsl(0 84.2% 60.2%)\",\n destructiveForeground: \"hsl(0 0% 98%)\",\n muted: \"hsl(210 40% 96.1%)\",\n mutedForeground: \"hsl(215.4 16.3% 46.9%)\",\n card: \"hsl(0 0% 100%)\",\n cardForeground: \"hsl(222.2 84% 4.9%)\",\n popover: \"hsl(0 0% 100%)\",\n popoverForeground: \"hsl(222.2 84% 4.9%)\",\n radius: \"0.5rem\",\n};\n\n/**\n * Default dashboard theme (dark mode)\n */\nexport const DEFAULT_DASHBOARD_THEME_DARK: DashboardTheme = {\n primary: \"hsl(217.2 91.2% 59.8%)\",\n secondary: \"hsl(217.2 32.6% 17.5%)\",\n sidebarBackground: \"hsl(222.2 47.4% 11.2%)\",\n sidebarForeground: \"hsl(210 40% 98%)\",\n sidebarBorder: \"hsl(217.2 32.6% 17.5%)\",\n headerBackground: \"hsla(222.2 47.4% 11.2%, 0.8)\",\n background: \"hsl(222.2 84% 4.9%)\",\n foreground: \"hsl(210 40% 98%)\",\n border: \"hsl(217.2 32.6% 17.5%)\",\n accent: \"hsl(217.2 91.2% 59.8%)\",\n accentForeground: \"hsl(0 0% 100%)\",\n destructive: \"hsl(0 62.8% 30.6%)\",\n destructiveForeground: \"hsl(0 0% 98%)\",\n muted: \"hsl(217.2 32.6% 17.5%)\",\n mutedForeground: \"hsl(215 20.2% 65.1%)\",\n card: \"hsl(222.2 84% 4.9%)\",\n cardForeground: \"hsl(210 40% 98%)\",\n popover: \"hsl(222.2 84% 4.9%)\",\n popoverForeground: \"hsl(210 40% 98%)\",\n radius: \"0.5rem\",\n};\n\n// =============================================================================\n// Theme Presets\n// =============================================================================\n\n/**\n * Available theme presets\n */\nexport const DASHBOARD_THEME_PRESETS: DashboardThemePreset[] = [\n {\n name: \"default\",\n theme: DEFAULT_DASHBOARD_THEME,\n dark: false,\n },\n {\n name: \"default-dark\",\n theme: DEFAULT_DASHBOARD_THEME_DARK,\n dark: true,\n },\n {\n name: \"blue\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(221.2 83.2% 53.3%)\",\n accent: \"hsl(221.2 83.2% 53.3%)\",\n },\n dark: false,\n },\n {\n name: \"blue-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(221.2 83.2% 53.3%)\",\n accent: \"hsl(221.2 83.2% 53.3%)\",\n },\n dark: true,\n },\n {\n name: \"purple\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(271.5 81.3% 55.9%)\",\n accent: \"hsl(271.5 81.3% 55.9%)\",\n },\n dark: false,\n },\n {\n name: \"purple-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(271.5 81.3% 55.9%)\",\n accent: \"hsl(271.5 81.3% 55.9%)\",\n },\n dark: true,\n },\n {\n name: \"green\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(142.1 76.2% 36.3%)\",\n accent: \"hsl(142.1 76.2% 36.3%)\",\n },\n dark: false,\n },\n {\n name: \"green-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(142.1 76.2% 36.3%)\",\n accent: \"hsl(142.1 76.2% 36.3%)\",\n },\n dark: true,\n },\n {\n name: \"orange\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(24.6 95% 53.1%)\",\n accent: \"hsl(24.6 95% 53.1%)\",\n },\n dark: false,\n },\n {\n name: \"orange-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(24.6 95% 53.1%)\",\n accent: \"hsl(24.6 95% 53.1%)\",\n },\n dark: true,\n },\n];\n\n// =============================================================================\n// Theme Utilities\n// =============================================================================\n\n/**\n * Apply theme to document root via CSS variables\n *\n * @param theme - Theme configuration to apply\n */\nexport function applyDashboardTheme(theme: DashboardTheme): void {\n if (typeof document === \"undefined\") return;\n\n const root = document.documentElement;\n\n // Map theme keys to CSS variable names\n const cssVars: Record<string, string | undefined> = {\n \"--primary\": theme.primary,\n \"--secondary\": theme.secondary,\n \"--sidebar\": theme.sidebarBackground,\n \"--sidebar-foreground\": theme.sidebarForeground,\n \"--sidebar-border\": theme.sidebarBorder,\n \"--background\": theme.background,\n \"--foreground\": theme.foreground,\n \"--border\": theme.border,\n \"--accent\": theme.accent,\n \"--accent-foreground\": theme.accentForeground,\n \"--destructive\": theme.destructive,\n \"--destructive-foreground\": theme.destructiveForeground,\n \"--muted\": theme.muted,\n \"--muted-foreground\": theme.mutedForeground,\n \"--card\": theme.card,\n \"--card-foreground\": theme.cardForeground,\n \"--popover\": theme.popover,\n \"--popover-foreground\": theme.popoverForeground,\n \"--radius\": theme.radius,\n };\n\n // Apply CSS variables\n Object.entries(cssVars).forEach(([key, value]) => {\n if (value) {\n root.style.setProperty(key, value);\n }\n });\n}\n\n/**\n * Get theme preset by name\n *\n * @param name - Preset name\n * @returns Theme preset or undefined\n */\nexport function getDashboardThemePreset(name: string): DashboardThemePreset | undefined {\n return DASHBOARD_THEME_PRESETS.find((preset) => preset.name === name);\n}\n\n/**\n * Merge custom theme with default theme\n *\n * @param customTheme - Custom theme configuration\n * @param dark - Whether to use dark mode base\n * @returns Merged theme configuration\n */\nexport function mergeDashboardTheme(\n customTheme: Partial<DashboardTheme>,\n dark = false\n): DashboardTheme {\n const baseTheme = dark ? DEFAULT_DASHBOARD_THEME_DARK : DEFAULT_DASHBOARD_THEME;\n return {\n ...baseTheme,\n ...customTheme,\n } as DashboardTheme;\n}\n","/**\n * Dashboard Hooks\n *\n * Custom React hooks for dashboard functionality\n */\n\nimport { useState, useCallback } from \"react\";\nimport type { DashboardNotification } from \"../types\";\n\n/**\n * Use Notifications Hook\n *\n * Manages notification state and actions\n *\n * @param initialNotifications - Initial notification list\n * @returns Notification state and actions\n */\nexport function useNotifications(initialNotifications: DashboardNotification[] = []) {\n const [notifications, setNotifications] = useState<DashboardNotification[]>(initialNotifications);\n\n const markAllRead = useCallback(() => {\n setNotifications((prev) =>\n prev.map((n) => ({ ...n, read: true }))\n );\n }, []);\n\n const dismiss = useCallback((id: string) => {\n setNotifications((prev) => prev.filter((n) => n.id !== id));\n }, []);\n\n const add = useCallback((notification: Omit<DashboardNotification, \"id\">) => {\n const newNotification: DashboardNotification = {\n ...notification,\n id: crypto.randomUUID(),\n read: false,\n createdAt: new Date(),\n };\n setNotifications((prev) => [newNotification, ...prev]);\n }, []);\n\n return {\n notifications,\n markAllRead,\n dismiss,\n add,\n };\n}\n\n/**\n * Use Sidebar Hook\n *\n * Manages sidebar state\n *\n * @returns Sidebar state and actions\n */\nexport function useSidebar(initialCollapsed = false) {\n const [collapsed, setCollapsed] = useState(initialCollapsed);\n const [mobileOpen, setMobileOpen] = useState(false);\n\n const toggle = useCallback(() => {\n setCollapsed((prev) => !prev);\n }, []);\n\n const openMobile = useCallback(() => {\n setMobileOpen(true);\n }, []);\n\n const closeMobile = useCallback(() => {\n setMobileOpen(false);\n }, []);\n\n return {\n collapsed,\n setCollapsed,\n toggle,\n mobileOpen,\n setMobileOpen: setMobileOpen,\n openMobile,\n closeMobile,\n };\n}\n"],"mappings":"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","names":["useState","useLocation","jsx","jsxs","useState","Menu","ChevronDown","Button","useTranslation","jsx","jsxs","useTranslation","useState","Button","Menu","ChevronDown","jsx","jsxs","useLocation","useState","useState"]}
|
package/package.json
CHANGED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { DashboardThemePreset, DashboardTheme } from '../types/index.js';
|
|
2
|
-
import 'lucide-react';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Dashboard Theme System
|
|
6
|
-
*
|
|
7
|
-
* Provides theme configuration and presets for the dashboard layout.
|
|
8
|
-
* Themes are applied via CSS variables, allowing runtime customization.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Default dashboard theme (light mode)
|
|
13
|
-
*/
|
|
14
|
-
declare const DEFAULT_DASHBOARD_THEME: DashboardTheme;
|
|
15
|
-
/**
|
|
16
|
-
* Default dashboard theme (dark mode)
|
|
17
|
-
*/
|
|
18
|
-
declare const DEFAULT_DASHBOARD_THEME_DARK: DashboardTheme;
|
|
19
|
-
/**
|
|
20
|
-
* Available theme presets
|
|
21
|
-
*/
|
|
22
|
-
declare const DASHBOARD_THEME_PRESETS: DashboardThemePreset[];
|
|
23
|
-
/**
|
|
24
|
-
* Apply theme to document root via CSS variables
|
|
25
|
-
*
|
|
26
|
-
* @param theme - Theme configuration to apply
|
|
27
|
-
*/
|
|
28
|
-
declare function applyDashboardTheme(theme: DashboardTheme): void;
|
|
29
|
-
/**
|
|
30
|
-
* Get theme preset by name
|
|
31
|
-
*
|
|
32
|
-
* @param name - Preset name
|
|
33
|
-
* @returns Theme preset or undefined
|
|
34
|
-
*/
|
|
35
|
-
declare function getDashboardThemePreset(name: string): DashboardThemePreset | undefined;
|
|
36
|
-
/**
|
|
37
|
-
* Merge custom theme with default theme
|
|
38
|
-
*
|
|
39
|
-
* @param customTheme - Custom theme configuration
|
|
40
|
-
* @param dark - Whether to use dark mode base
|
|
41
|
-
* @returns Merged theme configuration
|
|
42
|
-
*/
|
|
43
|
-
declare function mergeDashboardTheme(customTheme: Partial<DashboardTheme>, dark?: boolean): DashboardTheme;
|
|
44
|
-
|
|
45
|
-
export { DASHBOARD_THEME_PRESETS, DEFAULT_DASHBOARD_THEME, DEFAULT_DASHBOARD_THEME_DARK, applyDashboardTheme, getDashboardThemePreset, mergeDashboardTheme };
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
// src/domain/theme/index.ts
|
|
4
|
-
var DEFAULT_DASHBOARD_THEME = {
|
|
5
|
-
primary: "hsl(222.2 47.4% 11.2%)",
|
|
6
|
-
secondary: "hsl(217.2 32.6% 17.5%)",
|
|
7
|
-
sidebarBackground: "hsl(222.2 47.4% 11.2%)",
|
|
8
|
-
sidebarForeground: "hsl(210 40% 98%)",
|
|
9
|
-
sidebarBorder: "hsl(217.2 32.6% 17.5%)",
|
|
10
|
-
headerBackground: "hsla(0, 0%, 100%, 0.8)",
|
|
11
|
-
background: "hsl(0 0% 100%)",
|
|
12
|
-
foreground: "hsl(222.2 84% 4.9%)",
|
|
13
|
-
border: "hsl(214.3 31.8% 91.4%)",
|
|
14
|
-
accent: "hsl(217.2 91.2% 59.8%)",
|
|
15
|
-
accentForeground: "hsl(0 0% 100%)",
|
|
16
|
-
destructive: "hsl(0 84.2% 60.2%)",
|
|
17
|
-
destructiveForeground: "hsl(0 0% 98%)",
|
|
18
|
-
muted: "hsl(210 40% 96.1%)",
|
|
19
|
-
mutedForeground: "hsl(215.4 16.3% 46.9%)",
|
|
20
|
-
card: "hsl(0 0% 100%)",
|
|
21
|
-
cardForeground: "hsl(222.2 84% 4.9%)",
|
|
22
|
-
popover: "hsl(0 0% 100%)",
|
|
23
|
-
popoverForeground: "hsl(222.2 84% 4.9%)",
|
|
24
|
-
radius: "0.5rem"
|
|
25
|
-
};
|
|
26
|
-
var DEFAULT_DASHBOARD_THEME_DARK = {
|
|
27
|
-
primary: "hsl(217.2 91.2% 59.8%)",
|
|
28
|
-
secondary: "hsl(217.2 32.6% 17.5%)",
|
|
29
|
-
sidebarBackground: "hsl(222.2 47.4% 11.2%)",
|
|
30
|
-
sidebarForeground: "hsl(210 40% 98%)",
|
|
31
|
-
sidebarBorder: "hsl(217.2 32.6% 17.5%)",
|
|
32
|
-
headerBackground: "hsla(222.2 47.4% 11.2%, 0.8)",
|
|
33
|
-
background: "hsl(222.2 84% 4.9%)",
|
|
34
|
-
foreground: "hsl(210 40% 98%)",
|
|
35
|
-
border: "hsl(217.2 32.6% 17.5%)",
|
|
36
|
-
accent: "hsl(217.2 91.2% 59.8%)",
|
|
37
|
-
accentForeground: "hsl(0 0% 100%)",
|
|
38
|
-
destructive: "hsl(0 62.8% 30.6%)",
|
|
39
|
-
destructiveForeground: "hsl(0 0% 98%)",
|
|
40
|
-
muted: "hsl(217.2 32.6% 17.5%)",
|
|
41
|
-
mutedForeground: "hsl(215 20.2% 65.1%)",
|
|
42
|
-
card: "hsl(222.2 84% 4.9%)",
|
|
43
|
-
cardForeground: "hsl(210 40% 98%)",
|
|
44
|
-
popover: "hsl(222.2 84% 4.9%)",
|
|
45
|
-
popoverForeground: "hsl(210 40% 98%)",
|
|
46
|
-
radius: "0.5rem"
|
|
47
|
-
};
|
|
48
|
-
var DASHBOARD_THEME_PRESETS = [
|
|
49
|
-
{
|
|
50
|
-
name: "default",
|
|
51
|
-
theme: DEFAULT_DASHBOARD_THEME,
|
|
52
|
-
dark: false
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: "default-dark",
|
|
56
|
-
theme: DEFAULT_DASHBOARD_THEME_DARK,
|
|
57
|
-
dark: true
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: "blue",
|
|
61
|
-
theme: {
|
|
62
|
-
...DEFAULT_DASHBOARD_THEME,
|
|
63
|
-
primary: "hsl(221.2 83.2% 53.3%)",
|
|
64
|
-
accent: "hsl(221.2 83.2% 53.3%)"
|
|
65
|
-
},
|
|
66
|
-
dark: false
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: "blue-dark",
|
|
70
|
-
theme: {
|
|
71
|
-
...DEFAULT_DASHBOARD_THEME_DARK,
|
|
72
|
-
primary: "hsl(221.2 83.2% 53.3%)",
|
|
73
|
-
accent: "hsl(221.2 83.2% 53.3%)"
|
|
74
|
-
},
|
|
75
|
-
dark: true
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
name: "purple",
|
|
79
|
-
theme: {
|
|
80
|
-
...DEFAULT_DASHBOARD_THEME,
|
|
81
|
-
primary: "hsl(271.5 81.3% 55.9%)",
|
|
82
|
-
accent: "hsl(271.5 81.3% 55.9%)"
|
|
83
|
-
},
|
|
84
|
-
dark: false
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
name: "purple-dark",
|
|
88
|
-
theme: {
|
|
89
|
-
...DEFAULT_DASHBOARD_THEME_DARK,
|
|
90
|
-
primary: "hsl(271.5 81.3% 55.9%)",
|
|
91
|
-
accent: "hsl(271.5 81.3% 55.9%)"
|
|
92
|
-
},
|
|
93
|
-
dark: true
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: "green",
|
|
97
|
-
theme: {
|
|
98
|
-
...DEFAULT_DASHBOARD_THEME,
|
|
99
|
-
primary: "hsl(142.1 76.2% 36.3%)",
|
|
100
|
-
accent: "hsl(142.1 76.2% 36.3%)"
|
|
101
|
-
},
|
|
102
|
-
dark: false
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
name: "green-dark",
|
|
106
|
-
theme: {
|
|
107
|
-
...DEFAULT_DASHBOARD_THEME_DARK,
|
|
108
|
-
primary: "hsl(142.1 76.2% 36.3%)",
|
|
109
|
-
accent: "hsl(142.1 76.2% 36.3%)"
|
|
110
|
-
},
|
|
111
|
-
dark: true
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
name: "orange",
|
|
115
|
-
theme: {
|
|
116
|
-
...DEFAULT_DASHBOARD_THEME,
|
|
117
|
-
primary: "hsl(24.6 95% 53.1%)",
|
|
118
|
-
accent: "hsl(24.6 95% 53.1%)"
|
|
119
|
-
},
|
|
120
|
-
dark: false
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
name: "orange-dark",
|
|
124
|
-
theme: {
|
|
125
|
-
...DEFAULT_DASHBOARD_THEME_DARK,
|
|
126
|
-
primary: "hsl(24.6 95% 53.1%)",
|
|
127
|
-
accent: "hsl(24.6 95% 53.1%)"
|
|
128
|
-
},
|
|
129
|
-
dark: true
|
|
130
|
-
}
|
|
131
|
-
];
|
|
132
|
-
function applyDashboardTheme(theme) {
|
|
133
|
-
if (typeof document === "undefined") return;
|
|
134
|
-
const root = document.documentElement;
|
|
135
|
-
const cssVars = {
|
|
136
|
-
"--primary": theme.primary,
|
|
137
|
-
"--secondary": theme.secondary,
|
|
138
|
-
"--sidebar": theme.sidebarBackground,
|
|
139
|
-
"--sidebar-foreground": theme.sidebarForeground,
|
|
140
|
-
"--sidebar-border": theme.sidebarBorder,
|
|
141
|
-
"--background": theme.background,
|
|
142
|
-
"--foreground": theme.foreground,
|
|
143
|
-
"--border": theme.border,
|
|
144
|
-
"--accent": theme.accent,
|
|
145
|
-
"--accent-foreground": theme.accentForeground,
|
|
146
|
-
"--destructive": theme.destructive,
|
|
147
|
-
"--destructive-foreground": theme.destructiveForeground,
|
|
148
|
-
"--muted": theme.muted,
|
|
149
|
-
"--muted-foreground": theme.mutedForeground,
|
|
150
|
-
"--card": theme.card,
|
|
151
|
-
"--card-foreground": theme.cardForeground,
|
|
152
|
-
"--popover": theme.popover,
|
|
153
|
-
"--popover-foreground": theme.popoverForeground,
|
|
154
|
-
"--radius": theme.radius
|
|
155
|
-
};
|
|
156
|
-
Object.entries(cssVars).forEach(([key, value]) => {
|
|
157
|
-
if (value) {
|
|
158
|
-
root.style.setProperty(key, value);
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
function getDashboardThemePreset(name) {
|
|
163
|
-
return DASHBOARD_THEME_PRESETS.find((preset) => preset.name === name);
|
|
164
|
-
}
|
|
165
|
-
function mergeDashboardTheme(customTheme, dark = false) {
|
|
166
|
-
const baseTheme = dark ? DEFAULT_DASHBOARD_THEME_DARK : DEFAULT_DASHBOARD_THEME;
|
|
167
|
-
return {
|
|
168
|
-
...baseTheme,
|
|
169
|
-
...customTheme
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
export {
|
|
173
|
-
DASHBOARD_THEME_PRESETS,
|
|
174
|
-
DEFAULT_DASHBOARD_THEME,
|
|
175
|
-
DEFAULT_DASHBOARD_THEME_DARK,
|
|
176
|
-
applyDashboardTheme,
|
|
177
|
-
getDashboardThemePreset,
|
|
178
|
-
mergeDashboardTheme
|
|
179
|
-
};
|
|
180
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/domain/theme/index.ts"],"sourcesContent":["/**\n * Dashboard Theme System\n *\n * Provides theme configuration and presets for the dashboard layout.\n * Themes are applied via CSS variables, allowing runtime customization.\n */\n\nimport type { DashboardTheme, DashboardThemePreset } from \"../types\";\n\n// =============================================================================\n// Default Theme\n// =============================================================================\n\n/**\n * Default dashboard theme (light mode)\n */\nexport const DEFAULT_DASHBOARD_THEME: DashboardTheme = {\n primary: \"hsl(222.2 47.4% 11.2%)\",\n secondary: \"hsl(217.2 32.6% 17.5%)\",\n sidebarBackground: \"hsl(222.2 47.4% 11.2%)\",\n sidebarForeground: \"hsl(210 40% 98%)\",\n sidebarBorder: \"hsl(217.2 32.6% 17.5%)\",\n headerBackground: \"hsla(0, 0%, 100%, 0.8)\",\n background: \"hsl(0 0% 100%)\",\n foreground: \"hsl(222.2 84% 4.9%)\",\n border: \"hsl(214.3 31.8% 91.4%)\",\n accent: \"hsl(217.2 91.2% 59.8%)\",\n accentForeground: \"hsl(0 0% 100%)\",\n destructive: \"hsl(0 84.2% 60.2%)\",\n destructiveForeground: \"hsl(0 0% 98%)\",\n muted: \"hsl(210 40% 96.1%)\",\n mutedForeground: \"hsl(215.4 16.3% 46.9%)\",\n card: \"hsl(0 0% 100%)\",\n cardForeground: \"hsl(222.2 84% 4.9%)\",\n popover: \"hsl(0 0% 100%)\",\n popoverForeground: \"hsl(222.2 84% 4.9%)\",\n radius: \"0.5rem\",\n};\n\n/**\n * Default dashboard theme (dark mode)\n */\nexport const DEFAULT_DASHBOARD_THEME_DARK: DashboardTheme = {\n primary: \"hsl(217.2 91.2% 59.8%)\",\n secondary: \"hsl(217.2 32.6% 17.5%)\",\n sidebarBackground: \"hsl(222.2 47.4% 11.2%)\",\n sidebarForeground: \"hsl(210 40% 98%)\",\n sidebarBorder: \"hsl(217.2 32.6% 17.5%)\",\n headerBackground: \"hsla(222.2 47.4% 11.2%, 0.8)\",\n background: \"hsl(222.2 84% 4.9%)\",\n foreground: \"hsl(210 40% 98%)\",\n border: \"hsl(217.2 32.6% 17.5%)\",\n accent: \"hsl(217.2 91.2% 59.8%)\",\n accentForeground: \"hsl(0 0% 100%)\",\n destructive: \"hsl(0 62.8% 30.6%)\",\n destructiveForeground: \"hsl(0 0% 98%)\",\n muted: \"hsl(217.2 32.6% 17.5%)\",\n mutedForeground: \"hsl(215 20.2% 65.1%)\",\n card: \"hsl(222.2 84% 4.9%)\",\n cardForeground: \"hsl(210 40% 98%)\",\n popover: \"hsl(222.2 84% 4.9%)\",\n popoverForeground: \"hsl(210 40% 98%)\",\n radius: \"0.5rem\",\n};\n\n// =============================================================================\n// Theme Presets\n// =============================================================================\n\n/**\n * Available theme presets\n */\nexport const DASHBOARD_THEME_PRESETS: DashboardThemePreset[] = [\n {\n name: \"default\",\n theme: DEFAULT_DASHBOARD_THEME,\n dark: false,\n },\n {\n name: \"default-dark\",\n theme: DEFAULT_DASHBOARD_THEME_DARK,\n dark: true,\n },\n {\n name: \"blue\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(221.2 83.2% 53.3%)\",\n accent: \"hsl(221.2 83.2% 53.3%)\",\n },\n dark: false,\n },\n {\n name: \"blue-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(221.2 83.2% 53.3%)\",\n accent: \"hsl(221.2 83.2% 53.3%)\",\n },\n dark: true,\n },\n {\n name: \"purple\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(271.5 81.3% 55.9%)\",\n accent: \"hsl(271.5 81.3% 55.9%)\",\n },\n dark: false,\n },\n {\n name: \"purple-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(271.5 81.3% 55.9%)\",\n accent: \"hsl(271.5 81.3% 55.9%)\",\n },\n dark: true,\n },\n {\n name: \"green\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(142.1 76.2% 36.3%)\",\n accent: \"hsl(142.1 76.2% 36.3%)\",\n },\n dark: false,\n },\n {\n name: \"green-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(142.1 76.2% 36.3%)\",\n accent: \"hsl(142.1 76.2% 36.3%)\",\n },\n dark: true,\n },\n {\n name: \"orange\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME,\n primary: \"hsl(24.6 95% 53.1%)\",\n accent: \"hsl(24.6 95% 53.1%)\",\n },\n dark: false,\n },\n {\n name: \"orange-dark\",\n theme: {\n ...DEFAULT_DASHBOARD_THEME_DARK,\n primary: \"hsl(24.6 95% 53.1%)\",\n accent: \"hsl(24.6 95% 53.1%)\",\n },\n dark: true,\n },\n];\n\n// =============================================================================\n// Theme Utilities\n// =============================================================================\n\n/**\n * Apply theme to document root via CSS variables\n *\n * @param theme - Theme configuration to apply\n */\nexport function applyDashboardTheme(theme: DashboardTheme): void {\n if (typeof document === \"undefined\") return;\n\n const root = document.documentElement;\n\n // Map theme keys to CSS variable names\n const cssVars: Record<string, string | undefined> = {\n \"--primary\": theme.primary,\n \"--secondary\": theme.secondary,\n \"--sidebar\": theme.sidebarBackground,\n \"--sidebar-foreground\": theme.sidebarForeground,\n \"--sidebar-border\": theme.sidebarBorder,\n \"--background\": theme.background,\n \"--foreground\": theme.foreground,\n \"--border\": theme.border,\n \"--accent\": theme.accent,\n \"--accent-foreground\": theme.accentForeground,\n \"--destructive\": theme.destructive,\n \"--destructive-foreground\": theme.destructiveForeground,\n \"--muted\": theme.muted,\n \"--muted-foreground\": theme.mutedForeground,\n \"--card\": theme.card,\n \"--card-foreground\": theme.cardForeground,\n \"--popover\": theme.popover,\n \"--popover-foreground\": theme.popoverForeground,\n \"--radius\": theme.radius,\n };\n\n // Apply CSS variables\n Object.entries(cssVars).forEach(([key, value]) => {\n if (value) {\n root.style.setProperty(key, value);\n }\n });\n}\n\n/**\n * Get theme preset by name\n *\n * @param name - Preset name\n * @returns Theme preset or undefined\n */\nexport function getDashboardThemePreset(name: string): DashboardThemePreset | undefined {\n return DASHBOARD_THEME_PRESETS.find((preset) => preset.name === name);\n}\n\n/**\n * Merge custom theme with default theme\n *\n * @param customTheme - Custom theme configuration\n * @param dark - Whether to use dark mode base\n * @returns Merged theme configuration\n */\nexport function mergeDashboardTheme(\n customTheme: Partial<DashboardTheme>,\n dark = false\n): DashboardTheme {\n const baseTheme = dark ? DEFAULT_DASHBOARD_THEME_DARK : DEFAULT_DASHBOARD_THEME;\n return {\n ...baseTheme,\n ...customTheme,\n } as DashboardTheme;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { LucideIcon } from 'lucide-react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Dashboard Types
|
|
5
|
-
*
|
|
6
|
-
* Core type definitions for the dashboard layout system
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Single sidebar menu item
|
|
11
|
-
*/
|
|
12
|
-
interface SidebarItem {
|
|
13
|
-
/** Display label (can be i18n key) */
|
|
14
|
-
label: string;
|
|
15
|
-
/** Icon from lucide-react */
|
|
16
|
-
icon: LucideIcon;
|
|
17
|
-
/** Route path */
|
|
18
|
-
path: string;
|
|
19
|
-
/** Filter by app type (optional) */
|
|
20
|
-
requiredApp?: 'mobile' | 'web';
|
|
21
|
-
/** Enable/disable this item (default: true) */
|
|
22
|
-
enabled?: boolean;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Group of sidebar items with a title
|
|
26
|
-
*/
|
|
27
|
-
interface SidebarGroup {
|
|
28
|
-
/** Group title (can be i18n key) */
|
|
29
|
-
title: string;
|
|
30
|
-
/** Items in this group */
|
|
31
|
-
items: SidebarItem[];
|
|
32
|
-
/** Optional: Route to title mapping for page headers */
|
|
33
|
-
titleMap?: Record<string, string>;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Dashboard header props
|
|
37
|
-
*/
|
|
38
|
-
interface DashboardHeaderProps {
|
|
39
|
-
/** Whether sidebar is collapsed */
|
|
40
|
-
collapsed: boolean;
|
|
41
|
-
/** Toggle sidebar collapsed state */
|
|
42
|
-
setCollapsed: (collapsed: boolean) => void;
|
|
43
|
-
/** Toggle mobile menu open state */
|
|
44
|
-
setMobileOpen: (open: boolean) => void;
|
|
45
|
-
/** Current page title */
|
|
46
|
-
title: string;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Dashboard sidebar props
|
|
50
|
-
*/
|
|
51
|
-
interface DashboardSidebarProps {
|
|
52
|
-
/** Whether sidebar is collapsed */
|
|
53
|
-
collapsed: boolean;
|
|
54
|
-
/** Toggle sidebar collapsed state */
|
|
55
|
-
setCollapsed: (collapsed: boolean) => void;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Dashboard layout configuration
|
|
59
|
-
*/
|
|
60
|
-
interface DashboardLayoutConfig {
|
|
61
|
-
/** Sidebar groups */
|
|
62
|
-
sidebarGroups: SidebarGroup[];
|
|
63
|
-
/** Extra title mappings for routes */
|
|
64
|
-
extraTitleMap?: Record<string, string>;
|
|
65
|
-
/** Brand name */
|
|
66
|
-
brandName?: string;
|
|
67
|
-
/** Brand tagline */
|
|
68
|
-
brandTagline?: string;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Dashboard theme configuration
|
|
72
|
-
* Extends CSS variables for customization
|
|
73
|
-
*/
|
|
74
|
-
interface DashboardTheme {
|
|
75
|
-
/** Primary color (CSS variable compatible) */
|
|
76
|
-
primary?: string;
|
|
77
|
-
/** Secondary color */
|
|
78
|
-
secondary?: string;
|
|
79
|
-
/** Sidebar background */
|
|
80
|
-
sidebarBackground?: string;
|
|
81
|
-
/** Sidebar foreground */
|
|
82
|
-
sidebarForeground?: string;
|
|
83
|
-
/** Sidebar border */
|
|
84
|
-
sidebarBorder?: string;
|
|
85
|
-
/** Header background */
|
|
86
|
-
headerBackground?: string;
|
|
87
|
-
/** Background color */
|
|
88
|
-
background?: string;
|
|
89
|
-
/** Foreground color */
|
|
90
|
-
foreground?: string;
|
|
91
|
-
/** Border color */
|
|
92
|
-
border?: string;
|
|
93
|
-
/** Accent color */
|
|
94
|
-
accent?: string;
|
|
95
|
-
/** Accent foreground */
|
|
96
|
-
accentForeground?: string;
|
|
97
|
-
/** Destructive color */
|
|
98
|
-
destructive?: string;
|
|
99
|
-
/** Destructive foreground */
|
|
100
|
-
destructiveForeground?: string;
|
|
101
|
-
/** Muted background */
|
|
102
|
-
muted?: string;
|
|
103
|
-
/** Muted foreground */
|
|
104
|
-
mutedForeground?: string;
|
|
105
|
-
/** Card background */
|
|
106
|
-
card?: string;
|
|
107
|
-
/** Card foreground */
|
|
108
|
-
cardForeground?: string;
|
|
109
|
-
/** Popover background */
|
|
110
|
-
popover?: string;
|
|
111
|
-
/** Popover foreground */
|
|
112
|
-
popoverForeground?: string;
|
|
113
|
-
/** Radius (border-radius) */
|
|
114
|
-
radius?: string;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Theme preset for quick setup
|
|
118
|
-
*/
|
|
119
|
-
interface DashboardThemePreset {
|
|
120
|
-
/** Preset name */
|
|
121
|
-
name: string;
|
|
122
|
-
/** Theme configuration */
|
|
123
|
-
theme: DashboardTheme;
|
|
124
|
-
/** Whether this is a dark theme */
|
|
125
|
-
dark?: boolean;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Notification item
|
|
129
|
-
*/
|
|
130
|
-
interface DashboardNotification {
|
|
131
|
-
/** Unique ID */
|
|
132
|
-
id: string;
|
|
133
|
-
/** Notification text */
|
|
134
|
-
text: string;
|
|
135
|
-
/** Whether notification is read */
|
|
136
|
-
read: boolean;
|
|
137
|
-
/** Creation timestamp */
|
|
138
|
-
createdAt: Date | string | number;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* User profile info for header
|
|
142
|
-
*/
|
|
143
|
-
interface DashboardUser {
|
|
144
|
-
/** User ID */
|
|
145
|
-
id: string;
|
|
146
|
-
/** Display name */
|
|
147
|
-
name?: string;
|
|
148
|
-
/** Email address */
|
|
149
|
-
email?: string;
|
|
150
|
-
/** Avatar URL */
|
|
151
|
-
avatar?: string;
|
|
152
|
-
/** Whether user has mobile app access */
|
|
153
|
-
hasMobileApp?: boolean;
|
|
154
|
-
/** Whether user has web app access */
|
|
155
|
-
hasWebApp?: boolean;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Navigation item for user menu
|
|
159
|
-
*/
|
|
160
|
-
interface UserNavMenuItem {
|
|
161
|
-
/** Display label */
|
|
162
|
-
label: string;
|
|
163
|
-
/** Icon component */
|
|
164
|
-
icon: React.ComponentType<{
|
|
165
|
-
className?: string;
|
|
166
|
-
}>;
|
|
167
|
-
/** Route path */
|
|
168
|
-
path: string;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export type { DashboardHeaderProps, DashboardLayoutConfig, DashboardNotification, DashboardSidebarProps, DashboardTheme, DashboardThemePreset, DashboardUser, SidebarGroup, SidebarItem, UserNavMenuItem };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
import { DashboardNotification } from '../../domain/types/index.js';
|
|
3
|
-
import 'lucide-react';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Use Notifications Hook
|
|
7
|
-
*
|
|
8
|
-
* Manages notification state and actions
|
|
9
|
-
*
|
|
10
|
-
* @param initialNotifications - Initial notification list
|
|
11
|
-
* @returns Notification state and actions
|
|
12
|
-
*/
|
|
13
|
-
declare function useNotifications(initialNotifications?: DashboardNotification[]): {
|
|
14
|
-
notifications: DashboardNotification[];
|
|
15
|
-
markAllRead: () => void;
|
|
16
|
-
dismiss: (id: string) => void;
|
|
17
|
-
add: (notification: Omit<DashboardNotification, "id">) => void;
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Use Sidebar Hook
|
|
21
|
-
*
|
|
22
|
-
* Manages sidebar state
|
|
23
|
-
*
|
|
24
|
-
* @returns Sidebar state and actions
|
|
25
|
-
*/
|
|
26
|
-
declare function useSidebar(initialCollapsed?: boolean): {
|
|
27
|
-
collapsed: boolean;
|
|
28
|
-
setCollapsed: react.Dispatch<react.SetStateAction<boolean>>;
|
|
29
|
-
toggle: () => void;
|
|
30
|
-
mobileOpen: boolean;
|
|
31
|
-
setMobileOpen: react.Dispatch<react.SetStateAction<boolean>>;
|
|
32
|
-
openMobile: () => void;
|
|
33
|
-
closeMobile: () => void;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export { useNotifications, useSidebar };
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
// src/infrastructure/hooks/index.ts
|
|
4
|
-
import { useState, useCallback } from "react";
|
|
5
|
-
function useNotifications(initialNotifications = []) {
|
|
6
|
-
const [notifications, setNotifications] = useState(initialNotifications);
|
|
7
|
-
const markAllRead = useCallback(() => {
|
|
8
|
-
setNotifications(
|
|
9
|
-
(prev) => prev.map((n) => ({ ...n, read: true }))
|
|
10
|
-
);
|
|
11
|
-
}, []);
|
|
12
|
-
const dismiss = useCallback((id) => {
|
|
13
|
-
setNotifications((prev) => prev.filter((n) => n.id !== id));
|
|
14
|
-
}, []);
|
|
15
|
-
const add = useCallback((notification) => {
|
|
16
|
-
const newNotification = {
|
|
17
|
-
...notification,
|
|
18
|
-
id: crypto.randomUUID(),
|
|
19
|
-
read: false,
|
|
20
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
21
|
-
};
|
|
22
|
-
setNotifications((prev) => [newNotification, ...prev]);
|
|
23
|
-
}, []);
|
|
24
|
-
return {
|
|
25
|
-
notifications,
|
|
26
|
-
markAllRead,
|
|
27
|
-
dismiss,
|
|
28
|
-
add
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function useSidebar(initialCollapsed = false) {
|
|
32
|
-
const [collapsed, setCollapsed] = useState(initialCollapsed);
|
|
33
|
-
const [mobileOpen, setMobileOpen] = useState(false);
|
|
34
|
-
const toggle = useCallback(() => {
|
|
35
|
-
setCollapsed((prev) => !prev);
|
|
36
|
-
}, []);
|
|
37
|
-
const openMobile = useCallback(() => {
|
|
38
|
-
setMobileOpen(true);
|
|
39
|
-
}, []);
|
|
40
|
-
const closeMobile = useCallback(() => {
|
|
41
|
-
setMobileOpen(false);
|
|
42
|
-
}, []);
|
|
43
|
-
return {
|
|
44
|
-
collapsed,
|
|
45
|
-
setCollapsed,
|
|
46
|
-
toggle,
|
|
47
|
-
mobileOpen,
|
|
48
|
-
setMobileOpen,
|
|
49
|
-
openMobile,
|
|
50
|
-
closeMobile
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
export {
|
|
54
|
-
useNotifications,
|
|
55
|
-
useSidebar
|
|
56
|
-
};
|
|
57
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/infrastructure/hooks/index.ts"],"sourcesContent":["/**\n * Dashboard Hooks\n *\n * Custom React hooks for dashboard functionality\n */\n\nimport { useState, useCallback } from \"react\";\nimport type { DashboardNotification } from \"../../domain/types\";\n\n/**\n * Use Notifications Hook\n *\n * Manages notification state and actions\n *\n * @param initialNotifications - Initial notification list\n * @returns Notification state and actions\n */\nexport function useNotifications(initialNotifications: DashboardNotification[] = []) {\n const [notifications, setNotifications] = useState<DashboardNotification[]>(initialNotifications);\n\n const markAllRead = useCallback(() => {\n setNotifications((prev) =>\n prev.map((n) => ({ ...n, read: true }))\n );\n }, []);\n\n const dismiss = useCallback((id: string) => {\n setNotifications((prev) => prev.filter((n) => n.id !== id));\n }, []);\n\n const add = useCallback((notification: Omit<DashboardNotification, \"id\">) => {\n const newNotification: DashboardNotification = {\n ...notification,\n id: crypto.randomUUID(),\n read: false,\n createdAt: new Date(),\n };\n setNotifications((prev) => [newNotification, ...prev]);\n }, []);\n\n return {\n notifications,\n markAllRead,\n dismiss,\n add,\n };\n}\n\n/**\n * Use Sidebar Hook\n *\n * Manages sidebar state\n *\n * @returns Sidebar state and actions\n */\nexport function useSidebar(initialCollapsed = false) {\n const [collapsed, setCollapsed] = useState(initialCollapsed);\n const [mobileOpen, setMobileOpen] = useState(false);\n\n const toggle = useCallback(() => {\n setCollapsed((prev) => !prev);\n }, []);\n\n const openMobile = useCallback(() => {\n setMobileOpen(true);\n }, []);\n\n const closeMobile = useCallback(() => {\n setMobileOpen(false);\n }, []);\n\n return {\n collapsed,\n setCollapsed,\n toggle,\n mobileOpen,\n setMobileOpen: setMobileOpen,\n openMobile,\n closeMobile,\n };\n}\n"],"mappings":";;;AAMA,SAAS,UAAU,mBAAmB;AAW/B,SAAS,iBAAiB,uBAAgD,CAAC,GAAG;AACnF,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAkC,oBAAoB;AAEhG,QAAM,cAAc,YAAY,MAAM;AACpC;AAAA,MAAiB,CAAC,SAChB,KAAK,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,KAAK,EAAE;AAAA,IACxC;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,YAAY,CAAC,OAAe;AAC1C,qBAAiB,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;AAAA,EAC5D,GAAG,CAAC,CAAC;AAEL,QAAM,MAAM,YAAY,CAAC,iBAAoD;AAC3E,UAAM,kBAAyC;AAAA,MAC7C,GAAG;AAAA,MACH,IAAI,OAAO,WAAW;AAAA,MACtB,MAAM;AAAA,MACN,WAAW,oBAAI,KAAK;AAAA,IACtB;AACA,qBAAiB,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC;AAAA,EACvD,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AASO,SAAS,WAAW,mBAAmB,OAAO;AACnD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,gBAAgB;AAC3D,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAElD,QAAM,SAAS,YAAY,MAAM;AAC/B,iBAAa,CAAC,SAAS,CAAC,IAAI;AAAA,EAC9B,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,YAAY,MAAM;AACnC,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,YAAY,MAAM;AACpC,kBAAc,KAAK;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dashboard Utilities
|
|
3
|
-
*
|
|
4
|
-
* Utility functions for dashboard operations
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Format notification timestamp to relative time
|
|
8
|
-
*
|
|
9
|
-
* @param createdAt - Notification creation timestamp
|
|
10
|
-
* @param t - i18n translation function
|
|
11
|
-
* @returns Formatted time string
|
|
12
|
-
*/
|
|
13
|
-
declare function formatNotificationTime(createdAt: Date | string | number, t: (key: string, params?: Record<string, unknown>) => string): string;
|
|
14
|
-
/**
|
|
15
|
-
* Check if a path is active
|
|
16
|
-
*
|
|
17
|
-
* @param currentPath - Current location pathname
|
|
18
|
-
* @param targetPath - Target path to check
|
|
19
|
-
* @returns True if paths match
|
|
20
|
-
*/
|
|
21
|
-
declare function isPathActive(currentPath: string, targetPath: string): boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Get page title from sidebar configuration
|
|
24
|
-
*
|
|
25
|
-
* @param pathname - Current pathname
|
|
26
|
-
* @param sidebarGroups - Sidebar groups configuration
|
|
27
|
-
* @param extraTitleMap - Extra title mappings
|
|
28
|
-
* @returns Page title or null
|
|
29
|
-
*/
|
|
30
|
-
declare function getPageTitle(pathname: string, sidebarGroups: Array<{
|
|
31
|
-
items: Array<{
|
|
32
|
-
path: string;
|
|
33
|
-
label: string;
|
|
34
|
-
}>;
|
|
35
|
-
}>, extraTitleMap?: Record<string, string>): string | null;
|
|
36
|
-
/**
|
|
37
|
-
* Filter sidebar items by app type and enabled status
|
|
38
|
-
*
|
|
39
|
-
* @param items - Sidebar items to filter
|
|
40
|
-
* @param user - Current user object
|
|
41
|
-
* @returns Filtered items
|
|
42
|
-
*/
|
|
43
|
-
declare function filterSidebarItems<T extends {
|
|
44
|
-
enabled?: boolean;
|
|
45
|
-
requiredApp?: "mobile" | "web";
|
|
46
|
-
}>(items: T[], user?: {
|
|
47
|
-
hasMobileApp?: boolean;
|
|
48
|
-
hasWebApp?: boolean;
|
|
49
|
-
}): T[];
|
|
50
|
-
/**
|
|
51
|
-
* Generate notification ID
|
|
52
|
-
*
|
|
53
|
-
* @returns Unique notification ID
|
|
54
|
-
*/
|
|
55
|
-
declare function generateNotificationId(): string;
|
|
56
|
-
|
|
57
|
-
export { filterSidebarItems, formatNotificationTime, generateNotificationId, getPageTitle, isPathActive };
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
// src/infrastructure/utils/index.ts
|
|
4
|
-
function formatNotificationTime(createdAt, t) {
|
|
5
|
-
const date = new Date(createdAt);
|
|
6
|
-
const secs = Math.floor((Date.now() - date.getTime()) / 1e3);
|
|
7
|
-
if (secs < 60) return t("dashboard.activityFeed.times.justNow");
|
|
8
|
-
if (secs < 3600) return t("dashboard.activityFeed.times.minutesAgo", { val: Math.floor(secs / 60) });
|
|
9
|
-
if (secs < 86400) return t("dashboard.activityFeed.times.hoursAgo", { val: Math.floor(secs / 3600) });
|
|
10
|
-
return t("dashboard.activityFeed.times.daysAgo", { val: Math.floor(secs / 86400) });
|
|
11
|
-
}
|
|
12
|
-
function isPathActive(currentPath, targetPath) {
|
|
13
|
-
return currentPath === targetPath;
|
|
14
|
-
}
|
|
15
|
-
function getPageTitle(pathname, sidebarGroups, extraTitleMap) {
|
|
16
|
-
for (const group of sidebarGroups) {
|
|
17
|
-
const item = group.items.find((i) => i.path === pathname);
|
|
18
|
-
if (item) return item.label;
|
|
19
|
-
}
|
|
20
|
-
if (extraTitleMap?.[pathname]) {
|
|
21
|
-
return extraTitleMap[pathname];
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
function filterSidebarItems(items, user) {
|
|
26
|
-
return items.filter((item) => {
|
|
27
|
-
if (item.enabled === false) return false;
|
|
28
|
-
if (!item.requiredApp) return true;
|
|
29
|
-
if (item.requiredApp === "mobile") return user?.hasMobileApp ?? false;
|
|
30
|
-
if (item.requiredApp === "web") return user?.hasWebApp ?? false;
|
|
31
|
-
return true;
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
function generateNotificationId() {
|
|
35
|
-
return crypto.randomUUID();
|
|
36
|
-
}
|
|
37
|
-
export {
|
|
38
|
-
filterSidebarItems,
|
|
39
|
-
formatNotificationTime,
|
|
40
|
-
generateNotificationId,
|
|
41
|
-
getPageTitle,
|
|
42
|
-
isPathActive
|
|
43
|
-
};
|
|
44
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/infrastructure/utils/index.ts"],"sourcesContent":["/**\n * Dashboard Utilities\n *\n * Utility functions for dashboard operations\n */\n\n/**\n * Format notification timestamp to relative time\n *\n * @param createdAt - Notification creation timestamp\n * @param t - i18n translation function\n * @returns Formatted time string\n */\nexport function formatNotificationTime(\n createdAt: Date | string | number,\n t: (key: string, params?: Record<string, unknown>) => string\n): string {\n const date = new Date(createdAt);\n const secs = Math.floor((Date.now() - date.getTime()) / 1000);\n\n if (secs < 60) return t(\"dashboard.activityFeed.times.justNow\");\n if (secs < 3600) return t(\"dashboard.activityFeed.times.minutesAgo\", { val: Math.floor(secs / 60) });\n if (secs < 86400) return t(\"dashboard.activityFeed.times.hoursAgo\", { val: Math.floor(secs / 3600) });\n return t(\"dashboard.activityFeed.times.daysAgo\", { val: Math.floor(secs / 86400) });\n}\n\n/**\n * Check if a path is active\n *\n * @param currentPath - Current location pathname\n * @param targetPath - Target path to check\n * @returns True if paths match\n */\nexport function isPathActive(currentPath: string, targetPath: string): boolean {\n return currentPath === targetPath;\n}\n\n/**\n * Get page title from sidebar configuration\n *\n * @param pathname - Current pathname\n * @param sidebarGroups - Sidebar groups configuration\n * @param extraTitleMap - Extra title mappings\n * @returns Page title or null\n */\nexport function getPageTitle(\n pathname: string,\n sidebarGroups: Array<{ items: Array<{ path: string; label: string }> }>,\n extraTitleMap?: Record<string, string>\n): string | null {\n // Search in sidebar groups\n for (const group of sidebarGroups) {\n const item = group.items.find((i) => i.path === pathname);\n if (item) return item.label;\n }\n\n // Search in extra title map\n if (extraTitleMap?.[pathname]) {\n return extraTitleMap[pathname];\n }\n\n return null;\n}\n\n/**\n * Filter sidebar items by app type and enabled status\n *\n * @param items - Sidebar items to filter\n * @param user - Current user object\n * @returns Filtered items\n */\nexport function filterSidebarItems<T extends { enabled?: boolean; requiredApp?: \"mobile\" | \"web\" }>(\n items: T[],\n user?: { hasMobileApp?: boolean; hasWebApp?: boolean }\n): T[] {\n return items.filter((item) => {\n // Skip disabled items\n if (item.enabled === false) return false;\n\n // Skip items that require specific app types\n if (!item.requiredApp) return true;\n if (item.requiredApp === \"mobile\") return user?.hasMobileApp ?? false;\n if (item.requiredApp === \"web\") return user?.hasWebApp ?? false;\n\n return true;\n });\n}\n\n/**\n * Generate notification ID\n *\n * @returns Unique notification ID\n */\nexport function generateNotificationId(): string {\n return crypto.randomUUID();\n}\n"],"mappings":";;;AAaO,SAAS,uBACd,WACA,GACQ;AACR,QAAM,OAAO,IAAI,KAAK,SAAS;AAC/B,QAAM,OAAO,KAAK,OAAO,KAAK,IAAI,IAAI,KAAK,QAAQ,KAAK,GAAI;AAE5D,MAAI,OAAO,GAAI,QAAO,EAAE,sCAAsC;AAC9D,MAAI,OAAO,KAAM,QAAO,EAAE,2CAA2C,EAAE,KAAK,KAAK,MAAM,OAAO,EAAE,EAAE,CAAC;AACnG,MAAI,OAAO,MAAO,QAAO,EAAE,yCAAyC,EAAE,KAAK,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC;AACpG,SAAO,EAAE,wCAAwC,EAAE,KAAK,KAAK,MAAM,OAAO,KAAK,EAAE,CAAC;AACpF;AASO,SAAS,aAAa,aAAqB,YAA6B;AAC7E,SAAO,gBAAgB;AACzB;AAUO,SAAS,aACd,UACA,eACA,eACe;AAEf,aAAW,SAAS,eAAe;AACjC,UAAM,OAAO,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AACxD,QAAI,KAAM,QAAO,KAAK;AAAA,EACxB;AAGA,MAAI,gBAAgB,QAAQ,GAAG;AAC7B,WAAO,cAAc,QAAQ;AAAA,EAC/B;AAEA,SAAO;AACT;AASO,SAAS,mBACd,OACA,MACK;AACL,SAAO,MAAM,OAAO,CAAC,SAAS;AAE5B,QAAI,KAAK,YAAY,MAAO,QAAO;AAGnC,QAAI,CAAC,KAAK,YAAa,QAAO;AAC9B,QAAI,KAAK,gBAAgB,SAAU,QAAO,MAAM,gBAAgB;AAChE,QAAI,KAAK,gBAAgB,MAAO,QAAO,MAAM,aAAa;AAE1D,WAAO;AAAA,EACT,CAAC;AACH;AAOO,SAAS,yBAAiC;AAC/C,SAAO,OAAO,WAAW;AAC3B;","names":[]}
|