fix(client): correctly style the document details page for light/dark mode
This commit is contained in:
parent
1178493030
commit
63e3035bac
|
|
@ -19,7 +19,8 @@ import {
|
|||
Error as ErrorIcon,
|
||||
Info as InfoIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { modernTokens } from '../theme';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { useTheme as useMuiTheme } from '@mui/material/styles';
|
||||
|
||||
interface FileIntegrityDisplayProps {
|
||||
fileHash?: string;
|
||||
|
|
@ -43,6 +44,8 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
compact = false,
|
||||
}) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const { modernTokens } = useTheme();
|
||||
const theme = useMuiTheme();
|
||||
|
||||
const copyToClipboard = (text: string) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
|
|
@ -60,7 +63,7 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
return {
|
||||
status: 'unknown',
|
||||
icon: <InfoIcon />,
|
||||
color: modernTokens.colors.neutral[500],
|
||||
color: theme.palette.text.secondary,
|
||||
message: 'Hash not available',
|
||||
};
|
||||
}
|
||||
|
|
@ -70,7 +73,7 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
return {
|
||||
status: 'verified',
|
||||
icon: <CheckIcon />,
|
||||
color: modernTokens.colors.success[500],
|
||||
color: theme.palette.success.main,
|
||||
message: 'File integrity verified',
|
||||
};
|
||||
}
|
||||
|
|
@ -78,7 +81,7 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
return {
|
||||
status: 'warning',
|
||||
icon: <WarningIcon />,
|
||||
color: modernTokens.colors.warning[500],
|
||||
color: theme.palette.warning.main,
|
||||
message: 'Hash format unusual',
|
||||
};
|
||||
};
|
||||
|
|
@ -107,8 +110,8 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
<Paper
|
||||
sx={{
|
||||
p: 2,
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.neutral[50]} 0%, ${modernTokens.colors.primary[50]} 100%)`,
|
||||
border: `1px solid ${modernTokens.colors.neutral[200]}`,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
|
||||
|
|
@ -183,8 +186,8 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
<Paper
|
||||
sx={{
|
||||
p: 3,
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.neutral[50]} 0%, ${modernTokens.colors.primary[50]} 100%)`,
|
||||
border: `1px solid ${modernTokens.colors.neutral[200]}`,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
|
|
@ -194,7 +197,7 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
sx={{
|
||||
fontSize: 24,
|
||||
mr: 1.5,
|
||||
color: modernTokens.colors.primary[500]
|
||||
color: theme.palette.primary.main
|
||||
}}
|
||||
/>
|
||||
<Typography variant="h6" sx={{ fontWeight: 600 }}>
|
||||
|
|
@ -221,7 +224,7 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
sx={{
|
||||
fontSize: 18,
|
||||
mr: 1,
|
||||
color: modernTokens.colors.neutral[600]
|
||||
color: theme.palette.text.secondary
|
||||
}}
|
||||
/>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
|
||||
|
|
@ -235,9 +238,9 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
p: 2,
|
||||
backgroundColor: modernTokens.colors.neutral[100],
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
borderRadius: 1,
|
||||
border: `1px solid ${modernTokens.colors.neutral[200]}`,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
|
|
@ -247,7 +250,7 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
flex: 1,
|
||||
wordBreak: 'break-all',
|
||||
fontSize: '0.8rem',
|
||||
color: modernTokens.colors.neutral[700],
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
{fileHash}
|
||||
|
|
@ -294,8 +297,8 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
size="small"
|
||||
sx={{
|
||||
fontSize: '0.75rem',
|
||||
backgroundColor: modernTokens.colors.neutral[100],
|
||||
border: `1px solid ${modernTokens.colors.neutral[300]}`,
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
|
@ -329,9 +332,9 @@ const FileIntegrityDisplay: React.FC<FileIntegrityDisplayProps> = ({
|
|||
size="small"
|
||||
sx={{
|
||||
fontSize: '0.75rem',
|
||||
backgroundColor: modernTokens.colors.primary[50],
|
||||
color: modernTokens.colors.primary[700],
|
||||
border: `1px solid ${modernTokens.colors.primary[200]}`,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
color: theme.palette.primary.dark,
|
||||
border: `1px solid ${theme.palette.primary.main}`,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ import {
|
|||
Schedule as ScheduleIcon,
|
||||
Person as PersonIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { modernTokens } from '../theme';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { useTheme as useMuiTheme } from '@mui/material/styles';
|
||||
import { documentService } from '../services/api';
|
||||
|
||||
interface ProcessingTimelineProps {
|
||||
|
|
@ -70,6 +71,8 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
const [expanded, setExpanded] = useState(!compact);
|
||||
const [retryHistory, setRetryHistory] = useState<any[]>([]);
|
||||
const [loadingHistory, setLoadingHistory] = useState(false);
|
||||
const { modernTokens } = useTheme();
|
||||
const theme = useMuiTheme();
|
||||
|
||||
const getStatusIcon = (type: string, status: string) => {
|
||||
switch (type) {
|
||||
|
|
@ -90,13 +93,13 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'success':
|
||||
return modernTokens.colors.success[500];
|
||||
return theme.palette.success.main;
|
||||
case 'error':
|
||||
return modernTokens.colors.error[500];
|
||||
return theme.palette.error.main;
|
||||
case 'warning':
|
||||
return modernTokens.colors.warning[500];
|
||||
return theme.palette.warning.main;
|
||||
default:
|
||||
return modernTokens.colors.info[500];
|
||||
return theme.palette.info.main;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -213,8 +216,8 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
<Paper
|
||||
sx={{
|
||||
p: 2,
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.neutral[50]} 0%, ${modernTokens.colors.info[50]} 100%)`,
|
||||
border: `1px solid ${modernTokens.colors.neutral[200]}`,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||
|
|
@ -223,7 +226,7 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
sx={{
|
||||
fontSize: 18,
|
||||
mr: 1,
|
||||
color: modernTokens.colors.primary[500]
|
||||
color: theme.palette.primary.main
|
||||
}}
|
||||
/>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
|
||||
|
|
@ -274,8 +277,8 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
<Paper
|
||||
sx={{
|
||||
p: 3,
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.neutral[50]} 0%, ${modernTokens.colors.info[50]} 100%)`,
|
||||
border: `1px solid ${modernTokens.colors.neutral[200]}`,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
|
|
@ -297,7 +300,7 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
<Chip
|
||||
label={`${events.length} events`}
|
||||
size="small"
|
||||
sx={{ backgroundColor: modernTokens.colors.neutral[100] }}
|
||||
sx={{ backgroundColor: theme.palette.action.hover }}
|
||||
/>
|
||||
{ocrRetryCount > 0 && (
|
||||
<Chip
|
||||
|
|
@ -324,7 +327,7 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
</TimelineDot>
|
||||
{index < events.length - 1 && (
|
||||
<TimelineConnector
|
||||
sx={{ backgroundColor: modernTokens.colors.neutral[300] }}
|
||||
sx={{ backgroundColor: theme.palette.action.selected }}
|
||||
/>
|
||||
)}
|
||||
</TimelineSeparator>
|
||||
|
|
@ -347,7 +350,7 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
|
||||
{event.metadata?.userId && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mt: 1 }}>
|
||||
<PersonIcon sx={{ fontSize: 14, mr: 0.5, color: modernTokens.colors.neutral[500] }} />
|
||||
<PersonIcon sx={{ fontSize: 14, mr: 0.5, color: theme.palette.text.secondary }} />
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
User: {event.metadata.userId.substring(0, 8)}...
|
||||
</Typography>
|
||||
|
|
@ -366,7 +369,7 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
|
||||
{/* Retry History Section */}
|
||||
{ocrRetryCount > 0 && (
|
||||
<Box sx={{ mt: 3, pt: 2, borderTop: `1px solid ${modernTokens.colors.neutral[200]}` }}>
|
||||
<Box sx={{ mt: 3, pt: 2, borderTop: `1px solid ${theme.palette.divider}` }}>
|
||||
<Button
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
endIcon={<ExpandIcon sx={{ transform: expanded ? 'rotate(180deg)' : 'none' }} />}
|
||||
|
|
@ -390,8 +393,8 @@ const ProcessingTimeline: React.FC<ProcessingTimelineProps> = ({
|
|||
key={retry.id}
|
||||
sx={{
|
||||
p: 2,
|
||||
backgroundColor: modernTokens.colors.neutral[50],
|
||||
border: `1px solid ${modernTokens.colors.neutral[200]}`,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle2">
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||
import { createTheme, Theme, ThemeProvider as MuiThemeProvider } from '@mui/material/styles';
|
||||
import { PaletteMode } from '@mui/material';
|
||||
import { modernTokens } from '../theme';
|
||||
|
||||
interface ThemeContextType {
|
||||
mode: PaletteMode;
|
||||
toggleTheme: () => void;
|
||||
modernTokens: typeof modernTokens;
|
||||
glassEffect: (alphaValue?: number) => object;
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
||||
|
|
@ -21,19 +24,53 @@ interface ThemeProviderProps {
|
|||
children: ReactNode;
|
||||
}
|
||||
|
||||
// Glassmorphism effect that adapts to theme mode
|
||||
const createGlassEffect = (mode: PaletteMode) => (alphaValue: number = 0.1) => ({
|
||||
background: mode === 'light'
|
||||
? `rgba(255, 255, 255, ${alphaValue})`
|
||||
: `rgba(30, 30, 30, ${alphaValue})`,
|
||||
backdropFilter: 'blur(10px)',
|
||||
border: mode === 'light'
|
||||
? '1px solid rgba(255, 255, 255, 0.2)'
|
||||
: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
boxShadow: mode === 'light'
|
||||
? modernTokens.shadows.glass
|
||||
: '0 8px 32px 0 rgba(0, 0, 0, 0.37)',
|
||||
});
|
||||
|
||||
const createAppTheme = (mode: PaletteMode): Theme => {
|
||||
return createTheme({
|
||||
palette: {
|
||||
mode,
|
||||
primary: {
|
||||
main: '#667eea',
|
||||
light: '#9bb5ff',
|
||||
dark: '#304ffe',
|
||||
main: modernTokens.colors.primary[500],
|
||||
light: modernTokens.colors.primary[300],
|
||||
dark: modernTokens.colors.primary[700],
|
||||
50: modernTokens.colors.primary[50],
|
||||
100: modernTokens.colors.primary[100],
|
||||
200: modernTokens.colors.primary[200],
|
||||
300: modernTokens.colors.primary[300],
|
||||
400: modernTokens.colors.primary[400],
|
||||
500: modernTokens.colors.primary[500],
|
||||
600: modernTokens.colors.primary[600],
|
||||
700: modernTokens.colors.primary[700],
|
||||
800: modernTokens.colors.primary[800],
|
||||
900: modernTokens.colors.primary[900],
|
||||
},
|
||||
secondary: {
|
||||
main: '#764ba2',
|
||||
light: '#a777d9',
|
||||
dark: '#4c1e74',
|
||||
main: modernTokens.colors.secondary[500],
|
||||
light: modernTokens.colors.secondary[300],
|
||||
dark: modernTokens.colors.secondary[700],
|
||||
50: modernTokens.colors.secondary[50],
|
||||
100: modernTokens.colors.secondary[100],
|
||||
200: modernTokens.colors.secondary[200],
|
||||
300: modernTokens.colors.secondary[300],
|
||||
400: modernTokens.colors.secondary[400],
|
||||
500: modernTokens.colors.secondary[500],
|
||||
600: modernTokens.colors.secondary[600],
|
||||
700: modernTokens.colors.secondary[700],
|
||||
800: modernTokens.colors.secondary[800],
|
||||
900: modernTokens.colors.secondary[900],
|
||||
},
|
||||
background: {
|
||||
default: mode === 'light' ? '#fafafa' : '#121212',
|
||||
|
|
@ -43,6 +80,66 @@ const createAppTheme = (mode: PaletteMode): Theme => {
|
|||
primary: mode === 'light' ? '#333333' : '#f8fafc',
|
||||
secondary: mode === 'light' ? '#666666' : '#cbd5e1',
|
||||
},
|
||||
success: {
|
||||
main: modernTokens.colors.success[500],
|
||||
light: modernTokens.colors.success[50],
|
||||
dark: modernTokens.colors.success[600],
|
||||
50: modernTokens.colors.success[50],
|
||||
100: mode === 'light' ? '#dcfce7' : '#14532d',
|
||||
200: mode === 'light' ? '#bbf7d0' : '#166534',
|
||||
300: mode === 'light' ? '#86efac' : '#15803d',
|
||||
400: mode === 'light' ? '#4ade80' : '#16a34a',
|
||||
500: modernTokens.colors.success[500],
|
||||
600: modernTokens.colors.success[600],
|
||||
700: mode === 'light' ? '#15803d' : '#4ade80',
|
||||
800: mode === 'light' ? '#166534' : '#86efac',
|
||||
900: mode === 'light' ? '#14532d' : '#dcfce7',
|
||||
},
|
||||
warning: {
|
||||
main: modernTokens.colors.warning[500],
|
||||
light: modernTokens.colors.warning[50],
|
||||
dark: modernTokens.colors.warning[600],
|
||||
50: modernTokens.colors.warning[50],
|
||||
100: mode === 'light' ? '#fef3c7' : '#78350f',
|
||||
200: mode === 'light' ? '#fde68a' : '#92400e',
|
||||
300: mode === 'light' ? '#fcd34d' : '#b45309',
|
||||
400: mode === 'light' ? '#fbbf24' : '#d97706',
|
||||
500: modernTokens.colors.warning[500],
|
||||
600: modernTokens.colors.warning[600],
|
||||
700: mode === 'light' ? '#b45309' : '#fbbf24',
|
||||
800: mode === 'light' ? '#92400e' : '#fcd34d',
|
||||
900: mode === 'light' ? '#78350f' : '#fef3c7',
|
||||
},
|
||||
error: {
|
||||
main: modernTokens.colors.error[500],
|
||||
light: modernTokens.colors.error[50],
|
||||
dark: modernTokens.colors.error[600],
|
||||
50: modernTokens.colors.error[50],
|
||||
100: mode === 'light' ? '#fee2e2' : '#7f1d1d',
|
||||
200: mode === 'light' ? '#fecaca' : '#991b1b',
|
||||
300: mode === 'light' ? '#fca5a5' : '#b91c1c',
|
||||
400: mode === 'light' ? '#f87171' : '#dc2626',
|
||||
500: modernTokens.colors.error[500],
|
||||
600: modernTokens.colors.error[600],
|
||||
700: mode === 'light' ? '#b91c1c' : '#f87171',
|
||||
800: mode === 'light' ? '#991b1b' : '#fca5a5',
|
||||
900: mode === 'light' ? '#7f1d1d' : '#fee2e2',
|
||||
},
|
||||
info: {
|
||||
main: modernTokens.colors.info[500],
|
||||
light: modernTokens.colors.info[50],
|
||||
dark: modernTokens.colors.info[600],
|
||||
50: modernTokens.colors.info[50],
|
||||
100: mode === 'light' ? '#dbeafe' : '#1e3a8a',
|
||||
200: mode === 'light' ? '#bfdbfe' : '#1e40af',
|
||||
300: mode === 'light' ? '#93c5fd' : '#1d4ed8',
|
||||
400: mode === 'light' ? '#60a5fa' : '#2563eb',
|
||||
500: modernTokens.colors.info[500],
|
||||
600: modernTokens.colors.info[600],
|
||||
700: mode === 'light' ? '#1d4ed8' : '#60a5fa',
|
||||
800: mode === 'light' ? '#1e40af' : '#93c5fd',
|
||||
900: mode === 'light' ? '#1e3a8a' : '#dbeafe',
|
||||
},
|
||||
divider: mode === 'light' ? 'rgba(0, 0, 0, 0.12)' : 'rgba(255, 255, 255, 0.12)',
|
||||
},
|
||||
typography: {
|
||||
|
|
@ -148,6 +245,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
|
|||
};
|
||||
|
||||
const theme = createAppTheme(mode);
|
||||
const glassEffect = createGlassEffect(mode);
|
||||
|
||||
// Listen for system theme changes
|
||||
useEffect(() => {
|
||||
|
|
@ -164,7 +262,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ mode, toggleTheme }}>
|
||||
<ThemeContext.Provider value={{ mode, toggleTheme, modernTokens, glassEffect }}>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
{children}
|
||||
</MuiThemeProvider>
|
||||
|
|
|
|||
|
|
@ -56,12 +56,15 @@ import MetadataParser from '../components/MetadataParser';
|
|||
import FileIntegrityDisplay from '../components/FileIntegrityDisplay';
|
||||
import ProcessingTimeline from '../components/ProcessingTimeline';
|
||||
import { RetryHistoryModal } from '../components/RetryHistoryModal';
|
||||
import { modernTokens, glassEffect } from '../theme';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { useTheme as useMuiTheme } from '@mui/material/styles';
|
||||
import api from '../services/api';
|
||||
|
||||
const DocumentDetailsPage: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { mode, modernTokens, glassEffect } = useTheme();
|
||||
const theme = useMuiTheme();
|
||||
const [document, setDocument] = useState<Document | null>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
|
@ -335,7 +338,7 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
<Box
|
||||
sx={{
|
||||
minHeight: '100vh',
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.primary[50]} 0%, ${modernTokens.colors.secondary[50]} 100%)`,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="xl" sx={{ py: 4 }}>
|
||||
|
|
@ -347,9 +350,9 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
onClick={() => navigate('/documents')}
|
||||
sx={{
|
||||
mb: 3,
|
||||
color: modernTokens.colors.neutral[600],
|
||||
color: theme.palette.text.secondary,
|
||||
'&:hover': {
|
||||
backgroundColor: modernTokens.colors.neutral[100],
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
|
@ -358,10 +361,10 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||
<Typography
|
||||
variant="h2"
|
||||
variant="h4"
|
||||
sx={{
|
||||
fontWeight: 800,
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.primary[600]} 0%, ${modernTokens.colors.secondary[600]} 100%)`,
|
||||
fontWeight: 700,
|
||||
background: `linear-gradient(135deg, ${theme.palette.primary.main} 0%, ${theme.palette.secondary.main} 100%)`,
|
||||
backgroundClip: 'text',
|
||||
WebkitBackgroundClip: 'text',
|
||||
color: 'transparent',
|
||||
|
|
@ -377,11 +380,12 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
<IconButton
|
||||
onClick={handleDownload}
|
||||
sx={{
|
||||
...glassEffect(0.2),
|
||||
color: modernTokens.colors.primary[600],
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
backdropFilter: 'blur(10px)',
|
||||
color: theme.palette.primary.main,
|
||||
'&:hover': {
|
||||
transform: 'scale(1.05)',
|
||||
backgroundColor: modernTokens.colors.primary[100],
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
|
@ -393,11 +397,12 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
<IconButton
|
||||
onClick={handleViewDocument}
|
||||
sx={{
|
||||
...glassEffect(0.2),
|
||||
color: modernTokens.colors.primary[600],
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
backdropFilter: 'blur(10px)',
|
||||
color: theme.palette.primary.main,
|
||||
'&:hover': {
|
||||
transform: 'scale(1.05)',
|
||||
backgroundColor: modernTokens.colors.primary[100],
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
|
@ -410,11 +415,12 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
<IconButton
|
||||
onClick={handleViewOcr}
|
||||
sx={{
|
||||
...glassEffect(0.2),
|
||||
color: modernTokens.colors.secondary[600],
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
backdropFilter: 'blur(10px)',
|
||||
color: theme.palette.secondary.main,
|
||||
'&:hover': {
|
||||
transform: 'scale(1.05)',
|
||||
backgroundColor: modernTokens.colors.secondary[100],
|
||||
backgroundColor: theme.palette.secondary.light,
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
|
@ -438,9 +444,9 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
<Grid item xs={12} lg={5}>
|
||||
<Card
|
||||
sx={{
|
||||
...glassEffect(0.3),
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
backdropFilter: 'blur(10px)',
|
||||
height: 'fit-content',
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.neutral[0]} 0%, ${modernTokens.colors.primary[50]} 100%)`,
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ p: 4 }}>
|
||||
|
|
@ -452,7 +458,7 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
justifyContent: 'center',
|
||||
mb: 4,
|
||||
p: 4,
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.primary[100]} 0%, ${modernTokens.colors.secondary[100]} 100%)`,
|
||||
background: `linear-gradient(135deg, ${theme.palette.primary.light} 0%, ${theme.palette.secondary.light} 100%)`,
|
||||
borderRadius: 3,
|
||||
minHeight: 280,
|
||||
position: 'relative',
|
||||
|
|
@ -481,15 +487,15 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
objectFit: 'contain',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
boxShadow: modernTokens.shadows.lg,
|
||||
boxShadow: theme.shadows[8],
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.transform = 'scale(1.05) rotateY(5deg)';
|
||||
e.currentTarget.style.boxShadow = modernTokens.shadows.xl;
|
||||
e.currentTarget.style.boxShadow = theme.shadows[12];
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.transform = 'scale(1) rotateY(0deg)';
|
||||
e.currentTarget.style.boxShadow = modernTokens.shadows.lg;
|
||||
e.currentTarget.style.boxShadow = theme.shadows[8];
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -503,7 +509,7 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<Box sx={{ fontSize: 120, color: modernTokens.colors.primary[400], display: 'flex' }}>
|
||||
<Box sx={{ fontSize: 120, color: theme.palette.primary.main, display: 'flex' }}>
|
||||
{getFileIcon(document.mime_type)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
|
@ -515,10 +521,10 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
<Chip
|
||||
label={document.mime_type}
|
||||
sx={{
|
||||
backgroundColor: modernTokens.colors.primary[100],
|
||||
color: modernTokens.colors.primary[700],
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
color: theme.palette.primary.dark,
|
||||
fontWeight: 600,
|
||||
border: `1px solid ${modernTokens.colors.primary[300]}`,
|
||||
border: `1px solid ${theme.palette.primary.main}`,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
|
@ -566,10 +572,10 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
onClick={handleViewProcessedImage}
|
||||
disabled={processedImageLoading}
|
||||
sx={{
|
||||
backgroundColor: modernTokens.colors.secondary[100],
|
||||
color: modernTokens.colors.secondary[600],
|
||||
backgroundColor: theme.palette.secondary.light,
|
||||
color: theme.palette.secondary.dark,
|
||||
'&:hover': {
|
||||
backgroundColor: modernTokens.colors.secondary[200],
|
||||
backgroundColor: theme.palette.secondary[200],
|
||||
transform: 'scale(1.1)',
|
||||
},
|
||||
}}
|
||||
|
|
@ -588,10 +594,10 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
onClick={handleRetryOcr}
|
||||
disabled={retryingOcr}
|
||||
sx={{
|
||||
backgroundColor: modernTokens.colors.warning[100],
|
||||
color: modernTokens.colors.warning[600],
|
||||
backgroundColor: theme.palette.warning.light,
|
||||
color: theme.palette.warning.dark,
|
||||
'&:hover': {
|
||||
backgroundColor: modernTokens.colors.warning[200],
|
||||
backgroundColor: theme.palette.warning[200],
|
||||
transform: 'scale(1.1)',
|
||||
},
|
||||
}}
|
||||
|
|
@ -608,10 +614,10 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
<IconButton
|
||||
onClick={handleShowRetryHistory}
|
||||
sx={{
|
||||
backgroundColor: modernTokens.colors.info[100],
|
||||
color: modernTokens.colors.info[600],
|
||||
backgroundColor: theme.palette.info.light,
|
||||
color: theme.palette.info.dark,
|
||||
'&:hover': {
|
||||
backgroundColor: modernTokens.colors.info[200],
|
||||
backgroundColor: theme.palette.info[200],
|
||||
transform: 'scale(1.1)',
|
||||
},
|
||||
}}
|
||||
|
|
@ -622,21 +628,194 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
</Stack>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* File Integrity Display - Moved here */}
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<FileIntegrityDisplay
|
||||
fileHash={document.file_hash}
|
||||
fileName={document.original_filename}
|
||||
fileSize={document.file_size}
|
||||
mimeType={document.mime_type}
|
||||
createdAt={document.created_at}
|
||||
updatedAt={document.updated_at}
|
||||
userId={document.user_id}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<Grid item xs={12} lg={7}>
|
||||
<Stack spacing={4}>
|
||||
{/* File Integrity Display */}
|
||||
<FileIntegrityDisplay
|
||||
fileHash={document.file_hash}
|
||||
fileName={document.original_filename}
|
||||
fileSize={document.file_size}
|
||||
mimeType={document.mime_type}
|
||||
createdAt={document.created_at}
|
||||
updatedAt={document.updated_at}
|
||||
userId={document.user_id}
|
||||
/>
|
||||
<Stack spacing={4}>
|
||||
{/* OCR Text Section - Moved higher */}
|
||||
{document.has_ocr_text && (
|
||||
<Card
|
||||
sx={{
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
backdropFilter: 'blur(10px)',
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ p: 4 }}>
|
||||
<Typography variant="h5" sx={{ mb: 3, fontWeight: 700 }}>
|
||||
🔍 Extracted Text (OCR)
|
||||
</Typography>
|
||||
|
||||
{ocrLoading ? (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }}>
|
||||
<CircularProgress size={32} sx={{ mr: 2 }} />
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
Loading OCR analysis...
|
||||
</Typography>
|
||||
</Box>
|
||||
) : ocrData ? (
|
||||
<>
|
||||
{/* Enhanced OCR Stats */}
|
||||
<Box sx={{ mb: 4, display: 'flex', gap: 2, flexWrap: 'wrap' }}>
|
||||
{ocrData.ocr_confidence && (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
backgroundColor: mode === 'light' ? modernTokens.colors.primary[100] : modernTokens.colors.primary[800],
|
||||
border: `1px solid ${mode === 'light' ? modernTokens.colors.primary[300] : modernTokens.colors.primary[600]}`,
|
||||
textAlign: 'center',
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, color: mode === 'light' ? modernTokens.colors.primary[700] : modernTokens.colors.primary[300] }}>
|
||||
{Math.round(ocrData.ocr_confidence)}%
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Confidence
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{ocrData.ocr_word_count && (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
backgroundColor: mode === 'light' ? modernTokens.colors.secondary[100] : modernTokens.colors.secondary[800],
|
||||
border: `1px solid ${mode === 'light' ? modernTokens.colors.secondary[300] : modernTokens.colors.secondary[600]}`,
|
||||
textAlign: 'center',
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, color: mode === 'light' ? modernTokens.colors.secondary[700] : modernTokens.colors.secondary[300] }}>
|
||||
{ocrData.ocr_word_count.toLocaleString()}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Words
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{ocrData.ocr_processing_time_ms && (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
backgroundColor: mode === 'light' ? modernTokens.colors.info[100] : modernTokens.colors.info[800],
|
||||
border: `1px solid ${mode === 'light' ? modernTokens.colors.info[300] : modernTokens.colors.info[600]}`,
|
||||
textAlign: 'center',
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, color: mode === 'light' ? modernTokens.colors.info[700] : modernTokens.colors.info[300] }}>
|
||||
{ocrData.ocr_processing_time_ms}ms
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Processing Time
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* OCR Error Display */}
|
||||
{ocrData.ocr_error && (
|
||||
<Alert
|
||||
severity="error"
|
||||
sx={{
|
||||
mb: 3,
|
||||
borderRadius: 2,
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
|
||||
OCR Processing Error
|
||||
</Typography>
|
||||
<Typography variant="body2">{ocrData.ocr_error}</Typography>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Full OCR Text Display */}
|
||||
<Paper
|
||||
sx={{
|
||||
p: 4,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
borderRadius: 3,
|
||||
maxHeight: 400,
|
||||
overflow: 'auto',
|
||||
// Custom scrollbar styling
|
||||
'&::-webkit-scrollbar': {
|
||||
width: '8px',
|
||||
},
|
||||
'&::-webkit-scrollbar-track': {
|
||||
backgroundColor: mode === 'light' ? modernTokens.colors.neutral[100] : modernTokens.colors.neutral[800],
|
||||
borderRadius: '4px',
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
backgroundColor: mode === 'light' ? modernTokens.colors.neutral[300] : modernTokens.colors.neutral[600],
|
||||
borderRadius: '4px',
|
||||
'&:hover': {
|
||||
backgroundColor: mode === 'light' ? modernTokens.colors.neutral[400] : modernTokens.colors.neutral[500],
|
||||
},
|
||||
},
|
||||
// Firefox scrollbar styling
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: mode === 'light'
|
||||
? `${modernTokens.colors.neutral[300]} ${modernTokens.colors.neutral[100]}`
|
||||
: `${modernTokens.colors.neutral[600]} ${modernTokens.colors.neutral[800]}`,
|
||||
}}
|
||||
>
|
||||
{ocrData.ocr_text ? (
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
fontFamily: '"Inter", monospace',
|
||||
whiteSpace: 'pre-wrap',
|
||||
lineHeight: 1.8,
|
||||
fontSize: '0.95rem',
|
||||
}}
|
||||
>
|
||||
{ocrData.ocr_text}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography variant="body1" color="text.secondary" sx={{ fontStyle: 'italic', textAlign: 'center', py: 4 }}>
|
||||
No OCR text available for this document.
|
||||
</Typography>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
{/* Processing Info */}
|
||||
{ocrData.ocr_completed_at && (
|
||||
<Box sx={{ mt: 3, pt: 3, borderTop: `1px solid ${theme.palette.divider}` }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
✅ Processing completed: {new Date(ocrData.ocr_completed_at).toLocaleString()}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Alert
|
||||
severity="info"
|
||||
sx={{
|
||||
borderRadius: 2,
|
||||
}}
|
||||
>
|
||||
OCR text is available but failed to load. Please try refreshing the page.
|
||||
</Alert>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Processing Timeline */}
|
||||
<ProcessingTimeline
|
||||
|
|
@ -654,8 +833,8 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
{document.source_metadata && Object.keys(document.source_metadata).length > 0 && (
|
||||
<Card
|
||||
sx={{
|
||||
...glassEffect(0.2),
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.neutral[0]} 0%, ${modernTokens.colors.info[50]} 100%)`,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
backdropFilter: 'blur(10px)',
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ p: 4 }}>
|
||||
|
|
@ -673,8 +852,8 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
{/* Tags and Labels */}
|
||||
<Card
|
||||
sx={{
|
||||
...glassEffect(0.2),
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.neutral[0]} 0%, ${modernTokens.colors.secondary[50]} 100%)`,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
backdropFilter: 'blur(10px)',
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ p: 4 }}>
|
||||
|
|
@ -686,10 +865,10 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
startIcon={<EditIcon />}
|
||||
onClick={() => setShowLabelDialog(true)}
|
||||
sx={{
|
||||
backgroundColor: modernTokens.colors.secondary[100],
|
||||
color: modernTokens.colors.secondary[700],
|
||||
backgroundColor: theme.palette.secondary.light,
|
||||
color: theme.palette.secondary.dark,
|
||||
'&:hover': {
|
||||
backgroundColor: modernTokens.colors.secondary[200],
|
||||
backgroundColor: theme.palette.secondary[200],
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
|
@ -709,9 +888,9 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
key={index}
|
||||
label={tag}
|
||||
sx={{
|
||||
backgroundColor: modernTokens.colors.primary[100],
|
||||
color: modernTokens.colors.primary[700],
|
||||
border: `1px solid ${modernTokens.colors.primary[300]}`,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
color: theme.palette.primary.dark,
|
||||
border: `1px solid ${theme.palette.primary.main}`,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
/>
|
||||
|
|
@ -753,212 +932,6 @@ const DocumentDetailsPage: React.FC = () => {
|
|||
</Grid>
|
||||
</Fade>
|
||||
|
||||
{/* OCR Text Section */}
|
||||
{document.has_ocr_text && (
|
||||
<Fade in timeout={1000}>
|
||||
<Card
|
||||
sx={{
|
||||
mt: 4,
|
||||
...glassEffect(0.2),
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.neutral[0]} 0%, ${modernTokens.colors.success[50]} 100%)`,
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ p: 4 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 3 }}>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700 }}>
|
||||
🔍 Extracted Text (OCR)
|
||||
</Typography>
|
||||
<Button
|
||||
startIcon={<SpeedIcon />}
|
||||
onClick={handleViewOcr}
|
||||
sx={{
|
||||
backgroundColor: modernTokens.colors.success[100],
|
||||
color: modernTokens.colors.success[700],
|
||||
'&:hover': {
|
||||
backgroundColor: modernTokens.colors.success[200],
|
||||
},
|
||||
}}
|
||||
>
|
||||
View Full Text
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{ocrLoading ? (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }}>
|
||||
<CircularProgress size={32} sx={{ mr: 2 }} />
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
Loading OCR analysis...
|
||||
</Typography>
|
||||
</Box>
|
||||
) : ocrData ? (
|
||||
<>
|
||||
{/* Enhanced OCR Stats */}
|
||||
<Box sx={{ mb: 4, display: 'flex', gap: 2, flexWrap: 'wrap' }}>
|
||||
{ocrData.ocr_confidence && (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
backgroundColor: modernTokens.colors.primary[100],
|
||||
border: `1px solid ${modernTokens.colors.primary[300]}`,
|
||||
textAlign: 'center',
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, color: modernTokens.colors.primary[700] }}>
|
||||
{Math.round(ocrData.ocr_confidence)}%
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Confidence
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{ocrData.ocr_word_count && (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
backgroundColor: modernTokens.colors.secondary[100],
|
||||
border: `1px solid ${modernTokens.colors.secondary[300]}`,
|
||||
textAlign: 'center',
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, color: modernTokens.colors.secondary[700] }}>
|
||||
{ocrData.ocr_word_count.toLocaleString()}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Words
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{ocrData.ocr_processing_time_ms && (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
backgroundColor: modernTokens.colors.info[100],
|
||||
border: `1px solid ${modernTokens.colors.info[300]}`,
|
||||
textAlign: 'center',
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, color: modernTokens.colors.info[700] }}>
|
||||
{ocrData.ocr_processing_time_ms}ms
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Processing Time
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* OCR Error Display */}
|
||||
{ocrData.ocr_error && (
|
||||
<Alert
|
||||
severity="error"
|
||||
sx={{
|
||||
mb: 3,
|
||||
borderRadius: 2,
|
||||
backgroundColor: modernTokens.colors.error[50],
|
||||
border: `1px solid ${modernTokens.colors.error[200]}`,
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
|
||||
OCR Processing Error
|
||||
</Typography>
|
||||
<Typography variant="body2">{ocrData.ocr_error}</Typography>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* OCR Text Preview */}
|
||||
<Paper
|
||||
sx={{
|
||||
p: 4,
|
||||
background: `linear-gradient(135deg, ${modernTokens.colors.neutral[50]} 0%, ${modernTokens.colors.neutral[100]} 100%)`,
|
||||
border: `1px solid ${modernTokens.colors.neutral[300]}`,
|
||||
borderRadius: 3,
|
||||
maxHeight: 300,
|
||||
overflow: 'auto',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{ocrData.ocr_text ? (
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
fontFamily: '"Inter", monospace',
|
||||
whiteSpace: 'pre-wrap',
|
||||
lineHeight: 1.8,
|
||||
color: modernTokens.colors.neutral[800],
|
||||
fontSize: '0.95rem',
|
||||
}}
|
||||
>
|
||||
{ocrData.ocr_text.length > 500
|
||||
? `${ocrData.ocr_text.substring(0, 500)}...`
|
||||
: ocrData.ocr_text
|
||||
}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography variant="body1" color="text.secondary" sx={{ fontStyle: 'italic', textAlign: 'center', py: 4 }}>
|
||||
No OCR text available for this document.
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{ocrData.ocr_text && ocrData.ocr_text.length > 500 && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 60,
|
||||
background: `linear-gradient(transparent, ${modernTokens.colors.neutral[100]})`,
|
||||
display: 'flex',
|
||||
alignItems: 'end',
|
||||
justifyContent: 'center',
|
||||
pb: 2,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={handleViewOcr}
|
||||
size="small"
|
||||
sx={{
|
||||
backgroundColor: modernTokens.colors.neutral[0],
|
||||
boxShadow: modernTokens.shadows.sm,
|
||||
}}
|
||||
>
|
||||
View Full Text
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
{/* Processing Info */}
|
||||
{ocrData.ocr_completed_at && (
|
||||
<Box sx={{ mt: 3, pt: 3, borderTop: `1px solid ${modernTokens.colors.neutral[200]}` }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
✅ Processing completed: {new Date(ocrData.ocr_completed_at).toLocaleString()}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Alert
|
||||
severity="info"
|
||||
sx={{
|
||||
borderRadius: 2,
|
||||
backgroundColor: modernTokens.colors.info[50],
|
||||
border: `1px solid ${modernTokens.colors.info[200]}`,
|
||||
}}
|
||||
>
|
||||
OCR text is available but failed to load. Try clicking the "View Full Text" button above.
|
||||
</Alert>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Fade>
|
||||
)}
|
||||
</Container>
|
||||
|
||||
{/* OCR Text Dialog */}
|
||||
|
|
|
|||
|
|
@ -43,23 +43,51 @@ export const modernTokens = {
|
|||
},
|
||||
success: {
|
||||
50: '#f0fdf4',
|
||||
100: '#dcfce7',
|
||||
200: '#bbf7d0',
|
||||
300: '#86efac',
|
||||
400: '#4ade80',
|
||||
500: '#22c55e',
|
||||
600: '#16a34a',
|
||||
700: '#15803d',
|
||||
800: '#166534',
|
||||
900: '#14532d',
|
||||
},
|
||||
warning: {
|
||||
50: '#fffbeb',
|
||||
100: '#fef3c7',
|
||||
200: '#fde68a',
|
||||
300: '#fcd34d',
|
||||
400: '#fbbf24',
|
||||
500: '#f59e0b',
|
||||
600: '#d97706',
|
||||
700: '#b45309',
|
||||
800: '#92400e',
|
||||
900: '#78350f',
|
||||
},
|
||||
error: {
|
||||
50: '#fef2f2',
|
||||
100: '#fee2e2',
|
||||
200: '#fecaca',
|
||||
300: '#fca5a5',
|
||||
400: '#f87171',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
800: '#991b1b',
|
||||
900: '#7f1d1d',
|
||||
},
|
||||
info: {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
200: '#bfdbfe',
|
||||
300: '#93c5fd',
|
||||
400: '#60a5fa',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
900: '#1e3a8a',
|
||||
},
|
||||
},
|
||||
shadows: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue