'use client'; import React, { useState, useEffect } from 'react'; import { ChevronLeft, Clock, User, MessageSquare, Paperclip, Send, CheckCircle2, ArrowRight } from 'lucide-react'; import Link from 'next/link'; import ticketService from '@/services/ticketService'; import type { IssueTicket, TicketComment } from '@/types'; const statusFlow = ['OPEN', 'ASSIGNED', 'IN_PROGRESS', 'RESOLVED', 'CLOSED']; const statusColor: Record = { OPEN: 'bg-status-open-bg text-status-open-text', ASSIGNED: 'bg-status-pending-bg text-status-pending-text', IN_PROGRESS: 'bg-status-pending-bg text-status-pending-text', RESOLVED: 'bg-status-resolved-bg text-status-resolved-text', CLOSED: 'bg-gray-100 text-text-muted', }; const statusLabel: Record = { OPEN: 'Open', ASSIGNED: 'Assigned', IN_PROGRESS: 'In Progress', RESOLVED: 'Resolved', CLOSED: 'Closed', }; const categoryLabel: Record = { BUG: '버그 리포트', FEATURE: '기능 요청', SUPPORT: '기술 지원', QA: 'QA 이슈', VACATION: '휴가 신청', EXPENSE: '비용 청구', EQUIPMENT_REQUEST: '장비 요청', GENERAL: '일반 문의', }; const priorityColor: Record = { LOW: 'bg-gray-100 text-gray-600', NORMAL: 'bg-status-open-bg text-status-open-text', HIGH: 'bg-status-pending-bg text-status-pending-text', CRITICAL: 'bg-status-critical-bg text-status-critical-text', }; export default function IssueDetailPage({ params }: { params: Promise<{ id: string }> }) { const resolvedParams = React.use(params); const ticketId = parseInt(resolvedParams.id); const [ticket, setTicket] = useState(null); const [comments, setComments] = useState([]); const [newComment, setNewComment] = useState(''); const [status, setStatus] = useState('OPEN'); const [loading, setLoading] = useState(true); useEffect(() => { Promise.all([ ticketService.getTicket(ticketId), ticketService.getComments(ticketId), ]).then(([ticketRes, commentsRes]) => { setTicket(ticketRes.data); setComments(commentsRes.data); setStatus(ticketRes.data.status); }).catch(console.error).finally(() => setLoading(false)); }, [ticketId]); const currentIdx = statusFlow.indexOf(status); const canAdvance = currentIdx < statusFlow.length - 1; const isClosed = status === 'CLOSED'; const handleAdvance = async () => { if (!canAdvance) return; const nextStatus = statusFlow[currentIdx + 1]; try { await ticketService.updateStatus(ticketId, nextStatus); setStatus(nextStatus); } catch (err: unknown) { alert((err as { message?: string })?.message || '상태 변경 실패'); } }; const handlePostComment = async () => { if (!newComment.trim()) return; try { const res = await ticketService.addComment(ticketId, newComment.trim()); setComments([...comments, res.data]); setNewComment(''); } catch { alert('댓글 등록 실패'); } }; if (loading) return
Loading...
; if (!ticket) return
Ticket not found
; return (
Back to Issues
{statusLabel[status]} {!isClosed && ( )}

{ticket.title}

Project

{ticket.projectName || '해당 없음'}

Category

{categoryLabel[ticket.category] || ticket.category}

Priority {ticket.priority}
Due

{ticket.dueDate ? <>{new Date(ticket.dueDate).toLocaleDateString('ko-KR')} : '-'}

Description

{ticket.description}

{ticket.category === 'VACATION' && (

휴가 신청

세부 일정은 description을 확인하세요.

)} {ticket.category === 'EXPENSE' && (

비용 청구

)} {ticket.category === 'EQUIPMENT_REQUEST' && (

장비 요청

)}

Timeline ({comments.length})

{comments.map((c) => (
{(c.userName || 'U')[0]}
{c.userName || 'User'} {new Date(c.createdAt).toLocaleString('ko-KR')}

{c.commentText}

{c.attachedFileUrl && (
{c.attachedFileUrl.split('/').pop()}
)}
))}
{!isClosed && (
A