import { useMemo } from "react";
import { shallow } from "zustand/shallow";
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { classNames } from "@calcom/lib";
import { OutOfOfficeInSlots } from "../../../../bookings/Booker/components/OutOfOfficeInSlots";
import { useBookerTime } from "../../../../bookings/Booker/components/hooks/useBookerTime";
import { useCalendarStore } from "../../state/store";
import type { CalendarAvailableTimeslots } from "../../types/state";
import type { GridCellToDateProps } from "../../utils";
import { gridCellToDateTime } from "../../utils";
type EmptyCellProps = GridCellToDateProps & {
isDisabled?: boolean;
topOffsetMinutes?: number;
};
export function EmptyCell(props: EmptyCellProps) {
const cellToDate = gridCellToDateTime({
day: props.day,
gridCellIdx: props.gridCellIdx,
totalGridCells: props.totalGridCells,
selectionLength: props.selectionLength,
startHour: props.startHour,
timezone: props.timezone,
});
const minutesFromStart = (cellToDate.hour() - props.startHour) * 60 + cellToDate.minute();
return | ;
}
type AvailableCellProps = {
availableSlots: CalendarAvailableTimeslots;
day: GridCellToDateProps["day"];
startHour: GridCellToDateProps["startHour"];
};
export function AvailableCellsForDay({ availableSlots, day, startHour }: AvailableCellProps) {
const { timezone } = useBookerTime();
const date = dayjs(day);
const dateFormatted = date.format("YYYY-MM-DD");
const slotsForToday = availableSlots && availableSlots[dateFormatted];
const slots = useMemo(() => {
const calculatedSlots: {
slot: CalendarAvailableTimeslots[string][number];
topOffsetMinutes: number;
firstSlot?: CalendarAvailableTimeslots[string][number];
timezone?: string;
}[] = [];
// first and last slot for ooo to display range correctly in week view
let firstSlotIndex = -1;
let lastSlotIndex = -1;
let areAllSlotsAway = true;
let startEndTimeDuration = 0;
slotsForToday?.forEach((slot, index) => {
const startTime = dayjs(slot.start).tz(timezone);
const topOffsetMinutes = (startTime.hour() - startHour) * 60 + startTime.minute();
calculatedSlots.push({ slot, topOffsetMinutes });
if (!slot.away) {
areAllSlotsAway = false;
} else {
if (firstSlotIndex === -1) {
firstSlotIndex = index;
}
lastSlotIndex = index;
}
});
if (areAllSlotsAway && firstSlotIndex !== -1) {
const firstSlot = slotsForToday[firstSlotIndex];
const lastSlot = slotsForToday[lastSlotIndex];
startEndTimeDuration = dayjs(lastSlot.end).diff(dayjs(firstSlot.start), "minutes");
if (firstSlot.toUser == null) {
return null;
}
// This will return null if all slots are away and the first slot has no user
return {
slots: calculatedSlots,
startEndTimeDuration,
firstSlot,
timezone,
};
}
return { slots: calculatedSlots, startEndTimeDuration };
}, [slotsForToday, startHour, timezone]);
if (slots === null) return null;
if (slots.startEndTimeDuration) {
const { firstSlot, startEndTimeDuration } = slots;
return (
);
}
return (
<>
{slots.slots.map((slot, index) => (
|
))}
>
);
}
type CellProps = {
isDisabled?: boolean;
topOffsetMinutes?: number;
timeSlot: Dayjs;
};
function Cell({ isDisabled, topOffsetMinutes, timeSlot }: CellProps) {
const { timeFormat } = useBookerTime();
const { onEmptyCellClick, hoverEventDuration } = useCalendarStore(
(state) => ({
onEmptyCellClick: state.onEmptyCellClick,
hoverEventDuration: state.hoverEventDuration,
}),
shallow
);
return (
{
onEmptyCellClick && onEmptyCellClick(timeSlot.toDate());
}}>
{!isDisabled && hoverEventDuration !== 0 && (
15 && "items-start pt-3",
hoverEventDuration && hoverEventDuration < 15 && "items-center"
)}
style={{
height: `calc(${hoverEventDuration}*var(--one-minute-height) - 2px)`,
zIndex: 80,
// @TODO: This used to be 90% as per Sean's work. I think this was needed when
// multiple events are stacked next to each other. We might need to add this back later.
width: "calc(100% - 2px)",
}}>
{timeSlot.format(timeFormat)}
)}
);
}
function CustomCell({
timeSlot,
children,
topOffsetMinutes,
startEndTimeDuration,
}: CellProps & { children: React.ReactNode; startEndTimeDuration?: number }) {
return (
);
}