Files
calendar/apps/web/modules/bookings/components/BookingCalendarView.tsx
T
Rajiv SahalGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
38b43f75ba refactor: remove circular dependencies from CalendarView atom (#27850)
* fix: decouple large calendar from features

* fix: move calendar to features since this is the better approach

* chore: move OOO slots to features

* feat: add DefaultOutOfOfficeSlot fallback for calendar OOO rendering

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* chore: cleanup unused LargeCalendar component

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-02-17 08:14:33 +00:00

108 lines
3.8 KiB
TypeScript

"use client";
import dayjs from "@calcom/dayjs";
import { useTimePreferences } from "@calcom/features/bookings/lib";
import type { CalendarEvent } from "@calcom/features/calendars/weeklyview/types/events";
import { useGetTheme } from "@calcom/lib/hooks/useTheme";
import { Calendar } from "@calcom/features/calendars/weeklyview/components/Calendar";
import { useBanners } from "@calcom/web/modules/shell/banners/useBanners";
import { useEffect, useMemo } from "react";
import { useBookingDetailsSheetStore } from "../store/bookingDetailsSheetStore";
import type { BookingOutput } from "../types";
type BookingCalendarViewProps = {
bookings: BookingOutput[];
currentWeekStart: dayjs.Dayjs;
onWeekStartChange: (weekStart: dayjs.Dayjs) => void;
};
export function BookingCalendarView({
bookings,
currentWeekStart,
onWeekStartChange,
}: BookingCalendarViewProps) {
const setSelectedBookingUid = useBookingDetailsSheetStore((state) => state.setSelectedBookingUid);
const selectedBookingUid = useBookingDetailsSheetStore((state) => state.selectedBookingUid);
const { timezone } = useTimePreferences();
const { resolvedTheme, forcedTheme } = useGetTheme();
const { bannersHeight } = useBanners();
const startDate = useMemo(() => currentWeekStart.toDate(), [currentWeekStart]);
const endDate = useMemo(() => currentWeekStart.add(6, "day").toDate(), [currentWeekStart]);
// Intentionally only runs on mount to trigger the initial currentWeekStart
useEffect(() => {
onWeekStartChange(currentWeekStart);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const events = useMemo<CalendarEvent[]>(() => {
const hasDarkTheme = !forcedTheme && resolvedTheme === "dark";
return bookings
.filter((booking) => {
const bookingStart = dayjs(booking.startTime);
return (
(bookingStart.isAfter(currentWeekStart) || bookingStart.isSame(currentWeekStart)) &&
bookingStart.isBefore(currentWeekStart.add(7, "day"))
);
})
.sort((a, b) => {
const startDiff = new Date(a.startTime).getTime() - new Date(b.startTime).getTime();
if (startDiff !== 0) return startDiff;
return new Date(a.endTime).getTime() - new Date(b.endTime).getTime();
})
.map((booking, idx) => {
// Parse eventTypeColor and extract the appropriate color based on theme
const eventTypeColor =
booking.eventType?.eventTypeColor &&
booking.eventType.eventTypeColor[hasDarkTheme ? "darkEventTypeColor" : "lightEventTypeColor"];
return {
id: idx,
title: booking.title,
start: new Date(booking.startTime),
end: new Date(booking.endTime),
options: {
status: booking.status,
...(eventTypeColor && { color: eventTypeColor }),
bookingUid: booking.uid,
},
};
});
}, [bookings, currentWeekStart, resolvedTheme, forcedTheme]);
return (
<>
<div
className="border-subtle flex flex-1 flex-col overflow-y-auto overflow-x-hidden rounded-2xl border"
style={{ height: `calc(100vh - 6rem - ${bannersHeight}px)` }}>
<Calendar
timezone={timezone}
sortEvents
startHour={0}
endHour={23}
events={events}
startDate={startDate}
endDate={endDate}
gridCellsPerHour={4}
hoverEventDuration={0}
showBackgroundPattern={false}
showBorder={false}
borderColor="subtle"
selectedBookingUid={selectedBookingUid}
onEventClick={(event) => {
const bookingUid = event.options?.bookingUid;
if (bookingUid) {
setSelectedBookingUid(bookingUid);
}
}}
showTimezone
hideHeader
updateCurrentTimeOnFocus
/>
</div>
</>
);
}