'use client' import { ArrowLeft, Calendar, Globe, Loader2, Mail, Send, Shield, User } from 'lucide-react' import Link from 'next/link' import { useEffect, useRef, useState } from 'react' import { adminReplyTicket, assignTicket, updateTicketPriority, updateTicketStatus, } from '@/actions/tickets' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Textarea } from '@/components/ui/textarea' import { useServerAction } from '@/hooks/use-server-action' import { TICKET_PRIORITIES, TICKET_STATUSES } from '@/lib/validators/ticket' interface TicketMessage { id: number message: string isStaff: boolean createdAt: string user: { id: number; username: string; look: string; rank: number } } interface TicketInfo { id: number subject: string category: string priority: string status: string createdAt: string closedAt: string | null creator: { id: number username: string look: string rank: number mail: string ipRegister: string accountCreated: number } assignee: { id: number; username: string } | null } interface StaffMember { id: number username: string } const STATUS_COLORS: Record = { open: 'bg-[var(--admin-info-subtle)] text-[var(--admin-info)]', in_progress: 'bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)]', waiting: 'bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)]', closed: 'bg-muted text-muted-foreground', } const PRIORITY_COLORS: Record = { low: 'text-muted-foreground', normal: 'text-[var(--admin-info)]', high: 'text-[var(--admin-warning)]', urgent: 'text-[var(--admin-error)]', } export function AdminTicketDetail({ ticket, messages: initialMessages, staffMembers, currentUserId, canEdit, }: { ticket: TicketInfo messages: TicketMessage[] staffMembers: StaffMember[] currentUserId: number canEdit: boolean }) { const [messages] = useState(initialMessages) const [reply, setReply] = useState('') const { run, isPending } = useServerAction() const messagesEndRef = useRef(null) // biome-ignore lint/correctness/useExhaustiveDependencies: explicitly chosen here, see surrounding code useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [messages]) function handleReply() { if (!reply.trim() || isPending) return run(() => adminReplyTicket({ ticketId: ticket.id, message: reply.trim() }), { successMessage: 'Reply sent!', onSuccess: () => setReply(''), }) } function handleStatusChange(status: string) { run( () => updateTicketStatus({ ticketId: ticket.id, status: status as (typeof TICKET_STATUSES)[number], }), { successMessage: `Status changed to ${status}`, }, ) } function handleAssign(assigneeId: string) { const id = assigneeId === 'unassign' ? null : Number(assigneeId) run(() => assignTicket({ ticketId: ticket.id, assigneeId: id }), { successMessage: id ? 'Ticket assigned' : 'Ticket unassigned', }) } function handlePriorityChange(priority: string) { run( () => updateTicketPriority({ ticketId: ticket.id, priority: priority as (typeof TICKET_PRIORITIES)[number], }), { successMessage: `Priority changed to ${priority}`, }, ) } const isClosed = ticket.status === 'closed' return (
{/* Header */}
#{ticket.id} {ticket.status.replace('_', ' ')} {ticket.priority} {ticket.category}

{ticket.subject}

{/* Messages */}
{messages.map((msg) => (
{msg.isStaff ? ( ) : ( )}
{msg.user.username} {msg.isStaff && ' (Staff)'} {new Date(msg.createdAt).toLocaleString()}

{msg.message}

))}
{/* Reply */} {canEdit && !isClosed && (