108 lines
3.7 KiB
TypeScript
108 lines
3.7 KiB
TypeScript
import { NextResponse } from "next/server"
|
|
import { ConvexHttpClient } from "convex/browser"
|
|
import { api } from "@/convex/_generated/api"
|
|
import type { Id } from "@/convex/_generated/dataModel"
|
|
import { assertAuthenticatedSession } from "@/lib/auth-server"
|
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
|
import { requireConvexUrl } from "@/server/convex-client"
|
|
|
|
export const runtime = "nodejs"
|
|
|
|
const MAX_RESULTS = 10
|
|
|
|
type TicketRecord = {
|
|
id: string | number
|
|
reference: string | number
|
|
subject?: string | null
|
|
status?: string | null
|
|
priority?: string | null
|
|
requester?: { name?: string | null } | null
|
|
assignee?: { name?: string | null } | null
|
|
company?: { name?: string | null } | null
|
|
updatedAt?: number | null
|
|
}
|
|
|
|
function isTicketRecord(value: unknown): value is TicketRecord {
|
|
if (!value || typeof value !== "object") {
|
|
return false
|
|
}
|
|
const record = value as Record<string, unknown>
|
|
const hasValidId = typeof record.id === "string" || typeof record.id === "number"
|
|
const hasValidReference = typeof record.reference === "string" || typeof record.reference === "number"
|
|
return hasValidId && hasValidReference
|
|
}
|
|
|
|
function normalizeRole(role?: string | null) {
|
|
return (role ?? "").toLowerCase()
|
|
}
|
|
|
|
export async function GET(request: Request) {
|
|
const session = await assertAuthenticatedSession()
|
|
if (!session) {
|
|
return NextResponse.json({ items: [] }, { status: 401 })
|
|
}
|
|
|
|
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 })
|
|
}
|
|
|
|
const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
|
|
const url = new URL(request.url)
|
|
const rawQuery = url.searchParams.get("q") ?? ""
|
|
const query = rawQuery.trim()
|
|
|
|
const client = new ConvexHttpClient(requireConvexUrl())
|
|
|
|
// 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 })
|
|
}
|
|
|
|
// Pesquisar pelos tickets visíveis ao viewer (assunto, resumo ou #referência)
|
|
let tickets: TicketRecord[] = []
|
|
try {
|
|
const res = await client.query(api.tickets.list, {
|
|
tenantId,
|
|
viewerId: viewerId as unknown as Id<"users">,
|
|
search: query,
|
|
limit: 40,
|
|
})
|
|
tickets = Array.isArray(res) ? res.filter(isTicketRecord) : []
|
|
} catch (error) {
|
|
console.error("[mentions] tickets.list failed", error)
|
|
return NextResponse.json({ items: [] }, { status: 500 })
|
|
}
|
|
|
|
const basePath = isAgentOrAdmin ? "/tickets" : "/portal/tickets"
|
|
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 })
|
|
}
|