@djangocfg/layouts 2.1.218 → 2.1.221

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,33 @@ 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 }}
39
+ debug={true}
33
40
  >
34
41
  {children}
35
42
  </BaseApp>
@@ -39,29 +46,35 @@ export default function RootLayout({ children }) {
39
46
  }
40
47
  ```
41
48
 
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
49
+ **Props:**
63
50
 
64
- Smart layout router built on BaseApp - automatically selects layout based on route:
51
+ | Prop | Type | Default | Description |
52
+ |------|------|---------|-------------|
53
+ | `project` | `string` | — | App name — auto-passed to `MonitorProvider` and debug panel |
54
+ | `theme` | `ThemeConfig` | — | Theme config (defaultTheme, storageKey) |
55
+ | `auth` | `AuthConfig` | — | Auth provider config |
56
+ | `analytics` | `AnalyticsConfig` | — | Google Analytics config |
57
+ | `centrifugo` | `CentrifugoConfig` | — | WebSocket real-time config |
58
+ | `errorTracking` | `ErrorTrackingConfig` | — | Validation/CORS/network error capture |
59
+ | `errorBoundary` | `ErrorBoundaryConfig` | enabled | React error boundary |
60
+ | `swr` | `SWRConfigOptions` | — | SWR data fetching config |
61
+ | `pwaInstall` | `PwaInstallConfig` | — | PWA install prompt |
62
+ | `mcpChat` | `McpChatConfig` | — | AI chat widget |
63
+ | `monitor` | `MonitorConfig` | — | Override monitor config (project/environment come from `project` prop by default) |
64
+ | `debug` | `boolean` | `true` | Debug panel — `Cmd+D` or `?debug=1` to open |
65
+
66
+ **Included automatically:**
67
+ - ThemeProvider, TooltipProvider, SWRConfig, DialogProvider
68
+ - AuthProvider + AuthDialog
69
+ - AnalyticsProvider, CentrifugoProvider, PwaProvider
70
+ - ErrorTrackingProvider, ErrorBoundary
71
+ - MonitorProvider (auto-enabled via `project` prop)
72
+ - DebugButton from `@djangocfg/debuger` (disable with `debug={false}`)
73
+ - NextTopLoader, Toaster
74
+
75
+ ## AppLayout
76
+
77
+ Smart layout router — selects layout based on current route. Wraps `BaseApp`, accepts all its props.
65
78
 
66
79
  ```tsx
67
80
  import { AppLayout } from '@djangocfg/layouts';
@@ -69,32 +82,20 @@ import { PublicLayout } from './_layouts/PublicLayout';
69
82
  import { PrivateLayout } from './_layouts/PrivateLayout';
70
83
  import { AdminLayout } from './_layouts/AdminLayout';
71
84
 
72
- // app/layout.tsx
73
85
  export default function RootLayout({ children }) {
74
86
  return (
75
87
  <html lang="en" suppressHydrationWarning>
76
88
  <body>
77
89
  <AppLayout
78
- // Provider configs (passed to BaseApp)
90
+ project="my-app"
79
91
  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)
92
+ auth={{ apiUrl: process.env.NEXT_PUBLIC_API_URL }}
93
+ debug={true}
94
+
95
+ publicLayout={{ component: PublicLayout, enabledPath: ['/', '/legal', '/contact'] }}
96
+ privateLayout={{ component: PrivateLayout, enabledPath: ['/dashboard', '/profile'] }}
97
+ adminLayout={{ component: AdminLayout, enabledPath: '/admin' }}
98
+
98
99
  noLayoutPaths={['/private/terminal', '/embed']}
99
100
  >
100
101
  {children}
@@ -105,709 +106,163 @@ export default function RootLayout({ children }) {
105
106
  }
106
107
  ```
107
108
 
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
109
  **Layout priority:** Admin → Private → Public → Fallback
125
110
 
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
129
-
130
- Simple, props-based layout components. No complex configs needed!
111
+ **`noLayoutPaths`** render without layout wrapper (providers still active). Useful for fullscreen pages.
131
112
 
132
- ### Available Layouts
113
+ ### i18n
133
114
 
134
115
  ```tsx
135
- import { PublicLayout, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
116
+ import { useLocaleSwitcher } from '@djangocfg/nextjs/i18n/client';
136
117
 
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>
118
+ const { locale, locales, changeLocale } = useLocaleSwitcher();
157
119
 
158
- // Auth page
159
- <AuthLayout
160
- logo="/logo.svg"
161
- title="Sign In"
162
- subtitle="Welcome back"
120
+ <AppLayout
121
+ i18n={{ locale, locales, onLocaleChange: changeLocale }}
163
122
  >
164
- <LoginForm />
165
- </AuthLayout>
123
+ {children}
124
+ </AppLayout>
166
125
  ```
167
126
 
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:
127
+ ## Built-in Layouts
181
128
 
182
129
  ```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
- }
130
+ import { PublicLayout, PrivateLayout, AuthLayout, AdminLayout, ProfileLayout } from '@djangocfg/layouts';
202
131
  ```
203
132
 
204
- The `LocaleSwitcher` component will automatically appear in PublicLayout and PrivateLayout headers.
205
-
206
- ### LocaleSwitcher Component
133
+ | Layout | Description |
134
+ |--------|-------------|
135
+ | `PublicLayout` | Public pages with nav (home, docs, contact, legal) |
136
+ | `PrivateLayout` | Authenticated pages with sidebar |
137
+ | `AuthLayout` | OTP + OAuth + 2FA authentication flow |
138
+ | `AdminLayout` | Admin panel |
139
+ | `ProfileLayout` | User profile with 2FA management |
207
140
 
208
- A presentational locale switcher component that can be used standalone:
141
+ ### AuthLayout
209
142
 
210
143
  ```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}
