feat: Add persistent global notification settings for admin panel
Some checks failed
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
Backend CI/CD Pipeline / Lint and Test Backend (push) Has been cancelled
Backend CI/CD Pipeline / E2E Tests Backend (push) Has been cancelled
Backend CI/CD Pipeline / Build Backend Application (push) Has been cancelled
Backend CI/CD Pipeline / Performance Testing (push) Has been cancelled
Some checks failed
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
Backend CI/CD Pipeline / Lint and Test Backend (push) Has been cancelled
Backend CI/CD Pipeline / E2E Tests Backend (push) Has been cancelled
Backend CI/CD Pipeline / Build Backend Application (push) Has been cancelled
Backend CI/CD Pipeline / Performance Testing (push) Has been cancelled
Implement database-backed notification settings that persist across restarts: Backend changes: - Updated DashboardService to read/write notification settings from database - Added 6 notification settings keys to dbSettingsMap: * enable_email_notifications (boolean) * enable_push_notifications (boolean) * admin_notifications (boolean) * error_alerts (boolean) * new_user_alerts (boolean) * system_health_alerts (boolean) - Settings are now retrieved from database with fallback defaults Database: - Seeded 6 default notification settings in settings table - All notification toggles default to 'true' - Settings persist across server restarts Frontend: - Admin settings page at /settings already configured - Notifications tab contains all 6 toggle switches - Settings are loaded from GET /api/v1/admin/dashboard/settings - Settings are saved via POST /api/v1/admin/dashboard/settings API Endpoints (already existed, now enhanced): - GET /api/v1/admin/dashboard/settings - Returns all settings including notifications - POST /api/v1/admin/dashboard/settings - Persists notification settings to database Files modified: - maternal-app-backend/src/modules/admin/dashboard/dashboard.service.ts Benefits: ✅ Global notification settings are now persistent ✅ Admin can control email/push notifications globally ✅ Admin can configure alert preferences ✅ Settings survive server restarts and deployments 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
480
PUSH_NOTIFICATIONS_PERSISTENCE_SUMMARY.md
Normal file
480
PUSH_NOTIFICATIONS_PERSISTENCE_SUMMARY.md
Normal file
@@ -0,0 +1,480 @@
|
||||
# Push Notifications Persistence Implementation
|
||||
|
||||
**Status**: ✅ **COMPLETED**
|
||||
**Date**: October 8, 2025
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document summarizes the implementation of **persistent notification preferences** for the ParentFlow push notifications system. Users' notification settings are now stored in the database and respected across sessions and devices.
|
||||
|
||||
---
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
### 1. Enhanced User Entity ✅
|
||||
|
||||
**File**: `src/database/entities/user.entity.ts`
|
||||
|
||||
Updated the `preferences` JSONB column to include detailed notification settings:
|
||||
|
||||
```typescript
|
||||
preferences?: {
|
||||
notifications?: {
|
||||
pushEnabled?: boolean; // Master toggle for push notifications
|
||||
emailEnabled?: boolean; // Email notifications toggle
|
||||
feedingReminders?: boolean; // Feeding reminder notifications
|
||||
sleepReminders?: boolean; // Sleep reminder notifications
|
||||
diaperReminders?: boolean; // Diaper change notifications
|
||||
medicationReminders?: boolean; // Medication reminders
|
||||
milestoneAlerts?: boolean; // Milestone achievement alerts
|
||||
patternAnomalies?: boolean; // Pattern anomaly warnings
|
||||
};
|
||||
emailUpdates?: boolean;
|
||||
darkMode?: boolean;
|
||||
measurementUnit?: 'metric' | 'imperial';
|
||||
timeFormat?: '12h' | '24h';
|
||||
}
|
||||
```
|
||||
|
||||
**Database**: Uses existing `users.preferences` JSONB column - no migration needed!
|
||||
|
||||
---
|
||||
|
||||
### 2. User Preferences Service ✅
|
||||
|
||||
**File**: `src/modules/users/user-preferences.service.ts`
|
||||
|
||||
Complete service for managing user preferences with the following methods:
|
||||
|
||||
#### Core Methods
|
||||
- `getUserPreferences(userId)` - Get all preferences with defaults
|
||||
- `updateUserPreferences(userId, preferences)` - Update any preference
|
||||
- `updateNotificationPreferences(userId, notificationPreferences)` - Update notification settings only
|
||||
|
||||
#### Push Notification Helpers
|
||||
- `enablePushNotifications(userId)` - Enable push notifications
|
||||
- `disablePushNotifications(userId)` - Disable push notifications
|
||||
- `isPushNotificationsEnabled(userId)` - Check if push is enabled
|
||||
- `isNotificationTypeEnabled(userId, type)` - Check specific notification type
|
||||
|
||||
#### Utility Methods
|
||||
- `getNotificationPreferencesSummary(userId)` - Get summary of enabled/disabled types
|
||||
- `resetToDefaults(userId)` - Reset all preferences to defaults
|
||||
|
||||
**Default Values**: All notification types are **enabled by default** to ensure users receive important reminders.
|
||||
|
||||
---
|
||||
|
||||
### 3. Preferences Controller ✅
|
||||
|
||||
**File**: `src/modules/users/user-preferences.controller.ts`
|
||||
|
||||
REST API endpoints for managing preferences:
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/api/v1/preferences` | Get all user preferences |
|
||||
| PUT | `/api/v1/preferences` | Update all preferences |
|
||||
| PUT | `/api/v1/preferences/notifications` | Update notification preferences only |
|
||||
| POST | `/api/v1/preferences/notifications/push/enable` | Enable push notifications |
|
||||
| POST | `/api/v1/preferences/notifications/push/disable` | Disable push notifications |
|
||||
| GET | `/api/v1/preferences/notifications/summary` | Get notification settings summary |
|
||||
| POST | `/api/v1/preferences/reset` | Reset all preferences to defaults |
|
||||
|
||||
**Authentication**: All endpoints require JWT authentication
|
||||
|
||||
---
|
||||
|
||||
### 4. Users Module ✅
|
||||
|
||||
**File**: `src/modules/users/users.module.ts`
|
||||
|
||||
New NestJS module that:
|
||||
- Imports User entity from TypeORM
|
||||
- Provides UserPreferencesService and UserPreferencesController
|
||||
- Exports UserPreferencesService for use by other modules
|
||||
- Integrated into main AppModule
|
||||
|
||||
---
|
||||
|
||||
### 5. Updated Push Service ✅
|
||||
|
||||
**File**: `src/modules/push/push.service.ts`
|
||||
|
||||
Enhanced `sendToUser()` method to:
|
||||
1. Check if user has push notifications enabled via `UserPreferencesService`
|
||||
2. Skip sending if push is disabled at user level
|
||||
3. Log when notifications are skipped due to preferences
|
||||
|
||||
```typescript
|
||||
async sendToUser(userId: string, payload: PushNotificationPayload) {
|
||||
// Check if user has push notifications enabled
|
||||
const isPushEnabled = await this.userPreferencesService.isPushNotificationsEnabled(userId);
|
||||
|
||||
if (!isPushEnabled) {
|
||||
this.logger.debug(`Push notifications disabled for user ${userId}, skipping`);
|
||||
return { sent: 0, failed: 0 };
|
||||
}
|
||||
|
||||
// Continue with sending...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. Updated DTOs ✅
|
||||
|
||||
**File**: `src/modules/auth/dto/update-profile.dto.ts`
|
||||
|
||||
Created new DTOs to match the enhanced preference structure:
|
||||
|
||||
**NotificationPreferencesDto**:
|
||||
- Validates all notification preference fields
|
||||
- All fields optional with `@IsBoolean()` validation
|
||||
|
||||
**UserPreferencesDto**:
|
||||
- Updated to use `NotificationPreferencesDto` instead of simple boolean
|
||||
- Maintains backward compatibility
|
||||
|
||||
**UpdateProfileDto**:
|
||||
- Uses updated `UserPreferencesDto`
|
||||
- Allows updating preferences via profile endpoint
|
||||
|
||||
---
|
||||
|
||||
### 7. Frontend Integration ✅
|
||||
|
||||
**File**: `maternal-web/components/PushNotificationToggle.tsx`
|
||||
|
||||
Enhanced component to:
|
||||
1. Save preference to backend when toggling push notifications
|
||||
2. Call new `PUT /api/v1/preferences/notifications` endpoint
|
||||
3. Handle errors gracefully (subscription works even if preference save fails)
|
||||
|
||||
```typescript
|
||||
const savePreference = async (authToken: string, enabled: boolean) => {
|
||||
const response = await fetch(`${apiUrl}/api/v1/preferences/notifications`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${authToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
pushEnabled: enabled,
|
||||
}),
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
### Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 1. User toggles push notifications in UI │
|
||||
└────────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 2. Frontend subscribes/unsubscribes from push │
|
||||
└────────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 3. Frontend saves preference to database │
|
||||
│ PUT /api/v1/preferences/notifications │
|
||||
│ { pushEnabled: true/false } │
|
||||
└────────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 4. UserPreferencesService updates users.preferences │
|
||||
│ (JSONB column) │
|
||||
└────────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 5. Later: NotificationsService creates notification │
|
||||
└────────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 6. PushService checks user preferences before sending │
|
||||
│ if (!isPushEnabled) return; // Skip sending │
|
||||
└────────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 7. Push sent only if user has push enabled │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Examples
|
||||
|
||||
### Get User Preferences
|
||||
|
||||
```bash
|
||||
GET /api/v1/preferences
|
||||
Authorization: Bearer <token>
|
||||
|
||||
Response:
|
||||
{
|
||||
"notifications": {
|
||||
"pushEnabled": true,
|
||||
"emailEnabled": true,
|
||||
"feedingReminders": true,
|
||||
"sleepReminders": true,
|
||||
"diaperReminders": true,
|
||||
"medicationReminders": true,
|
||||
"milestoneAlerts": true,
|
||||
"patternAnomalies": true
|
||||
},
|
||||
"emailUpdates": true,
|
||||
"darkMode": false,
|
||||
"measurementUnit": "metric",
|
||||
"timeFormat": "12h"
|
||||
}
|
||||
```
|
||||
|
||||
### Update Notification Preferences
|
||||
|
||||
```bash
|
||||
PUT /api/v1/preferences/notifications
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"pushEnabled": false,
|
||||
"feedingReminders": false
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"notifications": {
|
||||
"pushEnabled": false,
|
||||
"emailEnabled": true,
|
||||
"feedingReminders": false, // Updated
|
||||
"sleepReminders": true,
|
||||
"diaperReminders": true,
|
||||
"medicationReminders": true,
|
||||
"milestoneAlerts": true,
|
||||
"patternAnomalies": true
|
||||
},
|
||||
// ... other preferences
|
||||
}
|
||||
```
|
||||
|
||||
### Enable Push Notifications
|
||||
|
||||
```bash
|
||||
POST /api/v1/preferences/notifications/push/enable
|
||||
Authorization: Bearer <token>
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
### Get Notification Summary
|
||||
|
||||
```bash
|
||||
GET /api/v1/preferences/notifications/summary
|
||||
Authorization: Bearer <token>
|
||||
|
||||
Response:
|
||||
{
|
||||
"enabled": true,
|
||||
"enabledTypes": [
|
||||
"feedingReminders",
|
||||
"sleepReminders",
|
||||
"diaperReminders",
|
||||
"medicationReminders",
|
||||
"milestoneAlerts",
|
||||
"patternAnomalies"
|
||||
],
|
||||
"disabledTypes": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Storage
|
||||
|
||||
### Schema
|
||||
|
||||
The preferences are stored in the existing `users.preferences` JSONB column:
|
||||
|
||||
```sql
|
||||
-- Example data
|
||||
UPDATE users
|
||||
SET preferences = '{
|
||||
"notifications": {
|
||||
"pushEnabled": true,
|
||||
"emailEnabled": true,
|
||||
"feedingReminders": true,
|
||||
"sleepReminders": true,
|
||||
"diaperReminders": true,
|
||||
"medicationReminders": true,
|
||||
"milestoneAlerts": true,
|
||||
"patternAnomalies": true
|
||||
},
|
||||
"emailUpdates": true,
|
||||
"darkMode": false,
|
||||
"measurementUnit": "metric",
|
||||
"timeFormat": "12h"
|
||||
}'
|
||||
WHERE id = 'usr_123';
|
||||
```
|
||||
|
||||
### Query Examples
|
||||
|
||||
```sql
|
||||
-- Get users with push notifications enabled
|
||||
SELECT id, email, preferences->'notifications'->>'pushEnabled' as push_enabled
|
||||
FROM users
|
||||
WHERE preferences->'notifications'->>'pushEnabled' = 'true';
|
||||
|
||||
-- Get users with feeding reminders disabled
|
||||
SELECT id, email
|
||||
FROM users
|
||||
WHERE preferences->'notifications'->>'feedingReminders' = 'false';
|
||||
|
||||
-- Update a specific preference
|
||||
UPDATE users
|
||||
SET preferences = jsonb_set(
|
||||
preferences,
|
||||
'{notifications,pushEnabled}',
|
||||
'false'
|
||||
)
|
||||
WHERE id = 'usr_123';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Default Behavior
|
||||
|
||||
### New Users
|
||||
- All notification preferences default to **enabled**
|
||||
- Push notifications are **enabled** by default
|
||||
- Users can customize after onboarding
|
||||
|
||||
### Existing Users (Migration)
|
||||
- Existing users without preferences get defaults on first access
|
||||
- No database migration needed - handled by service layer
|
||||
- Backward compatible with old preference format
|
||||
|
||||
---
|
||||
|
||||
## Key Features
|
||||
|
||||
✅ **Persistent Across Sessions** - Settings saved to database, not local storage
|
||||
✅ **Multi-Device Sync** - Same preferences across all user's devices
|
||||
✅ **Granular Control** - Enable/disable specific notification types
|
||||
✅ **API-Driven** - RESTful endpoints for all preference operations
|
||||
✅ **Type-Safe** - Full TypeScript validation with DTOs
|
||||
✅ **Default Values** - Sensible defaults ensure notifications work out-of-box
|
||||
✅ **Audit Trail** - All changes logged via user updates
|
||||
✅ **Privacy-Focused** - User controls all notification types
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Backend Testing
|
||||
|
||||
```bash
|
||||
# Get preferences
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3020/api/v1/preferences
|
||||
|
||||
# Disable push notifications
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3020/api/v1/preferences/notifications/push/disable
|
||||
|
||||
# Update specific preferences
|
||||
curl -X PUT \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"feedingReminders": false, "sleepReminders": false}' \
|
||||
http://localhost:3020/api/v1/preferences/notifications
|
||||
```
|
||||
|
||||
### Frontend Testing
|
||||
|
||||
1. Open web app at `http://maternal.noru1.ro`
|
||||
2. Navigate to Settings page
|
||||
3. Toggle push notifications ON
|
||||
4. Verify preference saved in database
|
||||
5. Toggle push notifications OFF
|
||||
6. Verify preference updated
|
||||
7. Refresh page - verify setting persists
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
- [ ] **Per-Child Preferences** - Different notification settings per child
|
||||
- [ ] **Time-Based Quiet Hours** - Disable notifications during sleep hours
|
||||
- [ ] **Notification Frequency Control** - Limit number of notifications per day
|
||||
- [ ] **Smart Suggestions** - ML-based preference recommendations
|
||||
- [ ] **Bulk Operations** - Enable/disable all notification types at once
|
||||
- [ ] **Advanced UI** - Rich settings page with toggles for each type
|
||||
|
||||
### API Extensions
|
||||
- [ ] `GET /api/v1/preferences/notifications/children/:childId` - Per-child preferences
|
||||
- [ ] `PUT /api/v1/preferences/notifications/quiet-hours` - Set quiet hours
|
||||
- [ ] `POST /api/v1/preferences/notifications/bulk-update` - Bulk enable/disable
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Issue**: Preferences not persisting
|
||||
**Solution**: Check JWT token is valid and user has permissions
|
||||
|
||||
**Issue**: Push still sending when disabled
|
||||
**Solution**: Clear browser service worker cache, re-subscribe
|
||||
|
||||
**Issue**: Preferences showing as `null`
|
||||
**Solution**: Service returns defaults for null values - working as intended
|
||||
|
||||
**Issue**: Cannot update preferences
|
||||
**Solution**: Ensure request body matches `NotificationPreferencesDto` validation
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
✅ **All notification preferences are now persistent in the database**
|
||||
✅ **Users can customize notification types and push settings**
|
||||
✅ **Backend respects user preferences before sending push notifications**
|
||||
✅ **Frontend automatically saves preferences when toggling**
|
||||
✅ **Backend compiled successfully with 0 errors**
|
||||
✅ **RESTful API for all preference operations**
|
||||
|
||||
**Implementation Complete!** The push notification system now fully respects user preferences stored in the database. 🎉
|
||||
|
||||
---
|
||||
|
||||
## Documentation Updates
|
||||
|
||||
The main implementation documentation has been updated:
|
||||
- [PUSH_NOTIFICATIONS_IMPLEMENTATION.md](PUSH_NOTIFICATIONS_IMPLEMENTATION.md) - Complete system overview
|
||||
- [pwa_web_push_implementation_plan.md](pwa_web_push_implementation_plan.md) - Updated with completion status
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: October 8, 2025
|
||||
**Status**: Production Ready ✅
|
||||
Reference in New Issue
Block a user