@djangocfg/layouts 2.1.37 → 2.1.39

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.
Files changed (77) hide show
  1. package/README.md +204 -18
  2. package/package.json +5 -5
  3. package/src/components/errors/index.ts +9 -0
  4. package/src/components/errors/types.ts +38 -0
  5. package/src/layouts/AppLayout/AppLayout.tsx +33 -45
  6. package/src/layouts/AppLayout/BaseApp.tsx +104 -33
  7. package/src/layouts/AuthLayout/AuthContext.tsx +7 -1
  8. package/src/layouts/AuthLayout/OAuthProviders.tsx +1 -10
  9. package/src/layouts/AuthLayout/OTPForm.tsx +1 -0
  10. package/src/layouts/PrivateLayout/PrivateLayout.tsx +1 -1
  11. package/src/layouts/PublicLayout/PublicLayout.tsx +1 -1
  12. package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
  13. package/src/layouts/PublicLayout/components/PublicNavigation.tsx +1 -1
  14. package/src/layouts/_components/UserMenu.tsx +1 -1
  15. package/src/layouts/index.ts +1 -1
  16. package/src/layouts/types/index.ts +47 -0
  17. package/src/layouts/types/layout.types.ts +61 -0
  18. package/src/layouts/types/providers.types.ts +65 -0
  19. package/src/layouts/types/ui.types.ts +103 -0
  20. package/src/snippets/Analytics/index.ts +1 -0
  21. package/src/snippets/Analytics/types.ts +10 -0
  22. package/src/snippets/PWAInstall/@docs/README.md +92 -0
  23. package/src/snippets/PWAInstall/README.md +185 -0
  24. package/src/snippets/{PWA → PWAInstall}/components/A2HSHint.tsx +85 -84
  25. package/src/snippets/PWAInstall/components/DesktopGuide.tsx +229 -0
  26. package/src/snippets/PWAInstall/context/InstallContext.tsx +102 -0
  27. package/src/snippets/{PWA → PWAInstall}/hooks/useInstallPrompt.ts +3 -0
  28. package/src/snippets/{PWA → PWAInstall}/index.ts +12 -31
  29. package/src/snippets/{PWA → PWAInstall}/types/components.ts +0 -6
  30. package/src/snippets/PWAInstall/types/config.ts +22 -0
  31. package/src/snippets/{PWA → PWAInstall}/types/index.ts +4 -4
  32. package/src/snippets/{PWA → PWAInstall}/utils/localStorage.ts +1 -23
  33. package/src/snippets/PushNotifications/@docs/README.md +191 -0
  34. package/src/snippets/PushNotifications/@docs/guides/django-integration.md +648 -0
  35. package/src/snippets/PushNotifications/@docs/guides/service-worker.md +467 -0
  36. package/src/snippets/PushNotifications/@docs/guides/vapid-setup.md +352 -0
  37. package/src/snippets/PushNotifications/README.md +328 -0
  38. package/src/snippets/{PWA → PushNotifications}/config.ts +2 -2
  39. package/src/snippets/PushNotifications/context/DjangoPushContext.tsx +190 -0
  40. package/src/snippets/{PWA → PushNotifications}/hooks/useDjangoPush.ts +63 -81
  41. package/src/snippets/{PWA → PushNotifications}/hooks/usePushNotifications.ts +12 -8
  42. package/src/snippets/PushNotifications/index.ts +87 -0
  43. package/src/snippets/PushNotifications/types/config.ts +28 -0
  44. package/src/snippets/PushNotifications/types/index.ts +9 -0
  45. package/src/snippets/PushNotifications/utils/localStorage.ts +60 -0
  46. package/src/snippets/PushNotifications/utils/logger.ts +149 -0
  47. package/src/snippets/PushNotifications/utils/platform.ts +151 -0
  48. package/src/snippets/index.ts +37 -12
  49. package/src/layouts/shared/index.ts +0 -21
  50. package/src/layouts/shared/types.ts +0 -247
  51. package/src/snippets/PWA/@refactoring/ARCHITECTURE_ANALYSIS.md +0 -1179
  52. package/src/snippets/PWA/@refactoring/EXECUTIVE_SUMMARY.md +0 -271
  53. package/src/snippets/PWA/@refactoring/README.md +0 -204
  54. package/src/snippets/PWA/@refactoring/REFACTORING_PROPOSALS.md +0 -1109
  55. package/src/snippets/PWA/@refactoring2/COMPARISON-WITH-NEXTJS.md +0 -718
  56. package/src/snippets/PWA/@refactoring2/P1-FIXES-COMPLETED.md +0 -188
  57. package/src/snippets/PWA/@refactoring2/POST-P0-ANALYSIS.md +0 -362
  58. package/src/snippets/PWA/@refactoring2/README.md +0 -85
  59. package/src/snippets/PWA/@refactoring2/RECOMMENDATIONS.md +0 -1321
  60. package/src/snippets/PWA/@refactoring2/REMAINING-ISSUES.md +0 -557
  61. package/src/snippets/PWA/README.md +0 -387
  62. package/src/snippets/PWA/context/DjangoPushContext.tsx +0 -105
  63. package/src/snippets/PWA/context/InstallContext.tsx +0 -118
  64. package/src/snippets/PWA/context/PushContext.tsx +0 -156
  65. /package/src/layouts/{shared → types}/README.md +0 -0
  66. /package/src/snippets/{PWA/@docs/research.md → PWAInstall/@docs/research/ios-android-install-flows.md} +0 -0
  67. /package/src/snippets/{PWA → PWAInstall}/components/IOSGuide.tsx +0 -0
  68. /package/src/snippets/{PWA → PWAInstall}/components/IOSGuideDrawer.tsx +0 -0
  69. /package/src/snippets/{PWA → PWAInstall}/components/IOSGuideModal.tsx +0 -0
  70. /package/src/snippets/{PWA → PWAInstall}/hooks/useIsPWA.ts +0 -0
  71. /package/src/snippets/{PWA → PWAInstall}/types/install.ts +0 -0
  72. /package/src/snippets/{PWA → PWAInstall}/types/platform.ts +0 -0
  73. /package/src/snippets/{PWA → PWAInstall}/utils/logger.ts +0 -0
  74. /package/src/snippets/{PWA → PWAInstall}/utils/platform.ts +0 -0
  75. /package/src/snippets/{PWA → PushNotifications}/components/PushPrompt.tsx +0 -0
  76. /package/src/snippets/{PWA → PushNotifications}/types/push.ts +0 -0
  77. /package/src/snippets/{PWA → PushNotifications}/utils/vapid.ts +0 -0
