/** * interactions.repository.ts * * Queries de banco para todas as interações sobre tickets existentes. * Única camada que toca o Drizzle neste módulo. */ import { eq } from 'drizzle-orm'; import type { Database } from '@/db/connection.js'; import { tickets, comments, users } from '@/db/schema.js'; import type { TicketStatus, TicketPriority } from '@/db/schema.js'; // ─── Tickets ────────────────────────────────────────────────────────────────── export async function updateTicketStatus( db: Database, ticketId: number, status: TicketStatus, ) { const now = new Date().toISOString(); const [updated] = await db .update(tickets) .set({ status, updatedAt: now, // Registra o momento exato da resolução para métricas de SLA resolvedAt: status === 'resolved' || status === 'closed' ? now : null, }) .where(eq(tickets.id, ticketId)) .returning(); return updated ?? null; } export async function assignTicket( db: Database, ticketId: number, assigneeId: number, ) { const [updated] = await db .update(tickets) .set({ assigneeId, // Atribuir um agente muda automaticamente o status de 'open' → 'in_progress' status: 'in_progress', updatedAt: new Date().toISOString(), }) .where(eq(tickets.id, ticketId)) .returning(); return updated ?? null; } export async function updateTicketPriority( db: Database, ticketId: number, priority: TicketPriority, ) { const [updated] = await db .update(tickets) .set({ priority, updatedAt: new Date().toISOString(), }) .where(eq(tickets.id, ticketId)) .returning(); return updated ?? null; } // ─── Comentários ────────────────────────────────────────────────────────────── export async function addComment( db: Database, data: { ticketId: number; authorId: number; content: string; isInternal: boolean; }, ) { const now = new Date().toISOString(); const [comment] = await db .insert(comments) .values({ ...data, createdAt: now, updatedAt: now, }) .returning(); return comment; } export async function findCommentsByTicketId( db: Database, ticketId: number, // Filtro: agentes veem tudo, solicitantes só veem comentários públicos includeInternal: boolean = true, ) { const rows = await db .select({ id: comments.id, ticketId: comments.ticketId, authorId: comments.authorId, content: comments.content, isInternal: comments.isInternal, createdAt: comments.createdAt, updatedAt: comments.updatedAt, authorName: users.name, }) .from(comments) .leftJoin(users, eq(comments.authorId, users.id)) .where(eq(comments.ticketId, ticketId)) .orderBy(comments.createdAt); // Cronológico: mais antigos primeiro // Filtra notas internas em memória (conjunto pequeno por ticket) return includeInternal ? rows : rows.filter((c) => !c.isInternal); } export async function deleteComment( db: Database, commentId: number, ) { const [deleted] = await db .delete(comments) .where(eq(comments.id, commentId)) .returning(); return deleted ?? null; }