import { forwardRef, useState, useCallback, useRef } from 'react' import { Dialog, Button, Progress } from '@/components/ui' import { HiCloudArrowUp, HiXMark } from 'react-icons/hi2' import classNames from 'classnames' export interface FileUploadModalProps { isOpen: boolean onClose: () => void onUpload: (files: File[]) => Promise currentFolderId?: string loading?: boolean className?: string } interface UploadFileWithProgress { id: string file: File progress: number status: 'pending' | 'uploading' | 'completed' | 'error' error?: string errorDetail?: string } const FileUploadModal = forwardRef((props, ref) => { const { isOpen, onClose, onUpload, currentFolderId, loading = false, className } = props const [uploadFiles, setUploadFiles] = useState([]) const [isDragOver, setIsDragOver] = useState(false) const [uploading, setUploading] = useState(false) const fileInputRef = useRef(null) const generateFileId = () => Math.random().toString(36).substr(2, 9) const formatFileSize = (bytes: number): string => { const sizes = ['B', 'KB', 'MB', 'GB', 'TB'] // Handle undefined, null, NaN values if (!bytes || bytes === 0 || isNaN(bytes)) return '0 B' const i = Math.floor(Math.log(bytes) / Math.log(1024)) return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + ' ' + sizes[i] } const handleFilesSelect = useCallback((files: FileList | File[]) => { const fileArray = Array.from(files) const newFiles: UploadFileWithProgress[] = fileArray.map((file) => ({ id: generateFileId(), file: file, progress: 0, status: 'pending' as const, })) setUploadFiles((prev) => [...prev, ...newFiles]) }, []) const handleFileInputChange = (e: React.ChangeEvent) => { if (e.target.files) { handleFilesSelect(e.target.files) } } const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault() setIsDragOver(true) }, []) const handleDragLeave = useCallback((e: React.DragEvent) => { e.preventDefault() setIsDragOver(false) }, []) const handleDrop = useCallback( (e: React.DragEvent) => { e.preventDefault() setIsDragOver(false) if (e.dataTransfer.files) { handleFilesSelect(e.dataTransfer.files) } }, [handleFilesSelect], ) const removeFile = (fileId: string) => { setUploadFiles((prev) => prev.filter((f) => f.id !== fileId)) } const handleUpload = async () => { if (uploadFiles.length === 0) return setUploading(true) const filesToUpload = uploadFiles.filter((f) => f.status === 'pending') // Upload files one by one for (const fileData of filesToUpload) { let progressInterval: NodeJS.Timeout | null = null try { // Set status to uploading setUploadFiles((prev) => prev.map((f) => (f.id === fileData.id ? { ...f, status: 'uploading' as const } : f)), ) // Simulate progress for visual feedback progressInterval = setInterval(() => { setUploadFiles((prev) => prev.map((f) => { if (f.id === fileData.id && f.progress < 90) { return { ...f, progress: f.progress + 10 } } return f }), ) }, 100) // Call the actual upload function for single file await onUpload([fileData.file]) // Clear progress interval if (progressInterval) { clearInterval(progressInterval) progressInterval = null } // Mark as completed and remove from list after delay setUploadFiles((prev) => prev.map((f) => f.id === fileData.id ? { ...f, status: 'completed' as const, progress: 100 } : f, ), ) // Remove completed files from list after 2 seconds setTimeout(() => { setUploadFiles((prev) => prev.filter((f) => f.id !== fileData.id)) }, 2000) } catch (error: any) { console.error('Upload failed for file:', fileData.file.name, error) // Clear progress interval in case of error if (progressInterval) { clearInterval(progressInterval) progressInterval = null } // Extract detailed error message from ABP response let errorMessage = 'Upload failed' let detailMessage = '' if (error?.response?.data?.error) { const errorData = error.response.data.error // Ana hata mesajı if (errorData.message) { errorMessage = errorData.message } // Detay mesajı - validationErrors veya details'den if (errorData.details) { detailMessage = errorData.details } else if (errorData.validationErrors && errorData.validationErrors.length > 0) { detailMessage = errorData.validationErrors[0].message || errorData.validationErrors[0] } // Dosya boyutu kontrolü için özel mesaj if (detailMessage.includes('Request body too large') || detailMessage.includes('max request body size')) { const maxSizeMB = 30 // 30MB limit errorMessage = 'File too large' detailMessage = `File size exceeds the maximum allowed size of ${maxSizeMB}MB. Your file is ${(fileData.file.size / (1024 * 1024)).toFixed(1)}MB.` } } else if (error?.message) { errorMessage = error.message } else if (typeof error === 'string') { errorMessage = error } // Mark as error with detailed message setUploadFiles((prev) => prev.map((f) => f.id === fileData.id ? { ...f, status: 'error' as const, error: errorMessage, errorDetail: detailMessage, progress: 0, } : f, ), ) } } setUploading(false) // Check if all files are processed (completed or error) const remainingFiles = uploadFiles.filter( (f) => f.status === 'pending' || f.status === 'uploading', ) if (remainingFiles.length === 0) { // If no pending files and no errors, close modal const hasErrors = uploadFiles.some((f) => f.status === 'error') if (!hasErrors) { setTimeout(() => { onClose() setUploadFiles([]) }, 2000) } } } const handleClose = () => { if (!uploading) { setUploadFiles([]) onClose() } } const clearCompletedFiles = () => { setUploadFiles((prev) => prev.filter((f) => f.status !== 'completed')) } const clearErrorFiles = () => { setUploadFiles((prev) => prev.filter((f) => f.status !== 'error')) } const totalFiles = uploadFiles.length const completedFiles = uploadFiles.filter((f) => f.status === 'completed').length const errorFiles = uploadFiles.filter((f) => f.status === 'error').length const pendingFiles = uploadFiles.filter((f) => f.status === 'pending').length const hasError = errorFiles > 0 return (

Upload Files

Choose files or drag and drop here

Select one or more files to upload

{/* File List */} {uploadFiles.length > 0 && (
{uploadFiles.map((file) => (
{/* File name and size in one line */}

{file.file.name}

{formatFileSize(file.file.size)}
{/* Progress Bar */} {file.status === 'uploading' && (
)} {/* Status Messages */} {file.status === 'completed' && (

✓ Upload completed

)} {file.status === 'error' && (

✗ {file.error || 'Upload failed'}

{file.errorDetail && (

{file.errorDetail}

)}
)}
{(file.status === 'pending' || file.status === 'error') && (
))}
)}
{/* Footer */}
{uploading && ( Uploading files... {completedFiles > 0 && `(${completedFiles} completed)`} )} {!uploading && !hasError && pendingFiles > 0 && ( Ready to upload {pendingFiles} file(s) )} {!uploading && !hasError && pendingFiles === 0 && totalFiles === 0 && ( No files selected )}
{hasError && !uploading && ( )}
) }) FileUploadModal.displayName = 'FileUploadModal' export default FileUploadModal