Files
maternal-app/PUSH_NOTIFICATIONS_PERSISTENCE_SUMMARY.md
Andrei 9b31d56c1d
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
feat: Add persistent global notification settings for admin panel
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>
2025-10-08 23:09:13 +00:00

15 KiB

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:

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
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)
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

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

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

POST /api/v1/preferences/notifications/push/enable
Authorization: Bearer <token>

Response:
{
  "success": true
}

Get Notification Summary

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:

-- 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

-- 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

# 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:


Last Updated: October 8, 2025 Status: Production Ready