"use client" import * as React from "react" import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, } from "@/components/ui/sheet" import { Button } from "@/components/ui/button" import { Download, FileText, File, ImageIcon, AlertCircle } from "lucide-react" import { Badge } from "@/components/ui/badge" import { Separator } from "@/components/ui/separator" import { formatDate } from "@/lib/utils" import prettyBytes from "pretty-bytes" // 견적서 첨부파일 타입 정의 export interface QuotationAttachment { id: number quotationId: number revisionId: number fileName: string originalFileName: string fileSize: number fileType: string | null filePath: string description: string | null uploadedBy: number vendorId: number isVendorUpload: boolean createdAt: Date updatedAt: Date } // 견적서 정보 타입 interface QuotationInfo { id: number quotationCode: string | null vendorName?: string rfqCode?: string } interface TechSalesQuotationAttachmentsSheetProps extends React.ComponentPropsWithRef { quotation: QuotationInfo | null attachments: QuotationAttachment[] isLoading?: boolean } export function TechSalesQuotationAttachmentsSheet({ quotation, attachments, isLoading = false, ...props }: TechSalesQuotationAttachmentsSheetProps) { // 파일 아이콘 선택 함수 const getFileIcon = (fileName: string) => { const ext = fileName.split('.').pop()?.toLowerCase(); if (!ext) return ; // 이미지 파일 if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'].includes(ext)) { return ; } // PDF 파일 if (ext === 'pdf') { return ; } // Excel 파일 if (['xlsx', 'xls', 'csv'].includes(ext)) { return ; } // Word 파일 if (['docx', 'doc'].includes(ext)) { return ; } // 기본 파일 return ; }; // 파일 다운로드 처리 const handleDownload = (attachment: QuotationAttachment) => { const link = document.createElement('a'); link.href = attachment.filePath; link.download = attachment.originalFileName || attachment.fileName; link.target = '_blank'; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; // 리비전별로 첨부파일 그룹핑 const groupedAttachments = React.useMemo(() => { const groups = new Map(); attachments.forEach(attachment => { const revisionId = attachment.revisionId; if (!groups.has(revisionId)) { groups.set(revisionId, []); } groups.get(revisionId)!.push(attachment); }); // 리비전 ID 기준 내림차순 정렬 (최신 버전이 위에) return Array.from(groups.entries()) .sort(([a], [b]) => b - a) .map(([revisionId, files]) => ({ revisionId, files: files.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) })); }, [attachments]); return ( 견적서 첨부파일
견적서: {quotation?.quotationCode || "N/A"}
{quotation?.vendorName && (
벤더: {quotation.vendorName}
)} {quotation?.rfqCode && (
RFQ: {quotation.rfqCode}
)}
{isLoading ? (

첨부파일 로딩 중...

) : attachments.length === 0 ? (

첨부파일이 없습니다

이 견적서에는 첨부된 파일이 없습니다.

) : (
첨부파일 ({attachments.length}개)
{groupedAttachments.map((group, groupIndex) => (
{/* 리비전 헤더 */}
{group.revisionId === 0 ? "초기 버전" : `버전 ${group.revisionId}`} ({group.files.length}개 파일)
{/* 해당 리비전의 첨부파일들 */} {group.files.map((attachment) => (
{getFileIcon(attachment.fileName)}

{attachment.originalFileName || attachment.fileName}

{prettyBytes(attachment.fileSize)} {attachment.isVendorUpload ? "벤더 업로드" : "시스템"}

{formatDate(attachment.createdAt)}

{attachment.description && (

{attachment.description}

)}
{/* 다운로드 버튼 */}
))} {/* 그룹 간 구분선 (마지막 그룹 제외) */} {groupIndex < groupedAttachments.length - 1 && ( )}
))}
)}
) }