"use client"; import React, { useState, useEffect, useMemo } 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, Download, FileIcon, AlertCircle, } from "lucide-react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { fetchGetRevTreeCompleteList, parseRevisionTree, fetchGetActivityFileList, type ActivityFileApiResponse, } from "@/lib/swp/api-client"; import { downloadVendorFile } from "@/lib/swp/vendor-actions"; import type { DocumentListItem } from "@/lib/swp/document-service"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { formatSwpDateShort, formatFileSize } from "@/lib/swp/utils"; interface SwpDocumentDetailDialogProps { open: boolean; onOpenChange: (open: boolean) => void; document: DocumentListItem | null; projNo: string; vendorCode: string; userId: string; } // Activity 행 데이터 interface ActivityRow { revNo: string; revSeq: string; stage: string; actvNo: string; inOut: "IN" | "OUT"; statusCode: string; statusName: string; transmittalNo: string; refActivityNo: string; createDate: string; createEmpNo: string; } export function SwpDocumentDetailDialog({ open, onOpenChange, document, projNo, }: SwpDocumentDetailDialogProps) { const [activities, setActivities] = useState([]); const [isLoading, setIsLoading] = useState(false); const [selectedActivity, setSelectedActivity] = useState(null); const [activityFiles, setActivityFiles] = useState([]); const [isLoadingFiles, setIsLoadingFiles] = useState(false); // 문서 상세 로드 useEffect(() => { if (open && document) { loadDocumentDetail(); } else { // 다이얼로그 닫힐 때 초기화 setSelectedActivity(null); setActivityFiles([]); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, document?.DOC_NO]); const loadDocumentDetail = async () => { if (!document) return; setIsLoading(true); setSelectedActivity(null); setActivityFiles([]); try { // GetRevTreeCompleteList 호출 const tree = await fetchGetRevTreeCompleteList({ proj_no: projNo, doc_no: document.DOC_NO, }); const parsed = await parseRevisionTree(tree); // Activity를 flat한 배열로 변환 (테이블용) const flatActivities: ActivityRow[] = []; parsed.revisions.forEach((rev) => { rev.activities.forEach((act) => { flatActivities.push({ revNo: rev.revNo, revSeq: rev.revSeq, stage: rev.stage, actvNo: act.actvNo, inOut: act.inOut, statusCode: act.statusCode, statusName: act.statusName, transmittalNo: act.transmittalNo, refActivityNo: act.refActivityNo, createDate: act.createDate, createEmpNo: act.createEmpNo, }); }); }); setActivities(flatActivities); } catch (error) { console.error("Failed to retrieve document details:", error); toast.error("Failed to load document revision tree"); } finally { setIsLoading(false); } }; // Activity 선택 및 파일 로드 const handleActivityClick = async (activity: ActivityRow) => { if (selectedActivity?.actvNo === activity.actvNo) { // 같은 Activity 클릭 시 토글 setSelectedActivity(null); setActivityFiles([]); return; } setSelectedActivity(activity); setIsLoadingFiles(true); setActivityFiles([]); try { // GetActivityFileList 호출 const files = await fetchGetActivityFileList({ proj_no: projNo, doc_no: document?.DOC_NO || "", rev_seq: activity.revSeq, }); // 해당 Activity의 파일만 필터링 const activitySpecificFiles = files.filter( (f) => f.ACTV_NO === activity.actvNo ); setActivityFiles(activitySpecificFiles); } catch (error) { console.error("Failed to retrieve file list:", error); toast.error("Failed to load file list"); } finally { setIsLoadingFiles(false); } }; const handleDownloadFile = async (fileName: string, ownDocNo: string) => { try { toast.info("Downloading file..."); const result = await downloadVendorFile(projNo, ownDocNo, fileName); if (!result.success || !result.data) { toast.error(result.error || "File download failed"); 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(`File download complete: ${fileName}`); } catch (error) { console.error("File download failed:", error); toast.error("Failed to download file"); } }; // Revision별로 Activity 그룹핑 및 정렬 (rowspan용) const groupedActivities = useMemo(() => { // 1. REV 내림차순, createDate 내림차순으로 정렬 const sortedActivities = [...activities].sort((a, b) => { // REV 비교 (내림차순) const revCompare = b.revNo.localeCompare(a.revNo); if (revCompare !== 0) return revCompare; // 같은 REV 내에서는 createDate 내림차순 return b.createDate.localeCompare(a.createDate); }); // 2. 그룹핑 const groups: Map = new Map(); sortedActivities.forEach((activity) => { const key = `${activity.revNo}|${activity.stage}`; if (!groups.has(key)) { groups.set(key, []); } groups.get(key)!.push(activity); }); return groups; }, [activities]); return ( Document Revision History {document && ( {document.DOC_NO} - {document.DOC_TITLE} )} {document && (
{/* 문서 정보 */}
Project: {document.PROJ_NO} {document.PROJ_NM && ( ({document.PROJ_NM}) )}
Package: {document.PKG_NO || "-"}
Vendor: {document.CPY_NM || "-"} {document.VNDR_CD && ( ({document.VNDR_CD}) )}
Latest Revision: {document.LTST_REV_NO || "-"}
Total Activity: {activities.length}
{/* Activity 테이블 */} {isLoading ? (
Loading revision tree...
) : activities.length > 0 ? ( <> {/* Activity 테이블 (위) */}
Rev Stage IN/OUT Status Transmittal No Activity No Ref Activity Modified By {Array.from(groupedActivities.entries()).map(([key, groupActivities]) => { const [revNo, stage] = key.split("|"); return groupActivities.map((activity, idx) => ( handleActivityClick(activity)} > {/* Rev 컬럼 (첫 행만 표시, rowspan) */} {idx === 0 && ( {revNo} )} {/* Stage 컬럼 (첫 행만 표시, rowspan) */} {idx === 0 && ( {stage} )} {activity.inOut}
{activity.statusName}
{activity.transmittalNo || "-"} {activity.actvNo} {activity.refActivityNo || "-"} {formatSwpDateShort(activity.createDate)} {activity.createEmpNo}
)); })}
{/* 파일 목록 (아래) */}

File List

{selectedActivity ? (

Activity: {selectedActivity.actvNo} / Rev {selectedActivity.revNo} ({selectedActivity.stage}) / {selectedActivity.inOut}

) : (

Select an Activity to view the file list

)}
{selectedActivity ? ( isLoadingFiles ? (
Loading files...
) : activityFiles.length > 0 ? ( File Name Size Date Download {activityFiles.map((file) => ( {file.FILE_NM} {file.FILE_SZ ? formatFileSize(file.FILE_SZ) : "-"} {file.CRTE_DTM ? formatSwpDateShort(file.CRTE_DTM) : "-"} ))}
) : (

No files

) ) : (

Please select an Activity

)}
) : (

No Activity information

)}
)}
); }