diff --git a/client/src/components/Dashboard.jsx b/client/src/components/Dashboard.jsx index d333e7a..a27bb73 100755 --- a/client/src/components/Dashboard.jsx +++ b/client/src/components/Dashboard.jsx @@ -3,6 +3,7 @@ import axios from 'axios'; import CpasBadge, { getTier } from './CpasBadge'; import EmployeeModal from './EmployeeModal'; import AuditLog from './AuditLog'; +import DashboardMobile from './DashboardMobile'; const AT_RISK_THRESHOLD = 2; @@ -28,13 +29,26 @@ function isAtRisk(points) { return boundary !== null && (boundary - points) <= AT_RISK_THRESHOLD; } +// Media query hook +function useMediaQuery(query) { + const [matches, setMatches] = useState(false); + useEffect(() => { + const media = window.matchMedia(query); + if (media.matches !== matches) setMatches(media.matches); + const listener = () => setMatches(media.matches); + media.addEventListener('change', listener); + return () => media.removeEventListener('change', listener); + }, [matches, query]); + return matches; +} + const s = { wrap: { padding: '32px 40px', color: '#f8f9fa' }, header: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '24px', flexWrap: 'wrap', gap: '12px' }, title: { fontSize: '24px', fontWeight: 700, color: '#f8f9fa' }, subtitle: { fontSize: '13px', color: '#b5b5c0', marginTop: '3px' }, statsRow: { display: 'flex', gap: '16px', flexWrap: 'wrap', marginBottom: '28px' }, - statCard: { flex: '1', minWidth: '140px', background: '#181924', border: '1px solid #30313f', borderRadius: '8px', padding: '16px', textAlign: 'center' }, + statCard: { flex: '1', minWidth: '140px', background: '#181924', border: '1px solid #303136', borderRadius: '8px', padding: '16px', textAlign: 'center' }, statNum: { fontSize: '28px', fontWeight: 800, color: '#f8f9fa' }, statLbl: { fontSize: '11px', color: '#b5b5c0', marginTop: '4px' }, search: { padding: '10px 14px', border: '1px solid #333544', borderRadius: '6px', fontSize: '14px', width: '260px', background: '#050608', color: '#f8f9fa' }, @@ -49,6 +63,55 @@ const s = { auditBtn: { padding: '9px 18px', background: 'none', color: '#9ca0b8', border: '1px solid #2a2b3a', borderRadius: '6px', cursor: 'pointer', fontWeight: 600, fontSize: '13px' }, }; +// Mobile styles +const mobileStyles = ` + @media (max-width: 768px) { + .dashboard-wrap { + padding: 16px !important; + } + .dashboard-header { + flex-direction: column; + align-items: flex-start !important; + } + .dashboard-title { + font-size: 20px !important; + } + .dashboard-subtitle { + font-size: 12px !important; + } + .dashboard-stats { + gap: 10px !important; + } + .dashboard-stat-card { + min-width: calc(50% - 5px) !important; + padding: 12px !important; + } + .stat-num { + font-size: 24px !important; + } + .stat-lbl { + font-size: 10px !important; + } + .toolbar-right { + width: 100%; + flex-direction: column; + } + .search-input { + width: 100% !important; + } + .toolbar-btn { + width: 100%; + justify-content: center; + } + } + + @media (max-width: 480px) { + .dashboard-stat-card { + min-width: 100% !important; + } + } +`; + export default function Dashboard() { const [employees, setEmployees] = useState([]); const [filtered, setFiltered] = useState([]); @@ -56,6 +119,7 @@ export default function Dashboard() { const [selectedId, setSelectedId] = useState(null); const [showAudit, setShowAudit] = useState(false); const [loading, setLoading] = useState(true); + const isMobile = useMediaQuery('(max-width: 768px)'); const load = useCallback(() => { setLoading(true); @@ -82,49 +146,53 @@ export default function Dashboard() { return ( <> -
Loading…
+ ) : isMobile ? ( +