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:
2026-07-13 16:05:36 +08:00
co-authored by Claude Sonnet 4.6
parent 370b985375
commit 690485f74e
8 changed files with 307 additions and 30 deletions
+13 -1
View File
@@ -10,6 +10,15 @@ import { getApiKey } from '@/lib/settings'
export const dynamic = 'force-dynamic'
export async function POST(request: Request) {
try {
return await handlePost(request)
} catch (err) {
console.error('Unhandled error in POST /api/incidents:', err)
return NextResponse.json({ error: 'Internal server error', details: [String(err)] }, { status: 500 })
}
}
async function handlePost(request: Request) {
const supabase = await createClient()
const { data, error: authError } = await supabase.auth.getUser()
if (authError || !data?.user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
@@ -80,13 +89,16 @@ export async function POST(request: Request) {
const { data: zone, error: zoneError } = await supabase
.from('zones')
.select('id, site_id')
.select('id, site_id, active, sites(active)')
.eq('qr_code_token', input.zone_token)
.single()
if (zoneError || !zone) {
return NextResponse.json({ error: 'Zone not found' }, { status: 404 })
}
if (zone.active === false || (zone.sites as { active?: boolean } | null)?.active === false) {
return NextResponse.json({ error: 'This location is no longer active for reporting.' }, { status: 409 })
}
const { data: incident, error: incidentError } = await supabase
.from('incidents')