Compare commits

..

2 Commits

View File

@@ -77,6 +77,10 @@ export default function Dashboard() {
const maxPoints = employees.reduce((m, e) => Math.max(m, e.active_points), 0);
return (
// FIX: Fragment wraps both s.wrap AND EmployeeModal so the modal is
// outside the s.wrap div — React synthetic events will no longer bubble
// from inside the modal up through the Dashboard's DOM tree.
<>
<div style={s.wrap}>
<div style={s.header}>
<div>
@@ -84,7 +88,12 @@ export default function Dashboard() {
<div style={s.subtitle}>Click any employee name to view their full profile</div>
</div>
<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
<input style={s.search} placeholder="Search name, dept, supervisor…" value={search} onChange={e => setSearch(e.target.value)} />
<input
style={s.search}
placeholder="Search name, dept, supervisor…"
value={search}
onChange={e => setSearch(e.target.value)}
/>
<button style={s.refreshBtn} onClick={load}> Refresh</button>
</div>
</div>
@@ -129,17 +138,26 @@ export default function Dashboard() {
</thead>
<tbody>
{filtered.length === 0 && (
<tr><td colSpan={7} style={{ ...s.td, textAlign: 'center', ...s.zeroRow }}>No employees found.</td></tr>
<tr>
<td colSpan={7} style={{ ...s.td, textAlign: 'center', ...s.zeroRow }}>
No employees found.
</td>
</tr>
)}
{filtered.map((emp, i) => {
const risk = isAtRisk(emp.active_points);
const tier = getTier(emp.active_points);
const boundary = nextTierBoundary(emp.active_points);
return (
<tr key={emp.id} style={{ background: risk ? '#181200' : i % 2 === 0 ? '#111217' : '#151622' }}>
<tr
key={emp.id}
style={{ background: risk ? '#181200' : i % 2 === 0 ? '#111217' : '#151622' }}
>
<td style={{ ...s.td, color: '#77798a', fontSize: '12px' }}>{i + 1}</td>
<td style={s.td}>
<button style={s.nameBtn} onClick={() => setSelectedId(emp.id)}>{emp.name}</button>
<button style={s.nameBtn} onClick={() => setSelectedId(emp.id)}>
{emp.name}
</button>
{risk && (
<span style={s.atRiskBadge}>
{boundary - emp.active_points} pt{boundary - emp.active_points > 1 ? 's' : ''} to {getTier(boundary).label.split('—')[0].trim()}
@@ -149,7 +167,9 @@ export default function Dashboard() {
<td style={{ ...s.td, color: '#c0c2d6' }}>{emp.department || '—'}</td>
<td style={{ ...s.td, color: '#c0c2d6' }}>{emp.supervisor || '—'}</td>
<td style={s.td}><CpasBadge points={emp.active_points} /></td>
<td style={{ ...s.td, fontWeight: 700, color: tier.color, fontSize: '16px' }}>{emp.active_points}</td>
<td style={{ ...s.td, fontWeight: 700, color: tier.color, fontSize: '16px' }}>
{emp.active_points}
</td>
<td style={{ ...s.td, color: '#c0c2d6' }}>{emp.violation_count}</td>
</tr>
);
@@ -157,13 +177,17 @@ export default function Dashboard() {
</tbody>
</table>
)}
</div>
{/* FIX: EmployeeModal is now OUTSIDE <div style={s.wrap}>.
React synthetic events no longer bubble from modal buttons
up through Dashboard's component tree. */}
{selectedId && (
<EmployeeModal
employeeId={selectedId}
onClose={() => { setSelectedId(null); load(); }}
/>
)}
</div>
</>
);
}