diff --git a/src/modules/notifications/interfaces/notification.interface.ts b/src/modules/notifications/interfaces/notification.interface.ts index 00c4fe5..431ab72 100644 --- a/src/modules/notifications/interfaces/notification.interface.ts +++ b/src/modules/notifications/interfaces/notification.interface.ts @@ -15,7 +15,7 @@ export enum NotifTitleEnum { REVIEW_CREATED = 'review.created', ORDER_STATUS_CHANGED = 'order.status.changed', } -export type recipientType = { userId: string; restaurantId: string } | { adminId: string; restaurantId: string }; +export type recipientType = { userId: string } | { adminId: string }; export interface NotifRequestMessage { subject: NotifTitleEnum; @@ -37,6 +37,7 @@ export interface NotifRequest { // timestamp: Date; // notifType: NotifTypeEnum; // channels: NotifChannelEnum[]; + restaurantId: string; recipients: recipientType[]; message: NotifRequestMessage; metadata: { diff --git a/src/modules/notifications/notifications.gateway.ts b/src/modules/notifications/notifications.gateway.ts index 9cab6a3..d925f22 100644 --- a/src/modules/notifications/notifications.gateway.ts +++ b/src/modules/notifications/notifications.gateway.ts @@ -18,8 +18,8 @@ import { WsAdminAuthGuard, type AuthenticatedSocket } from './guards/ws-admin-au cors: { origin: true, credentials: true, - connectionGuard: WsAdminAuthGuard, }, + // connectionGuard: WsAdminAuthGuard, namespace: '/notifications', transports: ['websocket', 'polling'], }) @@ -35,21 +35,23 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco ) {} handleConnection(client: AuthenticatedSocket) { - console.log(client); - this.logger.log(`Client connected: ${client.id}`); + this.logger.log(`Admin connected: ${client.adminId}`); + this.handleJoinRoom(client); } handleDisconnect(client: Socket) { this.logger.log(`Client disconnected: ${client.id}`); } - sendAdminNotification(client: AuthenticatedSocket, event: string, data: any) { - const room = this.getRoom(client); + sendInAppNotification(repipient: { adminId: string; restaurantId: string }, event: string, data: any) { + this.logger.log(`Sending in app notification to admin: ${repipient.adminId}`); + const room = this.getRoom(repipient.adminId, repipient.restaurantId); this.server.to(room).emit(event, data); + this.logger.log(`In app notification sent to admin: ${repipient.adminId}`); } - private getRoom(client: AuthenticatedSocket) { - return `restaurant:${client.restId}-admin:${client.adminId}`; + private getRoom(adminId: string, restaurantId: string) { + return `restaurant:${restaurantId}-admin:${adminId}`; } /** * Join a room for a specific restaurant (admin/staff) @@ -57,14 +59,12 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco * 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' }); - // } + handleJoinRoom(client: AuthenticatedSocket) { + const room = this.getRoom(client.adminId!, client.restId!); + void client.join(room); + this.logger.log(`Admin ${client.adminId} (Client ${client.id}) joined room: ${room}`); + void client.emit('joined', { room, message: 'Successfully Admin joined room' }); + } /** * Join a room for a specific cookie/session (public user) diff --git a/src/modules/notifications/services/notification.service.ts b/src/modules/notifications/services/notification.service.ts index d0069bb..3280f66 100644 --- a/src/modules/notifications/services/notification.service.ts +++ b/src/modules/notifications/services/notification.service.ts @@ -3,19 +3,12 @@ import { EntityManager } from '@mikro-orm/postgresql'; import { Notification } from '../entities/notification.entity'; import { NotificationPreferenceService } from './notification-preference.service'; import { NotificationQueueService } from './notification-queue.service'; +import { NotificationsGateway } from '../notifications.gateway'; import { NotificationQueueJob } from '../interfaces/notification-queue.interface'; import { Restaurant } from '../../restaurants/entities/restaurant.entity'; import { User } from '../../users/entities/user.entity'; import { NotifChannelEnum, NotifRequest, NotifTitleEnum } from '../interfaces/notification.interface'; -export interface SendNotificationParams { - restaurantId: string; - userId?: string; - title: NotifTitleEnum; - content: string; - idempotencyKey?: string; -} - @Injectable() export class NotificationService { private readonly logger = new Logger(NotificationService.name); @@ -24,22 +17,23 @@ export class NotificationService { private readonly em: EntityManager, private readonly preferenceService: NotificationPreferenceService, private readonly queueService: NotificationQueueService, + private readonly notificationGateway: NotificationsGateway, ) {} async sendNotification(params: NotifRequest): Promise { - const { recipients, message, metadata } = params; + const { recipients, message, metadata, restaurantId } = params; // create Database notifications const notifications = await this.createAdminBulkNotifications( recipients.map(recipient => ({ - restaurantId: recipient.restaurantId, + restaurantId, title: message.subject, content: message.body, adminId: 'adminId' in recipient ? recipient.adminId : null, userId: 'userId' in recipient ? recipient.userId : null, })), ); - const restaurantId = recipients[0].restaurantId; + // get admin prefrences const preference = await this.preferenceService.findByRestaurantAndType(restaurantId, message.subject); @@ -48,6 +42,17 @@ export class NotificationService { return notifications; } + // send in app notification + if (preference?.channels?.includes(NotifChannelEnum.IN_APP)) { + recipients.forEach(recipient => { + if ('adminId' in recipient) { + this.notificationGateway.sendInAppNotification({ adminId: recipient.adminId, restaurantId }, 'notification', { + title: message.subject, + content: message.body, + }); + } + }); + } // const job: NotificationQueueJob = { // restaurantId, // userId, diff --git a/src/modules/pager/listeners/notification.listeners.ts b/src/modules/pager/listeners/notification.listeners.ts index 9b0f276..6f7b7d3 100644 --- a/src/modules/pager/listeners/notification.listeners.ts +++ b/src/modules/pager/listeners/notification.listeners.ts @@ -22,10 +22,10 @@ export class PagerListeners { // get admnin os restuaraant that have pager permissuins const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_PAGER); const recipients = admins.map(admin => ({ - restaurantId: event.restaurantId, adminId: admin.id, })); await this.notificationService.sendNotification({ + restaurantId: event.restaurantId, message: { subject: NotifTitleEnum.PAGER_CREATED, body: `Your pager has created successfully`, diff --git a/src/modules/pager/providers/pager.service.ts b/src/modules/pager/providers/pager.service.ts index da866ba..f77046f 100644 --- a/src/modules/pager/providers/pager.service.ts +++ b/src/modules/pager/providers/pager.service.ts @@ -48,14 +48,14 @@ export class PagerService { } } const cookieId = userCookieId || ulid().toString(); - const existingPager = await this.em.findOne(Pager, { - restaurant: { id: restaurant!.id }, - status: PagerStatus.Pending, - cookieId, - }); - if (existingPager) { - throw new BadRequestException('Pager already exists'); - } + // const existingPager = await this.em.findOne(Pager, { + // restaurant: { id: restaurant!.id }, + // status: PagerStatus.Pending, + // cookieId, + // }); + // if (existingPager) { + // throw new BadRequestException('Pager already exists'); + // } const pager = this.em.create(Pager, { ...createPagerDto,