From d6740503bee4d7b9ce6d9b80df4d46ba5dbf302f Mon Sep 17 00:00:00 2001 From: Vinoth Kumar V <123581387+VinoV1999@users.noreply.github.com> Date: Fri, 25 Aug 2023 05:09:45 +0530 Subject: [PATCH] feat: Column View Available Slots Fix (#9815) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vinoth Kumar V Co-authored-by: Peer Richelsen Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Co-authored-by: alannnc Co-authored-by: Shivam Kalra Co-authored-by: Omar López --- packages/features/bookings/Booker/Booker.tsx | 39 +++++++++++++++- .../Booker/components/AvailableTimeSlots.tsx | 46 +++++++++---------- .../bookings/Booker/components/Header.tsx | 9 ++-- packages/features/bookings/Booker/config.ts | 2 +- .../features/bookings/Booker/utils/event.ts | 3 ++ .../bookings/components/AvailableTimes.tsx | 5 +- .../schedules/lib/use-schedule/useSchedule.ts | 4 +- .../lib/use-schedule/useSlotsForDate.ts | 8 ++-- 8 files changed, 81 insertions(+), 35 deletions(-) diff --git a/packages/features/bookings/Booker/Booker.tsx b/packages/features/bookings/Booker/Booker.tsx index fb53c0e7f3..d273d11fdf 100644 --- a/packages/features/bookings/Booker/Booker.tsx +++ b/packages/features/bookings/Booker/Booker.tsx @@ -5,7 +5,9 @@ import StickyBox from "react-sticky-box"; import { shallow } from "zustand/shallow"; import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager"; +import dayjs from "@calcom/dayjs"; import { useEmbedType, useEmbedUiConfig, useIsEmbed } from "@calcom/embed-core/embed-iframe"; +import { useNonEmptyScheduleDays } from "@calcom/features/schedules"; import classNames from "@calcom/lib/classNames"; import useMediaQuery from "@calcom/lib/hooks/useMediaQuery"; import { BookerLayouts, defaultBookerLayoutSettings } from "@calcom/prisma/zod-utils"; @@ -74,6 +76,9 @@ const BookerComponent = ({ // In Embed we give preference to embed configuration for the layout.If that's not set, we use the App configuration for the event layout // But if it's mobile view, there is only one layout supported which is 'mobile' const layout = isEmbed ? (isMobile ? "mobile" : validateLayout(embedUiConfig.layout) || _layout) : _layout; + const columnViewExtraDays = useRef( + isTablet ? extraDaysConfig[layout].tablet : extraDaysConfig[layout].desktop + ); const [bookerState, setBookerState] = useBookerStore((state) => [state.state, state.setState], shallow); const selectedDate = useBookerStore((state) => state.selectedDate); @@ -87,9 +92,36 @@ const BookerComponent = ({ shallow ); + const date = dayjs(selectedDate).format("YYYY-MM-DD"); + const schedule = useScheduleForEvent({ prefetchNextMonth: true }); + const nonEmptyScheduleDays = useNonEmptyScheduleDays(schedule?.data?.slots).filter( + (slot) => dayjs(selectedDate).diff(slot, "day") <= 0 + ); + const extraDays = isTablet ? extraDaysConfig[layout].tablet : extraDaysConfig[layout].desktop; const bookerLayouts = event.data?.profile?.bookerLayouts || defaultBookerLayoutSettings; const animationScope = useBookerResizeAnimation(layout, bookerState); + const totalWeekDays = 7; + const addonDays = + nonEmptyScheduleDays.length < extraDays + ? (extraDays - nonEmptyScheduleDays.length + 1) * totalWeekDays + : nonEmptyScheduleDays.length === extraDays + ? totalWeekDays + : 0; + + //Taking one more avaliable slot(extraDays + 1) to claculate the no of days in between, that next and prev button need to shift. + const availableSlots = nonEmptyScheduleDays.slice(0, extraDays + 1); + if (nonEmptyScheduleDays.length !== 0) + columnViewExtraDays.current = + Math.abs(dayjs(selectedDate).diff(availableSlots[availableSlots.length - 2], "day")) + addonDays; + const prefetchNextMonth = + dayjs(date).month() !== dayjs(date).add(columnViewExtraDays.current, "day").month(); + const monthCount = + dayjs(date).add(1, "month").month() !== dayjs(date).add(columnViewExtraDays.current, "day").month() + ? 2 + : undefined; + const nextSlots = + Math.abs(dayjs(selectedDate).diff(availableSlots[availableSlots.length - 1], "day")) + addonDays; // I would expect isEmbed to be not needed here as it's handled in derived variable layout, but somehow removing it breaks the views. const defaultLayout = isEmbed @@ -214,8 +246,9 @@ const BookerComponent = ({ )}>
diff --git a/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx b/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx index 676e4e4933..1782113d97 100644 --- a/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx +++ b/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx @@ -3,16 +3,21 @@ import { useMemo, useRef, useEffect } from "react"; import dayjs from "@calcom/dayjs"; import { useIsEmbed } from "@calcom/embed-core/embed-iframe"; import { AvailableTimes, AvailableTimesSkeleton } from "@calcom/features/bookings"; -import { useSlotsForMultipleDates } from "@calcom/features/schedules/lib/use-schedule/useSlotsForDate"; +import { useSlotsForAvailableDates } from "@calcom/features/schedules/lib/use-schedule/useSlotsForDate"; import { classNames } from "@calcom/lib"; +import { trpc } from "@calcom/trpc"; import useMediaQuery from "@calcom/lib/hooks/useMediaQuery"; import { useBookerStore } from "../store"; import { useEvent, useScheduleForEvent } from "../utils/event"; +import { useNonEmptyScheduleDays } from "@calcom/features/schedules"; +import { BookerLayouts } from "@calcom/prisma/zod-utils"; type AvailableTimeSlotsProps = { extraDays?: number; limitHeight?: boolean; + prefetchNextMonth: boolean; + monthCount: number | undefined; seatsPerTimeSlot?: number | null; }; @@ -23,7 +28,8 @@ type AvailableTimeSlotsProps = { * will also fetch the next `extraDays` days and show multiple days * in columns next to each other. */ -export const AvailableTimeSlots = ({ extraDays, limitHeight, seatsPerTimeSlot }: AvailableTimeSlotsProps) => { +export const AvailableTimeSlots = ({ extraDays, limitHeight, seatsPerTimeSlot, prefetchNextMonth, monthCount}: AvailableTimeSlotsProps) => { + const reserveSlotMutation = trpc.viewer.public.slots.reserveSlot.useMutation(); const isMobile = useMediaQuery("(max-width: 768px)"); const selectedDate = useBookerStore((state) => state.selectedDate); const setSelectedTimeslot = useBookerStore((state) => state.setSelectedTimeslot); @@ -31,6 +37,8 @@ export const AvailableTimeSlots = ({ extraDays, limitHeight, seatsPerTimeSlot }: const isEmbed = useIsEmbed(); const event = useEvent(); const date = selectedDate || dayjs().format("YYYY-MM-DD"); + const [layout] = useBookerStore((state) => [state.layout]); + const isColumnView = layout === BookerLayouts.COLUMN_VIEW; const containerRef = useRef(null); const onTimeSelect = ( @@ -57,29 +65,19 @@ export const AvailableTimeSlots = ({ extraDays, limitHeight, seatsPerTimeSlot }: }; const schedule = useScheduleForEvent({ - prefetchNextMonth: !!extraDays && dayjs(date).month() !== dayjs(date).add(extraDays, "day").month(), + prefetchNextMonth, + monthCount, }); + const nonEmptyScheduleDays = useNonEmptyScheduleDays(schedule?.data?.slots) + const nonEmptyScheduleDaysFromSelectedDate = nonEmptyScheduleDays.filter((slot)=>dayjs(selectedDate).diff(slot,'day')<=0); // Creates an array of dates to fetch slots for. // If `extraDays` is passed in, we will extend the array with the next `extraDays` days. - const dates = useMemo( - () => - !extraDays - ? [date] - : [ - // If NO date is selected yet, we show by default the upcomming `nextDays` days. - date, - ...Array.from({ length: extraDays }).map((_, index) => - dayjs(date) - .add(index + 1, "day") - .format("YYYY-MM-DD") - ), - ], - [date, extraDays] - ); - - const isMultipleDates = dates.length > 1; - const slotsPerDay = useSlotsForMultipleDates(dates, schedule?.data?.slots); + const dates = !extraDays + ? [date]: nonEmptyScheduleDaysFromSelectedDate.length > 0 + ? nonEmptyScheduleDaysFromSelectedDate.slice(0, extraDays):[]; + + const slotsPerDay = useSlotsForAvailableDates(dates, schedule?.data?.slots); useEffect(() => { if (isEmbed) return; @@ -103,13 +101,15 @@ export const AvailableTimeSlots = ({ extraDays, limitHeight, seatsPerTimeSlot }: ))} ); }; + diff --git a/packages/features/bookings/Booker/components/Header.tsx b/packages/features/bookings/Booker/components/Header.tsx index f7613a470f..5d65575129 100644 --- a/packages/features/bookings/Booker/components/Header.tsx +++ b/packages/features/bookings/Booker/components/Header.tsx @@ -16,10 +16,12 @@ export function Header({ extraDays, isMobile, enabledLayouts, + nextSlots, }: { extraDays: number; isMobile: boolean; enabledLayouts: BookerLayouts[]; + nextSlots: number; }) { const { t, i18n } = useLocale(); const [layout, setLayout] = useBookerStore((state) => [state.layout, state.setLayout], shallow); @@ -56,8 +58,7 @@ export function Header({ if (isMonthView) { return ; } - - const endDate = selectedDate.add(extraDays - 1, "days"); + const endDate = selectedDate.add(layout === BookerLayouts.COLUMN_VIEW ? extraDays : extraDays - 1, "days"); const isSameMonth = () => { return selectedDate.format("MMM") === endDate.format("MMM"); @@ -91,7 +92,7 @@ export function Header({ color="minimal" StartIcon={ChevronLeft} aria-label="Previous Day" - onClick={() => addToSelectedDate(-extraDays)} + onClick={() => addToSelectedDate(layout === BookerLayouts.COLUMN_VIEW ? -nextSlots : -extraDays)} />