diff --git a/frontend/admin-panel/src/components/ui/MediaSelector.tsx b/frontend/admin-panel/src/components/ui/MediaSelector.tsx index 6bfc828..ca96e24 100644 --- a/frontend/admin-panel/src/components/ui/MediaSelector.tsx +++ b/frontend/admin-panel/src/components/ui/MediaSelector.tsx @@ -46,6 +46,7 @@ export default function MediaSelector({ isOpen, onClose, onSelect, multiple = fa const [previewUrl, setPreviewUrl] = useState(null); const [uploadProgress, setUploadProgress] = useState(null); const [filterType, setFilterType] = useState<'all' | 'image' | 'video' | 'audio' | 'pdf'>('all'); + const [dragOver, setDragOver] = useState(false); const fetchMedia = useCallback(async () => { try { @@ -187,13 +188,11 @@ export default function MediaSelector({ isOpen, onClose, onSelect, multiple = fa } }; - const [dragOver, setDragOver] = useState(false); - const handleDrop = async (e: React.DragEvent) => { e.preventDefault(); setDragOver(false); if (!e.dataTransfer.files || e.dataTransfer.files.length === 0) return; - const files = Array.from(e.dataTransfer.files).filter(f => f.type.startsWith('image/')); + const files = Array.from(e.dataTransfer.files); if (files.length === 0) return; setIsUploading(true); @@ -203,7 +202,12 @@ export default function MediaSelector({ isOpen, onClose, onSelect, multiple = fa const formData = new FormData(); formData.append('file', file); return api.post('/admin/media/upload', formData, { - headers: { 'Content-Type': 'multipart/form-data' } + headers: { 'Content-Type': 'multipart/form-data' }, + onUploadProgress: (progressEvent) => { + const total = progressEvent.total || file.size; + const percent = Math.round((progressEvent.loaded * 100) / total); + setUploadProgress(percent); + } }); }); const results = await Promise.allSettled(uploadPromises); @@ -212,14 +216,18 @@ export default function MediaSelector({ isOpen, onClose, onSelect, multiple = fa }); if (successCount > 0) { - toast.success(`${successCount} تصویر با موفقیت آپلود شد`); + toast.success(`${successCount} فایل با موفقیت بارگذاری شد`); await fetchMedia(); } + if (successCount < files.length) { + toast.error(`خطا در بارگذاری ${files.length - successCount} فایل`); + } } catch (error) { console.error('Drop upload failed', error); - toast.error('خطا در آپلود تصاویر رها شده'); + toast.error('خطا در آپلود فایل‌های رها شده'); } finally { setIsUploading(false); + setUploadProgress(null); } }; @@ -243,7 +251,7 @@ export default function MediaSelector({ isOpen, onClose, onSelect, multiple = fa {dragOver && (
-

تصاویر را اینجا رها کنید تا آپلود شوند

+

فایل‌ها (تصویر، ویدئو، پادکست، PDF) را اینجا رها کنید تا آپلود شوند

)} diff --git a/frontend/admin-panel/src/pages/Media.tsx b/frontend/admin-panel/src/pages/Media.tsx index 5ce3404..f58e7d8 100644 --- a/frontend/admin-panel/src/pages/Media.tsx +++ b/frontend/admin-panel/src/pages/Media.tsx @@ -257,7 +257,26 @@ export default function MediaManager() { const totalSize = mediaList.reduce((sum, m) => sum + m.size, 0); return ( -
+
{ e.preventDefault(); setIsDragOver(true); }} + onDragLeave={(e) => { + if (!e.currentTarget.contains(e.relatedTarget as Node)) { + setIsDragOver(false); + } + }} + onDrop={handleDrop} + > + {/* Full Page Drag & Drop Overlay */} + {isDragOver && ( +
+ +

فایل‌ها را در هر جای صفحه رها کنید تا آپلود شوند

+

پشتیبانی از تصاویر، ویدئوها، پادکست‌های صوتی و اسناد PDF

+
+ )} +