144
+ <AuthLayout
145
+ sourceUrl="https://example.com"
146
+ redirectUrl="/dashboard"
147
+ logoUrl="/logo.svg"
148
+ enableGithubAuth={true}
149
+ termsUrl="/terms"
150
+ privacyUrl="/privacy"
229
151
  />
230
152
  ```
231
153
 
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:
154
+ ### ProfileLayout
258
155
 
259
156
  ```tsx
260
- // appLayoutConfig.ts
261
- export const appLayoutConfig: AppLayoutConfig = {
262
- // ...
263
- analytics: {
264
- googleTrackingId: 'G-XXXXXXXXXX',
265
- },
266
- };
157
+ <ProfileLayout
158
+ enable2FA={true}
159
+ showMemberSince={true}
160
+ />
267
161
  ```
268
162
 
269
- Analytics is automatically initialized by `AppLayout`. Works only in production (`NODE_ENV === 'production'`).
163
+ ## Monitor & Debug
270
164
 
271
- ### Usage
165
+ Both are auto-enabled via `project` prop — no extra config needed.
272
166
 
273
167
  ```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');
168
+ // Monitor auto-starts, window.monitor available in DevTools:
169
+ window.monitor.error('Something broke', { context: 'checkout' })
170
+ window.monitor.warn('Slow response', { ms: 2500 })
171
+ window.monitor.flush() // send buffer immediately
172
+ window.monitor.status() // show current state
287
173
  ```
288
174
 
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:
175
+ Override monitor defaults:
311
176
 
312
177
  ```tsx
313
- import { BaseApp } from '@djangocfg/layouts';
314
-
315
178
  <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
- }}
179
+ project="my-app"
180
+ monitor={{ baseUrl: 'https://api.example.com', captureConsole: false }}
324
181
  >
325
- {children}
326
- </BaseApp>
327
182
  ```
328
183
 
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
184
+ Debug panel: `Cmd+D` or `?debug=1` in URL. Disable: `debug={false}`.
335
185
 
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
186
+ ## Error Tracking
340
187
 
341
188
  ```tsx
342
- import { usePwa } from '@djangocfg/layouts/snippets';
343
-
344
- // PWA status
345
- const { isPWA, isInstallable } = usePwa();
346
- ```
189
+ import { useErrorEmitter, emitRuntimeError } from '@djangocfg/layouts';
347
190
 
348
- ## Snippets
191
+ // In components
192
+ const { emitError } = useErrorEmitter('MyComponent');
193
+ emitError('Something failed', error);
349
194
 
350
- Reusable UI components and hooks ready to use.
351
-
352
- ```tsx
353
- import {
354
- Breadcrumbs,
355
- AuthDialog,
356
- usePwa,
357
- } from '@djangocfg/layouts/snippets';
195
+ // Outside React
196
+ emitRuntimeError('MyUtil', 'Operation failed', error);
358
197
  ```
359
198
 
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)
374
-
375
- ### Breadcrumbs
376
-
377
- ```tsx
378
- import Breadcrumbs from '@djangocfg/layouts/snippets';
379
-
380
- // Auto-generate from current path
381
- <Breadcrumbs />
382
-
383
- // Or provide custom items
384
- <Breadcrumbs
385
- items={[
386
- { path: '/', label: 'Home', isActive: false },
387
- { path: '/products', label: 'Products', isActive: true },
388
- ]}
389
- />
390
- ```
391
-
392
- ### AuthDialog
393
-
394
- ```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.
407
-
408
- ### Core Components
409
-
410
- ```tsx
411
- import {
412
- JsonLd,
413
- LucideIcon,
414
- PageProgress,
415
- Suspense
416
- } from '@djangocfg/layouts/components/core';
417
- ```
418
-
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
199
+ ## Error Pages
427
200
 
