import { useState, useRef, useCallback, type DragEvent, type ChangeEvent } from 'react'; import { motion, AnimatePresence } from 'motion/react'; import { SectionWrapper } from '../report/ui/SectionWrapper'; import { VideoFilled, FileTextFilled } from '../icons/FilledIcons'; // ─── Types ─── type UploadCategory = 'all' | 'image' | 'video' | 'text'; interface UploadedAsset { id: string; file: File; category: 'image' | 'video' | 'text'; previewUrl: string | null; name: string; size: string; uploadedAt: Date; } // ─── Helpers ─── function categorize(file: File): 'image' | 'video' | 'text' { if (file.type.startsWith('image/')) return 'image'; if (file.type.startsWith('video/')) return 'video'; return 'text'; } function formatSize(bytes: number): string { if (bytes >= 1_048_576) return `${(bytes / 1_048_576).toFixed(1)} MB`; if (bytes >= 1024) return `${Math.round(bytes / 1024)} KB`; return `${bytes} B`; } function uid() { return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; } const categoryConfig: Record = { all: { label: '전체' }, image: { label: 'Image' }, video: { label: 'Video' }, text: { label: 'Text' }, }; const categoryBadge: Record<'image' | 'video' | 'text', string> = { image: 'bg-[#F3F0FF] text-[#4A3A7C] shadow-[2px_3px_6px_rgba(155,138,212,0.12)]', video: 'bg-[#FFF0F0] text-[#7C3A4B] shadow-[2px_3px_6px_rgba(212,136,154,0.12)]', text: 'bg-[#FFF6ED] text-[#7C5C3A] shadow-[2px_3px_6px_rgba(212,168,114,0.12)]', }; const ACCEPT_MAP: Record = { 'image/*': '.jpg,.jpeg,.png,.gif,.webp,.svg', 'video/*': '.mp4,.mov,.webm,.avi', 'text/*': '.txt,.md,.doc,.docx,.pdf,.csv,.json', }; const ALL_ACCEPT = Object.values(ACCEPT_MAP).join(','); // ─── Component ─── export default function MyAssetUpload() { const [assets, setAssets] = useState([]); const [activeFilter, setActiveFilter] = useState('all'); const [isDragOver, setIsDragOver] = useState(false); const inputRef = useRef(null); const processFiles = useCallback((files: FileList | File[]) => { const newAssets: UploadedAsset[] = Array.from(files).map((file) => { const cat = categorize(file); const previewUrl = cat === 'image' || cat === 'video' ? URL.createObjectURL(file) : null; return { id: uid(), file, category: cat, previewUrl, name: file.name, size: formatSize(file.size), uploadedAt: new Date(), }; }); setAssets((prev) => [...newAssets, ...prev]); }, []); const handleDrop = useCallback( (e: DragEvent) => { e.preventDefault(); setIsDragOver(false); if (e.dataTransfer.files.length) processFiles(e.dataTransfer.files); }, [processFiles], ); const handleInputChange = useCallback( (e: ChangeEvent) => { if (e.target.files?.length) { processFiles(e.target.files); e.target.value = ''; } }, [processFiles], ); const removeAsset = useCallback((id: string) => { setAssets((prev) => { const found = prev.find((a) => a.id === id); if (found?.previewUrl) URL.revokeObjectURL(found.previewUrl); return prev.filter((a) => a.id !== id); }); }, []); const filtered = activeFilter === 'all' ? assets : assets.filter((a) => a.category === activeFilter); const counts = { all: assets.length, image: assets.filter((a) => a.category === 'image').length, video: assets.filter((a) => a.category === 'video').length, text: assets.filter((a) => a.category === 'text').length, }; return ( {/* Drop Zone */}
{ e.preventDefault(); setIsDragOver(true); }} onDragLeave={() => setIsDragOver(false)} onDrop={handleDrop} onClick={() => inputRef.current?.click()} className={`relative rounded-2xl border-2 border-dashed p-10 md:p-14 text-center cursor-pointer transition-all mb-8 ${ isDragOver ? 'border-[#9B8AD4] bg-[#F3F0FF]/60 scale-[1.01]' : 'border-slate-200 bg-slate-50/50 hover:border-[#D5CDF5] hover:bg-[#F3F0FF]/20' }`} > {/* Upload Icon */}

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

Image, Video, Text 파일 지원 (JPG, PNG, MP4, MOV, TXT, PDF, DOC 등)

{/* File Type Badges */}
{(['image', 'video', 'text'] as const).map((cat) => ( {cat === 'image' ? 'Image' : cat === 'video' ? 'Video' : 'Text'} ))}
{/* Filter Tabs + Count */} {assets.length > 0 && ( <>
{(Object.keys(categoryConfig) as UploadCategory[]).map((key) => ( ))}
{/* Uploaded Assets Grid */}
{filtered.map((asset) => ( {/* Preview Area */}
{asset.category === 'image' && asset.previewUrl && ( {asset.name} )} {asset.category === 'video' && asset.previewUrl && (
{/* Info */}

{asset.name}

{asset.size}

))}
)}
); }