@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,262 @@
1
+ /**
2
+ * Footer Component
3
+ *
4
+ * Responsive footer with Desktop and Mobile variants
5
+ * Refactored from _old/MainLayout - uses context only!
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import React from 'react';
11
+ import Link from 'next/link';
12
+ import { useIsMobile } from '@djangocfg/ui/hooks';
13
+ import { useAppContext } from '../../../context';
14
+
15
+ /**
16
+ * Footer Component
17
+ *
18
+ * Features:
19
+ * - Responsive (Desktop/Mobile variants)
20
+ * - Project info with logo and description
21
+ * - Badge (optional)
22
+ * - Social links (GitHub, LinkedIn, Twitter, Telegram)
23
+ * - Menu sections (single items or grouped)
24
+ * - Legal links (Privacy, Terms, Security, Cookies, etc.)
25
+ * - Copyright and branding
26
+ *
27
+ * All data from context!
28
+ */
29
+ export function Footer() {
30
+ const { config } = useAppContext();
31
+ const isMobile = useIsMobile();
32
+
33
+ const { app, publicLayout } = config;
34
+ const footer = publicLayout.footer;
35
+ const currentYear = new Date().getFullYear();
36
+
37
+ if (isMobile) {
38
+ return (
39
+ <footer className="lg:hidden bg-background border-t border-border mt-auto">
40
+ <div className="w-full px-4 py-8">
41
+ {/* Project Info */}
42
+ <div className="text-center space-y-4 mb-6">
43
+ <div className="flex items-center justify-center space-x-2">
44
+ <div className="w-6 h-6 flex items-center justify-center">
45
+ <img
46
+ src={app.logoPath}
47
+ alt={`${app.name} Logo`}
48
+ className="w-full h-full object-contain"
49
+ />
50
+ </div>
51
+ <span className="text-lg font-bold text-foreground">{app.name}</span>
52
+ </div>
53
+ {app.description && (
54
+ <p className="text-muted-foreground text-sm leading-relaxed max-w-md mx-auto">
55
+ {app.description}
56
+ </p>
57
+ )}
58
+ </div>
59
+
60
+ {/* Quick Links */}
61
+ <div className="flex flex-wrap justify-center gap-4 mb-6">
62
+ {footer.links.docs && (
63
+ <a
64
+ href={footer.links.docs}
65
+ target="_blank"
66
+ rel="noopener noreferrer"
67
+ className="text-sm text-muted-foreground hover:text-primary transition-colors"
68
+ title="Documentation"
69
+ >
70
+ Docs
71
+ </a>
72
+ )}
73
+ {footer.links.privacy && (
74
+ <Link
75
+ href={footer.links.privacy}
76
+ className="text-sm text-muted-foreground hover:text-primary transition-colors"
77
+ >
78
+ Privacy
79
+ </Link>
80
+ )}
81
+ {footer.links.terms && (
82
+ <Link
83
+ href={footer.links.terms}
84
+ className="text-sm text-muted-foreground hover:text-primary transition-colors"
85
+ >
86
+ Terms
87
+ </Link>
88
+ )}
89
+ </div>
90
+
91
+ {/* Bottom Section */}
92
+ <div className="border-t border-border pt-4">
93
+ <div className="text-center space-y-2">
94
+ <div className="text-sm text-muted-foreground">
95
+ © {currentYear} {app.name}. All rights reserved.
96
+ </div>
97
+ <div className="text-sm text-muted-foreground">
98
+ Made with ❤️ by{' '}
99
+ <a
100
+ href="https://reforms.ai"
101
+ target="_blank"
102
+ rel="noopener noreferrer"
103
+ className="hover:text-primary transition-colors"
104
+ >
105
+ ReformsAI
106
+ </a>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </footer>
112
+ );
113
+ }
114
+
115
+ // Desktop Footer
116
+ return (
117
+ <footer className="max-lg:hidden bg-background border-t border-border mt-auto">
118
+ <div className="w-full px-8 lg:px-16 xl:px-24 py-12">
119
+ <div className="flex flex-col gap-8">
120
+ {/* Top Section - Two Column Layout */}
121
+ <div className="flex gap-8">
122
+ {/* Left Column - Project Info */}
123
+ <div className="space-y-4" style={{ width: '30%', minWidth: '300px' }}>
124
+ <div className="flex items-center gap-2">
125
+ <div className="w-8 h-8 flex items-center justify-center">
126
+ <img
127
+ src={app.logoPath}
128
+ alt={`${app.name} Logo`}
129
+ className="w-full h-full object-contain"
130
+ />
131
+ </div>
132
+ <span className="text-xl font-bold text-foreground">{app.name}</span>
133
+ </div>
134
+ {app.description && (
135
+ <p className="text-muted-foreground text-sm leading-relaxed">
136
+ {app.description}
137
+ </p>
138
+ )}
139
+ {/* Badge */}
140
+ {footer.badge && (
141
+ <div className="pt-2">
142
+ <span className="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-primary/10 hover:bg-primary/15 border border-primary/20 text-xs font-medium text-primary transition-colors">
143
+ <footer.badge.icon className="w-3.5 h-3.5" />
144
+ {footer.badge.text}
145
+ </span>
146
+ </div>
147
+ )}
148
+ </div>
149
+
150
+ {/* Right Column - Footer Menu Sections */}
151
+ <div className="grid grid-cols-2 gap-8">
152
+ {footer.menuSections.map((section) => {
153
+ // Single item section - render as direct link
154
+ if (section.items.length === 1) {
155
+ const item = section.items[0];
156
+ if (!item) return null;
157
+
158
+ return (
159
+ <div key={section.title}>
160
+ <Link
161
+ href={item.path}
162
+ className="text-muted-foreground hover:text-primary text-sm transition-colors"
163
+ >
164
+ {item.label}
165
+ </Link>
166
+ </div>
167
+ );
168
+ }
169
+
170
+ // Multiple items - render as section
171
+ return (
172
+ <div key={section.title}>
173
+ <h3 className="text-lg font-semibold text-foreground mb-4">
174
+ {section.title}
175
+ </h3>
176
+ <ul className="space-y-2">
177
+ {section.items.map((item) => (
178
+ <li key={item.path}>
179
+ <Link
180
+ href={item.path}
181
+ className="text-muted-foreground hover:text-primary text-sm transition-colors"
182
+ >
183
+ {item.label}
184
+ </Link>
185
+ </li>
186
+ ))}
187
+ </ul>
188
+ </div>
189
+ );
190
+ })}
191
+ </div>
192
+ </div>
193
+
194
+ {/* Bottom Section */}
195
+ <div className="border-t border-border" style={{ marginTop: '2rem', paddingTop: '2rem' }}>
196
+ <div className="flex justify-between items-center gap-4">
197
+ <div className="text-xs text-muted-foreground">
198
+ © {currentYear} {app.name}. All rights reserved.
199
+ </div>
200
+ <div className="text-xs text-muted-foreground flex items-center gap-1">
201
+ Made with ❤️ by{' '}
202
+ <a
203
+ href="https://reforms.ai"
204
+ target="_blank"
205
+ rel="noopener noreferrer"
206
+ className="hover:text-primary transition-colors"
207
+ >
208
+ ReformsAI
209
+ </a>
210
+ </div>
211
+ <div className="flex flex-wrap items-center gap-4">
212
+ {footer.links.docs && (
213
+ <a
214
+ href={footer.links.docs}
215
+ target="_blank"
216
+ rel="noopener noreferrer"
217
+ className="text-xs text-muted-foreground hover:text-primary transition-colors"
218
+ title="Documentation"
219
+ >
220
+ Docs
221
+ </a>
222
+ )}
223
+ {footer.links.privacy && (
224
+ <Link
225
+ href={footer.links.privacy}
226
+ className="text-xs text-muted-foreground hover:text-primary transition-colors"
227
+ >
228
+ Privacy Policy
229
+ </Link>
230
+ )}
231
+ {footer.links.terms && (
232
+ <Link
233
+ href={footer.links.terms}
234
+ className="text-xs text-muted-foreground hover:text-primary transition-colors"
235
+ >
236
+ Terms of Service
237
+ </Link>
238
+ )}
239
+ {footer.links.security && (
240
+ <Link
241
+ href={footer.links.security}
242
+ className="text-xs text-muted-foreground hover:text-primary transition-colors"
243
+ >
244
+ Security
245
+ </Link>
246
+ )}
247
+ {footer.links.cookies && (
248
+ <Link
249
+ href={footer.links.cookies}
250
+ className="text-xs text-muted-foreground hover:text-primary transition-colors"
251
+ >
252
+ Cookies
253
+ </Link>
254
+ )}
255
+ </div>
256
+ </div>
257
+ </div>
258
+ </div>
259
+ </div>
260
+ </footer>
261
+ );
262
+ }
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Mobile Menu Drawer
3
+ *
4
+ * Full-screen slide-in menu for mobile devices
5
+ * Refactored from _old/MainLayout - uses context only!
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import React from 'react';
11
+ import { createPortal } from 'react-dom';
12
+ import Link from 'next/link';
13
+ import { Crown, LogOut, Settings, User, X } from 'lucide-react';
14
+ import { ButtonLink, Card, CardContent } from '@djangocfg/ui/components';
15
+ import { ThemeToggle } from '@djangocfg/ui/theme';
16
+ import { useAppContext } from '../../../context';
17
+ import { useAuth } from '../../../../../auth';
18
+ import { useNavigation } from '../../../hooks';
19
+
20
+ /**
21
+ * Mobile Menu Component
22
+ *
23
+ * Features:
24
+ * - Slide-in drawer from right
25
+ * - User card with info (authenticated)
26
+ * - Welcome card with sign in (guest)
27
+ * - Navigation sections
28
+ * - Theme toggle
29
+ * - Backdrop overlay
30
+ *
31
+ * All data from context!
32
+ */
33
+ export function MobileMenu() {
34
+ const { config, mobileMenuOpen, closeMobileMenu } = useAppContext();
35
+ const { user, isAuthenticated, logout } = useAuth();
36
+ const { isActive } = useNavigation();
37
+
38
+ const { app, publicLayout, routes } = config;
39
+
40
+ // Animation state
41
+ const [isAnimating, setIsAnimating] = React.useState(false);
42
+
43
+ // Trigger animation when menu opens
44
+ React.useEffect(() => {
45
+ if (mobileMenuOpen) {
46
+ // Small delay to trigger animation
47
+ setTimeout(() => setIsAnimating(true), 10);
48
+ } else {
49
+ setIsAnimating(false);
50
+ }
51
+ }, [mobileMenuOpen]);
52
+
53
+ const handleLogout = () => {
54
+ logout();
55
+ handleClose();
56
+ };
57
+
58
+ const handleClose = () => {
59
+ setIsAnimating(false);
60
+ setTimeout(closeMobileMenu, 300); // Wait for animation
61
+ };
62
+
63
+ const handleNavigate = () => {
64
+ handleClose();
65
+ };
66
+
67
+ if (!mobileMenuOpen) return null;
68
+
69
+ // Portal to body to avoid z-index and positioning issues
70
+ if (typeof window === 'undefined') return null;
71
+
72
+ return createPortal(
73
+ <>
74
+ {/* Backdrop with fade animation */}
75
+ <div
76
+ className={`fixed inset-0 z-[9998] bg-black/50 backdrop-blur-sm transition-opacity duration-300 lg:hidden ${
77
+ isAnimating ? 'opacity-100' : 'opacity-0'
78
+ }`}
79
+ onClick={handleClose}
80
+ aria-hidden="true"
81
+ />
82
+
83
+ {/* Menu Content with slide animation */}
84
+ <div
85
+ className={`fixed top-0 right-0 bottom-0 w-80 z-[9999] bg-card backdrop-blur-xl border-l border-border shadow-2xl transition-transform duration-300 ease-out lg:hidden ${
86
+ isAnimating ? 'translate-x-0' : 'translate-x-full'
87
+ }`}
88
+ role="dialog"
89
+ aria-modal="true"
90
+ aria-label="Mobile navigation menu"
91
+ >
92
+ <div className="flex flex-col h-full">
93
+ {/* Header */}
94
+ <div className="flex items-center justify-between p-4 border-b border-border/30">
95
+ <div className="flex items-center gap-3">
96
+ <img
97
+ src={app.logoPath}
98
+ alt={`${app.name} Logo`}
99
+ className="h-8 w-auto object-contain"
100
+ />
101
+ <span className="text-lg font-bold text-foreground">
102
+ {app.name}
103
+ </span>
104
+ </div>
105
+ <button
106
+ onClick={handleClose}
107
+ className="p-2 rounded-lg transition-colors hover:bg-accent/50"
108
+ aria-label="Close menu"
109
+ >
110
+ <X className="size-5" />
111
+ </button>
112
+ </div>
113
+
114
+ {/* Scrollable Content */}
115
+ <div className="flex-1 overflow-y-auto p-4 space-y-6">
116
+ {/* User Menu Card - Authenticated */}
117
+ {isAuthenticated ? (
118
+ <Card className="border-primary/20 shadow-lg bg-accent/30">
119
+ <CardContent className="p-4">
120
+ {/* User Info Header */}
121
+ <div className="flex items-center gap-3 mb-4 p-3 rounded-lg border border-border bg-accent/50">
122
+ <div className="w-10 h-10 rounded-full flex items-center justify-center bg-primary flex-shrink-0 overflow-hidden">
123
+ {user?.avatar ? (
124
+ <img
125
+ src={user.avatar}
126
+ alt={user?.email || 'User'}
127
+ className="w-10 h-10 rounded-full object-cover"
128
+ />
129
+ ) : (
130
+ <User className="w-5 h-5 text-primary-foreground" />
131
+ )}
132
+ </div>
133
+ <div className="flex-1 min-w-0">
134
+ <p className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
135
+ Signed in as
136
+ </p>
137
+ <p className="text-sm font-semibold truncate text-foreground">
138
+ {user?.email}
139
+ </p>
140
+ </div>
141
+ <div className="flex items-center gap-1">
142
+ <div className="size-2 rounded-full animate-pulse bg-green-500"></div>
143
+ <span className="text-xs font-medium text-green-600 dark:text-green-400">
144
+ Active
145
+ </span>
146
+ </div>
147
+ </div>
148
+
149
+ {/* Action Buttons */}
150
+ <div className="space-y-3">
151
+ {/* Dashboard link */}
152
+ {publicLayout.userMenu.dashboardPath && (
153
+ <ButtonLink
154
+ href={publicLayout.userMenu.dashboardPath}
155
+ variant="default"
156
+ size="sm"
157
+ className="w-full justify-center"
158
+ onClick={handleNavigate}
159
+ >
160
+ <Crown className="w-4 h-4 mr-2" />
161
+ Dashboard
162
+ </ButtonLink>
163
+ )}
164
+
165
+ {/* Profile link */}
166
+ <ButtonLink
167
+ href={publicLayout.userMenu.profilePath}
168
+ variant="outline"
169
+ size="sm"
170
+ className="w-full justify-center"
171
+ onClick={handleNavigate}
172
+ >
173
+ <Settings className="w-4 h-4 mr-2" />
174
+ Profile Settings
175
+ </ButtonLink>
176
+
177
+ {/* Logout */}
178
+ <button
179
+ onClick={handleLogout}
180
+ className="w-full flex items-center justify-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors border text-destructive border-destructive/30 hover:bg-destructive/10"
181
+ >
182
+ <LogOut className="w-4 h-4" />
183
+ Sign Out
184
+ </button>
185
+
186
+ {/* Theme toggle */}
187
+ <div className="flex justify-center pt-2 border-t border-border/30">
188
+ <ThemeToggle />
189
+ </div>
190
+ </div>
191
+ </CardContent>
192
+ </Card>
193
+ ) : (
194
+ /* Guest Card */
195
+ <Card className="border-border bg-accent/30">
196
+ <CardContent className="p-4">
197
+ <div className="text-center space-y-4">
198
+ <div className="w-12 h-12 rounded-full flex items-center justify-center mx-auto bg-muted flex-shrink-0">
199
+ <User className="w-6 h-6 text-muted-foreground" />
200
+ </div>
201
+ <div>
202
+ <p className="text-sm font-medium mb-1 text-foreground">
203
+ Welcome!
204
+ </p>
205
+ <p className="text-xs text-muted-foreground">
206
+ Sign in to access your dashboard
207
+ </p>
208
+ </div>
209
+ <ButtonLink
210
+ href={routes.auth}
211
+ variant="default"
212
+ size="lg"
213
+ className="w-full justify-center"
214
+ onClick={handleNavigate}
215
+ >
216
+ <User className="w-5 h-5 mr-2" />
217
+ Sign In
218
+ </ButtonLink>
219
+
220
+ {/* Theme toggle */}
221
+ <div className="flex justify-center pt-2 border-t border-border/30">
222
+ <ThemeToggle />
223
+ </div>
224
+ </div>
225
+ </CardContent>
226
+ </Card>
227
+ )}
228
+
229
+ {/* Navigation Sections */}
230
+ <div className="space-y-6">
231
+ {publicLayout.navigation.menuSections.map((section) => {
232
+ // Single item section
233
+ if (section.items.length === 1) {
234
+ const item = section.items[0];
235
+ if (!item) return null;
236
+
237
+ return (
238
+ <div key={section.title} className="space-y-2">
239
+ <Link
240
+ href={item.path}
241
+ className={`block px-4 py-3 rounded-lg text-base font-medium transition-colors ${
242
+ isActive(item.path)
243
+ ? 'text-primary border border-primary/20 bg-primary/[0.1]'
244
+ : 'text-muted-foreground hover:text-primary hover:bg-accent/50'
245
+ }`}
246
+ onClick={handleNavigate}
247
+ >
248
+ {item.label}
249
+ </Link>
250
+ </div>
251
+ );
252
+ }
253
+
254
+ // Multiple items section
255
+ return (
256
+ <div key={section.title} className="space-y-3">
257
+ <h3 className="px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
258
+ {section.title}
259
+ </h3>
260
+ <div className="space-y-1">
261
+ {section.items.map((item) => (
262
+ <Link
263
+ key={item.path}
264
+ href={item.path}
265
+ className={`block px-4 py-3 rounded-lg text-base font-medium transition-colors ${
266
+ isActive(item.path)
267
+ ? 'bg-accent text-accent-foreground'
268
+ : 'text-foreground hover:bg-accent hover:text-accent-foreground'
269
+ }`}
270
+ onClick={handleNavigate}
271
+ >
272
+ {item.label}
273
+ </Link>
274
+ ))}
275
+ </div>
276
+ </div>
277
+ );
278
+ })}
279
+ </div>
280
+
281
+ {/* Bottom spacer */}
282
+ <div className="h-20"></div>
283
+ </div>
284
+ </div>
285
+ </div>
286
+ </>,
287
+ document.body
288
+ );
289
+ }