import { AlertCircle, CheckCircle2, Clock, Loader2, Ticket } from 'lucide-react' import Link from 'next/link' import { redirect } from 'next/navigation' import { Badge } from '@/components/ui/badge' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { canAccess, getAdminContext, PERMS } from '@/lib/permissions' import { prisma } from '@/lib/prisma' const STATUS_CONFIG = { open: { label: 'Open', icon: AlertCircle, class: 'bg-blue-500/10 text-blue-500' }, in_progress: { label: 'In Progress', icon: Loader2, class: 'bg-yellow-500/10 text-yellow-500' }, waiting: { label: 'Waiting', icon: Clock, class: 'bg-orange-500/10 text-orange-500' }, closed: { label: 'Closed', icon: CheckCircle2, class: 'bg-muted text-muted-foreground' }, } as const const PRIORITY_COLORS = { low: '', normal: 'text-blue-500', high: 'text-orange-500', urgent: 'text-red-500 font-bold', } interface PageProps { searchParams: Promise<{ status?: string; page?: string }> } export default async function AdminTicketsPage({ searchParams }: PageProps) { const { session, permissions } = await getAdminContext() if (!canAccess(permissions, PERMS.TICKETS_VIEW, session.user.rank)) { redirect('/admin') } const sp = await searchParams const statusFilter = sp.status || 'active' const page = Math.max(1, Number(sp.page) || 1) const perPage = 20 const where = statusFilter === 'active' ? { status: { not: 'closed' } } : statusFilter === 'all' ? {} : { status: statusFilter } const [tickets, total, stats] = await Promise.all([ prisma.websiteTicket.findMany({ where, orderBy: [{ createdAt: 'desc' }], skip: (page - 1) * perPage, take: perPage, include: { creator: { select: { id: true, username: true } }, assignee: { select: { id: true, username: true } }, _count: { select: { messages: true } }, }, }), prisma.websiteTicket.count({ where }), Promise.all([ prisma.websiteTicket.count({ where: { status: 'open' } }), prisma.websiteTicket.count({ where: { status: 'in_progress' } }), prisma.websiteTicket.count({ where: { status: 'waiting' } }), prisma.websiteTicket.count({ where: { status: 'closed' } }), ]), ]) const [openCount, inProgressCount, waitingCount, closedCount] = stats const totalPages = Math.ceil(total / perPage) return (

Support Tickets

Manage user support requests

{/* Stats */}
Open
{openCount}
In Progress
{inProgressCount}
Waiting
{waitingCount}
Closed
{closedCount}
{/* Filter tabs */}
{[ { key: 'active', label: 'Active' }, { key: 'open', label: 'Open' }, { key: 'in_progress', label: 'In Progress' }, { key: 'waiting', label: 'Waiting' }, { key: 'closed', label: 'Closed' }, { key: 'all', label: 'All' }, ].map(({ key, label }) => ( {label} ))}
{/* Ticket list */}
Ticket Status Priority Assigned Date
{tickets.length === 0 ? (
No tickets found.
) : ( tickets.map((ticket) => { const status = STATUS_CONFIG[ticket.status as keyof typeof STATUS_CONFIG] ?? STATUS_CONFIG.open const StatusIcon = status.icon return (
#{ticket.id} {ticket.subject}
by {ticket.creator.username} · {ticket._count.messages} msg ·{' '} {ticket.category}
{status.label} {ticket.priority} {ticket.assignee?.username ?? '—'} {new Date(ticket.createdAt).toLocaleDateString()} ) }) )}
{/* Pagination */} {totalPages > 1 && (
{page > 1 && ( Previous )} Page {page} of {totalPages} {page < totalPages && ( Next )}
)}
) }