useBookingFilter — Custom Hook
Co-locates date and guest state with useCallback setters and a useMemo-driven price estimate. Invalid date ranges are silently rejected.
export function useBookingFilter() {
const [checkIn, setCheckIn] = useState<Date | null>(null);
const [checkOut, setCheckOut] = useState<Date | null>(null);
const [guests, setGuests] = useState<GuestCounts>({ adults: 1, children: 0, infants: 0 });
const updateDates = useCallback((newCheckIn: Date | null, newCheckOut: Date | null) => {
if (newCheckIn && newCheckOut && newCheckOut <= newCheckIn) {
return; // Reject invalid range
}
setCheckIn(newCheckIn);
setCheckOut(newCheckOut);
}, []);
const updateGuests = useCallback((type: keyof GuestCounts, count: number) => {
setGuests(prev => {
const limit = GUEST_LIMITS[type];
if (count < limit.min || count > limit.max) return prev;
return { ...prev, [type]: count };
});
}, []);
const priceEstimate = useMemo(() => {
if (!checkIn || !checkOut) return 0;
const timeDiff = checkOut.getTime() - checkIn.getTime();
if (timeDiff <= 0) return 0;
const nights = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
const guestMultiplier = guests.adults + (guests.children * CHILD_MULTIPLIER);
return nights * BASE_PRICE * guestMultiplier;
}, [checkIn, checkOut, guests.adults, guests.children]);
return { checkIn, checkOut, guests, priceEstimate, updateDates, updateGuests };
}