mirror of
https://github.com/Sea-Haven-Industries/proposal-system.git
synced 2026-10-03 17:43:18 +00:00
feat(web): apply Sea Haven Ops design system re-theme and layout fixes
Re-theme the MUI app from teal (#0B5A73) to the Sea Haven Ops neutral-navy structure (#111827) with action-blue (#2563EB) as the sole brand accent, driven through the theme tokens so all screens update consistently. Cards become border-driven (no shadow); table headers gray-50; status/priority chips aligned to design-system token values. Layout fixes: - Topbar: square bottom corners (was inheriting MuiPaper radius) - Sidebar: remove duplicate user tag (already shown in topbar) - Main: drop redundant ml that double-counted the persistent drawer width - New Proposal form: center the constrained container (mx: auto)
This commit is contained in:
parent
2549ce3afc
commit
f502f8afc2
10 changed files with 89 additions and 92 deletions
|
|
@ -1,9 +1,6 @@
|
||||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { Box, Button, Card, CardContent, Toolbar, Typography } from '@mui/material';
|
import { Box, Button, Card, CardContent, Toolbar, Typography } from '@mui/material';
|
||||||
import PeopleIcon from '@mui/icons-material/People';
|
import PeopleIcon from '@mui/icons-material/People';
|
||||||
import { selectSidebarOpen } from './app/slices/uiSlice';
|
|
||||||
import type { RootState } from './app/store';
|
|
||||||
import ProtectedRoute, { RoleGuard } from './components/ProtectedRoute';
|
import ProtectedRoute, { RoleGuard } from './components/ProtectedRoute';
|
||||||
import Topbar from './components/Topbar';
|
import Topbar from './components/Topbar';
|
||||||
import Sidebar from './components/Sidebar';
|
import Sidebar from './components/Sidebar';
|
||||||
|
|
@ -15,11 +12,8 @@ import ProposalDetailPage from './pages/proposals/detail/ProposalDetailPage';
|
||||||
import ProposalListPage from './pages/proposals/list/ProposalListPage';
|
import ProposalListPage from './pages/proposals/list/ProposalListPage';
|
||||||
import AdminDashboard from './pages/admin/dashboard/AdminDashboard';
|
import AdminDashboard from './pages/admin/dashboard/AdminDashboard';
|
||||||
import AdminWorkspace from './pages/admin/workspace/AdminWorkspace';
|
import AdminWorkspace from './pages/admin/workspace/AdminWorkspace';
|
||||||
import { DRAWER_WIDTH } from './constants';
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const sidebarOpen = useSelector((state: RootState) => selectSidebarOpen(state));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
|
|
@ -36,9 +30,8 @@ export default function App() {
|
||||||
component="main"
|
component="main"
|
||||||
sx={{
|
sx={{
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
|
minWidth: 0,
|
||||||
p: 1.25,
|
p: 1.25,
|
||||||
ml: sidebarOpen ? `${DRAWER_WIDTH}px` : 0,
|
|
||||||
transition: 'margin-left 250ms ease',
|
|
||||||
minHeight: '100vh',
|
minHeight: '100vh',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -179,12 +179,6 @@ export default function Sidebar() {
|
||||||
{navItems.map((item) => renderNavItem(item))}
|
{navItems.map((item) => renderNavItem(item))}
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Box>
|
||||||
{user && !isCollapsed && (
|
|
||||||
<Box sx={{ p: 2, mt: 'auto', borderTop: '1px solid #DDE3E8' }}>
|
|
||||||
<Box sx={{ fontSize: '13px', fontWeight: 500, color: '#1F2933' }}>{user.displayName}</Box>
|
|
||||||
<Box sx={{ fontSize: '11px', color: '#8A949E' }}>{user.role}</Box>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export default function Topbar() {
|
||||||
sx={{
|
sx={{
|
||||||
background: '#FEF3C7',
|
background: '#FEF3C7',
|
||||||
color: '#92400E',
|
color: '#92400E',
|
||||||
border: '1px solid #FCD34D',
|
border: '1px solid #FDE68A',
|
||||||
height: 22,
|
height: 22,
|
||||||
borderRadius: '999px',
|
borderRadius: '999px',
|
||||||
fontSize: '11px',
|
fontSize: '11px',
|
||||||
|
|
@ -55,7 +55,7 @@ export default function Topbar() {
|
||||||
{user && (
|
{user && (
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<Avatar sx={{ width: 32, height: 32, bgcolor: '#0E7490', fontSize: 12, fontWeight: 600 }}>
|
<Avatar sx={{ width: 32, height: 32, bgcolor: '#2563EB', fontSize: 12, fontWeight: 600 }}>
|
||||||
{initials}
|
{initials}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', lineHeight: 1.2 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', lineHeight: 1.2 }}>
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,14 @@ export const STATUS_LABELS: Record<string, string> = {
|
||||||
Revised: 'Revised',
|
Revised: 'Revised',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sea Haven Ops status chips — flat fill + thin border + bold label (tokens/base.css).
|
||||||
export const STATUS_CHIP_STYLES: Record<string, { bgcolor: string; color: string; border: string }> = {
|
export const STATUS_CHIP_STYLES: Record<string, { bgcolor: string; color: string; border: string }> = {
|
||||||
Draft: { bgcolor: '#F1F5F9', color: '#334155', border: '1px solid #CBD5E1' },
|
Draft: { bgcolor: '#F3F4F6', color: '#374151', border: '1px solid #D1D5DB' },
|
||||||
InReview: { bgcolor: '#E0F2FE', color: '#075985', border: '1px solid #7DD3FC' },
|
InReview: { bgcolor: '#E0F2FE', color: '#075985', border: '1px solid #7DD3FC' },
|
||||||
Approved: { bgcolor: '#DCFCE7', color: '#166534', border: '1px solid #86EFAC' },
|
Approved: { bgcolor: '#DCFCE7', color: '#166534', border: '1px solid #86EFAC' },
|
||||||
Sent: { bgcolor: '#DBEAFE', color: '#1E40AF', border: '1px solid #93C5FD' },
|
Sent: { bgcolor: '#DBEAFE', color: '#1E40AF', border: '1px solid #BFDBFE' },
|
||||||
Revised: { bgcolor: '#F3E8FF', color: '#6B21A8', border: '1px solid #C084FC' },
|
Revised: { bgcolor: '#F3E8FF', color: '#6B21A8', border: '1px solid #C084FC' },
|
||||||
Rejected: { bgcolor: '#FEE2E2', color: '#991B1B', border: '1px solid #FCA5A5' },
|
Rejected: { bgcolor: '#FEE2E2', color: '#991B1B', border: '1px solid #FECACA' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DRAWER_WIDTH = 216;
|
export const DRAWER_WIDTH = 216;
|
||||||
|
|
@ -42,13 +43,13 @@ export const DRAWER_WIDTH = 216;
|
||||||
export const PRIORITY_LABELS: Record<string, string> = {
|
export const PRIORITY_LABELS: Record<string, string> = {
|
||||||
Standard: 'Standard',
|
Standard: 'Standard',
|
||||||
Urgent: 'Urgent',
|
Urgent: 'Urgent',
|
||||||
Emergency: 'Emergency Dispatch',
|
Emergency: 'Emergency',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PRIORITY_CHIP_STYLES: Record<string, { bgcolor: string; color: string; border: string }> = {
|
export const PRIORITY_CHIP_STYLES: Record<string, { bgcolor: string; color: string; border: string }> = {
|
||||||
Standard: { bgcolor: '#E2E8F0', color: '#334155', border: '1px solid #CBD5E1' },
|
Standard: { bgcolor: '#F3F4F6', color: '#374151', border: '1px solid #D1D5DB' },
|
||||||
Urgent: { bgcolor: '#FFEDD5', color: '#9A3412', border: '1px solid #FDBA74' },
|
Urgent: { bgcolor: '#FFEDD5', color: '#9A3412', border: '1px solid #FED7AA' },
|
||||||
Emergency: { bgcolor: '#FEE2E2', color: '#991B1B', border: '1px solid #F87171' },
|
Emergency: { bgcolor: '#FEE2E2', color: '#991B1B', border: '1px solid #FECACA' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PRIORITY_ROW_SX: Record<string, object> = {
|
export const PRIORITY_ROW_SX: Record<string, object> = {
|
||||||
|
|
|
||||||
|
|
@ -50,13 +50,13 @@ const TAB_STATUSES = ['', 'InReview', 'Approved', 'Sent', 'Revised'] as const;
|
||||||
|
|
||||||
function StatCard({ icon, label, value, color, subtitle }: { icon: React.ReactNode; label: string; value: string; color: string; subtitle?: string }) {
|
function StatCard({ icon, label, value, color, subtitle }: { icon: React.ReactNode; label: string; value: string; color: string; subtitle?: string }) {
|
||||||
return (
|
return (
|
||||||
<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)' } }}>
|
<Card sx={{ borderRadius: '8px', transition: 'border-color 120ms ease', '&:hover': { borderColor: '#9CA3AF' } }}>
|
||||||
<CardContent sx={{ textAlign: 'center', py: 2.5 }}>
|
<CardContent sx={{ textAlign: 'center', py: 2.5 }}>
|
||||||
<Box sx={{ color, display: 'flex', justifyContent: 'center', mb: 1 }}>{icon}</Box>
|
<Box sx={{ color, display: 'flex', justifyContent: 'center', mb: 1 }}>{icon}</Box>
|
||||||
<Typography sx={{ fontSize: '12px', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600, mb: 1 }}>
|
<Typography sx={{ fontSize: '12px', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600, mb: 1 }}>
|
||||||
{label}
|
{label}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography sx={{ fontSize: '36px', fontWeight: 800, color: '#073B4C', letterSpacing: '-0.03em' }}>
|
<Typography sx={{ fontSize: '30px', fontWeight: 700, color: '#111827', letterSpacing: '-0.02em' }}>
|
||||||
{value}
|
{value}
|
||||||
</Typography>
|
</Typography>
|
||||||
{subtitle && (
|
{subtitle && (
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,7 @@ export default function AdminWorkspace() {
|
||||||
component="button"
|
component="button"
|
||||||
underline="hover"
|
underline="hover"
|
||||||
onClick={() => guardedNavigate('/admin')}
|
onClick={() => guardedNavigate('/admin')}
|
||||||
sx={{ fontSize: 14, color: '#0B5A73', cursor: 'pointer' }}
|
sx={{ fontSize: 14, color: '#2563EB', cursor: 'pointer' }}
|
||||||
>
|
>
|
||||||
Admin Queue
|
Admin Queue
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -452,7 +452,7 @@ export default function AdminWorkspace() {
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* Action Bar */}
|
{/* Action Bar */}
|
||||||
<Card sx={{ mt: 2, position: 'sticky', bottom: 0, zIndex: 10, bgcolor: 'rgba(255,255,255,0.96)', backdropFilter: 'blur(8px)', borderTop: '1px solid #D6E0EA', boxShadow: '0 -4px 12px rgba(15,23,42,0.06)', border: 'none' }}>
|
<Card sx={{ mt: 2, position: 'sticky', bottom: 0, zIndex: 10, bgcolor: 'rgba(255,255,255,0.96)', backdropFilter: 'blur(8px)', borderTop: '1px solid #E5E7EB', boxShadow: '0 -4px 12px rgba(17,24,39,0.06)', border: 'none' }}>
|
||||||
<CardContent sx={{ py: 1.5, display: 'flex', gap: 2, alignItems: 'center', flexWrap: 'wrap' }}>
|
<CardContent sx={{ py: 1.5, display: 'flex', gap: 2, alignItems: 'center', flexWrap: 'wrap' }}>
|
||||||
{dirty && (
|
{dirty && (
|
||||||
<Alert severity="info" sx={{ py: 0, flexGrow: 1 }}>Unsaved changes</Alert>
|
<Alert severity="info" sx={{ py: 0, flexGrow: 1 }}>Unsaved changes</Alert>
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ export default function LoginPage() {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
pt: '7%',
|
pt: '7%',
|
||||||
background: 'linear-gradient(135deg, #073B4C 0%, #172554 100%)',
|
background: 'linear-gradient(135deg, #111827 0%, #172554 100%)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ width: '100%', maxWidth: 420, mx: 2 }}>
|
<Box sx={{ width: '100%', maxWidth: 420, mx: 2 }}>
|
||||||
|
|
@ -79,22 +79,22 @@ export default function LoginPage() {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
bgcolor: '#fff',
|
bgcolor: '#fff',
|
||||||
borderBottom: '1px solid #D6E0EA',
|
borderBottom: '1px solid #E5E7EB',
|
||||||
px: 3,
|
px: 3,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ fontSize: '24px', fontWeight: 600, color: '#0B4F6C' }}>
|
<Typography sx={{ fontSize: '24px', fontWeight: 700, color: '#111827', letterSpacing: '-0.02em' }}>
|
||||||
Sea Haven Industries
|
Sea Haven Industries
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
bgcolor: '#07384D',
|
bgcolor: '#111827',
|
||||||
height: 40,
|
height: 40,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
borderBottom: '1px solid #D6E0EA',
|
borderBottom: '1px solid #E5E7EB',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ fontSize: '14px', fontWeight: 700, color: '#fff' }}>
|
<Typography sx={{ fontSize: '14px', fontWeight: 700, color: '#fff' }}>
|
||||||
|
|
@ -120,7 +120,7 @@ export default function LoginPage() {
|
||||||
startIcon={<AdminPanelSettingsIcon />}
|
startIcon={<AdminPanelSettingsIcon />}
|
||||||
onClick={() => handleDevLogin('SysAdmin')}
|
onClick={() => handleDevLogin('SysAdmin')}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
sx={{ py: 1.5, height: 64, borderRadius: '8px', bgcolor: '#0B5A73', '&:hover': { bgcolor: '#084A5F' } }}
|
sx={{ py: 1.5, height: 64, borderRadius: '8px', bgcolor: '#2563EB', '&:hover': { bgcolor: '#1D4ED8' } }}
|
||||||
>
|
>
|
||||||
<Box sx={{ textAlign: 'left', flex: 1 }}>
|
<Box sx={{ textAlign: 'left', flex: 1 }}>
|
||||||
<Box>SysAdmin</Box>
|
<Box>SysAdmin</Box>
|
||||||
|
|
@ -134,7 +134,7 @@ export default function LoginPage() {
|
||||||
startIcon={<EngineeringIcon />}
|
startIcon={<EngineeringIcon />}
|
||||||
onClick={() => handleDevLogin('Admin')}
|
onClick={() => handleDevLogin('Admin')}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
sx={{ py: 1.5, height: 64, borderRadius: '8px', bgcolor: '#0B5A73', '&:hover': { bgcolor: '#084A5F' } }}
|
sx={{ py: 1.5, height: 64, borderRadius: '8px', bgcolor: '#2563EB', '&:hover': { bgcolor: '#1D4ED8' } }}
|
||||||
>
|
>
|
||||||
<Box sx={{ textAlign: 'left', flex: 1 }}>
|
<Box sx={{ textAlign: 'left', flex: 1 }}>
|
||||||
<Box>Admin</Box>
|
<Box>Admin</Box>
|
||||||
|
|
@ -148,7 +148,7 @@ export default function LoginPage() {
|
||||||
startIcon={<PersonIcon />}
|
startIcon={<PersonIcon />}
|
||||||
onClick={() => handleDevLogin('Dispatcher')}
|
onClick={() => handleDevLogin('Dispatcher')}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
sx={{ py: 1.5, height: 64, borderRadius: '8px', bgcolor: '#0B5A73', '&:hover': { bgcolor: '#084A5F' } }}
|
sx={{ py: 1.5, height: 64, borderRadius: '8px', bgcolor: '#2563EB', '&:hover': { bgcolor: '#1D4ED8' } }}
|
||||||
>
|
>
|
||||||
<Box sx={{ textAlign: 'left', flex: 1 }}>
|
<Box sx={{ textAlign: 'left', flex: 1 }}>
|
||||||
<Box>Dispatcher</Box>
|
<Box>Dispatcher</Box>
|
||||||
|
|
@ -172,7 +172,7 @@ export default function LoginPage() {
|
||||||
fullWidth
|
fullWidth
|
||||||
startIcon={<GoogleIcon />}
|
startIcon={<GoogleIcon />}
|
||||||
href={buildLoginUrl()}
|
href={buildLoginUrl()}
|
||||||
sx={{ py: 1.5, height: 64, borderRadius: '8px', bgcolor: '#0B5A73', '&:hover': { bgcolor: '#084A5F' } }}
|
sx={{ py: 1.5, height: 64, borderRadius: '8px', bgcolor: '#2563EB', '&:hover': { bgcolor: '#1D4ED8' } }}
|
||||||
>
|
>
|
||||||
Sign in with Google
|
Sign in with Google
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -32,13 +32,13 @@ import { useDocumentTitle } from '../../hooks/useDocumentTitle';
|
||||||
|
|
||||||
function KpiCard({ icon, label, value, subtitle }: { icon: React.ReactNode; label: string; value: string; subtitle?: string }) {
|
function KpiCard({ icon, label, value, subtitle }: { icon: React.ReactNode; label: string; value: string; subtitle?: string }) {
|
||||||
return (
|
return (
|
||||||
<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)' } }}>
|
<Card sx={{ borderRadius: '8px', transition: 'border-color 120ms ease', '&:hover': { borderColor: '#9CA3AF' } }}>
|
||||||
<CardContent sx={{ textAlign: 'center', py: 2.5 }}>
|
<CardContent sx={{ textAlign: 'center', py: 2.5 }}>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 1, color: '#0B5A73' }}>{icon}</Box>
|
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 1, color: '#2563EB' }}>{icon}</Box>
|
||||||
<Typography sx={{ fontSize: '12px', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600, mb: 1 }}>
|
<Typography sx={{ fontSize: '12px', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600, mb: 1 }}>
|
||||||
{label}
|
{label}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography sx={{ fontSize: '36px', fontWeight: 800, color: '#073B4C', letterSpacing: '-0.03em' }}>
|
<Typography sx={{ fontSize: '30px', fontWeight: 700, color: '#111827', letterSpacing: '-0.02em' }}>
|
||||||
{value}
|
{value}
|
||||||
</Typography>
|
</Typography>
|
||||||
{subtitle && (
|
{subtitle && (
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ export default function ProposalFormPage() {
|
||||||
}, [form.workOrderNumber, form.customerAddress, form.serviceCategory, form.scopeOfWork, customCategory]);
|
}, [form.workOrderNumber, form.customerAddress, form.serviceCategory, form.scopeOfWork, customCategory]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ maxWidth: 1200 }}>
|
<Box sx={{ maxWidth: 1200, mx: 'auto' }}>
|
||||||
<Typography variant="h5" sx={{ mb: 3 }}>
|
<Typography variant="h5" sx={{ mb: 3 }}>
|
||||||
New Proposal Request
|
New Proposal Request
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
|
||||||
113
web/src/theme.ts
113
web/src/theme.ts
|
|
@ -1,53 +1,56 @@
|
||||||
import { createTheme } from '@mui/material/styles';
|
import { createTheme } from '@mui/material/styles';
|
||||||
|
|
||||||
|
// Sea Haven Ops design system — flat, dense, border-driven enterprise UI.
|
||||||
|
// Neutral navy/slate/gray structure; action-blue #2563EB is the only brand accent.
|
||||||
|
// Tokens mirror the Sea Haven Ops design bundle (tokens/colors.css, spacing.css, typography.css).
|
||||||
export const theme = createTheme({
|
export const theme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#0B5A73',
|
main: '#2563EB', // action blue — primary buttons, links, selected
|
||||||
light: '#0E7490',
|
light: '#3B82F6',
|
||||||
dark: '#073B4C',
|
dark: '#1D4ED8', // contained-button hover/pressed
|
||||||
contrastText: '#FFFFFF',
|
contrastText: '#FFFFFF',
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: '#475569',
|
main: '#4B5563', // slate-600
|
||||||
light: '#64748B',
|
light: '#6B7280',
|
||||||
dark: '#334155',
|
dark: '#374151',
|
||||||
contrastText: '#FFFFFF',
|
contrastText: '#FFFFFF',
|
||||||
},
|
},
|
||||||
success: {
|
success: {
|
||||||
main: '#2E7D32',
|
main: '#16A34A',
|
||||||
light: '#DCFCE7',
|
light: '#DCFCE7',
|
||||||
dark: '#1B5E20',
|
dark: '#166534',
|
||||||
contrastText: '#FFFFFF',
|
contrastText: '#FFFFFF',
|
||||||
},
|
},
|
||||||
warning: {
|
warning: {
|
||||||
main: '#F97316',
|
main: '#D97706', // orange — Urgent
|
||||||
light: '#FFEDD5',
|
light: '#FFEDD5',
|
||||||
dark: '#C2410C',
|
dark: '#9A3412',
|
||||||
contrastText: '#FFFFFF',
|
contrastText: '#FFFFFF',
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
main: '#DC2626',
|
main: '#DC2626', // red — Emergency / overdue / destructive
|
||||||
light: '#FEE2E2',
|
light: '#FEE2E2',
|
||||||
dark: '#991B1B',
|
dark: '#991B1B',
|
||||||
contrastText: '#FFFFFF',
|
contrastText: '#FFFFFF',
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
main: '#0284C7',
|
main: '#2563EB',
|
||||||
light: '#E0F2FE',
|
light: '#DBEAFE',
|
||||||
dark: '#075985',
|
dark: '#1E40AF',
|
||||||
contrastText: '#FFFFFF',
|
contrastText: '#FFFFFF',
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
default: '#F4F7FA',
|
default: '#F3F4F6', // gray-100 page background
|
||||||
paper: '#FFFFFF',
|
paper: '#FFFFFF',
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
primary: '#1E293B',
|
primary: '#111827', // navy-900
|
||||||
secondary: '#475569',
|
secondary: '#4B5563', // slate-600
|
||||||
disabled: '#94A3B8',
|
disabled: '#9CA3AF', // gray-400
|
||||||
},
|
},
|
||||||
divider: '#E2E8F0',
|
divider: '#E5E7EB', // gray-200
|
||||||
},
|
},
|
||||||
|
|
||||||
typography: {
|
typography: {
|
||||||
|
|
@ -60,12 +63,12 @@ export const theme = createTheme({
|
||||||
letterSpacing: '-0.02em',
|
letterSpacing: '-0.02em',
|
||||||
},
|
},
|
||||||
h5: {
|
h5: {
|
||||||
fontWeight: 700,
|
fontWeight: 600,
|
||||||
fontSize: '18px',
|
fontSize: '18px',
|
||||||
lineHeight: 1.3,
|
lineHeight: 1.3,
|
||||||
},
|
},
|
||||||
h6: {
|
h6: {
|
||||||
fontWeight: 700,
|
fontWeight: 600,
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
lineHeight: 1.4,
|
lineHeight: 1.4,
|
||||||
},
|
},
|
||||||
|
|
@ -85,11 +88,11 @@ export const theme = createTheme({
|
||||||
caption: {
|
caption: {
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
lineHeight: 1.35,
|
lineHeight: 1.35,
|
||||||
color: '#64748B',
|
color: '#6B7280',
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: 700,
|
fontWeight: 600,
|
||||||
textTransform: 'none' as const,
|
textTransform: 'none' as const,
|
||||||
},
|
},
|
||||||
overline: {
|
overline: {
|
||||||
|
|
@ -98,12 +101,12 @@ export const theme = createTheme({
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
letterSpacing: '0.08em',
|
letterSpacing: '0.08em',
|
||||||
textTransform: 'uppercase' as const,
|
textTransform: 'uppercase' as const,
|
||||||
color: '#94A3B8',
|
color: '#6B7280',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
shape: {
|
shape: {
|
||||||
borderRadius: 8,
|
borderRadius: 6,
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -111,7 +114,7 @@ export const theme = createTheme({
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
body: {
|
body: {
|
||||||
fontFeatureSettings: '"tnum"',
|
fontFeatureSettings: '"tnum"',
|
||||||
backgroundColor: '#F4F7FA',
|
backgroundColor: '#F3F4F6',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -122,8 +125,9 @@ export const theme = createTheme({
|
||||||
},
|
},
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
backgroundColor: '#073B4C',
|
backgroundColor: '#111827', // navy-900
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
|
borderRadius: 0,
|
||||||
borderBottom: '1px solid rgba(255,255,255,0.08)',
|
borderBottom: '1px solid rgba(255,255,255,0.08)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -133,7 +137,7 @@ export const theme = createTheme({
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
paper: {
|
paper: {
|
||||||
backgroundColor: '#FFFFFF',
|
backgroundColor: '#FFFFFF',
|
||||||
borderRight: '1px solid #DDE3E8',
|
borderRight: '1px solid #E5E7EB', // border-subtle
|
||||||
width: 216,
|
width: 216,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -144,9 +148,10 @@ export const theme = createTheme({
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
},
|
},
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
|
// Cards are border-driven — no shadow (overlays only).
|
||||||
root: {
|
root: {
|
||||||
border: '1px solid #D6E0EA',
|
border: '1px solid #E5E7EB',
|
||||||
boxShadow: '0 1px 2px rgba(15, 23, 42, 0.06)',
|
boxShadow: 'none',
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -182,10 +187,11 @@ export const theme = createTheme({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
outlined: {
|
outlined: {
|
||||||
borderColor: '#BFD0DF',
|
borderColor: '#D1D5DB', // border-default
|
||||||
|
color: '#111827',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
borderColor: '#0B5A73',
|
borderColor: '#9CA3AF', // border-strong
|
||||||
backgroundColor: '#E6F3F7',
|
backgroundColor: '#F9FAFB', // gray-50
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -210,13 +216,13 @@ export const theme = createTheme({
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
minHeight: 38,
|
minHeight: 38,
|
||||||
'& .MuiOutlinedInput-notchedOutline': {
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
borderColor: '#BFD0DF',
|
borderColor: '#D1D5DB',
|
||||||
},
|
},
|
||||||
'&:hover .MuiOutlinedInput-notchedOutline': {
|
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||||
borderColor: '#0E7490',
|
borderColor: '#9CA3AF',
|
||||||
},
|
},
|
||||||
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||||
borderColor: '#0B5A73',
|
borderColor: '#2563EB',
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -231,9 +237,9 @@ export const theme = createTheme({
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: '#64748B',
|
color: '#6B7280',
|
||||||
'&.Mui-focused': {
|
'&.Mui-focused': {
|
||||||
color: '#0B5A73',
|
color: '#2563EB',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -243,13 +249,13 @@ export const theme = createTheme({
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
'& .MuiTableCell-head': {
|
'& .MuiTableCell-head': {
|
||||||
color: '#334155',
|
color: '#374151', // slate-700
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
paddingTop: 12,
|
paddingTop: 12,
|
||||||
paddingBottom: 12,
|
paddingBottom: 12,
|
||||||
borderBottom: '1px solid #DDE3E8',
|
borderBottom: '1px solid #E5E7EB',
|
||||||
backgroundColor: '#F8FAFC',
|
backgroundColor: '#F9FAFB', // gray-50
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -259,10 +265,10 @@ export const theme = createTheme({
|
||||||
MuiTableCell: {
|
MuiTableCell: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
color: '#1E293B',
|
color: '#111827',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
padding: '12px 14px',
|
padding: '12px 14px',
|
||||||
borderBottom: '1px solid #E2E8F0',
|
borderBottom: '1px solid #E5E7EB',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -271,7 +277,7 @@ export const theme = createTheme({
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: '#F8FAFC',
|
backgroundColor: '#F9FAFB',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -307,17 +313,20 @@ export const theme = createTheme({
|
||||||
marginLeft: 8,
|
marginLeft: 8,
|
||||||
marginRight: 8,
|
marginRight: 8,
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
color: '#374151',
|
color: '#4B5563',
|
||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
backgroundColor: '#E6F2F8',
|
backgroundColor: '#EFF6FF', // blue-50
|
||||||
borderLeft: '3px solid #0B80B5',
|
borderLeft: '3px solid #2563EB',
|
||||||
color: '#1F2933',
|
color: '#111827',
|
||||||
|
'& .MuiListItemIcon-root': {
|
||||||
|
color: '#2563EB',
|
||||||
|
},
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: '#DCECF5',
|
backgroundColor: '#DBEAFE', // blue-100
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: '#F1F5F9',
|
backgroundColor: '#F3F4F6', // gray-100
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -326,7 +335,7 @@ export const theme = createTheme({
|
||||||
MuiListItemIcon: {
|
MuiListItemIcon: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
color: '#8A949E',
|
color: '#9CA3AF', // gray-400 — inactive nav icon
|
||||||
minWidth: 36,
|
minWidth: 36,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -336,7 +345,7 @@ export const theme = createTheme({
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
backgroundColor: '#1F2933',
|
backgroundColor: '#1F2937', // navy-800
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue