import { useState, useRef } from 'react'; import { Upload, File, X, CheckCircle, AlertCircle } from 'lucide-react'; /* * SFR-02 공통컴포넌트: 파일 업로드 */ interface FileUploadProps { /** 허용 확장자 (예: '.xlsx,.csv,.pdf') */ accept?: string; /** 다중 파일 허용 */ multiple?: boolean; /** 최대 파일 크기 (MB) */ maxSizeMB?: number; /** 파일 선택 콜백 */ onFilesSelected?: (files: File[]) => void; className?: string; } export function FileUpload({ accept = '.xlsx,.csv,.pdf,.hwp,.docx', multiple = false, maxSizeMB = 50, onFilesSelected, className = '', }: FileUploadProps) { const inputRef = useRef(null); const [files, setFiles] = useState<{ file: File; status: 'ready' | 'done' | 'error'; msg?: string }[]>([]); const [dragOver, setDragOver] = useState(false); const processFiles = (fileList: FileList) => { const newFiles = Array.from(fileList).map((file) => { if (file.size > maxSizeMB * 1024 * 1024) { return { file, status: 'error' as const, msg: `파일 크기 초과 (최대 ${maxSizeMB}MB)` }; } return { file, status: 'ready' as const }; }); setFiles((prev) => (multiple ? [...prev, ...newFiles] : newFiles)); onFilesSelected?.(newFiles.filter((f) => f.status === 'ready').map((f) => f.file)); }; const removeFile = (index: number) => { setFiles((prev) => prev.filter((_, i) => i !== index)); }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); setDragOver(false); if (e.dataTransfer.files.length) processFiles(e.dataTransfer.files); }; const formatSize = (bytes: number) => { if (bytes < 1024) return `${bytes}B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`; return `${(bytes / (1024 * 1024)).toFixed(1)}MB`; }; return (
inputRef.current?.click()} onDragOver={(e) => { e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={handleDrop} className={`border-2 border-dashed rounded-lg p-6 text-center cursor-pointer transition-colors ${ dragOver ? 'border-blue-500/50 bg-blue-500/5' : 'border-slate-700/50 hover:border-slate-600/50 hover:bg-surface-overlay' }`} >

클릭하여 파일을 선택하거나 드래그하여 업로드

{accept.replace(/\./g, '').toUpperCase().replace(/,/g, ' · ')} | 최대 {maxSizeMB}MB {multiple && ' | 다중 파일 가능'}

e.target.files && processFiles(e.target.files)} className="hidden" />
{/* 파일 목록 */} {files.length > 0 && (
{files.map((f, i) => (
{f.status === 'error' ? ( ) : f.status === 'done' ? ( ) : ( )} {f.file.name} {formatSize(f.file.size)} {f.msg && {f.msg}}
))}
)}
); }