This commit is contained in:
2026-03-06 12:53:40 -06:00
parent e8962f058c
commit 333cad41d7
7 changed files with 671 additions and 169 deletions

View File

@@ -0,0 +1,68 @@
import React, { useState } from 'react';
const RESOLUTION_TYPES = [
'Corrective Training Completed',
'Management Discretion',
'Data Entry Error',
'Successfully Appealed',
];
const s = {
overlay: { position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.65)', zIndex: 2000, display: 'flex', alignItems: 'center', justifyContent: 'center' },
box: { background: 'white', borderRadius: '10px', padding: '28px', width: '440px', maxWidth: '95vw', boxShadow: '0 8px 32px rgba(0,0,0,0.22)' },
title: { fontSize: '17px', fontWeight: 700, color: '#2c3e50', marginBottom: '6px' },
sub: { fontSize: '12px', color: '#888', marginBottom: '20px' },
label: { fontWeight: 600, color: '#555', fontSize: '12px', marginBottom: '5px', display: 'block' },
input: { width: '100%', padding: '9px 12px', border: '1px solid #ddd', borderRadius: '5px', fontSize: '13px', fontFamily: 'inherit', marginBottom: '14px' },
btnRow: { display: 'flex', gap: '10px', justifyContent: 'flex-end', marginTop: '8px' },
btnOk: { padding: '10px 22px', background: '#856404', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', fontWeight: 700, fontSize: '13px' },
btnCancel:{ padding: '10px 22px', background: '#f1f3f5', color: '#555', border: 'none', borderRadius: '6px', cursor: 'pointer', fontWeight: 600, fontSize: '13px' },
violBox: { background: '#fff3cd', border: '1px solid #ffc107', borderRadius: '6px', padding: '10px 14px', marginBottom: '18px', fontSize: '13px' },
};
export default function NegateModal({ violation, onConfirm, onCancel }) {
const [resType, setResType] = useState('');
const [details, setDetails] = useState('');
const [resolvedBy, setResolvedBy] = useState('');
const [error, setError] = useState('');
const handleSubmit = () => {
if (!resType) { setError('Please select a resolution type.'); return; }
onConfirm({ resolution_type: resType, details, resolved_by: resolvedBy });
};
return (
<div style={s.overlay}>
<div style={s.box}>
<div style={s.title}> Negate Violation Points</div>
<div style={s.sub}>This will zero out the points from this incident. The record remains in the audit log.</div>
<div style={s.violBox}>
<strong>{violation.violation_name}</strong> &nbsp;·&nbsp; {violation.points} pts &nbsp;·&nbsp; {violation.incident_date}
</div>
<label style={s.label}>Resolution Type *</label>
<select style={s.input} value={resType} onChange={e => { setResType(e.target.value); setError(''); }}>
<option value="">-- Select Resolution --</option>
{RESOLUTION_TYPES.map(r => <option key={r} value={r}>{r}</option>)}
</select>
<label style={s.label}>Additional Details</label>
<textarea style={{ ...s.input, resize: 'vertical', minHeight: '70px' }}
placeholder="Training course completed, specific context, approving manager notes…"
value={details} onChange={e => setDetails(e.target.value)} />
<label style={s.label}>Resolved By</label>
<input style={s.input} type="text" placeholder="Officer / Manager name"
value={resolvedBy} onChange={e => setResolvedBy(e.target.value)} />
{error && <div style={{ color: '#c0392b', fontSize: '12px', marginBottom: '10px' }}>{error}</div>}
<div style={s.btnRow}>
<button style={s.btnCancel} onClick={onCancel}>Cancel</button>
<button style={s.btnOk} onClick={handleSubmit}>Confirm Negation</button>
</div>
</div>
</div>
);
}