// components/rfq/response-detail-sheet.tsx "use client"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; import { FileText, Upload, Download, AlertCircle, MessageSquare, FileCheck, Eye } from "lucide-react"; import { formatDateTime, formatFileSize } from "@/lib/utils"; import { cn } from "@/lib/utils"; import type { EnhancedVendorResponse } from "@/lib/b-rfq/service"; // 파일 다운로드 핸들러 (API 사용) async function handleFileDownload( filePath: string, fileName: string, type: "client" | "vendor" = "client", id?: number ) { try { const params = new URLSearchParams({ path: filePath, type: type, }); // ID가 있으면 추가 if (id) { if (type === "client") { params.append("revisionId", id.toString()); } else { params.append("responseFileId", id.toString()); } } const response = await fetch(`/api/rfq-attachments/download?${params.toString()}`); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || `Download failed: ${response.status}`); } // Blob으로 파일 데이터 받기 const blob = await response.blob(); // 임시 URL 생성하여 다운로드 const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = fileName; document.body.appendChild(link); link.click(); // 정리 document.body.removeChild(link); window.URL.revokeObjectURL(url); console.log("✅ 파일 다운로드 성공:", fileName); } catch (error) { console.error("❌ 파일 다운로드 실패:", error); // 사용자에게 에러 알림 (토스트나 알럿으로 대체 가능) alert(`파일 다운로드에 실패했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}`); } } // 효과적인 상태별 아이콘 및 색상 function getEffectiveStatusInfo(effectiveStatus: string) { switch (effectiveStatus) { case "NOT_RESPONDED": return { label: "미응답", variant: "outline" as const }; case "UP_TO_DATE": return { label: "최신", variant: "default" as const }; case "VERSION_MISMATCH": return { label: "업데이트 필요", variant: "secondary" as const }; case "REVISION_REQUESTED": return { label: "수정요청", variant: "secondary" as const }; case "WAIVED": return { label: "포기", variant: "outline" as const }; default: return { label: effectiveStatus, variant: "outline" as const }; } } interface ResponseDetailSheetProps { response: EnhancedVendorResponse; trigger?: React.ReactNode; } export function ResponseDetailSheet({ response, trigger }: ResponseDetailSheetProps) { const hasMultipleRevisions = response.attachment?.revisions && response.attachment.revisions.length > 1; const hasResponseFiles = response.responseAttachments && response.responseAttachments.length > 0; return ( {trigger || ( )} 상세 정보 - {response.serialNo} {response.attachmentType} • {response.attachment?.revisions?.[0]?.originalFileName}
{/* 기본 정보 */}

기본 정보

상태
{getEffectiveStatusInfo(response.effectiveStatus).label}
현재 리비전
{response.currentRevision}
응답 리비전
{response.respondedRevision || "-"}
응답일
{response.respondedAt ? formatDateTime(new Date(response.respondedAt)) : "-"}
요청일
{formatDateTime(new Date(response.requestedAt))}
응답 파일 수
{response.totalResponseFiles}개
{/* 코멘트 정보 */}

코멘트

{response.responseComment && (
발주처 응답 코멘트
{response.responseComment}
)} {response.vendorComment && (
내부 메모
{response.vendorComment}
)} {response.attachment?.revisions?.find(r => r.revisionComment) && (
발주처 요청 사항
{response.attachment.revisions.find(r => r.revisionComment)?.revisionComment}
)} {!response.responseComment && !response.vendorComment && !response.attachment?.revisions?.find(r => r.revisionComment) && (
코멘트가 없습니다.
)}
{/* 발주처 리비전 히스토리 */} {hasMultipleRevisions && (

발주처 리비전 히스토리 ({response.attachment!.revisions.length}개)

{response.attachment!.revisions .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) .map((revision) => (
{revision.revisionNo}
{revision.originalFileName}
{formatFileSize(revision.fileSize)} • {formatDateTime(new Date(revision.createdAt))}
{revision.revisionComment && (
"{revision.revisionComment}"
)}
{revision.isLatest && ( 최신 )} {revision.revisionNo === response.respondedRevision && ( 응답됨 )}
))}
)} {/* 벤더 응답 파일들 */} {hasResponseFiles && (

벤더 응답 파일들 ({response.totalResponseFiles}개)

{response.responseAttachments! .sort((a, b) => new Date(b.uploadedAt).getTime() - new Date(a.uploadedAt).getTime()) .map((file) => (
파일 #{file.fileSequence}
{file.originalFileName}
{formatFileSize(file.fileSize)} • {formatDateTime(new Date(file.uploadedAt))}
{file.description && (
"{file.description}"
)}
{file.isLatestResponseFile && ( 최신 )}
))}
)} {!hasMultipleRevisions && !hasResponseFiles && (

추가 파일이나 리비전 정보가 없습니다.

)}
); }