@djangocfg/layouts 2.1.138 → 2.1.140

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 (27) hide show
  1. package/README.md +2 -96
  2. package/package.json +14 -14
  3. package/src/components/errors/ErrorsTracker/components/ErrorToast.tsx +3 -21
  4. package/src/components/errors/ErrorsTracker/providers/ErrorTrackingProvider.tsx +83 -2
  5. package/src/layouts/AppLayout/AppLayout.tsx +0 -6
  6. package/src/layouts/AppLayout/BaseApp.tsx +47 -75
  7. package/src/layouts/types/index.ts +0 -1
  8. package/src/layouts/types/layout.types.ts +0 -4
  9. package/src/snippets/index.ts +0 -23
  10. package/src/snippets/PushNotifications/@docs/README.md +0 -191
  11. package/src/snippets/PushNotifications/@docs/guides/django-integration.md +0 -648
  12. package/src/snippets/PushNotifications/@docs/guides/service-worker.md +0 -467
  13. package/src/snippets/PushNotifications/@docs/guides/vapid-setup.md +0 -352
  14. package/src/snippets/PushNotifications/README.md +0 -328
  15. package/src/snippets/PushNotifications/components/PushPrompt.tsx +0 -184
  16. package/src/snippets/PushNotifications/config.ts +0 -20
  17. package/src/snippets/PushNotifications/context/DjangoPushContext.tsx +0 -192
  18. package/src/snippets/PushNotifications/hooks/useDjangoPush.ts +0 -260
  19. package/src/snippets/PushNotifications/hooks/usePushNotifications.ts +0 -225
  20. package/src/snippets/PushNotifications/index.ts +0 -97
  21. package/src/snippets/PushNotifications/types/config.ts +0 -28
  22. package/src/snippets/PushNotifications/types/index.ts +0 -9
  23. package/src/snippets/PushNotifications/types/push.ts +0 -21
  24. package/src/snippets/PushNotifications/utils/localStorage.ts +0 -60
  25. package/src/snippets/PushNotifications/utils/logger.ts +0 -149
  26. package/src/snippets/PushNotifications/utils/platform.ts +0 -326
  27. package/src/snippets/PushNotifications/utils/vapid.ts +0 -226