428
201
  ```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
202
  import { ErrorLayout } from '@djangocfg/layouts/components/errors';
449
203
 
204
+ // app/not-found.tsx
450
205
  export default function NotFound() {
451
206
  return <ErrorLayout code={404} supportEmail="support@example.com" />;
452
207
  }
208
+ ```
453
209
 
454
- // app/error.tsx
455
- 'use client';
456
- import { ErrorLayout } from '@djangocfg/layouts/components/errors';
210
+ ## PWA
457
211
 
458
- export default function Error({ error, reset }) {
459
- return <ErrorLayout code={500} supportEmail="support@example.com" />;
460
- }
212
+ ```tsx
213
+ <BaseApp
214
+ pwaInstall={{
215
+ enabled: true,
216
+ showInstallHint: true,
217
+ logo: '/logo192.png',
218
+ delayMs: 3000,
219
+ resetAfterDays: 7,
220
+ resumeLastPage: true,
221
+ }}
222
+ >
461
223
  ```
462
224
 
463
- ### Redirect Component
225
+ ## Redirect
464
226
 
465
227
  ```tsx
466
228
  import { RedirectPage } from '@djangocfg/layouts/components/RedirectPage';
467
229
 
468
- // app/page.tsx
469
230
  export default function Page() {
470
- return (
471
- <RedirectPage
472
- authenticatedPath="/dashboard"
473
- unauthenticatedPath="/auth"
474
- loadingText="Loading..."
475
- />
476
- );
231
+ return <RedirectPage authenticatedPath="/dashboard" unauthenticatedPath="/auth" />;
477
232
  }
478
233
  ```
479
234
 
480
- ### Error Tracking
235
+ ## Legal Pages
481
236
 
482
237
  ```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';
238
+ import { PrivacyPage, TermsPage, CookiesPage, SecurityPage } from '@djangocfg/layouts/pages/legal';
508
239
 
509
- try {
510
- doSomething();
511
- } catch (error) {
512
- emitRuntimeError('MyUtility', 'Something failed', error);
513
- }
514
-
515
- // Access errors
516
- const { errors, clearErrors } = useErrors();
517
- ```
518
-
519
- ### Update Notifier
520
-
521
- ```tsx
522
- import { UpdateNotifier } from '@djangocfg/layouts/components/UpdateNotifier';
523
-
524
- <UpdateNotifier />
525
- ```
526
-
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
240
  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
241
  ```
556
242
 
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
- ```
582
-
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
243
  ## Exports
590
244
 
591
245
  | Path | Content |
592
246
  |------|---------|
593
- | `@djangocfg/layouts` | Main exports (all modules) |
247
+ | `@djangocfg/layouts` | All exports |
594
248
  | `@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) |
249
+ | `@djangocfg/layouts/snippets` | Reusable components |
250
+ | `@djangocfg/layouts/components` | Utility components |
251
+ | `@djangocfg/layouts/pages/legal` | Legal pages |
252
+ | `@djangocfg/layouts/utils` | OG image utils |
600
253
  | `@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)
254
+ | `@djangocfg/layouts/styles/dashboard` | Dashboard CSS |
604
255
 
605
256
  ## Extension Packages
606
257
 
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
258
+ | Package | Description |
259
+ |---------|-------------|
260
+ | `@djangocfg/ext-newsletter` | Newsletter subscription |
261
+ | `@djangocfg/ext-knowbase` | Knowledge base + AI chat |
262
+ | `@djangocfg/ext-leads` | Lead capture forms |
263
+ | `@djangocfg/ext-payments` | Payments & subscriptions |
264
+ | `@djangocfg/ext-support` | Support tickets |
805
265
 
806
266
  ## License
807
267
 
