refactor(admin): split evidence, process-tasks, iace/hazards pages

Extract components and hooks into _components/ and _hooks/ subdirectories
to reduce each page.tsx to under 500 LOC (was 1545/1383/1316).

Final line counts: evidence=213, process-tasks=304, hazards=157.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-16 17:12:15 +02:00
parent e0c1d21879
commit 1fcd8244b1
27 changed files with 2621 additions and 4083 deletions

View File

@@ -0,0 +1,149 @@
'use client'
import { useState, useCallback, useEffect } from 'react'
import { ProcessTask, TaskStats, TaskFormData, CompleteFormData, API } from '../_components/types'
export function useProcessTasks() {
const [activeTab, setActiveTab] = useState<'overview' | 'all' | 'calendar'>('overview')
const [tasks, setTasks] = useState<ProcessTask[]>([])
const [totalTasks, setTotalTasks] = useState(0)
const [stats, setStats] = useState<TaskStats | null>(null)
const [upcomingTasks, setUpcomingTasks] = useState<ProcessTask[]>([])
const [filterStatus, setFilterStatus] = useState('')
const [filterCategory, setFilterCategory] = useState('')
const [filterFrequency, setFilterFrequency] = useState('')
const [page, setPage] = useState(0)
const PAGE_SIZE = 25
const [loading, setLoading] = useState(true)
const [toast, setToast] = useState<string | null>(null)
const [showForm, setShowForm] = useState(false)
const [editTask, setEditTask] = useState<ProcessTask | null>(null)
const [detailTask, setDetailTask] = useState<ProcessTask | null>(null)
const [completeTask, setCompleteTask] = useState<ProcessTask | null>(null)
const [skipTask, setSkipTask] = useState<ProcessTask | null>(null)
const loadStats = useCallback(async () => {
try {
const res = await fetch(`${API}/stats`)
if (res.ok) setStats(await res.json())
} catch { /* ignore */ }
}, [])
const loadUpcoming = useCallback(async () => {
try {
const res = await fetch(`${API}/upcoming?days=30`)
if (res.ok) { const data = await res.json(); setUpcomingTasks(data.tasks || []) }
} catch { /* ignore */ }
}, [])
const loadTasks = useCallback(async () => {
setLoading(true)
try {
const params = new URLSearchParams()
if (filterStatus) params.set('status', filterStatus)
if (filterCategory) params.set('category', filterCategory)
if (filterFrequency) params.set('frequency', filterFrequency)
params.set('limit', String(PAGE_SIZE))
params.set('offset', String(page * PAGE_SIZE))
const res = await fetch(`${API}?${params}`)
if (res.ok) { const data = await res.json(); setTasks(data.tasks || []); setTotalTasks(data.total || 0) }
} catch { /* ignore */ }
setLoading(false)
}, [filterStatus, filterCategory, filterFrequency, page])
useEffect(() => {
loadStats()
loadUpcoming()
loadTasks()
}, [loadStats, loadUpcoming, loadTasks])
const handleCreate = async (data: TaskFormData) => {
const res = await fetch(API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!res.ok) {
const err = await res.json().catch(() => ({}))
throw new Error(err.detail || 'Fehler beim Erstellen')
}
setToast('Aufgabe erstellt')
loadTasks(); loadStats(); loadUpcoming()
}
const handleUpdate = async (data: TaskFormData) => {
if (!editTask) return
const res = await fetch(`${API}/${editTask.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!res.ok) {
const err = await res.json().catch(() => ({}))
throw new Error(err.detail || 'Fehler beim Speichern')
}
setEditTask(null)
setToast('Aufgabe aktualisiert')
loadTasks(); loadStats(); loadUpcoming()
}
const handleComplete = async (data: CompleteFormData) => {
if (!completeTask) return
const res = await fetch(`${API}/${completeTask.id}/complete`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!res.ok) throw new Error('Fehler')
setCompleteTask(null)
setToast('Aufgabe als erledigt markiert')
loadTasks(); loadStats(); loadUpcoming()
}
const handleSkip = async (reason: string) => {
if (!skipTask) return
const res = await fetch(`${API}/${skipTask.id}/skip`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reason }),
})
if (!res.ok) throw new Error('Fehler')
setSkipTask(null)
setToast('Aufgabe uebersprungen')
loadTasks(); loadStats(); loadUpcoming()
}
const handleDelete = async (id: string) => {
const res = await fetch(`${API}/${id}`, { method: 'DELETE' })
if (!res.ok) throw new Error('Fehler beim Loeschen')
setToast('Aufgabe geloescht')
loadTasks(); loadStats(); loadUpcoming()
}
const handleSeed = async () => {
const res = await fetch(`${API}/seed`, { method: 'POST' })
if (res.ok) {
const data = await res.json()
setToast(`${data.seeded} Standard-Aufgaben erstellt`)
loadTasks(); loadStats(); loadUpcoming()
}
}
const totalPages = Math.ceil(totalTasks / PAGE_SIZE)
return {
activeTab, setActiveTab,
tasks, totalTasks, stats, upcomingTasks,
filterStatus, setFilterStatus,
filterCategory, setFilterCategory,
filterFrequency, setFilterFrequency,
page, setPage, PAGE_SIZE, totalPages,
loading, toast, setToast,
showForm, setShowForm,
editTask, setEditTask,
detailTask, setDetailTask,
completeTask, setCompleteTask,
skipTask, setSkipTask,
handleCreate, handleUpdate, handleComplete, handleSkip, handleDelete, handleSeed,
}
}