import { useCallback, useMemo, useEffect } from "react"; import type { Dayjs } from "@calcom/dayjs"; import dayjs from "@calcom/dayjs"; import { trpc } from "@calcom/trpc/react"; import { getWeekStart } from "../lib/weekUtils"; import type { NavigationCapabilities } from "../store/bookingDetailsSheetStore"; import type { BookingListingStatus } from "../types"; import { useNearestFutureBooking } from "./useNearestFutureBooking"; import { useNearestPastBooking } from "./useNearestPastBooking"; interface UseCalendarNavigationCapabilitiesProps { currentWeekStart: Dayjs; setCurrentWeekStart: (date: Dayjs) => void; userWeekStart: number; /** Filters to use for probe queries and prefetching */ filters: { statuses: BookingListingStatus[]; userIds?: number[]; }; } /** * Calendar view navigation capabilities adapter. * Provides week-based navigation logic for the booking details sheet. * * This hook: * - Uses probe queries to find nearest bookings in each direction * - Jumps directly to the week containing the nearest booking (not just adjacent week) * - Disables navigation buttons when no bookings exist in that direction * - Prefetches booking data for the target weeks to improve navigation performance */ export function useCalendarNavigationCapabilities({ currentWeekStart, setCurrentWeekStart, userWeekStart, filters, }: UseCalendarNavigationCapabilitiesProps): NavigationCapabilities { const trpcUtils = trpc.useUtils(); // Probe queries for navigation - find nearest bookings in each direction const { nearestBooking: nearestFutureBooking } = useNearestFutureBooking({ currentWeekStart, filters, }); const { nearestBooking: nearestPastBooking } = useNearestPastBooking({ currentWeekStart, filters, }); const hasFutureBooking = !!nearestFutureBooking; const hasPastBooking = !!nearestPastBooking; const nextBookingDate = nearestFutureBooking?.startTime?.toString() ?? null; const prevBookingDate = nearestPastBooking?.startTime?.toString() ?? null; // Calculate target week starts for prefetching const nextWeekStart = useMemo(() => { if (!nextBookingDate) return null; return getWeekStart(dayjs(nextBookingDate), userWeekStart); }, [nextBookingDate, userWeekStart]); const prevWeekStart = useMemo(() => { if (!prevBookingDate) return null; return getWeekStart(dayjs(prevBookingDate), userWeekStart); }, [prevBookingDate, userWeekStart]); // Prefetch bookings for the next week with bookings // Uses prefetchInfinite to match the useInfiniteQuery used in BookingCalendarContainer useEffect(() => { if (!nextWeekStart) return; trpcUtils.viewer.bookings.get.prefetchInfinite({ limit: 100, filters: { statuses: filters.statuses, userIds: filters.userIds, afterStartDate: nextWeekStart.startOf("day").toISOString(), beforeEndDate: nextWeekStart.add(6, "day").endOf("day").toISOString(), }, }); }, [nextWeekStart, filters.statuses, filters.userIds, trpcUtils]); // Prefetch bookings for the previous week with bookings // Uses prefetchInfinite to match the useInfiniteQuery used in BookingCalendarContainer useEffect(() => { if (!prevWeekStart) return; trpcUtils.viewer.bookings.get.prefetchInfinite({ limit: 100, filters: { statuses: filters.statuses, userIds: filters.userIds, afterStartDate: prevWeekStart.startOf("day").toISOString(), beforeEndDate: prevWeekStart.add(6, "day").endOf("day").toISOString(), }, }); }, [prevWeekStart, filters.statuses, filters.userIds, trpcUtils]); const canNavigateToNextPeriod = useCallback(() => hasFutureBooking, [hasFutureBooking]); const canNavigateToPreviousPeriod = useCallback(() => hasPastBooking, [hasPastBooking]); const requestNextPeriod = useCallback(() => { if (!nextBookingDate) return; const targetWeekStart = getWeekStart(dayjs(nextBookingDate), userWeekStart); setCurrentWeekStart(targetWeekStart); }, [nextBookingDate, setCurrentWeekStart, userWeekStart]); const requestPreviousPeriod = useCallback(() => { if (!prevBookingDate) return; const targetWeekStart = getWeekStart(dayjs(prevBookingDate), userWeekStart); setCurrentWeekStart(targetWeekStart); }, [prevBookingDate, setCurrentWeekStart, userWeekStart]); return useMemo( () => ({ canNavigateToNextPeriod, canNavigateToPreviousPeriod, requestNextPeriod, requestPreviousPeriod, }), [canNavigateToNextPeriod, canNavigateToPreviousPeriod, requestNextPeriod, requestPreviousPeriod] ); }