'use client'; import { useEffect, useState, useRef } from 'react'; import { useParams, useRouter } from 'next/navigation'; import api from '@/lib/api'; interface Attachment { id: string; fileName: string; originalName: string; mimeType: string; sizeBytes: number; uploadedBy: string; createdAt: string; } interface Comment { id: string; authorName: string; authorType: string; content: string; createdAt: string; attachments: Attachment[]; } interface Ticket { id: string; tenantId: string; tenantName: string; tenantSlug: string; subject: string; description: string; category: string; priority: string; status: string; assignedToId: string | null; assignee: { id: string; firstName: string; lastName: string } | null; comments: Comment[]; attachments: Attachment[]; createdAt: string; updatedAt: string; } const statusOptions = ['open', 'in_progress', 'waiting_tenant', 'resolved', 'closed']; function formatBytes(bytes: number) { if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; } export default function TicketDetailPage() { const params = useParams(); const router = useRouter(); const ticketId = params.id as string; const [ticket, setTicket] = useState(null); const [loading, setLoading] = useState(true); const [comment, setComment] = useState(''); const [submitting, setSubmitting] = useState(false); const [uploading, setUploading] = useState(false); const [selectedFiles, setSelectedFiles] = useState([]); const fileInputRef = useRef(null); useEffect(() => { loadTicket(); }, [ticketId]); async function loadTicket() { try { const res = await api.get(`/support/tickets/${ticketId}`); setTicket(res.data.data); } catch { // ticket not found } finally { setLoading(false); } } async function handleAddComment() { if (!comment.trim() && selectedFiles.length === 0) return; setSubmitting(true); try { if (comment.trim()) { await api.post(`/support/tickets/${ticketId}/comments`, { content: comment }); } if (selectedFiles.length > 0) { const formData = new FormData(); selectedFiles.forEach((f) => formData.append('files', f)); await api.post(`/support/tickets/${ticketId}/attachments`, formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); } setComment(''); setSelectedFiles([]); loadTicket(); } catch (err) { console.error('Failed to submit:', err); } finally { setSubmitting(false); } } async function handleStatusChange(newStatus: string) { try { await api.patch(`/support/tickets/${ticketId}`, { status: newStatus }); loadTicket(); } catch (err) { console.error('Failed to update status:', err); } } async function handleAssign() { try { await api.patch(`/support/tickets/${ticketId}/assign`, { adminId: 'self' }); loadTicket(); } catch (err) { console.error('Failed to assign:', err); } } async function handleUploadFiles() { if (selectedFiles.length === 0) return; setUploading(true); try { const formData = new FormData(); selectedFiles.forEach((f) => formData.append('files', f)); await api.post(`/support/tickets/${ticketId}/attachments`, formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); setSelectedFiles([]); loadTicket(); } catch (err) { console.error('Failed to upload:', err); } finally { setUploading(false); } } async function handleDeleteAttachment(attachmentId: string) { try { await api.delete(`/support/tickets/${ticketId}/attachments/${attachmentId}`); loadTicket(); } catch (err) { console.error('Failed to delete attachment:', err); } } if (loading) return
Loading...
; if (!ticket) return
Ticket not found
; const ADMIN_API_URL = process.env.NEXT_PUBLIC_ADMIN_API_URL || 'http://localhost:3004/api'; return (
{/* Main content */}

{ticket.subject}

{ticket.description}

Created {new Date(ticket.createdAt).toLocaleString()} by {ticket.tenantName}
{/* Ticket-level attachments */} {ticket.attachments.length > 0 && (

Attachments

{ticket.attachments.map((a) => (
{a.mimeType.startsWith('image/') ? ( ) : ( )} {a.originalName} ({formatBytes(a.sizeBytes)})
))}
)} {/* Comments thread */}

Conversation ({ticket.comments.length})

{ticket.comments.map((c) => (
{c.authorName} {c.authorType === 'super_admin' ? 'Admin' : 'Tenant'}
{new Date(c.createdAt).toLocaleString()}

{c.content}

{/* Comment attachments */} {c.attachments.length > 0 && (
{c.attachments.map((a) => ( {a.originalName} ({formatBytes(a.sizeBytes)}) ))}
)}
))} {/* Add comment */}