import React, { useState, useCallback, useRef, useEffect } from 'react'; import { Stack, Button, Text, Center, Box, Divider } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import UploadFileIcon from '@mui/icons-material/UploadFile'; import { useTranslation } from 'react-i18next'; import { fileStorage } from '../../services/fileStorage'; import { FileWithUrl } from '../../types/file'; import FileGrid from './FileGrid'; import MultiSelectControls from './MultiSelectControls'; import { useFileManager } from '../../hooks/useFileManager'; interface FileUploadSelectorProps { // Appearance title?: string; subtitle?: string; showDropzone?: boolean; // File handling sharedFiles?: any[]; onFileSelect?: (file: File) => void; onFilesSelect: (files: File[]) => void; accept?: string[]; // Loading state loading?: boolean; disabled?: boolean; // Recent files showRecentFiles?: boolean; maxRecentFiles?: number; } const FileUploadSelector = ({ title, subtitle, showDropzone = true, sharedFiles = [], onFileSelect, onFilesSelect, accept = ["application/pdf", "application/zip", "application/x-zip-compressed"], loading = false, disabled = false, showRecentFiles = true, maxRecentFiles = 8, }: FileUploadSelectorProps) => { const { t } = useTranslation(); const fileInputRef = useRef(null); const [recentFiles, setRecentFiles] = useState([]); const [selectedFiles, setSelectedFiles] = useState([]); const { loadRecentFiles, handleRemoveFile, storeFile, convertToFile, createFileSelectionHandlers } = useFileManager(); const refreshRecentFiles = useCallback(async () => { const files = await loadRecentFiles(); setRecentFiles(files); }, [loadRecentFiles]); const handleNewFileUpload = useCallback(async (uploadedFiles: File[]) => { if (uploadedFiles.length === 0) return; if (showRecentFiles) { try { for (const file of uploadedFiles) { await storeFile(file); } refreshRecentFiles(); } catch (error) { console.error('Failed to save files to recent:', error); } } if (onFilesSelect) { onFilesSelect(uploadedFiles); } else if (onFileSelect) { onFileSelect(uploadedFiles[0]); } }, [onFileSelect, onFilesSelect, showRecentFiles, storeFile, refreshRecentFiles]); const handleFileInputChange = useCallback((event: React.ChangeEvent) => { const files = event.target.files; if (files && files.length > 0) { const fileArray = Array.from(files); console.log('File input change:', fileArray.length, 'files'); handleNewFileUpload(fileArray); } if (fileInputRef.current) { fileInputRef.current.value = ''; } }, [handleNewFileUpload]); const openFileDialog = useCallback(() => { fileInputRef.current?.click(); }, []); const handleRecentFileSelection = useCallback(async (file: FileWithUrl) => { try { const fileObj = await convertToFile(file); if (onFilesSelect) { onFilesSelect([fileObj]); } else if (onFileSelect) { onFileSelect(fileObj); } } catch (error) { console.error('Failed to load file from recent:', error); } }, [onFileSelect, onFilesSelect, convertToFile]); const selectionHandlers = createFileSelectionHandlers(selectedFiles, setSelectedFiles); const handleSelectedRecentFiles = useCallback(async () => { if (onFilesSelect) { await selectionHandlers.selectMultipleFiles(recentFiles, onFilesSelect); } }, [recentFiles, onFilesSelect, selectionHandlers]); const handleRemoveFileByIndex = useCallback(async (index: number) => { await handleRemoveFile(index, recentFiles, setRecentFiles); const file = recentFiles[index]; setSelectedFiles(prev => prev.filter(id => id !== (file.id || file.name))); }, [handleRemoveFile, recentFiles]); useEffect(() => { if (showRecentFiles) { refreshRecentFiles(); } }, [showRecentFiles, refreshRecentFiles]); // Get default title and subtitle from translations if not provided const displayTitle = title || t("fileUpload.selectFiles", "Select files"); const displaySubtitle = subtitle || t("fileUpload.chooseFromStorageMultiple", "Choose files from storage or upload new PDFs"); return ( <> {/* Title and description */} {displayTitle} {displaySubtitle} {/* Action buttons */} {showDropzone ? (
{t("fileUpload.dropFilesHere", "Drop files here or click to upload")} {accept.includes('application/pdf') && accept.includes('application/zip') ? t("fileUpload.pdfAndZipFiles", "PDF and ZIP files") : accept.includes('application/pdf') ? t("fileUpload.pdfFilesOnly", "PDF files only") : t("fileUpload.supportedFileTypes", "Supported file types") }
) : ( {/* Manual file input as backup */} )}
{/* Recent Files Section */} {showRecentFiles && recentFiles.length > 0 && ( {t("fileUpload.recentFiles", "Recent Files")} { await Promise.all(recentFiles.map(async (file) => { await fileStorage.deleteFile(file.id || file.name); })); setRecentFiles([]); setSelectedFiles([]); }} /> { await Promise.all(recentFiles.map(async (file) => { await fileStorage.deleteFile(file.id || file.name); })); setRecentFiles([]); setSelectedFiles([]); }} /> )}
); }; export default FileUploadSelector;