'use client' import * as React from 'react' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { FileText, Download, Trash2 } from 'lucide-react' import { useToast } from '@/hooks/use-toast' import { useTransition } from 'react' import { uploadPreQuoteDocument, getPreQuoteDocuments, getPreQuoteDocumentForDownload, deletePreQuoteDocument } from '../../pre-quote/service' import { downloadFile } from '@/lib/file-download' interface UploadedDocument { id: number fileName: string originalFileName: string fileSize: number | null filePath: string title: string | null description: string | null uploadedAt: string uploadedBy: string } interface SimpleFileUploadProps { biddingId: number companyId: number userId: string readOnly?: boolean } export function SimpleFileUpload({ biddingId, companyId, userId, readOnly = false }: SimpleFileUploadProps) { const { toast } = useToast() const [isPending, startTransition] = useTransition() const [documents, setDocuments] = React.useState([]) const [isLoading, setIsLoading] = React.useState(true) // 업로드된 문서 목록 로드 const loadDocuments = React.useCallback(async () => { try { setIsLoading(true) const docs = await getPreQuoteDocuments(biddingId, companyId) // docs가 undefined이거나 배열이 아닌 경우 빈 배열로 처리 if (!docs || !Array.isArray(docs)) { setDocuments([]) return } // Date를 string으로 변환 const mappedDocs = docs.map(doc => ({ ...doc, uploadedAt: doc.uploadedAt?.toString() || '', uploadedBy: doc.uploadedBy || '' })) setDocuments(mappedDocs) } catch (error) { console.error('Failed to load documents:', error) setDocuments([]) // 에러 시에도 빈 배열로 설정 toast({ title: '오류', description: '업로드된 문서 목록을 불러오는데 실패했습니다.', variant: 'destructive', }) } finally { setIsLoading(false) } }, [biddingId, companyId, toast]) React.useEffect(() => { loadDocuments() }, [loadDocuments]) // 파일 업로드 처리 const handleFileUpload = (event: React.ChangeEvent) => { const files = event.target.files if (!files || files.length === 0) return const file = files[0] // 파일 크기 체크 (50MB 제한) if (file.size > 50 * 1024 * 1024) { toast({ title: '파일 크기 초과', description: '파일 크기가 50MB를 초과합니다.', variant: 'destructive', }) return } // 파일 타입 체크 const allowedTypes = [ 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'image/jpeg', 'image/png', 'application/zip' ] if (!allowedTypes.includes(file.type)) { toast({ title: '지원하지 않는 파일 형식', description: 'PDF, Word, Excel, 이미지, ZIP 파일만 업로드 가능합니다.', variant: 'destructive', }) return } startTransition(async () => { const result = await uploadPreQuoteDocument(biddingId, companyId, file, userId) if (result.success) { toast({ title: '업로드 완료', description: result.message, }) await loadDocuments() // 문서 목록 새로고침 } else { toast({ title: '업로드 실패', description: result.error, variant: 'destructive', }) } }) // input 초기화 event.target.value = '' } // 파일 다운로드 const handleDownload = (document: UploadedDocument) => { startTransition(async () => { const result = await getPreQuoteDocumentForDownload(document.id, biddingId, companyId) if (result.success) { try { if (result.document?.filePath && result.document?.originalFileName) { await downloadFile(result.document.filePath, result.document.originalFileName, { showToast: true }) } else { throw new Error('파일 정보가 없습니다.') } } catch (error) { console.error('파일 다운로드 실패:', error) toast({ title: '다운로드 실패', description: '파일 다운로드에 실패했습니다.', variant: 'destructive', }) } } else { toast({ title: '다운로드 실패', description: result.error, variant: 'destructive', }) } }) } // 파일 삭제 const handleDelete = (document: UploadedDocument) => { if (!confirm(`"${document.originalFileName}" 파일을 삭제하시겠습니까?`)) { return } startTransition(async () => { const result = await deletePreQuoteDocument(document.id, biddingId, companyId, userId) if (result.success) { toast({ title: '삭제 완료', description: result.message, }) await loadDocuments() // 문서 목록 새로고침 } else { toast({ title: '삭제 실패', description: result.error, variant: 'destructive', }) } }) } // 파일 크기 포맷팅 const formatFileSize = (bytes: number | null) => { if (!bytes) return '-' if (bytes === 0) return '0 Bytes' const k = 1024 const sizes = ['Bytes', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] } return ( 견적 문서 업로드 {!readOnly && (

지원 형식: PDF, Word, Excel, 이미지, ZIP (최대 50MB)

)} {/* 업로드된 문서 목록 */} {isLoading ? (

문서 목록을 불러오는 중...

) : documents.length > 0 ? (
파일명 크기 업로드일 작성자 작업 {documents.map((doc) => (
{doc.originalFileName}
{formatFileSize(doc.fileSize)} {new Date(doc.uploadedAt).toLocaleDateString('ko-KR')} {doc.uploadedBy}
{!readOnly && doc.uploadedBy === userId && ( )}
))}
) : (

업로드된 문서가 없습니다

)}
) }