diff --git a/client/src/components/DashboardMobile.jsx b/client/src/components/DashboardMobile.jsx new file mode 100644 index 0000000..cc7f2c1 --- /dev/null +++ b/client/src/components/DashboardMobile.jsx @@ -0,0 +1,157 @@ +import React from 'react'; +import CpasBadge, { getTier } from './CpasBadge'; + +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 = { + card: { + background: '#181924', + border: '1px solid #2a2b3a', + borderRadius: '10px', + padding: '16px', + marginBottom: '12px', + boxShadow: '0 1px 4px rgba(0,0,0,0.4)', + }, + cardAtRisk: { + background: '#181200', + border: '1px solid #d4af37', + }, + row: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + padding: '8px 0', + borderBottom: '1px solid rgba(255,255,255,0.05)', + }, + rowLast: { + borderBottom: 'none', + }, + label: { + fontSize: '11px', + fontWeight: 600, + color: '#9ca0b8', + textTransform: 'uppercase', + letterSpacing: '0.5px', + }, + value: { + fontSize: '14px', + fontWeight: 600, + color: '#f8f9fa', + textAlign: 'right', + }, + name: { + fontSize: '16px', + fontWeight: 700, + color: '#d4af37', + marginBottom: '8px', + cursor: 'pointer', + textDecoration: 'underline dotted', + background: 'none', + border: 'none', + padding: 0, + textAlign: 'left', + width: '100%', + }, + atRiskBadge: { + display: 'inline-block', + marginTop: '4px', + padding: '3px 8px', + borderRadius: '10px', + fontSize: '10px', + fontWeight: 700, + background: '#3b2e00', + color: '#ffd666', + border: '1px solid #d4af37', + }, + points: { + fontSize: '28px', + fontWeight: 800, + textAlign: 'center', + margin: '8px 0', + }, +}; + +export default function DashboardMobile({ employees, onEmployeeClick }) { + if (!employees || employees.length === 0) { + return ( +
+ No employees found. +
+ ); + } + + return ( +
+ {employees.map((emp) => { + const risk = isAtRisk(emp.active_points); + const tier = getTier(emp.active_points); + const boundary = nextTierBoundary(emp.active_points); + const cardStyle = risk ? { ...s.card, ...s.cardAtRisk } : s.card; + + return ( +
+ + {risk && ( +
+ ⚠ {boundary - emp.active_points} pt{boundary - emp.active_points > 1 ? 's' : ''} to {getTier(boundary).label.split('—')[0].trim()} +
+ )} + +
+ Tier / Standing + +
+ +
+ Active Points + {emp.active_points} +
+ +
+ 90-Day Violations + {emp.violation_count} +
+ + {emp.department && ( +
+ Department + {emp.department} +
+ )} + + {emp.supervisor && ( +
+ Supervisor + {emp.supervisor} +
+ )} +
+ ); + })} +
+ ); +}