'use client'; import { useCallback, useEffect, useState } from 'react'; import TimeInput from './TimeInput'; interface SofueTermin { id: number; wtermin: string; verein: string; name: string; vorname: string; anzahl: number; mitarbeiter: string | null; bemerkung: string | null; } type Aktion = 'absage' | 'verschiebung'; function gruppe(t: SofueTermin): string { if (t.verein.trim()) return t.verein.trim(); return [t.vorname, t.name].map((s) => s.trim()).filter(Boolean).join(' ') || '—'; } function datumDe(wtermin: string): string { const [j, m, t] = wtermin.split(' ')[0].split('-'); return `${t}.${m}.${j}`; } function zeitDe(wtermin: string): string { return (wtermin.split(' ')[1] ?? '').slice(0, 5); } export default function Fuehrungen() { const [termine, setTermine] = useState(null); const [ladefehler, setLadefehler] = useState(false); // Offener Bestätigungsbereich: welche Führung, welche Aktion const [offen, setOffen] = useState<{ id: number; aktion: Aktion } | null>(null); const [grund, setGrund] = useState(''); const [neuesDatum, setNeuesDatum] = useState(''); const [neueZeit, setNeueZeit] = useState('20:00'); const [speichert, setSpeichert] = useState(false); const [meldung, setMeldung] = useState<{ art: 'ok' | 'fehler'; text: string } | null>(null); const laden = useCallback(() => { fetch('/api/sofue') .then((r) => { if (!r.ok) throw new Error(); return r.json(); }) .then((d: SofueTermin[]) => { setTermine(d); setLadefehler(false); }) .catch(() => setLadefehler(true)); }, []); useEffect(() => { laden(); }, [laden]); function oeffnen(t: SofueTermin, aktion: Aktion) { setOffen({ id: t.id, aktion }); setGrund(''); setNeuesDatum(t.wtermin.slice(0, 10)); setNeueZeit(zeitDe(t.wtermin) || '20:00'); setMeldung(null); } async function ausfuehren(t: SofueTermin, aktion: Aktion) { if (aktion === 'verschiebung' && !neuesDatum) { setMeldung({ art: 'fehler', text: 'Bitte ein neues Datum wählen.' }); return; } setSpeichert(true); try { const res = await fetch(`/api/sofue/${t.id}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ aktion, grund, termin: aktion === 'verschiebung' ? `${neuesDatum} ${neueZeit}:00` : undefined, }), }); if (!res.ok) throw new Error(); setMeldung({ art: 'ok', text: aktion === 'absage' ? `Führung „${gruppe(t)}" vom ${datumDe(t.wtermin)} als ausgefallen vermerkt.` : `Führung „${gruppe(t)}" verschoben auf den ${datumDe(neuesDatum)} um ${neueZeit} Uhr.`, }); setOffen(null); laden(); } catch { setMeldung({ art: 'fehler', text: 'Die Änderung konnte nicht gespeichert werden.' }); } finally { setSpeichert(false); } } const thCls = 'px-3 py-2 border border-gray-300 text-xs font-semibold bg-gray-100 text-left whitespace-nowrap text-gray-900'; const tdCls = 'px-3 py-2 border border-gray-200 text-sm text-gray-900'; const btnCls = 'px-2 py-1 text-xs font-medium rounded-lg border-2 transition-colors whitespace-nowrap'; const inputCls = 'px-2 py-1 border-2 border-gray-400 rounded-lg bg-white text-sm text-gray-900 focus:border-blue-500 focus:outline-none'; return (

Kommende zugesagte Sonderführungen. Fällt eine Führung aus oder wird sie verschoben, kann das hier vermerkt werden — ein Logbuch-Eintrag ist dafür nicht nötig.

{meldung && (
{meldung.text}
)} {ladefehler && (
Die Termine konnten nicht geladen werden.
)} {!termine && !ladefehler &&
Lade Termine...
} {termine && termine.length === 0 && (
Keine kommenden Sonderführungen.
)} {termine && termine.length > 0 && (
{termine.map((t) => ( ))}
Termin Gruppe Pers. BEO Aktion
{datumDe(t.wtermin)}
{zeitDe(t.wtermin)} Uhr
{gruppe(t)} {t.anzahl} {t.mitarbeiter || '—'} {offen?.id === t.id ? (
{offen.aktion === 'verschiebung' && (
setNeuesDatum(e.target.value)} className={inputCls} />
)} setGrund(e.target.value.slice(0, 150))} maxLength={150} placeholder="Grund (optional)" className={`${inputCls} w-full`} />
) : (
)}
)}
); }