"use client"; import { useState, useEffect, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle, } from "@/components/ui/card"; import { TimeSlot } from "./time-slot"; import { cn, formatDate } from "@/lib/utils"; // Types for API responses interface Site { id: string; name: string; slug: string; openTime: string; closeTime: string; timezone: string; } interface Court { id: string; name: string; type: string; status: string; pricePerHour: number; isActive: boolean; site: Site; } interface Slot { time: string; available: boolean; price: number; bookingId?: string; clientName?: string; } interface CourtAvailability { court: { id: string; name: string; type: string; status: string; isActive: boolean; site: Site; }; date: string; slots: Slot[]; } export interface SelectedSlot { courtId: string; courtName: string; date: string; time: string; available: boolean; price: number; bookingId?: string; } interface BookingCalendarProps { siteId?: string; onSlotClick?: (slot: SelectedSlot) => void; } export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) { const [date, setDate] = useState(() => { const today = new Date(); today.setHours(0, 0, 0, 0); return today; }); const [courts, setCourts] = useState([]); const [availability, setAvailability] = useState>( new Map() ); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Format date as YYYY-MM-DD for API const formatDateForApi = (d: Date): string => { const year = d.getFullYear(); const month = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; }; // Fetch courts const fetchCourts = useCallback(async () => { try { const url = siteId ? `/api/courts?siteId=${siteId}` : "/api/courts"; const response = await fetch(url); if (!response.ok) { throw new Error("Error al cargar las canchas"); } const data = await response.json(); setCourts(data); return data as Court[]; } catch (err) { setError(err instanceof Error ? err.message : "Error desconocido"); return []; } }, [siteId]); // Fetch availability for a court const fetchCourtAvailability = useCallback( async (courtId: string, dateStr: string) => { try { const response = await fetch( `/api/courts/${courtId}/availability?date=${dateStr}` ); if (!response.ok) { throw new Error(`Error al cargar disponibilidad`); } return (await response.json()) as CourtAvailability; } catch (err) { console.error(`Error fetching availability for court ${courtId}:`, err); return null; } }, [] ); // Fetch all availability const fetchAllAvailability = useCallback( async (courtsList: Court[], dateStr: string) => { setLoading(true); const newAvailability = new Map(); const promises = courtsList.map(async (court) => { const avail = await fetchCourtAvailability(court.id, dateStr); if (avail) { newAvailability.set(court.id, avail); } }); await Promise.all(promises); setAvailability(newAvailability); setLoading(false); }, [fetchCourtAvailability] ); // Initial load useEffect(() => { const load = async () => { setLoading(true); const courtsList = await fetchCourts(); if (courtsList.length > 0) { await fetchAllAvailability(courtsList, formatDateForApi(date)); } else { setLoading(false); } }; load(); }, [fetchCourts, fetchAllAvailability, date]); // Navigation functions const goToPrevDay = () => { const newDate = new Date(date); newDate.setDate(newDate.getDate() - 1); setDate(newDate); }; const goToToday = () => { const today = new Date(); today.setHours(0, 0, 0, 0); setDate(today); }; const goToNextDay = () => { const newDate = new Date(date); newDate.setDate(newDate.getDate() + 1); setDate(newDate); }; // Handle slot click const handleSlotClick = (court: Court, slot: Slot) => { if (onSlotClick) { onSlotClick({ courtId: court.id, courtName: court.name, date: formatDateForApi(date), time: slot.time, available: slot.available, price: slot.price, bookingId: slot.bookingId, }); } }; // Get all unique time slots across all courts const getAllTimeSlots = (): string[] => { const times = new Set(); availability.forEach((avail) => { avail.slots.forEach((slot) => { times.add(slot.time); }); }); return Array.from(times).sort(); }; // Check if current date is today const isToday = (): boolean => { const today = new Date(); today.setHours(0, 0, 0, 0); return date.getTime() === today.getTime(); }; if (error) { return (

{error}

); } const timeSlots = getAllTimeSlots(); return (
Calendario

{formatDate(date)}

{loading ? (

Cargando disponibilidad...

) : courts.length === 0 ? (

No hay canchas disponibles.

) : (
{/* Header with court names */}
= 5 && "grid-cols-5" )} > {courts.map((court) => (

{court.name}

{court.type === "INDOOR" ? "Interior" : "Exterior"}

))}
{/* Time slots grid */}
{timeSlots.map((time) => (
= 5 && "grid-cols-5" )} > {courts.map((court) => { const courtAvail = availability.get(court.id); const slot = courtAvail?.slots.find((s) => s.time === time); if (!slot) { return (
No disponible
); } return (
handleSlotClick(court, slot)} />
); })}
))} {timeSlots.length === 0 && (

No hay horarios disponibles para este día.

)}
)} ); }