Files
calendar/apps/web/modules/bookings/hooks/useCalendarNavigationCapabilities.ts
T
Eunjae LeeGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>sean-brydon
52d5261b51 feat(booking): implement cross-page/week navigation in booking details sheet with view persistence (#25545)
* display UTM parameters

* persist view (list | calendar) in localStorage

* move to next page on clicking "next" of last item in the page

improve navigation

^ Conflicts:
^	apps/web/modules/bookings/components/BookingCalendarContainer.tsx

* fix navigation on calendar view

* Delete apps/web/modules/bookings/NAVIGATION_IMPLEMENTATION.md

* avoid page size being 0

* check feature flag on user level

* use map to improve useBookingListData

* address feedback

* feat(bookings): add smart navigation for calendar view

- Add sort option to tRPC bookings.get schema and handler
- Create NAVIGATION_PROBE_WINDOW_MONTHS constant (3 months)
- Create useNearestFutureBooking hook to find nearest future booking
- Create useNearestPastBooking hook to find nearest past booking
- Update useCalendarNavigationCapabilities to use probe results
- Update BookingCalendarContainer to wire up probe hooks

This enables the booking details sheet to:
- Disable next/prev buttons when no bookings exist in that direction
- Jump directly to the week containing the nearest booking

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix type error

* feat: add booking selection state when using slideover (#25637)

* implement booking selection when using slideover

* remove pixel shift

* fix

* auto scroll to selected event on calendar

* make DateValues header sticky when scrolling

---------

Co-authored-by: Eunjae Lee <hey@eunjae.dev>

* prefetch previous / next weeks on calendar view

* clean up classes

* handle "fetched & but no data" situation more correctly in useCalendarAutoSelector

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2025-12-15 16:04:00 +00:00

127 lines
4.5 KiB
TypeScript

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]
);
}