feat(client): update titles of cards used on search and sources page

This commit is contained in:
perf3ct
2025-06-26 16:46:19 +00:00
parent fc66495da0
commit 325b9321fc
3 changed files with 221 additions and 109 deletions

View File

@@ -24,6 +24,68 @@
.search-chip {
font-size: 0.7rem !important;
height: 18px !important;
max-width: 100px;
}
}
/* Prevent text overflow in search components */
.search-result-card {
overflow: hidden;
}
.search-result-card .MuiCardContent-root {
overflow: hidden;
}
/* Ensure proper flex behavior for narrow windows */
@media (max-width: 1024px) {
/* Prevent horizontal overflow in search results */
.search-result-card {
min-width: 0;
}
/* Ensure chips wrap properly */
.MuiChip-root {
flex-shrink: 0;
margin: 2px;
}
/* Prevent button groups from overflowing */
.MuiToggleButtonGroup-root {
flex-wrap: wrap;
gap: 4px;
}
/* Ensure search stats wrap on narrow screens */
.search-stats-container {
flex-wrap: wrap;
gap: 8px;
}
}
/* Extra small screens */
@media (max-width: 480px) {
/* Stack search mode buttons vertically */
.MuiToggleButtonGroup-root {
flex-direction: column;
width: 100%;
}
.MuiToggleButtonGroup-root .MuiToggleButton-root {
width: 100%;
}
/* Reduce chip sizes further */
.search-chip {
font-size: 0.65rem !important;
height: 16px !important;
padding: 0 6px !important;
}
/* Stack action buttons vertically in cards */
.search-card-actions {
flex-direction: column;
gap: 4px;
}
}

View File

