Files
calendar/packages/features/calendars/components/NoAvailabilityDialog.tsx
T
Benny JooGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
c7ee3984ef refactor: move feature-flags, calendars, and schedules components from packages/features to apps/web/modules (#27222)
* refactor: move feature-flags, calendars, and schedules components from packages/features to apps/web/modules

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix

* fix

* fix

* fix: move DatePicker and DateOverrideInputDialog to apps/web/modules and update imports

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* rename

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* wip

* wip

* wip

* fix unit tests

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-29 16:41:44 +02:00

105 lines
3.4 KiB
TypeScript

import { useState } from "react";
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { calculatePeriodLimits, isTimeViolatingFutureLimit } from "@calcom/lib/isOutOfBounds";
import type { PeriodData } from "@calcom/types/Event";
import { Button } from "@calcom/ui/components/button";
import { Dialog, DialogContent, DialogFooter, DialogClose } from "@calcom/ui/components/dialog";
// Determines if the next month will pass the booking limits for 'ROLLING' and 'RANGE' period types
const useNoFutureAvailability = (browsingDate: Dayjs, periodData: PeriodData) => {
const { periodDays, periodType, periodCountCalendarDays, periodEndDate, periodStartDate } = periodData;
const periodLimits = calculatePeriodLimits({
periodType,
periodDays,
periodCountCalendarDays,
periodStartDate,
periodEndDate,
allDatesWithBookabilityStatusInBookerTz: null, // Temporary workaround
_skipRollingWindowCheck: true,
eventUtcOffset: 0,
bookerUtcOffset: 0,
});
// Check if limit is before start of next month
const isOutOfBoundsByPeriod = isTimeViolatingFutureLimit({
time: browsingDate.add(1, "month").startOf("month").toDate(),
periodLimits,
});
return isOutOfBoundsByPeriod;
};
// Creates message to use in dialog explaining lack of availability based on period type
const useDescription = (noFutureAvailability: boolean, p: PeriodData) => {
const { t } = useLocale();
if (!noFutureAvailability) return "";
if (p.periodType === "ROLLING") {
const daysDescription = p.periodCountCalendarDays ? t("calendar_days") : t("business_days");
return t("no_availability_rolling", { days: `${p.periodDays} ${daysDescription}` });
}
if (p.periodType === "RANGE") {
return t("no_availability_range", { date: dayjs(p.periodEndDate).format("MMMM D YYYY") });
}
return "";
};
// This component is used to show a dialog when there is no availability in the selected month.
const NoAvailabilityDialog = ({
month,
nextMonthButton,
browsingDate,
periodData,
}: {
month: string | null;
nextMonthButton: () => void;
browsingDate: Dayjs;
periodData: PeriodData;
}) => {
const { t } = useLocale();
const [isOpenDialog, setIsOpenDialog] = useState(true);
const noFutureAvailability = useNoFutureAvailability(browsingDate, periodData);
const description = useDescription(noFutureAvailability, periodData);
const closeDialog = () => {
setIsOpenDialog(false);
};
return (
<Dialog
open={isOpenDialog}
onOpenChange={(open) => {
setIsOpenDialog(open);
}}>
<DialogContent
title={t("no_availability_in_month", { month: month })}
type="creation"
description={description}
preventCloseOnOutsideClick={false}>
<DialogFooter>
<DialogClose
color={noFutureAvailability ? "primary" : "secondary"}
onClick={closeDialog}
data-testid="close_dialog_button">
{t("close")}
</DialogClose>
{!noFutureAvailability && (
<Button
color="primary"
onClick={nextMonthButton}
data-testid="view_next_month"
EndIcon="arrow-right">
{t("view_next_month")}
</Button>
)}
</DialogFooter>
</DialogContent>
</Dialog>
);
};
export default NoAvailabilityDialog;