From 9f071a9370ad8b948713b0b38090f78c326ec3ad Mon Sep 17 00:00:00 2001 From: perf3ct Date: Wed, 25 Jun 2025 22:18:05 +0000 Subject: [PATCH 1/3] feat(client): fix the theme for the FailedOcr, and dashboard values --- .../src/components/Dashboard/Dashboard.tsx | 22 +++++-- .../EnhancedSnippetViewer.tsx | 3 +- .../MimeTypeFacetFilter.tsx | 17 +++++- frontend/src/pages/DocumentsPage.tsx | 17 ++++-- frontend/src/pages/FailedOcrPage.tsx | 9 ++- frontend/src/pages/SourcesPage.tsx | 58 ++++++++++++++----- 6 files changed, 97 insertions(+), 29 deletions(-) diff --git a/frontend/src/components/Dashboard/Dashboard.tsx b/frontend/src/components/Dashboard/Dashboard.tsx index 0056d90..4737e7e 100644 --- a/frontend/src/components/Dashboard/Dashboard.tsx +++ b/frontend/src/components/Dashboard/Dashboard.tsx @@ -522,8 +522,20 @@ const Dashboard: React.FC = () => { // Fetch documents with better error handling let docs: Document[] = []; try { - const docsResponse = await api.get('/documents'); - docs = Array.isArray(docsResponse.data) ? docsResponse.data : []; + const docsResponse = await api.get('/documents', { + params: { + limit: 10, + offset: 0, + } + }); + // Handle both direct array response and paginated response + if (Array.isArray(docsResponse.data)) { + docs = docsResponse.data; + } else if (docsResponse.data?.documents) { + docs = docsResponse.data.documents; + } else { + docs = []; + } } catch (docError) { console.error('Failed to fetch documents:', docError); // Continue with empty documents array @@ -623,7 +635,7 @@ const Dashboard: React.FC = () => { subtitle="Files in your library" icon={DocumentIcon} color="#6366f1" - trend="+12% this month" + trend={stats.totalDocuments > 0 ? `${stats.totalDocuments} total` : 'No documents yet'} /> @@ -633,7 +645,7 @@ const Dashboard: React.FC = () => { subtitle="Total file size" icon={StorageIcon} color="#10b981" - trend="+2.4 GB this week" + trend={stats.totalSize > 0 ? `${formatBytes(stats.totalSize)} used` : 'No storage used'} /> @@ -653,7 +665,7 @@ const Dashboard: React.FC = () => { subtitle="Ready for search" icon={SearchableIcon} color="#8b5cf6" - trend="100% indexed" + trend={stats.searchablePages > 0 ? `${stats.searchablePages} indexed` : 'Nothing indexed yet'} /> diff --git a/frontend/src/components/EnhancedSnippetViewer/EnhancedSnippetViewer.tsx b/frontend/src/components/EnhancedSnippetViewer/EnhancedSnippetViewer.tsx index 7a47136..2923304 100644 --- a/frontend/src/components/EnhancedSnippetViewer/EnhancedSnippetViewer.tsx +++ b/frontend/src/components/EnhancedSnippetViewer/EnhancedSnippetViewer.tsx @@ -273,10 +273,11 @@ const EnhancedSnippetViewer: React.FC = ({ {snippets.length > 0 && ( 999 ? `${Math.floor(snippets.length/1000)}K` : snippets.length} matches`} size="small" color="primary" variant="outlined" + sx={{ maxWidth: '100px', '& .MuiChip-label': { overflow: 'hidden', textOverflow: 'ellipsis' } }} /> )} diff --git a/frontend/src/components/MimeTypeFacetFilter/MimeTypeFacetFilter.tsx b/frontend/src/components/MimeTypeFacetFilter/MimeTypeFacetFilter.tsx index a4ca2b7..e9b00c4 100644 --- a/frontend/src/components/MimeTypeFacetFilter/MimeTypeFacetFilter.tsx +++ b/frontend/src/components/MimeTypeFacetFilter/MimeTypeFacetFilter.tsx @@ -230,10 +230,11 @@ const MimeTypeFacetFilter: React.FC = ({ {group.label} 999 ? `${Math.floor(totalCount/1000)}K` : totalCount} size="small" variant={selectedCount > 0 ? "filled" : "outlined"} color={selectedCount > 0 ? "primary" : "default"} + sx={{ maxWidth: '60px', '& .MuiChip-label': { overflow: 'hidden', textOverflow: 'ellipsis' } }} /> @@ -254,7 +255,12 @@ const MimeTypeFacetFilter: React.FC = ({ {getMimeTypeLabel(facet.value)} - + 999 ? `${Math.floor(facet.count/1000)}K` : facet.count} + size="small" + variant="outlined" + sx={{ maxWidth: '50px', '& .MuiChip-label': { overflow: 'hidden', textOverflow: 'ellipsis' } }} + /> } sx={{ display: 'flex', width: '100%', mb: 0.5 }} @@ -285,7 +291,12 @@ const MimeTypeFacetFilter: React.FC = ({ {getMimeTypeLabel(facet.value)} - + 999 ? `${Math.floor(facet.count/1000)}K` : facet.count} + size="small" + variant="outlined" + sx={{ maxWidth: '50px', '& .MuiChip-label': { overflow: 'hidden', textOverflow: 'ellipsis' } }} + /> } sx={{ display: 'flex', width: '100%', mb: 0.5 }} diff --git a/frontend/src/pages/DocumentsPage.tsx b/frontend/src/pages/DocumentsPage.tsx index 9f88d5a..b1c79e4 100644 --- a/frontend/src/pages/DocumentsPage.tsx +++ b/frontend/src/pages/DocumentsPage.tsx @@ -547,8 +547,8 @@ const DocumentsPage: React.FC = () => { gap: 2, color: 'primary.contrastText' }}> - - {selectedDocuments.size} of {sortedDocuments.length} documents selected + + {selectedDocuments.size > 999 ? `${Math.floor(selectedDocuments.size/1000)}K` : selectedDocuments.size} of {sortedDocuments.length > 999 ? `${Math.floor(sortedDocuments.length/1000)}K` : sortedDocuments.length} documents selected )} @@ -785,7 +785,16 @@ const DocumentsPage: React.FC = () => { size="small" color="primary" variant="outlined" - sx={{ fontSize: '0.7rem', height: '20px' }} + sx={{ + fontSize: '0.7rem', + height: '20px', + maxWidth: '120px', + '& .MuiChip-label': { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + } + }} /> ))} {doc.tags.length > 3 && ( diff --git a/frontend/src/pages/FailedOcrPage.tsx b/frontend/src/pages/FailedOcrPage.tsx index 6ccd06b..260fe94 100644 --- a/frontend/src/pages/FailedOcrPage.tsx +++ b/frontend/src/pages/FailedOcrPage.tsx @@ -484,7 +484,12 @@ const FailedOcrPage: React.FC = () => { - + theme.palette.mode === 'dark' ? 'grey.900' : 'grey.50', + borderRadius: 1 + }}> Error Details @@ -504,7 +509,7 @@ const FailedOcrPage: React.FC = () => { variant="body2" sx={{ fontFamily: 'monospace', - bgcolor: 'grey.100', + bgcolor: (theme) => theme.palette.mode === 'dark' ? 'grey.800' : 'grey.100', p: 1, borderRadius: 1, fontSize: '0.75rem', diff --git a/frontend/src/pages/SourcesPage.tsx b/frontend/src/pages/SourcesPage.tsx index 305d595..76a3ecd 100644 --- a/frontend/src/pages/SourcesPage.tsx +++ b/frontend/src/pages/SourcesPage.tsx @@ -576,16 +576,19 @@ const SourcesPage: React.FC = () => { icon: React.ReactNode; label: string; value: string | number; - color?: 'primary' | 'success' | 'warning' | 'error' + color?: 'primary' | 'success' | 'warning' | 'error' | 'info' }) => ( { } }} > - + {icon} - - + + {typeof value === 'number' ? value.toLocaleString() : value} - + {label} @@ -782,8 +804,8 @@ const SourcesPage: React.FC = () => { {/* Stats Grid */} - - + + } label="Files Synced" @@ -791,7 +813,7 @@ const SourcesPage: React.FC = () => { color="success" /> - + } label="Files Pending" @@ -799,15 +821,23 @@ const SourcesPage: React.FC = () => { color="warning" /> - + + } + label="OCR Processed" + value={source.total_files_synced} + color="info" + /> + + } - label="Total Size" + label="Total Size (Downloaded)" value={formatBytes(source.total_size_bytes)} color="primary" /> - + } label="Last Sync" From a6d9446d1fda442f3f193d6fdcdfc75003329a4e Mon Sep 17 00:00:00 2001 From: perf3ct Date: Wed, 25 Jun 2025 22:42:41 +0000 Subject: [PATCH 2/3] feat(client): also update the text color used on dashboard --- .../src/components/Dashboard/Dashboard.tsx | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/Dashboard/Dashboard.tsx b/frontend/src/components/Dashboard/Dashboard.tsx index 4737e7e..cf00516 100644 --- a/frontend/src/components/Dashboard/Dashboard.tsx +++ b/frontend/src/components/Dashboard/Dashboard.tsx @@ -82,19 +82,24 @@ interface QuickAction { // Stats Card Component const StatsCard: React.FC = ({ title, value, subtitle, icon: Icon, color, trend }) => { const theme = useTheme(); + const isDarkMode = theme.palette.mode === 'dark'; return ( = ({ title, value, subtitle, icon: Ico right: 0, width: '120px', height: '120px', - background: 'linear-gradient(135deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.05) 100%)', + background: isDarkMode + ? `linear-gradient(135deg, ${alpha(color, 0.15)} 0%, ${alpha(color, 0.05)} 100%)` + : 'linear-gradient(135deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.05) 100%)', borderRadius: '50%', transform: 'translate(40px, -40px)', }, @@ -118,7 +125,9 @@ const StatsCard: React.FC = ({ title, value, subtitle, icon: Ico left: 0, right: 0, bottom: 0, - background: 'linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%)', + background: isDarkMode + ? `linear-gradient(135deg, ${alpha(color, 0.08)} 0%, ${alpha(color, 0.03)} 100%)` + : 'linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%)', backdropFilter: 'blur(10px)', }, }} @@ -177,16 +186,21 @@ const StatsCard: React.FC = ({ title, value, subtitle, icon: Ico width: 64, height: 64, borderRadius: 3, - background: 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.15) 100%)', + background: isDarkMode + ? `linear-gradient(135deg, ${alpha(color, 0.25)} 0%, ${alpha(color, 0.15)} 100%)` + : 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.15) 100%)', backdropFilter: 'blur(20px)', display: 'flex', alignItems: 'center', justifyContent: 'center', - border: '1px solid rgba(255,255,255,0.2)', + border: isDarkMode + ? `1px solid ${alpha(color, 0.4)}` + : '1px solid rgba(255,255,255,0.2)', boxShadow: '0 8px 32px rgba(0,0,0,0.1)', }}> From dd8555b088ddbf1cb2412975a0f94b857867d99e Mon Sep 17 00:00:00 2001 From: perf3ct Date: Wed, 25 Jun 2025 22:49:53 +0000 Subject: [PATCH 3/3] fix(client): fix broken 'download' function on FailedOCR page --- .../src/components/Dashboard/Dashboard.tsx | 11 ++++++---- frontend/src/pages/FailedOcrPage.tsx | 16 ++++++++++++-- frontend/src/services/api.ts | 22 +++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Dashboard/Dashboard.tsx b/frontend/src/components/Dashboard/Dashboard.tsx index cf00516..0a95df9 100644 --- a/frontend/src/components/Dashboard/Dashboard.tsx +++ b/frontend/src/components/Dashboard/Dashboard.tsx @@ -38,7 +38,7 @@ import { } from '@mui/icons-material'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; -import api from '../../services/api'; +import api, { documentService } from '../../services/api'; interface Document { id: string; @@ -377,9 +377,12 @@ const RecentDocuments: React.FC = ({ documents = [] }) => { - const downloadUrl = `/api/documents/${doc.id}/download`; - window.open(downloadUrl, '_blank'); + onClick={async () => { + try { + await documentService.downloadFile(doc.id, doc.original_filename || doc.filename); + } catch (error) { + console.error('Download failed:', error); + } }} > diff --git a/frontend/src/pages/FailedOcrPage.tsx b/frontend/src/pages/FailedOcrPage.tsx index 260fe94..b11fe60 100644 --- a/frontend/src/pages/FailedOcrPage.tsx +++ b/frontend/src/pages/FailedOcrPage.tsx @@ -473,7 +473,13 @@ const FailedOcrPage: React.FC = () => { window.open(`/api/documents/${document.id}/download`, '_blank')} + onClick={async () => { + try { + await documentService.downloadFile(document.id, document.original_filename || document.filename); + } catch (error) { + console.error('Download failed:', error); + } + }} > @@ -760,7 +766,13 @@ const FailedOcrPage: React.FC = () => { window.open(`/api/documents/${doc.id}/download`, '_blank')} + onClick={async () => { + try { + await documentService.downloadFile(doc.id, doc.original_filename || doc.filename); + } catch (error) { + console.error('Download failed:', error); + } + }} sx={{ color: theme.palette.secondary.main }} > diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index e4c15c2..db0cf7e 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -151,6 +151,28 @@ export const documentService = { }) }, + downloadFile: async (id: string, filename?: string) => { + try { + const response = await api.get(`/documents/${id}/download`, { + responseType: 'blob', + }); + + // Create blob URL and trigger download + const blob = new Blob([response.data], { type: response.headers['content-type'] }); + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename || `document-${id}`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + } catch (error) { + console.error('Download failed:', error); + throw error; + } + }, + getOcrText: (id: string) => { return api.get(`/documents/${id}/ocr`) },