Skip to content

fix: duplicate getSchedule call#23256

Closed
emrysal wants to merge 17 commits intomainfrom
bugfix/duplicated-getSchedule-call
Closed

fix: duplicate getSchedule call#23256
emrysal wants to merge 17 commits intomainfrom
bugfix/duplicated-getSchedule-call

Conversation

@emrysal
Copy link
Contributor

@emrysal emrysal commented Aug 21, 2025

What does this PR do?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 21, 2025

Walkthrough

This PR removes useTimesForSchedule and introduces lib/use-schedule/getTimesForSchedule with tests. It drops prefetchNextMonth across schedule APIs, makes monthCount explicit (default 1), and updates hooks to use getTimesForSchedule. Booker wrappers now compute monthCount only for MONTH_VIEW and stop passing prefetchNextMonth. A new Booker/hooks/useEvent is added; related imports switch from utils/lib to hooks. Types consolidate to UseScheduleForEventReturnType; component prop types updated accordingly. DatePicker and calendars adjust month tooltip/badge logic, derive month from store, and refine initial date selection. Several files update import paths; no broad runtime changes beyond the scheduling window logic.

Possibly related PRs

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/duplicated-getSchedule-call

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@keithwillcode keithwillcode added core area: core, team members only foundation labels Aug 21, 2025
@vercel
Copy link

vercel bot commented Aug 21, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Aug 27, 2025 2:40pm
cal-eu Ignored Ignored Aug 27, 2025 2:40pm

@github-actions
Copy link
Contributor

github-actions bot commented Aug 21, 2025

E2E results are ready!

@emrysal emrysal marked this pull request as ready for review August 22, 2025 00:35
@emrysal emrysal requested a review from a team August 22, 2025 00:35
@emrysal emrysal requested a review from a team as a code owner August 22, 2025 00:35
@dosubot dosubot bot added the bookings area: bookings, availability, timezones, double booking label Aug 22, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1)

241-252: Guard against undefined columnViewExtraDays and simplify monthCount logic

Accessing bookerLayout.columnViewExtraDays.current when layout is not COLUMN_VIEW can throw at runtime. Also, the nested ternary is hard to follow.

Apply this refactor to avoid unsafe access and improve readability:

