"use client"; import React, { useState, useEffect } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Loader2, ChevronDown, ChevronRight, Download, FileIcon, XCircle, AlertCircle, } from "lucide-react"; import { fetchVendorDocumentDetail, cancelVendorFile, downloadVendorFile, } from "@/lib/swp/vendor-actions"; import type { DocumentListItem, DocumentDetail } from "@/lib/swp/document-service"; import { toast } from "sonner"; interface SwpDocumentDetailDialogProps { open: boolean; onOpenChange: (open: boolean) => void; document: DocumentListItem | null; projNo: string; vendorCode: string; userId: string; } export function SwpDocumentDetailDialog({ open, onOpenChange, document, projNo, }: SwpDocumentDetailDialogProps) { const [detail, setDetail] = useState(null); const [isLoading, setIsLoading] = useState(false); const [expandedRevisions, setExpandedRevisions] = useState>(new Set()); const [expandedActivities, setExpandedActivities] = useState>(new Set()); const [isAllExpanded, setIsAllExpanded] = useState(true); // 기본값 true // 문서 상세 로드 useEffect(() => { if (open && document) { loadDocumentDetail(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, document?.DOC_NO]); const loadDocumentDetail = async () => { if (!document) return; setIsLoading(true); try { const detailData = await fetchVendorDocumentDetail(projNo, document.DOC_NO); setDetail(detailData); // 모든 리비전 자동 펼치기 const allRevKeys = new Set(); const allActKeys = new Set(); detailData.revisions.forEach((revision) => { const revKey = `${revision.revNo}|${revision.revSeq}`; allRevKeys.add(revKey); // 모든 액티비티도 자동 펼치기 revision.activities.forEach((activity) => { const actKey = `${revKey}|${activity.actvNo}`; allActKeys.add(actKey); }); }); setExpandedRevisions(allRevKeys); setExpandedActivities(allActKeys); setIsAllExpanded(true); } catch (error) { console.error("문서 상세 조회 실패:", error); toast.error("문서 상세 정보를 불러오는데 실패했습니다"); } finally { setIsLoading(false); } }; const toggleRevision = (revKey: string) => { setExpandedRevisions((prev) => { const newSet = new Set(prev); if (newSet.has(revKey)) { newSet.delete(revKey); } else { newSet.add(revKey); } return newSet; }); }; const toggleActivity = (actKey: string) => { setExpandedActivities((prev) => { const newSet = new Set(prev); if (newSet.has(actKey)) { newSet.delete(actKey); } else { newSet.add(actKey); } return newSet; }); }; // 일괄 열기/닫기 const handleToggleAll = () => { if (!detail) return; if (isAllExpanded) { // 모두 닫기 setExpandedRevisions(new Set()); setExpandedActivities(new Set()); setIsAllExpanded(false); } else { // 모두 열기 const allRevKeys = new Set(); const allActKeys = new Set(); detail.revisions.forEach((revision) => { const revKey = `${revision.revNo}|${revision.revSeq}`; allRevKeys.add(revKey); revision.activities.forEach((activity) => { const actKey = `${revKey}|${activity.actvNo}`; allActKeys.add(actKey); }); }); setExpandedRevisions(allRevKeys); setExpandedActivities(allActKeys); setIsAllExpanded(true); } }; const handleCancelFile = async (boxSeq: string, actvSeq: string, fileName: string) => { try { await cancelVendorFile(boxSeq, actvSeq); toast.success(`파일 취소 완료: ${fileName}`); // 문서 상세 재로드 await loadDocumentDetail(); } catch (error) { console.error("파일 취소 실패:", error); toast.error("파일 취소에 실패했습니다"); } }; const handleDownloadFile = async (fileName: string, ownDocNo: string) => { try { toast.info("파일 다운로드 중..."); const result = await downloadVendorFile(projNo, ownDocNo, fileName); if (!result.success || !result.data) { toast.error(result.error || "파일 다운로드 실패"); return; } // Blob 생성 및 다운로드 const blob = new Blob([Buffer.from(result.data)], { type: result.mimeType }); const url = URL.createObjectURL(blob); const link = window.document.createElement("a"); link.href = url; link.download = result.fileName || fileName; window.document.body.appendChild(link); link.click(); window.document.body.removeChild(link); URL.revokeObjectURL(url); toast.success(`파일 다운로드 완료: ${fileName}`); } catch (error) { console.error("파일 다운로드 실패:", error); toast.error("파일 다운로드에 실패했습니다"); } }; return ( 문서 상세 {document && ( {document.DOC_NO} - {document.DOC_TITLE} )} {document && (
{/* 문서 정보 */}
프로젝트:
{document.PROJ_NO}
{document.PROJ_NM && (
{document.PROJ_NM}
)}
패키지:
{document.PKG_NO || "-"}
업체:
{document.CPY_NM || "-"}
{document.VNDR_CD && (
{document.VNDR_CD}
)}
최신 리비전:
{document.LTST_REV_NO || "-"}
{/* 리비전 및 액티비티 트리 */} {isLoading ? (
문서 상세 로딩 중...
) : detail && detail.revisions.length > 0 ? (
{/* 일괄 열기/닫기 버튼 */}
{detail.revisions.map((revision) => { const revKey = `${revision.revNo}|${revision.revSeq}`; const isRevExpanded = expandedRevisions.has(revKey); return (
{/* 리비전 헤더 */}
toggleRevision(revKey)} >
{isRevExpanded ? ( ) : ( )} REV {revision.revNo} {revision.stage} {revision.activities.length}개 액티비티 / {revision.totalFiles}개 파일
{/* 액티비티 목록 */} {isRevExpanded && (
{revision.activities.map((activity) => { const actKey = `${revKey}|${activity.actvNo}`; const isActExpanded = expandedActivities.has(actKey); // Activity 타입에 따른 색상 const activityColor = activity.type === "Receive" ? "bg-blue-100 text-blue-800" : activity.type === "Send" ? "bg-green-100 text-green-800" : "bg-purple-100 text-purple-800"; return (
{/* 액티비티 헤더 */}
toggleActivity(actKey)} >
{isActExpanded ? ( ) : ( )} {activity.type} {activity.actvNo} {activity.toFrom} {activity.files.length}개 파일
{/* 파일 목록 */} {isActExpanded && (
{activity.files.map((file, idx) => (
{file.fileNm} {file.fileSz && ( ({formatFileSize(file.fileSz)}) )} {file.stat && ( {file.statNm || file.stat} )}
{file.canCancel && file.boxSeq && file.actvSeq && ( )}
))}
)}
); })}
)}
); })}
) : (

리비전 정보가 없습니다

)}
)}
); } function formatFileSize(sizeStr: string): string { const bytes = parseInt(sizeStr, 10); if (isNaN(bytes)) return sizeStr; const kb = bytes / 1024; const mb = kb / 1024; return mb >= 1 ? `${mb.toFixed(2)} MB` : `${kb.toFixed(2)} KB`; }