808
- MIT
809
-
810
- ## Links
811
-
812
- - [DjangoCFG Documentation](https://djangocfg.com)
813
- - [GitHub](https://github.com/markolofsen/django-cfg)
268
+ 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.218",
3
+ "version": "2.1.221",
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.218",
78
- "@djangocfg/centrifugo": "^2.1.218",
79
- "@djangocfg/i18n": "^2.1.218",
80
- "@djangocfg/monitor": "^2.1.218",
81
- "@djangocfg/ui-core": "^2.1.218",
82
- "@djangocfg/ui-nextjs": "^2.1.218",
83
- "@djangocfg/ui-tools": "^2.1.218",
77
+ "@djangocfg/api": "^2.1.221",
78
+ "@djangocfg/centrifugo": "^2.1.221",
79
+ "@djangocfg/i18n": "^2.1.221",
80
+ "@djangocfg/monitor": "^2.1.221",
81
+ "@djangocfg/ui-core": "^2.1.221",
82
+ "@djangocfg/ui-nextjs": "^2.1.221",
83
+ "@djangocfg/ui-tools": "^2.1.221",
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.221",
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.218",
112
- "@djangocfg/i18n": "^2.1.218",
113
- "@djangocfg/centrifugo": "^2.1.218",
114
- "@djangocfg/monitor": "^2.1.218",
115
- "@djangocfg/typescript-config": "^2.1.218",
116
- "@djangocfg/ui-core": "^2.1.218",
117
- "@djangocfg/ui-nextjs": "^2.1.218",
118
- "@djangocfg/ui-tools": "^2.1.218",
112
+ "@djangocfg/api": "^2.1.221",
113
+ "@djangocfg/i18n": "^2.1.221",
114
+ "@djangocfg/centrifugo": "^2.1.221",
115
+ "@djangocfg/monitor": "^2.1.221",
116
+ "@djangocfg/typescript-config": "^2.1.221",
117
+ "@djangocfg/ui-core": "^2.1.221",
118
+ "@djangocfg/ui-nextjs": "^2.1.221",
119
+ "@djangocfg/ui-tools": "^2.1.221",
119
120
  "@types/node": "^24.7.2",
120
121
  "@types/react": "^19.1.0",
121
122
  "@types/react-dom": "^19.1.0",
@@ -91,6 +91,9 @@ interface LayoutConfig {
91
91
  export interface AppLayoutProps {
92
92
  children: ReactNode;
93
93
 
94
+ /** Project name — used as default for monitor.project and debug panel title */
95
+ project?: string;
96
+
94
97
  /** Public layout component with enabled paths */
95
98
  publicLayout?: LayoutConfig;
96
99
 
@@ -139,6 +142,9 @@ export interface AppLayoutProps {
139
142
 
140
143
  /** Monitor configuration — initialises window.monitor + auto-captures JS errors & console */
141
144
  monitor?: MonitorConfig;
145
+
146
+ /** Debug panel — set false to disable. Default: true */
147
+ debug?: boolean;
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,13 @@ 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
+ // Monitor — project prop as default, override with monitor config
111
+ const monitorConfig = {
112
+ project,
113
+ environment: process.env.NODE_ENV,
114
+ ...monitor,
115
+ };
116
+
101
117
  const content = (
102
118
  <ThemeProvider
103
119
  defaultTheme={theme?.defaultTheme || 'system'}
@@ -130,7 +146,7 @@ export function BaseApp({
130
146
  network={errorTracking?.network}
131
147
  onError={errorTracking?.onError}
132
148
  >
133
- {monitor && <MonitorProvider {...monitor} />}
149
+ <MonitorProvider {...monitorConfig} />
134
150
  {children}
135
151
  <NextTopLoader
136
152
  color="hsl(var(--primary))"
@@ -171,6 +187,9 @@ export function BaseApp({
171
187
 
172
188
  {/* Auth Dialog - global auth prompt */}
173
189
  <AuthDialog authPath={auth?.routes?.auth} />
190
+
191
+ {/* Debug Panel — enabled by default, disable with debug={false} */}
192
+ {debug !== false && <DebugButton />}
174
193
  </ErrorTrackingProvider>
175
194
  </PwaProvider>
176
195
  </CentrifugoProvider>
@@ -29,6 +29,9 @@ import type { MonitorConfig } from '@djangocfg/monitor';
29
29
  export interface BaseLayoutProps {
30
30
  children: ReactNode;
31
31
 
32
+ /** Project name — used as default for monitor.project and debug panel title */
33
+ project?: string;
34
+
32
35
  /** Theme configuration */
33
36
  theme?: ThemeConfig;
34
37
 
@@ -58,4 +61,7 @@ export interface BaseLayoutProps {
58
61
 
59
62
  /** Monitor configuration — initialises window.monitor + auto-captures JS errors & console */
60
63
  monitor?: MonitorConfig;
64
+
65
+ /** Debug panel — set false to disable. Default: true */
66
+ debug?: boolean;
61
67
  }