import React, { useState, useEffect, useCallback } from 'react'; import axios from 'axios'; import CpasBadge, { getTier } from './CpasBadge'; import EmployeeModal from './EmployeeModal'; import AuditLog from './AuditLog'; const AT_RISK_THRESHOLD = 2; const TIERS = [ { min: 0, max: 4 }, { min: 5, max: 9 }, { min: 10, max: 14 }, { min: 15, max: 19 }, { min: 20, max: 24 }, { min: 25, max: 29 }, { min: 30, max: 999 }, ]; function nextTierBoundary(points) { for (const t of TIERS) { if (points >= t.min && points <= t.max && t.max < 999) return t.max + 1; } return null; } function isAtRisk(points) { const boundary = nextTierBoundary(points); return boundary !== null && (boundary - points) <= AT_RISK_THRESHOLD; } 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' }, 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' }, table: { width: '100%', borderCollapse: 'collapse', background: '#111217', borderRadius: '8px', overflow: 'hidden', boxShadow: '0 1px 8px rgba(0,0,0,0.6)', border: '1px solid #222' }, th: { background: '#000000', color: '#f8f9fa', padding: '10px 14px', textAlign: 'left', fontSize: '12px', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.5px' }, td: { padding: '11px 14px', borderBottom: '1px solid #1c1d29', fontSize: '13px', verticalAlign: 'middle', color: '#f8f9fa' }, nameBtn: { background: 'none', border: 'none', cursor: 'pointer', fontWeight: 600, color: '#d4af37', fontSize: '14px', padding: 0, textDecoration: 'underline dotted' }, atRiskBadge: { display: 'inline-block', marginLeft: '8px', padding: '2px 8px', borderRadius: '10px', fontSize: '10px', fontWeight: 700, background: '#3b2e00', color: '#ffd666', border: '1px solid #d4af37', verticalAlign: 'middle' }, zeroRow: { color: '#77798a', fontStyle: 'italic', fontSize: '12px' }, toolbarRight: { display: 'flex', gap: '10px', alignItems: 'center' }, refreshBtn: { padding: '9px 18px', background: '#d4af37', color: '#000', border: 'none', borderRadius: '6px', cursor: 'pointer', fontWeight: 600, fontSize: '13px' }, auditBtn: { padding: '9px 18px', background: 'none', color: '#9ca0b8', border: '1px solid #2a2b3a', borderRadius: '6px', cursor: 'pointer', fontWeight: 600, fontSize: '13px' }, }; export default function Dashboard() { const [employees, setEmployees] = useState([]); const [filtered, setFiltered] = useState([]); const [search, setSearch] = useState(''); const [selectedId, setSelectedId] = useState(null); const [showAudit, setShowAudit] = useState(false); const [loading, setLoading] = useState(true); const load = useCallback(() => { setLoading(true); axios.get('/api/dashboard') .then(r => { setEmployees(r.data); setFiltered(r.data); }) .finally(() => setLoading(false)); }, []); useEffect(() => { load(); }, [load]); useEffect(() => { const q = search.toLowerCase(); setFiltered(employees.filter(e => e.name.toLowerCase().includes(q) || (e.department || '').toLowerCase().includes(q) || (e.supervisor || '').toLowerCase().includes(q) )); }, [search, employees]); const atRiskCount = employees.filter(e => isAtRisk(e.active_points)).length; const activeCount = employees.filter(e => e.active_points > 0).length; const cleanCount = employees.filter(e => e.active_points === 0).length; const maxPoints = employees.reduce((m, e) => Math.max(m, e.active_points), 0); return ( <>
Company Dashboard
Click any employee name to view their full profile
setSearch(e.target.value)} />
{employees.length}
Total Employees
{cleanCount}
Elite Standing (0 pts)
{activeCount}
With Active Points
{atRiskCount}
At Risk (≤{AT_RISK_THRESHOLD} pts to next tier)
{maxPoints}
Highest Active Score
{loading ? (

Loading…

) : ( {filtered.length === 0 && ( )} {filtered.map((emp, i) => { const risk = isAtRisk(emp.active_points); const tier = getTier(emp.active_points); const boundary = nextTierBoundary(emp.active_points); return ( ); })}
# Employee Department Supervisor Tier / Standing Active Points 90-Day Violations
No employees found.
{i + 1} {risk && ( ⚠ {boundary - emp.active_points} pt{boundary - emp.active_points > 1 ? 's' : ''} to {getTier(boundary).label.split('—')[0].trim()} )} {emp.department || '—'} {emp.supervisor || '—'} {emp.active_points} {emp.violation_count}
)}
{selectedId && ( { setSelectedId(null); load(); }} /> )} {showAudit && setShowAudit(false)} />} ); }