From c657ef972feeafff16ab0e07cb4771f7dd141ba0 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 10 Jul 2025 09:55:45 +0000 Subject: (대표님) 20250710 작업사항 - 평가 첨부, 로그인, SEDP 변경 요구사항 반영 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/evaluation/table/evaluation-details-dialog.tsx | 591 +++++++++++++-------- 1 file changed, 356 insertions(+), 235 deletions(-) (limited to 'lib/evaluation/table/evaluation-details-dialog.tsx') diff --git a/lib/evaluation/table/evaluation-details-dialog.tsx b/lib/evaluation/table/evaluation-details-dialog.tsx index df4ef016..2f682402 100644 --- a/lib/evaluation/table/evaluation-details-dialog.tsx +++ b/lib/evaluation/table/evaluation-details-dialog.tsx @@ -10,7 +10,11 @@ import { Clock, MessageSquare, Award, - FileText + FileText, + Paperclip, + Download, + File, + BarChart3 } from "lucide-react" import { @@ -19,6 +23,12 @@ import { 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" @@ -26,7 +36,10 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@ import { Separator } from "@/components/ui/separator" import { Skeleton } from "@/components/ui/skeleton" import { PeriodicEvaluationView } from "@/db/schema" -import { getEvaluationDetails, type EvaluationDetailData } from "../service" +import { getEvaluationDetails} from "../service" +import { AttachmentDetail, EvaluationDetailResponse } from "@/types/evaluation-form" +// 파일 다운로드 유틸리티 import +import { downloadFile, formatFileSize, getFileInfo } from "@/lib/file-download" interface EvaluationDetailsDialogProps { open: boolean @@ -75,10 +88,30 @@ export function EvaluationDetailsDialog({ evaluation, }: EvaluationDetailsDialogProps) { const [isLoading, setIsLoading] = React.useState(false) - const [evaluationDetails, setEvaluationDetails] = React.useState<{ - evaluationInfo: any - reviewerDetails: EvaluationDetailData[] - } | null>(null) + const [evaluationDetails, setEvaluationDetails] = React.useState(null) + + // 첨부파일 다운로드 핸들러 - downloadFile 사용 + const handleDownloadAttachment = async (attachment: AttachmentDetail) => { + try { + await downloadFile( + attachment.publicPath, + attachment.originalFileName, + { + action: 'download', + showToast: true, + showSuccessToast: true, + onError: (error) => { + console.error("파일 다운로드 실패:", error) + }, + onSuccess: (fileName, fileSize) => { + console.log("파일 다운로드 성공:", fileName, fileSize ? formatFileSize(fileSize) : '') + } + } + ) + } catch (error) { + console.error("다운로드 처리 중 오류:", error) + } + } // 평가 상세 정보 로드 React.useEffect(() => { @@ -109,258 +142,346 @@ export function EvaluationDetailsDialog({ if (!evaluation) return null return ( - - - - - - 평가 상세 - - - {/* 평가 기본 정보 */} - - - - - 평가 정보 - - - -
- {/* 협력업체 */} -
- 협력업체: - {evaluation.vendorName} - ({evaluation.vendorCode}) -
+ + + + {/* 고정 헤더 */} + + + + 평가 상세 + + + {/* 평가 기본 정보 */} + + + + + 평가 정보 + + + +
+ {/* 협력업체 */} +
+ 협력업체: + {evaluation.vendorName} + ({evaluation.vendorCode}) +
- {/* 평가년도 */} -
- 년도: - {evaluation.evaluationYear}년 -
+ {/* 평가년도 */} +
+ 년도: + {evaluation.evaluationYear}년 +
- {/* 구분 */} -
- 구분: - - {evaluation.division === "PLANT" ? "해양" : "조선"} - -
+ {/* 구분 */} +
+ 구분: + + {evaluation.division === "PLANT" ? "해양" : "조선"} + +
- {/* 진행상태 */} -
- 상태: - {evaluation.status} -
+ {/* 진행상태 */} +
+ 상태: + {evaluation.status} +
- {/* 평가점수/등급 */} -
- 평가점수/등급: - {evaluation.evaluationScore ? ( -
- - {Number(evaluation.evaluationScore).toFixed(1)}점 - - {evaluation.evaluationGrade && ( - - {evaluation.evaluationGrade} - - )} -
- ) : ( - - - )} -
+ {/* 평가점수/등급 */} +
+ 평가점수/등급: + {evaluation.evaluationScore ? ( +
+ + {Number(evaluation.evaluationScore).toFixed(1)}점 + + {evaluation.evaluationGrade && ( + + {evaluation.evaluationGrade} + + )} +
+ ) : ( + - + )} +
- {/* 확정점수/등급 */} -
- 확정점수/등급: - {evaluation.finalScore ? ( -
- - {Number(evaluation.finalScore).toFixed(1)}점 - - {evaluation.finalGrade && ( - - {evaluation.finalGrade} - - )} -
- ) : ( - 미확정 - )} + {/* 확정점수/등급 */} +
+ 확정점수/등급: + {evaluation.finalScore ? ( +
+ + {Number(evaluation.finalScore).toFixed(1)}점 + + {evaluation.finalGrade && ( + + {evaluation.finalGrade} + + )} +
+ ) : ( + 미확정 + )} +
-
-
-
-
- {isLoading ? ( -
- - - - - - - + -
- ) : evaluationDetails ? ( -
- {/* 통합 평가 테이블 */} - - - - - 평가 상세 내역 - - - - {evaluationDetails.reviewerDetails.some(r => r.evaluationItems.length > 0) ? ( - - - - 담당자 - {/* 상태 */} - 평가부문 - 항목 - 구분 - 범위 - 선택옵션 - 점수 - 의견 - - - - {evaluationDetails.reviewerDetails.map((reviewer) => - reviewer.evaluationItems.map((item, index) => ( - - -
-
{reviewer.departmentName}
-
- {reviewer.reviewerName} -
-
-
- {/* - {reviewer.isCompleted ? ( - - - 완료 - - ) : ( - - - 진행중 - - )} - */} - - - {CATEGORY_LABELS[item.category as keyof typeof CATEGORY_LABELS] || item.category} - - - - {CATEGORY_LABELS2[item.item as keyof typeof CATEGORY_LABELS2] || item.item} - - - {item.classification} - - - {item.range || "-"} - - - {item.scoreType === "variable" ? ( - 직접 입력 - ) : ( - item.selectedDetail || "-" - )} - - - {item.score !== null ? ( - - {item.score.toFixed(1)} + + + {/* 스크롤 가능한 컨텐츠 영역 */} +
+ {isLoading ? ( +
+ + + + + + + + +
+ ) : evaluationDetails ? ( +
+ {/* 통합 평가 테이블 */} + + + + + 평가 상세 내역 + + + + {evaluationDetails.reviewerDetails.some(r => r.evaluationItems.length > 0) ? ( +
+ + + 담당자 + 평가부문 + 항목 + 구분 + 범위 + 선택옵션 + 점수 + 첨부파일 + 의견 + + + + {evaluationDetails.reviewerDetails.map((reviewer) => + reviewer.evaluationItems.map((item, index) => ( + + +
+
{reviewer.departmentName}
+
+ {reviewer.reviewerName} +
+
+
+ + + {CATEGORY_LABELS[item.category as keyof typeof CATEGORY_LABELS] || item.category} + + + + {CATEGORY_LABELS2[item.item as keyof typeof CATEGORY_LABELS2] || item.item} + + + {item.classification} + + + {item.range || "-"} + + + {item.scoreType === "variable" ? ( + 직접 입력 + ) : ( + item.selectedDetail || "-" + )} + + + {item.score !== null ? ( + + {item.score.toFixed(1)} + + ) : ( + - + )} + + + {/* 📎 첨부파일 컬럼 - 개선된 버전 */} + + {item.attachments.length > 0 ? ( +
+ {item.attachments.map((attachment) => { + const fileInfo = getFileInfo(attachment.originalFileName) + return ( +
+ {fileInfo.icon} + + + + {attachment.originalFileName} + + + +
+
{attachment.originalFileName}
+
크기: {formatFileSize(attachment.fileSize)}
+
타입: {fileInfo.type}
+ {attachment.description && ( +
설명: {attachment.description}
+ )} +
업로드: {attachment.uploadedByName}
+
+ {fileInfo.canPreview ? "미리보기 가능" : "다운로드만 가능"} +
+
+
+
+ +
+ ) + })} + {item.attachments.length > 1 && ( +
+ 총 {formatFileSize(item.attachmentTotalSize)} +
+ )} +
+ ) : ( +
+ 첨부파일 없음 +
+ )} +
+ + + {item.comment || ( + 의견 없음 + )} + +
+ )) + )} +
+
+ ) : ( +
+ +
평가 항목이 없습니다
+
+ )} +
+
+ + {/* 리뷰어별 종합 의견 (있는 경우만) */} + {evaluationDetails.reviewerDetails.some(r => r.reviewerComment) && ( + + + + + 종합 의견 + + + + {evaluationDetails.reviewerDetails + .filter(reviewer => reviewer.reviewerComment) + .map((reviewer) => ( +
+
+ {reviewer.departmentName} + {reviewer.reviewerName} + {reviewer.totalAttachments > 0 && ( + + + {reviewer.totalAttachments}개 파일 - ) : ( - - )} - - - {item.comment || ( - 의견 없음 - )} - - - )) - )} - - - ) : ( -
- -
평가 항목이 없습니다
-
+
+
+ {reviewer.reviewerComment} +
+
+ ))} +
+
)} - - - {/* 리뷰어별 종합 의견 (있는 경우만) */} - {evaluationDetails.reviewerDetails.some(r => r.reviewerComment) && ( - - - - - 종합 의견 - - - - {evaluationDetails.reviewerDetails - .filter(reviewer => reviewer.reviewerComment) - .map((reviewer) => ( -
-
- {reviewer.departmentName} - {reviewer.reviewerName} + {/* 📎 첨부파일 요약 (파일이 많은 경우) */} + {evaluationDetails.attachmentStats.totalFiles > 5 && ( + + + + + 첨부파일 요약 + + + +
+
+
전체 파일 수
+
{evaluationDetails.attachmentStats.totalFiles}개
-
- {reviewer.reviewerComment} +
+
전체 파일 크기
+
{formatFileSize(evaluationDetails.attachmentStats.totalSize)}
+
+
+
첨부 질문 수
+
{evaluationDetails.attachmentStats.questionsWithAttachments}개
+
+
+
첨부 담당자 수
+
{evaluationDetails.attachmentStats.reviewersWithAttachments}명
- ))} - - - )} + + + )} - {evaluationDetails.reviewerDetails.length === 0 && ( - + {evaluationDetails.reviewerDetails.length === 0 && ( + + +
+ +
배정된 리뷰어가 없습니다
+
+
+
+ )} +
+ ) : ( +
- -
배정된 리뷰어가 없습니다
+ 평가 상세 정보를 불러올 수 없습니다
)}
- ) : ( - - -
- 평가 상세 정보를 불러올 수 없습니다 -
-
-
- )} -
- -
- -
+ {/* 고정 푸터 */} +
+ +
+ +
+ ) } \ No newline at end of file -- cgit v1.2.3