feat: delete + deactivate for sites, zones, and trucks; harden incident API error handling
- zones: add active column (migration 20260713000005) - sites/zones API: PATCH (toggle active) + DELETE (blocked when incidents reference it) - trucks API: PATCH + DELETE with same pattern - admin page: select active for sites + zones - site-zone-manager + truck-manager: deactivate toggle + delete button per row with busyId - incidents API: reject reports on deactivated zone/site; wrap handler in top-level try-catch so unhandled errors return JSON (not HTML) - report-form: parse JSON separately so HTTP status code surfaces instead of generic "Something went wrong" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
This commit is contained in:
@@ -177,15 +177,26 @@ export function ReportForm({ zoneToken, trucks, initialTruckId }: Props) {
|
||||
files.forEach(f => fd.append('files', f))
|
||||
|
||||
const res = await fetch('/ims/api/incidents', { method: 'POST', body: fd })
|
||||
const data = await res.json()
|
||||
|
||||
// Parse JSON separately so a non-JSON response (HTML error page) surfaces the status code
|
||||
let data: Record<string, unknown> = {}
|
||||
try {
|
||||
data = await res.json()
|
||||
} catch {
|
||||
console.error('Non-JSON response from /api/incidents', res.status, res.statusText)
|
||||
setError(`${t.errorGeneric} (HTTP ${res.status})`)
|
||||
return
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
setError(data.details ? data.details.join('. ') : data.error)
|
||||
const msg = Array.isArray(data.details) ? (data.details as string[]).join('. ') : (data.error as string)
|
||||
setError(msg ?? t.errorGeneric)
|
||||
return
|
||||
}
|
||||
|
||||
router.push(`/report/success?ref=${data.reference_no}`)
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error('Submit error:', err)
|
||||
setError(t.errorGeneric)
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
|
||||
Reference in New Issue
Block a user