@djangocfg/layouts 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/package.json +86 -0
  4. package/src/auth/README.md +962 -0
  5. package/src/auth/context/AuthContext.tsx +458 -0
  6. package/src/auth/context/index.ts +2 -0
  7. package/src/auth/context/types.ts +63 -0
  8. package/src/auth/hooks/index.ts +6 -0
  9. package/src/auth/hooks/useAuthForm.ts +329 -0
  10. package/src/auth/hooks/useAuthGuard.ts +23 -0
  11. package/src/auth/hooks/useAuthRedirect.ts +51 -0
  12. package/src/auth/hooks/useAutoAuth.ts +42 -0
  13. package/src/auth/hooks/useLocalStorage.ts +211 -0
  14. package/src/auth/hooks/useSessionStorage.ts +186 -0
  15. package/src/auth/index.ts +10 -0
  16. package/src/auth/middlewares/index.ts +1 -0
  17. package/src/auth/middlewares/proxy.ts +24 -0
  18. package/src/auth/server.ts +6 -0
  19. package/src/auth/utils/errors.ts +34 -0
  20. package/src/auth/utils/index.ts +2 -0
  21. package/src/auth/utils/validation.ts +14 -0
  22. package/src/index.ts +15 -0
  23. package/src/layouts/AppLayout/AppLayout.tsx +123 -0
  24. package/src/layouts/AppLayout/README.md +204 -0
  25. package/src/layouts/AppLayout/SUMMARY.md +240 -0
  26. package/src/layouts/AppLayout/USAGE.md +312 -0
  27. package/src/layouts/AppLayout/components/PageProgress.tsx +104 -0
  28. package/src/layouts/AppLayout/components/Seo.tsx +87 -0
  29. package/src/layouts/AppLayout/components/index.ts +6 -0
  30. package/src/layouts/AppLayout/context/AppContext.tsx +146 -0
  31. package/src/layouts/AppLayout/context/index.ts +5 -0
  32. package/src/layouts/AppLayout/hooks/index.ts +6 -0
  33. package/src/layouts/AppLayout/hooks/useLayoutMode.ts +26 -0
  34. package/src/layouts/AppLayout/hooks/useNavigation.ts +49 -0
  35. package/src/layouts/AppLayout/index.ts +31 -0
  36. package/src/layouts/AppLayout/layouts/AuthLayout/AuthContext.tsx +51 -0
  37. package/src/layouts/AppLayout/layouts/AuthLayout/AuthHelp.tsx +111 -0
  38. package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +40 -0
  39. package/src/layouts/AppLayout/layouts/AuthLayout/IdentifierForm.tsx +330 -0
  40. package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +158 -0
  41. package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +13 -0
  42. package/src/layouts/AppLayout/layouts/AuthLayout/types.ts +61 -0
  43. package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +92 -0
  44. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +60 -0
  45. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +170 -0
  46. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +164 -0
  47. package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +7 -0
  48. package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +5 -0
  49. package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +44 -0
  50. package/src/layouts/AppLayout/layouts/PublicLayout/components/DesktopUserMenu.tsx +136 -0
  51. package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +262 -0
  52. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenu.tsx +289 -0
  53. package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +159 -0
  54. package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +5 -0
  55. package/src/layouts/AppLayout/layouts/index.ts +7 -0
  56. package/src/layouts/AppLayout/providers/CoreProviders.tsx +47 -0
  57. package/src/layouts/AppLayout/providers/index.ts +5 -0
  58. package/src/layouts/AppLayout/types/config.ts +40 -0
  59. package/src/layouts/AppLayout/types/index.ts +10 -0
  60. package/src/layouts/AppLayout/types/layout.ts +47 -0
  61. package/src/layouts/AppLayout/types/navigation.ts +41 -0
  62. package/src/layouts/AppLayout/types/routes.ts +45 -0
  63. package/src/layouts/AppLayout/utils/index.ts +5 -0
  64. package/src/layouts/AppLayout/utils/routeDetection.ts +31 -0
  65. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +125 -0
  66. package/src/layouts/PaymentsLayout/README.md +133 -0
  67. package/src/layouts/PaymentsLayout/components/CreateApiKeyDialog.tsx +172 -0
  68. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +203 -0
  69. package/src/layouts/PaymentsLayout/components/DeleteApiKeyDialog.tsx +100 -0
  70. package/src/layouts/PaymentsLayout/components/index.ts +4 -0
  71. package/src/layouts/PaymentsLayout/events.ts +106 -0
  72. package/src/layouts/PaymentsLayout/index.ts +20 -0
  73. package/src/layouts/PaymentsLayout/types.ts +19 -0
  74. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeyMetrics.tsx +109 -0
  75. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +194 -0
  76. package/src/layouts/PaymentsLayout/views/apikeys/components/index.ts +3 -0
  77. package/src/layouts/PaymentsLayout/views/apikeys/index.tsx +19 -0
  78. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +99 -0
  79. package/src/layouts/PaymentsLayout/views/overview/components/MetricsCards.tsx +103 -0
  80. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +138 -0
  81. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +4 -0
  82. package/src/layouts/PaymentsLayout/views/overview/index.tsx +23 -0
  83. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +282 -0
  84. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +2 -0
  85. package/src/layouts/PaymentsLayout/views/payments/index.tsx +18 -0
  86. package/src/layouts/PaymentsLayout/views/tariffs/index.tsx +29 -0
  87. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +29 -0
  88. package/src/layouts/ProfileLayout/ProfileLayout.tsx +110 -0
  89. package/src/layouts/ProfileLayout/components/AvatarSection.tsx +146 -0
  90. package/src/layouts/ProfileLayout/components/ProfileForm.tsx +208 -0
  91. package/src/layouts/ProfileLayout/components/index.ts +3 -0
  92. package/src/layouts/ProfileLayout/index.ts +3 -0
  93. package/src/layouts/SupportLayout/README.md +91 -0
  94. package/src/layouts/SupportLayout/SupportLayout.tsx +178 -0
  95. package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +154 -0
  96. package/src/layouts/SupportLayout/components/MessageInput.tsx +92 -0
  97. package/src/layouts/SupportLayout/components/MessageList.tsx +312 -0
  98. package/src/layouts/SupportLayout/components/TicketCard.tsx +96 -0
  99. package/src/layouts/SupportLayout/components/TicketList.tsx +152 -0
  100. package/src/layouts/SupportLayout/components/index.ts +6 -0
  101. package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +260 -0
  102. package/src/layouts/SupportLayout/context/index.ts +2 -0
  103. package/src/layouts/SupportLayout/events.ts +31 -0
  104. package/src/layouts/SupportLayout/hooks/index.ts +2 -0
  105. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +118 -0
  106. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +91 -0
  107. package/src/layouts/SupportLayout/index.ts +6 -0
  108. package/src/layouts/SupportLayout/types.ts +23 -0
  109. package/src/layouts/index.ts +9 -0
  110. package/src/snippets/AuthDialog/AuthDialog.tsx +88 -0
  111. package/src/snippets/AuthDialog/events.ts +21 -0
  112. package/src/snippets/AuthDialog/index.ts +3 -0
  113. package/src/snippets/AuthDialog/useAuthDialog.ts +27 -0
  114. package/src/snippets/Breadcrumbs.tsx +80 -0
  115. package/src/snippets/Chat/ChatUIContext.tsx +110 -0
  116. package/src/snippets/Chat/ChatWidget.tsx +476 -0
  117. package/src/snippets/Chat/README.md +122 -0
  118. package/src/snippets/Chat/components/MessageInput.tsx +124 -0
  119. package/src/snippets/Chat/components/MessageList.tsx +168 -0
  120. package/src/snippets/Chat/components/SessionList.tsx +192 -0
  121. package/src/snippets/Chat/components/index.ts +9 -0
  122. package/src/snippets/Chat/hooks/index.ts +6 -0
  123. package/src/snippets/Chat/hooks/useInfiniteSessions.ts +83 -0
  124. package/src/snippets/Chat/index.tsx +44 -0
  125. package/src/snippets/Chat/types.ts +79 -0
  126. package/src/snippets/VideoPlayer/README.md +203 -0
  127. package/src/snippets/VideoPlayer/VideoControls.tsx +133 -0
  128. package/src/snippets/VideoPlayer/VideoPlayer.tsx +114 -0
  129. package/src/snippets/VideoPlayer/index.ts +8 -0
  130. package/src/snippets/VideoPlayer/types.ts +61 -0
  131. package/src/snippets/index.ts +10 -0
  132. package/src/styles/dashboard.css +41 -0
  133. package/src/styles/index.css +20 -0
  134. package/src/styles/sources.css +6 -0
  135. package/src/types/index.ts +1 -0
  136. package/src/types/pageConfig.ts +103 -0
  137. package/src/utils/index.ts +6 -0
  138. package/src/utils/logger.ts +57 -0
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Dashboard Content
3
+ *
4
+ * Main content wrapper for dashboard pages
5
+ * Refactored from _old/DashboardLayout - uses context only!
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import React from 'react';
11
+ import { cn } from '@djangocfg/ui/lib';
12
+ import { useAppContext } from '../../../context';
13
+
14
+ interface DashboardContentProps {
15
+ children: React.ReactNode;
16
+ className?: string;
17
+ }
18
+
19
+ const paddingVariants = {
20
+ none: '',
21
+ default: 'p-6',
22
+ sm: 'p-4',
23
+ md: 'p-6',
24
+ lg: 'p-8',
25
+ };
26
+
27
+ /**
28
+ * Dashboard Content Component
29
+ *
30
+ * Features:
31
+ * - Configurable padding from context
32
+ * - Full-width container
33
+ * - Custom className support
34
+ *
35
+ * Padding controlled by config.privateLayout.contentPadding
36
+ */
37
+ export function DashboardContent({
38
+ children,
39
+ className,
40
+ }: DashboardContentProps) {
41
+ const { config } = useAppContext();
42
+ const { privateLayout } = config;
43
+
44
+ const padding =
45
+ paddingVariants[
46
+ privateLayout.contentPadding as keyof typeof paddingVariants
47
+ ] || paddingVariants.default;
48
+
49
+ return (
50
+ <main
51
+ className={cn(
52
+ 'w-full bg-background relative min-h-full',
53
+ padding,
54
+ className
55
+ )}
56
+ >
57
+ {children}
58
+ </main>
59
+ );
60
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Dashboard Header
3
+ *
4
+ * Header for private/dashboard layout
5
+ * Refactored from _old/DashboardLayout - uses context only!
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import { Bell, LogIn, LogOut, User } from 'lucide-react';
11
+ import React from 'react';
12
+
13
+ import {
14
+ Avatar,
15
+ AvatarFallback,
16
+ AvatarImage,
17
+ Badge,
18
+ Button,
19
+ DropdownMenu,
20
+ DropdownMenuContent,
21
+ DropdownMenuItem,
22
+ DropdownMenuLabel,
23
+ DropdownMenuSeparator,
24
+ DropdownMenuTrigger,
25
+ Separator,
26
+ SidebarTrigger,
27
+ } from '@djangocfg/ui';
28
+ import { ThemeToggle } from '@djangocfg/ui/theme';
29
+ import { useAuthDialog } from '../../../../../snippets';
30
+ import { useAppContext } from '../../../context';
31
+ import { useAuth } from '../../../../../auth';
32
+ import { useNavigation } from '../../../hooks';
33
+
34
+ /**
35
+ * Dashboard Header Component
36
+ *
37
+ * Features:
38
+ * - Sidebar trigger (mobile)
39
+ * - Page title
40
+ * - Custom header actions
41
+ * - Notifications button with badge
42
+ * - Theme toggle
43
+ * - User dropdown with avatar, profile, logout
44
+ * - Login button for guests
45
+ *
46
+ * All data from context!
47
+ */
48
+ export function DashboardHeader() {
49
+ const { config } = useAppContext();
50
+ const { user } = useAuth();
51
+ const { getPageTitle } = useNavigation();
52
+ const { openAuthDialog } = useAuthDialog();
53
+ const { logout } = useAuth();
54
+
55
+ const { privateLayout } = config;
56
+ const pageTitle = getPageTitle();
57
+
58
+ // Notification handler - TODO: implement notification system
59
+ const handleNotificationClick = () => {
60
+ console.log('Notifications clicked');
61
+ };
62
+
63
+ const handleLogin = () => {
64
+ openAuthDialog();
65
+ };
66
+
67
+ return (
68
+ <header className="sticky top-0 py-2 z-50 h-16 flex items-center justify-between px-4 shrink-0 bg-background border-b border-border">
69
+ {/* Left side */}
70
+ <div className="flex items-center gap-4">
71
+ <SidebarTrigger className="-ml-1" />
72
+ <Separator orientation="vertical" className="mr-2 h-4" />
73
+
74
+ {pageTitle && (
75
+ <h1 className="text-lg font-semibold text-foreground">{pageTitle}</h1>
76
+ )}
77
+ </div>
78
+
79
+ {/* Right side */}
80
+ <div className="flex items-center gap-3">
81
+ {/* Custom header actions */}
82
+ {privateLayout.headerActions}
83
+
84
+ {/* Notifications */}
85
+ <Button
86
+ variant="ghost"
87
+ size="icon"
88
+ className="relative"
89
+ onClick={handleNotificationClick}
90
+ >
91
+ <Bell className="h-5 w-5" />
92
+ {/* TODO: implement notification count from context */}
93
+ </Button>
94
+
95
+ {/* Theme Toggle */}
96
+ <ThemeToggle />
97
+
98
+ {/* User menu or Login button */}
99
+ {user ? (
100
+ <DropdownMenu>
101
+ <DropdownMenuTrigger asChild>
102
+ <Button variant="ghost" className="flex items-center gap-2 p-2">
103
+ <Avatar className="h-8 w-8">
104
+ <AvatarImage
105
+ src={user.avatar || ''}
106
+ alt={user.display_username || user.email || ''}
107
+ />
108
+ <AvatarFallback className="bg-primary/10 text-primary">
109
+ {user.display_username?.charAt(0)?.toUpperCase() || 'U'}
110
+ </AvatarFallback>
111
+ </Avatar>
112
+ <span className="hidden md:block text-sm font-medium">
113
+ {user.display_username}
114
+ </span>
115
+ </Button>
116
+ </DropdownMenuTrigger>
117
+
118
+ <DropdownMenuContent align="end" className="w-48">
119
+ <DropdownMenuLabel className="p-0 font-normal">
120
+ <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
121
+ <Avatar className="h-8 w-8">
122
+ <AvatarImage
123
+ src={user.avatar || ''}
124
+ alt={user.display_username || user.email || ''}
125
+ />
126
+ <AvatarFallback className="bg-primary/10 text-primary">
127
+ {user.display_username?.charAt(0)?.toUpperCase() || 'U'}
128
+ </AvatarFallback>
129
+ </Avatar>
130
+ <div className="grid flex-1 text-left text-sm leading-tight">
131
+ <span className="truncate font-semibold">
132
+ {user.display_username || user.full_name || user.email}
133
+ </span>
134
+ <span className="truncate text-xs text-muted-foreground">
135
+ {user.email}
136
+ </span>
137
+ </div>
138
+ </div>
139
+ </DropdownMenuLabel>
140
+
141
+ <DropdownMenuSeparator />
142
+
143
+ <DropdownMenuItem asChild>
144
+ <a
145
+ href={privateLayout.profileHref}
146
+ className="flex items-center gap-2"
147
+ >
148
+ <User className="h-4 w-4" />
149
+ Profile
150
+ </a>
151
+ </DropdownMenuItem>
152
+
153
+ <DropdownMenuSeparator />
154
+
155
+ <DropdownMenuItem onClick={logout}>
156
+ <LogOut className="mr-2 h-4 w-4" />
157
+ Logout
158
+ </DropdownMenuItem>
159
+ </DropdownMenuContent>
160
+ </DropdownMenu>
161
+ ) : (
162
+ <Button onClick={handleLogin} size="sm" className="gap-2">
163
+ <LogIn className="h-4 w-4" />
164
+ <span className="hidden sm:inline">Sign In</span>
165
+ </Button>
166
+ )}
167
+ </div>
168
+ </header>
169
+ );
170
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Dashboard Sidebar
3
+ *
4
+ * Sidebar navigation for private/dashboard layout
5
+ * Refactored from _old/DashboardLayout - uses context only!
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import Link from 'next/link';
11
+ import React from 'react';
12
+
13
+ import {
14
+ Sidebar,
15
+ SidebarContent,
16
+ SidebarFooter,
17
+ SidebarGroup,
18
+ SidebarGroupContent,
19
+ SidebarGroupLabel,
20
+ SidebarHeader,
21
+ SidebarMenu,
22
+ SidebarMenuBadge,
23
+ SidebarMenuButton,
24
+ SidebarMenuItem,
25
+ SidebarMenuSub,
26
+ SidebarMenuSubButton,
27
+ SidebarMenuSubItem,
28
+ useSidebar,
29
+ } from '@djangocfg/ui/components';
30
+ import { useAppContext } from '../../../context';
31
+ import { useNavigation } from '../../../hooks';
32
+
33
+ /**
34
+ * Dashboard Sidebar Component
35
+ *
36
+ * Features:
37
+ * - Project logo and name (clickable to home)
38
+ * - Menu groups with labels
39
+ * - Menu items with icons, labels, badges
40
+ * - Sub-menu items (nested navigation)
41
+ * - Active state detection
42
+ * - Optional footer content
43
+ *
44
+ * All data from context!
45
+ */
46
+ export function DashboardSidebar() {
47
+ const { config } = useAppContext();
48
+ const { currentPath } = useNavigation();
49
+ const { state, isMobile } = useSidebar();
50
+
51
+ const { app, privateLayout } = config;
52
+
53
+ const isActiveRoute = (path: string) => {
54
+ // Only exact match - no prefix matching
55
+ // This ensures /private/jobs ONLY matches /private/jobs
56
+ // and NOT /private, /private/jobs/123, etc.
57
+ return currentPath === path;
58
+ };
59
+
60
+ return (
61
+ <Sidebar collapsible="icon">
62
+ <SidebarHeader>
63
+ <div
64
+ className="flex items-center gap-3"
65
+ style={state === "collapsed" ? {
66
+ paddingLeft: '7px',
67
+ paddingTop: '0.5rem',
68
+ paddingBottom: '0.5rem',
69
+ transition: 'padding 200ms ease-in-out'
70
+ } : {
71
+ padding: '0.5rem',
72
+ transition: 'padding 200ms ease-in-out'
73
+ }}
74
+ >
75
+ <Link href={privateLayout.homeHref}>
76
+ <div className="flex items-center gap-3">
77
+ {app.logoPath ? (
78
+ <img
79
+ src={app.logoPath}
80
+ alt={app.name}
81
+ className={isMobile ? "h-10 w-10 flex-shrink-0" : "h-8 w-8 flex-shrink-0"}
82
+ />
83
+ ) : (
84
+ <div className={isMobile ? "h-10 w-10 bg-primary rounded-lg flex items-center justify-center flex-shrink-0" : "h-8 w-8 bg-primary rounded-lg flex items-center justify-center flex-shrink-0"}>
85
+ <span className="text-primary-foreground font-bold text-sm">
86
+ {app.name.charAt(0).toUpperCase()}
87
+ </span>
88
+ </div>
89
+ )}
90
+ {state !== "collapsed" && (
91
+ <span className={isMobile ? "font-semibold text-foreground truncate text-base" : "font-semibold text-foreground truncate"} style={{ whiteSpace: 'nowrap' }}>
92
+ {app.name}
93
+ </span>
94
+ )}
95
+ </div>
96
+ </Link>
97
+ </div>
98
+ </SidebarHeader>
99
+
100
+ <SidebarContent>
101
+ {privateLayout.menuGroups.map((group) => (
102
+ <SidebarGroup key={group.label}>
103
+ <SidebarGroupLabel>{group.label}</SidebarGroupLabel>
104
+ <SidebarGroupContent>
105
+ <SidebarMenu>
106
+ {group.items.map((item) => {
107
+ const isActive = isActiveRoute(item.path);
108
+ const Icon = item.icon;
109
+
110
+ return (
111
+ <SidebarMenuItem key={item.path}>
112
+ <SidebarMenuButton
113
+ asChild
114
+ isActive={isActive}
115
+ tooltip={item.label}
116
+ size={isMobile ? "lg" : "default"}
117
+ >
118
+ <Link href={item.path}>
119
+ <Icon className={isMobile ? "h-5 w-5" : "h-4 w-4"} />
120
+ <span className={isMobile ? "text-base" : ""}>{item.label}</span>
121
+ {item.badge && (
122
+ <SidebarMenuBadge>{item.badge}</SidebarMenuBadge>
123
+ )}
124
+ </Link>
125
+ </SidebarMenuButton>
126
+
127
+ {/* Submenu */}
128
+ {item.subItems && item.subItems.length > 0 && (
129
+ <SidebarMenuSub>
130
+ {item.subItems.map((subItem) => {
131
+ const isSubActive = isActiveRoute(subItem.path);
132
+ const SubIcon = subItem.icon;
133
+
134
+ return (
135
+ <SidebarMenuSubItem key={subItem.path}>
136
+ <SidebarMenuSubButton
137
+ asChild
138
+ isActive={isSubActive}
139
+ size={isMobile ? "md" : "md"}
140
+ >
141
+ <Link href={subItem.path}>
142
+ <SubIcon className={isMobile ? "h-5 w-5" : "h-4 w-4"} />
143
+ <span className={isMobile ? "text-base" : ""}>{subItem.label}</span>
144
+ </Link>
145
+ </SidebarMenuSubButton>
146
+ </SidebarMenuSubItem>
147
+ );
148
+ })}
149
+ </SidebarMenuSub>
150
+ )}
151
+ </SidebarMenuItem>
152
+ );
153
+ })}
154
+ </SidebarMenu>
155
+ </SidebarGroupContent>
156
+ </SidebarGroup>
157
+ ))}
158
+ </SidebarContent>
159
+
160
+ {/* TODO: implement footer content if needed */}
161
+ {/* <SidebarFooter>Footer content here</SidebarFooter> */}
162
+ </Sidebar>
163
+ );
164
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Private Layout Components
3
+ */
4
+
5
+ export { DashboardHeader } from './DashboardHeader';
6
+ export { DashboardSidebar } from './DashboardSidebar';
7
+ export { DashboardContent } from './DashboardContent';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * PrivateLayout Module
3
+ */
4
+
5
+ export * from './PrivateLayout';
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Public Layout
3
+ *
4
+ * Layout for public pages (landing, docs, etc.)
5
+ * All data accessed through context - no prop drilling
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import React, { ReactNode } from 'react';
11
+ import { Navigation } from './components/Navigation';
12
+ import { Footer } from './components/Footer';
13
+
14
+ export interface PublicLayoutProps {
15
+ children: ReactNode;
16
+ }
17
+
18
+ /**
19
+ * Public Layout Component
20
+ *
21
+ * Features:
22
+ * - Top navigation bar
23
+ * - User menu integration
24
+ * - Footer with links
25
+ * - Mobile responsive
26
+ *
27
+ * All data from useAppContext() and useAuth() - no props needed!
28
+ */
29
+ export function PublicLayout({ children }: PublicLayoutProps) {
30
+ return (
31
+ <div className="min-h-screen flex flex-col">
32
+ {/* Navigation - gets data from context */}
33
+ <Navigation />
34
+
35
+ {/* Main Content */}
36
+ <main className="flex-1">
37
+ {children}
38
+ </main>
39
+
40
+ {/* Footer - gets data from context */}
41
+ <Footer />
42
+ </div>
43
+ );
44
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Desktop User Menu
3
+ *
4
+ * User dropdown menu for desktop navigation
5
+ * Refactored from _old/MainLayout - uses context only!
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import React from 'react';
11
+ import { useRouter } from 'next/router';
12
+ import { ChevronDown, LayoutDashboard, LogOut, User } from 'lucide-react';
13
+ import { ButtonLink } from '@djangocfg/ui/components';
14
+ import { useAppContext } from '../../../context';
15
+ import { useAuth } from '../../../../../auth';
16
+
17
+ /**
18
+ * Desktop User Menu Component
19
+ *
20
+ * Features:
21
+ * - Sign in button for guests
22
+ * - Dashboard link for authenticated users (if not on dashboard)
23
+ * - User dropdown with email and profile link
24
+ * - Logout button
25
+ *
26
+ * All data from context!
27
+ */
28
+ export function DesktopUserMenu() {
29
+ const router = useRouter();
30
+ const { config, userMenuOpen, toggleUserMenu, closeUserMenu } = useAppContext();
31
+ const { user, isAuthenticated, logout } = useAuth();
32
+
33
+ const { routes, publicLayout } = config;
34
+
35
+ const handleLogout = () => {
36
+ logout();
37
+ closeUserMenu();
38
+ };
39
+
40
+ const isDashboard = publicLayout.userMenu.dashboardPath
41
+ ? router.pathname.includes(publicLayout.userMenu.dashboardPath)
42
+ : false;
43
+
44
+ return (
45
+ <div className="flex items-center gap-3">
46
+ {/* Authenticated user */}
47
+ {isAuthenticated ? (
48
+ <div className="flex items-center gap-3">
49
+ {/* Dashboard button (only if not on dashboard) */}
50
+ {publicLayout.userMenu.dashboardPath && !isDashboard && (
51
+ <ButtonLink
52
+ href={publicLayout.userMenu.dashboardPath}
53
+ variant="default"
54
+ size="sm"
55
+ >
56
+ <LayoutDashboard className="size-4 mr-2" />
57
+ Dashboard
58
+ </ButtonLink>
59
+ )}
60
+
61
+ {/* User Dropdown */}
62
+ <div className="relative">
63
+ <button
64
+ className="flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors text-foreground hover:text-primary hover:bg-accent/50"
65
+ onClick={toggleUserMenu}
66
+ aria-haspopup="true"
67
+ aria-expanded={userMenuOpen}
68
+ >
69
+ <User className="size-4" />
70
+ <span className="max-w-[120px] truncate">{user?.email}</span>
71
+ <ChevronDown
72
+ className={`size-4 transition-transform ${
73
+ userMenuOpen ? 'rotate-180' : ''
74
+ }`}
75
+ />
76
+ </button>
77
+
78
+ {userMenuOpen && (
79
+ <>
80
+ {/* Backdrop */}
81
+ <div
82
+ className="fixed inset-0 z-[9995]"
83
+ onClick={closeUserMenu}
84
+ aria-hidden="true"
85
+ />
86
+ {/* Dropdown */}
87
+ <div
88
+ className="absolute top-full right-0 mt-2 w-48 rounded-lg shadow-lg backdrop-blur-xl z-[9996] bg-card/95 border border-border/50"
89
+ role="menu"
90
+ aria-label="User menu"
91
+ >
92
+ <div className="p-2">
93
+ {/* User info */}
94
+ <div className="px-3 py-2 text-sm mb-2 border-b text-muted-foreground border-border/30">
95
+ Signed in as:
96
+ <div className="font-medium truncate text-foreground">
97
+ {user?.email}
98
+ </div>
99
+ </div>
100
+
101
+ {/* Profile link */}
102
+ <ButtonLink
103
+ href={publicLayout.userMenu.profilePath}
104
+ variant="ghost"
105
+ size="sm"
106
+ className="w-full justify-start"
107
+ onClick={closeUserMenu}
108
+ >
109
+ <User className="size-4 mr-2" />
110
+ Profile
111
+ </ButtonLink>
112
+
113
+ {/* Logout button */}
114
+ <button
115
+ onClick={handleLogout}
116
+ className="w-full flex items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors text-destructive hover:bg-destructive/[0.1]"
117
+ >
118
+ <LogOut className="size-4" />
119
+ <span>Sign out</span>
120
+ </button>
121
+ </div>
122
+ </div>
123
+ </>
124
+ )}
125
+ </div>
126
+ </div>
127
+ ) : (
128
+ /* Guest - Sign in button */
129
+ <ButtonLink href={routes.auth} variant="default" size="sm" className="h-9 gap-1.5">
130
+ <User className="w-4 h-4" />
131
+ Sign In
132
+ </ButtonLink>
133
+ )}
134
+ </div>
135
+ );
136
+ }