feat: Add an admin endpoint to retrieve the total count of unread support messages.

This commit is contained in:
pradeepkumar
2026-03-19 04:19:55 +05:30
parent c3a3df704e
commit 4d5a3fc36d
4 changed files with 73 additions and 6 deletions

View File

@@ -82,7 +82,13 @@ export class MessagesGateway implements OnGatewayConnection, OnGatewayDisconnect
} }
this.userSocketMap.get(userId)!.add(client.id); this.userSocketMap.get(userId)!.add(client.id);
this.logger.log(`Client connected successfully: ${client.id} (User: ${userId})`); this.logger.log(`Client connected successfully: ${client.id} (User: ${userId}, Role: ${payload.role})`);
// Auto-join admin users to admin_room for support chat notifications
if (payload.role === 'ADMIN') {
client.join('admin_room');
this.logger.log(`Admin ${userId} joined admin_room`);
}
// Update user online status in background (don't await) // Update user online status in background (don't await)
this.messagesService.updateOnlineStatus(userId, true) this.messagesService.updateOnlineStatus(userId, true)
@@ -337,6 +343,12 @@ export class MessagesGateway implements OnGatewayConnection, OnGatewayDisconnect
// Broadcast to all clients in the support chat room // Broadcast to all clients in the support chat room
this.server.to(`support:${data.chatId}`).emit('support_new_message', message); this.server.to(`support:${data.chatId}`).emit('support_new_message', message);
// Notify admins about new unread count (for sidebar badge)
if (senderRole !== 'ADMIN') {
const unreadTotal = await this.supportChatService.getAdminUnreadTotal();
this.server.to('admin_room').emit('support_unread_update', { unreadTotal });
}
return { success: true, message }; return { success: true, message };
} catch (error) { } catch (error) {
return { error: error.message }; return { error: error.message };

View File

@@ -9,6 +9,8 @@ import {
UseGuards, UseGuards,
HttpCode, HttpCode,
HttpStatus, HttpStatus,
Inject,
forwardRef,
} from '@nestjs/common'; } from '@nestjs/common';
import { import {
ApiTags, ApiTags,
@@ -24,13 +26,18 @@ import { JwtAuthGuard } from '../auth/guards';
import { CurrentUser } from '../auth/decorators'; import { CurrentUser } from '../auth/decorators';
import { Roles } from '../auth/decorators/roles.decorator'; import { Roles } from '../auth/decorators/roles.decorator';
import { UserRole, SupportChatStatus } from '@prisma/client'; import { UserRole, SupportChatStatus } from '@prisma/client';
import { MessagesGateway } from '../messages/messages.gateway';
@ApiTags('Support Chat') @ApiTags('Support Chat')
@Controller('support-chat') @Controller('support-chat')
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@ApiBearerAuth('JWT-auth') @ApiBearerAuth('JWT-auth')
export class SupportChatController { export class SupportChatController {
constructor(private readonly supportChatService: SupportChatService) {} constructor(
private readonly supportChatService: SupportChatService,
@Inject(forwardRef(() => MessagesGateway))
private readonly messagesGateway: MessagesGateway,
) {}
// ========================================== // ==========================================
// GET OR CREATE SUPPORT CHAT (User/Agent) // GET OR CREATE SUPPORT CHAT (User/Agent)
@@ -95,7 +102,24 @@ export class SupportChatController {
await this.supportChatService.getChat(chatId, userId, isAdmin); await this.supportChatService.getChat(chatId, userId, isAdmin);
const senderRole = role === UserRole.ADMIN ? 'ADMIN' : 'USER'; const senderRole = role === UserRole.ADMIN ? 'ADMIN' : 'USER';
return this.supportChatService.sendMessage(chatId, userId, senderRole, dto.content); const message = await this.supportChatService.sendMessage(chatId, userId, senderRole, dto.content);
// Emit WebSocket events for real-time updates
this.messagesGateway.server.to(`support:${chatId}`).emit('support_new_message', message);
// If user/agent sent the message, notify admins about unread count
if (senderRole !== 'ADMIN') {
const unreadTotal = await this.supportChatService.getAdminUnreadTotal();
this.messagesGateway.server.to('admin_room').emit('support_unread_update', { unreadTotal });
}
// If admin sent the message, notify the user directly
if (senderRole === 'ADMIN') {
const chat = await this.supportChatService.getChat(chatId, userId, true);
this.messagesGateway.sendToUser(chat.userId, 'support_new_message', message);
}
return message;
} }
// ========================================== // ==========================================
@@ -109,7 +133,26 @@ export class SupportChatController {
@Param('id') chatId: string, @Param('id') chatId: string,
@CurrentUser('role') role: UserRole, @CurrentUser('role') role: UserRole,
) { ) {
return this.supportChatService.markAsRead(chatId, role); const result = await this.supportChatService.markAsRead(chatId, role);
// If admin marked as read, update sidebar badge for all admins
if (role === UserRole.ADMIN) {
const unreadTotal = await this.supportChatService.getAdminUnreadTotal();
this.messagesGateway.server.to('admin_room').emit('support_unread_update', { unreadTotal });
}
return result;
}
// ==========================================
// ADMIN: GET TOTAL UNREAD COUNT
// ==========================================
@Get('admin/unread-total')
@Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'Admin: Get total unread support message count' })
@ApiResponse({ status: 200, description: 'Total unread count returned' })
async getUnreadTotal() {
return this.supportChatService.getAdminUnreadTotal();
} }
// ========================================== // ==========================================

View File

@@ -1,10 +1,11 @@
import { Module } from '@nestjs/common'; import { Module, forwardRef } from '@nestjs/common';
import { SupportChatController } from './support-chat.controller'; import { SupportChatController } from './support-chat.controller';
import { SupportChatService } from './support-chat.service'; import { SupportChatService } from './support-chat.service';
import { PrismaModule } from '../prisma/prisma.module'; import { PrismaModule } from '../prisma/prisma.module';
import { MessagesModule } from '../messages/messages.module';
@Module({ @Module({
imports: [PrismaModule], imports: [PrismaModule, forwardRef(() => MessagesModule)],
controllers: [SupportChatController], controllers: [SupportChatController],
providers: [SupportChatService], providers: [SupportChatService],
exports: [SupportChatService], exports: [SupportChatService],

View File

@@ -162,6 +162,17 @@ export class SupportChatService {
return { success: true }; return { success: true };
} }
/**
* Admin: get total unread message count across all open chats
*/
async getAdminUnreadTotal(): Promise<number> {
const result = await this.prisma.supportChat.aggregate({
where: { status: SupportChatStatus.OPEN },
_sum: { adminUnreadCount: true },
});
return result._sum.adminUnreadCount || 0;
}
/** /**
* Admin: get all support chats * Admin: get all support chats
*/ */