@@ -601,19 +601,21 @@ const SearchPage: React.FC = () => {
flexWrap: 'wrap',
gap: 2,
}}>
<Stack direction="row" spacing={2} alignItems="center">
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, alignItems: 'center' }}>
<Chip
icon={<TrendingIcon />}
label={`${totalResults} results`}
size="small"
color="primary"
variant="outlined"
sx={{ flexShrink: 0 }}
/>
<Chip
icon={<TimeIcon />}
label={`${queryTime}ms`}
size="small"
variant="outlined"
sx={{ flexShrink: 0 }}
/>
{advancedSettings.useEnhancedSearch && (
<Chip
@@ -622,9 +624,10 @@ const SearchPage: React.FC = () => {
size="small"
color="success"
variant="outlined"
sx={{ flexShrink: 0 }}
/>
)}
</Stack>
</Box>
{/* Simplified Search Mode Selector */}
<ToggleButtonGroup
@@ -647,7 +650,7 @@ const SearchPage: React.FC = () => {
<Typography variant="body2" color="text.secondary" gutterBottom>
Quick suggestions:
</Typography>
<Stack direction="row" spacing={1} flexWrap="wrap">
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{quickSuggestions.map((suggestion, index) => (
<Chip
key={index}
@@ -658,6 +661,7 @@ const SearchPage: React.FC = () => {
variant="outlined"
color="primary"
sx={{
flexShrink: 0,
'&:hover': {
backgroundColor: 'primary.main',
color: 'primary.contrastText',
@@ -665,7 +669,7 @@ const SearchPage: React.FC = () => {
}}
/>
))}
</Stack>
</Box>
</Box>
)}
@@ -675,7 +679,7 @@ const SearchPage: React.FC = () => {
<Typography variant="body2" color="text.secondary" gutterBottom>
Related searches:
</Typography>
<Stack direction="row" spacing={1} flexWrap="wrap">
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{suggestions.map((suggestion, index) => (
<Chip
key={index}
@@ -685,6 +689,7 @@ const SearchPage: React.FC = () => {
clickable
variant="outlined"
sx={{
flexShrink: 0,
'&:hover': {
backgroundColor: 'primary.light',
color: 'primary.contrastText',
@@ -692,7 +697,7 @@ const SearchPage: React.FC = () => {
}}
/>
))}
</Stack>
</Box>
</Box>
)}
@@ -771,9 +776,22 @@ const SearchPage: React.FC = () => {
onChange={handleTagsChange}
input={<OutlinedInput label="Select Tags" />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5, overflow: 'hidden' }}>
{selected.map((value) => (
<Chip key={value} label={value} size="small" />
<Chip
key={value}
label={value}
size="small"
sx={{
flexShrink: 0,
maxWidth: '100px',
'& .MuiChip-label': {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}
}}
/>
))}
</Box>
)}
@@ -1060,15 +1078,15 @@ const SearchPage: React.FC = () => {
</Box>
)}
<CardContent className="search-card" sx={{ flexGrow: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1 }}>
<CardContent className="search-card" sx={{ flexGrow: 1, overflow: 'hidden' }}>
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1, width: '100%' }}>
{viewMode === 'list' && (
<Box sx={{ mr: 1, mt: 0.5 }}>
{getFileIcon(doc.mime_type)}
</Box>
)}
<Box sx={{ flexGrow: 1, minWidth: 0, pr: 1 }}>
<Box sx={{ flexGrow: 1, minWidth: 0, overflow: 'hidden' }}>
<Typography
variant="h6"
sx={{
@@ -1078,24 +1096,28 @@ const SearchPage: React.FC = () => {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
display: 'block',
width: '100%',
}}
title={doc.original_filename}
>
{doc.original_filename}
</Typography>
<Stack direction="row" spacing={1} sx={{ mb: 1, flexWrap: 'wrap', gap: 0.5 }}>
<Box sx={{ mb: 1, display: 'flex', flexWrap: 'wrap', gap: 0.5, overflow: 'hidden' }}>
<Chip
className="search-chip"
label={formatFileSize(doc.file_size)}
size="small"
variant="outlined"
sx={{ flexShrink: 0 }}
/>
<Chip
className="search-chip"
label={formatDate(doc.created_at)}
size="small"
variant="outlined"
sx={{ flexShrink: 0 }}
/>
{doc.has_ocr_text && (
<Chip
@@ -1104,12 +1126,13 @@ const SearchPage: React.FC = () => {
size="small"
color="success"
variant="outlined"
sx={{ flexShrink: 0 }}
/>
)}
</Stack>
</Box>
{doc.tags.length > 0 && (
<Stack direction="row" spacing={0.5} sx={{ mb: 1, flexWrap: 'wrap' }}>
<Box sx={{ mb: 1, display: 'flex', flexWrap: 'wrap', gap: 0.5, overflow: 'hidden' }}>
{doc.tags.slice(0, 2).map((tag, index) => (
<Chip
key={index}
@@ -1118,7 +1141,17 @@ const SearchPage: React.FC = () => {
size="small"
color="primary"
variant="outlined"
sx={{ fontSize: '0.7rem', height: '18px' }}
sx={{
fontSize: '0.7rem',
height: '18px',
flexShrink: 0,
maxWidth: '120px',
'& .MuiChip-label': {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}
}}
/>
))}
{doc.tags.length > 2 && (
@@ -1127,10 +1160,10 @@ const SearchPage: React.FC = () => {
label={`+${doc.tags.length - 2}`}
size="small"
variant="outlined"
sx={{ fontSize: '0.7rem', height: '18px' }}
sx={{ fontSize: '0.7rem', height: '18px', flexShrink: 0 }}
/>
)}
</Stack>
</Box>
)}
{/* Enhanced Search Snippets */}
@@ -1150,37 +1183,49 @@ const SearchPage: React.FC = () => {
{/* Search Rank */}
{doc.search_rank && (
<Box sx={{ mt: 1 }}>
<Box sx={{ mt: 1, overflow: 'hidden' }}>
<Chip
className="search-chip"
label={`Relevance: ${(doc.search_rank * 100).toFixed(1)}%`}
size="small"
color="info"
variant="outlined"
sx={{ fontSize: '0.7rem', height: '18px' }}
sx={{
fontSize: '0.7rem',
height: '18px',
flexShrink: 0,
maxWidth: '150px',
'& .MuiChip-label': {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}
}}
/>
</Box>
)}
</Box>
<Tooltip title="View Details">
<IconButton
className="search-filter-button search-focusable"
size="small"
onClick={() => navigate(`/documents/${doc.id}`)}
>
<ViewIcon />
</IconButton>
</Tooltip>
<Tooltip title="Download">
<IconButton
className="search-filter-button search-focusable"
size="small"
onClick={() => handleDownload(doc)}
>
<DownloadIcon />
</IconButton>
</Tooltip>
<Box sx={{ display: 'flex', flexShrink: 0, ml: 'auto' }}>
<Tooltip title="View Details">
<IconButton
className="search-filter-button search-focusable"
size="small"
onClick={() => navigate(`/documents/${doc.id}`)}
>
<ViewIcon />
</IconButton>
</Tooltip>
<Tooltip title="Download">
<IconButton
className="search-filter-button search-focusable"
size="small"
onClick={() => handleDownload(doc)}
>
<DownloadIcon />
</IconButton>
</Tooltip>
</Box>
</Box>
</CardContent>
</Card>

View File

@@ -572,75 +572,84 @@ const SourcesPage: React.FC = () => {
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
};
const StatCard = ({ icon, label, value, color = 'primary' }: {
const StatCard = ({ icon, label, value, color = 'primary', tooltip }: {
icon: React.ReactNode;
label: string;
value: string | number;
color?: 'primary' | 'success' | 'warning' | 'error' | 'info'
}) => (
<Box
sx={{
p: 2.5,
borderRadius: 3,
background: `linear-gradient(135deg, ${alpha(theme.palette[color].main, 0.1)} 0%, ${alpha(theme.palette[color].main, 0.05)} 100%)`,
border: `1px solid ${alpha(theme.palette[color].main, 0.2)}`,
position: 'relative',
overflow: 'hidden',
height: '100px',
display: 'flex',
alignItems: 'center',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '3px',
background: `linear-gradient(90deg, ${theme.palette[color].main}, ${theme.palette[color].light})`,
}
}}
>
<Stack direction="row" alignItems="center" spacing={2} sx={{ width: '100%', overflow: 'hidden' }}>
<Avatar
sx={{
bgcolor: alpha(theme.palette[color].main, 0.15),
color: theme.palette[color].main,
width: 40,
height: 40,
flexShrink: 0
}}
>
{icon}
</Avatar>
<Box sx={{ minWidth: 0, flex: 1 }}>
<Typography
variant="h5"
fontWeight="bold"
color={theme.palette[color].main}
color?: 'primary' | 'success' | 'warning' | 'error' | 'info';
tooltip?: string;
}) => {
const card = (
<Box
sx={{
p: 2.5,
borderRadius: 3,
background: `linear-gradient(135deg, ${alpha(theme.palette[color].main, 0.1)} 0%, ${alpha(theme.palette[color].main, 0.05)} 100%)`,
border: `1px solid ${alpha(theme.palette[color].main, 0.2)}`,
position: 'relative',
overflow: 'hidden',
height: '100px',
display: 'flex',
alignItems: 'center',
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '3px',
background: `linear-gradient(90deg, ${theme.palette[color].main}, ${theme.palette[color].light})`,
}
}}
>
<Stack direction="row" alignItems="center" spacing={2} sx={{ width: '100%', overflow: 'hidden' }}>
<Avatar
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
bgcolor: alpha(theme.palette[color].main, 0.15),
color: theme.palette[color].main,
width: 40,
height: 40,
flexShrink: 0
}}
>
{typeof value === 'number' ? value.toLocaleString() : value}
</Typography>
<Typography
variant="body2"
color="text.secondary"
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
fontSize: '0.75rem'
}}
>
{label}
</Typography>
</Box>
</Stack>
</Box>
);
{icon}
</Avatar>
<Box sx={{ minWidth: 0, flex: 1 }}>
<Typography
variant="h5"
fontWeight="bold"
color={theme.palette[color].main}
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}}
>
{typeof value === 'number' ? value.toLocaleString() : value}
</Typography>
<Typography
variant="body2"
color="text.secondary"
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
fontSize: '0.75rem'
}}
>
{label}
</Typography>
</Box>
</Stack>
</Box>
);
return tooltip ? (
<Tooltip title={tooltip} arrow>
{card}
</Tooltip>
) : card;
};
const renderSourceCard = (source: Source) => (
<Fade in={true} key={source.id}>
@@ -808,9 +817,10 @@ const SourcesPage: React.FC = () => {
<Grid item xs={6} sm={4} md={3}>
<StatCard
icon={<TrendingUpIcon />}
label="Files Synced"
label="Files Processed"
value={source.total_files_synced}
color="success"
tooltip="Files attempted to be synced, including duplicates and skipped files"
/>
</Grid>
<Grid item xs={6} sm={4} md={3}>
@@ -819,14 +829,7 @@ const SourcesPage: React.FC = () => {
label="Files Pending"
value={source.total_files_pending}
color="warning"
/>
</Grid>
<Grid item xs={6} sm={4} md={3}>
<StatCard
icon={<AssessmentIcon />}
label="OCR Processed"
value={source.total_files_synced}
color="info"
tooltip="Files discovered but not yet processed during sync"
/>
</Grid>
<Grid item xs={6} sm={4} md={3}>
@@ -835,6 +838,7 @@ const SourcesPage: React.FC = () => {
label="Total Size (Downloaded)"
value={formatBytes(source.total_size_bytes)}
color="primary"
tooltip="Total size of files successfully downloaded from this source"
/>
</Grid>
<Grid item xs={6} sm={4} md={3}>
@@ -845,6 +849,7 @@ const SourcesPage: React.FC = () => {
? formatDistanceToNow(new Date(source.last_sync_at), { addSuffix: true })
: 'Never'}
color="primary"
tooltip="When this source was last synchronized"
/>
</Grid>
</Grid>