-
Report an Incident
+
+
{/* will be translated via ReportForm */}Report an Incident
+
+
{zoneName ? (
{siteName ?? 'Unknown Site'} — {zoneName}
diff --git a/components/incidents/report-form.tsx b/components/incidents/report-form.tsx
index 53b5a77..531eb66 100644
--- a/components/incidents/report-form.tsx
+++ b/components/incidents/report-form.tsx
@@ -1,26 +1,10 @@
'use client'
-import { useState } from 'react'
+import { useState, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { FileUpload } from '@/components/incidents/file-upload'
import type { IncidentType, MedicalStatus } from '@/lib/incidents/validate'
-
-const INCIDENT_TYPE_LABELS: Record = {
- injury: 'Injury / Medical',
- near_miss: 'Near Miss',
- hazard: 'Hazard / Unsafe Condition',
- asset_damage: 'Asset / Equipment Damage',
- environmental: 'Environmental Incident',
- security: 'Security Incident',
- fire: 'Fire / Emergency',
-}
-
-const MEDICAL_STATUS_LABELS: Record = {
- none: 'No treatment needed',
- first_aid: 'First aid only',
- medical_treatment: 'Medical treatment (non-LTI)',
- lti: 'Lost Time Injury (LTI)',
-}
+import { useTranslations } from '@/lib/i18n/context'
interface Props {
zoneToken: string | null
@@ -30,9 +14,32 @@ interface Props {
export function ReportForm({ zoneToken }: Props) {
const router = useRouter()
+ const t = useTranslations('ReportForm')
+ const itLabels = useTranslations('IncidentType')
+ const msLabels = useTranslations('MedicalStatus')
+
+ const INCIDENT_TYPE_OPTIONS: [IncidentType, string][] = [
+ ['injury', itLabels.injury],
+ ['near_miss', itLabels.near_miss],
+ ['hazard', itLabels.hazard],
+ ['asset_damage', itLabels.asset_damage],
+ ['environmental', itLabels.environmental],
+ ['security', itLabels.security],
+ ['fire', itLabels.fire],
+ ]
+
+ const MEDICAL_STATUS_OPTIONS: [MedicalStatus, string][] = [
+ ['none', msLabels.none],
+ ['first_aid', msLabels.first_aid],
+ ['medical_treatment', msLabels.medical_treatment],
+ ['lti', msLabels.lti],
+ ]
+
const [submitting, setSubmitting] = useState(false)
const [error, setError] = useState(null)
const [files, setFiles] = useState([])
+ const [isOnline, setIsOnline] = useState(true)
+ const [savedOffline, setSavedOffline] = useState(false)
const [qualityCheck, setQualityCheck] = useState<{
score: number
passes: boolean
@@ -49,11 +56,46 @@ export function ReportForm({ zoneToken }: Props) {
asset_involved: false,
})
+ useEffect(() => {
+ // eslint-disable-next-line react-hooks/set-state-in-effect
+ setIsOnline(navigator.onLine)
+ const onOnline = () => setIsOnline(true)
+ const onOffline = () => setIsOnline(false)
+ window.addEventListener('online', onOnline)
+ window.addEventListener('offline', onOffline)
+ return () => {
+ window.removeEventListener('online', onOnline)
+ window.removeEventListener('offline', onOffline)
+ }
+ }, [])
+
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setError(null)
setSubmitting(true)
+ // Offline path — save to IndexedDB
+ if (!isOnline) {
+ try {
+ const { addPendingReport } = await import('@/lib/offline/db')
+ await addPendingReport({
+ zone_token: zoneToken ?? '',
+ incident_type: form.incident_type as IncidentType,
+ description: form.description,
+ injury_involved: form.injury_involved,
+ asset_involved: form.asset_involved,
+ medical_status: form.medical_status || undefined,
+ created_at: new Date().toISOString(),
+ })
+ setSavedOffline(true)
+ } catch (err) {
+ console.error('Offline save error:', err)
+ setError(t.errorGeneric)
+ }
+ setSubmitting(false)
+ return
+ }
+
if (!overrideQuality) {
try {
const qcRes = await fetch('/api/incidents/ai/quality-check', {
@@ -78,7 +120,7 @@ export function ReportForm({ zoneToken }: Props) {
}
}
} catch {
- // Quality check failure is non-blocking — proceed with submission
+ // Quality check failure is non-blocking
}
}
@@ -104,14 +146,28 @@ export function ReportForm({ zoneToken }: Props) {
router.push(`/report/success?ref=${data.reference_no}`)
} catch {
- setError('Something went wrong. Please try again.')
+ setError(t.errorGeneric)
} finally {
setSubmitting(false)
}
}
+ if (savedOffline) {
+ return (
+
+ {t.savedOffline}
+
+ )
+ }
+
return (