'use client'; import { useState, useEffect, useCallback } from 'react'; import { Verfuegbarkeit } from '@/types'; interface BookingCalendarProps { onDateSelect: (date: string | null) => void; selectedDate: string | null; } const WEEKDAYS = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']; const MONTH_NAMES = [ 'Jänner', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember', ]; function getSaisonRange(year: number) { return { start: new Date(year, 2, 15), end: new Date(year, 5, 30), }; } function formatDate(d: Date): string { return d.toISOString().split('T')[0]; } export default function BookingCalendar({ onDateSelect, selectedDate }: BookingCalendarProps) { const today = new Date(); const currentYear = today.getFullYear(); const saison = getSaisonRange(currentYear); const initialMonth = today > saison.start ? today.getMonth() : saison.start.getMonth(); const [viewMonth, setViewMonth] = useState(initialMonth); const [viewYear] = useState(currentYear); const [auslastung, setAuslastung] = useState>({}); const [maxPerDay, setMaxPerDay] = useState(5); const [loading, setLoading] = useState(true); const loadVerfuegbarkeit = useCallback(async () => { try { const res = await fetch(`/api/pool/verfuegbarkeit?year=${currentYear}`); const data = await res.json(); if (data.verfuegbarkeit) { const map: Record = {}; data.verfuegbarkeit.forEach((v: Verfuegbarkeit) => { map[v.datum] = v.anzahl_buchungen; }); setAuslastung(map); } if (data.max_per_day) { setMaxPerDay(data.max_per_day); } } catch { // silent } finally { setLoading(false); } }, [currentYear]); useEffect(() => { loadVerfuegbarkeit(); }, [loadVerfuegbarkeit]); const firstDayOfMonth = new Date(viewYear, viewMonth, 1); let startDow = firstDayOfMonth.getDay() - 1; if (startDow < 0) startDow = 6; const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate(); const days: (Date | null)[] = []; for (let i = 0; i < startDow; i++) days.push(null); for (let d = 1; d <= daysInMonth; d++) { days.push(new Date(viewYear, viewMonth, d)); } const canGoBack = viewMonth > saison.start.getMonth(); const canGoForward = viewMonth < saison.end.getMonth(); // Count available days this month let availableDays = 0; for (let d = 1; d <= daysInMonth; d++) { const day = new Date(viewYear, viewMonth, d); const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(0, 0, 0, 0); if (day >= saison.start && day <= saison.end && day >= tomorrow) { const dateStr = formatDate(day); const count = auslastung[dateStr] || 0; if (count < maxPerDay) availableDays++; } } function getDayStatus(day: Date): 'disabled' | 'full' | 'available' | 'partial' { if (day < saison.start || day > saison.end) return 'disabled'; const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(0, 0, 0, 0); if (day < tomorrow) return 'disabled'; const dateStr = formatDate(day); const count = auslastung[dateStr] || 0; if (count >= maxPerDay) return 'full'; if (count >= maxPerDay - 2) return 'partial'; return 'available'; } return (
{/* Sticky availability header */} {!loading && (

{availableDays} {availableDays === 1 ? 'Tag' : 'Tage'} verfügbar im {MONTH_NAMES[viewMonth]}

)}
{/* Month navigation */}

{MONTH_NAMES[viewMonth]} {viewYear}

{/* Weekday headers */}
{WEEKDAYS.map((d) => (
{d}
))}
{/* Days grid */} {loading ? (
{Array.from({ length: 35 }).map((_, i) => (
))}
) : (
{days.map((day, i) => { if (!day) return
; const status = getDayStatus(day); const dateStr = formatDate(day); const isSelected = selectedDate === dateStr; const count = auslastung[dateStr] || 0; const freeSlots = maxPerDay - count; return ( ); })}
)} {/* Legend */}
Frei
Fast voll
Voll
); }