import Link from 'next/link'; import { ArrowLeft, ChevronLeft, ChevronRight } from 'lucide-react'; import { getTranslations } from 'next-intl/server'; import { createBookingManagementApi } from '@kit/booking-management/api'; import { getSupabaseServerClient } from '@kit/supabase/server-client'; import { Badge } from '@kit/ui/badge'; import { Button } from '@kit/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card'; import { AccountNotFound } from '~/components/account-not-found'; import { CmsPageShell } from '~/components/cms-page-shell'; interface PageProps { params: Promise<{ account: string }>; searchParams: Promise>; } const WEEKDAYS = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']; const MONTH_NAMES = [ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember', ]; function getDaysInMonth(year: number, month: number): number { return new Date(year, month + 1, 0).getDate(); } function getFirstWeekday(year: number, month: number): number { const day = new Date(year, month, 1).getDay(); // Convert Sunday=0 to Monday-based (Mo=0, Di=1 … So=6) return day === 0 ? 6 : day - 1; } function isDateInRange( date: string, checkIn: string, checkOut: string, ): boolean { return date >= checkIn && date < checkOut; } export default async function BookingCalendarPage({ params, searchParams }: PageProps) { const { account } = await params; const search = await searchParams; const t = await getTranslations('bookings'); const client = getSupabaseServerClient(); const { data: acct } = await client .from('accounts') .select('id') .eq('slug', account) .single(); if (!acct) { return ( ); } const api = createBookingManagementApi(client); const now = new Date(); const year = Number(search.year) || now.getFullYear(); const month = search.month != null ? Number(search.month) - 1 : now.getMonth(); // Compute prev/next month for navigation links const prevMonth = month === 0 ? 12 : month; const prevYear = month === 0 ? year - 1 : year; const nextMonth = month === 11 ? 1 : month + 2; const nextYear = month === 11 ? year + 1 : year; const daysInMonth = getDaysInMonth(year, month); const firstWeekday = getFirstWeekday(year, month); // Load bookings for this month const monthStart = `${year}-${String(month + 1).padStart(2, '0')}-01`; const monthEnd = `${year}-${String(month + 1).padStart(2, '0')}-${String(daysInMonth).padStart(2, '0')}`; const bookings = await api.bookings.list(acct.id, { from: monthStart, to: monthEnd, page: 1, }); // Build set of occupied dates const occupiedDates = new Set(); for (const booking of bookings.data) { const b = booking as Record; if (b.status === 'cancelled' || b.status === 'no_show') continue; const checkIn = String(b.check_in ?? ''); const checkOut = String(b.check_out ?? ''); if (!checkIn || !checkOut) continue; for (let d = 1; d <= daysInMonth; d++) { const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`; if (isDateInRange(dateStr, checkIn, checkOut)) { occupiedDates.add(dateStr); } } } // Build calendar grid cells const cells: Array<{ day: number | null; occupied: boolean; isToday: boolean; }> = []; // Empty cells before first day for (let i = 0; i < firstWeekday; i++) { cells.push({ day: null, occupied: false, isToday: false }); } const todayStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`; for (let d = 1; d <= daysInMonth; d++) { const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`; cells.push({ day: d, occupied: occupiedDates.has(dateStr), isToday: dateStr === todayStr, }); } // Fill remaining cells to complete the grid while (cells.length % 7 !== 0) { cells.push({ day: null, occupied: false, isToday: false }); } return (
{/* Header */}

{t('calendar.subtitle')}

{/* Month Navigation */}
{MONTH_NAMES[month]} {year}
{/* Weekday Header */}
{WEEKDAYS.map((day) => (
{day}
))}
{/* Calendar Grid */}
{cells.map((cell, idx) => (
{cell.day !== null && ( <> {cell.day} {cell.occupied && ( )} )}
))}
{/* Legend */}
{t('calendar.occupied')}
{t('calendar.free')}
{t('calendar.today')}
{/* Summary */}

{t('calendar.bookingsThisMonth')}

{bookings.data.length}

{t('calendar.daysOccupied', { occupied: occupiedDates.size, total: daysInMonth, })}
); }