/opt/mawid/apps/api/src/conversations
NameSizeModeActions
conversations.controller.ts10230644editdlrm
conversations.module.ts3040644editdlrm
conversations.service.ts24480644editdlrm
Edit: /opt/mawid/apps/api/src/conversations/conversations.service.ts (2448B)
import { Injectable, NotFoundException } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; @Injectable() export class ConversationsService { constructor(private readonly prisma: PrismaService) {} async list(clinicId: string) { const conversations = await this.prisma.conversation.findMany({ where: { clinicId }, include: { patient: { select: { id: true, name: true, waPhone: true, language: true } }, messages: { orderBy: { createdAt: 'desc' }, take: 1 }, }, orderBy: { lastMessageAt: { sort: 'desc', nulls: 'last' } }, take: 100, }); return conversations.map((c) => ({ id: c.id, patient: c.patient, lastMessageAt: c.lastMessageAt, lastMessage: c.messages[0]?.body ?? null, handedOff: Boolean((c.state as { handedOff?: boolean })?.handedOff), handoffReason: (c.state as { handoffReason?: string })?.handoffReason ?? null, })); } async messages(clinicId: string, id: string) { await this.assertExists(clinicId, id); return this.prisma.message.findMany({ where: { conversationId: id }, orderBy: { createdAt: 'asc' }, take: 500, select: { id: true, direction: true, type: true, body: true, createdAt: true }, }); } /** "Take over" pauses the agent for this conversation (Phase 6 page 3). */ async setTakeover(clinicId: string, id: string, takeover: boolean, actor: string) { const conversation = await this.assertExists(clinicId, id); const state = (conversation.state ?? {}) as Record; const nextState = takeover ? { ...state, handedOff: true, handoffReason: 'owner_takeover' } : { ...state, handedOff: false, handoffReason: undefined, agentTurnsMs: [] }; await this.prisma.conversation.update({ where: { id }, data: { state: JSON.parse(JSON.stringify(nextState)) }, }); await this.prisma.auditLog.create({ data: { clinicId, actor, action: takeover ? 'conversation:takeover' : 'conversation:resume_agent', meta: { conversationId: id }, }, }); return { id, handedOff: takeover }; } private async assertExists(clinicId: string, id: string) { const conversation = await this.prisma.conversation.findFirst({ where: { id, clinicId } }); if (!conversation) throw new NotFoundException('Conversation not found'); return conversation; } }