package/README.md CHANGED
@@ -29,10 +29,6 @@ export default function RootLayout({ children }) {
29
29
  analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
30
30
  centrifugo={{ enabled: true, url: process.env.NEXT_PUBLIC_CENTRIFUGO_URL }}
31
31
  pwaInstall={{ enabled: true, showInstallHint: true }}
32
- pushNotifications={{
33
- enabled: true,
34
- vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY || ''
35
- }}
36
32
  mcpChat={{ enabled: true, autoDetectEnvironment: true }}
37
33
  >
38
34
  {children}
@@ -51,7 +47,6 @@ export default function RootLayout({ children }) {
51
47
  - **AnalyticsProvider** - Google Analytics (optional)
52
48
  - **CentrifugoProvider** - WebSocket real-time (optional)
53
49
  - **PwaProvider** - PWA installation (optional)
54
- - **DjangoPushProvider** - Django web push integration with history (optional)
55
50
  - **ErrorTrackingProvider** - Error handling and tracking
56
51
  - **ErrorBoundary** - React error boundary
57
52
  - **MCP Chat Widget** - AI chat assistant (optional)
@@ -60,7 +55,6 @@ export default function RootLayout({ children }) {
60
55
  - **PageProgress** - NProgress bar for route changes
61
56
  - **Toaster** - Toast notifications container
62
57
  - **A2HSHint** - PWA install hint (if enabled)
63
- - **PushPrompt** - Push notification prompt (if enabled)
64
58
 
65
59
  > **Note:** Auth functionality is provided by `@djangocfg/api` package. See [@djangocfg/api documentation](../api/README.md) for auth usage.
66
60
 
@@ -84,10 +78,6 @@ export default function RootLayout({ children }) {
84
78
  theme={{ defaultTheme: 'system' }}
85
79
  analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
86
80
  pwaInstall={{ enabled: true }}
87
- pushNotifications={{
88
- enabled: true,
89
- vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY || ''
90
- }}
91
81
 
92
82
  // Layout components
93
83
  publicLayout={{
@@ -298,11 +288,7 @@ Built-in tracking for:
298
288
  - **OAuth events** - GitHub OAuth start, success, failure
299
289
  - **Errors** - React ErrorBoundary errors
300
290
 
301
- ## PWA & Push Notifications
302
-
303
- Progressive Web App features and Web Push notifications support.
304
-
305
- ### PWA Installation
291
+ ## PWA Installation
306
292
 
307
293
  Enable PWA installation prompts with `pwaInstall` config:
308
294
 
@@ -333,89 +319,13 @@ import { BaseApp } from '@djangocfg/layouts';
333
319
  **Page Resume:**
334
320
  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.
335
321
 
336
- ### Push Notifications
337
-
338
- Enable Web Push notifications with `pushNotifications` config:
339
-
340
- ```tsx
341
- import { BaseApp } from '@djangocfg/layouts';
342
-
343
- <BaseApp
344
- pushNotifications={{
345
- enabled: true,
346
- vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY || '',
347
- subscribeEndpoint: '/api/push/subscribe', // Backend subscription endpoint
348
- requirePWA: true, // Only show if installed as PWA
349
- autoSubscribe: false, // Auto-subscribe on install
350
- delayMs: 5000, // Delay before showing prompt
351
- resetAfterDays: 7, // Re-show after dismissal
352
- }}
353
- >
354
- {children}
355
- </BaseApp>
356
- ```
357
-
358
- **Features:**
359
- - **PushPrompt** - Permission request prompt
360
- - **VAPID authentication** - Secure push with VAPID keys
361
- - **Subscription management** - Auto-subscribe and unsubscribe
362
- - **PWA integration** - Can require PWA installation first
363
- - **Custom timing** - Configurable delay and reset
364
-
365
322
  ### Usage
366
323
 
367
324
  ```tsx
368
- import { usePwa, useDjangoPushContext } from '@djangocfg/layouts/snippets';
325
+ import { usePwa } from '@djangocfg/layouts/snippets';
369
326
 
370
327
  // PWA status
371
328
  const { isPWA, isInstallable } = usePwa();
372
-
373
- // Push notifications (Django integration)
374
- const {
375
- isSupported,
376
- isSubscribed,
377
- permission,
378
- pushes,
379
- subscribe,
380
- unsubscribe,
381
- sendPush,
382
- clearPushes,
383
- removePush,
384
- } = useDjangoPushContext();
385
-
386
- // Subscribe to push
387
- await subscribe();
388
-
389
- // Send test notification (via Django API)
390
- await sendPush({
391
- title: 'Hello!',
392
- body: 'Test notification',
393
- icon: '/icon.png',
394
- });
395
-
396
- // Manage push history
397
- clearPushes(); // Clear all
398
- removePush(pushId); // Remove specific push
399
- ```
400
-
401
- ### VAPID Keys Generation
402
-
403
- Generate VAPID keys for push notifications:
404
-
405
- ```bash
406
- # Using web-push CLI
407
- npx web-push generate-vapid-keys
408
-
409
- # Or use openssl
410
- openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem
411
- openssl ec -in vapid_private.pem -pubout -out vapid_public.pem
412
- ```
413
-
414
- Add keys to `.env.local`:
415
-
416
- ```env
417
- NEXT_PUBLIC_VAPID_PUBLIC_KEY=your_public_key_here
418
- VAPID_PRIVATE_KEY=your_private_key_here
419
329
  ```
420
330
 
421
331
  ## Snippets
@@ -427,7 +337,6 @@ import {
427
337
  Breadcrumbs,
428
338
  AuthDialog,
429
339
  usePwa,
430
- useDjangoPushContext,
431
340
  } from '@djangocfg/layouts/snippets';
432
341
  ```
433
342
 
@@ -438,11 +347,8 @@ import {
438
347
  | `AnalyticsProvider` | Analytics wrapper component |
439
348
  | `usePwa` | PWA status hook (isPWA, isInstallable, etc.) |
440
349
  | `usePWAPageResume` | Resume last page on PWA launch |
441
- | `useDjangoPushContext` | Django push notifications hook (subscribe, send, history) |
442
- | `usePush` | Generic push hook (for non-Django apps) |
443
350
  | `A2HSHint` | Add to Home Screen hint component |
444
351
  | `PWAPageResumeManager` | Component for PWA page resume (use via BaseApp config) |
445
- | `PushPrompt` | Push notification permission prompt |
446
352
 
447
353
  > **Extension Snippets:** Additional components are available in extension packages:
448
354
  > - `@djangocfg/ext-leads` - ContactForm, ContactPage, ContactInfo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.138",
3
+ "version": "2.1.140",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -74,12 +74,12 @@
74
74
  "check": "tsc --noEmit"
75
75
  },
76
76
  "peerDependencies": {
77
- "@djangocfg/api": "^2.1.138",
78
- "@djangocfg/centrifugo": "^2.1.138",
79
- "@djangocfg/i18n": "^2.1.138",
80
- "@djangocfg/ui-core": "^2.1.138",
81
- "@djangocfg/ui-nextjs": "^2.1.138",
82
- "@djangocfg/ui-tools": "^2.1.138",
77
+ "@djangocfg/api": "^2.1.140",
78
+ "@djangocfg/centrifugo": "^2.1.140",
79
+ "@djangocfg/i18n": "^2.1.140",
80
+ "@djangocfg/ui-core": "^2.1.140",
81
+ "@djangocfg/ui-nextjs": "^2.1.140",
82
+ "@djangocfg/ui-tools": "^2.1.140",
83
83
  "@hookform/resolvers": "^5.2.2",
84
84
  "consola": "^3.4.2",
85
85
  "lucide-react": "^0.545.0",
@@ -102,13 +102,13 @@
102
102
  "uuid": "^11.1.0"
103
103
  },
104
104
  "devDependencies": {
105
- "@djangocfg/api": "^2.1.138",
106
- "@djangocfg/i18n": "^2.1.138",
107
- "@djangocfg/centrifugo": "^2.1.138",
108
- "@djangocfg/typescript-config": "^2.1.138",
109
- "@djangocfg/ui-core": "^2.1.138",
110
- "@djangocfg/ui-nextjs": "^2.1.138",
111
- "@djangocfg/ui-tools": "^2.1.138",
105
+ "@djangocfg/api": "^2.1.140",
106
+ "@djangocfg/i18n": "^2.1.140",
107
+ "@djangocfg/centrifugo": "^2.1.140",
108
+ "@djangocfg/typescript-config": "^2.1.140",
109
+ "@djangocfg/ui-core": "^2.1.140",
110
+ "@djangocfg/ui-nextjs": "^2.1.140",
111
+ "@djangocfg/ui-tools": "^2.1.140",
112
112
  "@types/node": "^24.7.2",
113
113
  "@types/react": "^19.1.0",
114
114
  "@types/react-dom": "^19.1.0",
@@ -60,36 +60,18 @@ function buildValidationDescription(
60
60
 
61
61
  /**
62
62
  * Build CORS error description
63
+ * Simplified: just show that server is unavailable, without CORS details
63
64
  */
64
65
  function buildCORSDescription(
65
66
  detail: CORSErrorDetail,
66
67
  config: Required<CORSErrorConfig>
67
68
  ): React.ReactNode {
68
69
  const domain = extractDomain(detail.url);
69
- const parts: string[] = [];
70
-
71
- // Add method and URL info
72
- if (config.showMethod && config.showUrl) {
73
- parts.push(`${detail.method} ${detail.url}`);
74
- } else if (config.showUrl) {
75
- parts.push(detail.url);
76
- } else if (config.showMethod) {
77
- parts.push(`${detail.method} request blocked`);
78
- }
79
70
 
80
71
  return (
81
72
  <div className="flex flex-col gap-2 text-sm">
82
- {parts.length > 0 && (
83
- <div className="font-mono text-xs opacity-90">
84
- {parts.join(' • ')}
85
- </div>
86
- )}
87
-
88
- <div className="flex flex-col gap-2">
89
- <div className="font-medium">Request blocked by CORS policy</div>
90
- <div className="text-xs opacity-75">
91
- Check CORS configuration on {domain}
92
- </div>
73
+ <div className="opacity-90">
74
+ Server {domain} is unavailable
93
75
  </div>
94
76
 
95
77
  <ErrorButtons detail={detail} />
@@ -65,6 +65,84 @@ function generateErrorId(type: string): string {
65
65
  return `${type}-error-${Date.now()}-${++errorIdCounter}`;
66
66
  }
67
67
 
68
+ /**
69
+ * Generate deduplication key for error
70
+ * Used to prevent duplicate toasts for the same error
71
+ */
72
+ function getErrorDedupeKey(detail: ErrorDetail): string {
73
+ switch (detail.type) {
74
+ case 'validation':
75
+ return `validation:${detail.path}:${detail.method}`;
76
+ case 'cors':
77
+ return `cors:${detail.url}`;
78
+ case 'network':
79
+ return `network:${detail.url}:${detail.statusCode || 'unknown'}`;
80
+ case 'centrifugo':
81
+ return `centrifugo:${detail.method}:${detail.code || 'unknown'}`;
82
+ default:
83
+ return `unknown:${Date.now()}`;
84
+ }
85
+ }
86
+
87
+ /** Deduplication window in ms - same errors within this window are ignored */
88
+ const DEDUPE_WINDOW_MS = 5000;
89
+
90
+ /** Max entries in deduplication map before forced cleanup */
91
+ const DEDUPE_MAX_ENTRIES = 50;
92
+
93
+ /** Map to track recent errors for deduplication (browser-only) */
94
+ let recentErrors: Map<string, number> | null = null;
95
+
96
+ function getRecentErrorsMap(): Map<string, number> {
97
+ // Lazy init - only create in browser
98
+ if (typeof window === 'undefined') {
99
+ // Return dummy map for SSR (won't persist)
100
+ return new Map();
101
+ }
102
+ if (!recentErrors) {
103
+ recentErrors = new Map();
104
+ }
105
+ return recentErrors;
106
+ }
107
+
108
+ /**
109
+ * Check if error should be shown (not a duplicate)
110
+ * Returns true if error should be shown, false if it's a duplicate
111
+ */
112
+ function shouldShowError(detail: ErrorDetail): boolean {
113
+ const map = getRecentErrorsMap();
114
+ const key = getErrorDedupeKey(detail);
115
+ const now = Date.now();
116
+ const lastSeen = map.get(key);
117
+
118
+ // Clean up old entries when map gets too large
119
+ if (map.size > DEDUPE_MAX_ENTRIES) {
120
+ for (const [k, timestamp] of map.entries()) {
121
+ if (now - timestamp > DEDUPE_WINDOW_MS) {
122
+ map.delete(k);
123
+ }
124
+ }
125
+ // If still too large after cleanup, clear oldest half
126
+ if (map.size > DEDUPE_MAX_ENTRIES) {
127
+ const entries = Array.from(map.entries())
128
+ .sort((a, b) => a[1] - b[1])
129
+ .slice(0, Math.floor(map.size / 2));
130
+ for (const [k] of entries) {
131
+ map.delete(k);
132
+ }
133
+ }
134
+ }
135
+
136
+ // Check if we've seen this error recently
137
+ if (lastSeen && now - lastSeen < DEDUPE_WINDOW_MS) {
138
+ return false; // Duplicate, don't show
139
+ }
140
+
141
+ // Mark as seen
142
+ map.set(key, now);
143
+ return true;
144
+ }
145
+
68
146
  export interface ErrorTrackingProviderProps {
69
147
  children: ReactNode;
70
148
  validation?: Partial<ValidationErrorConfig>;
@@ -136,6 +214,9 @@ export function ErrorTrackingProvider({
136
214
  */
137
215
  const handleError = useCallback(
138
216
  (detail: ErrorDetail, config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | CentrifugoErrorConfig>) => {
217
+ // Check for duplicate errors (within deduplication window)
218
+ const isUnique = shouldShowError(detail);
219
+
139
220
  // Create stored error with ID
140
221
  const storedError: StoredError = {
141
222
  ...detail,
@@ -151,8 +232,8 @@ export function ErrorTrackingProvider({
151
232
  // Call custom error handler
152
233
  const shouldShowToast = onError?.(detail) !== false;
153
234
 
154
- // Show toast notification using Sonner
155
- if (config.showToast && shouldShowToast) {
235
+ // Show toast notification using Sonner (only for unique errors)
236
+ if (config.showToast && shouldShowToast && isUnique) {
156
237
  const toastOptions = createErrorToast(detail, config);
157
238
  toast.error(toastOptions.title, {
158
239
  description: toastOptions.description,
@@ -47,7 +47,6 @@ import type {
47
47
  SWRConfigOptions,
48
48
  McpChatConfig,
49
49
  PwaInstallConfig,
50
- PushNotificationsConfig,
51
50
  } from '../types';
52
51
  import type { AuthConfig } from '@djangocfg/api/auth';
53
52
 
@@ -147,9 +146,6 @@ export interface AppLayoutProps {
147
146
  /** PWA Install configuration */
148
147
  pwaInstall?: PwaInstallConfig;
149
148
 
150
- /** Push Notifications configuration */
151
- pushNotifications?: PushNotificationsConfig;
152
-
153
149
  /** i18n configuration for locale switching (applies to all layouts) */
154
150
  i18n?: I18nLayoutConfig;
155
151
  }
@@ -272,7 +268,6 @@ export function AppLayout(props: AppLayoutProps) {
272
268
  swr,
273
269
  mcpChat,
274
270
  pwaInstall,
275
- pushNotifications,
276
271
  } = props;
277
272
 
278
273
  return (
@@ -286,7 +281,6 @@ export function AppLayout(props: AppLayoutProps) {
286
281
  swr={swr}
287
282
  mcpChat={mcpChat}
288
283
  pwaInstall={pwaInstall}
289
- pushNotifications={pushNotifications}
290
284
  >
291
285
  <AppLayoutContent {...props} />
292
286
  </BaseApp>
@@ -10,7 +10,6 @@
10
10
  * - AnalyticsProvider (Google Analytics, optional)
11
11
  * - CentrifugoProvider (WebSocket real-time, optional)
12
12
  * - PwaProvider (PWA installation, optional)
13
- * - DjangoPushProvider (Django web push integration, optional)
14
13
  * - ErrorTrackingProvider (error handling)
15
14
  * - ErrorBoundary (React error boundary, enabled by default)
16
15
  * - MCP Chat Widget (optional, lazy loaded)
@@ -19,17 +18,12 @@
19
18
  * ```tsx
20
19
  * import { BaseApp } from '@djangocfg/layouts';
21
20
  *
22
- * // With PWA Install and Push Notifications
21
+ * // With PWA Install
23
22
  * <BaseApp
24
23
  * theme={{ defaultTheme: 'system' }}
25
24
  * auth={{ loginPath: '/auth/login' }}
26
25
  * analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
27
26
  * pwaInstall={{ enabled: true, logo: '/logo192.png' }}
28
- * pushNotifications={{
29
- * enabled: true,
30
- * vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY,
31
- * requirePWA: true
32
- * }}
33
27
  * mcpChat={{ enabled: true, autoDetectEnvironment: true }}
34
28
  * >
35
29
  * {children}
@@ -46,7 +40,6 @@
46
40
 
47
41
  import dynamic from 'next/dynamic';
48
42
  import NextTopLoader from 'nextjs-toploader';
49
- import { ReactNode } from 'react';
50
43
  import { SWRConfig } from 'swr';
51
44
 
52
45
  import { getCentrifugoAuthTokenRetrieve } from '@djangocfg/api';
@@ -58,7 +51,6 @@ import { ErrorBoundary } from '../../components/errors/ErrorBoundary';
58
51
  import { ErrorTrackingProvider } from '../../components/errors/ErrorsTracker';
59
52
  import { AnalyticsProvider } from '../../snippets/Analytics';
60
53
  import { AuthDialog } from '../../snippets/AuthDialog';
61
- import { DjangoPushProvider, PushPrompt } from '../../snippets/PushNotifications';
62
54
  import { A2HSHint, PWAPageResumeManager, PwaProvider } from '../../snippets/PWAInstall';
63
55
 
64
56
  import type { BaseLayoutProps } from '../types/layout.types';
@@ -76,7 +68,7 @@ export type BaseAppProps = BaseLayoutProps;
76
68
  * BaseApp - Core providers wrapper for any React/Next.js app
77
69
  *
78
70
  * Includes: ThemeProvider, TooltipProvider, SWRConfig, AuthProvider, AnalyticsProvider,
79
- * CentrifugoProvider, PwaProvider, PushProvider, ErrorTrackingProvider,
71
+ * CentrifugoProvider, PwaProvider, ErrorTrackingProvider,
80
72
  * ErrorBoundary (optional), MCP Chat (optional)
81
73
  * Also renders global Toaster and PageProgress components
82
74
  */
@@ -91,7 +83,6 @@ export function BaseApp({
91
83
  swr,
92
84
  mcpChat,
93
85
  pwaInstall,
94
- pushNotifications,
95
86
  }: BaseAppProps) {
96
87
  // ErrorBoundary is enabled by default
97
88
  const enableErrorBoundary = errorBoundary?.enabled !== false;
@@ -100,9 +91,6 @@ export function BaseApp({
100
91
  const pwaInstallEnabled = pwaInstall?.enabled !== false;
101
92
  const showInstallHint = pwaInstallEnabled && pwaInstall?.showInstallHint !== false;
102
93
 
103
- // Check if Push Notifications is enabled
104
- const pushEnabled = pushNotifications?.enabled !== false && !!pushNotifications?.vapidPublicKey;
105
-
106
94
  // Centrifugo configuration
107
95
  const centrifugoUrl = centrifugo?.url || process.env.NEXT_PUBLIC_CENTRIFUGO_URL;
108
96
  const centrifugoEnabled = centrifugo?.enabled !== false;
@@ -132,69 +120,53 @@ export function BaseApp({
132
120
  }}
133
121
  >
134
122
  <PwaProvider enabled={pwaInstallEnabled}>
135
- <DjangoPushProvider
136
- vapidPublicKey={pushNotifications?.vapidPublicKey || ''}
137
- autoSubscribe={pushNotifications?.autoSubscribe}
123
+ <ErrorTrackingProvider
124
+ validation={errorTracking?.validation}
125
+ cors={errorTracking?.cors}
126
+ network={errorTracking?.network}
127
+ onError={errorTracking?.onError}
138
128
  >
139
- <ErrorTrackingProvider
140
- validation={errorTracking?.validation}
141
- cors={errorTracking?.cors}
142
- network={errorTracking?.network}
143
- onError={errorTracking?.onError}
144
- >
145
- {children}
146
- <NextTopLoader
147
- color="hsl(var(--primary))"
148
- height={3}
149
- showSpinner={false}
150
- shadow="0 0 10px hsl(var(--primary)), 0 0 5px hsl(var(--primary))"
129
+ {children}
130
+ <NextTopLoader
131
+ color="hsl(var(--primary))"
132
+ height={3}
133
+ showSpinner={false}
134
+ shadow="0 0 10px hsl(var(--primary)), 0 0 5px hsl(var(--primary))"
135
+ />
136
+ <Toaster />
137
+
138
+ {/* PWA Install Hint */}
139
+ {showInstallHint && (
140
+ <A2HSHint
141
+ resetAfterDays={pwaInstall?.resetAfterDays}
142
+ delayMs={pwaInstall?.delayMs}
143
+ logo={pwaInstall?.logo}
144
+ />
145
+ )}
146
+
147
+ {/* PWA Page Resume Manager */}
148
+ {pwaInstallEnabled && pwaInstall?.resumeLastPage && (
149
+ <PWAPageResumeManager enabled={true} />
150
+ )}
151
+
152
+ {/* MCP Chat Widget - lazy loaded */}
153
+ {mcpChat?.enabled && (
154
+ <AIChatWidget
155
+ apiEndpoint={mcpChat.apiEndpoint}
156
+ title={mcpChat.title}
157
+ placeholder={mcpChat.placeholder}
158
+ greeting={mcpChat.greeting}
159
+ position={mcpChat.position}
160
+ variant={mcpChat.variant}
161
+ enableStreaming={mcpChat.enableStreaming}
162
+ autoDetectEnvironment={mcpChat.autoDetectEnvironment}
163
+ className={mcpChat.className}
151
164
  />
152
- <Toaster />
153
-
154
- {/* PWA Install Hint */}
155
- {showInstallHint && (
156
- <A2HSHint
157
- resetAfterDays={pwaInstall?.resetAfterDays}
158
- delayMs={pwaInstall?.delayMs}
159
- logo={pwaInstall?.logo}
160
- />
161
- )}
162
-
163
- {/* PWA Page Resume Manager */}
164
- {pwaInstallEnabled && pwaInstall?.resumeLastPage && (
165
- <PWAPageResumeManager enabled={true} />
166
- )}
167
-
168
- {/* Push Notifications Prompt */}
169
- {pushEnabled && (
170
- <PushPrompt
171
- vapidPublicKey={pushNotifications!.vapidPublicKey}
172
- subscribeEndpoint={pushNotifications?.subscribeEndpoint}
173
- requirePWA={pushNotifications?.requirePWA ?? true}
174
- delayMs={pushNotifications?.delayMs}
175
- resetAfterDays={pushNotifications?.resetAfterDays}
176
- />
177
- )}
178
-
179
- {/* MCP Chat Widget - lazy loaded */}
180
- {mcpChat?.enabled && (
181
- <AIChatWidget
182
- apiEndpoint={mcpChat.apiEndpoint}
183
- title={mcpChat.title}
184
- placeholder={mcpChat.placeholder}
185
- greeting={mcpChat.greeting}
186
- position={mcpChat.position}
187
- variant={mcpChat.variant}
188
- enableStreaming={mcpChat.enableStreaming}
189
- autoDetectEnvironment={mcpChat.autoDetectEnvironment}
190
- className={mcpChat.className}
191
- />
192
- )}
193
-
194
- {/* Auth Dialog - global auth prompt */}
195
- <AuthDialog authPath={auth?.routes?.auth} />
196
- </ErrorTrackingProvider>
197
- </DjangoPushProvider>
165
+ )}
166
+
167
+ {/* Auth Dialog - global auth prompt */}
168
+ <AuthDialog authPath={auth?.routes?.auth} />
169
+ </ErrorTrackingProvider>
198
170
  </PwaProvider>
199
171
  </CentrifugoProvider>
200
172
  </AnalyticsProvider>
@@ -15,7 +15,6 @@ export type { ThemeConfig, SWRConfigOptions, McpChatConfig, CentrifugoConfig } f
15
15
  // External provider configs (re-export for convenience)
16
16
  export type { AnalyticsConfig } from '../../snippets/Analytics/types';
17
17
  export type { PwaInstallConfig } from '../../snippets/PWAInstall/types';
18
- export type { PushNotificationsConfig } from '../../snippets/PushNotifications/types';
19
18
  export type {
20
19
  ErrorBoundaryConfig,
21
20
  ErrorTrackingConfig,
@@ -10,7 +10,6 @@ import type { AuthConfig } from '@djangocfg/api/auth';
10
10
  // Import provider configs from their modules
11
11
  import type { AnalyticsConfig } from '../../snippets/Analytics/types';
12
12
  import type { PwaInstallConfig } from '../../snippets/PWAInstall/types';
13
- import type { PushNotificationsConfig } from '../../snippets/PushNotifications/types';
14
13
  import type { ErrorBoundaryConfig, ErrorTrackingConfig } from '../../components/errors/types';
15
14
 
16
15
  // Import local provider configs
@@ -55,7 +54,4 @@ export interface BaseLayoutProps {
55
54
 
56
55
  /** PWA Install configuration (from snippets/PWAInstall) */
57
56
  pwaInstall?: PwaInstallConfig;
58
-
59
- /** Push Notifications configuration (from snippets/PushNotifications) */
60
- pushNotifications?: PushNotificationsConfig;
61
57
  }
@@ -76,26 +76,3 @@ export type {
76
76
  UseIsPWAOptions,
77
77
  } from './PWAInstall';
78
78
 
79
- // Push Notifications
80
- export {
81
- PushProvider,
82
- usePush,
83
- DjangoPushProvider,
84
- useDjangoPushContext,
85
- PushPrompt,
86
- usePushNotifications,
87
- useDjangoPush,
88
- DEFAULT_VAPID_PUBLIC_KEY,
89
- urlBase64ToUint8Array,
90
- isValidVapidKey,
91
- getVapidKeyInfo,
92
- safeUrlBase64ToUint8Array,
93
- VapidKeyError,
94
- clearAllPushData,
95
- } from './PushNotifications';
96
- export type {
97
- PushNotificationState,
98
- PushNotificationOptions,
99
- PushMessage,
100
- VapidKeyErrorCode,
101
- } from './PushNotifications';