From 6ade2c74c50969d4fcfb46beb8d7b4bb0203e978 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Mon, 9 Feb 2026 00:33:54 +0530 Subject: [PATCH] feat: Implement notification preferences management for both users and agents. --- prisma/schema.prisma | 3 ++ src/agents/agents.controller.ts | 48 +++++++++++++++++++++++++++++++ src/agents/agents.service.ts | 51 +++++++++++++++++++++++++++++++++ src/users/users.controller.ts | 50 ++++++++++++++++++++++++++++++++ src/users/users.service.ts | 41 ++++++++++++++++++++++++++ 5 files changed, 193 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f462bd7..f11b6f2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -102,6 +102,9 @@ model User { twoFactorBackupCodes String? // JSON array of hashed backup codes twoFactorVerifiedAt DateTime? // When 2FA was enabled + // User Preferences + notificationPreferences Json? // { email: {...}, push: {...} } + // Timestamps createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/src/agents/agents.controller.ts b/src/agents/agents.controller.ts index 883258f..7d70934 100644 --- a/src/agents/agents.controller.ts +++ b/src/agents/agents.controller.ts @@ -133,6 +133,54 @@ export class AgentsController { return this.agentsService.saveFieldValues(userId, dto); } + // Notification preferences endpoints + + @Get('preferences/notifications') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(UserRole.AGENT) + @ApiBearerAuth() + @ApiOperation({ summary: 'Get notification preferences (Agents only)' }) + @ApiResponse({ + status: 200, + description: 'Notification preferences retrieved', + schema: { + example: { + email: { + new_leads: true, + messages: true, + connection_requests: true, + property_updates: false, + marketing: false, + }, + push: { + push_messages: true, + push_leads: true, + push_reminders: false, + }, + }, + }, + }) + async getNotificationPreferences(@CurrentUser('id') userId: string) { + return this.agentsService.getNotificationPreferences(userId); + } + + @Put('preferences/notifications') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(UserRole.AGENT) + @ApiBearerAuth() + @ApiOperation({ summary: 'Update notification preferences (Agents only)' }) + @ApiResponse({ status: 200, description: 'Notification preferences updated' }) + async updateNotificationPreferences( + @CurrentUser('id') userId: string, + @Body() + data: { + email?: Record; + push?: Record; + }, + ) { + return this.agentsService.updateNotificationPreferences(userId, data); + } + // Public parameterized routes - MUST come after specific routes like profile/* @Get(':id') @Public() diff --git a/src/agents/agents.service.ts b/src/agents/agents.service.ts index 4b72588..35b082e 100644 --- a/src/agents/agents.service.ts +++ b/src/agents/agents.service.ts @@ -2,6 +2,7 @@ import { Injectable, NotFoundException, ConflictException, + BadRequestException, } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { @@ -789,4 +790,54 @@ export class AgentsService { ); } } + + // ============================================= + // NOTIFICATION PREFERENCES + // ============================================= + + async getNotificationPreferences(userId: string) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { notificationPreferences: true, role: true }, + }); + + if (!user) { + throw new NotFoundException('User not found'); + } + + if (user.role !== 'AGENT') { + throw new BadRequestException('This endpoint is for agents only'); + } + + // Return preferences or default empty object + return user.notificationPreferences || { email: {}, push: {} }; + } + + async updateNotificationPreferences( + userId: string, + preferences: { email?: Record; push?: Record }, + ) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { role: true }, + }); + + if (!user) { + throw new NotFoundException('User not found'); + } + + if (user.role !== 'AGENT') { + throw new BadRequestException('This endpoint is for agents only'); + } + + const updatedUser = await this.prisma.user.update({ + where: { id: userId }, + data: { + notificationPreferences: preferences, + }, + select: { notificationPreferences: true }, + }); + + return updatedUser.notificationPreferences; + } } diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 80e079b..f708e97 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -1,6 +1,7 @@ import { Controller, Get, + Put, Param, Patch, Body, @@ -79,6 +80,55 @@ export class UsersController { return this.usersService.updateMyUserProfile(user.id, data); } + // ============================================= + // NOTIFICATION PREFERENCES (for regular users) + // ============================================= + + @Get('preferences/notifications') + @Roles(UserRole.USER) + @ApiOperation({ summary: 'Get notification preferences (Regular users only)' }) + @ApiResponse({ + status: 200, + description: 'Notification preferences retrieved', + schema: { + example: { + email: { + new_leads: true, + messages: true, + connection_requests: true, + property_updates: false, + marketing: false, + }, + push: { + push_messages: true, + push_leads: true, + push_reminders: false, + }, + }, + }, + }) + async getNotificationPreferences(@CurrentUser() user: { id: string }) { + return this.usersService.getNotificationPreferences(user.id); + } + + @Put('preferences/notifications') + @Roles(UserRole.USER) + @ApiOperation({ summary: 'Update notification preferences (Regular users only)' }) + @ApiResponse({ + status: 200, + description: 'Notification preferences updated', + }) + async updateNotificationPreferences( + @CurrentUser() user: { id: string }, + @Body() + data: { + email?: Record; + push?: Record; + }, + ) { + return this.usersService.updateNotificationPreferences(user.id, data); + } + // ============================================= // ADMIN ENDPOINTS // ============================================= diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 15e5490..fc528b1 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -400,4 +400,45 @@ export class UsersService { message: `Verification ${data.status.toLowerCase()} successfully`, }; } + + // ============================================= + // NOTIFICATION PREFERENCES + // ============================================= + + async getNotificationPreferences(userId: string) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { notificationPreferences: true }, + }); + + if (!user) { + throw new NotFoundException('User not found'); + } + + // Return preferences or default empty object + return user.notificationPreferences || { email: {}, push: {} }; + } + + async updateNotificationPreferences( + userId: string, + preferences: { email?: Record; push?: Record }, + ) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + }); + + if (!user) { + throw new NotFoundException('User not found'); + } + + const updatedUser = await this.prisma.user.update({ + where: { id: userId }, + data: { + notificationPreferences: preferences, + }, + select: { notificationPreferences: true }, + }); + + return updatedUser.notificationPreferences; + } }