Repository: calcom/cal.com
Pull request #20451: fix: Improves dialog window that appears when there is no availability in a month. Adding a description to clarify limits when rolling or range period types are in use.
Author: login: shawnCaza
Labels: name: ✅ good first issue
description: Good for newcomers
name: 🧹 Improvements
description: Improvements to existing features. Mostly UX/UI
name: Low priority
description: Created by Linear-GitHub Sync
name: bookings
description: area: bookings, availability, timezones, double booking
name: i18n
description: area: i18n, translations
name: ui
description: area: UI, frontend, button, form, input
name: community
description: Created by Linear-GitHub Sync
name: ready-for-e2e
Created: 2025-03-30T20:50:51Z | Merged: 2025-04-15T18:16:01Z

## What does this PR do?

<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->

- Fixes #11321 
- Adds a description to the dialog window that appears when their is no availability in a month if it can be determined there will not be any availability in future months.
- This makes it clearer for users that new availability may open up, what conditions might be limiting them from booking in the current or future months, and avoids them needlessly searching through future months when no further options are available.
- Cal.com's standard dialog ui components were implemented to conform to Cal.com's design system and figma files in favour of the adhoc dialog currently in place.
- The heading tag in the DialogHeader component was changed from an `h3` to an `h2`. From my research there appears to be differences of opinion as to when / if `h1` or `h2` is the appropriate heading tag for a dialog. [There are compelling arguments made here that an `h1` would be better](https://github.com/mui/material-ui/issues/34250). I'm not aware of any reason why `h3` would make sense in this context.
- The 'View Next Month' button is no longer displayed when it can be determined that there is no availability in future months.
- A 'Close' button was added in all scenarios so that users can close the dialog when there are no other options or if they don't want to keep moving to future months.
- The new dialog was refactored into it's own file due to the growing length of calendars/DatePicker.tsx and to facilitate testing.
- The existing `calculatePeriodLimits` and `isTimeViolatingFutureLimit` functions were used to calculate availability of booking in future months. 
- To perform those calculations period data properties were added to `getPublicEventSelect`. The `PeriodData' type was created as a sub-type of Events to pass along PeriodData to the NoAvailabilityDialog component.
- Localized text was added to describe situations of no future availability for ROLLING and RANGE period types. LLM generated translations were used to create translations from English for most other languages.


## Visual Demo

#### Image Demo (if applicable):
![no availability before and after](https://github.com/user-attachments/assets/25679528-c37f-4424-9979-90f1b0a043dd)

Message when there is no availability in a month and the next month is outside of the period days of an event with a ROLLING period:
![Screen Shot 2025-03-30 at 2 15 44 PM](https://github.com/user-attachments/assets/75596fa0-4d28-4676-818b-5cade300d0a6)

Message when there is no availability in a month and the next month is past the end date of an a event using a RANGE period type:
![Screen Shot 2025-03-30 at 2 17 26 PM](https://github.com/user-attachments/assets/65526c76-6183-46f0-98f9-606e213f4521)

Dialog when there could be availability in future months:
![Screen Shot 2025-03-30 at 2 19 22 PM](https://github.com/user-attachments/assets/e18d3e1e-82a0-4f57-a2df-db3019f27b73)

Dialog Heading changed to an H2:
 
![Screen Shot 2025-03-30 at 2 20 49 PM](https://github.com/user-attachments/assets/c5027c10-c420-40ff-aff3-fbe5473641cf)


## Mandatory Tasks (DO NOT REMOVE)

- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] N/A  
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.

## How should this be tested?

Tests were created in NoAvailabilityDialog.test.tsx and can be run using `yarn test packages/features/calendars/NoAvailabilityDialog.test.tsx`

✓ Displays rolling period description and close button, when period type is ROLLING, and period ends before next month.
✓ Displays range period description when period type is RANGE and range ends before the next month
✓ calls nextMonthButton when 'View next month' is clicked
✓ Displays 'View next month' and close buttons, without description, when browsing current date with rolling 30-day period
✓ Displays 'View next month' and 'close' button when browsing current date with range periodType starting 10 days in past and ending in a future month

## Checklist



Linked issue #11321: Improve Availability Copy when using Limit Future Bookings settings
### Is your proposal related to a problem?

I set up the "Limit future bookings" to be "3 calendar days" into the future.
![Screenshot 2023-09-12 at 5 30 13 PM](https://github.com/calcom/cal.com/assets/4237280/e5c65cc5-4ff4-4b00-89f1-baad898fdf70)

When a user is trying to book, the availability feels a bit misleading, and I am worried that people will be discouraged from checking again (in my case) every three days.

![Screenshot 2023-09-12 at 5 28 25 PM](https://github.com/calcom/cal.com/assets/4237280/2587a687-1089-4512-8fc6-2026f0b3bc68)

There is indeed availability in the following months, just that it is too far into the future, so you must wait until the threshold is met if you care about a particular day.

### Describe the solution you'd like

Improve the copy text, making it clear that the user can not book that far into the future.

### Describe alternatives you've considered

- Disable the configuration

### Additional context


### Requirement/Document


Linked issue comments: user: login: github-actions[bot]
created_at: 2023-09-12T21:36:48Z
body: Thank you for opening your first issue, one of our team members will review it as soon as it possible. ❤️🎉
author_association: CONTRIBUTOR
user: login: sarthaksharma27
created_at: 2023-09-13T05:43:34Z
body: @yordis can you explain what the issue more clearly ?
author_association: NONE
user: login: PeerRich
created_at: 2023-09-13T13:43:01Z
body: what would be the ideal solution here?
author_association: MEMBER
user: login: yordis
created_at: 2023-09-13T16:23:41Z
body: In an ideal scenario, the component is aware of "Limit Future Bookings", "Limit total bookings duration", "Limit bookings frequency," and so on.

At the very least, change the  "No availability" copy.

Maybe something like "You can not book that far into the future" with a question mark icon or something explaining the availability.

I am not sure honestly. As a customer, I ended up removing the "Limit future bookings" setting because I rather let people book that far into the future, than thinking the whole year is unavailable 😭 While at the time, I would like to keep it, I am trying to avoid canceling meetings because I have no clue what I am gonna do a week from today 😄 

author_association: NONE
user: login: yisalk
created_at: 2023-09-23T16:47:58Z
body: @yordis, Can you please record a video and share it here? So we all can have an idea of the issue you are highlighting?
author_association: NONE
user: login: yordis
created_at: 2023-09-23T17:08:52Z
body: 
https://github.com/calcom/cal.com/assets/4237280/6d73c7b7-8580-43ad-8029-51da5102e51e


author_association: NONE
user: login: Kunal-debug-png
created_at: 2023-09-26T21:33:38Z
body: Provide clear information about the booking threshold and when additional slots are likely to open up. For example, you can say, "New booking slots open every three days. Please check back for availability." This helps users understand the booking process and encourages them to return. 
Also, can I work on this issue?
author_association: NONE
user: login: mjsf1234
created_at: 2023-10-02T19:35:16Z
body: I have understood the issue ...we can note down the limit -future-booking variable and show the user that the slot for booking is available to ${limit -future-booking variable} from today. @yordis can you assign this issue to me? I have done the local setup and am ready to raise the PR
author_association: NONE
user: login: khushalmajoka
created_at: 2023-10-04T05:52:27Z
body: Hi @yordis, I got the issue and trying to work on it.
Can you please check the below:

<img width="502" alt="image" src="https://github.com/calcom/cal.com/assets/56840292/5fb10588-4ae7-4fac-b731-67f2419c13f0">

Is this what you want?
author_association: NONE
user: login: yordis
created_at: 2023-10-04T05:55:55Z
body: @khushalmajoka I think that is a decent message, yes.
author_association: NONE
user: login: khushalmajoka
created_at: 2023-10-04T06:02:54Z
body: Ok so should i make the changes, finalize and create a PR? @yordis 
author_association: NONE
user: login: yordis
created_at: 2023-10-04T06:08:11Z
body: I would recommend you do so, and wait for people to review it!
author_association: NONE
user: login: khushalmajoka
created_at: 2023-10-04T06:09:08Z
body: Okay @yordis, Thankyou ❤️
author_association: NONE
user: login: Kick933
created_at: 2024-06-02T08:33:41Z
body: Hi, Since the previous PR has been marked stale, should we continue the discussion ahead? 
The last PR did not account for few cases, like if **date after threshold-days** falls in next month, we should continue to show view next month. But if the **date after threshold-days** falls in current month, we should change the message to saying "Please check back after x days" and remove the CTA (Button) as it becomes irrelevant in this case. This will keep the **Check next month** feature intact for all cases except what is required.
Also, is there any impact that I am not accounting for?
Would love to work on this.
author_association: NONE
user: login: Kick933
created_at: 2024-06-09T17:57:46Z
body: Hi, just wanted to check for any updates here? Can I pick this up and start working?
author_association: NONE
user: login: siv-io
created_at: 2024-08-18T07:56:17Z
body: Hey there, 

I am new to the codebase and trying to solve this issue just for learning and would greatly appreciate some help. 
Here's what I am trying to do... 

The popup message will show "Can only book no_of_days in advance" (see screenshot below)

![image](https://github.com/user-attachments/assets/3dac456d-ad17-48bd-bced-f3f8bee9fb2d)

### The problem:

I cannot get the no of days the user has configured in the "Limit future Bookings". 
![image](https://github.com/user-attachments/assets/9485f932-b921-4e2a-a53d-8dddb253a055)

![Screenshot 2024-08-18 132310](https://github.com/user-attachments/assets/e7a619d5-b497-4309-b43e-2745d493a250)

I am declaring a new variable "advanceBookingDays" to store the no of days user has set in Limit future bookings but it is always 'null'. 
Whereas the const "isBookingInPast" works perfectly fine. 

Feel free to ask for some context and code files...
author_association: NONE
user: login: v-a14
created_at: 2024-10-23T17:10:10Z
body: Hello @PeerRich 
Can I take this issue ? 
author_association: NONE
user: login: PaulZeliq
created_at: 2024-11-12T10:45:48Z
body: May I be assigned to this?
author_association: NONE
user: login: eklavya172004
created_at: 2024-11-18T17:00:49Z
body: I also want to work on the issue I got what are you trying to fix if you assign me this issue I would like to solve this
author_association: NONE
user: login: andrejpejovski
created_at: 2024-12-11T10:52:28Z
body: Hey guys can I get a little explanation of this checkbox "Always X days available"?

In future bookings feature I select 10 business days into the future and right below i have "Always 10 days available"

I searched for any clues but didn't found anything. Can someone explain what this feature (checkbox) does?
![image](https://github.com/user-attachments/assets/66008400-eeb1-462d-97a5-00d125dbeeaf)
author_association: NONE
user: login: Divyansh200102
created_at: 2025-03-16T23:20:04Z
body: @PeerRich is this issue still active?

author_association: NONE

Diff:
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index 5d1ecc39d8874c..42717f6aa96046 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -1869,6 +1869,9 @@
   "wrong_code": "Wrong verification code",
   "not_verified": "Not yet verified",
   "no_availability_in_month": "No availability in {{month}}",
+  "no_availability_rolling": "Scheduling is only available up to {{days}} in advance. Please check again soon.",
+  "no_availability_range":
+  "Scheduling ended on {{date}}. Please check again soon.",
   "view_previous_month": "View previous month",
   "view_next_month": "View next month",
   "send_code": "Send code",
diff --git a/packages/features/bookings/Booker/__tests__/Booker.test.tsx b/packages/features/bookings/Booker/__tests__/Booker.test.tsx
index 60f3db2251ade1..7912574b684414 100644
--- a/packages/features/bookings/Booker/__tests__/Booker.test.tsx
+++ b/packages/features/bookings/Booker/__tests__/Booker.test.tsx
@@ -63,6 +63,12 @@ const mockEvent = {
   isPending: false,
 };
 
+vi.mock("../../../calendars/NoAvailabilityDialog", () => ({
+  default: () => {
+    return null;
+  },
+}));
+
 const mockSchedule = {
   data: {
     slots: {
diff --git a/packages/features/bookings/Booker/components/DatePicker.tsx b/packages/features/bookings/Booker/components/DatePicker.tsx
index 3317f1c75d69c3..76726ca1b44120 100644
--- a/packages/features/bookings/Booker/components/DatePicker.tsx
+++ b/packages/features/bookings/Booker/components/DatePicker.tsx
@@ -7,6 +7,7 @@ import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
 import { weekdayToWeekIndex } from "@calcom/lib/date-fns";
 import { useLocale } from "@calcom/lib/hooks/useLocale";
 import type { User } from "@calcom/prisma/client";
+import type { PeriodData } from "@calcom/types/Event";
 
 import type { Slots } from "../../types";
 import { useBookerStore } from "../store";
@@ -57,7 +58,14 @@ export const DatePicker = ({
   scrollToTimeSlots,
 }: {
   event: {
-    data?: { subsetOfUsers: Pick<User, "weekStart">[] } | null;
+    data?: {
+      subsetOfUsers: Pick<User, "weekStart">[];
+      periodType?: PeriodData["periodType"];
+      periodStartDate?: PeriodData["periodStartDate"];
+      periodEndDate?: PeriodData["periodEndDate"];
+      periodDays?: PeriodData["periodDays"];
+      periodCountCalendarDays?: PeriodData["periodCountCalendarDays"];
+    } | null;
   };
   slots?: Slots;
   isLoading?: boolean;
@@ -95,6 +103,23 @@ export const DatePicker = ({
     isLoading: isLoading ?? true,
   });
   moveToNextMonthOnNoAvailability();
+
+  const periodData: PeriodData = {
+    ...{
+      periodType: "UNLIMITED",
+      periodStartDate: null,
+      periodEndDate: null,
+      periodDays: null,
+      periodCountCalendarDays: false,
+    },
+    ...(event?.data && {
+      periodType: event.data.periodType,
+      periodStartDate: event.data.periodStartDate,
+      periodEndDate: event.data.periodEndDate,
+      periodDays: event.data.periodDays,
+      periodCountCalendarDays: event.data.periodCountCalendarDays,
+    }),
+  };
   return (
     <DatePickerComponent
       customClassNames={{
@@ -117,6 +142,7 @@ export const DatePicker = ({
       weekStart={weekdayToWeekIndex(event?.data?.subsetOfUsers?.[0]?.weekStart)}
       slots={slots}
       scrollToTimeSlots={scrollToTimeSlots}
+      periodData={periodData}
     />
   );
 };
diff --git a/packages/features/calendars/DatePicker.tsx b/packages/features/calendars/DatePicker.tsx
index 7173b0a369067b..4a4049f79e40b6 100644
--- a/packages/features/calendars/DatePicker.tsx
+++ b/packages/features/calendars/DatePicker.tsx
@@ -10,10 +10,13 @@ import { daysInMonth, yyyymmdd } from "@calcom/lib/date-fns";
 import type { IFromUser, IToUser } from "@calcom/lib/getUserAvailability";
 import { useLocale } from "@calcom/lib/hooks/useLocale";
 import { weekdayNames } from "@calcom/lib/weekday";
+import type { PeriodData } from "@calcom/types/Event";
 import classNames from "@calcom/ui/classNames";
 import { Button } from "@calcom/ui/components/button";
 import { SkeletonText } from "@calcom/ui/components/skeleton";
 
+import NoAvailabilityDialog from "./NoAvailabilityDialog";
+
 export type DatePickerProps = {
   /** which day of the week to render the calendar. Usually Sunday (=0) or Monday (=1) - default: Sunday */
   weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
@@ -52,6 +55,7 @@ export type DatePickerProps = {
       emoji?: string;
     }[]
   >;
+  periodData?: PeriodData;
 };
 
 const Day = ({
@@ -111,25 +115,6 @@ const Day = ({
   );
 };
 
-const NoAvailabilityOverlay = ({
-  month,
-  nextMonthButton,
-}: {
-  month: string | null;
-  nextMonthButton: () => void;
-}) => {
-  const { t } = useLocale();
-
-  return (
-    <div className="bg-muted border-subtle absolute left-1/2 top-40 -mt-10 w-max -translate-x-1/2 -translate-y-1/2 transform rounded-md border p-8 shadow-sm">
-      <h4 className="text-emphasis mb-4 font-medium">{t("no_availability_in_month", { month: month })}</h4>
-      <Button onClick={nextMonthButton} color="primary" EndIcon="arrow-right" data-testid="view_next_month">
-        {t("view_next_month")}
-      </Button>
-    </div>
-  );
-};
-
 const Days = ({
   minDate,
   excludedDates = [],
@@ -143,6 +128,7 @@ const Days = ({
   slots,
   customClassName,
   isBookingInPast,
+  periodData,
   ...props
 }: Omit<DatePickerProps, "locale" | "className" | "weekStart"> & {
   DayComponent?: React.FC<React.ComponentProps<typeof Day>>;
@@ -156,6 +142,7 @@ const Days = ({
   };
   scrollToTimeSlots?: () => void;
   isBookingInPast: boolean;
+  periodData: PeriodData;
 }) => {
   // Create placeholder elements for empty days in first week
   const weekdayOfFirst = browsingDate.date(1).day();
@@ -282,9 +269,13 @@ const Days = ({
           )}
         </div>
       ))}
-
       {!props.isLoading && !isBookingInPast && includedDates && includedDates?.length === 0 && (
-        <NoAvailabilityOverlay month={month} nextMonthButton={nextMonthButton} />
+        <NoAvailabilityDialog
+          month={month}
+          nextMonthButton={nextMonthButton}
+          browsingDate={browsingDate}
+          periodData={periodData}
+        />
       )}
     </>
   );
@@ -299,6 +290,13 @@ const DatePicker = ({
   slots,
   customClassNames,
   includedDates,
+  periodData = {
+    periodStartDate: null,
+    periodEndDate: null,
+    periodCountCalendarDays: null,
+    periodDays: null,
+    periodType: "UNLIMITED",
+  },
   ...passThroughProps
 }: DatePickerProps &
   Partial<React.ComponentProps<typeof Days>> & {
@@ -319,7 +317,6 @@ const DatePicker = ({
   const { i18n, t } = useLocale();
   const bookingData = useBookerStore((state) => state.bookingData);
   const isBookingInPast = bookingData ? new Date(bookingData.endTime) < new Date() : false;
-
   const changeMonth = (newMonth: number) => {
     if (onMonthChange) {
       onMonthChange(browsingDate.add(newMonth, "month"));
@@ -408,6 +405,7 @@ const DatePicker = ({
           slots={slots}
           includedDates={includedDates}
           isBookingInPast={isBookingInPast}
+          periodData={periodData}
         />
       </div>
     </div>
diff --git a/packages/features/calendars/NoAvailabilityDialog.tsx b/packages/features/calendars/NoAvailabilityDialog.tsx
new file mode 100644
index 00000000000000..ef2053f74a94bb
--- /dev/null
+++ b/packages/features/calendars/NoAvailabilityDialog.tsx
@@ -0,0 +1,104 @@
+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;
diff --git a/packages/features/calendars/DatePicker.test.tsx b/packages/features/calendars/__tests__/DatePicker.test.tsx
similarity index 75%
rename from packages/features/calendars/DatePicker.test.tsx
rename to packages/features/calendars/__tests__/DatePicker.test.tsx
index 07a1cffdfcca01..634064a56e2019 100644
--- a/packages/features/calendars/DatePicker.test.tsx
+++ b/packages/features/calendars/__tests__/DatePicker.test.tsx
@@ -1,8 +1,9 @@
 import { render } from "@testing-library/react";
 
 import dayjs from "@calcom/dayjs";
+import { PeriodType } from "@calcom/prisma/enums";
 
-import { DatePicker } from "./DatePicker";
+import { DatePicker } from "../DatePicker";
 
 const noop = () => {
   /* noop */
@@ -11,7 +12,20 @@ const noop = () => {
 describe("Tests for DatePicker Component", () => {
   test("Should render correctly with default date", async () => {
     const testDate = dayjs("2024-02-20");
-    const { getByTestId } = render(<DatePicker onChange={noop} browsingDate={testDate} locale="en" />);
+    const { getByTestId } = render(
+      <DatePicker
+        onChange={noop}
+        browsingDate={testDate}
+        locale="en"
+        periodData={{
+          periodType: PeriodType.UNLIMITED,
+          periodDays: null,
+          periodCountCalendarDays: false,
+          periodStartDate: null,
+          periodEndDate: null,
+        }}
+      />
+    );
 
     const selectedMonthLabel = getByTestId("selected-month-label");
     await expect(selectedMonthLabel).toHaveAttribute("dateTime", testDate.format("YYYY-MM"));
diff --git a/packages/features/calendars/__tests__/NoAvailabilityDialog.test.tsx b/packages/features/calendars/__tests__/NoAvailabilityDialog.test.tsx
new file mode 100644
index 00000000000000..4d047d77087ce5
--- /dev/null
+++ b/packages/features/calendars/__tests__/NoAvailabilityDialog.test.tsx
@@ -0,0 +1,138 @@
+import { render, screen, fireEvent } from "@testing-library/react";
+import { vi } from "vitest";
+
+import dayjs from "@calcom/dayjs";
+import { PeriodType } from "@calcom/prisma/enums";
+
+import NoAvailabilityDialog from "../NoAvailabilityDialog";
+
+vi.mock("@calcom/lib/hooks/useLocale", () => ({
+  useLocale: () => ({
+    t: (key: string, vars?: { [key: string]: string | number }) => {
+      if (key === "no_availability_in_month") return `No availability in ${vars?.month}`;
+      if (key === "no_availability_rolling")
+        return `Scheduling is only available up to ${vars?.days} in advance. Please check again soon.`;
+      if (key === "no_availability_range")
+        return `Scheduling ended on ${vars?.date}. Please check again soon.`;
+      if (key === "close") return "Close";
+      if (key === "view_next_month") return "View next month";
+      if (key === "calendar_days") return "calendar days";
+      return key;
+    },
+  }),
+}));
+
+beforeEach(() => {
+  vi.clearAllMocks();
+});
+
+describe("NoAvailabilityOverlay", () => {
+  const defaultProps = {
+    month: "March",
+    nextMonthButton: vi.fn(),
+    browsingDate: dayjs(),
+    periodData: {
+      periodType: PeriodType.UNLIMITED,
+      periodDays: null,
+      periodCountCalendarDays: true,
+      periodStartDate: null,
+      periodEndDate: null,
+    },
+  };
+
+  test("Displays rolling period description and close button, when period type is ROLLING, and period ends before next month.", () => {
+    const periodDays = 5;
+
+    render(
+      <NoAvailabilityDialog
+        {...defaultProps}
+        browsingDate={dayjs().add(periodDays + 1, "day")}
+        periodData={{
+          ...defaultProps.periodData,
+          periodType: PeriodType.ROLLING,
+          periodDays: periodDays,
+        }}
+      />
+    );
+
+    expect(screen.getByRole("dialog")).toHaveTextContent(
+      `Scheduling is only available up to ${periodDays} calendar days in advance. Please check again soon.`
+    );
+    const nextMonthButton = screen.queryAllByTestId("view_next_month");
+    const closeButton = screen.getAllByTestId("close_dialog_button");
+    expect(nextMonthButton).toHaveLength(0);
+    expect(closeButton).toHaveLength(1);
+  });
+
+  test("Displays range period description when period type is RANGE and range ends before the next month", () => {
+    const startDate = dayjs().subtract(30, "days");
+    const endDate = dayjs();
+    render(
+      <NoAvailabilityDialog
+        {...defaultProps}
+        browsingDate={dayjs().add(40, "days")}
+        periodData={{
+          ...defaultProps.periodData,
+          periodType: "RANGE",
+          periodStartDate: startDate.toDate(),
+          periodEndDate: endDate.toDate(),
+        }}
+      />
+    );
+    expect(screen.getByRole("dialog")).toHaveTextContent(
+      `Scheduling ended on ${endDate.format("MMMM D YYYY")}. Please check again soon.`
+    );
+    const nextMonthButton = screen.queryAllByTestId("view_next_month");
+    const closeButton = screen.getAllByTestId("close_dialog_button");
+    expect(nextMonthButton).toHaveLength(0);
+    expect(closeButton).toHaveLength(1);
+  });
+
+  test("calls nextMonthButton when 'View next month' is clicked", () => {
+    render(<NoAvailabilityDialog {...defaultProps} />);
+    const nextButton = screen.getByText("View next month");
+    fireEvent.click(nextButton);
+    expect(defaultProps.nextMonthButton).toHaveBeenCalled();
+  });
+
+  test("Displays 'View next month' and close buttons, without description, when browsing current date with rolling 30-day period", () => {
+    render(
+      <NoAvailabilityDialog
+        {...defaultProps}
+        browsingDate={dayjs()}
+        periodData={{
+          ...defaultProps.periodData,
+          periodType: "ROLLING",
+          periodDays: 30,
+        }}
+      />
+    );
+    const description = screen.queryByTestId("dialog-subtitle");
+    const nextMonthButton = screen.getAllByTestId("view_next_month");
+    const closeButton = screen.getAllByTestId("close_dialog_button");
+    expect(description).not.toBeInTheDocument();
+    expect(nextMonthButton).toHaveLength(1);
+    expect(closeButton).toHaveLength(1);
+  });
+
+  test("Displays 'View next month' and 'close' button when browsing current date with range periodType starting 10 days in past and ending in a future month", () => {
+    render(
+      <NoAvailabilityDialog
+        {...defaultProps}
+        browsingDate={dayjs()}
+        periodData={{
+          ...defaultProps.periodData,
+          periodType: "RANGE",
+          periodStartDate: dayjs().subtract(10, "days").toDate(),
+          periodEndDate: dayjs().add(40, "days").toDate(),
+        }}
+      />
+    );
+    const description = screen.queryByTestId("dialog-subtitle");
+    const nextMonthButton = screen.getAllByTestId("view_next_month");
+    const closeButton = screen.getAllByTestId("close_dialog_button");
+    expect(description).not.toBeInTheDocument();
+    expect(nextMonthButton).toHaveLength(1);
+    expect(closeButton).toHaveLength(1);
+  });
+});
diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts
index 79a88c61ee824e..68c7a1139ee615 100644
--- a/packages/features/eventtypes/lib/getPublicEvent.ts
+++ b/packages/features/eventtypes/lib/getPublicEvent.ts
@@ -142,6 +142,11 @@ const getPublicEventSelect = (fetchAllUsers: boolean) => {
         timeZone: true,
       },
     },
+    periodType: true,
+    periodDays: true, // days if limiting future bookings
+    periodEndDate: true, //end date limit by range
+    periodStartDate: true, //start date limit by range
+    periodCountCalendarDays: true, // count calendar days? Or only business days based on periodDays
     hidden: true,
     assignAllTeamMembers: true,
     rescheduleWithSameRoundRobinHost: true,
diff --git a/packages/lib/__mocks__/logger.ts b/packages/lib/__mocks__/logger.ts
index 2c5decf19f6d84..976bcafbe4814a 100644
--- a/packages/lib/__mocks__/logger.ts
+++ b/packages/lib/__mocks__/logger.ts
@@ -6,7 +6,13 @@ const mockLogger = {
   info: vi.fn(),
   log: vi.fn(),
   warn: vi.fn(),
-  getSubLogger: vi.fn(),
+  getSubLogger: vi.fn(() => ({
+    debug: vi.fn(),
+    error: vi.fn(),
+    info: vi.fn(),
+    log: vi.fn(),
+    warn: vi.fn(),
+  })),
 };
 
 vi.mock("@calcom/lib/logger", () => ({
diff --git a/packages/types/Event.d.ts b/packages/types/Event.d.ts
index 4ab3f0d6dc74c5..42c20bd6644f9a 100644
--- a/packages/types/Event.d.ts
+++ b/packages/types/Event.d.ts
@@ -1,6 +1,12 @@
+import type { EventType } from "@calcom/prisma/client";
 import type { NewCalendarEventType, AdditionalInformation } from "@calcom/types/Calendar";
 
 import type { CrmData } from "./CrmService";
 import type { VideoCallData } from "./VideoApiAdapter";
 
 export type Event = AdditionalInformation | NewCalendarEventType | VideoCallData | CrmData;
+
+export type PeriodData = Pick<
+  EventType,
+  "periodType" | "periodStartDate" | "periodEndDate" | "periodDays" | "periodCountCalendarDays"
+>;
diff --git a/packages/ui/components/dialog/Dialog.tsx b/packages/ui/components/dialog/Dialog.tsx
index dbbd991b6bf982..17c56fbe4e16bd 100644
--- a/packages/ui/components/dialog/Dialog.tsx
+++ b/packages/ui/components/dialog/Dialog.tsx
@@ -126,12 +126,12 @@ export function DialogHeader(props: DialogHeaderProps) {
 
   return (
     <div className="mb-4">
-      <h3
+      <h2
         data-testid="dialog-title"
         className="leading-20 text-semibold text-emphasis font-cal mb-1 text-xl"
         id="modal-title">
         {props.title}
-      </h3>
+      </h2>
       {props.subtitle && (
         <p className="text-subtle text-sm" data-testid="dialog-subtitle">
           {props.subtitle}


Reviews:
user: login: TusharBhatt1
body: Hey @shawnCaza , we don't manually add locales to all , just add to `en` , rest will be taken care of
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T05:13:17Z
user: login: TusharBhatt1
body: Can you add a loom for it !
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:33:25Z
user: login: TusharBhatt1
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T16:45:09Z
user: login: shawnCaza
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T17:28:26Z
user: login: shawnCaza
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T17:29:58Z
user: login: shawnCaza
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T17:32:50Z
user: login: TusharBhatt1
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T12:34:29Z
user: login: TusharBhatt1
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T05:25:09Z

Review comments:
diff_hunk: @@ -1,6 +1,8 @@
 import { render } from "@testing-library/react";
+import * as React from "react";
 
path: packages/features/calendars/DatePicker.test.tsx
user: login: TusharBhatt1
body: Why this ?
created_at: 2025-04-07T16:45:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,6 +1,8 @@
 import { render } from "@testing-library/react";
+import * as React from "react";
path: packages/features/calendars/DatePicker.test.tsx
user: login: shawnCaza
body: ```suggestion

```
created_at: 2025-04-07T17:28:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,139 @@
+import { render, screen, fireEvent } from "@testing-library/react";
+import * as React from "react";
path: packages/features/calendars/NoAvailabilityDialog.test.tsx
user: login: shawnCaza
body: ```suggestion

```
created_at: 2025-04-07T17:29:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,6 +1,8 @@
 import { render } from "@testing-library/react";
+import * as React from "react";
 
path: packages/features/calendars/DatePicker.test.tsx
user: login: shawnCaza
body: @TusharBhatt1  Good catch, it isn't necessary. I've removed it.

That import was only added during development to try something out. 
created_at: 2025-04-07T17:32:50Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-30T20:50:56Z
body: @shawnCaza is attempting to deploy a commit to the **cal** Team on [Vercel](https://vercel.com).

A member of the Team first needs to [authorize it](https://vercel.com/git/authorize?team=cal&type=github&job=%7B%22headInfo%22%3A%7B%22sha%22%3A%22a70ea43197764f398860b42509062352849aa94a%22%7D%2C%22id%22%3A%22QmWXNsUpc1gLS4nZ5fBUmDTrVNKUjzvxtZB6Qgui9yZPxd%22%2C%22org%22%3A%22calcom%22%2C%22prId%22%3A20451%2C%22repo%22%3A%22cal.com%22%7D).


author_association: NONE
user: login: CLAassistant
created_at: 2025-03-30T20:50:58Z
body: [![CLA assistant check](https://cla-assistant.io/pull/badge/signed)](https://cla-assistant.io/calcom/cal.com?pullRequest=20451) <br/>All committers have signed the CLA.
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-30T20:51:03Z
body: Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.

Details:

```
Unknown release type "Fix" found in pull request title "Fix: Adds a description to the dialog window that appears when their is no availability in a month if it can be determined there will not be any availability in future months.". 

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit
```
<!-- Sticky Pull Request Commentpr-title-lint-error -->
author_association: CONTRIBUTOR
user: login: graphite-app[bot]
created_at: 2025-03-30T20:55:10Z
body: ### Graphite Automations
<details><summary><a href="https://app.graphite.dev/automations/?org=calcom&ruleId=qoTwHtX8MnP39HKxIt4V&ref=gt-automation-github-comment">"Add consumer team as reviewer"</a> took an action on this PR • (03/30/25)</summary><p>1 reviewer was added to this PR based on Keith Williams's automation.</p></details>
<details><summary><a href="https://app.graphite.dev/automations/?org=calcom&ruleId=PVL1B0jFo4nd5eKAT0W2&ref=gt-automation-github-comment">"Add foundation team as reviewer"</a> took an action on this PR • (03/30/25)</summary><p>1 reviewer was added to this PR based on Keith Williams's automation.</p></details>
<details><summary><a href="https://app.graphite.dev/automations/?org=calcom&ruleId=eVE8lkcUKifoeNzVzJEK&ref=gt-automation-github-comment">"Add community label"</a> took an action on this PR • (03/30/25)</summary><p>1 label was added to this PR based on Keith Williams's automation.</p></details>
<details><summary><a href="https://app.graphite.dev/automations/?org=calcom&ruleId=tJdOAwF9biWptZ2BLCTk&ref=gt-automation-github-comment">"Add ready-for-e2e label"</a> took an action on this PR • (04/10/25)</summary><p>1 label was added to this PR based on Keith Williams's automation.</p></details>
author_association: NONE
user: login: shawnCaza
created_at: 2025-04-01T13:05:53Z
body: Thanks @TusharBhatt1, I've removed all locale changes other than EN.
author_association: CONTRIBUTOR
user: login: the-roshanray
created_at: 2025-04-06T18:30:14Z
body: /attempt #20451
author_association: NONE
user: login: shawnCaza
created_at: 2025-04-07T16:28:09Z
body: @TusharBhatt1  I've added a [loom here](https://www.loom.com/share/d5a6ffb99e6244e59885b675ac3cf822?sid=0a7978b4-9c39-4677-a06d-eefa34e5cdaf).


author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-10T13:04:51Z
body: <!-- GENERATED-E2E-RESULTS -->
## E2E results are ready!
- [Workflow #56725.1 latest results](https://calcom.github.io/test-results/reports/Fixing-text-to-clarify-a-lack-of-availability-situation-when-the-limit-of-future-bookings-has-been-reached/14472496121/1)
author_association: CONTRIBUTOR
user: login: TusharBhatt1
created_at: 2025-04-10T17:25:32Z
body: @shawnCaza we have a failing test , can you fix
author_association: CONTRIBUTOR
user: login: shawnCaza
created_at: 2025-04-10T17:28:47Z
body: @TusharBhatt1 Will do. I've been looking into why the booker test is failing. I think it's a matter of improving the mocks it uses.
author_association: CONTRIBUTOR
user: login: shawnCaza
created_at: 2025-04-14T03:49:58Z
body: @TusharBhatt1 the tests are passing now.

I mocked the NoAvailabilityComponent to not return anything in the Booker test as it doesn't seem necessary for those tests.

I also expanded the logger mock to handle calls to getSubLogger. This one isn't strictly necessary with the above in place, but it does fix errors when this mock is in place and calls are being made to getSubLogger.

I also moved the 'calendars' folder tests to a '__tests__' folder to follow the conventions I saw elsewhere in cal.com.
author_association: CONTRIBUTOR