diff --git a/src/app/api/tickets/mentions/route.ts b/src/app/api/tickets/mentions/route.ts index ff271d7..c044fd5 100644 --- a/src/app/api/tickets/mentions/route.ts +++ b/src/app/api/tickets/mentions/route.ts @@ -1,13 +1,14 @@ import { NextResponse } from "next/server" - +import { ConvexHttpClient } from "convex/browser" +import { api } from "@/convex/_generated/api" +import type { Id } from "@/convex/_generated/dataModel" +import { env } from "@/lib/env" import { assertAuthenticatedSession } from "@/lib/auth-server" import { DEFAULT_TENANT_ID } from "@/lib/constants" -import { prisma } from "@/lib/prisma" export const runtime = "nodejs" const MAX_RESULTS = 10 -const MAX_SCAN = 60 function normalizeRole(role?: string | null) { return (role ?? "").toLowerCase() @@ -19,10 +20,14 @@ export async function GET(request: Request) { return NextResponse.json({ items: [] }, { status: 401 }) } + const convexUrl = env.NEXT_PUBLIC_CONVEX_URL + if (!convexUrl) { + return NextResponse.json({ items: [] }, { status: 500 }) + } + const normalizedRole = normalizeRole(session.user.role) const isAgentOrAdmin = normalizedRole === "admin" || normalizedRole === "agent" const canLinkOwnTickets = normalizedRole === "collaborator" - if (!isAgentOrAdmin && !canLinkOwnTickets) { return NextResponse.json({ items: [] }, { status: 403 }) } @@ -32,75 +37,65 @@ export async function GET(request: Request) { const rawQuery = url.searchParams.get("q") ?? "" const query = rawQuery.trim() - const whereBase: { - tenantId: string - requesterId?: string - } = { tenantId } + const client = new ConvexHttpClient(convexUrl) - if (!isAgentOrAdmin) { - whereBase.requesterId = session.user.id + // Garantir que o usuário exista no Convex para obter viewerId + let viewerId: string | null = null + try { + const ensured = await client.mutation(api.users.ensureUser, { + tenantId, + name: session.user.name ?? session.user.email, + email: session.user.email, + avatarUrl: session.user.avatarUrl ?? undefined, + role: session.user.role.toUpperCase(), + }) + viewerId = ensured?._id ?? null + } catch (error) { + console.error("[mentions] ensureUser failed", error) + return NextResponse.json({ items: [] }, { status: 500 }) + } + if (!viewerId) { + return NextResponse.json({ items: [] }, { status: 403 }) } - const numericQuery = /^\d+$/.test(query) - - // Fast path for numeric query: exact reference match at DB level - let filtered: any[] = [] - if (numericQuery) { - const ref = Number(query) - const exact = await prisma.ticket.findMany({ - where: { ...whereBase, reference: ref }, - include: { - assignee: { select: { name: true } }, - requester: { select: { name: true } }, - company: { select: { name: true } }, - }, - take: MAX_RESULTS, - }) - filtered = exact - } else { - const tickets = await prisma.ticket.findMany({ - where: whereBase, - include: { - assignee: { select: { name: true } }, - requester: { select: { name: true } }, - company: { select: { name: true } }, - }, - orderBy: { updatedAt: "desc" }, - take: MAX_SCAN, - }) - - const lowered = query.toLowerCase() - filtered = tickets - .filter((ticket) => { - if (!query) return true - const subject = ticket.subject ?? "" - if (subject.toLowerCase().includes(lowered)) return true - const requesterName = ticket.requester?.name ?? "" - if (requesterName.toLowerCase().includes(lowered)) return true - const companyName = ticket.company?.name ?? "" - if (companyName.toLowerCase().includes(lowered)) return true - return false - }) - .slice(0, MAX_RESULTS) + // Pesquisar pelos tickets visíveis ao viewer (assunto, resumo ou #referência) + let tickets: Array<{ + id: string + reference: number + subject: string + status: string + priority: string + requester?: { name?: string | null } | null + assignee?: { name?: string | null } | null + company?: { name?: string | null } | null + updatedAt?: number | null + }> = [] + try { + const res = (await client.query(api.tickets.list, { + tenantId, + viewerId: viewerId as unknown as Id<"users">, + search: query, + limit: 40, + })) as any[] + tickets = Array.isArray(res) ? res : [] + } catch (error) { + console.error("[mentions] tickets.list failed", error) + return NextResponse.json({ items: [] }, { status: 500 }) } const basePath = isAgentOrAdmin ? "/tickets" : "/portal/tickets" - - const items = filtered.map((ticket) => { - const subject = ticket.subject ?? "" - return { - id: ticket.id, - reference: ticket.reference, - subject, - status: ticket.status, - priority: ticket.priority, - requesterName: ticket.requester?.name ?? null, - assigneeName: ticket.assignee?.name ?? null, - companyName: ticket.company?.name ?? null, - url: `${basePath}/${ticket.id}`, - updatedAt: ticket.updatedAt.toISOString(), - } - }) + const items = tickets.slice(0, MAX_RESULTS).map((t) => ({ + id: String(t.id), + reference: Number(t.reference), + subject: String(t.subject ?? ""), + status: String(t.status ?? "PENDING"), + priority: String(t.priority ?? "MEDIUM"), + requesterName: t.requester?.name ?? null, + assigneeName: t.assignee?.name ?? null, + companyName: t.company?.name ?? null, + url: `${basePath}/${String(t.id)}`, + updatedAt: t.updatedAt ? new Date(t.updatedAt).toISOString() : new Date().toISOString(), + })) return NextResponse.json({ items }) }