@djangocfg/layouts 1.4.30 → 2.0.2
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 +5 -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
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @djangocfg/layouts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Simple, straightforward layout components for Next.js - import and use with props.
|
|
4
4
|
|
|
5
5
|
**Part of [DjangoCFG](https://djangocfg.com)** — modern Django framework for production-ready SaaS applications.
|
|
6
6
|
|
|
@@ -12,23 +12,67 @@ pnpm add @djangocfg/layouts
|
|
|
12
12
|
|
|
13
13
|
## Layouts
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
Simple, props-based layout components. No complex configs needed!
|
|
16
|
+
|
|
17
|
+
### Basic Layouts
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
```tsx
|
|
20
|
+
import { PublicLayout, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
|
|
21
|
+
|
|
22
|
+
// Public page
|
|
23
|
+
<PublicLayout
|
|
24
|
+
logo="/logo.svg"
|
|
25
|
+
siteName="My App"
|
|
26
|
+
navigation={navItems}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</PublicLayout>
|
|
30
|
+
|
|
31
|
+
// Private page
|
|
32
|
+
<PrivateLayout
|
|
33
|
+
sidebar={{ items: menuItems }}
|
|
34
|
+
header={{ title: 'Dashboard' }}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
</PrivateLayout>
|
|
38
|
+
|
|
39
|
+
// Auth page
|
|
40
|
+
<AuthLayout
|
|
41
|
+
logo="/logo.svg"
|
|
42
|
+
title="Sign In"
|
|
43
|
+
subtitle="Welcome back"
|
|
44
|
+
>
|
|
45
|
+
<LoginForm />
|
|
46
|
+
</AuthLayout>
|
|
19
47
|
```
|
|
20
48
|
|
|
21
49
|
| Layout | Description |
|
|
22
50
|
|--------|-------------|
|
|
23
|
-
| `
|
|
51
|
+
| `PublicLayout` | Public pages (home, docs, contact, legal) |
|
|
52
|
+
| `PrivateLayout` | Authenticated user pages (dashboard, profile) |
|
|
53
|
+
| `AuthLayout` | Authentication pages (login, signup, password reset) |
|
|
54
|
+
| `AdminLayout` | Admin panel layout |
|
|
55
|
+
| `AppLayout` | Smart layout router with config-based setup |
|
|
24
56
|
| `ProfileLayout` | User profile pages |
|
|
25
|
-
| `SupportLayout` | Support/help pages |
|
|
26
|
-
| `PaymentsLayout` | Payment flows |
|
|
27
|
-
|
|
28
|
-
|
|
57
|
+
| `SupportLayout` | Support/help pages with ticket system |
|
|
58
|
+
| `PaymentsLayout` | Payment flows and billing |
|
|
59
|
+
|
|
60
|
+
### AppLayout
|
|
61
|
+
|
|
62
|
+
Smart layout component that automatically selects the right layout based on config:
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { AppLayout } from '@djangocfg/layouts';
|
|
66
|
+
|
|
67
|
+
<AppLayout config={appLayoutConfig}>
|
|
68
|
+
{children}
|
|
69
|
+
</AppLayout>
|
|
70
|
+
```
|
|
29
71
|
|
|
30
72
|
## Auth
|
|
31
73
|
|
|
74
|
+
Complete authentication system with context, hooks, and middleware.
|
|
75
|
+
|
|
32
76
|
```tsx
|
|
33
77
|
import { AuthProvider, useAuth } from '@djangocfg/layouts/auth';
|
|
34
78
|
import { AuthDialog } from '@djangocfg/layouts/snippets';
|
|
@@ -43,8 +87,28 @@ import { AuthDialog } from '@djangocfg/layouts/snippets';
|
|
|
43
87
|
| `AuthProvider` | Auth context provider |
|
|
44
88
|
| `useAuth` | Auth hook (user, login, logout) |
|
|
45
89
|
| `useAuthGuard` | Route protection hook |
|
|
90
|
+
| `useAuthRedirect` | Redirect hook for auth flows |
|
|
91
|
+
| `useAutoAuth` | Auto-authentication hook |
|
|
46
92
|
| `authMiddleware` | Next.js middleware |
|
|
47
93
|
|
|
94
|
+
### Auth Context
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { AccountsProvider, useAccountsContext } from '@djangocfg/layouts/auth/context';
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Auth Hooks
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import {
|
|
104
|
+
useAuthForm,
|
|
105
|
+
useAuthGuard,
|
|
106
|
+
useAuthRedirect,
|
|
107
|
+
useAutoAuth,
|
|
108
|
+
useProfileCache
|
|
109
|
+
} from '@djangocfg/layouts/auth/hooks';
|
|
110
|
+
```
|
|
111
|
+
|
|
48
112
|
## Analytics
|
|
49
113
|
|
|
50
114
|
Google Analytics integration via `react-ga4`. Auto-tracks pageviews and user sessions.
|
|
@@ -102,24 +166,52 @@ Built-in tracking for:
|
|
|
102
166
|
|
|
103
167
|
## Snippets
|
|
104
168
|
|
|
169
|
+
Reusable UI components ready to use.
|
|
170
|
+
|
|
105
171
|
```tsx
|
|
106
|
-
import {
|
|
172
|
+
import {
|
|
173
|
+
Breadcrumbs,
|
|
174
|
+
AuthDialog,
|
|
175
|
+
ContactForm,
|
|
176
|
+
ContactPage,
|
|
177
|
+
KnowledgeChat,
|
|
178
|
+
ChatWidget
|
|
179
|
+
} from '@djangocfg/layouts/snippets';
|
|
107
180
|
```
|
|
108
181
|
|
|
109
182
|
| Snippet | Description |
|
|
110
183
|
|---------|-------------|
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
184
|
+
| `Breadcrumbs` | Navigation breadcrumbs with automatic path generation |
|
|
185
|
+
| `AuthDialog` | Auth modal (login/register) with event-based triggers |
|
|
186
|
+
| `ContactForm` | Contact form with API integration and validation |
|
|
187
|
+
| `ContactPage` | Complete contact page with form and info cards |
|
|
188
|
+
| `ContactInfo` | Contact details card component |
|
|
189
|
+
| `KnowledgeChat` | AI-powered chat widget with knowledge base |
|
|
190
|
+
| `ChatWidget` | Chat widget component |
|
|
118
191
|
| `AnalyticsProvider` | Analytics wrapper component |
|
|
119
192
|
|
|
193
|
+
### Breadcrumbs
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import Breadcrumbs from '@djangocfg/layouts/snippets';
|
|
197
|
+
|
|
198
|
+
// Auto-generate from current path
|
|
199
|
+
<Breadcrumbs />
|
|
200
|
+
|
|
201
|
+
// Or provide custom items
|
|
202
|
+
<Breadcrumbs
|
|
203
|
+
items={[
|
|
204
|
+
{ path: '/', label: 'Home', isActive: false },
|
|
205
|
+
{ path: '/products', label: 'Products', isActive: true },
|
|
206
|
+
]}
|
|
207
|
+
/>
|
|
208
|
+
```
|
|
209
|
+
|
|
120
210
|
### ContactPage
|
|
121
211
|
|
|
122
212
|
```tsx
|
|
213
|
+
import { ContactPage } from '@djangocfg/layouts/snippets';
|
|
214
|
+
|
|
123
215
|
// Minimal - all defaults configured
|
|
124
216
|
<ContactPage />
|
|
125
217
|
|
|
@@ -135,13 +227,75 @@ import { ContactPage, VideoPlayer, Breadcrumbs } from '@djangocfg/layouts/snippe
|
|
|
135
227
|
- localStorage draft saving
|
|
136
228
|
- Success state with icon
|
|
137
229
|
- Zod validation from `@djangocfg/api`
|
|
230
|
+
- Contact info cards (Email, WhatsApp, Location)
|
|
231
|
+
- Optional Calendly integration
|
|
232
|
+
|
|
233
|
+
### AuthDialog
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
import { AuthDialog, openAuthDialog } from '@djangocfg/layouts/snippets';
|
|
237
|
+
|
|
238
|
+
// Add to layout
|
|
239
|
+
<AuthDialog authPath="/auth" />
|
|
240
|
+
|
|
241
|
+
// Trigger from anywhere
|
|
242
|
+
openAuthDialog({ message: 'Sign in to continue' });
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Chat Widget
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
import { ChatWidget, ChatUIProvider, useChatUI } from '@djangocfg/layouts/snippets';
|
|
249
|
+
|
|
250
|
+
<ChatUIProvider>
|
|
251
|
+
<ChatWidget />
|
|
252
|
+
</ChatUIProvider>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Components
|
|
256
|
+
|
|
257
|
+
Utility components for common use cases.
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
import {
|
|
261
|
+
ErrorBoundary,
|
|
262
|
+
PageProgress,
|
|
263
|
+
JsonLd,
|
|
264
|
+
LucideIcon
|
|
265
|
+
} from '@djangocfg/layouts/components';
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
| Component | Description |
|
|
269
|
+
|-----------|-------------|
|
|
270
|
+
| `ErrorBoundary` | React error boundary component |
|
|
271
|
+
| `PageProgress` | Page loading progress indicator |
|
|
272
|
+
| `JsonLd` | JSON-LD structured data component |
|
|
273
|
+
| `LucideIcon` | Lucide icon wrapper component |
|
|
138
274
|
|
|
139
275
|
## Validation
|
|
140
276
|
|
|
141
277
|
Error tracking and validation utilities.
|
|
142
278
|
|
|
143
279
|
```tsx
|
|
144
|
-
import {
|
|
280
|
+
import {
|
|
281
|
+
ErrorTrackingProvider,
|
|
282
|
+
useErrors,
|
|
283
|
+
ErrorButtons,
|
|
284
|
+
ErrorToast
|
|
285
|
+
} from '@djangocfg/layouts/validation';
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Error Tracking
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
import { ErrorTrackingProvider, useErrors } from '@djangocfg/layouts/validation';
|
|
292
|
+
|
|
293
|
+
<ErrorTrackingProvider>
|
|
294
|
+
<YourApp />
|
|
295
|
+
</ErrorTrackingProvider>
|
|
296
|
+
|
|
297
|
+
// In components
|
|
298
|
+
const { addError, clearErrors, errors } = useErrors();
|
|
145
299
|
```
|
|
146
300
|
|
|
147
301
|
## Exports
|
|
@@ -151,10 +305,15 @@ import { ValidationErrorConfig, CORSErrorConfig, NetworkErrorConfig } from '@dja
|
|
|
151
305
|
| `@djangocfg/layouts` | Main exports (all modules) |
|
|
152
306
|
| `@djangocfg/layouts/layouts` | Layout components |
|
|
153
307
|
| `@djangocfg/layouts/auth` | Auth context & hooks |
|
|
308
|
+
| `@djangocfg/layouts/auth/context` | Auth context only |
|
|
309
|
+
| `@djangocfg/layouts/auth/hooks` | Auth hooks only |
|
|
154
310
|
| `@djangocfg/layouts/snippets` | Reusable components + Analytics |
|
|
311
|
+
| `@djangocfg/layouts/components` | Utility components |
|
|
312
|
+
| `@djangocfg/layouts/validation` | Validation & error tracking |
|
|
155
313
|
| `@djangocfg/layouts/utils` | Utilities |
|
|
156
314
|
| `@djangocfg/layouts/types` | TypeScript types |
|
|
157
315
|
| `@djangocfg/layouts/styles` | CSS |
|
|
316
|
+
| `@djangocfg/layouts/styles/dashboard` | Dashboard-specific CSS |
|
|
158
317
|
|
|
159
318
|
## Requirements
|
|
160
319
|
|
|
@@ -162,3 +321,103 @@ import { ValidationErrorConfig, CORSErrorConfig, NetworkErrorConfig } from '@dja
|
|
|
162
321
|
- React >= 19
|
|
163
322
|
- Tailwind CSS >= 4
|
|
164
323
|
- react-ga4 (bundled)
|
|
324
|
+
- @djangocfg/ui (peer dependency)
|
|
325
|
+
- @djangocfg/api (peer dependency)
|
|
326
|
+
|
|
327
|
+
## Philosophy
|
|
328
|
+
|
|
329
|
+
This package follows a **simple, props-based approach**:
|
|
330
|
+
|
|
331
|
+
- ✅ **No complex configs** - just pass props
|
|
332
|
+
- ✅ **Type-safe** - full TypeScript support
|
|
333
|
+
- ✅ **Flexible** - compose layouts as needed
|
|
334
|
+
- ✅ **Production-ready** - built for real apps
|
|
335
|
+
|
|
336
|
+
## Examples
|
|
337
|
+
|
|
338
|
+
### Complete App Setup
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
// app/layout.tsx
|
|
342
|
+
import { AppLayout } from '@djangocfg/layouts';
|
|
343
|
+
import { appLayoutConfig } from './_config/appLayoutConfig';
|
|
344
|
+
|
|
345
|
+
export default function RootLayout({ children }) {
|
|
346
|
+
return (
|
|
347
|
+
<AppLayout config={appLayoutConfig}>
|
|
348
|
+
{children}
|
|
349
|
+
</AppLayout>
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Public Page
|
|
355
|
+
|
|
356
|
+
```tsx
|
|
357
|
+
// app/page.tsx
|
|
358
|
+
import { PublicLayout } from '@djangocfg/layouts';
|
|
359
|
+
|
|
360
|
+
export default function HomePage() {
|
|
361
|
+
return (
|
|
362
|
+
<PublicLayout
|
|
363
|
+
logo="/logo.svg"
|
|
364
|
+
siteName="My App"
|
|
365
|
+
navigation={[
|
|
366
|
+
{ label: 'Home', href: '/' },
|
|
367
|
+
{ label: 'Docs', href: '/docs' },
|
|
368
|
+
{ label: 'Contact', href: '/contact' }
|
|
369
|
+
]}
|
|
370
|
+
>
|
|
371
|
+
<h1>Welcome</h1>
|
|
372
|
+
</PublicLayout>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Private Dashboard
|
|
378
|
+
|
|
379
|
+
```tsx
|
|
380
|
+
// app/dashboard/page.tsx
|
|
381
|
+
import { PrivateLayout } from '@djangocfg/layouts';
|
|
382
|
+
|
|
383
|
+
export default function DashboardPage() {
|
|
384
|
+
return (
|
|
385
|
+
<PrivateLayout
|
|
386
|
+
sidebar={{
|
|
387
|
+
items: [
|
|
388
|
+
{ label: 'Dashboard', href: '/dashboard', icon: 'LayoutDashboard' },
|
|
389
|
+
{ label: 'Settings', href: '/settings', icon: 'Settings' }
|
|
390
|
+
]
|
|
391
|
+
}}
|
|
392
|
+
header={{
|
|
393
|
+
title: 'Dashboard',
|
|
394
|
+
userMenu: { name: 'John Doe', email: 'john@example.com' }
|
|
395
|
+
}}
|
|
396
|
+
>
|
|
397
|
+
<h1>Dashboard</h1>
|
|
398
|
+
</PrivateLayout>
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Auth Page
|
|
404
|
+
|
|
405
|
+
```tsx
|
|
406
|
+
// app/auth/login/page.tsx
|
|
407
|
+
import { AuthLayout } from '@djangocfg/layouts';
|
|
408
|
+
import { LoginForm } from './_components/LoginForm';
|
|
409
|
+
|
|
410
|
+
export default function LoginPage() {
|
|
411
|
+
return (
|
|
412
|
+
<AuthLayout
|
|
413
|
+
logo="/logo.svg"
|
|
414
|
+
siteName="My App"
|
|
415
|
+
title="Sign In"
|
|
416
|
+
subtitle="Welcome back! Please sign in to continue."
|
|
417
|
+
>
|
|
418
|
+
<LoginForm />
|
|
419
|
+
</AuthLayout>
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"description": "Simple, straightforward layout components for Next.js - import and use with props",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"layouts",
|
|
7
7
|
"dashboard",
|
|
@@ -10,10 +10,7 @@
|
|
|
10
10
|
"nextjs",
|
|
11
11
|
"react",
|
|
12
12
|
"tailwindcss",
|
|
13
|
-
"typescript"
|
|
14
|
-
"django",
|
|
15
|
-
"templates",
|
|
16
|
-
"ui-kit"
|
|
13
|
+
"typescript"
|
|
17
14
|
],
|
|
18
15
|
"author": {
|
|
19
16
|
"name": "DjangoCFG",
|
|
@@ -37,6 +34,11 @@
|
|
|
37
34
|
"import": "./src/index.ts",
|
|
38
35
|
"default": "./src/index.ts"
|
|
39
36
|
},
|
|
37
|
+
"./layouts": {
|
|
38
|
+
"types": "./src/layouts/index.ts",
|
|
39
|
+
"import": "./src/layouts/index.ts",
|
|
40
|
+
"default": "./src/layouts/index.ts"
|
|
41
|
+
},
|
|
40
42
|
"./auth": {
|
|
41
43
|
"types": "./src/auth/index.ts",
|
|
42
44
|
"import": "./src/auth/index.ts",
|
|
@@ -52,21 +54,11 @@
|
|
|
52
54
|
"import": "./src/auth/hooks/index.ts",
|
|
53
55
|
"default": "./src/auth/hooks/index.ts"
|
|
54
56
|
},
|
|
55
|
-
"./layouts": {
|
|
56
|
-
"types": "./src/layouts/index.ts",
|
|
57
|
-
"import": "./src/layouts/index.ts",
|
|
58
|
-
"default": "./src/layouts/index.ts"
|
|
59
|
-
},
|
|
60
57
|
"./snippets": {
|
|
61
58
|
"types": "./src/snippets/index.ts",
|
|
62
59
|
"import": "./src/snippets/index.ts",
|
|
63
60
|
"default": "./src/snippets/index.ts"
|
|
64
61
|
},
|
|
65
|
-
"./types": {
|
|
66
|
-
"types": "./src/types/index.ts",
|
|
67
|
-
"import": "./src/types/index.ts",
|
|
68
|
-
"default": "./src/types/index.ts"
|
|
69
|
-
},
|
|
70
62
|
"./utils": {
|
|
71
63
|
"types": "./src/utils/index.ts",
|
|
72
64
|
"import": "./src/utils/index.ts",
|
|
@@ -85,9 +77,9 @@
|
|
|
85
77
|
"check": "tsc --noEmit"
|
|
86
78
|
},
|
|
87
79
|
"peerDependencies": {
|
|
88
|
-
"@djangocfg/api": "^1.4.
|
|
89
|
-
"@djangocfg/
|
|
90
|
-
"@djangocfg/ui": "^1.4.
|
|
80
|
+
"@djangocfg/api": "^1.4.32",
|
|
81
|
+
"@djangocfg/centrifugo": "^1.4.32",
|
|
82
|
+
"@djangocfg/ui": "^1.4.32",
|
|
91
83
|
"@hookform/resolvers": "^5.2.0",
|
|
92
84
|
"consola": "^3.4.2",
|
|
93
85
|
"lucide-react": "^0.468.0",
|
|
@@ -103,13 +95,11 @@
|
|
|
103
95
|
"zod": "^4.0.10"
|
|
104
96
|
},
|
|
105
97
|
"dependencies": {
|
|
106
|
-
"
|
|
107
|
-
"media-icons": "next",
|
|
108
|
-
"react-ga4": "^2.1.0",
|
|
109
|
-
"vidstack": "next"
|
|
98
|
+
"react-ga4": "^2.1.0"
|
|
110
99
|
},
|
|
111
100
|
"devDependencies": {
|
|
112
|
-
"@djangocfg/
|
|
101
|
+
"@djangocfg/centrifugo": "^1.4.32",
|
|
102
|
+
"@djangocfg/typescript-config": "^1.4.32",
|
|
113
103
|
"@types/node": "^24.7.2",
|
|
114
104
|
"@types/react": "19.2.2",
|
|
115
105
|
"@types/react-dom": "19.2.1",
|
|
@@ -120,3 +110,4 @@
|
|
|
120
110
|
"access": "public"
|
|
121
111
|
}
|
|
122
112
|
}
|
|
113
|
+
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
'use client';
|
|
3
3
|
|
|
4
|
-
import { useRouter, usePathname
|
|
4
|
+
import { useRouter, usePathname } from 'next/navigation';
|
|
5
5
|
import React, {
|
|
6
6
|
createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState
|
|
7
7
|
} from 'react';
|
|
8
8
|
|
|
9
9
|
import { api, Enums } from '@djangocfg/api';
|
|
10
10
|
import { useAccountsContext, AccountsProvider } from './AccountsContext';
|
|
11
|
-
import { useLocalStorage } from '@djangocfg/ui/hooks';
|
|
11
|
+
import { useLocalStorage, useQueryParams } from '@djangocfg/ui/hooks';
|
|
12
12
|
import { getCachedProfile, clearProfileCache } from '../hooks/useProfileCache';
|
|
13
13
|
|
|
14
14
|
import { authLogger } from '../../utils/logger';
|
|
@@ -52,7 +52,7 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
|
|
|
52
52
|
const [initialized, setInitialized] = useState(false);
|
|
53
53
|
const router = useRouter();
|
|
54
54
|
const pathname = usePathname();
|
|
55
|
-
const
|
|
55
|
+
const queryParams = useQueryParams();
|
|
56
56
|
|
|
57
57
|
// Use localStorage hooks for email, phone, and redirect
|
|
58
58
|
const [storedEmail, setStoredEmail, clearStoredEmail] = useLocalStorage<string | null>(EMAIL_STORAGE_KEY, null);
|
|
@@ -232,7 +232,7 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
|
|
|
232
232
|
const isAuthenticated = api.isAuthenticated();
|
|
233
233
|
const authRoute = config?.routes?.auth || defaultRoutes.auth;
|
|
234
234
|
const isAuthPage = pathname === authRoute;
|
|
235
|
-
const flowParam =
|
|
235
|
+
const flowParam = queryParams.get('flow');
|
|
236
236
|
|
|
237
237
|
// Only redirect authenticated users away from auth page if they're not in a flow
|
|
238
238
|
// This prevents interference with OTP verification flow
|
|
@@ -240,7 +240,7 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
|
|
|
240
240
|
const callbackUrl = config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
|
|
241
241
|
window.location.href = callbackUrl;
|
|
242
242
|
}
|
|
243
|
-
}, [initialized, pathname,
|
|
243
|
+
}, [initialized, pathname, queryParams, config?.routes]);
|
|
244
244
|
|
|
245
245
|
const pushToDefaultCallbackUrl = useCallback(() => {
|
|
246
246
|
const callbackUrl = config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useEffect } from 'react';
|
|
4
|
-
import { usePathname, useRouter
|
|
4
|
+
import { usePathname, useRouter } from 'next/navigation';
|
|
5
|
+
import { useQueryParams } from '@djangocfg/ui/hooks';
|
|
5
6
|
import { authLogger } from '../../utils/logger';
|
|
6
7
|
|
|
7
8
|
export interface UseAutoAuthOptions {
|
|
@@ -15,17 +16,17 @@ export interface UseAutoAuthOptions {
|
|
|
15
16
|
*/
|
|
16
17
|
export const useAutoAuth = (options: UseAutoAuthOptions = {}) => {
|
|
17
18
|
const { onOTPDetected, cleanupUrl = true } = options;
|
|
18
|
-
const
|
|
19
|
+
const queryParams = useQueryParams();
|
|
19
20
|
const pathname = usePathname();
|
|
20
21
|
const router = useRouter();
|
|
21
22
|
|
|
22
|
-
const isReady = !!pathname && !!
|
|
23
|
-
const hasOTP = !!(
|
|
23
|
+
const isReady = !!pathname && !!queryParams.get('otp');
|
|
24
|
+
const hasOTP = !!(queryParams.get('otp'));
|
|
24
25
|
|
|
25
26
|
useEffect(() => {
|
|
26
27
|
if (!isReady) return;
|
|
27
28
|
|
|
28
|
-
const queryOtp =
|
|
29
|
+
const queryOtp = queryParams.get('otp') as string;
|
|
29
30
|
|
|
30
31
|
// Handle OTP detection
|
|
31
32
|
if (queryOtp && typeof queryOtp === 'string' && queryOtp.length === 6) {
|
|
@@ -35,11 +36,11 @@ export const useAutoAuth = (options: UseAutoAuthOptions = {}) => {
|
|
|
35
36
|
|
|
36
37
|
// Clean up URL to remove sensitive params for security
|
|
37
38
|
if (cleanupUrl && queryOtp) {
|
|
38
|
-
const cleanQuery = Object.fromEntries(
|
|
39
|
+
const cleanQuery = Object.fromEntries(queryParams.entries());
|
|
39
40
|
delete cleanQuery.otp;
|
|
40
41
|
router.push(`${pathname}?${new URLSearchParams(cleanQuery).toString()}`);
|
|
41
42
|
}
|
|
42
|
-
}, [pathname,
|
|
43
|
+
}, [pathname, queryParams, onOTPDetected, cleanupUrl, router]);
|
|
43
44
|
|
|
44
45
|
return {
|
|
45
46
|
isReady,
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple ErrorBoundary Component
|
|
3
|
+
*
|
|
4
|
+
* Catches React errors and displays a fallback UI
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use client';
|
|
8
|
+
|
|
9
|
+
import React, { Component, ReactNode, ErrorInfo } from 'react';
|
|
10
|
+
|
|
11
|
+
interface ErrorBoundaryProps {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
supportEmail?: string;
|
|
14
|
+
onError?: (error: Error, errorInfo: ErrorInfo) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ErrorBoundaryState {
|
|
18
|
+
hasError: boolean;
|
|
19
|
+
error: Error | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
23
|
+
constructor(props: ErrorBoundaryProps) {
|
|
24
|
+
super(props);
|
|
25
|
+
this.state = { hasError: false, error: null };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
29
|
+
return { hasError: true, error };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
33
|
+
// Call custom error handler if provided
|
|
34
|
+
if (this.props.onError) {
|
|
35
|
+
this.props.onError(error, errorInfo);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Log to console in development
|
|
39
|
+
if (process.env.NODE_ENV === 'development') {
|
|
40
|
+
console.error('ErrorBoundary caught an error:', error, errorInfo);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
render() {
|
|
45
|
+
if (this.state.hasError) {
|
|
46
|
+
return (
|
|
47
|
+
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
|
48
|
+
<div className="max-w-md w-full space-y-4 text-center">
|
|
49
|
+
<h1 className="text-2xl font-bold text-foreground">Something went wrong</h1>
|
|
50
|
+
<p className="text-muted-foreground">
|
|
51
|
+
We're sorry, but something unexpected happened. Please try refreshing the page.
|
|
52
|
+
</p>
|
|
53
|
+
{this.props.supportEmail && (
|
|
54
|
+
<p className="text-sm text-muted-foreground">
|
|
55
|
+
If the problem persists, please contact{' '}
|
|
56
|
+
<a
|
|
57
|
+
href={`mailto:${this.props.supportEmail}`}
|
|
58
|
+
className="text-primary hover:underline"
|
|
59
|
+
>
|
|
60
|
+
{this.props.supportEmail}
|
|
61
|
+
</a>
|
|
62
|
+
</p>
|
|
63
|
+
)}
|
|
64
|
+
<button
|
|
65
|
+
onClick={() => window.location.reload()}
|
|
66
|
+
className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
|
|
67
|
+
>
|
|
68
|
+
Refresh Page
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return this.props.children;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface JsonLdProps {
|
|
4
|
+
data: Record<string, any> | Record<string, any>[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* JsonLd Component
|
|
9
|
+
*
|
|
10
|
+
* Renders JSON-LD structured data for SEO.
|
|
11
|
+
* Use this for schema.org markup (Organization, WebSite, FAQPage, etc.)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <JsonLd data={{
|
|
16
|
+
* "@context": "https://schema.org",
|
|
17
|
+
* "@type": "Organization",
|
|
18
|
+
* "name": "My Company"
|
|
19
|
+
* }} />
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function JsonLd({ data }: JsonLdProps) {
|
|
23
|
+
return (
|
|
24
|
+
<script
|
|
25
|
+
type="application/ld+json"
|
|
26
|
+
dangerouslySetInnerHTML={{
|
|
27
|
+
__html: JSON.stringify(data, null, 0),
|
|
28
|
+
}}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
}
|