// We do not need to worry about importing framer-motion here as it is lazy imported in Booker.
import * as HoverCard from "@radix-ui/react-hover-card";
import { AnimatePresence, m } from "framer-motion";
import { CalendarX2, ChevronRight } from "lucide-react";
import { useCallback, useState } from "react";
import dayjs from "@calcom/dayjs";
import type { Slots } from "@calcom/features/schedules";
import { classNames } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button, SkeletonText } from "@calcom/ui";
import { useBookerStore } from "../Booker/store";
import { useEvent } from "../Booker/utils/event";
import { getQueryParam } from "../Booker/utils/query-param";
import { useTimePreferences } from "../lib";
import { useCheckOverlapWithOverlay } from "../lib/useCheckOverlapWithOverlay";
import { SeatsAvailabilityText } from "./SeatsAvailabilityText";
type TOnTimeSelect = (
time: string,
attendees: number,
seatsPerTimeSlot?: number | null,
bookingUid?: string
) => void;
type AvailableTimesProps = {
slots: Slots[string];
onTimeSelect: TOnTimeSelect;
seatsPerTimeSlot?: number | null;
showAvailableSeatsCount?: boolean | null;
showTimeFormatToggle?: boolean;
className?: string;
selectedSlots?: string[];
};
const SlotItem = ({
slot,
seatsPerTimeSlot,
selectedSlots,
onTimeSelect,
showAvailableSeatsCount,
}: {
slot: Slots[string][number];
seatsPerTimeSlot?: number | null;
selectedSlots?: string[];
onTimeSelect: TOnTimeSelect;
showAvailableSeatsCount?: boolean | null;
}) => {
const { t } = useLocale();
const overlayCalendarToggled =
getQueryParam("overlayCalendar") === "true" || localStorage.getItem("overlayCalendarSwitchDefault");
const [timeFormat, timezone] = useTimePreferences((state) => [state.timeFormat, state.timezone]);
const bookingData = useBookerStore((state) => state.bookingData);
const layout = useBookerStore((state) => state.layout);
const { data: event } = useEvent();
const hasTimeSlots = !!seatsPerTimeSlot;
const computedDateWithUsersTimezone = dayjs.utc(slot.time).tz(timezone);
const bookingFull = !!(hasTimeSlots && slot.attendees && slot.attendees >= seatsPerTimeSlot);
const isHalfFull = slot.attendees && seatsPerTimeSlot && slot.attendees / seatsPerTimeSlot >= 0.5;
const isNearlyFull = slot.attendees && seatsPerTimeSlot && slot.attendees / seatsPerTimeSlot >= 0.83;
const colorClass = isNearlyFull ? "bg-rose-600" : isHalfFull ? "bg-yellow-500" : "bg-emerald-400";
const nowDate = dayjs();
const usersTimezoneDate = nowDate.tz(timezone);
const offset = (usersTimezoneDate.utcOffset() - nowDate.utcOffset()) / 60;
const { isOverlapping, overlappingTimeEnd, overlappingTimeStart } = useCheckOverlapWithOverlay({
start: computedDateWithUsersTimezone,
selectedDuration: event?.length ?? 0,
offset,
});
const [overlapConfirm, setOverlapConfirm] = useState(false);
const onButtonClick = useCallback(() => {
if (!overlayCalendarToggled) {
onTimeSelect(slot.time, slot?.attendees || 0, seatsPerTimeSlot, slot.bookingUid);
return;
}
if (isOverlapping && overlapConfirm) {
setOverlapConfirm(false);
return;
}
if (isOverlapping && !overlapConfirm) {
setOverlapConfirm(true);
return;
}
if (!overlapConfirm) {
onTimeSelect(slot.time, slot?.attendees || 0, seatsPerTimeSlot, slot.bookingUid);
}
}, [
overlayCalendarToggled,
isOverlapping,
overlapConfirm,
onTimeSelect,
slot.time,
slot?.attendees,
slot.bookingUid,
seatsPerTimeSlot,
]);
return (
Busy
{overlappingTimeStart} - {overlappingTimeEnd}
{t("all_booked_today")}