import { useState, useEffect } from 'react' import { Download, FileSpreadsheet, Loader2, Plus, Trash2 } from 'lucide-react' import api from '@/services/api' import type { ReportMeta } from '@/types' import { useAuth } from '@/context/AuthContext' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Checkbox } from '@/components/ui/checkbox' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Skeleton } from '@/components/ui/skeleton' import { Alert, AlertDescription } from '@/components/ui/alert' export function ReportsPage() { const { isAdmin } = useAuth() const currentYear = new Date().getFullYear() const currentKw = getISOWeek(new Date()) const [reports, setReports] = useState([]) const [totalReports, setTotalReports] = useState(0) const [loading, setLoading] = useState(true) // Report generation state const [genJahr, setGenJahr] = useState(currentYear) const [genKw, setGenKw] = useState(currentKw) const [generating, setGenerating] = useState(false) const [genError, setGenError] = useState('') const [genSuccess, setGenSuccess] = useState('') // Selection state for deletion const [selectedIds, setSelectedIds] = useState>(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 () => { setGenerating(true) setGenError('') setGenSuccess('') try { const res = await api.post('/reports/generate', null, { params: { jahr: genJahr, kw: genKw }, }) setGenSuccess(`Bericht für KW ${res.data.kw}/${res.data.jahr} wurde generiert.`) fetchReports() } catch { setGenError('Fehler beim Generieren des Berichts.') } finally { setGenerating(false) } } const downloadReport = (reportId: number) => { const token = localStorage.getItem('access_token') const url = `/api/reports/download/${reportId}` api.get(url, { responseType: 'blob' }) .then((res) => { const blob = new Blob([res.data as BlobPart], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', }) const blobUrl = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = blobUrl const contentDisposition = res.headers['content-disposition'] let filename = `bericht_${reportId}.xlsx` if (contentDisposition) { const match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/) if (match) filename = match[1].replace(/['"]/g, '') } link.download = filename document.body.appendChild(link) link.click() document.body.removeChild(link) window.URL.revokeObjectURL(blobUrl) }) .catch(() => { if (token) { window.open(`${url}?token=${encodeURIComponent(token)}`, '_blank') } }) } const toggleSelect = (id: number) => { setSelectedIds((prev) => { const next = new Set(prev) if (next.has(id)) { next.delete(id) } else { next.add(id) } return next }) } const toggleSelectAll = () => { if (selectedIds.size === reports.length) { setSelectedIds(new Set()) } else { setSelectedIds(new Set(reports.map((r) => r.id))) } } const deleteSelected = async () => { if (selectedIds.size === 0) return setDeleting(true) try { await api.delete('/reports/delete', { data: Array.from(selectedIds) }) setSelectedIds(new Set()) fetchReports() } catch { setGenError('Fehler beim Löschen der Berichte.') } finally { setDeleting(false) } } return (

Berichte

{/* Report generation (admin only) */} {isAdmin && ( Bericht generieren
setGenJahr(Number(e.target.value))} className="w-28" min={2020} max={2030} />
setGenKw(Number(e.target.value))} className="w-28" min={1} max={53} />
{genError && ( {genError} )} {genSuccess && ( {genSuccess} )}
)} {/* Reports table */}
Bisherige Berichte ({totalReports}) {isAdmin && selectedIds.size > 0 && ( )}
{loading ? (
{Array.from({ length: 5 }).map((_, i) => ( ))}
) : reports.length > 0 ? ( {isAdmin && ( 0 && selectedIds.size === reports.length} onCheckedChange={toggleSelectAll} /> )} Berichtsdatum Jahr KW Erstellt am Aktion {reports.map((r) => ( {isAdmin && ( toggleSelect(r.id)} /> )} {formatDate(r.report_date)} {r.jahr} KW {r.kw} {formatDateTime(r.generated_at)} ))}
) : (

Keine Berichte vorhanden.

)}
) } function getISOWeek(date: Date): number { const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())) const dayNum = d.getUTCDay() || 7 d.setUTCDate(d.getUTCDate() + 4 - dayNum) const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)) return Math.ceil(((d.getTime() - yearStart.getTime()) / 86_400_000 + 1) / 7) } function formatDate(dateStr: string): string { try { return new Date(dateStr).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', }) } catch { return dateStr } } function formatDateTime(dateStr: string): string { try { return new Date(dateStr).toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }) } catch { return dateStr } }