@djangocfg/layouts 2.1.36 → 2.1.37

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 (40) hide show
  1. package/package.json +5 -5
  2. package/src/layouts/AppLayout/BaseApp.tsx +31 -25
  3. package/src/layouts/shared/types.ts +36 -0
  4. package/src/snippets/McpChat/context/ChatContext.tsx +9 -0
  5. package/src/snippets/PWA/@docs/research.md +576 -0
  6. package/src/snippets/PWA/@refactoring/ARCHITECTURE_ANALYSIS.md +1179 -0
  7. package/src/snippets/PWA/@refactoring/EXECUTIVE_SUMMARY.md +271 -0
  8. package/src/snippets/PWA/@refactoring/README.md +204 -0
  9. package/src/snippets/PWA/@refactoring/REFACTORING_PROPOSALS.md +1109 -0
  10. package/src/snippets/PWA/@refactoring2/COMPARISON-WITH-NEXTJS.md +718 -0
  11. package/src/snippets/PWA/@refactoring2/P1-FIXES-COMPLETED.md +188 -0
  12. package/src/snippets/PWA/@refactoring2/POST-P0-ANALYSIS.md +362 -0
  13. package/src/snippets/PWA/@refactoring2/README.md +85 -0
  14. package/src/snippets/PWA/@refactoring2/RECOMMENDATIONS.md +1321 -0
  15. package/src/snippets/PWA/@refactoring2/REMAINING-ISSUES.md +557 -0
  16. package/src/snippets/PWA/README.md +387 -0
  17. package/src/snippets/PWA/components/A2HSHint.tsx +226 -0
  18. package/src/snippets/PWA/components/IOSGuide.tsx +29 -0
  19. package/src/snippets/PWA/components/IOSGuideDrawer.tsx +101 -0
  20. package/src/snippets/PWA/components/IOSGuideModal.tsx +101 -0
  21. package/src/snippets/PWA/components/PushPrompt.tsx +165 -0
  22. package/src/snippets/PWA/config.ts +20 -0
  23. package/src/snippets/PWA/context/DjangoPushContext.tsx +105 -0
  24. package/src/snippets/PWA/context/InstallContext.tsx +118 -0
  25. package/src/snippets/PWA/context/PushContext.tsx +156 -0
  26. package/src/snippets/PWA/hooks/useDjangoPush.ts +277 -0
  27. package/src/snippets/PWA/hooks/useInstallPrompt.ts +164 -0
  28. package/src/snippets/PWA/hooks/useIsPWA.ts +115 -0
  29. package/src/snippets/PWA/hooks/usePushNotifications.ts +205 -0
  30. package/src/snippets/PWA/index.ts +95 -0
  31. package/src/snippets/PWA/types/components.ts +101 -0
  32. package/src/snippets/PWA/types/index.ts +26 -0
  33. package/src/snippets/PWA/types/install.ts +38 -0
  34. package/src/snippets/PWA/types/platform.ts +29 -0
  35. package/src/snippets/PWA/types/push.ts +21 -0
  36. package/src/snippets/PWA/utils/localStorage.ts +203 -0
  37. package/src/snippets/PWA/utils/logger.ts +149 -0
  38. package/src/snippets/PWA/utils/platform.ts +151 -0
  39. package/src/snippets/PWA/utils/vapid.ts +226 -0
  40. package/src/snippets/index.ts +30 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.36",
3
+ "version": "2.1.37",
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.36",
96
- "@djangocfg/centrifugo": "^2.1.36",
97
- "@djangocfg/ui-nextjs": "^2.1.36",
95
+ "@djangocfg/api": "^2.1.37",
96
+ "@djangocfg/centrifugo": "^2.1.37",
97
+ "@djangocfg/ui-nextjs": "^2.1.37",
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.36",
117
+ "@djangocfg/typescript-config": "^2.1.37",
118
118
  "@types/node": "^24.7.2",
119
119
  "@types/react": "^19.1.0",
120
120
  "@types/react-dom": "^19.1.0",
@@ -6,6 +6,7 @@
6
6
  * - TooltipProvider (tooltip positioning)
7
7
  * - SWRConfig (data fetching)
8
8
  * - AuthProvider (authentication context)
9
+ * - PwaProvider (PWA context, optional)
9
10
  * - ErrorTrackingProvider (error handling)
10
11
  * - ErrorBoundary (React error boundary, enabled by default)
11
12
  * - MCP Chat Widget (optional, lazy loaded)
