'use client'; import { useEffect, useRef, useState } from 'react'; import type { Kuppel, LogbuchEintrag } from '@/types/logbuch'; interface Props { kuppel: Kuppel; refreshKey: number; onEdit: (entry: LogbuchEintrag) => void; limit?: number; compact?: boolean; } const pad = (n: number) => String(n).padStart(2, '0'); const MONATE = ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember']; function currentMonth() { const d = new Date(); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}`; } function monthLabel(ym: string) { const [y, m] = ym.split('-').map(Number); return `${MONATE[m - 1]} ${y}`; } function prevMonth(ym: string) { const [y, m] = ym.split('-').map(Number); return m === 1 ? `${y - 1}-12` : `${y}-${pad(m - 1)}`; } function nextMonth(ym: string) { const [y, m] = ym.split('-').map(Number); return m === 12 ? `${y + 1}-01` : `${y}-${pad(m + 1)}`; } function formatDate(dt: string, short = false): string { if (!dt) return ''; const d = new Date(dt); if (isNaN(d.getTime())) return dt; if (short) return `${pad(d.getDate())}.${pad(d.getMonth() + 1)}.`; return `${pad(d.getDate())}.${pad(d.getMonth() + 1)}.${d.getFullYear()}`; } function formatTime(dt: string): string { if (!dt) return ''; const d = new Date(dt); if (isNaN(d.getTime())) return dt; return `${pad(d.getHours())}:${pad(d.getMinutes())}`; } export default function LogbuchList({ kuppel, refreshKey, onEdit, limit = 10, compact = false }: Props) { const [entries, setEntries] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(0); const [month, setMonth] = useState(compact ? '' : currentMonth()); const [loading, setLoading] = useState(true); const [deleteId, setDeleteId] = useState(null); const [error, setError] = useState(''); const [printEntries, setPrintEntries] = useState(null); const [search, setSearch] = useState(''); const [activeSearch, setActiveSearch] = useState(''); const printPending = useRef(false); useEffect(() => { const t = setTimeout(() => setActiveSearch(search.trim()), 300); return () => clearTimeout(t); }, [search]); useEffect(() => { setPage(0); }, [kuppel, refreshKey, month, activeSearch]); useEffect(() => { setLoading(true); const offset = page * limit; const url = `/api/logbuch?kuppel=${encodeURIComponent(kuppel)}&limit=${limit}&offset=${offset}` + (activeSearch ? `&search=${encodeURIComponent(activeSearch)}` : (month ? `&month=${encodeURIComponent(month)}` : '')); fetch(url) .then((r) => { if (!r.ok) throw new Error(); return r.json(); }) .then((data) => { setEntries(data.entries); setTotal(data.total); setLoading(false); }) .catch(() => { setError('Fehler beim Laden.'); setLoading(false); }); }, [kuppel, refreshKey, limit, page, month, activeSearch]); useEffect(() => { function onAfterPrint() { setPrintEntries(null); } window.addEventListener('afterprint', onAfterPrint); return () => window.removeEventListener('afterprint', onAfterPrint); }, []); useEffect(() => { if (printPending.current && printEntries !== null) { printPending.current = false; window.print(); } }, [printEntries]); async function handlePrint() { const url = `/api/logbuch?kuppel=${encodeURIComponent(kuppel)}&limit=500&offset=0` + (month ? `&month=${encodeURIComponent(month)}` : '') + '&order=asc'; const data = await fetch(url).then((r) => r.json()); printPending.current = true; setPrintEntries(data.entries); } async function confirmDelete(id: number) { try { const res = await fetch(`/api/logbuch/${id}`, { method: 'DELETE' }); if (!res.ok) throw new Error(); setEntries((prev) => prev.filter((e) => e.ID !== id)); setTotal((t) => t - 1); } catch { setError('Fehler beim Löschen.'); } finally { setDeleteId(null); } } const toolbar = !compact && (
setMonth(e.target.value > currentMonth() ? currentMonth() : e.target.value)} className="border border-[#407BFF] rounded-lg px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-inset focus:ring-[#235CC8]" /> {month !== currentMonth() && ( )}
setSearch(e.target.value)} placeholder="Suche in Bemerkungen, Objekte, BEOs…" className="w-full px-3 py-1.5 pr-8 border border-[#407BFF] rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-inset focus:ring-[#235CC8]" /> {search ? ( ) : ( 🔍 )}
); const printHeader = !compact && (
Monat: {monthLabel(month)}
); const cell = compact ? 'px-1.5 py-1 border border-gray-200 text-xs' : 'px-3 py-2 border border-gray-200'; const head = compact ? 'px-1.5 py-1 border border-gray-300 text-xs font-semibold' : 'px-3 py-2 border border-gray-300'; const displayEntries = printEntries ?? entries; return (
{toolbar} {printHeader} {loading &&
Lade Einträge...
} {error &&
{error}
} {!loading && !error &&
{compact ? ( <> ) : ( )} {!compact && } {!compact && } {displayEntries.length === 0 ? ( ) : displayEntries.map((e) => ( {compact ? ( <> ) : ( )} {!compact && } {!compact && ( )} ))}
DatumStart EndeZeitArt Bes. BEOs ObjekteBemerkungenWetterAktionen
{activeSearch ? `Keine Einträge für „${activeSearch}" gefunden.` : `Keine Einträge für ${monthLabel(month)}.`}
{formatDate(e.Beginn, compact)}{formatTime(e.Beginn)} {formatTime(e.Ende)}
{formatTime(e.Beginn)}
{formatTime(e.Ende)}
{e.ArtFuehrung}
{e.SonderName &&
{e.SonderName}
}
{e.Besucher || ''} {e.BEOs ? (() => { const beos = e.BEOs.split(', '); if (e.created_by_kuerzel) { const idx = beos.indexOf(e.created_by_kuerzel); if (idx > 0) beos.unshift(beos.splice(idx, 1)[0]); } return beos.map((k, i, arr) => ( {k === e.created_by_kuerzel ? {k} : k} {i < arr.length - 1 ? ', ' : ''} )); })() : '—'} {e.Objekte || '—'}{e.Bemerkungen || ''} {e.WetterTemp !== null && !(parseFloat(String(e.WetterTemp)) === 0 && parseFloat(String(e.WetterFeuchte ?? 0)) === 0 && parseFloat(String(e.WetterDruck ?? 0)) === 0) && (
{e.WetterTemp} °C
{Math.round(e.WetterFeuchte ?? 0)} %
{Math.round(e.WetterDruck ?? 0)} hPa
)}
} {!compact && total > limit && (
Seite {page + 1} von {Math.ceil(total / limit)}
)} {deleteId !== null && (

Eintrag löschen?

Dieser Eintrag wird unwiderruflich gelöscht.

)}
); }