feat: add toast notifications to EmployeeModal for all actions

- Toast success/error on PDF download, negate, restore, hard delete
- Toast success on employee edit and violation amendment via modal callbacks
- Error details from API responses included in error toasts
This commit is contained in:
2026-03-07 21:40:36 -06:00
parent 114dbb1166
commit ecd3810050

View File

@@ -6,6 +6,7 @@ import EditEmployeeModal from './EditEmployeeModal';
import AmendViolationModal from './AmendViolationModal';
import ExpirationTimeline from './ExpirationTimeline';
import EmployeeNotes from './EmployeeNotes';
import { useToast } from './ToastProvider';
const s = {
overlay: {
@@ -97,6 +98,8 @@ export default function EmployeeModal({ employeeId, onClose }) {
const [editingEmp, setEditingEmp] = useState(false);
const [amending, setAmending] = useState(null); // violation object
const toast = useToast();
const load = useCallback(() => {
setLoading(true);
Promise.all([
@@ -116,34 +119,54 @@ export default function EmployeeModal({ employeeId, onClose }) {
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 link = document.createElement('a');
link.href = url;
link.download = `CPAS_${(empName || '').replace(/[^a-z0-9]/gi, '_')}_${date}.pdf`;
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
try {
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`;
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
toast.success('PDF downloaded.');
} catch (err) {
toast.error('PDF generation failed: ' + (err.response?.data?.error || err.message));
}
};
const handleHardDelete = async (id) => {
await axios.delete(`/api/violations/${id}`);
setConfirmDel(null);
load();
try {
await axios.delete(`/api/violations/${id}`);
toast.success('Violation permanently deleted.');
setConfirmDel(null);
load();
} catch (err) {
toast.error('Delete failed: ' + (err.response?.data?.error || err.message));
}
};
const handleRestore = async (id) => {
await axios.patch(`/api/violations/${id}/restore`);
setConfirmDel(null);
load();
try {
await axios.patch(`/api/violations/${id}/restore`);
toast.success('Violation restored to active.');
setConfirmDel(null);
load();
} catch (err) {
toast.error('Restore failed: ' + (err.response?.data?.error || err.message));
}
};
const handleNegate = async ({ resolution_type, details, resolved_by }) => {
await axios.patch(`/api/violations/${negating.id}/negate`, { resolution_type, details, resolved_by });
setNegating(null);
setConfirmDel(null);
load();
try {
await axios.patch(`/api/violations/${negating.id}/negate`, { resolution_type, details, resolved_by });
toast.success('Violation negated.');
setNegating(null);
setConfirmDel(null);
load();
} catch (err) {
toast.error('Negate failed: ' + (err.response?.data?.error || err.message));
}
};
const tier = score ? getTier(score.active_points) : null;
@@ -203,7 +226,7 @@ export default function EmployeeModal({ employeeId, onClose }) {
</div>
<div style={{ ...s.scoreCard, minWidth: '140px' }}>
<div style={{ fontSize: '13px', fontWeight: 700, color: tier?.color || '#f8f9fa' }}>
{tier ? tier.label : ''}
{tier ? tier.label : ''}
</div>
<div style={s.scoreLbl}>Current Tier</div>
</div>
@@ -405,14 +428,14 @@ export default function EmployeeModal({ employeeId, onClose }) {
<EditEmployeeModal
employee={employee}
onClose={() => setEditingEmp(false)}
onSaved={load}
onSaved={() => { toast.success('Employee updated.'); load(); }}
/>
)}
{amending && (
<AmendViolationModal
violation={amending}
onClose={() => setAmending(null)}
onSaved={load}
onSaved={() => { toast.success('Violation amended.'); load(); }}
/>
)}
</div>