From a6d4885a53db99f5accdc606a91205fcd10dbade Mon Sep 17 00:00:00 2001 From: jason Date: Fri, 6 Mar 2026 17:19:57 -0600 Subject: [PATCH] Upload files to "client/src/components" --- client/src/components/EmployeeModal.jsx | 459 +++++++----------------- client/src/components/NegateModal.jsx | 207 ++++------- 2 files changed, 201 insertions(+), 465 deletions(-) diff --git a/client/src/components/EmployeeModal.jsx b/client/src/components/EmployeeModal.jsx index 2f66c0b..aca7091 100755 --- a/client/src/components/EmployeeModal.jsx +++ b/client/src/components/EmployeeModal.jsx @@ -5,148 +5,66 @@ import NegateModal from './NegateModal'; const s = { overlay: { - position: 'fixed', - inset: 0, - background: 'rgba(0,0,0,0.75)', - zIndex: 1000, - display: 'flex', - alignItems: 'flex-start', - justifyContent: 'flex-end', + position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.75)', + zIndex: 1000, display: 'flex', alignItems: 'flex-start', justifyContent: 'flex-end', }, panel: { - background: '#111217', - color: '#f8f9fa', - width: '680px', - maxWidth: '95vw', - height: '100vh', - overflowY: 'auto', - boxShadow: '-4px 0 24px rgba(0,0,0,0.7)', - display: 'flex', - flexDirection: 'column', + background: '#111217', color: '#f8f9fa', width: '680px', maxWidth: '95vw', + height: '100vh', overflowY: 'auto', boxShadow: '-4px 0 24px rgba(0,0,0,0.7)', + display: 'flex', flexDirection: 'column', }, header: { - background: 'linear-gradient(135deg, #000000, #151622)', - color: 'white', - padding: '24px 28px', - position: 'sticky', - top: 0, - zIndex: 10, + background: 'linear-gradient(135deg, #000000, #151622)', color: 'white', + padding: '24px 28px', position: 'sticky', top: 0, zIndex: 10, borderBottom: '1px solid #222', }, closeBtn: { - float: 'right', - background: 'none', - border: 'none', - color: 'white', - fontSize: '22px', - cursor: 'pointer', - lineHeight: 1, - marginTop: '-2px', - }, - body: { - padding: '24px 28px', - flex: 1, - }, - scoreRow: { - display: 'flex', - gap: '12px', - flexWrap: 'wrap', - marginBottom: '24px', + float: 'right', background: 'none', border: 'none', color: 'white', + fontSize: '22px', cursor: 'pointer', lineHeight: 1, marginTop: '-2px', }, + body: { padding: '24px 28px', flex: 1 }, + scoreRow: { display: 'flex', gap: '12px', flexWrap: 'wrap', marginBottom: '24px' }, scoreCard: { - flex: '1', - minWidth: '100px', - background: '#181924', - borderRadius: '8px', - padding: '14px', - textAlign: 'center', - border: '1px solid #2a2b3a', - }, - scoreNum: { - fontSize: '26px', - fontWeight: 800, - }, - scoreLbl: { - fontSize: '11px', - color: '#b5b5c0', - marginTop: '3px', + flex: '1', minWidth: '100px', background: '#181924', borderRadius: '8px', + padding: '14px', textAlign: 'center', border: '1px solid #2a2b3a', }, + scoreNum: { fontSize: '26px', fontWeight: 800 }, + scoreLbl: { fontSize: '11px', color: '#b5b5c0', marginTop: '3px' }, sectionHd: { - fontSize: '13px', - fontWeight: 700, - color: '#f8f9fa', - textTransform: 'uppercase', - letterSpacing: '0.5px', - marginBottom: '10px', - marginTop: '24px', + fontSize: '13px', fontWeight: 700, color: '#f8f9fa', textTransform: 'uppercase', + letterSpacing: '0.5px', marginBottom: '10px', marginTop: '24px', }, table: { - width: '100%', - borderCollapse: 'collapse', - fontSize: '12px', - background: '#181924', - borderRadius: '6px', - overflow: 'hidden', - border: '1px solid #2a2b3a', + width: '100%', borderCollapse: 'collapse', fontSize: '12px', background: '#181924', + borderRadius: '6px', overflow: 'hidden', border: '1px solid #2a2b3a', }, th: { - background: '#050608', - padding: '8px 10px', - textAlign: 'left', - color: '#f8f9fa', - fontWeight: 600, - fontSize: '11px', - textTransform: 'uppercase', + background: '#050608', padding: '8px 10px', textAlign: 'left', color: '#f8f9fa', + fontWeight: 600, fontSize: '11px', textTransform: 'uppercase', }, td: { - padding: '9px 10px', - borderBottom: '1px solid #202231', - verticalAlign: 'top', - color: '#f8f9fa', - }, - negatedRow: { - background: '#151622', - color: '#9ca0b8', + padding: '9px 10px', borderBottom: '1px solid #202231', + verticalAlign: 'top', color: '#f8f9fa', }, + negatedRow: { background: '#151622', color: '#9ca0b8' }, actionBtn: (color) => ({ - background: 'none', - border: `1px solid ${color}`, - color, - borderRadius: '4px', - padding: '3px 8px', - fontSize: '11px', - cursor: 'pointer', - marginRight: '4px', - fontWeight: 600, + background: 'none', border: `1px solid ${color}`, color, + borderRadius: '4px', padding: '3px 8px', fontSize: '11px', + cursor: 'pointer', marginRight: '4px', fontWeight: 600, }), resTag: { - display: 'inline-block', - padding: '2px 8px', - borderRadius: '10px', - fontSize: '10px', - fontWeight: 700, - background: '#053321', - color: '#9ef7c1', - border: '1px solid #0f5132', + display: 'inline-block', padding: '2px 8px', borderRadius: '10px', + fontSize: '10px', fontWeight: 700, background: '#053321', + color: '#9ef7c1', border: '1px solid #0f5132', }, pdfBtn: { - background: 'none', - border: '1px solid #d4af37', - color: '#ffd666', - borderRadius: '4px', - padding: '3px 8px', - fontSize: '11px', - cursor: 'pointer', - fontWeight: 600, + background: 'none', border: '1px solid #d4af37', color: '#ffd666', + borderRadius: '4px', padding: '3px 8px', fontSize: '11px', + cursor: 'pointer', fontWeight: 600, }, deleteConfirm: { - background: '#3c1114', - border: '1px solid #f5c6cb', - borderRadius: '6px', - padding: '12px', - marginTop: '8px', - fontSize: '12px', - color: '#ffb3b8', + background: '#3c1114', border: '1px solid #f5c6cb', borderRadius: '6px', + padding: '12px', marginTop: '8px', fontSize: '12px', color: '#ffb3b8', }, }; @@ -174,17 +92,11 @@ export default function EmployeeModal({ employeeId, onClose }) { .finally(() => setLoading(false)); }, [employeeId]); - useEffect(() => { - load(); - }, [load]); + useEffect(() => { load(); }, [load]); const handleDownloadPdf = async (violId, empName, date) => { - const response = await axios.get(`/api/violations/${violId}/pdf`, { - responseType: 'blob', - }); - const url = window.URL.createObjectURL( - new Blob([response.data], { type: 'application/pdf' }), - ); + const response = await axios.get(`/api/violations/${violId}/pdf`, { responseType: 'blob' }); + const url = window.URL.createObjectURL(new Blob([response.data], { type: 'application/pdf' })); const link = document.createElement('a'); link.href = url; link.download = `CPAS_${(empName || '').replace(/[^a-z0-9]/gi, '_')}_${date}.pdf`; @@ -208,9 +120,7 @@ export default function EmployeeModal({ employeeId, onClose }) { const handleNegate = async ({ resolution_type, details, resolved_by }) => { await axios.patch(`/api/violations/${negating.id}/negate`, { - resolution_type, - details, - resolved_by, + resolution_type, details, resolved_by, }); setNegating(null); setConfirmDel(null); @@ -221,115 +131,68 @@ export default function EmployeeModal({ employeeId, onClose }) { const active = violations.filter((v) => !v.negated); const negated = violations.filter((v) => v.negated); + // FIX: overlay click only closes if clicking the backdrop itself, NOT children const handleOverlayClick = (e) => { if (e.target === e.currentTarget) onClose(); }; return ( + // FIX: panel uses onClick stopPropagation to prevent bubbling to overlay
-
+
e.stopPropagation()}> + + {/* ── Header ── */}
- -
- {loading ? 'Loading…' : employee?.name || 'Employee Profile'} + +
+ {employee ? employee.name : 'Employee'}
{employee && ( -
- {[employee.department, employee.supervisor ? `Supervisor: ${employee.supervisor}` : null] - .filter(Boolean) - .join(' · ')} +
+ {employee.department} {employee.supervisor && `· Supervisor: ${employee.supervisor}`}
)}
+ {/* ── Body ── */}
{loading ? ( -

- Loading… -

+
Loading…
) : ( <> -
-
-
- {score?.active_points ?? 0} + {/* Score Cards */} + {score && ( +
+
+
+ {score.active_points} +
+
Active Points
-
Active Points
-
-
-
{score?.violation_count ?? 0}
-
90-Day Violations
-
-
-
{active.length}
-
Total On Record
-
-
-
- {negated.length} +
+
{score.total_violations}
+
Total Violations
+
+
+
{score.negated_count}
+
Negated
+
+
+
+ {tier ? tier.label : '—'} +
+
Current Tier
-
Negated
-
-
- - {tier && ( -
- {tier.label} - - Rolling 90-day window · Points expire automatically -
)} + {score && } + {/* ── Active Violations ── */}
Active Violations
{active.length === 0 ? ( -

+

No active violations on record. -

+
) : ( @@ -346,91 +209,52 @@ export default function EmployeeModal({ employeeId, onClose }) { - + @@ -439,9 +263,10 @@ export default function EmployeeModal({ employeeId, onClose }) {
{v.incident_date}
{v.violation_name}
-
- {v.category} -
+
{v.category}
{v.details && ( -
+
{v.details}
)}
- {v.points} - {v.points} + {/* FIX: All buttons use e.stopPropagation() to prevent overlay close */} + -
- {confirmDel === v.id ? ( + {confirmDel === v.id && (
- Permanently delete? This cannot be - undone. -
+ Permanently delete? This cannot be undone. +
- ) : ( - )}
)} + {/* ── Negated / Resolved Violations ── */} {negated.length > 0 && ( <> -
Negated / Resolved Violations
+
Negated / Resolved
@@ -457,89 +282,60 @@ export default function EmployeeModal({ employeeId, onClose }) { - + @@ -553,6 +349,7 @@ export default function EmployeeModal({ employeeId, onClose }) { + {/* FIX: NegateModal rendered OUTSIDE the panel so it sits at root z-index:2000 */} {negating && ( { if (e.target === e.currentTarget && onCancel) onCancel(); }; return (
-
+ {/* FIX: stopPropagation prevents modal clicks from bubbling to overlay */} +
e.stopPropagation()}> +
-
⊘ Negate Violation Points
+
Negate Violation
- This will zero out the points from this incident. The record remains in the audit log. + Record resolution for: {violation.violation_name}
- {violation.violation_name} · {violation.points} pts · {violation.incident_date} + ⚠ {violation.points} pt{violation.points !== 1 ? 's' : ''} · {violation.incident_date} · {violation.category}
-
-
Resolution Type *
- -
+
Resolution Type
+ -
-
Additional Details
-
{v.incident_date} -
- {v.violation_name} -
-
- {v.category} -
-
- {v.points} +
{v.violation_name}
+
{v.category}
{v.points} {v.resolution_type} {v.resolution_details && ( -
+
{v.resolution_details}
)} {v.resolved_by && ( -
+
by {v.resolved_by}
)}
- {confirmDel === v.id ? ( + + + {confirmDel === v.id && (
- Permanently delete? -
+ Permanently delete? This cannot be undone. +
- ) : ( - )}