package/README.md CHANGED
@@ -24,7 +24,17 @@ export default function RootLayout({ children }) {
24
24
  return (
25
25
  <html lang="en" suppressHydrationWarning>
26
26
  <body>
27
- <BaseApp theme={{ defaultTheme: 'dark' }}>
27
+ <BaseApp
28
+ theme={{ defaultTheme: 'dark' }}
29
+ analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
30
+ centrifugo={{ enabled: true, url: process.env.NEXT_PUBLIC_CENTRIFUGO_URL }}
31
+ pwaInstall={{ enabled: true, showInstallHint: true }}
32
+ pushNotifications={{
33
+ enabled: true,
34
+ vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY || ''
35
+ }}
36
+ mcpChat={{ enabled: true, autoDetectEnvironment: true }}
37
+ >
28
38
  {children}
29
39
  </BaseApp>
30
40
  </body>
@@ -34,12 +44,23 @@ export default function RootLayout({ children }) {
34
44
  ```
35
45
 
36
46
  **Included providers:**
37
- - ThemeProvider (light/dark/system)
38
- - TooltipProvider
39
- - SWRConfig
40
- - AuthProvider (from `@djangocfg/api`)
41
- - ErrorTrackingProvider
42
- - Toaster + PageProgress
47
+ - **ThemeProvider** - Light/dark/system theme management
48
+ - **TooltipProvider** - Tooltip positioning context
49
+ - **SWRConfig** - Data fetching configuration
50
+ - **AuthProvider** - Authentication context (from `@djangocfg/api`)
51
+ - **AnalyticsProvider** - Google Analytics (optional)
52
+ - **CentrifugoProvider** - WebSocket real-time (optional)
53
+ - **PwaProvider** - PWA installation (optional)
54
+ - **DjangoPushProvider** - Django web push integration with history (optional)
55
+ - **ErrorTrackingProvider** - Error handling and tracking
56
+ - **ErrorBoundary** - React error boundary
57
+ - **MCP Chat Widget** - AI chat assistant (optional)
58
+
59
+ **Global components:**
60
+ - **PageProgress** - NProgress bar for route changes
61
+ - **Toaster** - Toast notifications container
62
+ - **A2HSHint** - PWA install hint (if enabled)
63
+ - **PushPrompt** - Push notification prompt (if enabled)
43
64
 
44
65
  > **Note:** Auth functionality is provided by `@djangocfg/api` package. See [@djangocfg/api documentation](../api/README.md) for auth usage.
45
66
 
@@ -51,6 +72,7 @@ Smart layout router built on BaseApp - automatically selects layout based on rou
51
72
  import { AppLayout } from '@djangocfg/layouts';
52
73
  import { PublicLayout } from './_layouts/PublicLayout';
53
74
  import { PrivateLayout } from './_layouts/PrivateLayout';
75
+ import { AdminLayout } from './_layouts/AdminLayout';
54
76
 
55
77
  // app/layout.tsx
56
78
  export default function RootLayout({ children }) {
@@ -58,6 +80,16 @@ export default function RootLayout({ children }) {
58
80
  <html lang="en" suppressHydrationWarning>
59
81
  <body>
60
82
  <AppLayout
83
+ // Provider configs (passed to BaseApp)
84
+ theme={{ defaultTheme: 'system' }}
85
+ analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
86
+ pwaInstall={{ enabled: true }}
87
+ pushNotifications={{
88
+ enabled: true,
89
+ vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY || ''
90
+ }}
91
+
92
+ // Layout components
61
93
  publicLayout={{
62
94
  component: PublicLayout,
63
95
  enabledPath: ['/', '/legal', '/contact']
@@ -66,7 +98,10 @@ export default function RootLayout({ children }) {
66
98
  component: PrivateLayout,
67
99
  enabledPath: ['/dashboard', '/profile']
68
100
  }}
69
- theme={{ defaultTheme: 'system' }}
101
+ adminLayout={{
102
+ component: AdminLayout,
103
+ enabledPath: '/admin'
104
+ }}
70
105
  >
71
106
  {children}
72
107
  </AppLayout>
@@ -185,14 +220,131 @@ Built-in tracking for:
185
220
  - **OAuth events** - GitHub OAuth start, success, failure
186
221
  - **Errors** - React ErrorBoundary errors
187
222
 
223
+ ## PWA & Push Notifications
224
+
225
+ Progressive Web App features and Web Push notifications support.
226
+
227
+ ### PWA Installation
228
+
229
+ Enable PWA installation prompts with `pwaInstall` config:
230
+
231
+ ```tsx
232
+ import { BaseApp } from '@djangocfg/layouts';
233
+
234
+ <BaseApp
235
+ pwaInstall={{
236
+ enabled: true,
237
+ showInstallHint: true, // Show A2HS hint
238
+ resetAfterDays: 3, // Re-show after dismissal
239
+ delayMs: 1000, // Delay before showing
240
+ logo: '/logo192.png', // PWA logo
241
+ }}
242
+ >
243
+ {children}
244
+ </BaseApp>
245
+ ```
246
+
247
+ **Features:**
248
+ - **A2HSHint** - Platform-specific install hints (iOS Safari, Android Chrome, Desktop)
249
+ - **Auto-detection** - Detects if running as PWA
250
+ - **Dismissal tracking** - Respects user dismissal with localStorage
251
+ - **Custom timing** - Configurable delay and reset periods
252
+
253
+ ### Push Notifications
254
+
255
+ Enable Web Push notifications with `pushNotifications` config:
256
+
257
+ ```tsx
258
+ import { BaseApp } from '@djangocfg/layouts';
259
+
260
+ <BaseApp
261
+ pushNotifications={{
262
+ enabled: true,
263
+ vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY || '',
264
+ subscribeEndpoint: '/api/push/subscribe', // Backend subscription endpoint
265
+ requirePWA: true, // Only show if installed as PWA
266
+ autoSubscribe: false, // Auto-subscribe on install
267
+ delayMs: 5000, // Delay before showing prompt
268
+ resetAfterDays: 7, // Re-show after dismissal
269
+ }}
270
+ >
271
+ {children}
272
+ </BaseApp>
273
+ ```
274
+
275
+ **Features:**
276
+ - **PushPrompt** - Permission request prompt
277
+ - **VAPID authentication** - Secure push with VAPID keys
278
+ - **Subscription management** - Auto-subscribe and unsubscribe
279
+ - **PWA integration** - Can require PWA installation first
280
+ - **Custom timing** - Configurable delay and reset
281
+
282
+ ### Usage
283
+
284
+ ```tsx
285
+ import { usePwa, useDjangoPushContext } from '@djangocfg/layouts/snippets';
286
+
287
+ // PWA status
288
+ const { isPWA, isInstallable } = usePwa();
289
+
290
+ // Push notifications (Django integration)
291
+ const {
292
+ isSupported,
293
+ isSubscribed,
294
+ permission,
295
+ pushes,
296
+ subscribe,
297
+ unsubscribe,
298
+ sendPush,
299
+ clearPushes,
300
+ removePush,
301
+ } = useDjangoPushContext();
302
+
303
+ // Subscribe to push
304
+ await subscribe();
305
+
306
+ // Send test notification (via Django API)
307
+ await sendPush({
308
+ title: 'Hello!',
309
+ body: 'Test notification',
310
+ icon: '/icon.png',
311
+ });
312
+
313
+ // Manage push history
314
+ clearPushes(); // Clear all
315
+ removePush(pushId); // Remove specific push
316
+ ```
317
+
318
+ ### VAPID Keys Generation
319
+
320
+ Generate VAPID keys for push notifications:
321
+
322
+ ```bash
323
+ # Using web-push CLI
324
+ npx web-push generate-vapid-keys
325
+
326
+ # Or use openssl
327
+ openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem
328
+ openssl ec -in vapid_private.pem -pubout -out vapid_public.pem
329
+ ```
330
+
331
+ Add keys to `.env.local`:
332
+
333
+ ```env
334
+ NEXT_PUBLIC_VAPID_PUBLIC_KEY=your_public_key_here
335
+ VAPID_PRIVATE_KEY=your_private_key_here
336
+ ```
337
+
188
338
  ## Snippets
189
339
 
190
- Reusable UI components ready to use.
340
+ Reusable UI components and hooks ready to use.
191
341
 
192
342
  ```tsx
193
343
  import {
194
344
  Breadcrumbs,
195
345
  AuthDialog,
346
+ usePwa,
347
+ useDjangoPushContext,
196
348
  } from '@djangocfg/layouts/snippets';
197
349
  ```
198
350
 
@@ -201,6 +353,11 @@ import {
201
353
  | `Breadcrumbs` | Navigation breadcrumbs with automatic path generation |
202
354
  | `AuthDialog` | Auth modal (login/register) with event-based triggers |
203
355
  | `AnalyticsProvider` | Analytics wrapper component |
356
+ | `usePwa` | PWA status hook (isPWA, isInstallable, etc.) |
357
+ | `useDjangoPushContext` | Django push notifications hook (subscribe, send, history) |
358
+ | `usePush` | Generic push hook (for non-Django apps) |
359
+ | `A2HSHint` | Add to Home Screen hint component |
360
+ | `PushPrompt` | Push notification permission prompt |
204
361
 
205
362
  > **Extension Snippets:** Additional components are available in extension packages:
206
363
  > - `@djangocfg/ext-leads` - ContactForm, ContactPage, ContactInfo
@@ -516,27 +673,56 @@ export default function DashboardPage() {
516
673
  }
517
674
  ```
518
675
 
519
- ### Auth Page
676
+ ### Auth Page (OTP Authentication)
520
677
 
521
678
  ```tsx
522
- // app/auth/login/page.tsx
679
+ // app/auth/page.tsx
680
+ 'use client';
681
+
523
682
  import { AuthLayout } from '@djangocfg/layouts';
524
- import { LoginForm } from './_components/LoginForm';
525
683
 
526
- export default function LoginPage() {
684
+ export default function AuthPage() {
527
685
  return (
528
686
  <AuthLayout
529
- logo="/logo.svg"
530
- siteName="My App"
531
- title="Sign In"
532
- subtitle="Welcome back! Please sign in to continue."
687
+ sourceUrl="https://example.com"
688
+ supportUrl="/support"
689
+ termsUrl="/terms"
690
+ privacyUrl="/privacy"
691
+ enablePhoneAuth={false}
692
+ enableGithubAuth={true}
693
+ redirectUrl="/dashboard"
694
+ onOTPSuccess={() => {
695
+ console.log('OTP authentication successful');
696
+ }}
697
+ onOAuthSuccess={(user, isNewUser, provider) => {
698
+ console.log('OAuth success:', { user, isNewUser, provider });
699
+ }}
533
700
  >
534
- <LoginForm />
701
+ <div className="text-center mb-6">
702
+ <h2 className="text-2xl font-bold">Welcome to My App</h2>
703
+ <p className="text-muted-foreground mt-2">
704
+ Sign in with your email or phone
705
+ </p>
706
+ </div>
535
707
  </AuthLayout>
536
708
  );
537
709
  }
538
710
  ```
539
711
 
712
+ **AuthLayout Props:**
713
+ | Prop | Type | Description |
714
+ |------|------|-------------|
715
+ | `sourceUrl` | `string` | Application URL for OTP emails |
716
+ | `redirectUrl` | `string` | URL to redirect after successful auth (default: `/dashboard`) |
717
+ | `enablePhoneAuth` | `boolean` | Enable phone number authentication |
718
+ | `enableGithubAuth` | `boolean` | Enable GitHub OAuth |
719
+ | `termsUrl` | `string` | Terms of service URL (shows checkbox if provided) |
720
+ | `privacyUrl` | `string` | Privacy policy URL |
721
+ | `supportUrl` | `string` | Support page URL |
722
+ | `onOTPSuccess` | `() => void` | Callback after successful OTP verification |
723
+ | `onOAuthSuccess` | `(user, isNewUser, provider) => void` | Callback after successful OAuth |
724
+ | `onError` | `(message: string) => void` | Error callback |
725
+
540
726
  ## License
541
727
 
542
728
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.37",
3
+ "version": "2.1.39",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -92,9 +92,9 @@
92
92
  "check": "tsc --noEmit"
93
93
  },
94
94
  "peerDependencies": {
95
- "@djangocfg/api": "^2.1.37",
96
- "@djangocfg/centrifugo": "^2.1.37",
97
- "@djangocfg/ui-nextjs": "^2.1.37",
95
+ "@djangocfg/api": "^2.1.39",
96
+ "@djangocfg/centrifugo": "^2.1.39",
97
+ "@djangocfg/ui-nextjs": "^2.1.39",
98
98
  "@hookform/resolvers": "^5.2.0",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
@@ -114,7 +114,7 @@
114
114
  "uuid": "^11.1.0"
115
115
  },
116
116
  "devDependencies": {
117
- "@djangocfg/typescript-config": "^2.1.37",
117
+ "@djangocfg/typescript-config": "^2.1.39",
118
118
  "@types/node": "^24.7.2",
119
119
  "@types/react": "^19.1.0",
120
120
  "@types/react-dom": "^19.1.0",
@@ -8,3 +8,12 @@ export type { ErrorLayoutProps } from './ErrorLayout';
8
8
  export { getErrorContent, ERROR_CODES } from './errorConfig';
9
9
  export type { ErrorContent } from './errorConfig';
10
10
 
11
+ // Configuration types
12
+ export type {
13
+ ErrorBoundaryConfig,
14
+ ErrorTrackingConfig,
15
+ ValidationErrorConfig,
16
+ CORSErrorConfig,
17
+ NetworkErrorConfig,
18
+ } from './types';
19
+
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Error Handling Configuration Types
3
+ *
4
+ * Configuration types for ErrorTrackingProvider and ErrorBoundary
5
+ */
6
+
7
+ import type { ErrorInfo } from 'react';
8
+
9
+ // Re-export ErrorTrackingConfig from ErrorsTracker
10
+ export type {
11
+ ErrorTrackingConfig,
12
+ ValidationErrorConfig,
13
+ CORSErrorConfig,
14
+ NetworkErrorConfig,
15
+ ErrorTypeConfig,
16
+ ErrorDetail,
17
+ ValidationErrorDetail,
18
+ CORSErrorDetail,
19
+ NetworkErrorDetail,
20
+ StoredError,
21
+ ErrorTrackingContextValue,
22
+ } from './ErrorsTracker/types';
23
+
24
+ /**
25
+ * Error Boundary Configuration
26
+ *
27
+ * Configuration for React ErrorBoundary component
28
+ */
29
+ export interface ErrorBoundaryConfig {
30
+ /** Enable error boundary (default: true) */
31
+ enabled?: boolean;
32
+
33
+ /** Support email to display in error UI */
34
+ supportEmail?: string;
35
+
36
+ /** Custom error handler called when error is caught */
37
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
38
+ }
@@ -38,18 +38,19 @@
38
38
 
39
39
  import React, { ReactNode, useMemo } from 'react';
40
40
  import { usePathname } from 'next/navigation';
41
- import { CentrifugoProvider } from '@djangocfg/centrifugo';
42
- import { getCentrifugoAuthTokenRetrieve } from '@djangocfg/api';
43
- import { AnalyticsProvider } from '../../snippets/Analytics';
44
41
  import { Suspense, ClientOnly } from '../../components/core';
45
42
  import { BaseApp } from './BaseApp';
46
43
  import type {
47
44
  ThemeConfig,
48
- LayoutErrorTrackingConfig,
45
+ AnalyticsConfig,
46
+ CentrifugoConfig,
47
+ ErrorTrackingConfig,
49
48
  ErrorBoundaryConfig,
50
49
  SWRConfigOptions,
51
50
  McpChatConfig,
52
- } from '../shared/types';
51
+ PwaInstallConfig,
52
+ PushNotificationsConfig,
53
+ } from '../types';
53
54
  import type { AuthConfig } from '@djangocfg/api/auth';
54
55
 
55
56
  export type LayoutMode = 'public' | 'private' | 'admin';
@@ -114,19 +115,13 @@ export interface AppLayoutProps {
114
115
  auth?: AuthConfig;
115
116
 
116
117
  /** Analytics configuration */
117
- analytics?: {
118
- googleTrackingId?: string;
119
- };
118
+ analytics?: AnalyticsConfig;
120
119
 
121
120
  /** Centrifugo configuration */
122
- centrifugo?: {
123
- enabled?: boolean;
124
- autoConnect?: boolean;
125
- url?: string;
126
- };
121
+ centrifugo?: CentrifugoConfig;
127
122
 
128
123
  /** Error tracking configuration */
129
- errorTracking?: LayoutErrorTrackingConfig;
124
+ errorTracking?: ErrorTrackingConfig;
130
125
 
131
126
  /** SWR configuration */
132
127
  swr?: SWRConfigOptions;
@@ -136,6 +131,12 @@ export interface AppLayoutProps {
136
131
 
137
132
  /** MCP Chat configuration */
138
133
  mcpChat?: McpChatConfig;
134
+
135
+ /** PWA Install configuration */
136
+ pwaInstall?: PwaInstallConfig;
137
+
138
+ /** Push Notifications configuration */
139
+ pushNotifications?: PushNotificationsConfig;
139
140
  }
140
141
 
141
142
  /**
@@ -213,52 +214,39 @@ function AppLayoutContent({
213
214
  }
214
215
  };
215
216
 
216
- const layoutContent = renderLayout();
217
-
218
- // Wrap with providers (order matters!)
219
- let content: ReactNode = layoutContent;
220
-
221
- // Analytics Provider (if enabled)
222
- if (analytics?.googleTrackingId) {
223
- content = <AnalyticsProvider trackingId={analytics.googleTrackingId}>{content}</AnalyticsProvider>;
224
- }
225
-
226
- // Centrifugo Provider (always wrap, but only enable if configured)
227
- // This ensures useCentrifugo() hook works even when Centrifugo is disabled
228
- const centrifugoUrl = centrifugo?.url || process.env.NEXT_PUBLIC_CENTRIFUGO_URL;
229
- const centrifugoEnabled = centrifugo?.enabled !== false;
230
-
231
- content = (
232
- <CentrifugoProvider
233
- enabled={centrifugoEnabled}
234
- autoConnect={centrifugoEnabled && centrifugo?.autoConnect}
235
- url={centrifugoUrl}
236
- onTokenRefresh={async () => {
237
- const response = await getCentrifugoAuthTokenRetrieve();
238
- return response.token;
239
- }}
240
- >
241
- {content}
242
- </CentrifugoProvider>
243
- );
244
-
245
- return content;
217
+ // No providers here - all providers now in BaseApp
218
+ return renderLayout();
246
219
  }
247
220
 
248
221
  /**
249
222
  * AppLayout - Main Component with All Providers
250
223
  */
251
224
  export function AppLayout(props: AppLayoutProps) {
252
- const { theme, auth, errorTracking, errorBoundary, swr, mcpChat } = props;
225
+ const {
226
+ theme,
227
+ auth,
228
+ analytics,
229
+ centrifugo,
230
+ errorTracking,
231
+ errorBoundary,
232
+ swr,
233
+ mcpChat,
234
+ pwaInstall,
235
+ pushNotifications,
236
+ } = props;
253
237
 
254
238
  return (
255
239
  <BaseApp
256
240
  theme={theme}
257
241
  auth={auth}
242
+ analytics={analytics}
243
+ centrifugo={centrifugo}
258
244
  errorTracking={errorTracking}
259
245
  errorBoundary={errorBoundary}
260
246
  swr={swr}
261
247
  mcpChat={mcpChat}
248
+ pwaInstall={pwaInstall}
249
+ pushNotifications={pushNotifications}
262
250
  >
263
251
  <AppLayoutContent {...props} />
264
252
  </BaseApp>