mirror of
https://github.com/Sea-Haven-Industries/proposal-system.git
synced 2026-09-30 06:33:13 +00:00
Apply SHOC design system styling across frontend
- Rewrite theme with SHOC palette (#0c4f6f primary, Nunito font, 4px radius) - Add global CSS with Google Fonts import for Nunito - Redesign Topbar with avatar initials, role subtitle, gradient header - Redesign Sidebar with 220px width, section headers, active state border - Restyle LoginPage with SHOC branded card and dev-mode role selector - Update AdminDashboard KPI cards to centered SHOC style - Add devLogin API method for local development auth flow
This commit is contained in:
parent
fc8def2f47
commit
3e9623b6fc
8 changed files with 384 additions and 78 deletions
|
|
@ -8,7 +8,6 @@ import {
|
|||
ListItemText,
|
||||
Toolbar,
|
||||
Box,
|
||||
Divider,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import DashboardIcon from '@mui/icons-material/Dashboard';
|
||||
|
|
@ -21,23 +20,34 @@ import { selectSidebarOpen } from '../app/slices/uiSlice';
|
|||
import { selectUser } from '../app/slices/authSlice';
|
||||
import type { RootState } from '../app/store';
|
||||
|
||||
const DRAWER_WIDTH = 240;
|
||||
const DRAWER_WIDTH = 220;
|
||||
|
||||
const dispatcherNav = [
|
||||
{ label: 'Dashboard', path: '/', icon: <DashboardIcon /> },
|
||||
{ label: 'My Proposals', path: '/proposals', icon: <DescriptionIcon /> },
|
||||
{ label: 'New Proposal', path: '/proposals/new', icon: <AddCircleIcon /> },
|
||||
{ label: 'Dashboard', path: '/', icon: <DashboardIcon fontSize="small" /> },
|
||||
{ label: 'My Proposals', path: '/proposals', icon: <DescriptionIcon fontSize="small" /> },
|
||||
{ label: 'New Proposal', path: '/proposals/new', icon: <AddCircleIcon fontSize="small" /> },
|
||||
];
|
||||
|
||||
const adminNav = [
|
||||
{ label: 'Admin Queue', path: '/admin', icon: <AdminPanelSettingsIcon /> },
|
||||
{ label: 'All Proposals', path: '/admin/proposals', icon: <AssignmentIcon /> },
|
||||
{ label: 'Admin Queue', path: '/admin', icon: <AdminPanelSettingsIcon fontSize="small" /> },
|
||||
{ label: 'All Proposals', path: '/admin/proposals', icon: <AssignmentIcon fontSize="small" /> },
|
||||
];
|
||||
|
||||
const sysadminNav = [
|
||||
{ label: 'Users', path: '/admin/users', icon: <PeopleIcon /> },
|
||||
{ label: 'Users', path: '/admin/users', icon: <PeopleIcon fontSize="small" /> },
|
||||
];
|
||||
|
||||
const ALL_NAV_PATHS = new Set(
|
||||
[...dispatcherNav, ...adminNav, ...sysadminNav].map(n => n.path)
|
||||
);
|
||||
|
||||
function isNavActive(pathname: string, itemPath: string): boolean {
|
||||
if (pathname === itemPath) return true;
|
||||
if (ALL_NAV_PATHS.has(pathname)) return false;
|
||||
if (itemPath === '/' || itemPath === '/admin') return false;
|
||||
return pathname.startsWith(itemPath + '/');
|
||||
}
|
||||
|
||||
export default function Sidebar() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
|
@ -61,46 +71,72 @@ export default function Sidebar() {
|
|||
},
|
||||
}}
|
||||
>
|
||||
<Toolbar />
|
||||
<Box sx={{ overflow: 'auto', flexGrow: 1 }}>
|
||||
<List>
|
||||
<Toolbar sx={{ minHeight: 64 }} />
|
||||
<Box sx={{ overflow: 'auto', flexGrow: 1, pt: 1 }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '11px',
|
||||
fontWeight: 600,
|
||||
letterSpacing: '1.2px',
|
||||
textTransform: 'uppercase',
|
||||
color: '#9aa0a6',
|
||||
px: 2.5,
|
||||
mb: 0.5,
|
||||
}}
|
||||
>
|
||||
Proposals
|
||||
</Typography>
|
||||
<List disablePadding>
|
||||
{navItems.map((item) => (
|
||||
<ListItemButton
|
||||
key={item.path}
|
||||
selected={location.pathname === item.path}
|
||||
selected={isNavActive(location.pathname, item.path)}
|
||||
onClick={() => navigate(item.path)}
|
||||
sx={{ py: 1 }}
|
||||
>
|
||||
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||
<ListItemText primary={item.label} />
|
||||
<ListItemText primary={item.label} primaryTypographyProps={{ fontSize: '13px' }} />
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
|
||||
{isAdmin && (
|
||||
<>
|
||||
<Divider />
|
||||
<Typography variant="overline" sx={{ px: 2, pt: 1, display: 'block', color: 'text.secondary' }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '11px',
|
||||
fontWeight: 600,
|
||||
letterSpacing: '1.2px',
|
||||
textTransform: 'uppercase',
|
||||
color: '#9aa0a6',
|
||||
px: 2.5,
|
||||
mt: 2,
|
||||
mb: 0.5,
|
||||
}}
|
||||
>
|
||||
Admin
|
||||
</Typography>
|
||||
<List>
|
||||
<List disablePadding>
|
||||
{adminNav.map((item) => (
|
||||
<ListItemButton
|
||||
key={item.path}
|
||||
selected={location.pathname === item.path || location.pathname.startsWith(item.path + '/')}
|
||||
selected={isNavActive(location.pathname, item.path)}
|
||||
onClick={() => navigate(item.path)}
|
||||
sx={{ py: 1 }}
|
||||
>
|
||||
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||
<ListItemText primary={item.label} />
|
||||
<ListItemText primary={item.label} primaryTypographyProps={{ fontSize: '13px' }} />
|
||||
</ListItemButton>
|
||||
))}
|
||||
{isSysAdmin && sysadminNav.map((item) => (
|
||||
<ListItemButton
|
||||
key={item.path}
|
||||
selected={location.pathname === item.path}
|
||||
selected={isNavActive(location.pathname, item.path)}
|
||||
onClick={() => navigate(item.path)}
|
||||
sx={{ py: 1 }}
|
||||
>
|
||||
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||
<ListItemText primary={item.label} />
|
||||
<ListItemText primary={item.label} primaryTypographyProps={{ fontSize: '13px' }} />
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
|
|
@ -108,9 +144,9 @@ export default function Sidebar() {
|
|||
)}
|
||||
</Box>
|
||||
{user && (
|
||||
<Box sx={{ p: 2, mt: 'auto', borderTop: 1, borderColor: 'divider' }}>
|
||||
<Box sx={{ fontSize: '0.85rem', fontWeight: 500 }}>{user.displayName}</Box>
|
||||
<Box sx={{ fontSize: '0.75rem', color: 'text.secondary' }}>{user.role}</Box>
|
||||
<Box sx={{ p: 2, mt: 'auto', borderTop: '1px solid #d9dde0' }}>
|
||||
<Box sx={{ fontSize: '13px', fontWeight: 500 }}>{user.displayName}</Box>
|
||||
<Box sx={{ fontSize: '11px', color: '#9aa0a6' }}>{user.role}</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Drawer>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useDispatch } from 'react-redux';
|
||||
import { AppBar, Toolbar, IconButton, Typography, Button, Box } from '@mui/material';
|
||||
import { AppBar, Toolbar, IconButton, Typography, Box, Avatar } from '@mui/material';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import LogoutIcon from '@mui/icons-material/Logout';
|
||||
import { toggleSidebar } from '../app/slices/uiSlice';
|
||||
|
|
@ -9,9 +9,13 @@ export default function Topbar() {
|
|||
const dispatch = useDispatch();
|
||||
const { user, logout } = useAuth();
|
||||
|
||||
const initials = user?.displayName
|
||||
? user.displayName.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2)
|
||||
: '';
|
||||
|
||||
return (
|
||||
<AppBar position="fixed" sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}>
|
||||
<Toolbar>
|
||||
<AppBar position="fixed" sx={{ zIndex: (theme) => theme.zIndex.drawer + 1, height: 64 }}>
|
||||
<Toolbar sx={{ height: 64, minHeight: 64 }}>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
edge="start"
|
||||
|
|
@ -20,15 +24,32 @@ export default function Topbar() {
|
|||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" noWrap sx={{ flexGrow: 1 }}>
|
||||
<Typography variant="h6" noWrap sx={{ flexGrow: 1, fontWeight: 600 }}>
|
||||
Proposal System
|
||||
</Typography>
|
||||
{user && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="body2">{user.displayName}</Typography>
|
||||
<Button color="inherit" onClick={logout} startIcon={<LogoutIcon />} size="small">
|
||||
Logout
|
||||
</Button>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Avatar sx={{ width: 32, height: 32, bgcolor: '#1fa8de', fontSize: 12, fontWeight: 600 }}>
|
||||
{initials}
|
||||
</Avatar>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', lineHeight: 1.2 }}>
|
||||
<Typography sx={{ fontSize: '13px', fontWeight: 600, color: '#fff' }}>
|
||||
{user.displayName}
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: '11px', color: 'rgba(255,255,255,0.5)' }}>
|
||||
{user.role}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{ width: '1px', height: 24, bgcolor: 'rgba(255,255,255,0.15)', mx: 0.5 }} />
|
||||
<IconButton
|
||||
onClick={logout}
|
||||
size="small"
|
||||
sx={{ color: 'rgba(255,255,255,0.5)', '&:hover': { color: '#ff6b6b', bgcolor: 'rgba(255,100,100,0.1)' } }}
|
||||
>
|
||||
<LogoutIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
)}
|
||||
</Toolbar>
|
||||
|
|
|
|||
27
web/src/index.css
Normal file
27
web/src/index.css
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
@import url("https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200;0,300;0,400;0,600;0,700;1,400&display=swap");
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body, #root {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Nunito", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
background: #f4f6f7;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
@ -21,6 +21,11 @@ export const authApi = {
|
|||
return res.data;
|
||||
},
|
||||
|
||||
devLogin: async (email: string, displayName: string, role: string): Promise<AuthUser> => {
|
||||
const res = await apiClient.post('/auth/dev-login', { email, displayName, role });
|
||||
return res.data;
|
||||
},
|
||||
|
||||
getMe: async (): Promise<UserProfile> => {
|
||||
const res = await apiClient.get('/users/me');
|
||||
return res.data;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { QueryClientProvider } from '@tanstack/react-query';
|
|||
import { ThemeProvider, CssBaseline } from '@mui/material';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import './index.css';
|
||||
import { store } from './app/store';
|
||||
import { queryClient } from './lib/queryClient';
|
||||
import { theme } from './theme';
|
||||
|
|
|
|||
|
|
@ -54,13 +54,15 @@ function formatDate(iso: string): string {
|
|||
|
||||
function StatCard({ icon, label, value, color }: { icon: React.ReactNode; label: string; value: string; color: string }) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent sx={{ display: 'flex', alignItems: 'center', gap: 2, py: 2 }}>
|
||||
<Box sx={{ color, display: 'flex' }}>{icon}</Box>
|
||||
<Box>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700 }}>{value}</Typography>
|
||||
<Typography variant="body2" color="text.secondary">{label}</Typography>
|
||||
</Box>
|
||||
<Card sx={{ borderRadius: '8px', transition: 'transform 0.2s, box-shadow 0.2s', '&:hover': { transform: 'translateY(-2px)', boxShadow: '0 4px 8px rgba(0,0,0,0.15)' } }}>
|
||||
<CardContent sx={{ textAlign: 'center', py: 2.5 }}>
|
||||
<Box sx={{ color, display: 'flex', justifyContent: 'center', mb: 1 }}>{icon}</Box>
|
||||
<Typography sx={{ fontSize: '12px', color: '#666', textTransform: 'uppercase', letterSpacing: '0.5px', mb: 1 }}>
|
||||
{label}
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: '32px', fontWeight: 700, color: '#003a5d' }}>
|
||||
{value}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Box, Button, Card, CardContent, Typography } from '@mui/material';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { Box, Button, Card, CardContent, Typography, Divider, Stack } from '@mui/material';
|
||||
import GoogleIcon from '@mui/icons-material/Google';
|
||||
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
|
||||
import EngineeringIcon from '@mui/icons-material/Engineering';
|
||||
import PersonIcon from '@mui/icons-material/Person';
|
||||
import { useAuth } from '../../hooks/useAuth';
|
||||
import { authApi } from '../../lib/api/auth';
|
||||
import { setUser, setError } from '../../app/slices/authSlice';
|
||||
|
||||
const COGNITO_DOMAIN = import.meta.env.VITE_COGNITO_DOMAIN || 'proposal-system-seahaven.auth.us-east-1.amazoncognito.com';
|
||||
const COGNITO_DOMAIN = import.meta.env.VITE_COGNITO_DOMAIN || '';
|
||||
const CLIENT_ID = import.meta.env.VITE_COGNITO_CLIENT_ID || '';
|
||||
const REDIRECT_URI = `${window.location.origin}/callback`;
|
||||
|
||||
|
|
@ -21,7 +27,11 @@ function buildLoginUrl(): string {
|
|||
|
||||
export default function LoginPage() {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const { isAuthenticated } = useAuth();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const isDevMode = !CLIENT_ID;
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
|
|
@ -29,36 +39,131 @@ export default function LoginPage() {
|
|||
}
|
||||
}, [isAuthenticated, navigate]);
|
||||
|
||||
const handleDevLogin = async (role: string) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const user = await authApi.devLogin('adam@seahavenind.com', 'Adam Moussa', role);
|
||||
dispatch(setUser(user));
|
||||
navigate('/', { replace: true });
|
||||
} catch (err) {
|
||||
dispatch(setError(err instanceof Error ? err.message : 'Dev login failed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: '100vh',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
bgcolor: 'background.default',
|
||||
justifyContent: 'flex-start',
|
||||
pt: '7%',
|
||||
bgcolor: '#333',
|
||||
}}
|
||||
>
|
||||
<Card sx={{ maxWidth: 400, width: '100%', mx: 2 }}>
|
||||
<CardContent sx={{ p: 4, textAlign: 'center' }}>
|
||||
<Typography variant="h5" gutterBottom>
|
||||
Proposal System
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 4 }}>
|
||||
Sign in with your Sea Haven Google account to continue.
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
fullWidth
|
||||
startIcon={<GoogleIcon />}
|
||||
href={buildLoginUrl()}
|
||||
sx={{ py: 1.5 }}
|
||||
<Box sx={{ width: '100%', maxWidth: 400, mx: 2 }}>
|
||||
<Card sx={{ borderRadius: 0, boxShadow: '0 0 10px rgba(0,0,0,0.3)', border: 'none' }}>
|
||||
<Box
|
||||
sx={{
|
||||
height: 100,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
bgcolor: '#fff',
|
||||
borderBottom: '1px solid #b8b5b5',
|
||||
px: 3,
|
||||
}}
|
||||
>
|
||||
Sign in with Google
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Typography sx={{ fontSize: '24px', fontWeight: 600, color: '#003A5D' }}>
|
||||
Sea Haven Industries
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
bgcolor: '#003A5D',
|
||||
height: 40,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderBottom: '1px solid #b8b5b5',
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: '14px', fontWeight: 700, color: '#fff' }}>
|
||||
Proposal System
|
||||
</Typography>
|
||||
</Box>
|
||||
<CardContent sx={{ p: '30px', textAlign: 'center' }}>
|
||||
{isDevMode ? (
|
||||
<>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
|
||||
Development Mode — Select a role to sign in as:
|
||||
</Typography>
|
||||
<Stack spacing={2}>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
fullWidth
|
||||
startIcon={<AdminPanelSettingsIcon />}
|
||||
onClick={() => handleDevLogin('SysAdmin')}
|
||||
disabled={loading}
|
||||
sx={{ py: 1.5, bgcolor: '#003A5D', '&:hover': { bgcolor: '#00527d' } }}
|
||||
>
|
||||
Sign in as SysAdmin
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
fullWidth
|
||||
startIcon={<EngineeringIcon />}
|
||||
onClick={() => handleDevLogin('Admin')}
|
||||
disabled={loading}
|
||||
sx={{ py: 1.5, bgcolor: '#0c4f6f', '&:hover': { bgcolor: '#083a52' } }}
|
||||
>
|
||||
Sign in as Admin
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="large"
|
||||
fullWidth
|
||||
startIcon={<PersonIcon />}
|
||||
onClick={() => handleDevLogin('Dispatcher')}
|
||||
disabled={loading}
|
||||
sx={{ py: 1.5 }}
|
||||
>
|
||||
Sign in as Dispatcher
|
||||
</Button>
|
||||
</Stack>
|
||||
<Divider sx={{ my: 2 }} />
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Dev login is only available when VITE_COGNITO_CLIENT_ID is not set
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 4 }}>
|
||||
Sign in with your Sea Haven Google account to continue.
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
fullWidth
|
||||
startIcon={<GoogleIcon />}
|
||||
href={buildLoginUrl()}
|
||||
sx={{ py: 1.5, bgcolor: '#003A5D', '&:hover': { bgcolor: '#00527d' } }}
|
||||
>
|
||||
Sign in with Google
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Typography sx={{ textAlign: 'center', color: '#fff', fontSize: '12px', mt: 2, opacity: 0.7 }}>
|
||||
Sea Haven Industries © {new Date().getFullYear()}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
139
web/src/theme.ts
139
web/src/theme.ts
|
|
@ -3,28 +3,36 @@ import { createTheme } from '@mui/material/styles';
|
|||
export const theme = createTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#1565c0',
|
||||
light: '#5e92f3',
|
||||
dark: '#003c8f',
|
||||
main: '#0c4f6f',
|
||||
light: '#1fa8de',
|
||||
dark: '#083a52',
|
||||
},
|
||||
secondary: {
|
||||
main: '#f57c00',
|
||||
light: '#ffad42',
|
||||
dark: '#bb4d00',
|
||||
main: '#007bff',
|
||||
light: '#4da3ff',
|
||||
dark: '#0056b3',
|
||||
},
|
||||
background: {
|
||||
default: '#f5f5f5',
|
||||
default: '#f4f6f7',
|
||||
paper: '#ffffff',
|
||||
},
|
||||
text: {
|
||||
primary: '#333333',
|
||||
secondary: '#666666',
|
||||
},
|
||||
divider: '#d9dde0',
|
||||
},
|
||||
typography: {
|
||||
fontFamily: '"Nunito Sans", "Helvetica", "Arial", sans-serif',
|
||||
h4: { fontWeight: 700 },
|
||||
h5: { fontWeight: 700 },
|
||||
h6: { fontWeight: 600 },
|
||||
fontFamily: '"Nunito", "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
||||
fontSize: 13,
|
||||
h4: { fontWeight: 600, fontSize: '28px' },
|
||||
h5: { fontWeight: 600, fontSize: '22px' },
|
||||
h6: { fontWeight: 600, fontSize: '16px' },
|
||||
body1: { fontSize: '14px' },
|
||||
body2: { fontSize: '13px' },
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 8,
|
||||
borderRadius: 4,
|
||||
},
|
||||
components: {
|
||||
MuiButton: {
|
||||
|
|
@ -32,12 +40,19 @@ export const theme = createTheme({
|
|||
root: {
|
||||
textTransform: 'none',
|
||||
fontWeight: 600,
|
||||
borderRadius: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAppBar: {
|
||||
defaultProps: {
|
||||
elevation: 1,
|
||||
elevation: 0,
|
||||
},
|
||||
styleOverrides: {
|
||||
root: {
|
||||
background: 'linear-gradient(#0c4f6f, #083a52)',
|
||||
borderBottom: '1px solid #062c3e',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiCard: {
|
||||
|
|
@ -46,8 +61,102 @@ export const theme = createTheme({
|
|||
},
|
||||
styleOverrides: {
|
||||
root: {
|
||||
border: '1px solid',
|
||||
borderColor: '#e0e0e0',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: 4,
|
||||
boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
defaultProps: {
|
||||
elevation: 0,
|
||||
},
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableHead: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'& .MuiTableCell-head': {
|
||||
fontWeight: 600,
|
||||
backgroundColor: '#f9f9f9',
|
||||
borderBottom: '2px solid #ddd',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableCell: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
fontSize: '14px',
|
||||
padding: '12px',
|
||||
borderBottom: '1px solid #eee',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableRow: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'&:hover': {
|
||||
backgroundColor: '#f9f9f9',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTextField: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'& .MuiOutlinedInput-root': {
|
||||
borderRadius: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiChip: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 4,
|
||||
fontWeight: 600,
|
||||
fontSize: '12px',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiDrawer: {
|
||||
styleOverrides: {
|
||||
paper: {
|
||||
borderRight: '1px solid #d9dde0',
|
||||
backgroundColor: '#fbfbfb',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiListItemButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 6,
|
||||
marginLeft: 8,
|
||||
marginRight: 8,
|
||||
marginBottom: 4,
|
||||
'&.Mui-selected': {
|
||||
backgroundColor: '#e6f1f7',
|
||||
borderLeft: '3px solid #0b80b5',
|
||||
'&:hover': {
|
||||
backgroundColor: '#dbedf5',
|
||||
},
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: '#eef2f4',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiListItemIcon: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: '#8c949a',
|
||||
minWidth: 36,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue