import { AlertOctagon, CheckCircle2, CircleDashed, Pencil, Printer, Trash2, UserPlus, XCircle } from 'lucide-react' import { useState, type ChangeEvent, type FormEvent, type ReactNode } from 'react' import { Link } from 'react-router-dom' import type { IncidentDetail, OshaCase } from '../../../shared/api-types.ts' import { employmentTypes, oshaCaseTypes, oshaSpecialCases } from '../../../shared/enums.ts' import { assessRecordability, FIRST_AID, OSHA_REPORTING_CONTACT, PRIVACY_CASES, severeInjuryReport, type EmploymentType } from '../../../shared/osha.ts' import type { OshaCaseInput } from '../../../shared/schemas.ts' import { formatDateTime, toDateTimeLocal } from '../../lib/dates' import { employmentTypeLabel, oshaCaseTypeLabel, oshaOutcomeLabel, oshaSpecialCaseLabel } from '../../lib/labels' import { useOshaCaseMutations, useOshaCases } from '../../lib/queries' import { Field } from '../FormSection' import { ErrorMessage, Loading } from '../QueryState' import { field, iconButton, primaryButton, secondaryButton, smallButton, textButton } from '../styles' import { Section } from './Section' /** Injured or ill people and their OSHA recordkeeping. Medical details are only shown to safety managers. */ export function OshaSection({ incident }: { incident: IncidentDetail }) { const cases = useOshaCases(incident.number, incident.can.manageOsha) const [editing, setEditing] = useState(null) const { remove } = useOshaCaseMutations(incident.number) if (!incident.can.manageOsha) { if (incident.injuredCount === 0) return null return (

{incident.injuredCount} {incident.injuredCount === 1 ? 'person' : 'people'} recorded. Injury and medical details are confidential and only visible to safety managers.

) } return (
setEditing('new')}> Add person ) } > {editing ? ( setEditing(null)} /> ) : cases.isPending ? ( ) : cases.data?.length ? (
    {cases.data.map((c) => ( setEditing(c)} onRemove={() => confirm(`Remove ${c.name} (${c.caseNumber}) from this incident? This deletes their OSHA case.`) && remove.mutate(c.id)} /> ))}
) : (

Nobody recorded. Add anyone who was hurt or made ill to decide whether it goes on the OSHA log.

)}
) } function Verdict({ recordable, status, outcome }: { recordable: boolean; status?: 'incomplete'; outcome: OshaCase['outcome'] }) { if (status === 'incomplete') { return ( Not assessed ) } return recordable ? ( OSHA recordable · {oshaOutcomeLabel[outcome ?? 'other']} ) : ( Not recordable ) } function SevereAlert({ osha }: { osha: OshaCase }) { if (!osha.severeReport || osha.reportedToOshaAt) return null return (
Report to OSHA within {osha.severeReport.withinHours} hours ({osha.severeReport.reasons.join(', ')}), by {formatDateTime(osha.severeReport.dueBy)}. {OSHA_REPORTING_CONTACT}
) } function CaseSummary({ osha, onEdit, onRemove }: { osha: OshaCase; onEdit: () => void; onRemove: () => void }) { const assessed = assessRecordability(osha) return (
  • {osha.name} {osha.privacyCase && Privacy case}

    Case {osha.caseNumber} · {[osha.jobTitle, osha.natureOfInjury, osha.bodyPart].filter(Boolean).join(' · ') || 'No details yet'}

    {osha.recordable && ( )}
    {osha.reportedToOshaAt &&

    Reported to OSHA {formatDateTime(osha.reportedToOshaAt)}

    } {osha.missingFields.length > 0 &&

    Still needed for Form 301 / OSHA submission: {osha.missingFields.join(', ')}.

    }
  • ) } type FormState = Required> & { reportedToOshaAt: string; sex: '' | 'M' | 'F' } function initialState(existing: OshaCase | null, incident: IncidentDetail): FormState { const c = existing return { name: c?.name ?? '', jobTitle: c?.jobTitle ?? '', employmentType: c?.employmentType ?? 'employee', bodyPart: c?.bodyPart ?? '', natureOfInjury: c?.natureOfInjury ?? '', caseType: c?.caseType ?? (incident.type === 'illness' ? 'other_illness' : 'injury'), workRelated: c?.workRelated ?? null, newCase: c?.newCase ?? null, died: c?.died ?? false, dateOfDeath: c?.dateOfDeath ?? '', daysAwayFromWork: c?.daysAwayFromWork ?? false, daysAway: c?.daysAway ?? 0, restrictedOrTransfer: c?.restrictedOrTransfer ?? false, daysRestricted: c?.daysRestricted ?? 0, medicalTreatment: c?.medicalTreatment ?? false, lossOfConsciousness: c?.lossOfConsciousness ?? false, significantDiagnosis: c?.significantDiagnosis ?? false, specialCase: c?.specialCase ?? 'none', privacyCase: c?.privacyCase ?? false, inpatientHospitalization: c?.inpatientHospitalization ?? false, amputation: c?.amputation ?? false, lossOfEye: c?.lossOfEye ?? false, reportedToOshaAt: c?.reportedToOshaAt ? toDateTimeLocal(new Date(c.reportedToOshaAt)) : '', employeeAddress: c?.employeeAddress ?? '', dateOfBirth: c?.dateOfBirth ?? '', dateOfHire: c?.dateOfHire ?? '', sex: c?.sex ?? '', physicianName: c?.physicianName ?? '', treatmentFacility: c?.treatmentFacility ?? '', treatedInEmergencyRoom: c?.treatedInEmergencyRoom ?? false, timeStartedWork: c?.timeStartedWork ?? '', activityBefore: c?.activityBefore ?? '', whatHappened: c?.whatHappened ?? incident.description ?? '', objectSubstance: c?.objectSubstance ?? '', } } const empty = (value: string) => (value.trim() === '' ? null : value.trim()) function Group({ title, help, children }: { title: string; help?: ReactNode; children: ReactNode }) { return (
    {title} {help &&
    {help}
    } {children}
    ) } function YesNo({ label, value, onChange }: { label: string; value: boolean | null; onChange: (value: boolean) => void }) { return (
    {label} {[true, false].map((option) => ( ))}
    ) } function Check({ label, checked, onChange, children }: { label: ReactNode; checked: boolean; onChange: (checked: boolean) => void; children?: ReactNode }) { return (
    {checked && children &&
    {children}
    }
    ) } function CaseForm({ incident, existing, onDone }: { incident: IncidentDetail; existing: OshaCase | null; onDone: () => void }) { const { create, update } = useOshaCaseMutations(incident.number) const [form, setForm] = useState(() => initialState(existing, incident)) const set = (key: K, value: FormState[K]) => setForm((current) => ({ ...current, [key]: value })) const text = (key: keyof FormState) => (event: ChangeEvent) => set(key, event.target.value as never) const verdict = assessRecordability({ ...form, employmentType: form.employmentType as EmploymentType }) const severe = severeInjuryReport(form) const totalDays = (form.daysAwayFromWork ? Number(form.daysAway) : 0) + (form.restrictedOrTransfer ? Number(form.daysRestricted) : 0) const mutation = existing ? update : create const submit = (event: FormEvent) => { event.preventDefault() const payload: OshaCaseInput & { name: string } = { ...form, name: form.name.trim(), daysAway: Number(form.daysAway) || 0, daysRestricted: Number(form.daysRestricted) || 0, sex: form.sex || null, reportedToOshaAt: form.reportedToOshaAt ? new Date(form.reportedToOshaAt).toISOString() : null, ...Object.fromEntries( (['jobTitle', 'bodyPart', 'natureOfInjury', 'employeeAddress', 'physicianName', 'treatmentFacility', 'activityBefore', 'whatHappened', 'objectSubstance', 'dateOfDeath', 'dateOfBirth', 'dateOfHire', 'timeStartedWork'] as const).map((key) => [ key, empty(String(form[key] ?? '')), ]), ), } if (existing) update.mutate({ id: existing.id, ...payload }, { onSuccess: onDone }) else create.mutate(payload, { onSuccess: onDone }) } return (
    set('workRelated', v)} /> set('newCase', v)} />

    Did it result in any of these? (tick all that apply)

    set('died', v)}> set('daysAwayFromWork', v)}> set('restrictedOrTransfer', v)}> {totalDays > 180 &&

    Stop counting once days away and restricted days reach 180 in total.

    } Medical treatment beyond first aid{' '}
    What counts as first aid? (the complete list)
      {FIRST_AID.map((item) => (
    • {item}
    • ))}

    Anything not on this list, such as prescription medication, stitches, rigid splints or physical therapy, is medical treatment.

    } checked={form.medicalTreatment} onChange={(v) => set('medicalTreatment', v)} /> set('lossOfConsciousness', v)} /> set('significantDiagnosis', v)} />
    {verdict.status === 'recordable' ? `Recordable: ${oshaOutcomeLabel[verdict.outcome!]}` : verdict.status === 'incomplete' ? 'Answer the questions above' : 'Not recordable'}
      {verdict.reasons.map((reason) => (
    • {reason}
    • ))}
    set('inpatientHospitalization', v)} /> set('amputation', v)} /> set('lossOfEye', v)} /> {severe && (
    Must be reported to OSHA within {severe.withinHours} hours. {OSHA_REPORTING_CONTACT}
    )}