@djangocfg/layouts 2.1.37 → 2.1.38

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
@@ -1,557 +0,0 @@
1
- # Remaining Issues After P0 Refactoring
2
-
3
- **Date**: December 2025
4
- **Status**: Post-P0 Analysis
5
-
6
- ## Summary
7
-
8
- Total remaining issues: **10**
9
-
10
- | Priority | Count | Status |
11
- |----------|-------|--------|
12
- | **P1** (High) | 3 | ⚠️ Should fix before next release |
13
- | **P2** (Medium) | 4 | ⏳ Can fix incrementally |
14
- | **P3** (Low) | 3 | 💡 Nice to have |
15
-
16
- ---
17
-
18
- ## P1: High Priority Issues
19
-
20
- Must be addressed before next production release.
21
-
22
- ### P1-1: Security Vulnerability in config.ts
23
-
24
- **Location**: `config.ts:10-11`
25
-
26
- **Issue**: Frontend package exposes server-side secrets
27
-
28
- ```typescript
29
- // ❌ SECURITY ISSUE
30
- export const VAPID_PRIVATE_KEY = process.env.VAPID_PRIVATE_KEY || '';
31
- export const VAPID_MAILTO = process.env.VAPID_MAILTO || '';
32
- ```
33
-
34
- **Problem**:
35
- - Private VAPID key should NEVER be in frontend code
36
- - Even if accessed via env var, bundlers may include it
37
- - This is a **critical security vulnerability**
38
-
39
- **Impact**: 🔴 Critical
40
- - Attackers can send unauthorized push notifications
41
- - Private key compromise requires regeneration
42
- - All existing subscriptions would need re-subscription
43
-
44
- **Effort**: Low (5 minutes)
45
-
46
- **Fix**: Remove these lines entirely from frontend package
47
-
48
- ```typescript
49
- // ✅ FIXED - Only expose public key
50
- export const DEFAULT_VAPID_PUBLIC_KEY = process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY || '';
51
- // Note: VAPID_PRIVATE_KEY should only exist in backend/API routes
52
- ```
53
-
54
- **See**: [RECOMMENDATIONS.md#fix-security-vulnerability](./RECOMMENDATIONS.md#fix-security-vulnerability)
55
-
56
- ---
57
-
58
- ### P1-2: Inconsistent Error Logging
59
-
60
- **Locations**:
61
- - `components/PushPrompt.tsx:106`
62
- - `components/A2HSHint.tsx:150`
63
- - `context/PushContext.tsx:114`
64
-
65
- **Issue**: 3 files still use `console.error` instead of `pwaLogger.error`
66
-
67
- **Code Examples**:
68
-
69
- ```typescript
70
- // ❌ PushPrompt.tsx:106
71
- console.error('[PushPrompt] Enable failed:', error);
72
-
73
- // ❌ A2HSHint.tsx:150
74
- console.error('[A2HSHint] Install error:', error);
75
-
76
- // ❌ PushContext.tsx:114
77
- console.error('Failed to send push:', error);
78
- ```
79
-
80
- **Problem**:
81
- - Inconsistent with P0 refactoring (logger.ts)
82
- - No debug mode control for these errors
83
- - Breaks the conditional logging pattern
84
-
85
- **Impact**: 🟡 Medium
86
- - Console spam in production (errors only)
87
- - Inconsistent DX
88
- - Harder to debug with mixed logging patterns
89
-
90
- **Effort**: Low (10 minutes)
91
-
92
- **Fix**: Replace all with `pwaLogger.error()`
93
-
94
- ```typescript
95
- // ✅ FIXED
96
- import { pwaLogger } from '../utils/logger';
97
-
98
- pwaLogger.error('[PushPrompt] Enable failed:', error);
99
- ```
100
-
101
- **Note**: `pwaLogger.error()` always logs (even in production), so behavior stays the same but now consistent.
102
-
103
- **See**: [RECOMMENDATIONS.md#fix-inconsistent-logging](./RECOMMENDATIONS.md#fix-inconsistent-logging)
104
-
105
- ---
106
-
107
- ### P1-3: Duplicated localStorage Keys
108
-
109
- **Locations**:
110
- - `components/A2HSHint.tsx:27`
111
- - `components/PushPrompt.tsx:18`
112
-
113
- **Issue**: Similar localStorage key patterns duplicated across files
114
-
115
- **Code**:
116
-
117
- ```typescript
118
- // A2HSHint.tsx:27
119
- const DISMISSED_KEY = 'cmdop-a2hs-dismissed';
120
-
121
- // PushPrompt.tsx:18
122
- const DISMISSED_KEY = 'pwa-push-dismissed';
123
- ```
124
-
125
- **Problem**:
126
- - DRY violation (Don't Repeat Yourself)
127
- - If we want to change key prefix, must update in 2+ places
128
- - Potential key collision if not careful
129
-
130
- **Impact**: 🟡 Medium
131
- - Maintenance overhead
132
- - Risk of inconsistent key naming
133
- - Harder to clear all PWA data
134
-
135
- **Effort**: Low (15 minutes)
136
-
137
- **Fix**: Centralize in `utils/localStorage.ts`
138
-
139
- ```typescript
140
- // utils/localStorage.ts
141
- export const STORAGE_KEYS = {
142
- // ... existing keys ...
143
- A2HS_DISMISSED: 'pwa_a2hs_dismissed_at',
144
- PUSH_DISMISSED: 'pwa_push_dismissed_at',
145
- } as const;
146
- ```
147
-
148
- **See**: [RECOMMENDATIONS.md#centralize-localstorage-keys](./RECOMMENDATIONS.md#centralize-localstorage-keys)
149
-
150
- ---
151
-
152
- ## P2: Medium Priority Issues
153
-
154
- Can be addressed incrementally over next few sprints.
155
-
156
- ### P2-1: Unused EngagementMetrics Code
157
-
158
- **Location**: `utils/localStorage.ts:132-205`
159
-
160
- **Issue**: Fully implemented engagement tracking but never used
161
-
162
- **Code**: ~74 lines of dead code
163
-
164
- ```typescript
165
- // utils/localStorage.ts - All unused
166
- export function getEngagementMetrics(): EngagementMetrics { ... }
167
- export function saveEngagementMetrics(metrics: EngagementMetrics): void { ... }
168
- export function trackAction(): void { ... }
169
- export function trackTimeSpent(ms: number): void { ... }
170
- export function updateVisit(): void { ... }
171
- ```
172
-
173
- **Problem**:
174
- - Dead code increases bundle size
175
- - Confusing for developers (is it used?)
176
- - Maintenance burden
177
-
178
- **Impact**: 🟡 Medium
179
- - ~2-3 KB bundle size (after minification)
180
- - Developer confusion
181
- - Potential bugs if someone starts using it without testing
182
-
183
- **Effort**: Low (choose option below)
184
-
185
- **Options**:
186
-
187
- **Option A**: Remove it entirely
188
- ```typescript
189
- // Remove lines 132-205 from localStorage.ts
190
- // Remove EngagementMetrics from types/components.ts
191
- ```
192
-
193
- **Option B**: Actually use it
194
- ```typescript
195
- // Add to InstallContext.tsx
196
- const { actions, timeSpent } = useEngagement();
197
-
198
- // Show install prompt based on engagement
199
- if (actions > 10 && timeSpent > 60000) {
200
- // User is engaged, show install prompt
201
- }
202
- ```
203
-
204
- **Recommendation**: Remove unless there's a plan to use it within 2 sprints
205
-
206
- **See**: [RECOMMENDATIONS.md#handle-unused-engagement-metrics](./RECOMMENDATIONS.md#handle-unused-engagement-metrics)
207
-
208
- ---
209
-
210
- ### P2-2: Context Composition Complexity
211
-
212
- **Location**: `context/InstallContext.tsx:81-84`
213
-
214
- **Issue**: Conditional context wrapping makes composition hard to follow
215
-
216
- **Code**:
217
-
218
- ```typescript
219
- // Wrap with PushProvider if configured
220
- if (config.pushNotifications) {
221
- content = <PushProvider {...config.pushNotifications}>{content}</PushProvider>;
222
- }
223
- ```
224
-
225
- **Problem**:
226
- - Magic wrapping based on config
227
- - Hard to understand composition tree
228
- - Difficult to test in isolation
229
-
230
- **Impact**: 🟡 Medium
231
- - Developer confusion
232
- - Harder to test
233
- - Implicit dependencies
234
-
235
- **Effort**: Medium (30 minutes)
236
-
237
- **Fix**: Make composition explicit
238
-
239
- ```typescript
240
- // ✅ Explicit composition (recommended)
241
- export function PwaProvider({ children, ...config }: PwaConfig & { children: React.ReactNode }) {
242
- if (config.enabled === false) return <>{children}</>;
243
-
244
- // Build context tree explicitly
245
- return (
246
- <InstallProvider {...config}>
247
- <ConditionalPushProvider enabled={!!config.pushNotifications} {...config.pushNotifications}>
248
- {children}
249
- </ConditionalPushProvider>
250
- </InstallProvider>
251
- );
252
- }
253
- ```
254
-
255
- **See**: [RECOMMENDATIONS.md#simplify-context-composition](./RECOMMENDATIONS.md#simplify-context-composition)
256
-
257
- ---
258
-
259
- ### P2-3: No Server-Side Push Subscription Persistence
260
-
261
- **Location**: `hooks/usePushNotifications.ts:117-123`
262
-
263
- **Issue**: Push subscriptions only saved client-side
264
-
265
- **Code**:
266
-
267
- ```typescript
268
- // Send subscription to server
269
- if (options.subscribeEndpoint) {
270
- await fetch(options.subscribeEndpoint, {
271
- method: 'POST',
272
- headers: { 'Content-Type': 'application/json' },
273
- body: JSON.stringify(subscription),
274
- });
275
- }
276
- ```
277
-
278
- **Problem**:
279
- - No error handling for server failure
280
- - No confirmation that server saved it
281
- - No way to sync subscriptions across devices
282
- - No unsubscribe endpoint
283
-
284
- **Impact**: 🟡 Medium
285
- - Silent failures possible
286
- - Can't manage subscriptions on backend
287
- - Can't send targeted push notifications
288
-
289
- **Effort**: Medium (1-2 hours)
290
-
291
- **Fix**: Add proper server-side persistence
292
-
293
- ```typescript
294
- // Backend API route needed
295
- POST /api/push/subscribe
296
- DELETE /api/push/unsubscribe
297
- GET /api/push/subscription
298
-
299
- // Frontend changes
300
- const response = await fetch(options.subscribeEndpoint, ...);
301
- if (!response.ok) {
302
- throw new Error('Server failed to save subscription');
303
- }
304
- const { success, subscriptionId } = await response.json();
305
- ```
306
-
307
- **See**: [RECOMMENDATIONS.md#add-server-side-persistence](./RECOMMENDATIONS.md#add-server-side-persistence)
308
-
309
- ---
310
-
311
- ### P2-4: Limited Error Recovery for Push Subscriptions
312
-
313
- **Location**: `hooks/usePushNotifications.ts:133-146`
314
-
315
- **Issue**: Push subscription errors have no recovery mechanism
316
-
317
- **Code**:
318
-
319
- ```typescript
320
- } catch (error: any) {
321
- pwaLogger.error('[usePushNotifications] Subscribe failed:', error);
322
-
323
- // Diagnostic logging but no recovery
324
- if (error.name === 'AbortError' || error.message?.includes('push service error')) {
325
- pwaLogger.error('[usePushNotifications] Push service blocked. Possible causes:');
326
- // ... more error logs
327
- }
328
-
329
- return false; // Just fail, no retry
330
- }
331
- ```
332
-
333
- **Problem**:
334
- - No retry logic for transient failures
335
- - No user-facing error messages
336
- - No recovery suggestions
337
-
338
- **Impact**: 🟡 Medium
339
- - Poor UX for temporary network issues
340
- - Users confused why it failed
341
- - No way to retry without page refresh
342
-
343
- **Effort**: Medium (1 hour)
344
-
345
- **Fix**: Add retry logic + user feedback
346
-
347
- ```typescript
348
- // Add retry with exponential backoff
349
- const subscribe = async (retries = 3): Promise<boolean> => {
350
- for (let i = 0; i < retries; i++) {
351
- try {
352
- // ... subscription logic
353
- return true;
354
- } catch (error) {
355
- if (i === retries - 1) {
356
- // Last attempt failed - show user error
357
- onError?.(getErrorMessage(error));
358
- return false;
359
- }
360
- // Wait before retry (exponential backoff)
361
- await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
362
- }
363
- }
364
- return false;
365
- };
366
- ```
367
-
368
- **See**: [RECOMMENDATIONS.md#improve-error-recovery](./RECOMMENDATIONS.md#improve-error-recovery)
369
-
370
- ---
371
-
372
- ## P3: Nice to Have
373
-
374
- Low priority improvements that would enhance quality but aren't blocking.
375
-
376
- ### P3-1: No Tests
377
-
378
- **Location**: Entire `src/snippets/PWA/` directory
379
-
380
- **Issue**: Zero unit tests, zero integration tests
381
-
382
- **Problem**:
383
- - Can't verify correctness
384
- - Regressions possible
385
- - Refactoring risky
386
-
387
- **Impact**: 🟢 Low (for now)
388
- - No issues currently
389
- - But future changes risky
390
-
391
- **Effort**: High (4-8 hours for full coverage)
392
-
393
- **Fix**: Add tests incrementally
394
-
395
- ```typescript
396
- // utils/platform.test.ts
397
- describe('isStandalone', () => {
398
- it('returns false when window is undefined', () => {
399
- expect(isStandalone()).toBe(false);
400
- });
401
-
402
- it('checks matchMedia for standalone display mode', () => {
403
- // ... test implementation
404
- });
405
- });
406
- ```
407
-
408
- **Priority**: Start with utils (pure functions), then hooks, then components
409
-
410
- **See**: [RECOMMENDATIONS.md#add-testing](./RECOMMENDATIONS.md#add-testing)
411
-
412
- ---
413
-
414
- ### P3-2: Limited Accessibility
415
-
416
- **Location**:
417
- - `components/A2HSHint.tsx`
418
- - `components/PushPrompt.tsx`
419
- - `components/IOSGuideModal.tsx`
420
-
421
- **Issue**: Missing some ARIA labels and keyboard navigation
422
-
423
- **Examples**:
424
- - Close buttons have aria-label ✅
425
- - But modals don't trap focus ❌
426
- - No keyboard shortcut to dismiss ❌
427
- - No screen reader announcements ❌
428
-
429
- **Impact**: 🟢 Low
430
- - Works with screen readers (basic)
431
- - But not optimal
432
-
433
- **Effort**: Medium (2-3 hours)
434
-
435
- **Fix**: Enhance accessibility
436
-
437
- ```typescript
438
- // Add focus trap
439
- import { useFocusTrap } from '@djangocfg/ui-nextjs/hooks';
440
-
441
- export function IOSGuideModal({ open, onDismiss }: Props) {
442
- const focusTrapRef = useFocusTrap<HTMLDivElement>(open);
443
-
444
- return (
445
- <Dialog open={open} onOpenChange={onDismiss}>
446
- <DialogContent ref={focusTrapRef} role="alertdialog" aria-describedby="ios-guide-description">
447
- {/* Add keyboard listener */}
448
- <div onKeyDown={(e) => e.key === 'Escape' && onDismiss()}>
449
- {/* content */}
450
- </div>
451
- </DialogContent>
452
- </Dialog>
453
- );
454
- }
455
- ```
456
-
457
- **See**: [RECOMMENDATIONS.md#improve-accessibility](./RECOMMENDATIONS.md#improve-accessibility)
458
-
459
- ---
460
-
461
- ### P3-3: TypeScript Not in Strict Mode
462
-
463
- **Location**: `tsconfig.json` (parent)
464
-
465
- **Issue**: TypeScript strict mode not enabled
466
-
467
- **Examples**:
468
- ```typescript
469
- // These would be caught in strict mode
470
- const value: any = something; // any should be avoided
471
- element?.addEventListener('click', handler); // handler type unclear
472
- ```
473
-
474
- **Impact**: 🟢 Low
475
- - Current code works fine
476
- - But future changes could introduce bugs
477
-
478
- **Effort**: Medium (2-4 hours to fix all errors)
479
-
480
- **Fix**: Enable strict mode incrementally
481
-
482
- ```json
483
- // tsconfig.json
484
- {
485
- "compilerOptions": {
486
- "strict": true,
487
- "noImplicitAny": true,
488
- "strictNullChecks": true,
489
- "strictFunctionTypes": true,
490
- "strictPropertyInitialization": true
491
- }
492
- }
493
- ```
494
-
495
- Then fix all resulting type errors.
496
-
497
- **See**: [RECOMMENDATIONS.md#enable-typescript-strict-mode](./RECOMMENDATIONS.md#enable-typescript-strict-mode)
498
-
499
- ---
500
-
501
- ## Issue Summary by File
502
-
503
- | File | P1 Issues | P2 Issues | P3 Issues | Total |
504
- |------|-----------|-----------|-----------|-------|
505
- | config.ts | 1 | 0 | 0 | 1 |
506
- | PushPrompt.tsx | 1 | 0 | 1 | 2 |
507
- | A2HSHint.tsx | 1 | 0 | 1 | 2 |
508
- | PushContext.tsx | 1 | 1 | 0 | 2 |
509
- | InstallContext.tsx | 0 | 1 | 0 | 1 |
510
- | usePushNotifications.ts | 0 | 1 | 0 | 1 |
511
- | localStorage.ts | 1 | 1 | 0 | 2 |
512
- | IOSGuide components | 0 | 0 | 1 | 1 |
513
- | Testing/TS Config | 0 | 0 | 2 | 2 |
514
-
515
- ---
516
-
517
- ## Recommended Fix Order
518
-
519
- ### Sprint 1 (P1 Issues - ~30 minutes total)
520
-
521
- 1. Fix security vulnerability (config.ts) - **5 min**
522
- 2. Replace console.error with pwaLogger (3 files) - **10 min**
523
- 3. Centralize localStorage keys - **15 min**
524
-
525
- **Impact**: Eliminates all P1 issues
526
-
527
- ### Sprint 2 (P2 High-Value Issues - ~2 hours)
528
-
529
- 4. Remove unused EngagementMetrics OR implement usage - **30 min**
530
- 5. Add server-side push subscription persistence - **1.5 hours**
531
-
532
- **Impact**: Reduces bundle size, enables proper push management
533
-
534
- ### Sprint 3 (P2 Polish - ~1.5 hours)
535
-
536
- 6. Simplify context composition - **30 min**
537
- 7. Improve error recovery for push subscriptions - **1 hour**
538
-
539
- **Impact**: Better DX and UX
540
-
541
- ### Sprint 4+ (P3 - Ongoing)
542
-
543
- 8. Add tests (incrementally)
544
- 9. Improve accessibility
545
- 10. Enable TypeScript strict mode
546
-
547
- **Impact**: Long-term quality improvements
548
-
549
- ---
550
-
551
- ## Conclusion
552
-
553
- **Critical**: Only 3 P1 issues, all fixable in ~30 minutes
554
- **Important**: 4 P2 issues, addressable over 2-3 sprints
555
- **Optional**: 3 P3 issues for long-term quality
556
-
557
- **Next Steps**: See [RECOMMENDATIONS.md](./RECOMMENDATIONS.md) for detailed fixes with code examples.