import { notFound, redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { TicketQueueBanner } from "@/components/admin/ticket-queue-banner"; import { getMinStaffRank } from "@/lib/admin/min-staff-rank"; import { positiveBigInt } from "@/lib/api"; import { activeBanWhere } from "@/lib/bans"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { AdminHelpTicketDetail } from "./admin-help-ticket-detail"; export default async function AdminHelpTicketDetailPage({ params, }: { params: Promise<{ id: string }>; }) { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.TICKETS_VIEW, session.user.rank)) { redirect("/admin"); } const t = await getTranslations("pages.admin.helpTickets"); const { id } = await params; const ticketId = positiveBigInt(id); if (!ticketId) notFound(); const ticket = await prisma.websiteHelpCenterTickets.findUnique({ where: { id: ticketId }, select: { id: true, userId: true, categoryId: true, title: true, content: true, open: true, createdAt: true, updatedAt: true, }, }); if (!ticket) notFound(); const replies = await prisma.websiteHelpCenterTicketReplies.findMany({ where: { ticketId: ticket.id }, orderBy: { id: "asc" }, select: { id: true, userId: true, content: true, createdAt: true, }, }); const authorIds = [ ...new Set([ ...(ticket.userId != null ? [ticket.userId] : []), ...replies.map((r) => r.userId), ]), ]; const [users, category, activeBan] = await Promise.all([ authorIds.length > 0 ? prisma.user.findMany({ where: { id: { in: authorIds } }, select: { id: true, username: true, rank: true, mail: true, }, }) : Promise.resolve([]), ticket.categoryId != null ? prisma.websiteHelpCenterCategories.findUnique({ where: { id: ticket.categoryId }, select: { name: true }, }) : Promise.resolve(null), ticket.userId != null ? prisma.ban.findFirst({ where: { userId: ticket.userId, ...activeBanWhere() }, select: { id: true }, }) : Promise.resolve(null), ]); const userById = new Map(users.map((u) => [u.id, u])); const openerId = ticket.userId; const minStaffRank = await getMinStaffRank(); const messages = [ { key: "opening", userId: openerId ?? 0, username: openerId != null ? (userById.get(openerId)?.username ?? `#${openerId}`) : "—", isStaff: false, content: ticket.content, createdAt: (ticket.createdAt ?? new Date()).toISOString(), }, ...replies.map((r) => { const user = userById.get(r.userId); const isStaff = r.userId !== openerId && (user?.rank ?? 0) >= minStaffRank; return { key: String(r.id), userId: r.userId, username: user?.username ?? `#${r.userId}`, isStaff, content: r.content, createdAt: (r.createdAt ?? new Date()).toISOString(), }; }), ]; const creator = openerId != null ? userById.get(openerId) : undefined; const canEdit = canAccess(permissions, PERMS.TICKETS_EDIT, session.user.rank); const canLiftBan = canAccess(permissions, PERMS.USERS_BAN, session.user.rank); const isBanAppeal = /ban\s*appeal/i.test(ticket.title) || /ban\s*appeal/i.test(category?.name ?? ""); return (