From cf89598279a2a257547f6e1d4192e35eba08f3de Mon Sep 17 00:00:00 2001 From: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Date: Wed, 9 Aug 2023 15:49:01 +0530 Subject: [PATCH] fix: Calendar View (#10514) Co-authored-by: Sean Brydon --- .../bookings/Booker/components/Header.tsx | 2 +- .../Booker/components/LargeCalendar.tsx | 21 ++++++++------- .../weeklyview/components/Calendar.tsx | 13 +++++++--- .../components/currentTime/index.tsx | 22 +++++++++++----- .../weeklyview/components/event/Empty.tsx | 26 ++++++++++++------- .../components/horizontalLines/index.tsx | 1 + .../calendars/weeklyview/types/state.ts | 1 + .../calendars/weeklyview/utils/index.ts | 25 ++++++++++++------ 8 files changed, 72 insertions(+), 39 deletions(-) diff --git a/packages/features/bookings/Booker/components/Header.tsx b/packages/features/bookings/Booker/components/Header.tsx index 9ac23eff02..4220c43060 100644 --- a/packages/features/bookings/Booker/components/Header.tsx +++ b/packages/features/bookings/Booker/components/Header.tsx @@ -57,7 +57,7 @@ export function Header({ return ; } - const endDate = selectedDate.add(extraDays, "days"); + const endDate = selectedDate.add(extraDays - 1, "days"); const isSameMonth = () => { return selectedDate.format("MMM") === endDate.format("MMM"); diff --git a/packages/features/bookings/Booker/components/LargeCalendar.tsx b/packages/features/bookings/Booker/components/LargeCalendar.tsx index b312f22821..021f53180c 100644 --- a/packages/features/bookings/Booker/components/LargeCalendar.tsx +++ b/packages/features/bookings/Booker/components/LargeCalendar.tsx @@ -4,7 +4,6 @@ import dayjs from "@calcom/dayjs"; import { Calendar } from "@calcom/features/calendars/weeklyview"; import type { CalendarAvailableTimeslots } from "@calcom/features/calendars/weeklyview/types/state"; -import { useTimePreferences } from "../../lib/timePreferences"; import { useBookerStore } from "../store"; import { useEvent, useScheduleForEvent } from "../utils/event"; @@ -16,7 +15,6 @@ export const LargeCalendar = ({ extraDays }: { extraDays: number }) => { const schedule = useScheduleForEvent({ prefetchNextMonth: !!extraDays && dayjs(date).month() !== dayjs(date).add(extraDays, "day").month(), }); - const { timezone } = useTimePreferences(); const event = useEvent(); const eventDuration = selectedEventDuration || event?.data?.length || 30; @@ -28,15 +26,18 @@ export const LargeCalendar = ({ extraDays }: { extraDays: number }) => { for (const day in schedule.data.slots) { availableTimeslots[day] = schedule.data.slots[day].map((slot) => ({ - // First formatting to LLL and then passing it to date prevents toDate() - // from changing the timezone to users local machine (instead of itmezone selected in UI dropdown) - start: new Date(dayjs(slot.time).utc().tz(timezone).format("LLL")), - end: new Date(dayjs(slot.time).utc().tz(timezone).add(eventDuration, "minutes").format("LLL")), + start: dayjs(slot.time).toDate(), + end: dayjs(slot.time).add(eventDuration, "minutes").toDate(), })); } return availableTimeslots; - }, [schedule, timezone, eventDuration]); + }, [schedule, eventDuration]); + + const startDate = selectedDate ? dayjs(selectedDate).toDate() : dayjs().toDate(); + const endDate = dayjs(startDate) + .add(extraDays - 1, "day") + .toDate(); return (
@@ -46,9 +47,9 @@ export const LargeCalendar = ({ extraDays }: { extraDays: number }) => { startHour={0} endHour={23} events={[]} - startDate={selectedDate ? new Date(selectedDate) : new Date()} - endDate={dayjs(selectedDate).add(extraDays, "day").toDate()} - onEmptyCellClick={(date) => setSelectedTimeslot(date.toString())} + startDate={startDate} + endDate={endDate} + onEmptyCellClick={(date) => setSelectedTimeslot(date.toISOString())} gridCellsPerHour={60 / eventDuration} hoverEventDuration={eventDuration} hideHeader diff --git a/packages/features/calendars/weeklyview/components/Calendar.tsx b/packages/features/calendars/weeklyview/components/Calendar.tsx index 72ee40d20f..ba33adc2a6 100644 --- a/packages/features/calendars/weeklyview/components/Calendar.tsx +++ b/packages/features/calendars/weeklyview/components/Calendar.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useMemo, useRef } from "react"; +import { useTimePreferences } from "@calcom/features/bookings/lib/timePreferences"; import { classNames } from "@calcom/lib"; import { useCalendarStore } from "../state/store"; @@ -22,6 +23,7 @@ export function Calendar(props: CalendarComponentProps) { const containerOffset = useRef(null); const schedulerGrid = useRef(null); const initalState = useCalendarStore((state) => state.initState); + const { timezone } = useTimePreferences(); const startDate = useCalendarStore((state) => state.startDate); const endDate = useCalendarStore((state) => state.endDate); @@ -32,7 +34,11 @@ export function Calendar(props: CalendarComponentProps) { const hideHeader = useCalendarStore((state) => state.hideHeader); const days = useMemo(() => getDaysBetweenDates(startDate, endDate), [startDate, endDate]); - const hours = useMemo(() => getHoursToDisplay(startHour || 0, endHour || 23), [startHour, endHour]); + + const hours = useMemo( + () => getHoursToDisplay(startHour || 0, endHour || 23, timezone), + [startHour, endHour, timezone] + ); const numberOfGridStopsPerDay = hours.length * usersCellsStopsPerHour; const hourSize = 58; @@ -108,7 +114,7 @@ export function Calendar(props: CalendarComponentProps) { {availableTimeslots ? ( @@ -119,11 +125,12 @@ export function Calendar(props: CalendarComponentProps) { return ( ); })} diff --git a/packages/features/calendars/weeklyview/components/currentTime/index.tsx b/packages/features/calendars/weeklyview/components/currentTime/index.tsx index e47a531be0..2840031461 100644 --- a/packages/features/calendars/weeklyview/components/currentTime/index.tsx +++ b/packages/features/calendars/weeklyview/components/currentTime/index.tsx @@ -5,6 +5,12 @@ import { useTimePreferences } from "@calcom/features/bookings/lib"; import { useCalendarStore } from "../../state/store"; +function calculateMinutesFromStart(startHour: number, currentHour: number, currentMinute: number) { + const startMinute = startHour * 60; + const currentMinuteOfDay = currentHour * 60 + currentMinute; + return currentMinuteOfDay - startMinute; +} + export function CurrentTime() { const currentTimeRef = useRef(null); const [scrolledIntoView, setScrolledIntoView] = useState(false); @@ -13,19 +19,21 @@ export function CurrentTime() { startHour: state.startHour || 0, endHour: state.endHour || 23, })); - const { timeFormat } = useTimePreferences(); + const { timeFormat, timezone } = useTimePreferences(); useEffect(() => { // Set the container scroll position based on the current time. - const currentHour = new Date().getHours(); - let currentMinute = new Date().getHours() * 60; - currentMinute = currentMinute + new Date().getMinutes(); + + const currentDateTime = dayjs().tz(timezone); // Get current date and time in the specified timezone + + const currentHour = currentDateTime.hour(); + const currentMinute = currentDateTime.minute(); if (currentHour > endHour || currentHour < startHour) { setCurrentTimePos(null); } - const minutesFromStart = currentMinute - startHour * 60; + const minutesFromStart = calculateMinutesFromStart(startHour, currentHour, currentMinute); setCurrentTimePos(minutesFromStart); if (!currentTimeRef.current || scrolledIntoView) return; @@ -35,7 +43,7 @@ export function CurrentTime() { setScrolledIntoView(true); }, 100); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [startHour, endHour, scrolledIntoView]); + }, [startHour, endHour, scrolledIntoView, timezone]); return (
-
{dayjs().format(timeFormat)}
+
{dayjs().tz(timezone).format(timeFormat)}
diff --git a/packages/features/calendars/weeklyview/components/event/Empty.tsx b/packages/features/calendars/weeklyview/components/event/Empty.tsx index bdf4d4ee76..c80341e0ea 100644 --- a/packages/features/calendars/weeklyview/components/event/Empty.tsx +++ b/packages/features/calendars/weeklyview/components/event/Empty.tsx @@ -23,12 +23,12 @@ export function EmptyCell(props: EmptyCellProps) { totalGridCells: props.totalGridCells, selectionLength: props.selectionLength, startHour: props.startHour, + timezone: props.timezone, }); - const minuesFromStart = - (cellToDate.toDate().getHours() - props.startHour) * 60 + cellToDate.toDate().getMinutes(); + const minuesFromStart = (cellToDate.hour() - props.startHour) * 60 + cellToDate.minute(); - return ; + return ; } type AvailableCellProps = { @@ -38,26 +38,29 @@ type AvailableCellProps = { }; export function AvailableCellsForDay({ availableSlots, day, startHour }: AvailableCellProps) { + const { timezone } = useTimePreferences(); + const slotsForToday = availableSlots && availableSlots[dayjs(day).format("YYYY-MM-DD")]; const slots = useMemo( () => slotsForToday?.map((slot) => ({ slot, - topOffsetMinutes: (slot.start.getHours() - startHour) * 60 + slot.start.getMinutes(), + topOffsetMinutes: + (dayjs(slot.start).tz(timezone).hour() - startHour) * 60 + dayjs(slot.start).tz(timezone).minute(), })), - [slotsForToday, startHour] + [slotsForToday, startHour, timezone] ); if (!availableSlots) return null; return ( <> - {slots?.map((slot) => { + {slots?.map((slot, index) => { return ( ); @@ -73,7 +76,8 @@ type CellProps = { }; function Cell({ isDisabled, topOffsetMinutes, timeSlot }: CellProps) { - const timeFormat = useTimePreferences((state) => state.timeFormat); + const { timeFormat } = useTimePreferences(); + const { onEmptyCellClick, hoverEventDuration } = useCalendarStore( (state) => ({ onEmptyCellClick: state.onEmptyCellClick, @@ -97,7 +101,9 @@ function Cell({ isDisabled, topOffsetMinutes, timeSlot }: CellProps) { overflow: "visible", top: topOffsetMinutes ? `calc(${topOffsetMinutes}*var(--one-minute-height))` : undefined, }} - onClick={() => onEmptyCellClick && onEmptyCellClick(timeSlot.toDate())}> + onClick={() => { + onEmptyCellClick && onEmptyCellClick(timeSlot.toDate()); + }}> {!isDisabled && hoverEventDuration !== 0 && (