feat: link tickets in comments and align admin sidebars

This commit is contained in:
Esdras Renan 2025-10-23 00:46:50 -03:00
parent c35eb673d3
commit b0f57009ac
15 changed files with 1606 additions and 424 deletions

View file

@ -0,0 +1,96 @@
import { NextResponse } from "next/server"
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()
}
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 whereBase: {
tenantId: string
requesterId?: string
} = { tenantId }
if (!isAgentOrAdmin) {
whereBase.requesterId = session.user.id
}
const numericQuery = /^\d+$/.test(query)
const take = numericQuery ? MAX_RESULTS : MAX_SCAN
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,
})
const lowered = query.toLowerCase()
const filtered = tickets
.filter((ticket) => {
if (!query) return true
const referenceMatch = String(ticket.reference).includes(query)
if (referenceMatch) 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)
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(),
}
})
return NextResponse.json({ items })
}