-  const monthCount =
-    ((bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time") ||
-      bookerLayout.layout === BookerLayouts.COLUMN_VIEW) &&
-    dayjs(date).add(1, "month").month() !==
-      dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
-      ? prefetchNextMonth
-        ? 3
-        : 2
-      : prefetchNextMonth
-      ? 2
-      : 1;
+  const isColumnView = bookerLayout.layout === BookerLayouts.COLUMN_VIEW;
+  const crossesBoundary = isColumnView
+    ? dayjs(date).add(1, "month").month() !==
+      dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
+    : false;
+
+  const monthCount =
+    (((bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time") || isColumnView) &&
+      crossesBoundary)
+      ? (prefetchNextMonth ? 3 : 2)
+      : (prefetchNextMonth ? 2 : 1);
🧹 Nitpick comments (1)
packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts (1)

20-22: Early return window looks correct; confirm intended inclusivity

Returning [startOfMonth, addMonths(startOfMonth, monthCount)] for falsy dayCount expands the full browsing window. Confirm backend expects the end to be exclusive.

If the backend expects an inclusive end, you might consider subtracting one day from the end.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0ea673e and 2e41a8f.

📒 Files selected for processing (5)
  • packages/features/bookings/Booker/utils/event.ts (0 hunks)
  • packages/features/schedules/lib/use-schedule/useSchedule.ts (2 hunks)
  • packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts (1 hunks)
  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1 hunks)
  • packages/platform/atoms/booker/BookerWebWrapper.tsx (3 hunks)
💤 Files with no reviewable changes (1)
  • packages/features/bookings/Booker/utils/event.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
  • packages/platform/atoms/booker/BookerWebWrapper.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
  • packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts
  • packages/features/schedules/lib/use-schedule/useSchedule.ts
  • packages/platform/atoms/booker/BookerWebWrapper.tsx
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
  • packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts
  • packages/features/schedules/lib/use-schedule/useSchedule.ts
  • packages/platform/atoms/booker/BookerWebWrapper.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts
  • packages/features/schedules/lib/use-schedule/useSchedule.ts
🧠 Learnings (1)
📚 Learning: 2025-07-28T11:50:23.946Z
Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*.{ts,tsx} : Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js `.utc()` in hot paths like loops

Applied to files:

  • packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts
🧬 Code graph analysis (2)
packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts (1)
packages/features/schedules/lib/use-schedule/useSchedule.ts (1)
  • UseScheduleWithCacheArgs (14-30)
packages/platform/atoms/booker/BookerWebWrapper.tsx (1)
packages/embeds/embed-core/src/types.ts (1)
  • BookerLayouts (6-6)
🔇 Additional comments (3)
packages/platform/atoms/booker/BookerWebWrapper.tsx (1)

99-101: dayjs.max is supported via the minMax plugin—no changes needed

  • The minMax plugin is imported in packages/dayjs/index.ts (import minmax from "dayjs/plugin/minMax") and registered with dayjs.extend(minmax) on line 25, so dayjs.max is available everywhere.
  • There’s no need for a plugin-free fallback in BookerWebWrapper.tsx; the existing setup already covers this use case.
packages/features/schedules/lib/use-schedule/useSchedule.ts (2)

22-23: No explicit monthCount call sites found; default=1 is safe

I searched the entire codebase for any explicit monthCount properties in TS/TSX files and found zero occurrences, confirming that no callers were relying on an “unset” value to fetch multiple months.

• Search command:
rg -nP '\bmonthCount\s*:' -C2 -g '*.ts' -g '*.tsx'
→ 0 matches

With no explicit overrides present, switching the default to 1 poses no regression risk.


101-121: Duplicate schedule-fetch gating verified

  • API V2 (useApiV2AvailableSlots) is enabled only when
    isCallingApiV2Slots = useApiV2 && Boolean(isTeamEvent) && options.enabled (lines 123–131)
  • TRPC V1 (trpc.viewer.slots.getSchedule.useQuery) is enabled only when
    options.enabled && !isCallingApiV2Slots (lines 135–140)
  • options.enabled itself is gated by !skipGetSchedule (i.e. cal.skipSlotsFetch=true), so setting that flag disables both V1 and V2 queries
  • A static search found no additional viewer.slots.getSchedule.useQuery calls in this file

All gating logic is correct. Proceed with approval.

@@ -1,41 +1,42 @@
import dayjs from "@calcom/dayjs";
import { startOfMonth, startOfDay, isSameMonth, addMonths, addDays } from "date-fns";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Parsing month via new Date("yyyy-MM") is non‑standard; use date-fns parse to avoid Safari bugs

new Date("2025-08") is implementation-dependent and can be invalid in Safari. Use parse("yyyy-MM", ...) for robust behavior.

-import { startOfMonth, startOfDay, isSameMonth, addMonths, addDays } from "date-fns";
+import { startOfMonth, startOfDay, isSameMonth, addMonths, addDays, parse } from "date-fns";

-  const browsingMonthStartDate = startOfMonth(month ? new Date(month) : currentDate);
+  const browsingMonthStartDate = startOfMonth(month ? parse(month, "yyyy-MM", currentDate) : currentDate);

Also applies to: 17-18

🤖 Prompt for AI Agents
In packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts around
lines 1 and 17-18, the code currently parses month strings using new
Date("yyyy-MM") which is implementation-dependent and breaks in Safari; replace
that logic by importing parse from date-fns and use parse(monthString,
"yyyy-MM", new Date()) (then wrap with isValid or startOfMonth as needed) so
parsing is deterministic across browsers; update the import line to include
parse and adjust the two locations (lines 17-18 and the earlier use) to use
parse("yyyy-MM", ...) instead of new Date("yyyy-MM").

Comment on lines 29 to 36
const selectedDateObj = new Date(selectedDate);
startTime = selectedDateObj.toISOString();
endTime = addDays(selectedDateObj, dayCount).toISOString();
} else if (isSameMonth(browsingMonthStartDate, currentDate)) {
startTime = startOfDay(currentDate).toISOString();
endTime = addDays(startOfDay(currentDate), dayCount).toISOString();
} else {
startTime = monthDayjs.startOf("month").toISOString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

selectedDate parsing uses UTC semantics; likely off by timezone vs prior Day.js behavior

new Date("yyyy-MM-dd") parses as UTC, whereas previous Day.js usage typically treated it as local date. This can shift windows by timezone and cause incorrect ranges.

Prefer parsing as a local date with parse("yyyy-MM-dd", ...):

-  if (selectedDate) {
-    const selectedDateObj = new Date(selectedDate);
-    startTime = selectedDateObj.toISOString();
-    endTime = addDays(selectedDateObj, dayCount).toISOString();
+  if (selectedDate) {
+    const selectedDateObj = parse(selectedDate, "yyyy-MM-dd", currentDate);
+    startTime = selectedDateObj.toISOString();
+    endTime = addDays(selectedDateObj, dayCount).toISOString();

Optionally add a unit test for a non-UTC timezone (e.g., America/Los_Angeles) to prevent regressions.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (selectedDate) {
const selectedDateObj = new Date(selectedDate);
startTime = selectedDateObj.toISOString();
endTime = addDays(selectedDateObj, dayCount).toISOString();
} else if (isSameMonth(browsingMonthStartDate, currentDate)) {
startTime = startOfDay(currentDate).toISOString();
endTime = addDays(startOfDay(currentDate), dayCount).toISOString();
} else {
if (selectedDate) {
const selectedDateObj = parse(selectedDate, "yyyy-MM-dd", currentDate);
startTime = selectedDateObj.toISOString();
endTime = addDays(selectedDateObj, dayCount).toISOString();

Comment on lines 155 to 162
const monthCount =
((bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time") ||
bookerLayout.layout === BookerLayouts.COLUMN_VIEW) &&
bookerLayout.layout === BookerLayouts.MONTH_VIEW &&
dayjs(date).add(1, "month").month() !==
dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
? 2
? prefetchNextMonth
? 2
: 1
: undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Incorrect dependency on columnViewExtraDays for MONTH_VIEW; potential runtime error

monthCount for MONTH_VIEW should not read columnViewExtraDays.current. This can be undefined and will crash. It also doesn’t affect month view logic.

Use only prefetchNextMonth to decide window size in MONTH_VIEW:

-  const monthCount =
-    bookerLayout.layout === BookerLayouts.MONTH_VIEW &&
-    dayjs(date).add(1, "month").month() !==
-      dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
-      ? prefetchNextMonth
-        ? 2
-        : 1
-      : undefined;
+  const monthCount =
+    bookerLayout.layout === BookerLayouts.MONTH_VIEW
+      ? (prefetchNextMonth ? 2 : 1)
+      : undefined;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const monthCount =
((bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time") ||
bookerLayout.layout === BookerLayouts.COLUMN_VIEW) &&
bookerLayout.layout === BookerLayouts.MONTH_VIEW &&
dayjs(date).add(1, "month").month() !==
dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
? 2
? prefetchNextMonth
? 2
: 1
: undefined;
const monthCount =
bookerLayout.layout === BookerLayouts.MONTH_VIEW
? (prefetchNextMonth ? 2 : 1)
: undefined;
🤖 Prompt for AI Agents
In packages/platform/atoms/booker/BookerWebWrapper.tsx around lines 155 to 162,
the MONTH_VIEW branch incorrectly reads bookerLayout.columnViewExtraDays.current
(which can be undefined) and may crash; replace that logic so monthCount for
MONTH_VIEW does not access columnViewExtraDays at all and instead is determined
solely by prefetchNextMonth (set monthCount = 2 when prefetchNextMonth is true,
otherwise 1); keep the existing conditional structure for non-MONTH_VIEW cases
and ensure no access to .current in the MONTH_VIEW path.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2e41a8f and 4655327.

📒 Files selected for processing (1)
  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
🧬 Code graph analysis (1)
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1)
packages/embeds/embed-core/src/types.ts (1)
  • BookerLayouts (6-6)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Production builds / Build Web App
  • GitHub Check: Production builds / Build API v1
  • GitHub Check: Tests / Unit
  • GitHub Check: Type check / check-types
  • GitHub Check: Codacy Static Code Analysis

Comment on lines +242 to +249
bookerLayout.layout === BookerLayouts.MONTH_VIEW &&
dayjs(date).add(1, "month").month() !==
dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
? 2
? prefetchNextMonth
? 2
: 1
: undefined;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Critical: monthCount logic references COLUMN_VIEW-only field under MONTH_VIEW, causing incorrect behavior and potential runtime error

The condition guards for BookerLayouts.MONTH_VIEW but then dereferences bookerLayout.columnViewExtraDays.current, which is defined for COLUMN_VIEW. This can be undefined for MONTH_VIEW and lead to an exception or, worse, silently wrong logic for determining monthCount. Conceptually, for month view we only need to choose between 1 or 2 months based on prefetchNextMonth; the extra-days boundary relevant to column view should not factor here.

Recommend simplifying monthCount to rely solely on prefetchNextMonth when layout is MONTH_VIEW:

-  const monthCount =
-    bookerLayout.layout === BookerLayouts.MONTH_VIEW &&
-    dayjs(date).add(1, "month").month() !==
-      dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
-      ? prefetchNextMonth
-        ? 2
-        : 1
-      : undefined;
+  const monthCount =
+    bookerLayout.layout === BookerLayouts.MONTH_VIEW
+      ? (prefetchNextMonth ? 2 : 1)
+      : undefined;

Follow-ups:

  • If you want to avoid unnecessary work, compute prefetchNextMonth only in the branch(es) where it’s actually needed (MONTH_VIEW here). That would trim a bit of dayjs work for WEEK/COLUMN views.

Verification:

  • Ensure useTimesForSchedule indeed defaults monthCount to 1 when undefined is passed, as assumed by the broader refactor. If that default is applied via parameter destructuring, passing undefined is fine; if not, you may want to coalesce to 1 at the call site.

🏁 Script executed:

#!/bin/bash
# Verify monthCount default in useTimesForSchedule
set -euo pipefail

# Locate the hook implementation
fd -a 'useTimesForSchedule.*\.(ts|tsx)'

# Show the function signature and look for a default `monthCount = 1`
rg -n -C3 -P 'export\s+(const|function)\s+useTimesForSchedule\b|function\s+useTimesForSchedule\b'

# Look for param destructuring defaults or internal defaulting of monthCount
rg -n -C2 -P '\bmonthCount\s*=\s*1\b|\bmonthCount\b.*\|\|\s*1' $(fd -a 'useTimesForSchedule.*\.(ts|tsx)')

Length of output: 1185


Simplify MONTH_VIEW monthCount logic and remove COLUMN_VIEW-only field

The current monthCount calculation in BookerPlatformWrapper.tsx (lines 242–249) references bookerLayout.columnViewExtraDays.current under BookerLayouts.MONTH_VIEW, which can be undefined in month view and is unnecessary for determining month count. Since useTimesForSchedule defaults monthCount to 1 when passed undefined, we can simplify this logic to rely solely on prefetchNextMonth for month view without altering downstream behavior.

Affected location:

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx (around line 242)

Proposed diff:

-  const monthCount =
-    bookerLayout.layout === BookerLayouts.MONTH_VIEW &&
-    dayjs(date).add(1, "month").month() !==
-      dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
-      ? prefetchNextMonth
-        ? 2
-        : 1
-      : undefined;
+  const monthCount =
+    bookerLayout.layout === BookerLayouts.MONTH_VIEW
+      ? (prefetchNextMonth ? 2 : 1)
+      : undefined;

– Verified that useTimesForSchedule defines monthCount = 1 by default in its parameter destructuring, so passing undefined would yield the same behavior (defaulting to 1) without extra coalescing. ([script output]).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bookerLayout.layout === BookerLayouts.MONTH_VIEW &&
dayjs(date).add(1, "month").month() !==
dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
? 2
? prefetchNextMonth
? 2
: 1
: undefined;
const monthCount =
bookerLayout.layout === BookerLayouts.MONTH_VIEW
? (prefetchNextMonth ? 2 : 1)
: undefined;
🤖 Prompt for AI Agents
In packages/platform/atoms/booker/BookerPlatformWrapper.tsx around lines 242 to
249, the MONTH_VIEW monthCount logic incorrectly references
bookerLayout.columnViewExtraDays.current (a COLUMN_VIEW-only field) — remove
that reference and simplify the expression so monthCount for MONTH_VIEW is
determined solely by prefetchNextMonth (e.g., prefetchNextMonth ? 2 :
undefined), relying on useTimesForSchedule to default undefined to 1.

Copy link
Contributor

@Udit-takkar Udit-takkar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emrysal can we add an E2E test for this? I have also fixed similar duplicate getSchedule bug in the past #17191 (due to different reason)

Comment on lines 157 to 158
dayjs(date).add(1, "month").month() !==
dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this check doing? Do we need this?

CarinaWolli
CarinaWolli previously approved these changes Aug 22, 2025
Copy link
Member

@CarinaWolli CarinaWolli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix looks good to me. getSchedule is now only called once and everything is still working as it should

@CarinaWolli CarinaWolli self-requested a review August 22, 2025 13:35
@keithwillcode
Copy link
Contributor

keithwillcode commented Aug 22, 2025

Screenshot 2025-08-22 at 4 41 21 PM @emrysal I'm seeing that the `startTime` is July 1 instead of August 1. I'm loading the month of August. I know that in terms of perf, this will basically be ignored but we should still fix it.

Copy link
Contributor

@keithwillcode keithwillcode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image Seems like the whole thing is off by 1 month. Check out the startTime and endTime params compared to the month I'm loading.

@dosubot dosubot bot added the 🐛 bug Something isn't working label Aug 27, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/features/bookings/Booker/components/AvailableTimeSlots.tsx (1)

205-208: Refactor AvailableTimeSlots.tsx to correctly derive and apply the browsing‐month badge

The suggested IIFE in the original comment illegally calls a React hook inside a nested function, violating the Rules of Hooks. Instead, pull the store’s month at the top of the component and compute availableMonth directly in the JSX.

Locations to update:

  • packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
    • After line 82 (where date is defined) – insert hooks to read state.month and derive currentMonth.
    • At the AvailableTimesHeader call around lines 180–190 – replace the existing availableMonth={…} expression.

Proposed diff:

--- a/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
+++ b/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
@@ 80,6 +80,9 @@ const AvailableTimeSlots = ({ onAvailableTimeSlotSelect, ...props }) => {
   const date = selectedDate || dayjs().format("YYYY-MM-DD");
   const [layout] = useBookerStoreContext((state) => [state.layout]);
   const isColumnView = layout === BookerLayouts.COLUMN_VIEW;
+
+  // Derive the browsing month (YYYY-MM) from store, falling back to the current date
+  const browsingMonth = useBookerStoreContext((state) => state.month);
+  const currentMonth = browsingMonth ?? dayjs(date).format("YYYY-MM");
 
   return (
     <>
@@ 180,14 +183,10 @@ const AvailableTimeSlots = ({ onAvailableTimeSlotSelect, ...props }) => {
               <AvailableTimesHeader
                 customClassNames={{
                   availableTimeSlotsHeaderContainer: customClassNames?.availableTimeSlotsHeaderContainer,
                   availableTimeSlotsTitle: customClassNames?.availableTimeSlotsTitle,
                   availableTimeSlotsTimeFormatToggle: customClassNames?.availableTimeSlotsTimeFormatToggle,
                 }}
                 key={slots.date}
-                date={dayjs(slots.date)}
+                date={dayjs(slots.date)}
                 showTimeFormatToggle={!isColumnView}
-                availableMonth={
-                  dayjs(selectedDate).format("MM") !== dayjs(slots.date).format("MM")
-                    ? dayjs(slots.date).format("MMM")
-                    : undefined
-                }
+                availableMonth={
+                  currentMonth !== dayjs(slots.date).format("YYYY-MM")
+                    ? dayjs(slots.date).format("MMM")
+                    : undefined
+                }
               />

This ensures we adhere to the Rules of Hooks and correctly label the first column of a new browsing month.

🧹 Nitpick comments (18)
packages/features/calendars/DatePicker.tsx (1)

287-301: Use Array.isArray and simplify boolean.

Minor safety/readability.

-    if (!hasDatesLoaded || selected instanceof Array) {
+    if (!hasDatesLoaded || Array.isArray(selected)) {
       return;
     }
@@
-      const shouldOmitUpdatingParams = selected?.isValid() ? false : true; // In case a date is selected and it is not available, then we have to change search params
+      // If a (valid) date was previously selected, update params; otherwise, omit
+      const shouldOmitUpdatingParams = !selected?.isValid();
packages/features/troubleshooter/components/LargeCalendar.tsx (1)

79-85: Localize user-visible strings (“Busy”, “Date Override”).

Per guidelines, wrap with t().

+import { useLocale } from "@calcom/lib/hooks/useLocale";
@@
-  const events = useMemo(() => {
+  const { t } = useLocale();
+  const events = useMemo(() => {
@@
-        title: event.title ?? `Busy`,
+        title: event.title ?? t("busy"),
@@
-          title: "Date Override",
+          title: t("date_override"),

Also applies to: 111-116

packages/features/schedules/hooks/useSlotsForDate.ts (1)

35-50: Avoid JSON.stringify in effect deps.

Serializing on every render is costly; prefer stable deps.

-  }, [JSON.stringify(dates), JSON.stringify(slots)]);
+  }, [dates.join('|'), slots ? Object.keys(slots).join('|') : 'no-slots']);

Or switch to a deep-compare hook where available.

packages/features/embed/Embed.tsx (3)

216-219: Open external link with noopener/noreferrer.

Prevents opener-based attacks and removes referrer leakage.

-                window.open("https://cal.com/help/routing/headless-routing", "_blank");
+                window.open("https://cal.com/help/routing/headless-routing", "_blank", "noopener,noreferrer");

959-960: Localize hardcoded UI strings.

Several user-facing strings bypass t(). Please wrap them for i18n.

Example:

-                        <div className="text-default mb-[9px] text-sm">Window sizing</div>
+                        <div className="text-default mb-[9px] text-sm">{t("window_sizing")}</div>
...
-                      <div className="mb-2 text-sm">Button text</div>
+                      <div className="mb-2 text-sm">{t("button_text")}</div>
...
-                      <div className="text-default my-2 text-sm">Display calendar icon</div>
+                      <div className="text-default my-2 text-sm">{t("display_calendar_icon")}</div>
...
-                      <div className="mb-2">Position of button</div>
+                      <div className="mb-2">{t("position_of_button")}</div>
...
-                        <div className="whitespace-nowrap">Button color</div>
+                        <div className="whitespace-nowrap">{t("button_color")}</div>
-                        <div className="whitespace-nowrap">Text color</div>
+                        <div className="whitespace-nowrap">{t("text_color")}</div>

Also applies to: 1012-1031, 1038-1053, 1059-1075, 1077-1119


861-876: Use modern Clipboard API with fallback.

execCommand is deprecated. Prefer navigator.clipboard with a fallback for non-secure contexts.

-  const handleCopyEmailText = () => {
+  const handleCopyEmailText = async () => {
     const contentElement = emailContentRef.current;
     if (contentElement !== null) {
-      const range = document.createRange();
-      range.selectNode(contentElement);
-      const selection = window.getSelection();
-      if (selection) {
-        selection.removeAllRanges();
-        selection.addRange(range);
-        document.execCommand("copy");
-        selection.removeAllRanges();
-      }
+      const html = contentElement.outerHTML;
+      try {
+        if ("clipboard" in navigator && "write" in navigator.clipboard && "ClipboardItem" in window) {
+          const item = new ClipboardItem({ "text/html": new Blob([html], { type: "text/html" }) });
+          await (navigator.clipboard as any).write([item]);
+        } else {
+          const range = document.createRange();
+          range.selectNode(contentElement);
+          const selection = window.getSelection();
+          if (selection) {
+            selection.removeAllRanges();
+            selection.addRange(range);
+            document.execCommand("copy");
+            selection.removeAllRanges();
+          }
+        }
+      } catch {
+        // noop: rely on fallback or silent failure
+      }
 
       showToast(t("code_copied"), "success");
     }
   };
packages/features/bookings/Booker/components/LargeCalendar.tsx (1)

42-51: Minor: avoid duplicate dayjs conversions inside the loop.

Cache dayjs(time) to reuse for start/end construction.

-      availableTimeslots[day] = schedule.slots[day].map((slot) => {
-        const { time, ...rest } = slot;
-        return {
-          start: dayjs(time).toDate(),
-          end: dayjs(time).add(eventDuration, "minutes").toDate(),
-          ...rest,
-        };
-      });
+      availableTimeslots[day] = schedule.slots[day].map(({ time, ...rest }) => {
+        const d = dayjs(time);
+        return { start: d.toDate(), end: d.add(eventDuration, "minutes").toDate(), ...rest };
+      });
packages/features/bookings/Booker/hooks/useEvent.ts (2)

6-6: Name exported types in PascalCase.

Prefer UseEventReturnType for consistency with TS type naming.

-export type useEventReturnType = ReturnType<typeof useEvent>;
+export type UseEventReturnType = ReturnType<typeof useEvent>;

22-34: Consider cache tuning to reduce redundant refetches.

Add staleTime and keepPreviousData to smooth navigation and avoid unnecessary network calls.

   const event = trpc.viewer.public.event.useQuery(
@@
-    {
-      refetchOnWindowFocus: false,
-      enabled: !props?.disabled && Boolean(username) && Boolean(eventSlug),
-    }
+    {
+      refetchOnWindowFocus: false,
+      enabled: !props?.disabled && Boolean(username) && Boolean(eventSlug),
+      staleTime: 30_000,
+      keepPreviousData: true,
+    }
   );
packages/features/bookings/Booker/components/AvailableTimeSlots.tsx (1)

127-138: Guard invalidate behind existence to avoid no-op calls.

Already behind a flag, but also short-circuit when schedule has no invalidate.

-      if (PUBLIC_INVALIDATE_AVAILABLE_SLOTS_ON_BOOKING_FORM) {
+      if (PUBLIC_INVALIDATE_AVAILABLE_SLOTS_ON_BOOKING_FORM && schedule?.invalidate) {
         // Ensures that user has latest available slots when they are about to confirm the booking by filling up the details
         schedule?.invalidate();
       }
packages/features/schedules/lib/use-schedule/getTimesForSchedule.test.ts (3)

79-79: Remove stray console.log from tests

Unnecessary logging adds noise and can hide flakes. Please remove.

-    console.log(new Date().toISOString());

106-110: Avoid redundant toString() on string

toLocalIsoWithOffset already returns a string. The extra .toString() is redundant.

-      expect(toLocalIsoWithOffset(new Date(startTime)).toString().startsWith("2025-08-23T00:00:00")).toBe(
+      expect(toLocalIsoWithOffset(new Date(startTime)).startsWith("2025-08-23T00:00:00")).toBe(
         true
       );
-      expect(toLocalIsoWithOffset(new Date(endTime)).toString().startsWith("2025-08-30T00:00:00")).toBe(true);
+      expect(toLocalIsoWithOffset(new Date(endTime)).startsWith("2025-08-30T00:00:00")).toBe(true);

29-36: Add a test for dayCount <= 0 and invalid month format

Two gaps to harden behavior:

  • When dayCount <= 0, assert we return full-month [start, end].
  • If an invalid month string is passed, decide whether to throw or fallback; add a test to lock this in once implementation is updated (see comments in getTimesForSchedule.ts).

I can add targeted test cases once you confirm desired behavior (throw vs fallback).

Also applies to: 52-66, 99-111, 113-127

packages/features/schedules/lib/use-schedule/getTimesForSchedule.ts (3)

10-13: Validate month string to prevent accidental NaN dates

parseMonth assumes a well-formed "YYYY-MM". Add a minimal guard to reduce surprises downstream.

-const parseMonth = (monthString: string) => {
-  const [year, month] = monthString.split("-").map(Number);
-  return new Date(year, month - 1);
-};
+const parseMonth = (monthString: string) => {
+  const match = /^(\d{4})-(\d{2})$/.exec(monthString);
+  if (!match) throw new Error(`Invalid month format: ${monthString}`);
+  const year = Number(match[1]);
+  const month = Number(match[2]);
+  if (month < 1 || month > 12) throw new Error(`Invalid month value: ${month}`);
+  return new Date(year, month - 1);
+};

17-25: Normalize non-positive monthCount

A non-positive monthCount would produce a backward or zero-length window. Consider clamping to at least 1.

 export const getTimesForSchedule = ({
   month,
-  monthCount = 1,
+  monthCount = 1,
   selectedDate,
   dayCount,
 }: GetTimesForScheduleProps): [string, string] => {
+  monthCount = Math.max(1, monthCount ?? 1);

21-26: Timezone semantics: compute boundaries in the intended booking timezone (future enhancement)

The current logic derives month/day boundaries from the runtime local timezone. If the user-selected timezone differs from the runtime, this can show confusing start dates (e.g., “July 31/July 1” while browsing August). Long-term, consider accepting a timezone parameter and using date-fns-tz to compute startOfMonth/startOfDay in that zone before converting to ISO.

If you want, I can propose a backward-compatible variant of getTimesForSchedule that accepts an optional IANA timezone and adds date-fns-tz with minimal surface area.

Also applies to: 34-49

packages/features/schedules/hooks/useSchedule.ts (1)

76-101: Consider stabilizing the query input object to reduce refetch noise

input is recreated on every render (new arrays/objects from searchParams, usernameList). If you observe unexpected refetches, memoize derived pieces (e.g., routedTeamMemberIds) or split stable/unstable parts.

packages/features/bookings/Booker/hooks/useScheduleForEvent.ts (1)

71-72: Minor style nit: use property shorthand

Tiny cleanup opportunity.

-    useApiV2: useApiV2,
+    useApiV2,
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b734d79 and 0d6d0be.

📒 Files selected for processing (17)
  • packages/features/bookings/Booker/Booker.tsx (1 hunks)
  • packages/features/bookings/Booker/components/AvailableTimeSlots.tsx (1 hunks)
  • packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx (1 hunks)
  • packages/features/bookings/Booker/components/DatePicker.tsx (2 hunks)
  • packages/features/bookings/Booker/components/LargeCalendar.tsx (2 hunks)
  • packages/features/bookings/Booker/hooks/useEvent.ts (1 hunks)
  • packages/features/bookings/Booker/hooks/useScheduleForEvent.ts (1 hunks)
  • packages/features/bookings/Booker/types.ts (2 hunks)
  • packages/features/calendars/DatePicker.tsx (5 hunks)
  • packages/features/embed/Embed.tsx (1 hunks)
  • packages/features/schedules/hooks/useNonEmptyScheduleDays.ts (1 hunks)
  • packages/features/schedules/hooks/useSchedule.ts (4 hunks)
  • packages/features/schedules/hooks/useSlotsForDate.ts (1 hunks)
  • packages/features/schedules/lib/use-schedule/getTimesForSchedule.test.ts (1 hunks)
  • packages/features/schedules/lib/use-schedule/getTimesForSchedule.ts (1 hunks)
  • packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts (0 hunks)
  • packages/features/troubleshooter/components/LargeCalendar.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/features/schedules/hooks/useNonEmptyScheduleDays.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/features/bookings/Booker/components/DatePicker.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/features/schedules/hooks/useSlotsForDate.ts
  • packages/features/schedules/lib/use-schedule/getTimesForSchedule.ts
  • packages/features/schedules/lib/use-schedule/getTimesForSchedule.test.ts
  • packages/features/bookings/Booker/types.ts
  • packages/features/bookings/Booker/hooks/useEvent.ts
  • packages/features/schedules/hooks/useSchedule.ts
  • packages/features/bookings/Booker/hooks/useScheduleForEvent.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/features/schedules/hooks/useSlotsForDate.ts
  • packages/features/troubleshooter/components/LargeCalendar.tsx
  • packages/features/bookings/Booker/Booker.tsx
  • packages/features/bookings/Booker/components/LargeCalendar.tsx
  • packages/features/schedules/lib/use-schedule/getTimesForSchedule.ts
  • packages/features/schedules/lib/use-schedule/getTimesForSchedule.test.ts
  • packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx
  • packages/features/bookings/Booker/types.ts
  • packages/features/bookings/Booker/hooks/useEvent.ts
  • packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
  • packages/features/embed/Embed.tsx
  • packages/features/schedules/hooks/useSchedule.ts
  • packages/features/calendars/DatePicker.tsx
  • packages/features/bookings/Booker/hooks/useScheduleForEvent.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/features/schedules/hooks/useSlotsForDate.ts
  • packages/features/troubleshooter/components/LargeCalendar.tsx
  • packages/features/bookings/Booker/Booker.tsx
  • packages/features/bookings/Booker/components/LargeCalendar.tsx
  • packages/features/schedules/lib/use-schedule/getTimesForSchedule.ts
  • packages/features/schedules/lib/use-schedule/getTimesForSchedule.test.ts
  • packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx
  • packages/features/bookings/Booker/types.ts
  • packages/features/bookings/Booker/hooks/useEvent.ts
  • packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
  • packages/features/embed/Embed.tsx
  • packages/features/schedules/hooks/useSchedule.ts
  • packages/features/calendars/DatePicker.tsx
  • packages/features/bookings/Booker/hooks/useScheduleForEvent.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/features/troubleshooter/components/LargeCalendar.tsx
  • packages/features/bookings/Booker/Booker.tsx
  • packages/features/bookings/Booker/components/LargeCalendar.tsx
  • packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx
  • packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
  • packages/features/embed/Embed.tsx
  • packages/features/calendars/DatePicker.tsx
🧠 Learnings (1)
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • packages/features/bookings/Booker/hooks/useScheduleForEvent.ts
🧬 Code graph analysis (7)
packages/features/bookings/Booker/components/LargeCalendar.tsx (1)
packages/features/bookings/Booker/hooks/useScheduleForEvent.ts (1)
  • UseScheduleForEventReturnType (9-9)
packages/features/schedules/lib/use-schedule/getTimesForSchedule.test.ts (1)
packages/features/schedules/lib/use-schedule/getTimesForSchedule.ts (1)
  • getTimesForSchedule (15-49)
packages/features/bookings/Booker/types.ts (1)
packages/features/bookings/Booker/hooks/useScheduleForEvent.ts (1)
  • UseScheduleForEventReturnType (9-9)
packages/features/bookings/Booker/hooks/useEvent.ts (3)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
  • useBookerStoreContext (24-35)
packages/trpc/server/routers/publicViewer/procedures/event.ts (1)
  • event (8-12)
apps/web/app/_trpc/trpc.ts (1)
  • trpc (7-7)
packages/features/bookings/Booker/components/AvailableTimeSlots.tsx (1)
packages/features/bookings/Booker/hooks/useScheduleForEvent.ts (1)
  • UseScheduleForEventReturnType (9-9)
packages/features/schedules/hooks/useSchedule.ts (1)
packages/features/schedules/lib/use-schedule/getTimesForSchedule.ts (1)
  • getTimesForSchedule (15-49)
packages/features/calendars/DatePicker.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
  • useBookerStoreContext (24-35)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (12)
packages/features/calendars/DatePicker.tsx (2)

305-306: Effect dependencies may miss updates when selection changes.

If selected changes (e.g., via URL param) after load, the effect won’t re-run. Confirm intent; otherwise include selected and a stable marker for daysToRenderForTheMonth.

Suggested adjustment (ensure no loops first):

-useEffect(useHandleInitialDateSelection, [hasDatesLoaded, month]);
+useEffect(useHandleInitialDateSelection, [hasDatesLoaded, month, Array.isArray(selected) ? 'multi' : selected?.format?.('YYYY-MM-DD')]);

399-403: Month label vs month key confusion.

Here month is a localized label (“August”), but elsewhere “month” often denotes a key like “YYYY-MM”. Since Days forwards month to NoAvailabilityDialog, confirm it expects a label, not a key. If it expects a key, pass browsingDate.format("YYYY-MM") instead.

Option if a key is required:

-const month = browsingDate
+const monthLabel = browsingDate
   ? new Intl.DateTimeFormat(i18n.language, { month: "long" }).format(
       new Date(browsingDate.year(), browsingDate.month())
     )
   : null;
+const month = browsingDate.format("YYYY-MM"); // key

…and use monthLabel for the header display.

packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx (1)

13-13: No lingering ../../utils/event imports detected.

I’ve scanned all .ts and .tsx files under packages/features/bookings for any references to the old ../../utils/event path and confirmed there are none. The hook import path migration is clean—no further action needed.

packages/features/troubleshooter/components/LargeCalendar.tsx (1)

5-5: Updated imports match the new hooks layout.

Also applies to: 8-8

packages/features/schedules/hooks/useSlotsForDate.ts (1)

3-3: Type import path update looks correct.

packages/features/bookings/Booker/components/LargeCalendar.tsx (1)

11-12: Type import/usage migration is correct.

Switch to UseScheduleForEventReturnType aligns with the new hook API.

Also applies to: 21-23

packages/features/bookings/Booker/types.ts (1)

15-16: Consistent public type usage for schedule.

Good move to import and use UseScheduleForEventReturnType.

Also applies to: 130-131

packages/features/bookings/Booker/components/AvailableTimeSlots.tsx (1)

8-10: Hook/type migration looks correct.

New hooks and UseScheduleForEventReturnType integration are consistent.

Also applies to: 17-24

packages/features/schedules/hooks/useSchedule.ts (2)

6-6: LGTM: deduplicates schedule fetching paths via enabled flags

Switch to getTimesForSchedule, explicit monthCount default, and gating of TRPC vs API V2 queries look correct and should prevent duplicate getSchedule calls.

Also applies to: 22-23, 40-41, 51-56, 114-121, 136-140


114-121: Duplicate schedule fetch toggles verified – no overlapping queries

  • In useSchedule.ts the only schedule-fetch call sites are:
    • useApiV2AvailableSlots({ …, enabled: isCallingApiV2Slots })
    • trpc.viewer.slots.getSchedule.useQuery(input, { enabled: options.enabled && !isCallingApiV2Slots })
  • The isCallingApiV2Slots flag and its negation ensure that exactly one of these hooks is active at any time, eliminating any possibility of duplicate network calls.
  • There are no prefetchNextMonth references in useSchedule.ts; the only occurrences of that identifier live in the separate booker wrapper files and do not impact this hook.

No further action needed here.

packages/features/bookings/Booker/hooks/useScheduleForEvent.ts (2)

4-4: LGTM: wrapper delegates cleanly to useSchedule and exposes a stable return type

Named export, new UseScheduleForEventReturnType, and removal of legacy knobs align with the refactor.

Also applies to: 9-10, 20-33, 57-72, 74-82


9-10: No legacy useScheduleForEventReturnType imports detected

I ran the provided grep check:

rg -n -C2 --glob '!**/node_modules/**' -e '\buseScheduleForEventReturnType\b'

and confirmed there are zero occurrences of the old alias. All consumers are now importing the new UseScheduleForEventReturnType.

import useSkipConfirmStep from "@calcom/features/bookings/Booker/components/hooks/useSkipConfirmStep";
import { getQueryParam } from "@calcom/features/bookings/Booker/utils/query-param";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules/lib/use-schedule/useNonEmptyScheduleDays";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules/hooks/useNonEmptyScheduleDays";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Import path migration looks good; check for stragglers.

The new hooks path aligns with the PR direction.

Run to find any remaining imports of the old path:


🏁 Script executed:

#!/bin/bash
fd -t f -e ts -e tsx | xargs rg -nP '@calcom/features/schedules/lib/use-schedule/useNonEmptyScheduleDays'

Length of output: 105


🏁 Script executed:

#!/bin/bash
# Search for any remaining imports under the old schedules lib path
fd -t f -e ts -e tsx | xargs rg -nP '@calcom/features/schedules/lib'

Length of output: 790


🏁 Script executed:

#!/bin/bash
# Locate where 'Slot' type is declared in the new hooks directory
fd -t f -e ts packages/features/schedules/hooks | xargs rg -nP 'export (type|interface) Slot\b' -n

Length of output: 745


Remaining import stragglers detected – please migrate these to the new hooks path

The import-path migration for useNonEmptyScheduleDays is correct, but the following files still reference the old
@calcom/features/schedules/lib/use-schedule/* paths. Update each of these to the new hooks location (@calcom/features/schedules/hooks/...):

• packages/features/bookings/Booker/components/AvailableTimeSlots.tsx (line 10)
• packages/features/bookings/components/AvailableTimes.tsx (line 12)
• packages/features/schedules/hooks/useSchedule.ts (line 6)
• packages/platform/atoms/booker/BookerPlatformWrapper.tsx (line 20)
• packages/features/embed/Embed.tsx (line 27)

For example, replace:

-import type { Slot } from "@calcom/features/schedules/lib/use-schedule/types";
+import type { Slot } from "@calcom/features/schedules/hooks/useSchedule";

and

-import { getTimesForSchedule } from "@calcom/features/schedules/lib/use-schedule/getTimesForSchedule";
+import { getTimesForSchedule } from "@calcom/features/schedules/hooks/useSchedule";

Once all of these imports are updated, the old path stragglers will be cleared and this migration can be approved.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/features/bookings/Booker/Booker.tsx around line 16 and in the listed
files (packages/features/bookings/Booker/components/AvailableTimeSlots.tsx line
10, packages/features/bookings/components/AvailableTimes.tsx line 12,
packages/features/schedules/hooks/useSchedule.ts line 6,
packages/platform/atoms/booker/BookerPlatformWrapper.tsx line 20,
packages/features/embed/Embed.tsx line 27) there are remaining imports
referencing the old path @calcom/features/schedules/lib/use-schedule/*; update
each import to the new hooks path @calcom/features/schedules/hooks/<hookName>
(e.g., replace imports from
@calcom/features/schedules/lib/use-schedule/useNonEmptyScheduleDays with
@calcom/features/schedules/hooks/useNonEmptyScheduleDays), ensure named/default
imports match the exported hook names, run typecheck/build to confirm
resolution, and remove any leftover old-path references.

Comment on lines +87 to 88
const [month] = useBookerStoreContext((state) => [state.month], shallow);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Day now hard-depends on Booker store — breaks consumers outside Booker.

Day calls useBookerStoreContext, which throws if no provider is mounted. DatePicker/Day is used outside the Booker flow; this will crash those screens. Pass a month key via props and decouple Day from the Booker store.

Apply this refactor:

@@
 const Day = ({
   date,
   active,
   disabled,
   away,
   emoji,
   customClassName,
   ...props
 }: JSX.IntrinsicElements["button"] & {
   active: boolean;
   date: Dayjs;
   away?: boolean;
   emoji?: string | null;
   customClassName?: {
     dayContainer?: string;
     dayActive?: string;
   };
+  // Key for comparing whether a given day belongs to the currently browsed month (YYYY-MM)
+  monthKey?: string | null;
 }) => {
   const { t } = useLocale();
   const enabledDateButtonEmbedStyles = useEmbedStyles("enabledDateButton");
   const disabledDateButtonEmbedStyles = useEmbedStyles("disabledDateButton");
-  const [month] = useBookerStoreContext((state) => [state.month], shallow);
+  // If no monthKey provided, default to the day's own month so comparisons safely no-op
+  const monthKey = props.monthKey ?? date.format("YYYY-MM");
@@
-  const content =
-    !disabled && month !== date.format("YYYY-MM") ? (
+  const content =
+    !disabled && monthKey !== date.format("YYYY-MM") ? (
       <Tooltip content={date.format("MMMM")}>{buttonContent}</Tooltip>
     ) : (
       buttonContent
     );
@@
-      {month !== date.format("YYYY-MM") && date.date() === 1 && (
+      {monthKey !== date.format("YYYY-MM") && date.date() === 1 && (
         <div
           className={classNames(
             "absolute top-0 z-10 mx-auto w-fit rounded-full font-semibold uppercase tracking-wide",
             active ? "text-white" : "text-default",
             disabled && "bg-emphasis"
           )}
           style={{
             fontSize: "10px",
             lineHeight: "13px",
             padding: disabled ? "0 3px" : "3px 3px 3px 4px",
           }}>
           {date.format("MMM")}
         </div>
       )}

And pass the key from Days:

@@
-            <DayComponent
+            <DayComponent
+              monthKey={browsingDate.format("YYYY-MM")}
               customClassName={{
                 dayContainer: customClassName?.datePickerDate,
                 dayActive: customClassName?.datePickerDateActive,
               }}
               date={day}

Also applies to: 123-129, 132-146

Comment on lines +19 to 21
import { useEvent } from "@calcom/features/bookings/Booker/hooks/useEvent";
import { useScheduleForEvent } from "@calcom/features/bookings/Booker/hooks/useScheduleForEvent";
import { useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Import path migration looks good.

Hooks repointing aligns with the new module layout. Please ensure all legacy imports are removed across the repo.

Run to confirm no remaining legacy imports:

Also applies to: 25-27


🏁 Script executed:

#!/bin/bash
rg -nP '(utils/event|schedules/lib/use-schedule/)' --type=ts --type=tsx -C2

Length of output: 105


🏁 Script executed:

#!/bin/bash
# Re-run legacy-import search without relying on ripgrep’s --type filter
rg -nP '(utils/event|schedules/lib/use-schedule/)' -g '*.ts' -g '*.tsx' -C2

Length of output: 3625


Legacy import references detected – migration incomplete

The grep search uncovered several remaining imports from the old schedules/lib/use-schedule paths. Please update these to the new hooks/modules layout:

  • packages/features/schedules/hooks/useSchedule.ts
    • Line 6: import { getTimesForSchedule } from "@calcom/features/schedules/lib/use-schedule/getTimesForSchedule";

  • packages/features/embed/Embed.tsx
    • Line 27: import type { Slot } from "@calcom/features/schedules/lib/use-schedule/types";

  • packages/platform/atoms/booker/BookerPlatformWrapper.tsx
    • Line 20: import { getTimesForSchedule } from "@calcom/features/schedules/lib/use-schedule/getTimesForSchedule";

  • packages/features/bookings/components/AvailableTimes.tsx
    • Line 12: import type { Slot } from "@calcom/features/schedules/lib/use-schedule/types";

  • packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
    • Line 10: import type { Slot } from "@calcom/features/schedules/lib/use-schedule/types";

Please replace these legacy imports with their new equivalents under @calcom/features/schedules/hooks (or the updated module paths). Once updated, re-run the grep check to confirm all legacy references are removed.

🤖 Prompt for AI Agents
In packages/features/embed/Embed.tsx around lines 19–27, there's a legacy type
import from "@calcom/features/schedules/lib/use-schedule/types" (Slot); replace
that legacy import with the new hooks module path under
"@calcom/features/schedules/hooks" (import the Slot type from the updated module
— e.g. the new types export in the schedules/hooks/useSchedule module or its
types file), update any other references in this file to use the new
name/location, and then re-run the grep check to confirm no remaining legacy
schedules/lib/use-schedule imports remain.

Comment on lines +44 to +49
const expectedStartTime = toLocalIsoWithOffset(new Date(2025, 9));
const expectedEndTime = new Date(2025, 11);

expect(toLocalIsoWithOffset(new Date(startTime))).toBe(expectedStartTime);
// expect(toLocalIsoWithOffset(new Date(endTime))).toBe(expectedEndTime);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect expected end month and re-enable the assertion

For a single-month selection of "2025-10", end should be 2025-11-01 (not 2025-12-01). Let's correct the expected value and restore the assertion.

-      const expectedStartTime = toLocalIsoWithOffset(new Date(2025, 9));
-      const expectedEndTime = new Date(2025, 11);
+      const expectedStartTime = toLocalIsoWithOffset(new Date(2025, 9));   // 2025-10-01 local
+      const expectedEndTime = toLocalIsoWithOffset(new Date(2025, 10));    // 2025-11-01 local

       expect(toLocalIsoWithOffset(new Date(startTime))).toBe(expectedStartTime);
-      // expect(toLocalIsoWithOffset(new Date(endTime))).toBe(expectedEndTime);
+      expect(toLocalIsoWithOffset(new Date(endTime))).toBe(expectedEndTime);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const expectedStartTime = toLocalIsoWithOffset(new Date(2025, 9));
const expectedEndTime = new Date(2025, 11);
expect(toLocalIsoWithOffset(new Date(startTime))).toBe(expectedStartTime);
// expect(toLocalIsoWithOffset(new Date(endTime))).toBe(expectedEndTime);
}
const expectedStartTime = toLocalIsoWithOffset(new Date(2025, 9)); // 2025-10-01 local
const expectedEndTime = toLocalIsoWithOffset(new Date(2025, 10)); // 2025-11-01 local
expect(toLocalIsoWithOffset(new Date(startTime))).toBe(expectedStartTime);
expect(toLocalIsoWithOffset(new Date(endTime))).toBe(expectedEndTime);
🤖 Prompt for AI Agents
In packages/features/schedules/lib/use-schedule/getTimesForSchedule.test.ts
around lines 44-49, the expected end month is off-by-one (uses new Date(2025,
11)) and the assertion is commented out; change the expectedEndTime to new
Date(2025, 10) (representing 2025-11-01) and re-enable the commented expect line
so it asserts toLocalIsoWithOffset(new Date(endTime)) equals the corrected
expectedEndTime.

Comment on lines +28 to +33
// Why the non-null assertions? All of these arguments are checked in the enabled condition,
// and the query will not run if they are null. However, the check in `enabled` does
// no satisfy typescript.
let startTime;
let endTime;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove stale comment and type start/end to avoid implicit any

The comment refers to non-null assertions that no longer exist. Also, startTime/endTime are implicitly any; type them as Date for safety.

-  // Why the non-null assertions? All of these arguments are checked in the enabled condition,
-  // and the query will not run if they are null. However, the check in `enabled` does
-  // no satisfy typescript.
-  let startTime;
-  let endTime;
+  let startTime: Date;
+  let endTime: Date;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Why the non-null assertions? All of these arguments are checked in the enabled condition,
// and the query will not run if they are null. However, the check in `enabled` does
// no satisfy typescript.
let startTime;
let endTime;
let startTime: Date;
let endTime: Date;
🤖 Prompt for AI Agents
In packages/features/schedules/lib/use-schedule/getTimesForSchedule.ts around
lines 28 to 33, remove the stale comment about non-null assertions and replace
the implicit-any declarations for startTime and endTime with explicit typing
(e.g. declare them as Date | undefined or Date depending on usage) so TypeScript
no longer infers any; initialize or narrow their types where they are assigned
so the compiler understands they are valid Dates and update any downstream usage
to handle the typed values.

Copy link
Member

@CarinaWolli CarinaWolli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still seeing the loading issues for the new month (when it's the end of the month): https://www.loom.com/share/bebb057993cd4d57ae5f0cd9ec3df5a0

@github-actions github-actions bot marked this pull request as draft September 2, 2025 08:30
@github-actions
Copy link
Contributor

This PR is being marked as stale due to inactivity.

@github-actions github-actions bot added the Stale label Sep 17, 2025
@emrysal emrysal closed this Sep 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bookings area: bookings, availability, timezones, double booking 🐛 bug Something isn't working core area: core, team members only foundation ready-for-e2e Stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants