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>
481 lines
15 KiB
Markdown
481 lines
15 KiB
Markdown
# 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 ✅
|