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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user