refactor: make timezone a required prop in Calendar component (#24567)

* refactor: make timezone a required prop in Calendar component

- Remove useBookerTime hook from Calendar.tsx
- Add timezone as required prop in CalendarState type
- Update all parent components to pass timezone prop:
  - packages/features/bookings/Booker/components/LargeCalendar.tsx
  - packages/features/calendar-view/LargeCalendar.tsx
  - packages/features/troubleshooter/components/LargeCalendar.tsx
- All parent components now use useTimePreferences hook to get timezone

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

* use right implementation

* remove a line

* use user's timezone instead of booker timezone

* get timezone from calendar store

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Eunjae Lee
2025-10-20 13:59:59 +00:00
committed by GitHub
co-authored by Devin AI
parent 68f9e86cdd
commit c289fac008
6 changed files with 16 additions and 5 deletions
@@ -3,6 +3,7 @@ import { useMemo, useEffect } from "react";
import dayjs from "@calcom/dayjs";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/components/hooks/useAvailableTimeSlots";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { Calendar } from "@calcom/features/calendars/weeklyview";
import type { CalendarEvent } from "@calcom/features/calendars/weeklyview/types/events";
@@ -31,6 +32,7 @@ export const LargeCalendar = ({
const overlayEvents = useOverlayCalendarStore((state) => state.overlayBusyDates);
const displayOverlay =
getQueryParam("overlayCalendar") === "true" || localStorage?.getItem("overlayCalendarSwitchDefault");
const { timezone } = useBookerTime();
const eventDuration = selectedEventDuration || event?.data?.length || 30;
@@ -43,7 +45,6 @@ export const LargeCalendar = ({
// HACK: force rerender when overlay events change
// Sine we dont use react router here we need to force rerender (ATOM SUPPORT)
// eslint-disable-next-line @typescript-eslint/no-empty-function
useEffect(() => {}, [displayOverlay]);
const overlayEventsForDate = useMemo(() => {
@@ -75,6 +76,7 @@ export const LargeCalendar = ({
gridCellsPerHour={60 / eventDuration}
hoverEventDuration={eventDuration}
hideHeader
timezone={timezone}
/>
</div>
);
@@ -3,6 +3,7 @@ import { useEffect, useMemo } from "react";
import dayjs from "@calcom/dayjs";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/components/hooks/useAvailableTimeSlots";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { Calendar } from "@calcom/features/calendars/weeklyview";
import { localStorage } from "@calcom/lib/webstorage";
@@ -29,6 +30,7 @@ export const LargeCalendar = ({
const selectedEventDuration = useBookerStoreContext((state) => state.selectedDuration);
const displayOverlay =
getQueryParam("overlayCalendar") === "true" || localStorage?.getItem("overlayCalendarSwitchDefault");
const { timezone } = useBookerTime();
const eventDuration = selectedEventDuration || event?.data?.length || 30;
@@ -85,6 +87,7 @@ export const LargeCalendar = ({
gridCellsPerHour={60 / eventDuration}
hoverEventDuration={eventDuration}
hideHeader
timezone={timezone}
/>
</div>
);
@@ -2,7 +2,6 @@ import React, { useEffect, useMemo, useRef } from "react";
import classNames from "@calcom/ui/classNames";
import { useBookerTime } from "../../../bookings/Booker/components/hooks/useBookerTime";
import { useCalendarStore } from "../state/store";
import "../styles/styles.css";
import type { CalendarComponentProps } from "../types/state";
@@ -23,7 +22,6 @@ export function Calendar(props: CalendarComponentProps) {
const containerOffset = useRef<HTMLDivElement | null>(null);
const schedulerGrid = useRef<HTMLOListElement | null>(null);
const initialState = useCalendarStore((state) => state.initState);
const { timezone } = useBookerTime();
const startDate = useCalendarStore((state) => state.startDate);
const endDate = useCalendarStore((state) => state.endDate);
@@ -32,6 +30,7 @@ export function Calendar(props: CalendarComponentProps) {
const usersCellsStopsPerHour = useCalendarStore((state) => state.gridCellsPerHour || 4);
const availableTimeslots = useCalendarStore((state) => state.availableTimeslots);
const hideHeader = useCalendarStore((state) => state.hideHeader);
const timezone = useCalendarStore((state) => state.timezone);
const days = useMemo(() => getDaysBetweenDates(startDate, endDate), [startDate, endDate]);
@@ -1,6 +1,7 @@
import { create } from "zustand";
import dayjs from "@calcom/dayjs";
import { CURRENT_TIMEZONE } from "@calcom/lib/timezoneConstants";
import type {
CalendarComponentProps,
@@ -18,6 +19,7 @@ const defaultState: CalendarComponentProps = {
startHour: 0,
endHour: 23,
gridCellsPerHour: 4,
timezone: CURRENT_TIMEZONE,
};
export const useCalendarStore = create<CalendarStoreProps>((set) => ({
@@ -62,7 +64,7 @@ export const useCalendarStore = create<CalendarStoreProps>((set) => ({
}
// We call this callback if we have it -> Allows you to change your state outside of the component
state.onDateChange && state.onDateChange(newStartDate, newEndDate);
state.onDateChange?.(newStartDate, newEndDate);
return {
startDate: newStartDate,
endDate: newEndDate,
@@ -70,7 +72,7 @@ export const useCalendarStore = create<CalendarStoreProps>((set) => ({
}
const newStartDate = dayjs(startDate).subtract(1, state.view).toDate();
const newEndDate = dayjs(endDate).subtract(1, state.view).toDate();
state.onDateChange && state.onDateChange(newStartDate, newEndDate);
state.onDateChange?.(newStartDate, newEndDate);
return {
startDate: newStartDate,
endDate: newEndDate,
@@ -127,6 +127,10 @@ export type CalendarState = {
* Optional boolean to hide the main header. Default the header will be visible.
*/
hideHeader?: boolean;
/**
* Timezone to use for displaying times in the calendar
*/
timezone: string;
};
export type CalendarComponentProps = CalendarPublicActions & CalendarState & { isPending?: boolean };
@@ -122,6 +122,7 @@ export const LargeCalendar = ({ extraDays }: { extraDays: number }) => {
gridCellsPerHour={60 / (event?.duration || 15)}
hoverEventDuration={30}
hideHeader
timezone={timezone}
/>
</div>
);