diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index 859cc41dd..081f746ee 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -1653,6 +1653,17 @@ "uploadError": "Failed to upload some files.", "failedToOpen": "Failed to open file. It may have been removed from storage.", "failedToLoad": "Failed to load file to active set.", - "storageCleared": "Browser cleared storage. Files have been removed. Please re-upload." + "storageCleared": "Browser cleared storage. Files have been removed. Please re-upload.", + "clearAll": "Clear All", + "reloadFiles": "Reload Files" + }, + "storage": { + "temporaryNotice": "Files are stored temporarily in your browser and may be cleared automatically", + "storageLimit": "Storage limit", + "storageUsed": "Temporary Storage used", + "storageFull": "Storage is nearly full. Consider removing some files.", + "fileTooLarge": "File too large. Maximum size per file is", + "storageQuotaExceeded": "Storage quota exceeded. Please remove some files before uploading more.", + "approximateSize": "Approximate size" } -} \ No newline at end of file +} diff --git a/frontend/src/components/fileManagement/FileCard.tsx b/frontend/src/components/fileManagement/FileCard.tsx index a7d0bae10..6b275e556 100644 --- a/frontend/src/components/fileManagement/FileCard.tsx +++ b/frontend/src/components/fileManagement/FileCard.tsx @@ -31,10 +31,10 @@ const FileCard = ({ file, onRemove, onDoubleClick, onView, onEdit, isSelected, o radius="md" withBorder p="xs" - style={{ - width: 225, - minWidth: 180, - maxWidth: 260, + style={{ + width: 225, + minWidth: 180, + maxWidth: 260, cursor: onDoubleClick ? "pointer" : undefined, position: 'relative', border: isSelected ? '2px solid var(--mantine-color-blue-6)' : undefined, @@ -109,25 +109,25 @@ const FileCard = ({ file, onRemove, onDoubleClick, onView, onEdit, isSelected, o )} {thumb ? ( - PDF thumbnail ) : isGenerating ? ( -
-
Generating...
) : ( -
)} - + {file.name} - + - + {getFileSize(file)} {getFileDate(file)} {file.storedInIndexedDB && ( - } > DB )} - + - + )} - )} - + - - + + ); }; -export default StorageStatsCard; \ No newline at end of file +export default StorageStatsCard; diff --git a/frontend/src/components/shared/FileUploadSelector.tsx b/frontend/src/components/shared/FileUploadSelector.tsx index dff6875e0..c4aed1148 100644 --- a/frontend/src/components/shared/FileUploadSelector.tsx +++ b/frontend/src/components/shared/FileUploadSelector.tsx @@ -59,7 +59,7 @@ const FileUploadSelector = ({ }, [allowMultiple, onFileSelect, onFilesSelect]); // Get default title and subtitle from translations if not provided - const displayTitle = title || t(allowMultiple ? "fileUpload.selectFiles" : "fileUpload.selectFile", + const displayTitle = title || t(allowMultiple ? "fileUpload.selectFiles" : "fileUpload.selectFile", allowMultiple ? "Select files" : "Select a file"); const displaySubtitle = subtitle || t(allowMultiple ? "fileUpload.chooseFromStorageMultiple" : "fileUpload.chooseFromStorage", allowMultiple ? "Choose files from storage or upload new PDFs" : "Choose a file from storage or upload a new PDF"); @@ -87,10 +87,7 @@ const FileUploadSelector = ({ disabled={disabled || sharedFiles.length === 0} loading={loading} > - {loading - ? t("fileUpload.loading", "Loading...") - : `${t("fileUpload.loadFromStorage", "Load from Storage")} (${sharedFiles.length} ${t("fileUpload.filesAvailable", "files available")})` - } + {loading ? "Loading..." : `Load from Storage (${sharedFiles.length} files available)`} @@ -112,7 +109,7 @@ const FileUploadSelector = ({ allowMultiple ? "Drop files here or click to upload" : "Drop file here or click to upload")} - {accept.includes('application/pdf') + {accept.includes('application/pdf') ? t("fileUpload.pdfFilesOnly", "PDF files only") : t("fileUpload.supportedFileTypes", "Supported file types") } diff --git a/frontend/src/styles/tailwind.css b/frontend/src/styles/tailwind.css index ebbd4f23a..9415e80ce 100644 --- a/frontend/src/styles/tailwind.css +++ b/frontend/src/styles/tailwind.css @@ -2,6 +2,14 @@ /* Import minimal theme variables */ @import './theme.css'; -@tailwind base; -@tailwind components; -@tailwind utilities; +@layer base { + @tailwind base; +} + +@layer components { + @tailwind components; +} + +@layer utilities { + @tailwind utilities; +} diff --git a/frontend/src/theme/mantineTheme.ts b/frontend/src/theme/mantineTheme.ts index 017b7d737..91670d6cf 100644 --- a/frontend/src/theme/mantineTheme.ts +++ b/frontend/src/theme/mantineTheme.ts @@ -30,7 +30,7 @@ const gray: MantineColorsTuple = [ export const mantineTheme = createTheme({ // Primary color primaryColor: 'primary', - + // Color palette colors: { primary, @@ -245,7 +245,7 @@ export const mantineTheme = createTheme({ }, control: { color: 'var(--text-secondary)', - '&[data-active]': { + '[dataActive]': { backgroundColor: 'var(--bg-surface)', color: 'var(--text-primary)', boxShadow: 'var(--shadow-sm)', @@ -261,7 +261,7 @@ export const mantineTheme = createTheme({ '*': { transition: 'background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease', }, - + // Custom scrollbar styling '*::-webkit-scrollbar': { width: '8px', @@ -278,4 +278,4 @@ export const mantineTheme = createTheme({ backgroundColor: 'var(--color-primary-500)', }, }), -}); \ No newline at end of file +}); diff --git a/frontend/src/types/file.ts b/frontend/src/types/file.ts index f2915e32e..c887c093b 100644 --- a/frontend/src/types/file.ts +++ b/frontend/src/types/file.ts @@ -11,9 +11,40 @@ export interface FileWithUrl extends File { export interface StorageConfig { useIndexedDB: boolean; - // Simplified - no thresholds needed, IndexedDB for everything + maxFileSize: number; // Maximum size per file in bytes + maxTotalStorage: number; // Maximum total storage in bytes + warningThreshold: number; // Warning threshold (percentage 0-1) } export const defaultStorageConfig: StorageConfig = { useIndexedDB: true, + maxFileSize: 100 * 1024 * 1024, // 100MB per file + maxTotalStorage: 1024 * 1024 * 1024, // 1GB default, will be updated dynamically + warningThreshold: 0.8, // Warn at 80% capacity +}; + +// Calculate and update storage limit: half of available storage or 10GB, whichever is smaller +export const initializeStorageConfig = async (): Promise => { + const tenGB = 10 * 1024 * 1024 * 1024; // 10GB in bytes + const oneGB = 1024 * 1024 * 1024; // 1GB fallback + + let maxTotalStorage = oneGB; // Default fallback + + // Try to estimate available storage + if ('storage' in navigator && 'estimate' in navigator.storage) { + try { + const estimate = await navigator.storage.estimate(); + if (estimate.quota) { + const halfQuota = estimate.quota / 2; + maxTotalStorage = Math.min(halfQuota, tenGB); + } + } catch (error) { + console.warn('Could not estimate storage quota, using 1GB default:', error); + } + } + + return { + ...defaultStorageConfig, + maxTotalStorage + }; }; \ No newline at end of file