@djangocfg/layouts 1.4.29 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +277 -18
- package/package.json +15 -24
- package/src/auth/context/AuthContext.tsx +8 -5
- package/src/auth/hooks/useAuthGuard.ts +1 -1
- package/src/auth/hooks/useAutoAuth.ts +8 -7
- package/src/components/ErrorBoundary.tsx +78 -0
- package/src/components/JsonLd.tsx +31 -0
- package/src/components/LucideIcon.tsx +91 -0
- package/src/components/PageProgress.tsx +127 -0
- package/src/components/Suspense.tsx +29 -0
- package/src/{layouts/AppLayout/components → components}/UpdateNotifier/UpdateNotifier.tsx +56 -49
- package/src/components/index.ts +10 -0
- package/src/index.ts +25 -7
- package/src/layouts/AdminLayout/AdminLayout.tsx +46 -0
- package/src/layouts/AdminLayout/index.ts +7 -0
- package/src/layouts/AppLayout/AppLayout.tsx +278 -326
- package/src/layouts/AppLayout/index.ts +2 -39
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthContext.tsx +3 -2
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthHelp.tsx +1 -0
- package/src/layouts/AuthLayout/AuthLayout.tsx +61 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/IdentifierForm.tsx +47 -34
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/OTPForm.tsx +2 -3
- package/src/layouts/AuthLayout/index.ts +24 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/types.ts +1 -0
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +144 -0
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +32 -0
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +57 -0
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +141 -0
- package/src/layouts/PrivateLayout/components/index.ts +8 -0
- package/src/layouts/PrivateLayout/index.ts +7 -0
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +15 -7
- package/src/layouts/PublicLayout/PublicLayout.tsx +121 -0
- package/src/layouts/PublicLayout/components/PublicFooter.tsx +190 -0
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +117 -0
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +101 -0
- package/src/layouts/PublicLayout/components/index.ts +8 -0
- package/src/layouts/PublicLayout/index.ts +7 -0
- package/src/layouts/_components/UserMenu.tsx +160 -0
- package/src/layouts/_components/index.ts +7 -0
- package/src/layouts/index.ts +15 -8
- package/src/snippets/Analytics/AnalyticsProvider.tsx +8 -4
- package/src/snippets/Analytics/useAnalytics.ts +11 -21
- package/src/snippets/Chat/ChatWidget.tsx +4 -4
- package/src/snippets/ContactForm/ContactFormProvider.tsx +32 -19
- package/src/snippets/ContactForm/ContactPage.tsx +2 -4
- package/src/snippets/ContactForm/types.ts +3 -2
- package/src/snippets/index.ts +0 -1
- package/src/layouts/AppLayout/README.md +0 -204
- package/src/layouts/AppLayout/SUMMARY.md +0 -240
- package/src/layouts/AppLayout/USAGE.md +0 -312
- package/src/layouts/AppLayout/components/ErrorBoundary.tsx +0 -112
- package/src/layouts/AppLayout/components/PageProgress.tsx +0 -123
- package/src/layouts/AppLayout/components/Seo.tsx +0 -171
- package/src/layouts/AppLayout/components/UserMenu.tsx +0 -385
- package/src/layouts/AppLayout/components/index.ts +0 -11
- package/src/layouts/AppLayout/context/AppContext.tsx +0 -151
- package/src/layouts/AppLayout/context/index.ts +0 -5
- package/src/layouts/AppLayout/hooks/index.ts +0 -8
- package/src/layouts/AppLayout/hooks/useLayoutMode.ts +0 -26
- package/src/layouts/AppLayout/hooks/useNavigation.ts +0 -51
- package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +0 -224
- package/src/layouts/AppLayout/layouts/AdminLayout/README.md +0 -409
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.example.tsx +0 -98
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.tsx +0 -149
- package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +0 -146
- package/src/layouts/AppLayout/layouts/AdminLayout/components/index.ts +0 -3
- package/src/layouts/AppLayout/layouts/AdminLayout/context/CfgAppContext.tsx +0 -48
- package/src/layouts/AppLayout/layouts/AdminLayout/context/index.ts +0 -2
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/index.ts +0 -6
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +0 -279
- package/src/layouts/AppLayout/layouts/AdminLayout/index.ts +0 -24
- package/src/layouts/AppLayout/layouts/AdminLayout/lottie/energizing.json +0 -1
- package/src/layouts/AppLayout/layouts/AdminLayout/types/index.ts +0 -45
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +0 -41
- package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +0 -15
- package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +0 -82
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +0 -62
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +0 -89
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +0 -181
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +0 -9
- package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +0 -44
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +0 -242
- package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileDrawer.tsx +0 -150
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +0 -169
- package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/index.ts +0 -7
- package/src/layouts/AppLayout/providers/CoreProviders.tsx +0 -80
- package/src/layouts/AppLayout/providers/index.ts +0 -5
- package/src/layouts/AppLayout/types/config.ts +0 -79
- package/src/layouts/AppLayout/types/index.ts +0 -11
- package/src/layouts/AppLayout/types/layout.ts +0 -54
- package/src/layouts/AppLayout/types/navigation.ts +0 -43
- package/src/layouts/AppLayout/types/page.ts +0 -80
- package/src/layouts/AppLayout/types/routes.ts +0 -43
- package/src/layouts/AppLayout/utils/index.ts +0 -5
- package/src/layouts/AppLayout/utils/routeDetection.ts +0 -31
- package/src/layouts/ErrorLayout/ErrorLayout.tsx +0 -173
- package/src/layouts/ErrorLayout/errorConfig.tsx +0 -152
- package/src/layouts/ErrorLayout/index.ts +0 -8
- package/src/layouts/SimpleLayout/SimpleLayout.tsx +0 -72
- package/src/layouts/SimpleLayout/index.ts +0 -3
- package/src/snippets/VideoPlayer/README.md +0 -238
- package/src/snippets/VideoPlayer/VideoControls.tsx +0 -137
- package/src/snippets/VideoPlayer/VideoPlayer.tsx +0 -248
- package/src/snippets/VideoPlayer/index.ts +0 -8
- package/src/snippets/VideoPlayer/types.ts +0 -61
- package/src/types/index.ts +0 -2
- package/src/types/pageConfig.ts +0 -100
- /package/src/{validation → components/ErrorsTracker}/README.md +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorButtons.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorToast.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/hooks.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/index.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/providers/ErrorTrackingProvider.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/types.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/curl-generator.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/formatters.ts +0 -0
- /package/src/{layouts/AppLayout/components → components}/UpdateNotifier/index.ts +0 -0
|
@@ -1,242 +0,0 @@
|
|
|
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 gap-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 items-center">
|
|
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-xs text-muted-foreground">
|
|
95
|
-
© {currentYear} {app.name}. All rights reserved.
|
|
96
|
-
</div>
|
|
97
|
-
<div className="text-xs text-muted-foreground flex items-center justify-center gap-1">
|
|
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-sm 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 (max 4 columns, rest wraps) */}
|
|
151
|
-
<div className="flex flex-wrap gap-8 flex-1">
|
|
152
|
-
{footer.menuSections.map((section) => (
|
|
153
|
-
<div key={section.title} className="flex-1 basis-[calc(25%-1.5rem)] min-w-[140px] max-w-[200px]">
|
|
154
|
-
<h3 className="text-sm font-semibold text-foreground mb-3">
|
|
155
|
-
{section.title}
|
|
156
|
-
</h3>
|
|
157
|
-
<ul className="space-y-2">
|
|
158
|
-
{section.items.map((item) => (
|
|
159
|
-
<li key={item.path}>
|
|
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
|
-
</li>
|
|
167
|
-
))}
|
|
168
|
-
</ul>
|
|
169
|
-
</div>
|
|
170
|
-
))}
|
|
171
|
-
</div>
|
|
172
|
-
</div>
|
|
173
|
-
|
|
174
|
-
{/* Bottom Section */}
|
|
175
|
-
<div className="border-t border-border" style={{ marginTop: '2rem', paddingTop: '2rem' }}>
|
|
176
|
-
<div className="flex justify-between items-center gap-4">
|
|
177
|
-
<div className="text-xs text-muted-foreground">
|
|
178
|
-
© {currentYear} {app.name}. All rights reserved.
|
|
179
|
-
</div>
|
|
180
|
-
<div className="text-xs text-muted-foreground flex items-center gap-1">
|
|
181
|
-
Made with ❤️ by{' '}
|
|
182
|
-
<a
|
|
183
|
-
href="https://reforms.ai"
|
|
184
|
-
target="_blank"
|
|
185
|
-
rel="noopener noreferrer"
|
|
186
|
-
className="hover:text-primary transition-colors"
|
|
187
|
-
>
|
|
188
|
-
ReformsAI
|
|
189
|
-
</a>
|
|
190
|
-
</div>
|
|
191
|
-
<div className="flex flex-wrap items-center gap-4">
|
|
192
|
-
{footer.links.docs && (
|
|
193
|
-
<a
|
|
194
|
-
href={footer.links.docs}
|
|
195
|
-
target="_blank"
|
|
196
|
-
rel="noopener noreferrer"
|
|
197
|
-
className="text-xs text-muted-foreground hover:text-primary transition-colors"
|
|
198
|
-
title="Documentation"
|
|
199
|
-
>
|
|
200
|
-
Docs
|
|
201
|
-
</a>
|
|
202
|
-
)}
|
|
203
|
-
{footer.links.privacy && (
|
|
204
|
-
<Link
|
|
205
|
-
href={footer.links.privacy}
|
|
206
|
-
className="text-xs text-muted-foreground hover:text-primary transition-colors"
|
|
207
|
-
>
|
|
208
|
-
Privacy Policy
|
|
209
|
-
</Link>
|
|
210
|
-
)}
|
|
211
|
-
{footer.links.terms && (
|
|
212
|
-
<Link
|
|
213
|
-
href={footer.links.terms}
|
|
214
|
-
className="text-xs text-muted-foreground hover:text-primary transition-colors"
|
|
215
|
-
>
|
|
216
|
-
Terms of Service
|
|
217
|
-
</Link>
|
|
218
|
-
)}
|
|
219
|
-
{footer.links.security && (
|
|
220
|
-
<Link
|
|
221
|
-
href={footer.links.security}
|
|
222
|
-
className="text-xs text-muted-foreground hover:text-primary transition-colors"
|
|
223
|
-
>
|
|
224
|
-
Security
|
|
225
|
-
</Link>
|
|
226
|
-
)}
|
|
227
|
-
{footer.links.cookies && (
|
|
228
|
-
<Link
|
|
229
|
-
href={footer.links.cookies}
|
|
230
|
-
className="text-xs text-muted-foreground hover:text-primary transition-colors"
|
|
231
|
-
>
|
|
232
|
-
Cookies
|
|
233
|
-
</Link>
|
|
234
|
-
)}
|
|
235
|
-
</div>
|
|
236
|
-
</div>
|
|
237
|
-
</div>
|
|
238
|
-
</div>
|
|
239
|
-
</div>
|
|
240
|
-
</footer>
|
|
241
|
-
);
|
|
242
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mobile Drawer
|
|
3
|
-
*
|
|
4
|
-
* Full-screen slide-in drawer for mobile navigation
|
|
5
|
-
* Uses @djangocfg/ui Drawer component (Vaul-based)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use client';
|
|
9
|
-
|
|
10
|
-
import React from 'react';
|
|
11
|
-
import Link from 'next/link';
|
|
12
|
-
import { X } from 'lucide-react';
|
|
13
|
-
import {
|
|
14
|
-
Drawer,
|
|
15
|
-
DrawerContent,
|
|
16
|
-
DrawerHeader,
|
|
17
|
-
DrawerTitle,
|
|
18
|
-
DrawerClose,
|
|
19
|
-
} from '@djangocfg/ui/components';
|
|
20
|
-
import { useAppContext } from '../../../context';
|
|
21
|
-
import { useNavigation } from '../../../hooks';
|
|
22
|
-
import { UserMenu } from '../../../components';
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Mobile Drawer Component
|
|
26
|
-
*
|
|
27
|
-
* Features:
|
|
28
|
-
* - Slide-in drawer from right (using Vaul)
|
|
29
|
-
* - UserMenu component (authenticated/guest)
|
|
30
|
-
* - Navigation sections
|
|
31
|
-
* - Smooth animations via Vaul library
|
|
32
|
-
*
|
|
33
|
-
* All data from context!
|
|
34
|
-
*/
|
|
35
|
-
export function MobileDrawer() {
|
|
36
|
-
const { config, mobileDrawerOpen, closeMobileDrawer } = useAppContext();
|
|
37
|
-
const { isActive } = useNavigation();
|
|
38
|
-
|
|
39
|
-
const { app, publicLayout } = config;
|
|
40
|
-
|
|
41
|
-
const handleNavigate = () => {
|
|
42
|
-
closeMobileDrawer();
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Prepare menu sections before render
|
|
46
|
-
const singleItemSections = React.useMemo(
|
|
47
|
-
() => publicLayout.navigation.menuSections.filter(s => s.items.length === 1),
|
|
48
|
-
[publicLayout.navigation.menuSections]
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const multipleItemsSections = React.useMemo(
|
|
52
|
-
() => publicLayout.navigation.menuSections.filter(s => s.items.length > 1),
|
|
53
|
-
[publicLayout.navigation.menuSections]
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<Drawer
|
|
58
|
-
open={mobileDrawerOpen}
|
|
59
|
-
onOpenChange={(open) => !open && closeMobileDrawer()}
|
|
60
|
-
direction="right"
|
|
61
|
-
>
|
|
62
|
-
<DrawerContent direction="right" className="w-80 lg:hidden">
|
|
63
|
-
{/* Header */}
|
|
64
|
-
<DrawerHeader className="flex flex-row items-center justify-between p-4 border-b border-border/30">
|
|
65
|
-
<div className="flex items-center gap-3">
|
|
66
|
-
<img
|
|
67
|
-
src={app.logoPath}
|
|
68
|
-
alt={`${app.name} Logo`}
|
|
69
|
-
className="h-8 w-auto object-contain"
|
|
70
|
-
/>
|
|
71
|
-
<DrawerTitle className="text-lg font-bold text-foreground">
|
|
72
|
-
{app.name}
|
|
73
|
-
</DrawerTitle>
|
|
74
|
-
</div>
|
|
75
|
-
<DrawerClose className="p-2 rounded-sm transition-colors hover:bg-accent/50">
|
|
76
|
-
<X className="size-5" />
|
|
77
|
-
<span className="sr-only">Close menu</span>
|
|
78
|
-
</DrawerClose>
|
|
79
|
-
</DrawerHeader>
|
|
80
|
-
|
|
81
|
-
{/* Scrollable Content */}
|
|
82
|
-
<div className="flex-1 overflow-y-auto p-4 space-y-6">
|
|
83
|
-
{/* User Menu Card */}
|
|
84
|
-
<UserMenu variant="mobile" onNavigate={handleNavigate} />
|
|
85
|
-
|
|
86
|
-
{/* Navigation Sections */}
|
|
87
|
-
<div className="space-y-6">
|
|
88
|
-
{/* Group all single-item sections into "Menu" */}
|
|
89
|
-
{singleItemSections.length > 0 && (
|
|
90
|
-
<div className="space-y-3">
|
|
91
|
-
<h3 className="px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
92
|
-
Menu
|
|
93
|
-
</h3>
|
|
94
|
-
<div className="space-y-1">
|
|
95
|
-
{singleItemSections.map((section) => {
|
|
96
|
-
const item = section.items[0];
|
|
97
|
-
if (!item) return null;
|
|
98
|
-
|
|
99
|
-
return (
|
|
100
|
-
<Link
|
|
101
|
-
key={item.path}
|
|
102
|
-
href={item.path}
|
|
103
|
-
className={`block px-4 py-3 rounded-sm text-base font-medium transition-colors ${
|
|
104
|
-
isActive(item.path)
|
|
105
|
-
? 'bg-accent text-accent-foreground'
|
|
106
|
-
: 'text-foreground hover:bg-accent hover:text-accent-foreground'
|
|
107
|
-
}`}
|
|
108
|
-
onClick={handleNavigate}
|
|
109
|
-
>
|
|
110
|
-
{item.label}
|
|
111
|
-
</Link>
|
|
112
|
-
);
|
|
113
|
-
})}
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
)}
|
|
117
|
-
|
|
118
|
-
{/* Render multiple-items sections normally */}
|
|
119
|
-
{multipleItemsSections.map((section) => (
|
|
120
|
-
<div key={section.title} className="space-y-3">
|
|
121
|
-
<h3 className="px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
122
|
-
{section.title}
|
|
123
|
-
</h3>
|
|
124
|
-
<div className="space-y-1">
|
|
125
|
-
{section.items.map((item) => (
|
|
126
|
-
<Link
|
|
127
|
-
key={item.path}
|
|
128
|
-
href={item.path}
|
|
129
|
-
className={`block px-4 py-3 rounded-sm text-base font-medium transition-colors ${
|
|
130
|
-
isActive(item.path)
|
|
131
|
-
? 'bg-accent text-accent-foreground'
|
|
132
|
-
: 'text-foreground hover:bg-accent hover:text-accent-foreground'
|
|
133
|
-
}`}
|
|
134
|
-
onClick={handleNavigate}
|
|
135
|
-
>
|
|
136
|
-
{item.label}
|
|
137
|
-
</Link>
|
|
138
|
-
))}
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
))}
|
|
142
|
-
</div>
|
|
143
|
-
|
|
144
|
-
{/* Bottom spacer */}
|
|
145
|
-
<div style={{ height: '15vh' }}></div>
|
|
146
|
-
</div>
|
|
147
|
-
</DrawerContent>
|
|
148
|
-
</Drawer>
|
|
149
|
-
);
|
|
150
|
-
}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Public Layout Navigation
|
|
3
|
-
*
|
|
4
|
-
* Full-featured navigation using @djangocfg/ui components
|
|
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 { Menu, ChevronDown } from 'lucide-react';
|
|
13
|
-
import {
|
|
14
|
-
DropdownMenu,
|
|
15
|
-
DropdownMenuTrigger,
|
|
16
|
-
DropdownMenuContent,
|
|
17
|
-
DropdownMenuItem,
|
|
18
|
-
} from '@djangocfg/ui/components';
|
|
19
|
-
import { ThemeToggle } from '@djangocfg/ui/theme';
|
|
20
|
-
import { cn } from '@djangocfg/ui/lib';
|
|
21
|
-
import { useIsMobile } from '@djangocfg/ui/hooks';
|
|
22
|
-
import { useAppContext } from '../../../context';
|
|
23
|
-
import { useNavigation } from '../../../hooks';
|
|
24
|
-
import { UserMenu } from '../../../components';
|
|
25
|
-
import { MobileDrawer } from './MobileDrawer';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Navigation Component
|
|
29
|
-
*
|
|
30
|
-
* Features:
|
|
31
|
-
* - Logo and branding
|
|
32
|
-
* - NavigationMenu from @djangocfg/ui (Radix UI based)
|
|
33
|
-
* - Theme toggle
|
|
34
|
-
* - User menu (desktop)
|
|
35
|
-
* - Mobile menu button
|
|
36
|
-
*
|
|
37
|
-
* All data from context - zero prop drilling!
|
|
38
|
-
*/
|
|
39
|
-
export function Navigation() {
|
|
40
|
-
const { config, toggleMobileDrawer } = useAppContext();
|
|
41
|
-
const { isActive } = useNavigation();
|
|
42
|
-
const isMobile = useIsMobile();
|
|
43
|
-
const [openDropdown, setOpenDropdown] = React.useState<string | null>(null);
|
|
44
|
-
|
|
45
|
-
const { app, publicLayout } = config;
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<nav className="sticky top-0 w-full backdrop-blur-xl z-50 isolate" style={{ backgroundColor: 'hsl(var(--background) / 0.8)', boxShadow: '0 1px 0 0 hsl(var(--border))', zIndex: 50 }}>
|
|
49
|
-
<div className="w-full px-4 sm:px-6 lg:px-8">
|
|
50
|
-
<div className="flex items-center justify-between py-2 min-h-[40px]">
|
|
51
|
-
{/* Left side - Logo and Navigation Menu */}
|
|
52
|
-
<div className="flex items-center gap-6">
|
|
53
|
-
{/* Logo */}
|
|
54
|
-
<Link
|
|
55
|
-
href={publicLayout.navigation.homePath}
|
|
56
|
-
className="flex items-center gap-3 group"
|
|
57
|
-
>
|
|
58
|
-
<img
|
|
59
|
-
src={app.logoPath}
|
|
60
|
-
alt={`${app.name} Logo`}
|
|
61
|
-
className="h-8 w-auto object-contain transition-transform duration-300 group-hover:scale-105"
|
|
62
|
-
/>
|
|
63
|
-
<span className="text-xl font-bold transition-colors duration-300 text-foreground group-hover:text-primary">
|
|
64
|
-
{app.name}
|
|
65
|
-
</span>
|
|
66
|
-
</Link>
|
|
67
|
-
|
|
68
|
-
{/* Desktop Navigation Menu */}
|
|
69
|
-
{!isMobile && (
|
|
70
|
-
<div className="flex items-center gap-1">
|
|
71
|
-
{publicLayout.navigation.menuSections.map((section) => {
|
|
72
|
-
// Single item section - render as direct link
|
|
73
|
-
if (section.items.length === 1) {
|
|
74
|
-
const item = section.items[0];
|
|
75
|
-
if (!item) return null;
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<Link
|
|
79
|
-
key={section.title}
|
|
80
|
-
href={item.path}
|
|
81
|
-
className={cn(
|
|
82
|
-
'inline-flex h-9 items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50',
|
|
83
|
-
isActive(item.path) && 'bg-accent text-accent-foreground'
|
|
84
|
-
)}
|
|
85
|
-
>
|
|
86
|
-
{item.label}
|
|
87
|
-
</Link>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Multiple items - render as dropdown menu
|
|
92
|
-
return (
|
|
93
|
-
<div
|
|
94
|
-
key={section.title}
|
|
95
|
-
onMouseEnter={() => setOpenDropdown(section.title)}
|
|
96
|
-
onMouseLeave={() => setOpenDropdown(null)}
|
|
97
|
-
>
|
|
98
|
-
<DropdownMenu
|
|
99
|
-
open={openDropdown === section.title}
|
|
100
|
-
onOpenChange={(open) => setOpenDropdown(open ? section.title : null)}
|
|
101
|
-
modal={false}
|
|
102
|
-
>
|
|
103
|
-
<DropdownMenuTrigger
|
|
104
|
-
className={cn(
|
|
105
|
-
"inline-flex h-9 items-center justify-center gap-1 rounded-md px-4 py-2 text-sm font-medium transition-colors focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50",
|
|
106
|
-
openDropdown === section.title ? "bg-accent text-accent-foreground" : "hover:bg-accent hover:text-accent-foreground"
|
|
107
|
-
)}
|
|
108
|
-
>
|
|
109
|
-
{section.title}
|
|
110
|
-
<ChevronDown className="h-3 w-3 transition-transform duration-200 group-data-[state=open]:rotate-180" />
|
|
111
|
-
</DropdownMenuTrigger>
|
|
112
|
-
<DropdownMenuContent
|
|
113
|
-
align="start"
|
|
114
|
-
sideOffset={0}
|
|
115
|
-
className="p-2"
|
|
116
|
-
style={{
|
|
117
|
-
minWidth: '250px',
|
|
118
|
-
backdropFilter: 'blur(24px)',
|
|
119
|
-
WebkitBackdropFilter: 'blur(24px)'
|
|
120
|
-
}}
|
|
121
|
-
>
|
|
122
|
-
{section.items.map((item) => (
|
|
123
|
-
<DropdownMenuItem key={item.path} asChild>
|
|
124
|
-
<Link
|
|
125
|
-
href={item.path}
|
|
126
|
-
className={cn(
|
|
127
|
-
'cursor-pointer w-full hover:bg-accent hover:text-accent-foreground transition-colors text-base px-4 py-3 rounded-md',
|
|
128
|
-
isActive(item.path) && 'bg-accent/50'
|
|
129
|
-
)}
|
|
130
|
-
>
|
|
131
|
-
{item.label}
|
|
132
|
-
</Link>
|
|
133
|
-
</DropdownMenuItem>
|
|
134
|
-
))}
|
|
135
|
-
</DropdownMenuContent>
|
|
136
|
-
</DropdownMenu>
|
|
137
|
-
</div>
|
|
138
|
-
);
|
|
139
|
-
})}
|
|
140
|
-
</div>
|
|
141
|
-
)}
|
|
142
|
-
</div>
|
|
143
|
-
|
|
144
|
-
{/* Right side - Theme Toggle & User Menu */}
|
|
145
|
-
{!isMobile && (
|
|
146
|
-
<div className="flex items-center gap-2">
|
|
147
|
-
<ThemeToggle />
|
|
148
|
-
<UserMenu variant="desktop" />
|
|
149
|
-
</div>
|
|
150
|
-
)}
|
|
151
|
-
|
|
152
|
-
{/* Mobile Menu Button - Only visible on mobile */}
|
|
153
|
-
{isMobile && (
|
|
154
|
-
<button
|
|
155
|
-
onClick={toggleMobileDrawer}
|
|
156
|
-
className="p-2 rounded-md transition-colors hover:bg-accent text-foreground hover:text-primary"
|
|
157
|
-
aria-label="Toggle mobile menu"
|
|
158
|
-
>
|
|
159
|
-
<Menu className="size-6" />
|
|
160
|
-
</button>
|
|
161
|
-
)}
|
|
162
|
-
</div>
|
|
163
|
-
</div>
|
|
164
|
-
|
|
165
|
-
{/* Mobile Drawer */}
|
|
166
|
-
<MobileDrawer />
|
|
167
|
-
</nav>
|
|
168
|
-
);
|
|
169
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
/**
|
|
3
|
-
* Core Providers
|
|
4
|
-
*
|
|
5
|
-
* Wraps application with all necessary providers
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use client';
|
|
9
|
-
|
|
10
|
-
import React, { ReactNode } from 'react';
|
|
11
|
-
import { ThemeProvider, Toaster } from '@djangocfg/ui';
|
|
12
|
-
import { AuthProvider } from '../../../auth';
|
|
13
|
-
import { ErrorTrackingProvider } from '../../../validation';
|
|
14
|
-
import type { AppLayoutConfig } from '../types';
|
|
15
|
-
import type { ValidationErrorConfig, CORSErrorConfig, NetworkErrorConfig } from '../../../validation';
|
|
16
|
-
|
|
17
|
-
export interface CoreProvidersProps {
|
|
18
|
-
children: ReactNode;
|
|
19
|
-
config: AppLayoutConfig;
|
|
20
|
-
/**
|
|
21
|
-
* Validation error tracking configuration
|
|
22
|
-
* @default { enabled: true, showToast: true, maxErrors: 50 }
|
|
23
|
-
*/
|
|
24
|
-
validation?: Partial<ValidationErrorConfig>;
|
|
25
|
-
/**
|
|
26
|
-
* CORS error tracking configuration
|
|
27
|
-
* @default { enabled: true, showToast: true }
|
|
28
|
-
*/
|
|
29
|
-
cors?: Partial<CORSErrorConfig>;
|
|
30
|
-
/**
|
|
31
|
-
* Network error tracking configuration
|
|
32
|
-
* @default { enabled: false }
|
|
33
|
-
*/
|
|
34
|
-
network?: Partial<NetworkErrorConfig>;
|
|
35
|
-
/**
|
|
36
|
-
* Custom error handler for all error types
|
|
37
|
-
*/
|
|
38
|
-
onError?: (error: any) => boolean | void;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Core Providers Wrapper
|
|
43
|
-
*
|
|
44
|
-
* Provides:
|
|
45
|
-
* - ThemeProvider (dark/light mode)
|
|
46
|
-
* - AuthProvider (authentication)
|
|
47
|
-
* - ErrorTrackingProvider (validation, CORS & network error tracking)
|
|
48
|
-
* - Toaster (notifications)
|
|
49
|
-
*/
|
|
50
|
-
export function CoreProviders({ children, config, validation, cors, network, onError }: CoreProvidersProps) {
|
|
51
|
-
return (
|
|
52
|
-
<ThemeProvider
|
|
53
|
-
defaultTheme={config.theme?.defaultTheme}
|
|
54
|
-
storageKey={config.theme?.storageKey}
|
|
55
|
-
>
|
|
56
|
-
<AuthProvider
|
|
57
|
-
config={{
|
|
58
|
-
apiUrl: config.api.baseUrl,
|
|
59
|
-
routes: {
|
|
60
|
-
auth: config.routes.auth,
|
|
61
|
-
defaultCallback: config.routes.defaultCallback,
|
|
62
|
-
defaultAuthCallback: config.routes.defaultAuthCallback || config.routes.defaultCallback,
|
|
63
|
-
},
|
|
64
|
-
}}
|
|
65
|
-
>
|
|
66
|
-
<ErrorTrackingProvider
|
|
67
|
-
validation={validation}
|
|
68
|
-
cors={cors}
|
|
69
|
-
network={network}
|
|
70
|
-
onError={onError}
|
|
71
|
-
>
|
|
72
|
-
{children}
|
|
73
|
-
</ErrorTrackingProvider>
|
|
74
|
-
</AuthProvider>
|
|
75
|
-
|
|
76
|
-
{/* Global toast notifications */}
|
|
77
|
-
<Toaster />
|
|
78
|
-
</ThemeProvider>
|
|
79
|
-
);
|
|
80
|
-
}
|