'use client'; import { useEffect, useState } from 'react'; interface MonthRow { monat: number; tageFuehrungen: number; tageBeob: number; tageTD: number; tageSonst: number; tageBEOS: number; tagesToT: number; tageGesamt: number; besucherRF: number; besucherSF: number; besucherSonF: number; besucherPrF: number; besucherToT: number; besucherGesamt: number; } interface StatsData { monthly: MonthRow[]; cumulative: number; tage: number; year: number; } const MONATE = [ 'Januar','Februar','März','April','Mai','Juni', 'Juli','August','September','Oktober','November','Dezember', ]; function n(v: number) { return v > 0 ? v.toLocaleString('de-DE') : ''; } export default function Statistik() { const [year, setYear] = useState(new Date().getFullYear()); const [data, setData] = useState(null); const [fetchError, setFetchError] = useState(null); const error = fetchError === year ? 'Fehler beim Laden der Statistik.' : ''; const loading = !error && (!data || data.year !== year); useEffect(() => { let cancelled = false; fetch(`/api/statistik?year=${year}`) .then((r) => { if (!r.ok) throw new Error(); return r.json(); }) .then((d: StatsData) => { if (!cancelled) { setData(d); setFetchError(null); } }) .catch(() => { if (!cancelled) setFetchError(year); }); return () => { cancelled = true; }; }, [year]); if (loading) return
Lade Statistik...
; if (error) return
{error}
; const rows = data!.monthly; function col(key: keyof MonthRow): number { return rows.reduce((s, r) => s + (r[key] as number), 0); } const thTop = 'px-3 py-2 border border-gray-300 text-xs font-semibold bg-gray-100 text-center text-gray-900'; const thSub = 'px-3 py-2 border border-gray-300 text-xs font-semibold bg-gray-50 whitespace-nowrap text-gray-900'; const thDiv = 'px-3 py-2 border border-gray-300 border-l-4 border-l-gray-400 text-xs font-semibold bg-gray-50 whitespace-nowrap text-gray-900'; const td = 'px-3 py-2 border border-gray-200 text-sm text-right tabular-nums text-gray-900'; const tdDiv = 'px-3 py-2 border border-gray-200 border-l-4 border-l-gray-400 text-sm text-right tabular-nums text-gray-900'; const tdL = 'px-3 py-2 border border-gray-200 text-sm text-left whitespace-nowrap text-gray-900'; const tdSum = 'px-3 py-2 border border-gray-200 text-sm text-right tabular-nums font-semibold bg-gray-50 text-gray-900'; const tdSumDiv = 'px-3 py-2 border border-gray-200 border-l-4 border-l-gray-400 text-sm text-right tabular-nums font-semibold bg-gray-50 text-gray-900'; return (
setYear(parseInt(e.target.value, 10) || new Date().getFullYear())} min={2000} max={2100} className="w-24 px-2 py-1 border-2 border-gray-400 rounded-lg bg-white text-gray-900 text-sm focus:border-blue-500 focus:outline-none" />
{MONATE.map((name, idx) => { const m = idx + 1; const r = rows.find((row) => row.monat === m); const hasData = r && r.tageGesamt > 0; const cls = hasData ? '' : 'text-gray-400'; return ( ); })}
Monat Besucher Anzahl
RF SF SonF PrF ToT Gesamt Führ. Beob. TD Sonst. BEOS ToT Gesamt
{name} {r ? n(r.besucherRF) : ''} {r ? n(r.besucherSF) : ''} {r ? n(r.besucherSonF) : ''} {r ? n(r.besucherPrF) : ''} {r ? n(r.besucherToT) : ''} {r ? n(r.besucherGesamt) : ''} {r ? n(r.tageFuehrungen) : ''} {r ? n(r.tageBeob) : ''} {r ? n(r.tageTD) : ''} {r ? n(r.tageSonst) : ''} {r ? n(r.tageBEOS) : ''} {r ? n(r.tagesToT) : ''} {r ? n(r.tageGesamt) : ''}
Summe {n(col('besucherRF'))} {n(col('besucherSF'))} {n(col('besucherSonF'))} {n(col('besucherPrF'))} {n(col('besucherToT'))} {n(col('besucherGesamt'))} {n(col('tageFuehrungen'))} {n(col('tageBeob'))} {n(col('tageTD'))} {n(col('tageSonst'))} {n(col('tageBEOS'))} {n(col('tagesToT'))} {n(col('tageGesamt'))}
Besucher {year}
{data?.cumulative.toLocaleString('de-DE') ?? 0}
Führungen {year}
{data?.tage ?? 0}
); }