import { Button } from '@/components/ui/button'; import { DatePicker } from '@/components/ui/date-picker'; import { Field, FieldError, FieldLabel } from '@/components/ui/field'; import { Input } from '@/components/ui/input'; import { ResponsiveDialog, ResponsiveDialogClose, ResponsiveDialogContent, ResponsiveDialogDescription, ResponsiveDialogFooter, ResponsiveDialogHeader, ResponsiveDialogTitle, } from '@/components/ui/responsive-dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { zodResolver } from '@hookform/resolvers/zod'; import { useEffect, useMemo, useState } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { useBooking } from '../booking-provider'; import { fromIsoDateLocal, getDayOfWeek, minutesToTime, timeToMinutes, toIsoDateLocal, } from '../lib/booking-time'; const bookingFormSchema = z.object({ customerName: z .string() .trim() .min(2, 'Ingresa un nombre válido.') .max(120, 'El nombre no puede superar los 120 caracteres.'), customerPhone: z .string() .trim() .min(6, 'Ingresa un telefono válido.') .max(30, 'El telefono no puede superar los 30 caracteres.'), }); type BookingForm = z.infer; export function BookingCreateDialog() { const { courts, bookings, selectedDate, selectedSlot, isCreateBookingOpen, createBookingError, isCreatingBooking, closeCreateBooking, createBooking, } = useBooking(); const [date, setDate] = useState(selectedSlot?.date ?? selectedDate); const [sportId, setSportId] = useState(selectedSlot?.sportId ?? 'all'); const [courtId, setCourtId] = useState(selectedSlot?.courtId ?? ''); const [startTime, setStartTime] = useState(selectedSlot?.startTime ?? ''); const { register, handleSubmit, reset, formState: { errors, isValid }, } = useForm({ resolver: zodResolver(bookingFormSchema), mode: 'onChange', defaultValues: { customerName: '', customerPhone: '', }, }); useEffect(() => { if (!isCreateBookingOpen) return; setDate(selectedSlot?.date ?? selectedDate); setSportId(selectedSlot?.sportId ?? 'all'); setCourtId(selectedSlot?.courtId ?? ''); setStartTime(selectedSlot?.startTime ?? ''); reset(); }, [isCreateBookingOpen, reset, selectedDate, selectedSlot]); const sports = [...new Map(courts.map((court) => [court.sport.id, court.sport])).values()]; const filteredCourts = useMemo(() => { if (sportId === 'all') return courts; return courts.filter((court) => court.sportId === sportId); }, [courts, sportId]); const selectedCourt = courts.find((court) => court.id === courtId); const availableStartTimes = useMemo(() => { if (!selectedCourt) return []; const times: string[] = []; const dayOfWeek = getDayOfWeek(date); const dayAvailability = selectedCourt.availability .filter((range) => range.dayOfWeek === dayOfWeek) .sort((a, b) => timeToMinutes(a.startTime) - timeToMinutes(b.startTime)); const courtBookings = bookings.filter( (booking) => booking.date === date && booking.courtId === selectedCourt.id && booking.status !== 'CANCELLED' ); for (const range of dayAvailability) { const start = timeToMinutes(range.startTime); const end = timeToMinutes(range.endTime); for ( let minute = start; minute + selectedCourt.slotDurationMinutes <= end; minute += selectedCourt.slotDurationMinutes ) { const slotEnd = minute + selectedCourt.slotDurationMinutes; const isBooked = courtBookings.some((booking) => { const bookingStart = timeToMinutes(booking.startTime); const bookingEnd = timeToMinutes(booking.endTime); return minute < bookingEnd && slotEnd > bookingStart; }); if (!isBooked) { times.push(minutesToTime(minute)); } } } return times; }, [bookings, date, selectedCourt]); useEffect(() => { if (startTime && !availableStartTimes.includes(startTime)) { setStartTime(''); } }, [availableStartTimes, startTime]); const onSubmit = async (values: BookingForm) => { await createBooking({ courtId, date, startTime, customerName: values.customerName, customerPhone: values.customerPhone, }); }; return ( !open && closeCreateBooking()} > event.preventDefault()} > Nueva reserva Crea un turno para atención telefónica o mostrador.
Fecha { setDate(nextDate ? toIsoDateLocal(nextDate) : selectedDate); }} /> Deporte Cancha Horario
Nombre Teléfono {createBookingError &&

{createBookingError}

}
); }