Skip to content

asif-jalil/room-availability-calender

Repository files navigation

Project Documentation

Overview

This project is part of the Grit System Technical Assessment for Front-End Engineers. The main objective is to enhance the calendar component by implementing infinite scrolling using a cursor query parameter in the useRoomRateAvailabilityCalendar query. Additionally, the candidate will optimize the horizontal scroll behavior of the calendar to ensure smooth and responsive navigation.

Solution

Infinite Scroll Implementation

To enable infinite scrolling in the calendar component, the following changes were made:

  • Replaced useQuery with useInfiniteQuery
    The useRoomRateAvailabilityCalendar hook now uses useInfiniteQuery for cursor-based pagination. The query function dynamically updates the cursor query parameter:

    return useInfiniteQuery({
      queryKey: ["property_room_calendar", params],
      queryFn: async ({ pageParam }) => {
        url.search = new URLSearchParams({
          start_date: params.start_date,
          end_date: params.end_date,
          cursor: pageParam.toString(),
        }).toString();
    
        return await Fetch<IResponse>({
          method: "GET",
          url,
        });
      },
      initialPageParam: 0,
      getNextPageParam: (lastPage) =>
        lastPage.data.room_categories.length ? lastPage.data.nextCursor : null,
    });
  • Implemented Intersection Observer
    An IntersectionObserver was added to detect when the user scrolls to the bottom of the content. A Box component is placed at the bottom of the page, and its visibility is tracked using a ref. When the observer detects the box is in view, it triggers the fetchNextPage function to load more data:

    const observerRef = useRef<HTMLDivElement | null>(null);
    
    useEffect(() => {
      const observer = new IntersectionObserver(
        (entries) => {
          const target = entries[0];
          if (
            target.isIntersecting &&
            !room_calendar.isFetching &&
            room_calendar.hasNextPage
          ) {
            if (process.env.NODE_ENV === "development") {
              console.log("Fetching next page...");
            }
            room_calendar.fetchNextPage();
          }
        },
        { threshold: 0.1 }
      );
    
      if (observerRef.current) observer.observe(observerRef.current);
    
      return () => {
        if (observerRef.current) observer.unobserve(observerRef.current);
      };
    }, [room_calendar.isFetching, room_calendar.hasNextPage]);
  • Updated Rendering Logic
    Updated the rendering logic to flatten and display data from all fetched pages:

    {
      room_calendar.isSuccess && room_calendar.data?.pages?.length > 0
        ? room_calendar.data.pages
            .flatMap((page) => page.data.room_categories)
            .map((room_category, index, array) => (
              <RoomRateAvailabilityCalendar
                key={index}
                index={index}
                InventoryRefs={InventoryRefs}
                isLastElement={index === array.length - 1}
                room_category={room_category}
                handleCalenderScroll={handleCalenderScroll}
              />
            ))
        : null;
    }

Horizontal Scroll Optimization

To ensure smooth horizontal scrolling, the following adjustments were made:

  • Scroll Calculation
    Adjusted the calculation of scrollLeft to account for both deltaX and deltaY. This ensures vertical mouse scrolling contributes to horizontal navigation when appropriate:

    useEffect(() => {
      const { current: rootContainer } = rootContainerRef;
      if (rootContainer) {
        const handler = (e: WheelEvent) => {
          if (
            mainGridContainerRef.current &&
            InventoryRefs.current &&
            calenderMonthsRef.current &&
            calenderDatesRef.current
          ) {
            console.log(e.deltaX, e.deltaY);
    
            if (e.deltaY !== 0 || e.deltaX !== 0) {
              e.preventDefault();
    
              let { scrollLeft } = mainGridContainerRef.current;
              scrollLeft += e.deltaX + e.deltaY;
    
              InventoryRefs.current.forEach((ref) => {
                if (ref.current) {
                  ref.current.scrollTo({ scrollLeft });
                }
              });
    
              calenderMonthsRef.current.scrollTo(scrollLeft);
              calenderDatesRef.current.scrollTo({ scrollLeft });
            }
          }
        };
    
        rootContainer.addEventListener("wheel", handler);
        return () => rootContainer.removeEventListener("wheel", handler);
      }
    });

Summary of Changes

Infinite Scroll

  • Replaced useQuery with useInfiniteQuery for cursor-based pagination.
  • Added an IntersectionObserver to detect the end of the content and trigger the next page fetch.
  • Updated the rendering logic to support dynamic data loading.

Horizontal Scroll

  • Enhanced the scrollLeft calculation to include both deltaX and deltaY, ensuring smooth and responsive scrolling.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages