feat(client): make frontend a little more modern
This commit is contained in:
parent
57c6b370d2
commit
cc74744bba
|
|
@ -375,19 +375,36 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
minWidth: 300,
|
minWidth: 320,
|
||||||
maxWidth: 400,
|
maxWidth: 420,
|
||||||
'& .MuiOutlinedInput-root': {
|
'& .MuiOutlinedInput-root': {
|
||||||
backgroundColor: 'background.paper',
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(248,250,252,0.90) 100%)',
|
||||||
transition: 'all 0.2s ease-in-out',
|
backdropFilter: 'blur(20px)',
|
||||||
|
border: '1px solid rgba(226,232,240,0.5)',
|
||||||
|
borderRadius: 3,
|
||||||
|
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
boxShadow: '0 4px 16px rgba(0,0,0,0.04)',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'background.paper',
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.98) 0%, rgba(248,250,252,0.95) 100%)',
|
||||||
borderColor: 'primary.main',
|
borderColor: 'rgba(99,102,241,0.4)',
|
||||||
|
transform: 'translateY(-2px)',
|
||||||
|
boxShadow: '0 8px 32px rgba(99,102,241,0.15)',
|
||||||
},
|
},
|
||||||
'&.Mui-focused': {
|
'&.Mui-focused': {
|
||||||
backgroundColor: 'background.paper',
|
background: 'linear-gradient(135deg, rgba(255,255,255,1) 0%, rgba(248,250,252,0.98) 100%)',
|
||||||
borderColor: 'primary.main',
|
borderColor: '#6366f1',
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
|
transform: 'translateY(-2px)',
|
||||||
|
boxShadow: '0 12px 40px rgba(99,102,241,0.2)',
|
||||||
|
},
|
||||||
|
'& .MuiInputBase-input': {
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: '0.025em',
|
||||||
|
fontSize: '0.95rem',
|
||||||
|
'&::placeholder': {
|
||||||
|
color: 'rgba(148,163,184,0.8)',
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
@ -423,20 +440,40 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
{({ TransitionProps }) => (
|
{({ TransitionProps }) => (
|
||||||
<Grow {...TransitionProps}>
|
<Grow {...TransitionProps}>
|
||||||
<Paper
|
<Paper
|
||||||
elevation={8}
|
elevation={0}
|
||||||
sx={{
|
sx={{
|
||||||
mt: 1,
|
mt: 1,
|
||||||
maxHeight: 400,
|
maxHeight: 420,
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
border: '1px solid',
|
background: 'linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(248,250,252,0.95) 100%)',
|
||||||
borderColor: 'divider',
|
backdropFilter: 'blur(24px)',
|
||||||
|
border: '1px solid rgba(226,232,240,0.6)',
|
||||||
|
borderRadius: 3,
|
||||||
|
boxShadow: '0 20px 60px rgba(0,0,0,0.12), 0 8px 25px rgba(0,0,0,0.08)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(loading || isTyping) && (
|
{(loading || isTyping) && (
|
||||||
<Box sx={{ p: 2, textAlign: 'center' }}>
|
<Box sx={{
|
||||||
<Stack spacing={1} alignItems="center">
|
p: 3,
|
||||||
<CircularProgress size={20} />
|
textAlign: 'center',
|
||||||
<Typography variant="body2" color="text.secondary">
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.02) 0%, rgba(139,92,246,0.02) 100%)',
|
||||||
|
}}>
|
||||||
|
<Stack spacing={1.5} alignItems="center">
|
||||||
|
<Box sx={{
|
||||||
|
p: 1.5,
|
||||||
|
borderRadius: 2,
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.1) 0%, rgba(139,92,246,0.1) 100%)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}>
|
||||||
|
<CircularProgress size={20} thickness={4} sx={{ color: '#6366f1' }} />
|
||||||
|
</Box>
|
||||||
|
<Typography variant="body2" sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: '0.025em',
|
||||||
|
}}>
|
||||||
{isTyping ? 'Searching as you type...' : 'Searching...'}
|
{isTyping ? 'Searching as you type...' : 'Searching...'}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
@ -461,18 +498,40 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading && !isTyping && query && results.length === 0 && (
|
{!loading && !isTyping && query && results.length === 0 && (
|
||||||
<Box sx={{ p: 2, textAlign: 'center' }}>
|
<Box sx={{
|
||||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
p: 3,
|
||||||
|
textAlign: 'center',
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.02) 0%, rgba(139,92,246,0.02) 100%)',
|
||||||
|
}}>
|
||||||
|
<Typography variant="body2" sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: '0.025em',
|
||||||
|
mb: 1,
|
||||||
|
}}>
|
||||||
No documents found for "{query}"
|
No documents found for "{query}"
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 2, display: 'block' }}>
|
<Typography variant="caption" sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
fontWeight: 500,
|
||||||
|
mb: 2,
|
||||||
|
display: 'block',
|
||||||
|
}}>
|
||||||
Press Enter to search with advanced options
|
Press Enter to search with advanced options
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{/* Smart suggestions for no results */}
|
{/* Smart suggestions for no results */}
|
||||||
{suggestions.length > 0 && (
|
{suggestions.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Typography variant="caption" color="text.primary" gutterBottom sx={{ display: 'block' }}>
|
<Typography variant="caption" sx={{
|
||||||
|
color: 'text.primary',
|
||||||
|
fontWeight: 600,
|
||||||
|
letterSpacing: '0.05em',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
mb: 1.5,
|
||||||
|
display: 'block',
|
||||||
|
}}>
|
||||||
Try these suggestions:
|
Try these suggestions:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Stack direction="row" spacing={0.5} justifyContent="center" flexWrap="wrap">
|
<Stack direction="row" spacing={0.5} justifyContent="center" flexWrap="wrap">
|
||||||
|
|
@ -484,7 +543,21 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
clickable
|
clickable
|
||||||
onClick={() => handleSuggestionClick(suggestion)}
|
onClick={() => handleSuggestionClick(suggestion)}
|
||||||
sx={{ fontSize: '0.7rem', height: 20 }}
|
sx={{
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
height: 24,
|
||||||
|
fontWeight: 500,
|
||||||
|
border: '1px solid rgba(99,102,241,0.3)',
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(248,250,252,0.6) 100%)',
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
'&:hover': {
|
||||||
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
|
color: 'white',
|
||||||
|
transform: 'translateY(-2px)',
|
||||||
|
boxShadow: '0 8px 24px rgba(99,102,241,0.2)',
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
@ -495,14 +568,35 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
|
|
||||||
{!loading && !isTyping && results.length > 0 && (
|
{!loading && !isTyping && results.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Box sx={{ p: 1, borderBottom: '1px solid', borderColor: 'divider' }}>
|
<Box sx={{
|
||||||
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ px: 1 }}>
|
p: 2,
|
||||||
<Typography variant="caption" color="text.secondary">
|
borderBottom: '1px solid rgba(226,232,240,0.4)',
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.03) 0%, rgba(139,92,246,0.03) 100%)',
|
||||||
|
}}>
|
||||||
|
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||||
|
<Typography variant="caption" sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
fontWeight: 600,
|
||||||
|
letterSpacing: '0.05em',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
}}>
|
||||||
Quick Results
|
Quick Results
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="caption" color="primary">
|
<Box sx={{
|
||||||
{results.length} found
|
px: 1.5,
|
||||||
</Typography>
|
py: 0.5,
|
||||||
|
borderRadius: 2,
|
||||||
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
|
color: 'white',
|
||||||
|
}}>
|
||||||
|
<Typography variant="caption" sx={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
}}>
|
||||||
|
{results.length} found
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
<List sx={{ py: 0 }}>
|
<List sx={{ py: 0 }}>
|
||||||
|
|
@ -512,10 +606,15 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
component="div"
|
component="div"
|
||||||
onClick={() => handleDocumentClick(doc)}
|
onClick={() => handleDocumentClick(doc)}
|
||||||
sx={{
|
sx={{
|
||||||
py: 1,
|
py: 1.5,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
borderRadius: 2,
|
||||||
|
mx: 1,
|
||||||
|
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'action.hover',
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.08) 0%, rgba(139,92,246,0.08) 100%)',
|
||||||
|
transform: 'translateX(4px)',
|
||||||
|
boxShadow: '0 4px 16px rgba(99,102,241,0.1)',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -587,13 +686,30 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
{results.length >= 5 && (
|
{results.length >= 5 && (
|
||||||
<Box sx={{ p: 1, textAlign: 'center', borderTop: '1px solid', borderColor: 'divider' }}>
|
<Box sx={{
|
||||||
<Typography
|
p: 2,
|
||||||
variant="caption"
|
textAlign: 'center',
|
||||||
color="primary"
|
borderTop: '1px solid rgba(226,232,240,0.4)',
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.03) 0%, rgba(139,92,246,0.03) 100%)',
|
||||||
|
}}>
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
px: 3,
|
||||||
|
py: 1.5,
|
||||||
|
borderRadius: 2,
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.1) 0%, rgba(139,92,246,0.1) 100%)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
'&:hover': { textDecoration: 'underline' },
|
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
'&:hover': {
|
||||||
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
|
transform: 'translateY(-2px)',
|
||||||
|
boxShadow: '0 8px 24px rgba(99,102,241,0.2)',
|
||||||
|
'& .view-all-text': {
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
saveRecentSearch(query);
|
saveRecentSearch(query);
|
||||||
|
|
@ -601,8 +717,20 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
navigate(`/search?q=${encodeURIComponent(query)}`);
|
navigate(`/search?q=${encodeURIComponent(query)}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
View all results for "{query}"
|
<Typography
|
||||||
</Typography>
|
className="view-all-text"
|
||||||
|
variant="caption"
|
||||||
|
sx={{
|
||||||
|
color: '#6366f1',
|
||||||
|
fontWeight: 600,
|
||||||
|
letterSpacing: '0.025em',
|
||||||
|
fontSize: '0.8rem',
|
||||||
|
transition: 'color 0.2s ease-in-out',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
View all results for "{query}"
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
@ -610,8 +738,18 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
|
|
||||||
{!query && recentSearches.length > 0 && (
|
{!query && recentSearches.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Box sx={{ p: 1, borderBottom: '1px solid', borderColor: 'divider' }}>
|
<Box sx={{
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ px: 1 }}>
|
p: 2,
|
||||||
|
borderBottom: '1px solid rgba(226,232,240,0.4)',
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.03) 0%, rgba(139,92,246,0.03) 100%)',
|
||||||
|
}}>
|
||||||
|
<Typography variant="caption" sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
fontWeight: 600,
|
||||||
|
letterSpacing: '0.05em',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
}}>
|
||||||
Recent Searches
|
Recent Searches
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -622,10 +760,15 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
component="div"
|
component="div"
|
||||||
onClick={() => handleRecentSearchClick(search)}
|
onClick={() => handleRecentSearchClick(search)}
|
||||||
sx={{
|
sx={{
|
||||||
py: 1,
|
py: 1.5,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
borderRadius: 2,
|
||||||
|
mx: 1,
|
||||||
|
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'action.hover',
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.08) 0%, rgba(139,92,246,0.08) 100%)',
|
||||||
|
transform: 'translateX(4px)',
|
||||||
|
boxShadow: '0 4px 16px rgba(99,102,241,0.1)',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -646,11 +789,28 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!query && recentSearches.length === 0 && (
|
{!query && recentSearches.length === 0 && (
|
||||||
<Box sx={{ p: 2, textAlign: 'center' }}>
|
<Box sx={{
|
||||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
p: 3,
|
||||||
|
textAlign: 'center',
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.02) 0%, rgba(139,92,246,0.02) 100%)',
|
||||||
|
}}>
|
||||||
|
<Typography variant="body2" sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: '0.025em',
|
||||||
|
mb: 1,
|
||||||
|
}}>
|
||||||
Start typing to search documents
|
Start typing to search documents
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 2, display: 'block' }}>
|
<Typography variant="caption" sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
fontWeight: 600,
|
||||||
|
letterSpacing: '0.05em',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
mb: 2,
|
||||||
|
display: 'block',
|
||||||
|
}}>
|
||||||
Popular searches:
|
Popular searches:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Stack direction="row" spacing={1} justifyContent="center" flexWrap="wrap">
|
<Stack direction="row" spacing={1} justifyContent="center" flexWrap="wrap">
|
||||||
|
|
@ -664,9 +824,16 @@ const GlobalSearchBar: React.FC<GlobalSearchBarProps> = ({ sx, ...props }) => {
|
||||||
onClick={() => handlePopularSearchClick(search)}
|
onClick={() => handlePopularSearchClick(search)}
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: '0.75rem',
|
fontSize: '0.75rem',
|
||||||
|
fontWeight: 500,
|
||||||
|
border: '1px solid rgba(99,102,241,0.3)',
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(248,250,252,0.6) 100%)',
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'primary.light',
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
color: 'primary.contrastText',
|
color: 'white',
|
||||||
|
transform: 'translateY(-2px)',
|
||||||
|
boxShadow: '0 8px 24px rgba(99,102,241,0.2)',
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -89,66 +89,145 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawer = (
|
const drawer = (
|
||||||
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
<Box sx={{
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
background: 'linear-gradient(180deg, rgba(255,255,255,0.95) 0%, rgba(248,250,252,0.95) 100%)',
|
||||||
|
backdropFilter: 'blur(20px)',
|
||||||
|
borderRight: '1px solid rgba(226,232,240,0.5)',
|
||||||
|
}}>
|
||||||
{/* Logo Section */}
|
{/* Logo Section */}
|
||||||
<Box sx={{ p: 3, borderBottom: 1, borderColor: 'divider' }}>
|
<Box sx={{
|
||||||
|
p: 3,
|
||||||
|
borderBottom: '1px solid rgba(226,232,240,0.3)',
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.05) 0%, rgba(139,92,246,0.05) 100%)',
|
||||||
|
}}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: 40,
|
width: 44,
|
||||||
height: 40,
|
height: 44,
|
||||||
borderRadius: 2,
|
borderRadius: 3,
|
||||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #ec4899 100%)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
fontWeight: 'bold',
|
fontWeight: 800,
|
||||||
fontSize: '1.2rem',
|
fontSize: '1.3rem',
|
||||||
|
boxShadow: '0 8px 32px rgba(99,102,241,0.3)',
|
||||||
|
position: 'relative',
|
||||||
|
'&::before': {
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
borderRadius: 3,
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.1) 100%)',
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
R
|
<Box sx={{ position: 'relative', zIndex: 1 }}>R</Box>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h6" sx={{
|
||||||
|
fontWeight: 800,
|
||||||
|
color: 'text.primary',
|
||||||
|
background: 'linear-gradient(135deg, #1e293b 0%, #6366f1 100%)',
|
||||||
|
backgroundClip: 'text',
|
||||||
|
WebkitBackgroundClip: 'text',
|
||||||
|
WebkitTextFillColor: 'transparent',
|
||||||
|
letterSpacing: '-0.025em',
|
||||||
|
}}>
|
||||||
|
Readur
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: '0.05em',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
}}>
|
||||||
|
AI Document Platform
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="h6" sx={{ fontWeight: 700, color: 'text.primary' }}>
|
|
||||||
Readur
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="body2" sx={{ color: 'text.secondary', mt: 0.5 }}>
|
|
||||||
Document Intelligence Platform
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Navigation */}
|
||||||
<List sx={{ flex: 1, px: 2, py: 1 }}>
|
<List sx={{ flex: 1, px: 3, py: 2 }}>
|
||||||
{navigationItems.map((item) => {
|
{navigationItems.map((item) => {
|
||||||
const isActive = location.pathname === item.path;
|
const isActive = location.pathname === item.path;
|
||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem key={item.text} sx={{ px: 0, mb: 0.5 }}>
|
<ListItem key={item.text} sx={{ px: 0, mb: 1 }}>
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
onClick={() => navigate(item.path)}
|
onClick={() => navigate(item.path)}
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 2,
|
borderRadius: 3,
|
||||||
minHeight: 48,
|
minHeight: 52,
|
||||||
backgroundColor: isActive ? 'primary.main' : 'transparent',
|
px: 2.5,
|
||||||
color: isActive ? 'primary.contrastText' : 'text.primary',
|
py: 1.5,
|
||||||
|
background: isActive
|
||||||
|
? 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)'
|
||||||
|
: 'transparent',
|
||||||
|
color: isActive ? 'white' : 'text.primary',
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
|
transition: 'all 0.2s ease-in-out',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: isActive ? 'primary.dark' : 'action.hover',
|
backgroundColor: isActive ? 'transparent' : 'rgba(99,102,241,0.08)',
|
||||||
|
transform: isActive ? 'none' : 'translateX(4px)',
|
||||||
|
'&::before': isActive ? {} : {
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
width: '3px',
|
||||||
|
background: 'linear-gradient(180deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
|
borderRadius: '0 2px 2px 0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
'&::after': isActive ? {
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%)',
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
} : {},
|
||||||
'& .MuiListItemIcon-root': {
|
'& .MuiListItemIcon-root': {
|
||||||
color: isActive ? 'primary.contrastText' : 'text.secondary',
|
color: isActive ? 'white' : 'text.secondary',
|
||||||
|
minWidth: 36,
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 1,
|
||||||
},
|
},
|
||||||
|
'& .MuiListItemText-root': {
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
...(isActive && {
|
||||||
|
boxShadow: '0 8px 32px rgba(99,102,241,0.3)',
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon sx={{ minWidth: 40 }}>
|
<ListItemIcon>
|
||||||
<Icon />
|
<Icon sx={{ fontSize: '1.25rem' }} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={item.text}
|
primary={item.text}
|
||||||
primaryTypographyProps={{
|
primaryTypographyProps={{
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.9rem',
|
||||||
fontWeight: isActive ? 600 : 500,
|
fontWeight: isActive ? 600 : 500,
|
||||||
|
letterSpacing: '0.025em',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
|
@ -158,20 +237,40 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
{/* User Info */}
|
{/* User Info */}
|
||||||
<Box sx={{ p: 2, borderTop: 1, borderColor: 'divider' }}>
|
<Box sx={{
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
p: 3,
|
||||||
|
borderTop: '1px solid rgba(226,232,240,0.3)',
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.03) 0%, rgba(139,92,246,0.03) 100%)',
|
||||||
|
}}>
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 2.5,
|
||||||
|
p: 2,
|
||||||
|
borderRadius: 3,
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(248,250,252,0.6) 100%)',
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
border: '1px solid rgba(255,255,255,0.3)',
|
||||||
|
boxShadow: '0 4px 16px rgba(0,0,0,0.04)',
|
||||||
|
}}>
|
||||||
<Avatar
|
<Avatar
|
||||||
sx={{
|
sx={{
|
||||||
width: 36,
|
width: 42,
|
||||||
height: 36,
|
height: 42,
|
||||||
bgcolor: 'primary.main',
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
fontSize: '0.875rem',
|
fontSize: '1rem',
|
||||||
|
fontWeight: 600,
|
||||||
|
boxShadow: '0 4px 16px rgba(99,102,241,0.3)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{user?.username?.charAt(0).toUpperCase()}
|
{user?.username?.charAt(0).toUpperCase()}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<Box sx={{ flex: 1, minWidth: 0 }}>
|
<Box sx={{ flex: 1, minWidth: 0 }}>
|
||||||
<Typography variant="body2" sx={{ fontWeight: 600, color: 'text.primary' }}>
|
<Typography variant="body2" sx={{
|
||||||
|
fontWeight: 600,
|
||||||
|
color: 'text.primary',
|
||||||
|
letterSpacing: '0.025em',
|
||||||
|
}}>
|
||||||
{user?.username}
|
{user?.username}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
|
|
@ -182,6 +281,8 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
fontWeight: 500,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{user?.email}
|
{user?.email}
|
||||||
|
|
@ -202,6 +303,10 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||||
sx={{
|
sx={{
|
||||||
width: { md: `calc(100% - ${drawerWidth}px)` },
|
width: { md: `calc(100% - ${drawerWidth}px)` },
|
||||||
ml: { md: `${drawerWidth}px` },
|
ml: { md: `${drawerWidth}px` },
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(248,250,252,0.90) 100%)',
|
||||||
|
backdropFilter: 'blur(20px)',
|
||||||
|
borderBottom: '1px solid rgba(226,232,240,0.5)',
|
||||||
|
boxShadow: '0 4px 32px rgba(0,0,0,0.04)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
|
|
@ -215,29 +320,77 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||||
<MenuIcon />
|
<MenuIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
<Typography variant="h6" noWrap component="div" sx={{ fontWeight: 600, mr: 2 }}>
|
<Typography variant="h6" noWrap component="div" sx={{
|
||||||
|
fontWeight: 700,
|
||||||
|
mr: 2,
|
||||||
|
background: 'linear-gradient(135deg, #1e293b 0%, #6366f1 100%)',
|
||||||
|
backgroundClip: 'text',
|
||||||
|
WebkitBackgroundClip: 'text',
|
||||||
|
WebkitTextFillColor: 'transparent',
|
||||||
|
letterSpacing: '-0.025em',
|
||||||
|
}}>
|
||||||
{navigationItems.find(item => item.path === location.pathname)?.text || 'Dashboard'}
|
{navigationItems.find(item => item.path === location.pathname)?.text || 'Dashboard'}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{/* Global Search Bar */}
|
{/* Global Search Bar */}
|
||||||
<Box sx={{ flexGrow: 1, display: 'flex', justifyContent: 'center', mr: 2 }}>
|
<Box sx={{ flexGrow: 1, display: 'flex', justifyContent: 'center', mr: 3 }}>
|
||||||
<GlobalSearchBar />
|
<GlobalSearchBar />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Notifications */}
|
{/* Notifications */}
|
||||||
<IconButton color="inherit" sx={{ mr: 1 }}>
|
<IconButton
|
||||||
<Badge badgeContent={3} color="secondary">
|
sx={{
|
||||||
<NotificationsIcon />
|
mr: 2,
|
||||||
|
color: 'text.secondary',
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(248,250,252,0.6) 100%)',
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
border: '1px solid rgba(255,255,255,0.3)',
|
||||||
|
borderRadius: 2.5,
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
transition: 'all 0.2s ease-in-out',
|
||||||
|
'&:hover': {
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.1) 0%, rgba(139,92,246,0.1) 100%)',
|
||||||
|
transform: 'translateY(-2px)',
|
||||||
|
boxShadow: '0 8px 24px rgba(99,102,241,0.15)',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Badge
|
||||||
|
badgeContent={3}
|
||||||
|
sx={{
|
||||||
|
'& .MuiBadge-badge': {
|
||||||
|
background: 'linear-gradient(135deg, #ef4444 0%, #f97316 100%)',
|
||||||
|
color: 'white',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NotificationsIcon sx={{ fontSize: '1.25rem' }} />
|
||||||
</Badge>
|
</Badge>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
{/* Profile Menu */}
|
{/* Profile Menu */}
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
|
||||||
onClick={handleProfileMenuOpen}
|
onClick={handleProfileMenuOpen}
|
||||||
sx={{ ml: 1 }}
|
sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(248,250,252,0.6) 100%)',
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
border: '1px solid rgba(255,255,255,0.3)',
|
||||||
|
borderRadius: 2.5,
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
transition: 'all 0.2s ease-in-out',
|
||||||
|
'&:hover': {
|
||||||
|
background: 'linear-gradient(135deg, rgba(99,102,241,0.1) 0%, rgba(139,92,246,0.1) 100%)',
|
||||||
|
transform: 'translateY(-2px)',
|
||||||
|
boxShadow: '0 8px 24px rgba(99,102,241,0.15)',
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<AccountIcon />
|
<AccountIcon sx={{ fontSize: '1.25rem' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
<Menu
|
<Menu
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue