Files
calendar/apps/web/modules/bookings/hooks/useCalendarAutoSelector.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

85 lines
2.8 KiB
TypeScript

import { useEffect, useRef } from "react";
import { useBookingDetailsSheetStore } from "../store/bookingDetailsSheetStore";
import type { BookingsGetOutput } from "../types";
/**
* Calendar-specific auto-selection logic hook.
* Handles auto-selecting bookings when navigating across weeks in calendar view.
*
* - For "first": Selects as soon as the first page loads (immediate)
* - For "last": Waits for all pages to load to ensure we get the actual last booking
*/
export function useCalendarAutoSelector(
bookings: BookingsGetOutput["bookings"],
hasNextPage: boolean,
isFetched: boolean,
isFetchingNextPage: boolean
) {
const pendingSelection = useBookingDetailsSheetStore((state) => state.pendingSelection);
const setSelectedBookingUid = useBookingDetailsSheetStore((state) => state.setSelectedBookingUid);
const clearPendingSelection = useBookingDetailsSheetStore((state) => state.clearPendingSelection);
const setIsTransitioning = useBookingDetailsSheetStore((state) => state.setIsTransitioning);
const bookingsRef = useRef(bookings);
useEffect(() => {
// Early return if no pending selection
if (!pendingSelection) {
return;
}
const hasBookingsChanged = bookings !== bookingsRef.current;
bookingsRef.current = bookings;
if (!hasBookingsChanged) return;
if (pendingSelection === "first" && isFetched && bookings.length === 0) {
// data fetching is finished but there is no booking to select
setIsTransitioning(false);
clearPendingSelection();
return;
}
// For "first", we can select immediately when the first page arrives
if (pendingSelection === "first" && bookings.length > 0) {
setSelectedBookingUid(bookings[0].uid);
setIsTransitioning(false);
clearPendingSelection();
return;
}
// For "last", wait until all pages are loaded to ensure we get the actual last booking
if (pendingSelection === "last") {
const isAllDataLoaded = !hasNextPage && !isFetchingNextPage;
if (isAllDataLoaded && bookings.length === 0) {
// data fetching is finished but there is no booking to select
setIsTransitioning(false);
clearPendingSelection();
return;
}
// Wait for all data to load AND for bookings to actually arrive
if (!isAllDataLoaded || bookings.length === 0) {
return;
}
const lastBooking = bookings[bookings.length - 1];
setSelectedBookingUid(lastBooking.uid);
// Clear transition state and pending selection after handling
setIsTransitioning(false);
clearPendingSelection();
}
}, [
bookings,
hasNextPage,
isFetched,
isFetchingNextPage,
pendingSelection,
setSelectedBookingUid,
clearPendingSelection,
setIsTransitioning,
]);
}