@djangocfg/layouts 2.1.219 → 2.1.222

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @djangocfg/layouts
2
2
 
3
- Simple, straightforward layout components for Next.js - import and use with props.
3
+ Layout components and providers for Next.js apps.
4
4
 
5
5
  **Part of [DjangoCFG](https://djangocfg.com)** — modern Django framework for production-ready SaaS applications.
6
6
 
@@ -10,26 +10,32 @@ Simple, straightforward layout components for Next.js - import and use with prop
10
10
  pnpm add @djangocfg/layouts
11
11
  ```
12
12
 
13
- ## Core Components
13
+ Add to `globals.css` (before `@import "tailwindcss"`):
14
14
 
15
- ### BaseApp
15
+ ```css
16
+ @import "@djangocfg/ui-nextjs/styles";
17
+ @import "@djangocfg/layouts/styles";
18
+ @import "@djangocfg/ui-tools/styles";
19
+ @import "@djangocfg/debuger/styles";
20
+ @import "tailwindcss";
21
+ ```
22
+
23
+ ## BaseApp
16
24
 
17
- Core providers wrapper - use when you need providers without layout routing:
25
+ Core providers wrapper. Use directly when you don't need route-based layout switching.
18
26
 
19
27
  ```tsx
20
28
  import { BaseApp } from '@djangocfg/layouts';
21
29
 
22
- // app/layout.tsx
23
30
  export default function RootLayout({ children }) {
24
31
  return (
25
32
  <html lang="en" suppressHydrationWarning>
26
33
  <body>
27
34
  <BaseApp
28
- theme={{ defaultTheme: 'dark' }}
35
+ project="my-app"
36
+ theme={{ defaultTheme: 'dark', storageKey: 'my-theme' }}
37
+ auth={{ apiUrl: process.env.NEXT_PUBLIC_API_URL }}
29
38
  analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
30
- centrifugo={{ enabled: true, url: process.env.NEXT_PUBLIC_CENTRIFUGO_URL }}
31
- pwaInstall={{ enabled: true, showInstallHint: true }}
32
- mcpChat={{ enabled: true, autoDetectEnvironment: true }}
33
39
  >
34
40
  {children}
35
41
  </BaseApp>
@@ -39,29 +45,35 @@ export default function RootLayout({ children }) {
39
45
  }
40
46
  ```
41
47
 
42
- **Included providers:**
43
- - **ThemeProvider** - Light/dark/system theme management
44
- - **TooltipProvider** - Tooltip positioning context
45
- - **SWRConfig** - Data fetching configuration
46
- - **AuthProvider** - Authentication context (from `@djangocfg/api`)
47
- - **AnalyticsProvider** - Google Analytics (optional)
48
- - **CentrifugoProvider** - WebSocket real-time (optional)
49
- - **PwaProvider** - PWA installation (optional)
50
- - **ErrorTrackingProvider** - Error handling and tracking
51
- - **ErrorBoundary** - React error boundary
52
- - **MonitorProvider** - Frontend error monitoring via `@djangocfg/monitor` (optional)
53
- - **MCP Chat Widget** - AI chat assistant (optional)
54
-
55
- **Global components:**
56
- - **PageProgress** - NProgress bar for route changes
57
- - **Toaster** - Toast notifications container
58
- - **A2HSHint** - PWA install hint (if enabled)
59
-
60
- > **Note:** Auth functionality is provided by `@djangocfg/api` package. See [@djangocfg/api documentation](../api/README.md) for auth usage.
61
-
62
- ### AppLayout
48
+ **Props:**
63
49
 
64
- Smart layout router built on BaseApp - automatically selects layout based on route:
50
+ | Prop | Type | Default | Description |
51
+ |------|------|---------|-------------|
52
+ | `project` | `string` | — | App name — auto-passed to `MonitorProvider` and debug panel |
53
+ | `theme` | `ThemeConfig` | — | Theme config (defaultTheme, storageKey) |
54
+ | `auth` | `AuthConfig` | — | Auth provider config |
55
+ | `analytics` | `AnalyticsConfig` | — | Google Analytics config |
56
+ | `centrifugo` | `CentrifugoConfig` | — | WebSocket real-time config |
57
+ | `errorTracking` | `ErrorTrackingConfig` | — | Validation/CORS/network error capture |
58
+ | `errorBoundary` | `ErrorBoundaryConfig` | enabled | React error boundary |
59
+ | `swr` | `SWRConfigOptions` | — | SWR data fetching config |
60
+ | `pwaInstall` | `PwaInstallConfig` | — | PWA install prompt |
61
+ | `mcpChat` | `McpChatConfig` | — | AI chat widget |
62
+ | `monitor` | `MonitorConfig` | — | Override monitor config (project/environment come from `project` prop by default) |
63
+ | `debug` | `DebugConfig` | enabled | Debug panel config — see below |
64
+
65
+ **Included automatically:**
66
+ - ThemeProvider, TooltipProvider, SWRConfig, DialogProvider
67
+ - AuthProvider + AuthDialog
68
+ - AnalyticsProvider, CentrifugoProvider, PwaProvider
69
+ - ErrorTrackingProvider, ErrorBoundary
70
+ - MonitorProvider (auto-enabled via `project` prop)
71
+ - DebugButton from `@djangocfg/debuger`
72
+ - NextTopLoader, Toaster
73
+
74
+ ## AppLayout
75
+
76
+ Smart layout router — selects layout based on current route. Wraps `BaseApp`, accepts all its props.
65
77
 
66
78
  ```tsx
67
79
  import { AppLayout } from '@djangocfg/layouts';
@@ -69,32 +81,19 @@ import { PublicLayout } from './_layouts/PublicLayout';
69
81
  import { PrivateLayout } from './_layouts/PrivateLayout';
70
82
  import { AdminLayout } from './_layouts/AdminLayout';
71
83
 
72
- // app/layout.tsx
73
84
  export default function RootLayout({ children }) {
74
85
  return (
75
86
  <html lang="en" suppressHydrationWarning>
76
87
  <body>
77
88
  <AppLayout
78
- // Provider configs (passed to BaseApp)
89
+ project="my-app"
79
90
  theme={{ defaultTheme: 'system' }}
80
- analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
81
- pwaInstall={{ enabled: true }}
82
-
83
- // Layout components
84
- publicLayout={{
85
- component: PublicLayout,
86
- enabledPath: ['/', '/legal', '/contact']
87
- }}
88
- privateLayout={{
89
- component: PrivateLayout,
90
- enabledPath: ['/dashboard', '/profile']
91
- }}
92
- adminLayout={{
93
- component: AdminLayout,
94
- enabledPath: '/admin'
95
- }}
96
-
97
- // Skip layout for fullscreen pages (providers still applied)
91
+ auth={{ apiUrl: process.env.NEXT_PUBLIC_API_URL }}
92
+
93
+ publicLayout={{ component: PublicLayout, enabledPath: ['/', '/legal', '/contact'] }}
94
+ privateLayout={{ component: PrivateLayout, enabledPath: ['/dashboard', '/profile'] }}
95
+ adminLayout={{ component: AdminLayout, enabledPath: '/admin' }}
96
+
98
97
  noLayoutPaths={['/private/terminal', '/embed']}
99
98
  >
100
99
  {children}
@@ -105,709 +104,182 @@ export default function RootLayout({ children }) {
105
104
  }
106
105
  ```
107
106
 
108
- **Monitor prop** — pass `monitor` to enable frontend error monitoring (installs `window.monitor` in DevTools):
109
-
110
- ```tsx
111
- <AppLayout
112
- monitor={{
113
- project: 'my-app',
114
- environment: process.env.NODE_ENV,
115
- }}
116
- // ...
117
- >
118
- {children}
119
- </AppLayout>
120
- ```
121
-
122
- After mount, `window.monitor.error(...)`, `.warn(...)`, `.flush()`, `.status()` are available in the browser console.
123
-
124
107
  **Layout priority:** Admin → Private → Public → Fallback
125
108
 
126
- **noLayoutPaths:** Paths that render without any layout wrapper. Useful for fullscreen pages (terminal, embed, print). Providers (auth, theme, centrifugo) are still applied.
127
-
128
- ## Layouts
109
+ **`noLayoutPaths`** render without layout wrapper (providers still active). Useful for fullscreen pages.
129
110
 
130
- Simple, props-based layout components. No complex configs needed!
131
-
132
- ### Available Layouts
111
+ ### i18n
133
112
 
134
113
  ```tsx
135
- import { PublicLayout, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
114
+ import { useLocaleSwitcher } from '@djangocfg/nextjs/i18n/client';
136
115
 
137
- // Public page
138
- <PublicLayout
139
- logo="/logo.svg"
140
- siteName="My App"
141
- navigation={navItems}
142
- >
143
- {children}
144
- </PublicLayout>
145
-
146
- // Private page
147
- <PrivateLayout
148
- sidebar={{
149
- groups: [
150
- { label: 'Main', items: menuItems }
151
- ]
152
- }}
153
- header={{ title: 'Dashboard' }}
154
- >
155
- {children}
156
- </PrivateLayout>
116
+ const { locale, locales, changeLocale } = useLocaleSwitcher();
157
117
 
158
- // Auth page
159
- <AuthLayout
160
- logo="/logo.svg"
161
- title="Sign In"
162
- subtitle="Welcome back"
118
+ <AppLayout
119
+ i18n={{ locale, locales, onLocaleChange: changeLocale }}
163
120
  >
164
- <LoginForm />
165
- </AuthLayout>
121
+ {children}
122
+ </AppLayout>
166
123
  ```
167
124
 
168
- | Layout | Description |
169
- |--------|-------------|
170
- | `BaseApp` | Core providers wrapper (Theme, Auth, SWR, ErrorTracking, Toaster) |
171
- | `AppLayout` | Smart layout router with route-based layout switching |
172
- | `PublicLayout` | Public pages (home, docs, contact, legal) |
173
- | `PrivateLayout` | Authenticated user pages (dashboard, profile) |
174
- | `AuthLayout` | Authentication pages (login, signup, password reset) |
175
- | `AdminLayout` | Admin panel layout |
176
- | `ProfileLayout` | User profile pages |
177
-
178
- ### i18n Support
179
-
180
- Pass `i18n` config to AppLayout for locale switching in all layouts:
125
+ ## Built-in Layouts
181
126
 
182
127
  ```tsx
183
- import { AppLayout } from '@djangocfg/layouts';
184
- import { useLocaleSwitcher } from '@djangocfg/nextjs/i18n/client';
185
-
186
- function RootLayout({ children }) {
187
- const { locale, locales, changeLocale } = useLocaleSwitcher();
188
-
189
- return (
190
- <AppLayout
191
- // ... other configs
192
- i18n={{
193
- locale,
194
- locales,
195
- onLocaleChange: changeLocale,
196
- }}
197
- >
198
- {children}
199
- </AppLayout>
200
- );
201
- }
128
+ import { PublicLayout, PrivateLayout, AuthLayout, AdminLayout, ProfileLayout } from '@djangocfg/layouts';
202
129
  ```
203
130
 
204
- The `LocaleSwitcher` component will automatically appear in PublicLayout and PrivateLayout headers.
205
-
206
- ### LocaleSwitcher Component
131
+ | Layout | Description |
132
+ |--------|-------------|
133
+ | `PublicLayout` | Public pages with nav (home, docs, contact, legal) |
134
+ | `PrivateLayout` | Authenticated pages with sidebar |
135
+ | `AuthLayout` | OTP + OAuth + 2FA authentication flow |
136
+ | `AdminLayout` | Admin panel |
137
+ | `ProfileLayout` | User profile with 2FA management |
207
138
 
208
- A presentational locale switcher component that can be used standalone:
139
+ ### AuthLayout
209
140
 
210
141
  ```tsx
211
- import { LocaleSwitcher } from '@djangocfg/layouts';
212
-
213
- // Basic usage (pass locale data via props)
214
- <LocaleSwitcher
215
- locale="en"
216
- locales={['en', 'ru', 'ko']}
217
- onChange={(locale) => router.push(`/${locale}`)}
218
- />
219
-
220
- // With custom labels and styling
221
- <LocaleSwitcher
222
- locale={currentLocale}
223
- locales={['en', 'ru', 'ko']}
224
- onChange={handleLocaleChange}
225
- labels={{ en: 'English', ru: 'Русский', ko: '한국어' }}
226
- variant="outline"
227
- size="sm"
228
- showIcon={true}
142
+ <AuthLayout
143
+ sourceUrl="https://example.com"
144
+ redirectUrl="/dashboard"
145
+ logoUrl="/logo.svg"
146
+ enableGithubAuth={true}
147
+ termsUrl="/terms"
148
+ privacyUrl="/privacy"
229
149
  />
230
150
  ```
231
151
 
232
- **Props:**
233
- | Prop | Type | Default | Description |
234
- |------|------|---------|-------------|
235
- | `locale` | `string` | - | Current locale code |
236
- | `locales` | `string[]` | - | Available locale codes |
237
- | `onChange` | `(locale: string) => void` | - | Callback when locale changes |
238
- | `labels` | `Record<string, string>` | Built-in | Custom labels for locales |
239
- | `showCode` | `boolean` | `false` | Show locale code with label |
240
- | `variant` | `'ghost' \| 'outline' \| 'default'` | `'ghost'` | Button variant |
241
- | `size` | `'sm' \| 'default' \| 'lg' \| 'icon'` | `'sm'` | Button size |
242
- | `showIcon` | `boolean` | `true` | Show globe icon |
243
- | `className` | `string` | - | Custom CSS class |
244
-
245
- > **Smart version:** For automatic locale detection with next-intl hooks, use `@djangocfg/nextjs/i18n/components` which wraps this component.
246
-
247
- > **Extension Layouts:** Additional layouts like `SupportLayout` and `PaymentsLayout` are available in extension packages:
248
- > - `@djangocfg/ext-support` - Support ticket layouts
249
- > - `@djangocfg/ext-payments` - Payment flow layouts
250
-
251
- ## Analytics
252
-
253
- Google Analytics integration via `react-ga4`. Auto-tracks pageviews and user sessions.
254
-
255
- ### Setup
256
-
257
- Add tracking ID to your config:
152
+ ### ProfileLayout
258
153
 
259
154
  ```tsx
260
- // appLayoutConfig.ts
261
- export const appLayoutConfig: AppLayoutConfig = {
262
- // ...
263
- analytics: {
264
- googleTrackingId: 'G-XXXXXXXXXX',
265
- },
266
- };
155
+ <ProfileLayout
156
+ enable2FA={true}
157
+ showMemberSince={true}
158
+ />
267
159
  ```
268
160
 
269
- Analytics is automatically initialized by `AppLayout`. Works only in production (`NODE_ENV === 'production'`).
161
+ ## Monitor & Debug
270
162
 
271
- ### Usage
163
+ Both auto-enabled via `project` prop — no extra config needed.
272
164
 
273
165
  ```tsx
274
- import { useAnalytics, Analytics, AnalyticsEvent, AnalyticsCategory } from '@djangocfg/layouts';
275
-
276
- // In React components - auto-tracks pageviews
277
- const { event, isEnabled } = useAnalytics();
278
-
279
- event(AnalyticsEvent.THEME_CHANGE, {
280
- category: AnalyticsCategory.ENGAGEMENT,
281
- label: 'dark',
282
- });
283
-
284
- // Outside React (utilities, handlers)
285
- Analytics.event('button_click', { category: 'engagement', label: 'signup' });
286
- Analytics.setUser('user-123');
166
+ // window.monitor available in DevTools:
167
+ window.monitor.error('Something broke', { context: 'checkout' })
168
+ window.monitor.warn('Slow response', { ms: 2500 })
169
+ window.monitor.flush() // send buffer immediately
170
+ window.monitor.status() // show current state
287
171
  ```
288
172
 
289
- ### Predefined Events
290
-
291
- | Category | Events |
292
- |----------|--------|
293
- | **Auth** | `AUTH_LOGIN_SUCCESS`, `AUTH_LOGOUT`, `AUTH_SESSION_EXPIRED`, `AUTH_TOKEN_REFRESH` |
294
- | **OAuth** | `AUTH_OAUTH_START`, `AUTH_OAUTH_SUCCESS`, `AUTH_OAUTH_FAIL` |
295
- | **Error** | `ERROR_BOUNDARY`, `ERROR_API`, `ERROR_VALIDATION`, `ERROR_NETWORK` |
296
- | **Navigation** | `NAV_ADMIN_ENTER`, `NAV_DASHBOARD_ENTER`, `NAV_PAGE_VIEW` |
297
- | **Engagement** | `THEME_CHANGE`, `SIDEBAR_TOGGLE`, `MOBILE_MENU_OPEN` |
298
-
299
- ### Auto-tracking
300
-
301
- Built-in tracking for:
302
- - **Page views** - on every route change
303
- - **User ID** - automatically set when user is authenticated
304
- - **Auth events** - login, logout, session expiry (from `@djangocfg/api`)
305
- - **OAuth events** - GitHub OAuth start, success, failure
306
- - **Errors** - React ErrorBoundary errors
307
-
308
- ## PWA Installation
309
-
310
- Enable PWA installation prompts with `pwaInstall` config:
173
+ Override monitor defaults:
311
174
 
312
175
  ```tsx
313
- import { BaseApp } from '@djangocfg/layouts';
314
-
315
176
  <BaseApp
316
- pwaInstall={{
317
- enabled: true,
318
- showInstallHint: true, // Show A2HS hint
319
- resetAfterDays: 3, // Re-show after dismissal
320
- delayMs: 1000, // Delay before showing
321
- logo: '/logo192.png', // PWA logo
322
- resumeLastPage: true, // Resume last page on PWA launch
323
- }}
177
+ project="my-app"
178
+ monitor={{ baseUrl: 'https://api.example.com', captureConsole: false }}
324
179
  >
325
- {children}
326
- </BaseApp>
327
180
  ```
328
181
 
329
- **Features:**
330
- - **A2HSHint** - Platform-specific install hints (iOS Safari/Chrome/Firefox, Android Chrome, Desktop)
331
- - **Page Resume** - Automatically navigate to last viewed page when PWA is launched
332
- - **Auto-detection** - Detects if running as PWA
333
- - **Dismissal tracking** - Respects user dismissal with localStorage
334
- - **Custom timing** - Configurable delay and reset periods
182
+ ### Debug panel
335
183
 
336
- **Page Resume:**
337
- When `resumeLastPage: true`, the app saves the current pathname on every navigation and restores it when the PWA is launched. Pages like `/auth`, `/login`, `/error` are automatically excluded. Data expires after 24 hours.
338
-
339
- ### Usage
340
-
341
- ```tsx
342
- import { usePwa } from '@djangocfg/layouts/snippets';
343
-
344
- // PWA status
345
- const { isPWA, isInstallable } = usePwa();
346
- ```
347
-
348
- ## Snippets
349
-
350
- Reusable UI components and hooks ready to use.
184
+ `Cmd+D` or `?debug=1` in URL to open.
351
185
 
352
186
  ```tsx
353
- import {
354
- Breadcrumbs,
355
- AuthDialog,
356
- usePwa,
357
- } from '@djangocfg/layouts/snippets';
358
- ```
359
-
360
- | Snippet | Description |
361
- |---------|-------------|
362
- | `Breadcrumbs` | Navigation breadcrumbs with automatic path generation |
363
- | `AuthDialog` | Auth modal (login/register) with event-based triggers |
364
- | `AnalyticsProvider` | Analytics wrapper component |
365
- | `usePwa` | PWA status hook (isPWA, isInstallable, etc.) |
366
- | `usePWAPageResume` | Resume last page on PWA launch |
367
- | `A2HSHint` | Add to Home Screen hint component |
368
- | `PWAPageResumeManager` | Component for PWA page resume (use via BaseApp config) |
369
-
370
- > **Extension Snippets:** Additional components are available in extension packages:
371
- > - `@djangocfg/ext-leads` - ContactForm, ContactPage, ContactInfo
372
- > - `@djangocfg/ext-knowbase` - KnowledgeChat, ChatWidget, ChatUIProvider
373
- > - `@djangocfg/ext-newsletter` - Hero (with newsletter subscription)
187
+ // enabled by default (omit or pass empty object)
188
+ <BaseApp project="my-app">
374
189
 
375
- ### Breadcrumbs
190
+ // disable
191
+ <BaseApp debug={{ enabled: false }}>
376
192
 
377
- ```tsx
378
- import Breadcrumbs from '@djangocfg/layouts/snippets';
193
+ // custom tabs (e.g. Zustand store viewer)
194
+ import type { CustomDebugTab } from '@djangocfg/debuger';
379
195
 
380
- // Auto-generate from current path
381
- <Breadcrumbs />
196
+ const myTabs: CustomDebugTab[] = [
197
+ { id: 'store', label: 'Stores', icon: Database, panel: StoreTab },
198
+ ];
382
199
 
383
- // Or provide custom items
384
- <Breadcrumbs
385
- items={[
386
- { path: '/', label: 'Home', isActive: false },
387
- { path: '/products', label: 'Products', isActive: true },
388
- ]}
389
- />
200
+ <AppLayout debug={{ panel: { tabs: myTabs } }}>
390
201
  ```
391
202
 
392
- ### AuthDialog
203
+ ## Error Tracking
393
204
 
394
205
  ```tsx
395
- import { AuthDialog, openAuthDialog } from '@djangocfg/layouts/snippets';
396
-
397
- // Add to layout
398
- <AuthDialog authPath="/auth" />
399
-
400
- // Trigger from anywhere
401
- openAuthDialog({ message: 'Sign in to continue' });
402
- ```
403
-
404
- ## Components
405
-
406
- Utility components organized by category.
206
+ import { useErrorEmitter, emitRuntimeError } from '@djangocfg/layouts';
407
207
 
408
- ### Core Components
208
+ // In components
209
+ const { emitError } = useErrorEmitter('MyComponent');
210
+ emitError('Something failed', error);
409
211
 
410
- ```tsx
411
- import {
412
- JsonLd,
413
- LucideIcon,
414
- PageProgress,
415
- Suspense
416
- } from '@djangocfg/layouts/components/core';
212
+ // Outside React
213
+ emitRuntimeError('MyUtil', 'Operation failed', error);
417
214
  ```
418
215
 
419
- | Component | Description |
420
- |-----------|-------------|
421
- | `JsonLd` | JSON-LD structured data component |
422
- | `LucideIcon` | Lucide icon wrapper component |
423
- | `PageProgress` | Page loading progress indicator |
424
- | `Suspense` | Suspense wrapper component |
425
-
426
- ### Error Components
216
+ ## Error Pages
427
217
 
428
218
  ```tsx
429
- import {
430
- ErrorBoundary,
431
- ErrorLayout,
432
- getErrorContent,
433
- ERROR_CODES
434
- } from '@djangocfg/layouts/components/errors';
435
- ```
436
-
437
- | Component | Description |
438
- |-----------|-------------|
439
- | `ErrorBoundary` | React error boundary component |
440
- | `ErrorLayout` | Reusable error page layout (404, 500, etc.) |
441
- | `getErrorContent` | Get error content by status code |
442
- | `ERROR_CODES` | Common HTTP error code constants |
443
-
444
- **ErrorLayout Usage:**
445
-
446
- ```tsx
447
- // app/not-found.tsx
448
219
  import { ErrorLayout } from '@djangocfg/layouts/components/errors';
449
220
 
221
+ // app/not-found.tsx
450
222
  export default function NotFound() {
451
223
  return <ErrorLayout code={404} supportEmail="support@example.com" />;
452
224
  }
453
-
454
- // app/error.tsx
455
- 'use client';
456
- import { ErrorLayout } from '@djangocfg/layouts/components/errors';
457
-
458
- export default function Error({ error, reset }) {
459
- return <ErrorLayout code={500} supportEmail="support@example.com" />;
460
- }
461
225
  ```
462
226
 
463
- ### Redirect Component
227
+ ## PWA
464
228
 
465
229
  ```tsx
466
- import { RedirectPage } from '@djangocfg/layouts/components/RedirectPage';
467
-
468
- // app/page.tsx
469
- export default function Page() {
470
- return (
471
- <RedirectPage
472
- authenticatedPath="/dashboard"
473
- unauthenticatedPath="/auth"
474
- loadingText="Loading..."
475
- />
476
- );
477
- }
230
+ <BaseApp
231
+ pwaInstall={{
232
+ enabled: true,
233
+ showInstallHint: true,
234
+ logo: '/logo192.png',
235
+ delayMs: 3000,
236
+ resetAfterDays: 7,
237
+ resumeLastPage: true,
238
+ }}
239
+ >
478
240
  ```
479
241
 
480
- ### Error Tracking
242
+ ## Redirect
481
243
 
482
244
  ```tsx
483
- import {
484
- ErrorTrackingProvider,
485
- useErrors,
486
- useErrorEmitter,
487
- emitRuntimeError,
488
- } from '@djangocfg/layouts';
489
-
490
- // Wrap your app
491
- <ErrorTrackingProvider>
492
- <YourApp />
493
- </ErrorTrackingProvider>
494
-
495
- // In React components - use hook
496
- function MyComponent() {
497
- const { emitError } = useErrorEmitter('MyComponent');
498
-
499
- try {
500
- doSomething();
501
- } catch (error) {
502
- emitError('Something failed', error);
503
- }
504
- }
505
-
506
- // Outside React (utilities, libraries) - use standalone function
507
- import { emitRuntimeError } from '@djangocfg/layouts';
245
+ import { RedirectPage } from '@djangocfg/layouts/components/RedirectPage';
508
246
 
509
- try {
510
- doSomething();
511
- } catch (error) {
512
- emitRuntimeError('MyUtility', 'Something failed', error);
247
+ export default function Page() {
248
+ return <RedirectPage authenticatedPath="/dashboard" unauthenticatedPath="/auth" />;
513
249
  }
514
-
515
- // Access errors
516
- const { errors, clearErrors } = useErrors();
517
250
  ```
518
251
 
519
- ### Update Notifier
252
+ ## Legal Pages
520
253
 
521
254
  ```tsx
522
- import { UpdateNotifier } from '@djangocfg/layouts/components/UpdateNotifier';
523
-
524
- <UpdateNotifier />
525
- ```
255
+ import { PrivacyPage, TermsPage, CookiesPage, SecurityPage } from '@djangocfg/layouts/pages/legal';
526
256
 
527
- ## Pages
528
-
529
- Ready-to-use page components.
530
-
531
- ### Legal Pages
532
-
533
- Pre-built legal page components with default configurations.
534
-
535
- ```tsx
536
- import {
537
- PrivacyPage,
538
- TermsPage,
539
- CookiesPage,
540
- SecurityPage
541
- } from '@djangocfg/layouts/pages/legal';
542
-
543
- // app/legal/privacy/page.tsx
544
257
  export default PrivacyPage;
545
-
546
- // Or customize
547
- import { PrivacyPage, privacyConfig } from '@djangocfg/layouts/pages/legal';
548
-
549
- export default function CustomPrivacy() {
550
- return <PrivacyPage config={{
551
- ...privacyConfig,
552
- lastUpdated: '2024-01-01',
553
- }} />;
554
- }
555
- ```
556
-
557
- | Page | Description |
558
- |------|-------------|
559
- | `PrivacyPage` | Privacy policy page |
560
- | `TermsPage` | Terms of service page |
561
- | `CookiesPage` | Cookie policy page |
562
- | `SecurityPage` | Security policy page |
563
-
564
- ## Utils
565
-
566
- Utility functions and helpers.
567
-
568
- ```tsx
569
- import {
570
- generateOgImageUrl,
571
- getAbsoluteOgImageUrl,
572
- createOgImageUrlBuilder
573
- } from '@djangocfg/layouts/utils/og-image';
574
-
575
- // Generate OG image URL
576
- const ogUrl = generateOgImageUrl('/api/og', {
577
- title: 'My Page',
578
- description: 'Page description',
579
- siteName: 'My Site',
580
- });
581
258
  ```
582
259
 
583
- | Utility | Description |
584
- |---------|-------------|
585
- | `generateOgImageUrl` | Generate OG image URL with base64 encoding |
586
- | `getAbsoluteOgImageUrl` | Get absolute OG image URL |
587
- | `createOgImageUrlBuilder` | Create OG image URL builder with defaults |
588
-
589
260
  ## Exports
590
261
 
591
262
  | Path | Content |
592
263
  |------|---------|
593
- | `@djangocfg/layouts` | Main exports (all modules) |
264
+ | `@djangocfg/layouts` | All exports |
594
265
  | `@djangocfg/layouts/layouts` | Layout components |
595
- | `@djangocfg/layouts/snippets` | Reusable components + Analytics |
596
- | `@djangocfg/layouts/components` | All utility components |
597
- | `@djangocfg/layouts/pages` | Page components (legal pages) |
598
- | `@djangocfg/layouts/pages/legal` | Legal page components |
599
- | `@djangocfg/layouts/utils` | Utilities (og-image, logger) |
266
+ | `@djangocfg/layouts/snippets` | Reusable components |
267
+ | `@djangocfg/layouts/components` | Utility components |
268
+ | `@djangocfg/layouts/pages/legal` | Legal pages |
269
+ | `@djangocfg/layouts/utils` | OG image utils |
600
270
  | `@djangocfg/layouts/styles` | CSS |
601
- | `@djangocfg/layouts/styles/dashboard` | Dashboard-specific CSS |
602
-
603
- > **Auth Exports:** For authentication, use `@djangocfg/api/auth` - See [@djangocfg/api documentation](../api/README.md)
271
+ | `@djangocfg/layouts/styles/dashboard` | Dashboard CSS |
604
272
 
605
273
  ## Extension Packages
606
274
 
607
- Additional functionality is available in extension packages:
608
-
609
- | Extension | Package | Description |
610
- |-----------|---------|-------------|
611
- | **Newsletter** | `@djangocfg/ext-newsletter` | Newsletter subscription and campaigns |
612
- | **Knowledge Base** | `@djangocfg/ext-knowbase` | Documentation, chat, RAG-powered AI |
613
- | **Leads** | `@djangocfg/ext-leads` | Lead capture and contact forms |
614
- | **Payments** | `@djangocfg/ext-payments` | Payment processing and subscriptions |
615
- | **Support** | `@djangocfg/ext-support` | Support tickets and helpdesk |
616
-
617
- Each extension has its own layouts, contexts, and components. See individual extension documentation for details.
618
-
619
- ## Requirements
620
-
621
- - Next.js >= 15
622
- - React >= 19
623
- - Tailwind CSS >= 4
624
- - react-ga4 (bundled)
625
- - @djangocfg/ui-nextjs (peer dependency)
626
- - @djangocfg/api (peer dependency)
627
-
628
- ## Philosophy
629
-
630
- This package follows a **simple, props-based approach**:
631
-
632
- - ✅ **No complex configs** - just pass props
633
- - ✅ **Type-safe** - full TypeScript support
634
- - ✅ **Flexible** - compose layouts as needed
635
- - ✅ **Production-ready** - built for real apps
636
- - ✅ **Modular** - core layouts in one package, extensions separate
637
-
638
- ## Examples
639
-
640
- ### Complete App Setup
641
-
642
- ```tsx
643
- // app/layout.tsx
644
- import { AppLayout } from '@djangocfg/layouts';
645
- import { appLayoutConfig } from './_config/appLayoutConfig';
646
-
647
- export default function RootLayout({ children }) {
648
- return (
649
- <AppLayout config={appLayoutConfig}>
650
- {children}
651
- </AppLayout>
652
- );
653
- }
654
- ```
655
-
656
- ### Public Page
657
-
658
- ```tsx
659
- // app/page.tsx
660
- import { PublicLayout } from '@djangocfg/layouts';
661
-
662
- export default function HomePage() {
663
- return (
664
- <PublicLayout
665
- logo="/logo.svg"
666
- siteName="My App"
667
- navigation={[
668
- { label: 'Home', href: '/' },
669
- { label: 'Docs', href: '/docs' },
670
- { label: 'Contact', href: '/contact' }
671
- ]}
672
- >
673
- <h1>Welcome</h1>
674
- </PublicLayout>
675
- );
676
- }
677
- ```
678
-
679
- ### Private Dashboard
680
-
681
- ```tsx
682
- // app/dashboard/page.tsx
683
- import { PrivateLayout } from '@djangocfg/layouts';
684
-
685
- export default function DashboardPage() {
686
- return (
687
- <PrivateLayout
688
- sidebar={{
689
- groups: [
690
- {
691
- label: 'Main',
692
- items: [
693
- { label: 'Dashboard', href: '/dashboard', icon: 'LayoutDashboard' },
694
- { label: 'Settings', href: '/settings', icon: 'Settings' }
695
- ]
696
- }
697
- ],
698
- homeHref: '/dashboard'
699
- }}
700
- header={{ title: 'Dashboard' }}
701
- >
702
- <h1>Dashboard</h1>
703
- </PrivateLayout>
704
- );
705
- }
706
- ```
707
-
708
- ### Auth Page (OTP + 2FA Authentication)
709
-
710
- ```tsx
711
- // app/auth/page.tsx
712
- 'use client';
713
-
714
- import { AuthLayout } from '@djangocfg/layouts';
715
-
716
- export default function AuthPage() {
717
- return (
718
- <AuthLayout
719
- sourceUrl="https://example.com"
720
- supportUrl="/support"
721
- termsUrl="/terms"
722
- privacyUrl="/privacy"
723
- enablePhoneAuth={false}
724
- enableGithubAuth={true}
725
- logoUrl="/logo.svg"
726
- redirectUrl="/dashboard"
727
- onOTPSuccess={() => {
728
- console.log('Authentication successful');
729
- }}
730
- onOAuthSuccess={(user, isNewUser, provider) => {
731
- console.log('OAuth success:', { user, isNewUser, provider });
732
- }}
733
- >
734
- <div className="text-center mb-6">
735
- <h2 className="text-2xl font-bold">Welcome to My App</h2>
736
- <p className="text-muted-foreground mt-2">
737
- Sign in with your email or phone
738
- </p>
739
- </div>
740
- </AuthLayout>
741
- );
742
- }
743
- ```
744
-
745
- **Authentication Flow:**
746
- 1. **Identifier** → Enter email/phone or click GitHub OAuth
747
- 2. **OTP** → Enter 6-digit verification code
748
- 3. **2FA** → Enter TOTP code (if 2FA enabled for user)
749
- 4. **Success** → Show logo animation, then redirect
750
-
751
- **AuthLayout Props:**
752
- | Prop | Type | Description |
753
- |------|------|-------------|
754
- | `sourceUrl` | `string` | Application URL for OTP emails |
755
- | `redirectUrl` | `string` | URL to redirect after successful auth (default: `/dashboard`) |
756
- | `logoUrl` | `string` | Logo URL for success screen (SVG recommended) |
757
- | `enablePhoneAuth` | `boolean` | Enable phone number authentication |
758
- | `enableGithubAuth` | `boolean` | Enable GitHub OAuth |
759
- | `enable2FASetup` | `boolean` | Enable 2FA setup prompt after login (default: `true`). Set to `false` to skip 2FA setup prompt - users can then configure 2FA from ProfileLayout instead. |
760
- | `termsUrl` | `string` | Terms of service URL (shows checkbox if provided) |
761
- | `privacyUrl` | `string` | Privacy policy URL |
762
- | `supportUrl` | `string` | Support page URL |
763
- | `onOTPSuccess` | `() => void` | Callback after successful authentication |
764
- | `onOAuthSuccess` | `(user, isNewUser, provider) => void` | Callback after successful OAuth |
765
- | `onError` | `(message: string) => void` | Error callback |
766
-
767
- ### Profile Page (with 2FA Management)
768
-
769
- ```tsx
770
- // app/profile/page.tsx
771
- 'use client';
772
-
773
- import { ProfileLayout } from '@djangocfg/layouts';
774
-
775
- export default function ProfilePage() {
776
- return (
777
- <ProfileLayout
778
- title="Profile Settings"
779
- description="Manage your account"
780
- enable2FA={true} // Show 2FA management section
781
- showMemberSince={true}
782
- showLastLogin={true}
783
- onUnauthenticated={() => {
784
- // Redirect to login
785
- }}
786
- />
787
- );
788
- }
789
- ```
790
-
791
- **ProfileLayout Props:**
792
- | Prop | Type | Description |
793
- |------|------|-------------|
794
- | `title` | `string` | Page title (default: "Profile Settings") |
795
- | `description` | `string` | Page description |
796
- | `enable2FA` | `boolean` | Show 2FA management section (default: `false`). When enabled, users can enable/disable Two-Factor Authentication from their profile. |
797
- | `showMemberSince` | `boolean` | Show member since date (default: `true`) |
798
- | `showLastLogin` | `boolean` | Show last login date (default: `true`) |
799
- | `onUnauthenticated` | `() => void` | Callback when user is not authenticated |
800
-
801
- **2FA Configuration Strategy:**
802
- - Use `enable2FASetup={false}` in AuthLayout to skip post-login 2FA setup prompt
803
- - Use `enable2FA={true}` in ProfileLayout to allow users to manage 2FA from settings
804
- - This gives users control over when to enable 2FA instead of forcing it during login
275
+ | Package | Description |
276
+ |---------|-------------|
277
+ | `@djangocfg/ext-newsletter` | Newsletter subscription |
278
+ | `@djangocfg/ext-knowbase` | Knowledge base + AI chat |
279
+ | `@djangocfg/ext-leads` | Lead capture forms |
280
+ | `@djangocfg/ext-payments` | Payments & subscriptions |
281
+ | `@djangocfg/ext-support` | Support tickets |
805
282
 
806
283
  ## License
807
284
 
808
- MIT
809
-
810
- ## Links
811
-
812
- - [DjangoCFG Documentation](https://djangocfg.com)
813
- - [GitHub](https://github.com/markolofsen/django-cfg)
285
+ MIT — [DjangoCFG](https://djangocfg.com) · [GitHub](https://github.com/markolofsen/django-cfg)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.219",
3
+ "version": "2.1.222",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -74,13 +74,13 @@
74
74
  "check": "tsc --noEmit"
75
75
  },
76
76
  "peerDependencies": {
77
- "@djangocfg/api": "^2.1.219",
78
- "@djangocfg/centrifugo": "^2.1.219",
79
- "@djangocfg/i18n": "^2.1.219",
80
- "@djangocfg/monitor": "^2.1.219",
81
- "@djangocfg/ui-core": "^2.1.219",
82
- "@djangocfg/ui-nextjs": "^2.1.219",
83
- "@djangocfg/ui-tools": "^2.1.219",
77
+ "@djangocfg/api": "^2.1.222",
78
+ "@djangocfg/centrifugo": "^2.1.222",
79
+ "@djangocfg/i18n": "^2.1.222",
80
+ "@djangocfg/monitor": "^2.1.222",
81
+ "@djangocfg/ui-core": "^2.1.222",
82
+ "@djangocfg/ui-nextjs": "^2.1.222",
83
+ "@djangocfg/ui-tools": "^2.1.222",
84
84
  "@hookform/resolvers": "^5.2.2",
85
85
  "consola": "^3.4.2",
86
86
  "lucide-react": "^0.545.0",
@@ -102,20 +102,21 @@
102
102
  }
103
103
  },
104
104
  "dependencies": {
105
+ "@djangocfg/debuger": "^2.1.222",
105
106
  "nextjs-toploader": "^3.9.17",
106
107
  "qrcode.react": "^4.2.0",
107
108
  "react-ga4": "^2.1.0",
108
109
  "uuid": "^11.1.0"
109
110
  },
110
111
  "devDependencies": {
111
- "@djangocfg/api": "^2.1.219",
112
- "@djangocfg/i18n": "^2.1.219",
113
- "@djangocfg/centrifugo": "^2.1.219",
114
- "@djangocfg/monitor": "^2.1.219",
115
- "@djangocfg/typescript-config": "^2.1.219",
116
- "@djangocfg/ui-core": "^2.1.219",
117
- "@djangocfg/ui-nextjs": "^2.1.219",
118
- "@djangocfg/ui-tools": "^2.1.219",
112
+ "@djangocfg/api": "^2.1.222",
113
+ "@djangocfg/i18n": "^2.1.222",
114
+ "@djangocfg/centrifugo": "^2.1.222",
115
+ "@djangocfg/monitor": "^2.1.222",
116
+ "@djangocfg/typescript-config": "^2.1.222",
117
+ "@djangocfg/ui-core": "^2.1.222",
118
+ "@djangocfg/ui-nextjs": "^2.1.222",
119
+ "@djangocfg/ui-tools": "^2.1.222",
119
120
  "@types/node": "^24.7.2",
120
121
  "@types/react": "^19.1.0",
121
122
  "@types/react-dom": "^19.1.0",
@@ -48,6 +48,7 @@ import type {
48
48
  SWRConfigOptions,
49
49
  McpChatConfig,
50
50
  PwaInstallConfig,
51
+ DebugConfig,
51
52
  } from '../types';
52
53
  import type { AuthConfig } from '@djangocfg/api/auth';
53
54
  import type { MonitorConfig } from '@djangocfg/monitor';
@@ -91,6 +92,9 @@ interface LayoutConfig {
91
92
  export interface AppLayoutProps {
92
93
  children: ReactNode;
93
94
 
95
+ /** Project name — used as default for monitor.project and debug panel title */
96
+ project?: string;
97
+
94
98
  /** Public layout component with enabled paths */
95
99
  publicLayout?: LayoutConfig;
96
100
 
@@ -139,6 +143,8 @@ export interface AppLayoutProps {
139
143
 
140
144
  /** Monitor configuration — initialises window.monitor + auto-captures JS errors & console */
141
145
  monitor?: MonitorConfig;
146
+
147
+ debug?: DebugConfig;
142
148
  }
143
149
 
144
150
  /**
@@ -251,6 +257,7 @@ function AppLayoutContent({
251
257
  */
252
258
  export function AppLayout(props: AppLayoutProps) {
253
259
  const {
260
+ project,
254
261
  theme,
255
262
  auth,
256
263
  analytics,
@@ -261,10 +268,12 @@ export function AppLayout(props: AppLayoutProps) {
261
268
  mcpChat,
262
269
  pwaInstall,
263
270
  monitor,
271
+ debug,
264
272
  } = props;
265
273
 
266
274
  return (
267
275
  <BaseApp
276
+ project={project}
268
277
  theme={theme}
269
278
  auth={auth}
270
279
  analytics={analytics}
@@ -275,6 +284,7 @@ export function AppLayout(props: AppLayoutProps) {
275
284
  mcpChat={mcpChat}
276
285
  pwaInstall={pwaInstall}
277
286
  monitor={monitor}
287
+ debug={debug}
278
288
  >
279
289
  <AppLayoutContent {...props} />
280
290
  </BaseApp>
@@ -57,12 +57,19 @@ import { A2HSHint, PWAPageResumeManager, PwaProvider } from '../../snippets/PWAI
57
57
 
58
58
  import type { BaseLayoutProps } from '../types/layout.types';
59
59
 
60
+ // Lazy load DebugButton from @djangocfg/debuger
61
+ const DebugButton = dynamic(
62
+ () => import('@djangocfg/debuger').then(mod => ({ default: mod.DebugButton })),
63
+ { ssr: false }
64
+ );
65
+
60
66
  // Lazy load MCP Chat Widget with dynamic import
61
67
  const AIChatWidget = dynamic(
62
68
  () => import('../../snippets/McpChat/components/AIChatWidget').then(mod => ({ default: mod.AIChatWidget })),
63
69
  { ssr: false }
64
70
  );
65
71
 
72
+
66
73
  // For backwards compatibility, re-export as BaseAppProps
67
74
  export type BaseAppProps = BaseLayoutProps;
68
75
 
@@ -76,6 +83,7 @@ export type BaseAppProps = BaseLayoutProps;
76
83
  */
77
84
  export function BaseApp({
78
85
  children,
86
+ project,
79
87
  theme,
80
88
  auth,
81
89
  analytics,
@@ -86,6 +94,7 @@ export function BaseApp({
86
94
  mcpChat,
87
95
  pwaInstall,
88
96
  monitor,
97
+ debug,
89
98
  }: BaseAppProps) {
90
99
  // ErrorBoundary is enabled by default
91
100
  const enableErrorBoundary = errorBoundary?.enabled !== false;
@@ -98,6 +107,17 @@ export function BaseApp({
98
107
  const centrifugoUrl = centrifugo?.url || process.env.NEXT_PUBLIC_CENTRIFUGO_URL;
99
108
  const centrifugoEnabled = centrifugo?.enabled !== false;
100
109
 
110
+ // Debug panel — enabled by default, disabled with { enabled: false }
111
+ const { enabled: debugEnabled = true, ...debugProps } = debug ?? {};
112
+
113
+
114
+ // Monitor — project prop as default, override with monitor config
115
+ const monitorConfig = {
116
+ project,
117
+ environment: process.env.NODE_ENV,
118
+ ...monitor,
119
+ };
120
+
101
121
  const content = (
102
122
  <ThemeProvider
103
123
  defaultTheme={theme?.defaultTheme || 'system'}
@@ -130,7 +150,7 @@ export function BaseApp({
130
150
  network={errorTracking?.network}
131
151
  onError={errorTracking?.onError}
132
152
  >
133
- {monitor && <MonitorProvider {...monitor} />}
153
+ <MonitorProvider {...monitorConfig} />
134
154
  {children}
135
155
  <NextTopLoader
136
156
  color="hsl(var(--primary))"
@@ -171,6 +191,9 @@ export function BaseApp({
171
191
 
172
192
  {/* Auth Dialog - global auth prompt */}
173
193
  <AuthDialog authPath={auth?.routes?.auth} />
194
+
195
+ {/* Debug Panel — enabled by default, disable with debug={{ enabled: false }} */}
196
+ {debugEnabled && <DebugButton {...debugProps} />}
174
197
  </ErrorTrackingProvider>
175
198
  </PwaProvider>
176
199
  </CentrifugoProvider>
@@ -43,4 +43,4 @@ export type {
43
43
  // Layout Types
44
44
  // ============================================================================
45
45
 
46
- export type { BaseLayoutProps } from './layout.types';
46
+ export type { BaseLayoutProps, DebugConfig } from './layout.types';
@@ -8,6 +8,7 @@ import type { ReactNode } from 'react';
8
8
  import type { AuthConfig } from '@djangocfg/api/auth';
9
9
 
10
10
  // Import provider configs from their modules
11
+ import type { DebugButtonProps } from '@djangocfg/debuger';
11
12
  import type { AnalyticsConfig } from '../../snippets/Analytics/types';
12
13
  import type { PwaInstallConfig } from '../../snippets/PWAInstall/types';
13
14
  import type { ErrorBoundaryConfig, ErrorTrackingConfig } from '../../components/errors/types';
@@ -29,6 +30,9 @@ import type { MonitorConfig } from '@djangocfg/monitor';
29
30
  export interface BaseLayoutProps {
30
31
  children: ReactNode;
31
32
 
33
+ /** Project name — used as default for monitor.project and debug panel title */
34
+ project?: string;
35
+
32
36
  /** Theme configuration */
33
37
  theme?: ThemeConfig;
34
38
 
@@ -58,4 +62,13 @@ export interface BaseLayoutProps {
58
62
 
59
63
  /** Monitor configuration — initialises window.monitor + auto-captures JS errors & console */
60
64
  monitor?: MonitorConfig;
65
+
66
+ /** Debug panel configuration */
67
+ debug?: DebugConfig;
68
+ }
69
+
70
+ /** Debug panel config — enabled by default, pass enabled: false to disable */
71
+ export interface DebugConfig extends DebugButtonProps {
72
+ /** Set false to disable the debug panel. Default: true */
73
+ enabled?: boolean;
61
74
  }