"use client"; import { useState, useEffect, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle, CardFooter, } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { cn, formatTime, formatCurrency, formatDate } from "@/lib/utils"; import type { SelectedSlot } from "./booking-calendar"; interface Client { id: string; firstName: string; lastName: string; email: string | null; phone: string | null; level: string | null; memberships: Array<{ id: string; status: string; remainingHours: number | null; plan: { id: string; name: string; discountPercent: number | null; }; }>; } interface ClientsResponse { data: Client[]; pagination: { total: number; limit: number; offset: number; hasMore: boolean; }; } interface BookingInfo { id: string; startTime: string; endTime: string; status: string; totalPrice: number; paymentType: string; client: { id: string; firstName: string; lastName: string; email: string | null; phone: string | null; }; court: { id: string; name: string; type: string; }; } interface BookingDialogProps { courtId: string; date: string; slot: SelectedSlot; onClose: () => void; onBookingCreated?: () => void; onBookingCancelled?: () => void; } export function BookingDialog({ courtId, date, slot, onClose, onBookingCreated, onBookingCancelled, }: BookingDialogProps) { const [searchQuery, setSearchQuery] = useState(""); const [clients, setClients] = useState([]); const [selectedClient, setSelectedClient] = useState(null); const [loadingClients, setLoadingClients] = useState(false); const [loadingBooking, setLoadingBooking] = useState(false); const [booking, setBooking] = useState(null); const [loadingBookingInfo, setLoadingBookingInfo] = useState(false); const [error, setError] = useState(null); const [creatingBooking, setCreatingBooking] = useState(false); const [cancellingBooking, setCancellingBooking] = useState(false); // Parse slot time to Date for display const [hours, minutes] = slot.time.split(":").map(Number); const slotDate = new Date(date); slotDate.setHours(hours, minutes, 0, 0); // Fetch booking info if slot is not available const fetchBookingInfo = useCallback(async () => { if (!slot.bookingId) return; setLoadingBookingInfo(true); try { const response = await fetch(`/api/bookings/${slot.bookingId}`); if (!response.ok) { throw new Error("Error al cargar la reserva"); } const data = await response.json(); setBooking(data); } catch (err) { setError(err instanceof Error ? err.message : "Error desconocido"); } finally { setLoadingBookingInfo(false); } }, [slot.bookingId]); // Fetch clients based on search const fetchClients = useCallback(async (search: string) => { if (!search || search.trim().length < 2) { setClients([]); return; } setLoadingClients(true); try { const response = await fetch( `/api/clients?search=${encodeURIComponent(search)}&limit=10` ); if (!response.ok) { throw new Error("Error al buscar clientes"); } const data: ClientsResponse = await response.json(); setClients(data.data); } catch (err) { console.error("Error fetching clients:", err); setClients([]); } finally { setLoadingClients(false); } }, []); // Load booking info on mount if slot is booked useEffect(() => { if (!slot.available && slot.bookingId) { fetchBookingInfo(); } }, [slot.available, slot.bookingId, fetchBookingInfo]); // Debounce client search useEffect(() => { const timer = setTimeout(() => { fetchClients(searchQuery); }, 300); return () => clearTimeout(timer); }, [searchQuery, fetchClients]); // Handle booking creation const handleCreateBooking = async () => { if (!selectedClient) return; setCreatingBooking(true); setError(null); try { // Calculate end time (1 hour later) const endDate = new Date(slotDate); endDate.setHours(endDate.getHours() + 1); const response = await fetch("/api/bookings", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ courtId, clientId: selectedClient.id, startTime: slotDate.toISOString(), endTime: endDate.toISOString(), paymentType: "CASH", }), }); if (!response.ok) { const data = await response.json(); throw new Error(data.error || "Error al crear la reserva"); } onBookingCreated?.(); onClose(); } catch (err) { setError(err instanceof Error ? err.message : "Error al crear la reserva"); } finally { setCreatingBooking(false); } }; // Handle booking cancellation const handleCancelBooking = async () => { if (!slot.bookingId) return; setCancellingBooking(true); setError(null); try { const response = await fetch(`/api/bookings/${slot.bookingId}`, { method: "DELETE", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ cancelReason: "Cancelada por el administrador", }), }); if (!response.ok) { const data = await response.json(); throw new Error(data.error || "Error al cancelar la reserva"); } onBookingCancelled?.(); onClose(); } catch (err) { setError( err instanceof Error ? err.message : "Error al cancelar la reserva" ); } finally { setCancellingBooking(false); } }; // Handle click outside to close const handleOverlayClick = (e: React.MouseEvent) => { if (e.target === e.currentTarget) { onClose(); } }; return (
{slot.available ? "Nueva Reserva" : "Detalle de Reserva"}

Cancha: {slot.courtName}

Fecha: {formatDate(date)}

Hora: {formatTime(slotDate)}

Precio:{" "} {formatCurrency(slot.price)}

{error && (
{error}
)} {/* Available slot - show client search */} {slot.available && (
setSearchQuery(e.target.value)} autoFocus />
{/* Client search results */}
{loadingClients && (
)} {!loadingClients && searchQuery.length >= 2 && clients.length === 0 && (

No se encontraron clientes.

)} {!loadingClients && clients.map((client) => ( ))}
{/* Selected client summary */} {selectedClient && (

Cliente seleccionado:

{selectedClient.firstName} {selectedClient.lastName}

{selectedClient.memberships.length > 0 && (

Membresia: {selectedClient.memberships[0].plan.name} {selectedClient.memberships[0].remainingHours !== null && selectedClient.memberships[0].remainingHours > 0 && ( ({selectedClient.memberships[0].remainingHours}h restantes) )}

)}
)}
)} {/* Booked slot - show booking info */} {!slot.available && (
{loadingBookingInfo && (
)} {!loadingBookingInfo && booking && (

Cliente

{booking.client.firstName} {booking.client.lastName}

{booking.client.phone && (

{booking.client.phone}

)} {booking.client.email && (

{booking.client.email}

)}

Estado

{booking.status === "CONFIRMED" && "Confirmada"} {booking.status === "PENDING" && "Pendiente"} {booking.status === "CANCELLED" && "Cancelada"} {booking.status === "COMPLETED" && "Completada"} {booking.status === "NO_SHOW" && "No asistio"}

Tipo de Pago

{booking.paymentType === "CASH" && "Efectivo"} {booking.paymentType === "CARD" && "Tarjeta"} {booking.paymentType === "TRANSFER" && "Transferencia"} {booking.paymentType === "MEMBERSHIP" && "Membresia"} {booking.paymentType === "FREE" && "Gratuito"}

Total

{formatCurrency(Number(booking.totalPrice))}

)} {!loadingBookingInfo && !booking && (

No se pudo cargar la informacion de la reserva.

)}
)}
{slot.available && ( )} {!slot.available && booking && booking.status !== "CANCELLED" && ( )}
); }