@@ -14,10 +15,11 @@
14
15
  * ```tsx
15
16
  * import { BaseApp } from '@djangocfg/layouts';
16
17
  *
17
- * // With MCP Chat enabled
18
+ * // With PWA and MCP Chat enabled
18
19
  * <BaseApp
19
20
  * theme={{ defaultTheme: 'system' }}
20
21
  * auth={{ loginPath: '/auth/login' }}
22
+ * pwa={{ enabled: true }}
21
23
  * mcpChat={{ enabled: true, autoDetectEnvironment: true }}
22
24
  * >
23
25
  * {children}
@@ -40,6 +42,7 @@ import { AuthProvider } from '@djangocfg/api/auth';
40
42
  import { ErrorTrackingProvider } from '../../components/errors/ErrorsTracker';
41
43
  import { ErrorBoundary } from '../../components/errors/ErrorBoundary';
42
44
  import { PageProgress } from '../../components/core/PageProgress';
45
+ import { PwaProvider } from '../../snippets/PWA';
43
46
  import type { BaseLayoutProps } from '../shared/types';
44
47
 
45
48
  // Lazy load MCP Chat Widget with dynamic import
@@ -65,6 +68,7 @@ export function BaseApp({
65
68
  errorBoundary,
66
69
  swr,
67
70
  mcpChat,
71
+ pwa,
68
72
  }: BaseAppProps) {
69
73
  // ErrorBoundary is enabled by default
70
74
  const enableErrorBoundary = errorBoundary?.enabled !== false;
@@ -83,31 +87,33 @@ export function BaseApp({
83
87
  }}
84
88
  >
85
89
  <AuthProvider config={auth}>
86
- <ErrorTrackingProvider
87
- validation={errorTracking?.validation}
88
- cors={errorTracking?.cors}
89
- network={errorTracking?.network}
90
- onError={errorTracking?.onError}
91
- >
92
- {children}
93
- <PageProgress />
94
- <Toaster />
90
+ <PwaProvider {...pwa}>
91
+ <ErrorTrackingProvider
92
+ validation={errorTracking?.validation}
93
+ cors={errorTracking?.cors}
94
+ network={errorTracking?.network}
95
+ onError={errorTracking?.onError}
96
+ >
97
+ {children}
98
+ <PageProgress />
99
+ <Toaster />
95
100
 
96
- {/* MCP Chat Widget - lazy loaded */}
97
- {mcpChat?.enabled && (
98
- <AIChatWidget
99
- apiEndpoint={mcpChat.apiEndpoint}
100
- title={mcpChat.title}
101
- placeholder={mcpChat.placeholder}
102
- greeting={mcpChat.greeting}
103
- position={mcpChat.position}
104
- variant={mcpChat.variant}
105
- enableStreaming={mcpChat.enableStreaming}
106
- autoDetectEnvironment={mcpChat.autoDetectEnvironment}
107
- className={mcpChat.className}
108
- />
109
- )}
110
- </ErrorTrackingProvider>
101
+ {/* MCP Chat Widget - lazy loaded */}
102
+ {mcpChat?.enabled && (
103
+ <AIChatWidget
104
+ apiEndpoint={mcpChat.apiEndpoint}
105
+ title={mcpChat.title}
106
+ placeholder={mcpChat.placeholder}
107
+ greeting={mcpChat.greeting}
108
+ position={mcpChat.position}
109
+ variant={mcpChat.variant}
110
+ enableStreaming={mcpChat.enableStreaming}
111
+ autoDetectEnvironment={mcpChat.autoDetectEnvironment}
112
+ className={mcpChat.className}
113
+ />
114
+ )}
115
+ </ErrorTrackingProvider>
116
+ </PwaProvider>
111
117
  </AuthProvider>
112
118
  </SWRConfig>
113
119
  </TooltipProvider>
@@ -88,6 +88,39 @@ export interface McpChatConfig {
88
88
  className?: string;
89
89
  }
90
90
 
91
+ // ============================================================================
92
+ // PWA Configuration
93
+ // ============================================================================
94
+
95
+ export interface PwaConfig {
96
+ /** Enable PWA provider and features */
97
+ enabled?: boolean;
98
+
99
+ /** Show A2HS hint (enabled by default when pwa.enabled is true) */
100
+ showInstallHint?: boolean;
101
+
102
+ /** Number of days before re-showing dismissed hint */
103
+ resetAfterDays?: number | null;
104
+
105
+ /** Delay before showing hint (ms) */
106
+ delayMs?: number;
107
+
108
+ /** App logo URL to display in hint */
109
+ logo?: string;
110
+
111
+ /** Push notifications configuration */
112
+ pushNotifications?: {
113
+ /** VAPID public key for push notifications */
114
+ vapidPublicKey: string;
115
+ /** API endpoint for subscription */
116
+ subscribeEndpoint?: string;
117
+ /** Delay before showing push prompt after PWA install (ms) */
118
+ delayMs?: number;
119
+ /** Number of days before re-showing dismissed push prompt */
120
+ resetAfterDays?: number;
121
+ };
122
+ }
123
+
91
124
  // ============================================================================
92
125
  // Base Layout Props
93
126
  // ============================================================================
@@ -112,6 +145,9 @@ export interface BaseLayoutProps {
112
145
 
113
146
  /** MCP chat configuration */
114
147
  mcpChat?: McpChatConfig;
148
+
149
+ /** PWA configuration */
150
+ pwa?: PwaConfig;
115
151
  }
116
152
 
117
153
  // ============================================================================
@@ -98,6 +98,7 @@ export function ChatProvider({
98
98
  const [error, setError] = useState<Error | null>(null);
99
99
  const [isMinimized, setIsMinimized] = useState(false);
100
100
  const isHydratedRef = useRef(false);
101
+ const mobileResetRef = useRef(false);
101
102
 
102
103
  // Display mode with localStorage persistence
103
104
  const [storedMode, setStoredMode] = useLocalStorage<ChatDisplayMode>(storageKeys.mode, 'closed');
@@ -110,6 +111,14 @@ export function ChatProvider({
110
111
 
111
112
  const isMobile = useIsMobile();
112
113
 
114
+ // On mobile, always start closed on page load (don't restore state)
115
+ useEffect(() => {
116
+ if (isMobile && !mobileResetRef.current && storedMode !== 'closed') {
117
+ mobileResetRef.current = true;
118
+ setStoredMode('closed');
119
+ }
120
+ }, [isMobile, storedMode, setStoredMode]);
121
+
113
122
  // Generate user ID if not exists
114
123
  useEffect(() => {
115
124
  if (!userId) {