@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 +142 -670
- package/package.json +17 -16
- package/src/layouts/AppLayout/AppLayout.tsx +10 -0
- package/src/layouts/AppLayout/BaseApp.tsx +24 -1
- package/src/layouts/types/index.ts +1 -1
- package/src/layouts/types/layout.types.ts +13 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @djangocfg/layouts
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
13
|
+
Add to `globals.css` (before `@import "tailwindcss"`):
|
|
14
14
|
|
|
15
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
89
|
+
project="my-app"
|
|
79
90
|
theme={{ defaultTheme: 'system' }}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
## Layouts
|
|
109
|
+
**`noLayoutPaths`** — render without layout wrapper (providers still active). Useful for fullscreen pages.
|
|
129
110
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
### Available Layouts
|
|
111
|
+
### i18n
|
|
133
112
|
|
|
134
113
|
```tsx
|
|
135
|
-
import {
|
|
114
|
+
import { useLocaleSwitcher } from '@djangocfg/nextjs/i18n/client';
|
|
136
115
|
|
|
137
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
logo="/logo.svg"
|
|
161
|
-
title="Sign In"
|
|
162
|
-
subtitle="Welcome back"
|
|
118
|
+
<AppLayout
|
|
119
|
+
i18n={{ locale, locales, onLocaleChange: changeLocale }}
|
|
163
120
|
>
|
|
164
|
-
|
|
165
|
-
</
|
|
121
|
+
{children}
|
|
122
|
+
</AppLayout>
|
|
166
123
|
```
|
|
167
124
|
|
|
168
|
-
|
|
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 {
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
139
|
+
### AuthLayout
|
|
209
140
|
|
|
210
141
|
```tsx
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
googleTrackingId: 'G-XXXXXXXXXX',
|
|
265
|
-
},
|
|
266
|
-
};
|
|
155
|
+
<ProfileLayout
|
|
156
|
+
enable2FA={true}
|
|
157
|
+
showMemberSince={true}
|
|
158
|
+
/>
|
|
267
159
|
```
|
|
268
160
|
|
|
269
|
-
|
|
161
|
+
## Monitor & Debug
|
|
270
162
|
|
|
271
|
-
|
|
163
|
+
Both auto-enabled via `project` prop — no extra config needed.
|
|
272
164
|
|
|
273
165
|
```tsx
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
317
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
354
|
-
|
|
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
|
-
|
|
190
|
+
// disable
|
|
191
|
+
<BaseApp debug={{ enabled: false }}>
|
|
376
192
|
|
|
377
|
-
|
|
378
|
-
import
|
|
193
|
+
// custom tabs (e.g. Zustand store viewer)
|
|
194
|
+
import type { CustomDebugTab } from '@djangocfg/debuger';
|
|
379
195
|
|
|
380
|
-
|
|
381
|
-
|
|
196
|
+
const myTabs: CustomDebugTab[] = [
|
|
197
|
+
{ id: 'store', label: 'Stores', icon: Database, panel: StoreTab },
|
|
198
|
+
];
|
|
382
199
|
|
|
383
|
-
|
|
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
|
-
|
|
203
|
+
## Error Tracking
|
|
393
204
|
|
|
394
205
|
```tsx
|
|
395
|
-
import {
|
|
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
|
-
|
|
208
|
+
// In components
|
|
209
|
+
const { emitError } = useErrorEmitter('MyComponent');
|
|
210
|
+
emitError('Something failed', error);
|
|
409
211
|
|
|
410
|
-
|
|
411
|
-
|
|
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
|
-
|
|
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
|
-
|
|
227
|
+
## PWA
|
|
464
228
|
|
|
465
229
|
```tsx
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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
|
-
|
|
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
|
-
|
|
510
|
-
|
|
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
|
-
|
|
252
|
+
## Legal Pages
|
|
520
253
|
|
|
521
254
|
```tsx
|
|
522
|
-
import {
|
|
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` |
|
|
264
|
+
| `@djangocfg/layouts` | All exports |
|
|
594
265
|
| `@djangocfg/layouts/layouts` | Layout components |
|
|
595
|
-
| `@djangocfg/layouts/snippets` | Reusable components
|
|
596
|
-
| `@djangocfg/layouts/components` |
|
|
597
|
-
| `@djangocfg/layouts/pages` |
|
|
598
|
-
| `@djangocfg/layouts/
|
|
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
|
|
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
|
-
|
|
608
|
-
|
|
609
|
-
|
|
|
610
|
-
|
|
611
|
-
|
|
|
612
|
-
|
|
|
613
|
-
|
|
|
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.
|
|
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.
|
|
78
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
79
|
-
"@djangocfg/i18n": "^2.1.
|
|
80
|
-
"@djangocfg/monitor": "^2.1.
|
|
81
|
-
"@djangocfg/ui-core": "^2.1.
|
|
82
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
83
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
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.
|
|
112
|
-
"@djangocfg/i18n": "^2.1.
|
|
113
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
114
|
-
"@djangocfg/monitor": "^2.1.
|
|
115
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
116
|
-
"@djangocfg/ui-core": "^2.1.
|
|
117
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
118
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
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
|
-
|
|
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>
|
|
@@ -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
|
}
|