'use client'; /** * FileList - 업로드된 파일 목록 표시 컴포넌트 * * 첨부파일 목록 표시, 다운로드, 삭제 기능 * * 사용 예시: * removeFile(index)} * onDownload={(file) => downloadFile(file)} * /> */ import { useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { FileText, Download, Trash2, ExternalLink, Loader2 } from 'lucide-react'; import { cn } from '@/lib/utils'; /** 새로 업로드된 파일 */ export interface NewFile { file: File; /** 업로드 진행률 (0-100) */ progress?: number; /** 업로드 중 여부 */ uploading?: boolean; /** 에러 메시지 */ error?: string; } /** 기존 파일 (서버에서 가져온) */ export interface ExistingFile { id: string | number; name: string; url?: string; size?: number; /** 삭제 중 여부 */ deleting?: boolean; } export interface FileListProps { /** 새로 업로드된 파일 목록 */ files?: NewFile[]; /** 기존 파일 목록 */ existingFiles?: ExistingFile[]; /** 새 파일 제거 콜백 */ onRemove?: (index: number) => void; /** 기존 파일 제거 콜백 */ onRemoveExisting?: (id: string | number) => void; /** 기존 파일 다운로드 콜백 */ onDownload?: (file: ExistingFile) => void; /** 읽기 전용 */ readOnly?: boolean; /** 삭제 버튼 표시 여부 (readOnly의 반대 개념) */ showRemove?: boolean; /** 추가 클래스 */ className?: string; /** 파일 없을 때 메시지 */ emptyMessage?: string; /** 컴팩트 모드 */ compact?: boolean; } // 파일 크기 포맷 function formatFileSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } // 파일 확장자에 따른 아이콘 색상 function getFileColor(fileName: string): string { const ext = (fileName || '').split('.').pop()?.toLowerCase(); switch (ext) { case 'pdf': return 'text-red-500'; case 'doc': case 'docx': return 'text-blue-500'; case 'xls': case 'xlsx': return 'text-green-500'; case 'jpg': case 'jpeg': case 'png': case 'gif': case 'webp': return 'text-purple-500'; case 'dwg': case 'dxf': return 'text-orange-500'; default: return 'text-gray-500'; } } export function FileList({ files = [], existingFiles = [], onRemove, onRemoveExisting, onDownload, readOnly = false, className, emptyMessage = '파일이 없습니다', compact = false, }: FileListProps) { const handleDownload = useCallback((file: ExistingFile) => { if (onDownload) { onDownload(file); } else if (file.url) { // 기본 다운로드 동작 const link = document.createElement('a'); link.href = file.url; link.download = file.name; link.click(); } }, [onDownload]); const totalFiles = files.length + existingFiles.length; if (totalFiles === 0) { return (
{emptyMessage}
); } return (
{/* 기존 파일 목록 */} {existingFiles.map((file) => (

{file.name}

{file.size && (

{formatFileSize(file.size)}

)}
{/* 다운로드 버튼 */} {(file.url || onDownload) && ( )} {/* 새 탭에서 열기 (인라인 뷰) */} {file.id && ( )} {/* 삭제 버튼 */} {!readOnly && onRemoveExisting && ( )}
))} {/* 새로 업로드된 파일 목록 */} {files.map((item, index) => (

{item.file.name}

{!item.error && ( (새 파일) )}

{formatFileSize(item.file.size)}

{/* 에러 메시지 */} {item.error && (

{item.error}

)} {/* 업로드 진행률 */} {item.uploading && item.progress !== undefined && (
)}
{/* 삭제/취소 버튼 */} {!readOnly && onRemove && ( )}
))}
); } export default FileList;