From ecb68de7b371cc25da0d45027747f6b07536fb1e Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Fri, 12 Dec 2025 20:13:18 +0330 Subject: [PATCH] update notif --- .../dto/create-preference.dto.ts | 22 +- .../dto/update-preference.dto.ts | 16 +- .../notification-preference.entity.ts | 4 - .../notification-queue.interface.ts | 4 +- .../notifications/notifications.gateway.ts | 251 ++++++++++++++--- src/modules/notifications/pager.gateway.ts | 254 ------------------ .../notification-preference.service.ts | 2 +- .../services/notification.service.ts | 9 +- .../data/notification-preferences.data.ts | 15 +- .../notification-preferences.seeder.ts | 8 +- 10 files changed, 255 insertions(+), 330 deletions(-) delete mode 100644 src/modules/notifications/pager.gateway.ts diff --git a/src/modules/notifications/dto/create-preference.dto.ts b/src/modules/notifications/dto/create-preference.dto.ts index 621c96a..63583fc 100644 --- a/src/modules/notifications/dto/create-preference.dto.ts +++ b/src/modules/notifications/dto/create-preference.dto.ts @@ -1,18 +1,9 @@ -import { IsNotEmpty, IsEnum } from 'class-validator'; +import { IsNotEmpty, IsEnum, IsArray } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { NotifTitleEnum } from '../interfaces/notification.interface'; -import { NotificationType } from '../interfaces/notification.interface'; +import { NotifChannelEnum } from '../interfaces/notification.interface'; export class CreatePreferenceDto { - @ApiProperty({ - description: 'Notification type (SMS, PUSH, BOTH, or NONE)', - enum: NotificationType, - example: NotificationType.SMS, - }) - @IsEnum(NotificationType) - @IsNotEmpty() - notificationType!: NotificationType; - @ApiProperty({ description: 'Notification title/type (e.g., order.created, review.created)', enum: NotifTitleEnum, @@ -21,4 +12,13 @@ export class CreatePreferenceDto { @IsEnum(NotifTitleEnum) @IsNotEmpty() title!: NotifTitleEnum; + + @ApiProperty({ + description: 'Notification type (SMS, PUSH, BOTH, or NONE)', + enum: NotifChannelEnum, + example: NotifChannelEnum.SMS, + }) + @IsArray() + @IsEnum(NotifChannelEnum, { each: true }) + channels!: NotifChannelEnum[]; } diff --git a/src/modules/notifications/dto/update-preference.dto.ts b/src/modules/notifications/dto/update-preference.dto.ts index 7e1193d..914ad28 100644 --- a/src/modules/notifications/dto/update-preference.dto.ts +++ b/src/modules/notifications/dto/update-preference.dto.ts @@ -1,15 +1,15 @@ import { ApiProperty } from '@nestjs/swagger'; -import { NotificationType } from '../interfaces/notification.interface'; -import { IsNotEmpty } from 'class-validator'; -import { IsEnum } from 'class-validator'; +import { NotifChannelEnum } from '../interfaces/notification.interface'; +import { IsArray, IsEnum } from 'class-validator'; export class UpdatePreferenceDto { @ApiProperty({ description: 'Notification type (SMS, PUSH, BOTH, or NONE)', - enum: NotificationType, - example: NotificationType.SMS, + enum: NotifChannelEnum, + example: NotifChannelEnum.SMS, }) - @IsEnum(NotificationType) - @IsNotEmpty() - notificationType!: NotificationType; + @IsEnum(NotifChannelEnum) + @IsArray() + @IsEnum(NotifChannelEnum, { each: true }) + channels!: NotifChannelEnum[]; } diff --git a/src/modules/notifications/entities/notification-preference.entity.ts b/src/modules/notifications/entities/notification-preference.entity.ts index 0597523..e3ab3bc 100644 --- a/src/modules/notifications/entities/notification-preference.entity.ts +++ b/src/modules/notifications/entities/notification-preference.entity.ts @@ -2,7 +2,6 @@ import { Entity, Property, ManyToOne, Unique } from '@mikro-orm/core'; import { BaseEntity } from '../../../common/entities/base.entity'; import { Restaurant } from '../../restaurants/entities/restaurant.entity'; import { NotifTitleEnum } from '../interfaces/notification.interface'; -import { Admin } from 'src/modules/admin/entities/admin.entity'; import { NotifChannelEnum } from '../interfaces/notification.interface'; @Entity({ tableName: 'notification_preferences' }) @@ -11,9 +10,6 @@ export class NotificationPreference extends BaseEntity { @ManyToOne(() => Restaurant) restaurant!: Restaurant; - @ManyToOne(() => Admin, { nullable: true }) - admin?: Admin; - @Property() title!: NotifTitleEnum; diff --git a/src/modules/notifications/interfaces/notification-queue.interface.ts b/src/modules/notifications/interfaces/notification-queue.interface.ts index 1efb012..525f359 100644 --- a/src/modules/notifications/interfaces/notification-queue.interface.ts +++ b/src/modules/notifications/interfaces/notification-queue.interface.ts @@ -1,4 +1,4 @@ -import type { NotificationType } from './notification.interface'; +import type { NotifChannelEnum } from './notification.interface'; import type { NotifTitleEnum } from './notification.interface'; export interface NotificationQueueJob { @@ -8,7 +8,7 @@ export interface NotificationQueueJob { content: string; idempotencyKey?: string; notificationId?: string; // For retries - notificationType: NotificationType; + channels: NotifChannelEnum[]; pushToken?: string; // FCM token for push notifications pushTokens?: string[]; // Multiple FCM tokens for bulk push notifications } diff --git a/src/modules/notifications/notifications.gateway.ts b/src/modules/notifications/notifications.gateway.ts index c40175e..9cab6a3 100644 --- a/src/modules/notifications/notifications.gateway.ts +++ b/src/modules/notifications/notifications.gateway.ts @@ -1,24 +1,27 @@ import { WebSocketGateway, WebSocketServer, - SubscribeMessage, + // SubscribeMessage, OnGatewayConnection, OnGatewayDisconnect, - MessageBody, - ConnectedSocket, + // MessageBody, + // ConnectedSocket, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; -import { Logger, UseGuards } from '@nestjs/common'; -import { WsAdminAuthGuard, type AuthenticatedSocket } from './guards/ws-admin-auth.guard'; -import { WsRestId } from './decorators/ws-rest-id.decorator'; +import { Logger, UseGuards, Inject, forwardRef } from '@nestjs/common'; +// import { WsRestId } from './decorators/ws-rest-id.decorator'; import { NotificationService } from './services/notification.service'; +import { WsAdminAuthGuard, type AuthenticatedSocket } from './guards/ws-admin-auth.guard'; +@UseGuards(WsAdminAuthGuard) @WebSocketGateway({ cors: { origin: true, credentials: true, + connectionGuard: WsAdminAuthGuard, }, namespace: '/notifications', + transports: ['websocket', 'polling'], }) export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisconnect { @WebSocketServer() @@ -26,9 +29,13 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco private readonly logger = new Logger(NotificationsGateway.name); - constructor(private readonly notificationService: NotificationService) {} + constructor( + @Inject(forwardRef(() => NotificationService)) + private readonly notificationService: NotificationService, + ) {} - handleConnection(client: Socket) { + handleConnection(client: AuthenticatedSocket) { + console.log(client); this.logger.log(`Client connected: ${client.id}`); } @@ -36,34 +43,212 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco this.logger.log(`Client disconnected: ${client.id}`); } + sendAdminNotification(client: AuthenticatedSocket, event: string, data: any) { + const room = this.getRoom(client); + this.server.to(room).emit(event, data); + } + + private getRoom(client: AuthenticatedSocket) { + return `restaurant:${client.restId}-admin:${client.adminId}`; + } /** - * Get admin restaurant notifications + * Join a room for a specific restaurant (admin/staff) * Requires admin authentication via JWT token * Restaurant ID is extracted from the authenticated admin's token - * This is a WebSocket substitution for GET /admin/notifications + * Room format: `restaurant:${restId}` */ - @UseGuards(WsAdminAuthGuard) - @SubscribeMessage('get:notifications') - async handleGetNotifications( - @ConnectedSocket() client: AuthenticatedSocket, - @WsRestId() restId: string, - @MessageBody() data?: { limit?: number }, - ) { - try { - const limit = data?.limit ? parseInt(data.limit.toString(), 10) : 50; - const notifications = await this.notificationService.findByRestaurant(restId, limit); + // @UseGuards(WsAdminAuthGuard) + // @SubscribeMessage('join:restaurant') + // handleJoinRestaurant(@ConnectedSocket() client: AuthenticatedSocket, @WsRestId() restId: string) { + // const room = `restaurant:${restId}`; + // void client.join(room); + // this.logger.log(`Admin ${client.adminId} (Client ${client.id}) joined room: ${room}`); + // void client.emit('joined', { room, message: 'Successfully joined restaurant room' }); + // } - void client.emit('notifications:list', { notifications }); - this.logger.log(`Admin ${client.adminId} requested notifications list for restaurant ${restId}`); - } catch (error) { - this.logger.error( - `Error getting notifications list: ${error instanceof Error ? error.message : 'Unknown error'}`, - ); - void client.emit('error', { - message: 'Failed to get notifications list', - code: 'GET_NOTIFICATIONS_ERROR', - details: error instanceof Error ? error.message : 'Unknown error', - }); - } - } + /** + * Join a room for a specific cookie/session (public user) + * Room format: `cookie:${cookieId}` + */ + // @SubscribeMessage('join:session') + // handleJoinSession(@ConnectedSocket() client: Socket, @MessageBody() data: { cookieId: string }) { + // if (!data.cookieId) { + // void client.emit('error', { message: 'Cookie ID is required' }); + // return; + // } + + // const room = `cookie:${data.cookieId}`; + // void client.join(room); + // this.logger.log(`Client ${client.id} joined room: ${room}`); + // void client.emit('joined', { room, message: 'Successfully joined session room' }); + // } + + /** + * Leave a room + */ + // @SubscribeMessage('leave:room') + // handleLeaveRoom(@ConnectedSocket() client: Socket, @MessageBody() data: { room: string }) { + // if (data.room) { + // void client.leave(data.room); + // this.logger.log(`Client ${client.id} left room: ${data.room}`); + // void client.emit('left', { room: data.room, message: 'Successfully left room' }); + // } + // } + + /** + * Get list of pagers for the restaurant (admin only) + * Requires admin authentication via JWT token + * Restaurant ID is extracted from the authenticated admin's token + */ + // @UseGuards(WsAdminAuthGuard) + // @SubscribeMessage('get:pagers') + // async handleGetPagers(@ConnectedSocket() client: AuthenticatedSocket, @WsRestId() restId: string) { + // try { + // const pagers = await this.pagerService.findAllByRestaurantId(restId); + + // const pagersData = pagers.map(pager => ({ + // id: pager.id, + // tableNumber: pager.tableNumber, + // message: pager.message, + // status: pager.status, + // cookieId: pager.cookieId, + // createdAt: pager.createdAt, + // updatedAt: pager.updatedAt, + // user: pager.user + // ? { + // id: pager.user.id, + // firstName: pager.user.firstName, + // lastName: pager.user.lastName, + // } + // : null, + // staff: pager.staff + // ? { + // id: pager.staff.id, + // firstName: pager.staff.firstName, + // lastName: pager.staff.lastName, + // } + // : null, + // })); + + // void client.emit('pagers:list', { pagers: pagersData }); + // this.logger.log(`Admin ${client.adminId} requested pagers list for restaurant ${restId}`); + // } catch (error) { + // this.logger.error(`Error getting pagers list: ${error instanceof Error ? error.message : 'Unknown error'}`); + // void client.emit('error', { + // message: 'Failed to get pagers list', + // code: 'GET_PAGERS_ERROR', + // details: error instanceof Error ? error.message : 'Unknown error', + // }); + // } + // } + + /** + * Update pager status (admin only) + * Requires admin authentication via JWT token + * Restaurant ID and Admin ID are extracted from the authenticated admin's token + */ + // @UseGuards(WsAdminAuthGuard) + // @SubscribeMessage('update:pager:status') + // async handleUpdatePagerStatus( + // @ConnectedSocket() client: AuthenticatedSocket, + // @WsRestId() restId: string, + // @WsAdminId() adminId: string, + // @MessageBody() data: { pagerId: string; status: PagerStatus }, + // ) { + // try { + // if (!data.pagerId || !data.status) { + // void client.emit('error', { + // message: 'Pager ID and status are required', + // code: 'INVALID_PAYLOAD', + // }); + // return; + // } + + // const pager = await this.pagerService.updateStatus(data.pagerId, restId, adminId, data.status); + + // const pagerData = { + // id: pager.id, + // tableNumber: pager.tableNumber, + // message: pager.message, + // status: pager.status, + // updatedAt: pager.updatedAt, + // staff: pager.staff + // ? { + // id: pager.staff.id, + // firstName: pager.staff.firstName, + // lastName: pager.staff.lastName, + // } + // : null, + // }; + + // void client.emit('pager:status:updated:response', { pager: pagerData }); + // this.logger.log(`Admin ${adminId} updated pager ${data.pagerId} status to ${data.status}`); + // } catch (error) { + // this.logger.error(`Error updating pager status: ${error instanceof Error ? error.message : 'Unknown error'}`); + // void client.emit('error', { + // message: error instanceof Error ? error.message : 'Failed to update pager status', + // code: 'UPDATE_PAGER_STATUS_ERROR', + // details: error instanceof Error ? error.message : 'Unknown error', + // }); + // } + // } + + // /** + // * Emit pager created event to restaurant room + // */ + // emitPagerCreated(pager: Pager) { + // const room = `restaurant:${pager.restaurant.id}`; + // this.server.to(room).emit('pager:created', { + // pager: { + // id: pager.id, + // tableNumber: pager.tableNumber, + // message: pager.message, + // status: pager.status, + // createdAt: pager.createdAt, + // user: pager.user + // ? { + // id: pager.user.id, + // firstName: pager.user.firstName, + // lastName: pager.user.lastName, + // } + // : null, + // }, + // }); + // this.logger.log(`Emitted pager:created to room: ${room}`); + // } + + // /** + // * Emit pager status updated event to both restaurant and cookie rooms + // */ + // emitPagerStatusUpdated(pager: Pager) { + // const restaurantRoom = `restaurant:${pager.restaurant.id}`; + // const cookieRoom = `cookie:${pager.cookieId}`; + + // const pagerData = { + // id: pager.id, + // tableNumber: pager.tableNumber, + // message: pager.message, + // status: pager.status, + // updatedAt: pager.updatedAt, + // staff: pager.staff + // ? { + // id: pager.staff.id, + // firstName: pager.staff.firstName, + // lastName: pager.staff.lastName, + // } + // : null, + // }; + + // // Emit to restaurant room (admin/staff) + // this.server.to(restaurantRoom).emit('pager:status:updated', { + // pager: pagerData, + // }); + // this.logger.log(`Emitted pager:status:updated to room: ${restaurantRoom}`); + + // // Emit to cookie room (public user) + // this.server.to(cookieRoom).emit('pager:status:updated', { + // pager: pagerData, + // }); + // this.logger.log(`Emitted pager:status:updated to room: ${cookieRoom}`); + // } } diff --git a/src/modules/notifications/pager.gateway.ts b/src/modules/notifications/pager.gateway.ts deleted file mode 100644 index 78364b1..0000000 --- a/src/modules/notifications/pager.gateway.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { - WebSocketGateway, - WebSocketServer, - // SubscribeMessage, - OnGatewayConnection, - OnGatewayDisconnect, - // MessageBody, - // ConnectedSocket, -} from '@nestjs/websockets'; -import { Server, Socket } from 'socket.io'; -import { Logger, UseGuards, Inject, forwardRef } from '@nestjs/common'; -// import { WsRestId } from './decorators/ws-rest-id.decorator'; -import { NotificationService } from './services/notification.service'; -import { WsAdminAuthGuard, type AuthenticatedSocket } from './guards/ws-admin-auth.guard'; - -@UseGuards(WsAdminAuthGuard) -@WebSocketGateway({ - cors: { - origin: true, - credentials: true, - connectionGuard: WsAdminAuthGuard, - }, - namespace: '/notifications', - transports: ['websocket', 'polling'], -}) -export class NotificationGateway implements OnGatewayConnection, OnGatewayDisconnect { - @WebSocketServer() - server!: Server; - - private readonly logger = new Logger(NotificationGateway.name); - - constructor( - @Inject(forwardRef(() => NotificationService)) - private readonly notificationService: NotificationService, - ) {} - - handleConnection(client: AuthenticatedSocket) { - console.log(client); - this.logger.log(`Client connected: ${client.id}`); - } - - handleDisconnect(client: Socket) { - this.logger.log(`Client disconnected: ${client.id}`); - } - - sendAdminNotification(client: AuthenticatedSocket, event: string, data: any) { - const room = this.getRoom(client); - this.server.to(room).emit(event, data); - } - - private getRoom(client: AuthenticatedSocket) { - return `restaurant:${client.restId}-admin:${client.adminId}`; - } - /** - * Join a room for a specific restaurant (admin/staff) - * Requires admin authentication via JWT token - * Restaurant ID is extracted from the authenticated admin's token - * Room format: `restaurant:${restId}` - */ - // @UseGuards(WsAdminAuthGuard) - // @SubscribeMessage('join:restaurant') - // handleJoinRestaurant(@ConnectedSocket() client: AuthenticatedSocket, @WsRestId() restId: string) { - // const room = `restaurant:${restId}`; - // void client.join(room); - // this.logger.log(`Admin ${client.adminId} (Client ${client.id}) joined room: ${room}`); - // void client.emit('joined', { room, message: 'Successfully joined restaurant room' }); - // } - - /** - * Join a room for a specific cookie/session (public user) - * Room format: `cookie:${cookieId}` - */ - // @SubscribeMessage('join:session') - // handleJoinSession(@ConnectedSocket() client: Socket, @MessageBody() data: { cookieId: string }) { - // if (!data.cookieId) { - // void client.emit('error', { message: 'Cookie ID is required' }); - // return; - // } - - // const room = `cookie:${data.cookieId}`; - // void client.join(room); - // this.logger.log(`Client ${client.id} joined room: ${room}`); - // void client.emit('joined', { room, message: 'Successfully joined session room' }); - // } - - /** - * Leave a room - */ - // @SubscribeMessage('leave:room') - // handleLeaveRoom(@ConnectedSocket() client: Socket, @MessageBody() data: { room: string }) { - // if (data.room) { - // void client.leave(data.room); - // this.logger.log(`Client ${client.id} left room: ${data.room}`); - // void client.emit('left', { room: data.room, message: 'Successfully left room' }); - // } - // } - - /** - * Get list of pagers for the restaurant (admin only) - * Requires admin authentication via JWT token - * Restaurant ID is extracted from the authenticated admin's token - */ - // @UseGuards(WsAdminAuthGuard) - // @SubscribeMessage('get:pagers') - // async handleGetPagers(@ConnectedSocket() client: AuthenticatedSocket, @WsRestId() restId: string) { - // try { - // const pagers = await this.pagerService.findAllByRestaurantId(restId); - - // const pagersData = pagers.map(pager => ({ - // id: pager.id, - // tableNumber: pager.tableNumber, - // message: pager.message, - // status: pager.status, - // cookieId: pager.cookieId, - // createdAt: pager.createdAt, - // updatedAt: pager.updatedAt, - // user: pager.user - // ? { - // id: pager.user.id, - // firstName: pager.user.firstName, - // lastName: pager.user.lastName, - // } - // : null, - // staff: pager.staff - // ? { - // id: pager.staff.id, - // firstName: pager.staff.firstName, - // lastName: pager.staff.lastName, - // } - // : null, - // })); - - // void client.emit('pagers:list', { pagers: pagersData }); - // this.logger.log(`Admin ${client.adminId} requested pagers list for restaurant ${restId}`); - // } catch (error) { - // this.logger.error(`Error getting pagers list: ${error instanceof Error ? error.message : 'Unknown error'}`); - // void client.emit('error', { - // message: 'Failed to get pagers list', - // code: 'GET_PAGERS_ERROR', - // details: error instanceof Error ? error.message : 'Unknown error', - // }); - // } - // } - - /** - * Update pager status (admin only) - * Requires admin authentication via JWT token - * Restaurant ID and Admin ID are extracted from the authenticated admin's token - */ - // @UseGuards(WsAdminAuthGuard) - // @SubscribeMessage('update:pager:status') - // async handleUpdatePagerStatus( - // @ConnectedSocket() client: AuthenticatedSocket, - // @WsRestId() restId: string, - // @WsAdminId() adminId: string, - // @MessageBody() data: { pagerId: string; status: PagerStatus }, - // ) { - // try { - // if (!data.pagerId || !data.status) { - // void client.emit('error', { - // message: 'Pager ID and status are required', - // code: 'INVALID_PAYLOAD', - // }); - // return; - // } - - // const pager = await this.pagerService.updateStatus(data.pagerId, restId, adminId, data.status); - - // const pagerData = { - // id: pager.id, - // tableNumber: pager.tableNumber, - // message: pager.message, - // status: pager.status, - // updatedAt: pager.updatedAt, - // staff: pager.staff - // ? { - // id: pager.staff.id, - // firstName: pager.staff.firstName, - // lastName: pager.staff.lastName, - // } - // : null, - // }; - - // void client.emit('pager:status:updated:response', { pager: pagerData }); - // this.logger.log(`Admin ${adminId} updated pager ${data.pagerId} status to ${data.status}`); - // } catch (error) { - // this.logger.error(`Error updating pager status: ${error instanceof Error ? error.message : 'Unknown error'}`); - // void client.emit('error', { - // message: error instanceof Error ? error.message : 'Failed to update pager status', - // code: 'UPDATE_PAGER_STATUS_ERROR', - // details: error instanceof Error ? error.message : 'Unknown error', - // }); - // } - // } - - // /** - // * Emit pager created event to restaurant room - // */ - // emitPagerCreated(pager: Pager) { - // const room = `restaurant:${pager.restaurant.id}`; - // this.server.to(room).emit('pager:created', { - // pager: { - // id: pager.id, - // tableNumber: pager.tableNumber, - // message: pager.message, - // status: pager.status, - // createdAt: pager.createdAt, - // user: pager.user - // ? { - // id: pager.user.id, - // firstName: pager.user.firstName, - // lastName: pager.user.lastName, - // } - // : null, - // }, - // }); - // this.logger.log(`Emitted pager:created to room: ${room}`); - // } - - // /** - // * Emit pager status updated event to both restaurant and cookie rooms - // */ - // emitPagerStatusUpdated(pager: Pager) { - // const restaurantRoom = `restaurant:${pager.restaurant.id}`; - // const cookieRoom = `cookie:${pager.cookieId}`; - - // const pagerData = { - // id: pager.id, - // tableNumber: pager.tableNumber, - // message: pager.message, - // status: pager.status, - // updatedAt: pager.updatedAt, - // staff: pager.staff - // ? { - // id: pager.staff.id, - // firstName: pager.staff.firstName, - // lastName: pager.staff.lastName, - // } - // : null, - // }; - - // // Emit to restaurant room (admin/staff) - // this.server.to(restaurantRoom).emit('pager:status:updated', { - // pager: pagerData, - // }); - // this.logger.log(`Emitted pager:status:updated to room: ${restaurantRoom}`); - - // // Emit to cookie room (public user) - // this.server.to(cookieRoom).emit('pager:status:updated', { - // pager: pagerData, - // }); - // this.logger.log(`Emitted pager:status:updated to room: ${cookieRoom}`); - // } -} diff --git a/src/modules/notifications/services/notification-preference.service.ts b/src/modules/notifications/services/notification-preference.service.ts index 3256328..f5fbc57 100644 --- a/src/modules/notifications/services/notification-preference.service.ts +++ b/src/modules/notifications/services/notification-preference.service.ts @@ -18,7 +18,7 @@ export class NotificationPreferenceService { const preference = this.em.create(NotificationPreference, { restaurant, - notificationType: dto.notificationType, + channels: dto.channels, title: dto.title, }); diff --git a/src/modules/notifications/services/notification.service.ts b/src/modules/notifications/services/notification.service.ts index efffea3..b374417 100644 --- a/src/modules/notifications/services/notification.service.ts +++ b/src/modules/notifications/services/notification.service.ts @@ -6,8 +6,7 @@ import { NotificationQueueService } from './notification-queue.service'; import { NotificationQueueJob } from '../interfaces/notification-queue.interface'; import { Restaurant } from '../../restaurants/entities/restaurant.entity'; import { User } from '../../users/entities/user.entity'; -import { NotificationType } from '../interfaces/notification.interface'; -import { NotifTitleEnum } from '../interfaces/notification.interface'; + import { NotifTitleEnum } from '../interfaces/notification.interface'; export interface SendNotificationParams { restaurantId: string; @@ -28,7 +27,7 @@ export class NotificationService { ) {} async sendNotification(params: SendNotificationParams): Promise { - const { restaurantId, userId, title, content, idempotencyKey } = params; + const { restaurantId, userId, title, content } = params; // Verify restaurant exists const restaurant = await this.em.findOne(Restaurant, { id: restaurantId }); @@ -59,7 +58,7 @@ export class NotificationService { await this.em.persistAndFlush(notification); - if (preference.notificationType === NotificationType.NONE) { + if (preference.channels.length === 0) { this.logger.warn(`Notification type is NONE for restaurant ${restaurantId}, title ${title}`); return notification; } @@ -71,7 +70,7 @@ export class NotificationService { content, // idempotencyKey: finalIdempotencyKey, notificationId: notification.id, - notificationType: preference.notificationType, + channels: preference.channels, }; // if (preference.notificationType === NotificationType.SMS) { // await this.queueService.addSmsNotification(job); diff --git a/src/seeders/data/notification-preferences.data.ts b/src/seeders/data/notification-preferences.data.ts index 6490cd6..3a3f37c 100644 --- a/src/seeders/data/notification-preferences.data.ts +++ b/src/seeders/data/notification-preferences.data.ts @@ -1,26 +1,25 @@ -import { NotifTitleEnum } from '../../modules/notifications/interfaces/notification.interface'; -import { NotificationType } from '../../modules/notifications/interfaces/notification.interface'; - +import { NotifChannelEnum, NotifTitleEnum } from '../../modules/notifications/interfaces/notification.interface'; + export interface NotificationPreferenceData { title: NotifTitleEnum; - notificationType: NotificationType; + channels: NotifChannelEnum[]; } export const notificationPreferencesData: NotificationPreferenceData[] = [ { title: NotifTitleEnum.ORDER_CREATED, - notificationType: NotificationType.SMS, + channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP], }, { title: NotifTitleEnum.PAYMENT_SUCCESS, - notificationType: NotificationType.SMS, + channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP], }, { title: NotifTitleEnum.REVIEW_CREATED, - notificationType: NotificationType.PUSH, + channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP], }, { title: NotifTitleEnum.ORDER_STATUS_CHANGED, - notificationType: NotificationType.Both, + channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP], }, ]; diff --git a/src/seeders/notification-preferences.seeder.ts b/src/seeders/notification-preferences.seeder.ts index 28f522c..0791d7e 100644 --- a/src/seeders/notification-preferences.seeder.ts +++ b/src/seeders/notification-preferences.seeder.ts @@ -1,6 +1,6 @@ import type { EntityManager } from '@mikro-orm/core'; import { NotificationPreference } from '../modules/notifications/entities/notification-preference.entity'; -import { Restaurant } from '../modules/restaurants/entities/restaurant.entity'; +import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity'; import { notificationPreferencesData } from './data/notification-preferences.data'; export class NotificationPreferencesSeeder { @@ -18,13 +18,13 @@ export class NotificationPreferencesSeeder { const preference = em.create(NotificationPreference, { restaurant, title: preferenceData.title, - notificationType: preferenceData.notificationType, + channels: preferenceData.channels, }); em.persist(preference); } else { // Update existing preference if notification type changed - if (existing.notificationType !== preferenceData.notificationType) { - existing.notificationType = preferenceData.notificationType; + if (existing.channels.length !== preferenceData.channels.length) { + existing.channels = preferenceData.channels; em.persist(existing); } }