refactor: migrate ReportsPage to TanStack Query

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
CCS Admin 2026-02-26 18:31:59 +00:00
parent 29b54e58a2
commit 150be9183c
2 changed files with 54 additions and 43 deletions

View file

@ -0,0 +1,38 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import api from '@/services/api'
import type { ReportMeta } from '@/types'
interface ReportsListResponse {
items: ReportMeta[]
total: number
}
export function useReports() {
return useQuery({
queryKey: ['reports'],
queryFn: () =>
api.get<ReportsListResponse>('/reports/list').then(r => r.data),
})
}
export function useGenerateReport() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (params: { jahr: number; kw: number }) =>
api.post<ReportMeta>('/reports/generate', null, { params }).then(r => r.data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['reports'] })
},
})
}
export function useDeleteReports() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (ids: number[]) =>
api.delete('/reports/delete', { data: ids }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['reports'] })
},
})
}

View file

@ -1,8 +1,8 @@
import { useState, useEffect } from 'react' import { useState } from 'react'
import { Download, FileSpreadsheet, Loader2, Plus, Trash2 } from 'lucide-react' import { Download, FileSpreadsheet, Loader2, Plus, Trash2 } from 'lucide-react'
import api from '@/services/api' import api from '@/services/api'
import type { ReportMeta } from '@/types'
import { useAuth } from '@/context/AuthContext' import { useAuth } from '@/context/AuthContext'
import { useReports, useGenerateReport, useDeleteReports } from '@/hooks/useReports'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Checkbox } from '@/components/ui/checkbox' import { Checkbox } from '@/components/ui/checkbox'
@ -19,54 +19,31 @@ export function ReportsPage() {
const currentYear = new Date().getFullYear() const currentYear = new Date().getFullYear()
const currentKw = getISOWeek(new Date()) const currentKw = getISOWeek(new Date())
const [reports, setReports] = useState<ReportMeta[]>([]) // TanStack Query hooks
const [totalReports, setTotalReports] = useState(0) const { data, isLoading: loading } = useReports()
const [loading, setLoading] = useState(true) const reports = data?.items ?? []
const totalReports = data?.total ?? 0
const generateMutation = useGenerateReport()
const deleteMutation = useDeleteReports()
// Report generation state // Report generation state
const [genJahr, setGenJahr] = useState(currentYear) const [genJahr, setGenJahr] = useState(currentYear)
const [genKw, setGenKw] = useState(currentKw) const [genKw, setGenKw] = useState(currentKw)
const [generating, setGenerating] = useState(false)
const [genError, setGenError] = useState('') const [genError, setGenError] = useState('')
const [genSuccess, setGenSuccess] = useState('') const [genSuccess, setGenSuccess] = useState('')
// Selection state for deletion // Selection state for deletion
const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set()) const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set())
const [deleting, setDeleting] = useState(false)
// Fetch reports list
const fetchReports = () => {
setLoading(true)
api.get<{ items: ReportMeta[]; total: number }>('/reports/list')
.then((res) => {
setReports(res.data.items)
setTotalReports(res.data.total)
})
.catch(() => {
setReports([])
setTotalReports(0)
})
.finally(() => setLoading(false))
}
useEffect(() => {
fetchReports()
}, [])
const generateReport = async () => { const generateReport = async () => {
setGenerating(true)
setGenError('') setGenError('')
setGenSuccess('') setGenSuccess('')
try { try {
const res = await api.post<ReportMeta>('/reports/generate', null, { const result = await generateMutation.mutateAsync({ jahr: genJahr, kw: genKw })
params: { jahr: genJahr, kw: genKw }, setGenSuccess(`Bericht für KW ${result.kw}/${result.jahr} wurde generiert.`)
})
setGenSuccess(`Bericht für KW ${res.data.kw}/${res.data.jahr} wurde generiert.`)
fetchReports()
} catch { } catch {
setGenError('Fehler beim Generieren des Berichts.') setGenError('Fehler beim Generieren des Berichts.')
} finally {
setGenerating(false)
} }
} }
@ -122,15 +99,11 @@ export function ReportsPage() {
const deleteSelected = async () => { const deleteSelected = async () => {
if (selectedIds.size === 0) return if (selectedIds.size === 0) return
setDeleting(true)
try { try {
await api.delete('/reports/delete', { data: Array.from(selectedIds) }) await deleteMutation.mutateAsync(Array.from(selectedIds))
setSelectedIds(new Set()) setSelectedIds(new Set())
fetchReports()
} catch { } catch {
setGenError('Fehler beim Löschen der Berichte.') setGenError('Fehler beim Löschen der Berichte.')
} finally {
setDeleting(false)
} }
} }
@ -173,8 +146,8 @@ export function ReportsPage() {
max={53} max={53}
/> />
</div> </div>
<Button onClick={generateReport} disabled={generating}> <Button onClick={generateReport} disabled={generateMutation.isPending}>
{generating ? ( {generateMutation.isPending ? (
<> <>
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Loader2 className="mr-2 h-4 w-4 animate-spin" />
Wird generiert... Wird generiert...
@ -213,9 +186,9 @@ export function ReportsPage() {
variant="destructive" variant="destructive"
size="sm" size="sm"
onClick={deleteSelected} onClick={deleteSelected}
disabled={deleting} disabled={deleteMutation.isPending}
> >
{deleting ? ( {deleteMutation.isPending ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : ( ) : (
<Trash2 className="mr-2 h-4 w-4" /> <Trash2 className="mr-2 h-4 w-4" />