Files
open-safetylog-ehs/src/components/IncidentTable.tsx
T
jayjennings 3e964d1176
CI / Lint, typecheck, test, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
feat(i18n): add internationalization support with English and Spanish translations
feat(kiosk): implement kiosk report submission functionality with offline support

test(outbox): add tests for outbox functionality including report submission and error handling

feat(outbox): create outbox management for queued reports with IndexedDB persistence

feat(uuid): add UUID generation utility for client IDs

feat(qr): implement QR code generation for reporting links by site and area

feat(kiosk): create Kiosk page for reporting incidents without an account
2026-10-06 11:38:40 -07:00

75 lines
3.4 KiB
TypeScript

import { AlertTriangle, MapPin } from 'lucide-react'
import { Link, useNavigate } from 'react-router-dom'
import type { Incident } from '../../shared/api-types.ts'
import { formatDate } from '../lib/dates'
import { incidentTypeLabel, incidentTypeTone } from '../lib/labels'
import { toneClasses } from '../lib/tones'
import { SeverityLabel, StatusBadge } from './Badges'
const th = 'border-b border-line px-2.5 py-3 font-mono text-3xs font-medium tracking-[0.06em] text-faint uppercase first:pl-6 last:pr-6'
const td = 'border-b border-line-soft px-2.5 py-3.5 text-2xs whitespace-nowrap text-body first:pl-6 last:pr-6'
export function IncidentTable({ incidents, wide = false }: { incidents: Incident[]; wide?: boolean }) {
const navigate = useNavigate()
return (
<div className="overflow-auto">
<table className={`w-full border-collapse text-left ${wide ? 'min-w-[900px]' : 'min-w-[720px]'}`}>
<thead>
<tr>
<th className={th}>Incident</th>
<th className={th}>Type</th>
<th className={th}>Severity</th>
<th className={th}>Location</th>
<th className={th}>Status</th>
<th className={th}>Occurred</th>
</tr>
</thead>
<tbody>
{incidents.map((incident) => (
<tr
key={incident.id}
className="cursor-pointer hover:bg-canvas [&:last-child>td]:border-b-0"
onClick={() => navigate(`/incidents/${incident.number}`)}
>
<td className={td}>
<div className="flex min-w-[250px] items-center gap-2.5">
<span className={`grid size-7 flex-none place-items-center rounded-[7px] ${toneClasses[incidentTypeTone[incident.type]]}`}>
<AlertTriangle size={14} />
</span>
<div className="min-w-0">
<Link
to={`/incidents/${incident.number}`}
className="block max-w-[320px] truncate text-xs font-semibold text-ink hover:underline"
onClick={(event) => event.stopPropagation()}
>
{incident.title}
</Link>
<small className="mt-1 block text-3xs text-faint">
{incident.reference} · {incident.anonymous ? 'Anonymous' : (incident.reporter?.name ?? incident.reporterName ?? 'Unknown')}
</small>
</div>
</div>
</td>
<td className={td}>{incidentTypeLabel[incident.type]}</td>
<td className={td}>
<SeverityLabel severity={incident.severity} />
</td>
<td className={td}>
<div className="flex max-w-[160px] flex-wrap items-center gap-1">
<MapPin size={14} className="flex-none" />
<span className="truncate">{incident.area?.name ?? incident.site.name}</span>
<small className="mt-1 ml-[18px] block w-full truncate text-3xs text-faint">{incident.locationDetail || incident.site.name}</small>
</div>
</td>
<td className={td}>
<StatusBadge status={incident.status} />
</td>
<td className={`${td} text-[#87938f]`}>{formatDate(incident.occurredAt)}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}