"use client" import * as React from "react" import { Eye, Building2, User, Calendar, CheckCircle2, Clock, MessageSquare, Award, FileText, Paperclip, Download, File, BarChart3, ChevronDown, ChevronRight } from "lucide-react" import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Separator } from "@/components/ui/separator" import { Skeleton } from "@/components/ui/skeleton" import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { PeriodicEvaluationView } from "@/db/schema" import { getVendorSubmissionDetails, type VendorSubmissionDetail } from "../vendor-submission-service" import { formatFileSize, getFileInfo } from "@/lib/file-download" interface VendorSubmissionDialogProps { open: boolean onOpenChange: (open: boolean) => void evaluation: PeriodicEvaluationView | null } // 상태별 배지 색상 const getSubmissionStatusBadge = (status: string) => { switch (status) { case "submitted": return 제출완료 case "draft": return 임시저장 case "reviewed": return 검토완료 default: return {status} } } // 진행률 계산 const getProgressPercentage = (completed: number, total: number) => { if (total === 0) return 0 return Math.round((completed / total) * 100) } export function VendorSubmissionDialog({ open, onOpenChange, evaluation, }: VendorSubmissionDialogProps) { const [isLoading, setIsLoading] = React.useState(false) const [submissionDetails, setSubmissionDetails] = React.useState(null) const [expandedGeneralItems, setExpandedGeneralItems] = React.useState>(new Set()) const [expandedEsgItems, setExpandedEsgItems] = React.useState>(new Set()) // 첨부파일 다운로드 핸들러 const handleDownloadAttachment = async (filePath: string, fileName: string) => { try { const { downloadFile } = await import('@/lib/file-download') await downloadFile(filePath, fileName, { action: 'download', showToast: true, showSuccessToast: true, onError: (error) => { console.error("파일 다운로드 실패:", error) }, }) } catch (error) { console.error("파일 다운로드 실패:", error) } } // 일반평가 항목 토글 const toggleGeneralItem = (itemId: number) => { const newExpanded = new Set(expandedGeneralItems) if (newExpanded.has(itemId)) { newExpanded.delete(itemId) } else { newExpanded.add(itemId) } setExpandedGeneralItems(newExpanded) } // ESG 평가 항목 토글 const toggleEsgItem = (itemId: number) => { const newExpanded = new Set(expandedEsgItems) if (newExpanded.has(itemId)) { newExpanded.delete(itemId) } else { newExpanded.add(itemId) } setExpandedEsgItems(newExpanded) } // 제출 상세 정보 로드 React.useEffect(() => { if (open && evaluation?.id) { const loadSubmissionDetails = async () => { try { setIsLoading(true) const details = await getVendorSubmissionDetails(evaluation.id) setSubmissionDetails(details) } catch (error) { console.error("Failed to load vendor submission details:", error) } finally { setIsLoading(false) } } loadSubmissionDetails() } }, [open, evaluation?.id]) // 다이얼로그 닫을 때 데이터 리셋 React.useEffect(() => { if (!open) { setSubmissionDetails(null) setExpandedGeneralItems(new Set()) setExpandedEsgItems(new Set()) } }, [open]) if (!evaluation) return null return ( {/* 고정 헤더 */} 협력업체 제출 상세 {/* 평가 기본 정보 */} 평가 정보
{/* 협력업체 */}
협력업체: {evaluation.vendorName} ({evaluation.vendorCode})
{/* 평가년도 */}
년도: {evaluation.evaluationYear}년
{/* 구분 */}
구분: {evaluation.division === "PLANT" ? "해양" : "조선"}
{/* 진행상태 */}
상태: {evaluation.status}
{/* 스크롤 가능한 컨텐츠 영역 */}
{isLoading ? (
) : submissionDetails ? (
{/* 제출 정보 요약 */} 제출 정보
제출 상태
{getSubmissionStatusBadge(submissionDetails.submissionStatus)}
제출일
{submissionDetails.submittedAt ? new Date(submissionDetails.submittedAt).toLocaleDateString('ko-KR') : "-" }
{/*
ESG 평균 점수
{submissionDetails.averageEsgScore ? `${submissionDetails.averageEsgScore.toFixed(1)}점` : "-" }
*/} {/*
검토자
{submissionDetails.reviewedBy || "-"}
*/}
{/* 진행률 표시 */} {/*
일반평가 진행률 {submissionDetails.completedGeneralItems}/{submissionDetails.totalGeneralItems} ({getProgressPercentage(submissionDetails.completedGeneralItems, submissionDetails.totalGeneralItems)}%)
ESG 평가 진행률 {submissionDetails.completedEsgItems}/{submissionDetails.totalEsgItems} ({getProgressPercentage(submissionDetails.completedEsgItems, submissionDetails.totalEsgItems)}%)
*/} {/* 탭으로 일반평가와 ESG 평가 구분 */} 일반평가 ({submissionDetails.generalEvaluations.length}개) {submissionDetails.vendor.country === "KR" && ( ESG 평가 ({submissionDetails.esgEvaluations.length}개) )} {/* 일반평가 탭 */} {submissionDetails.generalEvaluations.length > 0 ? (
{submissionDetails.generalEvaluations.map((item) => ( toggleGeneralItem(item.id)} >
{expandedGeneralItems.has(item.id) ? ( ) : ( )} {item.serialNumber}. {item.category}
{item.response ? ( 응답완료 ) : ( 미응답 )} {item.response?.hasAttachments && ( 첨부파일 )}
평가 항목
{item.inspectionItem}
{item.remarks && (
비고
{item.remarks}
)}
{item.response ? (
협력업체 응답
{item.response.responseText || "응답 내용이 없습니다."}
{/* 첨부파일 */} {item.response.attachments.length > 0 && (
첨부파일
{item.response.attachments.map((attachment) => { const fileInfo = getFileInfo(attachment.originalFileName) return (
{fileInfo.icon} {attachment.originalFileName}
{attachment.originalFileName}
크기: {formatFileSize(attachment.fileSize)}
타입: {fileInfo.type}
업로드: {attachment.uploadedBy}
) })}
)} {/* 검토자 의견 */} {item.response.reviewComments && (
검토자 의견
{item.response.reviewComments}
)}
) : (
아직 응답하지 않았습니다
)}
))}
) : (
일반평가 항목이 없습니다
)}
{/* ESG 평가 탭 */} {submissionDetails.esgEvaluations.length > 0 ? (
{submissionDetails.esgEvaluations.map((esgEvaluation) => ( toggleEsgItem(esgEvaluation.id)} >
{expandedEsgItems.has(esgEvaluation.id) ? ( ) : ( )} {esgEvaluation.serialNumber}. {esgEvaluation.category}
{esgEvaluation.evaluationItems.length}개 항목
평가 항목
{esgEvaluation.inspectionItem}
{/* ESG 평가 세부 항목들 */}
{esgEvaluation.evaluationItems.map((item) => (
{item.evaluationItem}
{item.evaluationItemDescription && (
{item.evaluationItemDescription}
)} {item.response ? (
선택된 답변 {item.response.answerOption.answerText} {item.response.selectedScore}점
{item.response.additionalComments && (
추가 의견:
{item.response.additionalComments}
)}
) : (
아직 응답하지 않았습니다
)}
))}
))}
) : (
ESG 평가 항목이 없습니다
)}
{/* 첨부파일 요약 */} {submissionDetails.attachmentStats.totalFiles > 0 && ( 첨부파일 요약
전체 파일 수
{submissionDetails.attachmentStats.totalFiles}개
전체 파일 크기
{formatFileSize(submissionDetails.attachmentStats.totalSize)}
일반평가 첨부파일
{submissionDetails.attachmentStats.generalEvaluationFiles}개
ESG 평가 첨부파일
{submissionDetails.attachmentStats.esgEvaluationFiles}개
)} {/* 검토자 의견 */} {submissionDetails.reviewComments && ( 검토자 의견
{submissionDetails.reviewComments}
)}
) : (
제출 내용이 없습니다
협력업체가 아직 평가를 제출하지 않았습니다
)}
{/* 고정 푸터 */}
) }