import { Injectable, NotFoundException } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { NotificationService } from '../notification/notification.service'; @Injectable() export class CommentService { constructor( private readonly prisma: PrismaService, private readonly notificationService: NotificationService, ) {} async findAll(tenantId: string, ticketId: string) { const db = this.prisma.forTenant(tenantId); const ticket = await db.ticket.findFirst({ where: { id: ticketId } }); if (!ticket) throw new NotFoundException('Ticket not found'); return this.prisma.ticketComment.findMany({ where: { tenantId, ticketId }, include: { author: { select: { id: true, firstName: true, lastName: true } }, attachments: true, }, orderBy: { createdAt: 'asc' }, }); } async create( tenantId: string, ticketId: string, userId: string, content: string, files?: Express.Multer.File[], ) { const db = this.prisma.forTenant(tenantId); const ticket = await db.ticket.findFirst({ where: { id: ticketId } }); if (!ticket) throw new NotFoundException('Ticket not found'); const comment = await this.prisma.ticketComment.create({ data: { tenantId, ticketId, userId, content, attachments: files?.length ? { create: files.map((f) => ({ fileName: f.originalname, filePath: f.filename, fileType: f.mimetype, fileSize: f.size, })), } : undefined, }, include: { author: { select: { id: true, firstName: true, lastName: true } }, attachments: true, }, }); // Parse @mentions and notify mentioned users const mentions = this.parseMentions(content); if (mentions.length > 0) { const db2 = this.prisma.forTenant(tenantId); const users = await db2.user.findMany({ where: { tenantId, isActive: true }, select: { id: true, firstName: true, lastName: true }, }); const commenterName = `${comment.author.firstName} ${comment.author.lastName}`; for (const mention of mentions) { const mentionedUser = users.find( (u) => `${u.firstName} ${u.lastName}`.toLowerCase() === mention.toLowerCase() || u.firstName.toLowerCase() === mention.toLowerCase(), ); if (mentionedUser && mentionedUser.id !== userId) { await this.notificationService.create(tenantId, { userId: mentionedUser.id, type: 'in_app', channel: 'mention', title: 'You were mentioned', message: `${commenterName} mentioned you in "${ticket.title}"`, ticketId, }); } } } // Notify ticket creator (if not the commenter) if (ticket.createdById && ticket.createdById !== userId) { const commenterName = `${comment.author.firstName} ${comment.author.lastName}`; await this.notificationService.create(tenantId, { userId: ticket.createdById, type: 'in_app', channel: 'comment_added', title: 'New comment on your ticket', message: `${commenterName} commented on "${ticket.title}"`, ticketId, }); } return comment; } /** Extract @FirstName or @FirstNameLastName from content */ private parseMentions(content: string): string[] { const regex = /@(\w+(?:\s+\w+)?)/g; const matches: string[] = []; let match: RegExpExecArray | null; while ((match = regex.exec(content)) !== null) { matches.push(match[1]); } return [...new Set(matches)]; } }