import { useState, useEffect } from "react"; import { Modal, Text, Button, Group, Stack, Checkbox, ScrollArea, Box, Badge, SimpleGrid, } from "@mantine/core"; import { useTranslation } from "react-i18next"; import DocumentThumbnail from "@app/components/shared/filePreview/DocumentThumbnail"; import { FileId } from "@app/types/file"; import { useFileActionTerminology } from "@app/hooks/useFileActionTerminology"; interface FilePickerModalProps { opened: boolean; onClose: () => void; storedFiles: any[]; // Files from storage (various formats supported) onSelectFiles: (selectedFiles: File[]) => void; } const FilePickerModal = ({ opened, onClose, storedFiles, onSelectFiles, }: FilePickerModalProps) => { const { t } = useTranslation(); const terminology = useFileActionTerminology(); const [selectedFileIds, setSelectedFileIds] = useState([]); // Reset selection when modal opens useEffect(() => { if (opened) { setSelectedFileIds([]); } }, [opened]); const toggleFileSelection = (fileId: FileId) => { setSelectedFileIds((prev) => { return prev.includes(fileId) ? prev.filter((id) => id !== fileId) : [...prev, fileId]; }); }; const selectAll = () => { setSelectedFileIds(storedFiles.map((f) => f.id).filter(Boolean)); }; const selectNone = () => { setSelectedFileIds([]); }; const handleConfirm = async () => { const selectedFiles = storedFiles.filter((f) => selectedFileIds.includes(f.id), ); // Convert stored files to File objects const convertedFiles = await Promise.all( selectedFiles.map(async (fileItem) => { try { // If it's already a File object, return as is if (fileItem instanceof File) { return fileItem; } // If it has a file property, use that if (fileItem.file && fileItem.file instanceof File) { return fileItem.file; } // If it's from IndexedDB storage, reconstruct the File if ( fileItem.arrayBuffer && typeof fileItem.arrayBuffer === "function" ) { const arrayBuffer = await fileItem.arrayBuffer(); const blob = new Blob([arrayBuffer], { type: fileItem.type || "application/pdf", }); return new File([blob], fileItem.name, { type: fileItem.type || "application/pdf", lastModified: fileItem.lastModified || Date.now(), }); } // If it has data property, reconstruct the File if (fileItem.data) { const blob = new Blob([fileItem.data], { type: fileItem.type || "application/pdf", }); return new File([blob], fileItem.name, { type: fileItem.type || "application/pdf", lastModified: fileItem.lastModified || Date.now(), }); } console.warn("Could not convert file item:", fileItem); return null; } catch (error) { console.error("Error converting file:", error, fileItem); return null; } }), ); // Filter out any null values and return valid Files const validFiles = convertedFiles.filter((f): f is File => f !== null); onSelectFiles(validFiles); onClose(); }; const formatFileSize = (bytes: number) => { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i]; }; return ( {storedFiles.length === 0 ? ( {terminology.noFilesInStorage} ) : ( <> {/* Selection controls */} {storedFiles.length}{" "} {t("fileUpload.filesAvailable", "files available")} {selectedFileIds.length > 0 && ( <> • {selectedFileIds.length} selected )} {/* File grid */} {storedFiles.map((file) => { const fileId = file.id; const isSelected = selectedFileIds.includes(fileId); return ( toggleFileSelection(fileId)} > toggleFileSelection(fileId)} onClick={(e) => e.stopPropagation()} /> {/* Thumbnail */} {/* File info */} {file.name} {formatFileSize( file.size || file.file?.size || 0, )} ); })} {/* Selection summary */} {selectedFileIds.length > 0 && ( {selectedFileIds.length}{" "} {t("fileManager.filesSelected", "files selected")} )} )} {/* Action buttons */} ); }; export default FilePickerModal;