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>
150 lines
5.2 KiB
TypeScript
150 lines
5.2 KiB
TypeScript
'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,
|
|
}
|
|
}
|