-
Notifications
You must be signed in to change notification settings - Fork 10.3k
fix: multiple widgets for Booker
atom
#22925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Hey there and thank you for opening this pull request! 👋🏼 We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted. Details:
|
WalkthroughAdds a React context provider for the Booker zustand store (BookerStoreProvider) plus selector and initializer hooks (useBookerStoreContext, useInitializeBookerStoreContext) and a factory-based store (createBookerStore) while preserving a default useBookerStore. Migrates most components, hooks, platform wrappers, embeds, and tests from the module-level store hook to the new context hook. Centralizes selectedTimeslot flow from AvailableTimeSlots into BookEventForm and EventMeta. Adds test utilities to render components with a mocked BookerStore context. Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 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 unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎ |
Booker
atom
…t tests - Create reusable test utility in test-utils.tsx with comprehensive mock store - Update Booker.test.tsx to use context-based testing approach - Fix DatePicker tests in both bookings and calendars packages - Simulate auto-advance behavior for month navigation tests - All 14 previously failing tests now pass Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🔭 Outside diff range comments (3)
packages/features/bookings/components/AvailableTimes.tsx (1)
271-274
: Bug: empty slots cause crash due to Array.every on empty arrayArray.every returns true on empty arrays; when slots is empty, this returns OOOSlot spreading undefined (runtime error).
Apply this diff:
- const oooAllDay = slots.every((slot) => slot.away); + const oooAllDay = slots.length > 0 && slots.every((slot) => slot.away);This preserves the later !slots.length UI while preventing an early return with undefined props.
packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSwitch.tsx (2)
63-73
: Add accessible name to icon-only ButtonIcon-only buttons need an accessible label. Please add an
aria-label
(localized).Apply this diff:
<Button size="base" data-testid="overlay-calendar-settings-button" variant="icon" color="secondary" StartIcon="settings" + aria-label={t("settings")} onClick={() => { setCalendarSettingsOverlay(true); }} />
19-22
: Fix global overlay store – isolate per Booker instanceI confirmed that
useOverlayCalendarStore
is created at the module level in
packages/features/bookings/Booker/components/OverlayCalendar/store.ts
via Zustand’screate()
. This means all Booker widgets on the same page share the same overlay state:
- Global store export (
useOverlayCalendarStore = create(...)
)- No enclosing Provider/context to scope it per instance
To prevent cross-instance bleed when multiple widgets are rendered:
• Introduce an instance-scoped store factory and provider, e.g.:
- Create
createOverlayCalendarStore()
- Implement
OverlayCalendarStoreProvider
(mirroringBookerStoreProvider
)- Expose a
useOverlayCalendarStoreContext()
hook that pulls from React context• Update all imports of
useOverlayCalendarStore
in:
OverlayCalendarSwitch.tsx
LargeCalendar.tsx
- any hooks under
Booker/components/hooks/
to use the new context-scoped hook.
• Alternatively, fold overlay modal state into the existing Booker store if that better fits the architecture.
♻️ Duplicate comments (1)
packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx (1)
30-31
: The previous review comment is still valid - add enabled guard to prevent null requests.The hook
useEventTypeById(eventId)
is called witheventId
that can benull/undefined
during initial render, causing requests to/eventTypes/null
. This issue persists with the context-based store migration.Apply this fix to prevent unnecessary API calls:
- const { data } = useEventTypeById(eventId); + const { data } = useEventTypeById(eventId, { + enabled: Boolean(eventId), + });
🧹 Nitpick comments (12)
packages/features/bookings/Booker/components/hooks/useBookerLayout.ts (2)
49-61
: Avoid shadowing outer “layout” variable inside effectThe local const layout in this effect shadows the outer layout, making the code harder to read and reason about. Rename it to layoutParam.
//setting layout from query param useEffect(() => { - const layout = getQueryParam("layout") as BookerLayouts; + const layoutParam = getQueryParam("layout") as BookerLayouts; if ( !isMobile && !isEmbed && - validateLayout(layout) && + validateLayout(layoutParam) && bookerLayouts?.enabledLayouts?.length && - layout !== _layout + layoutParam !== _layout ) { - const validLayout = bookerLayouts.enabledLayouts.find((userLayout) => userLayout === layout); + const validLayout = bookerLayouts.enabledLayouts.find((userLayout) => userLayout === layoutParam); validLayout && setLayout(validLayout); } }, [bookerLayouts, setLayout, _layout, isEmbed, isMobile]);
21-21
: Naming nit: prefer storeLayout over _layoutUnderscore prefix is easy to miss. storeLayout better communicates intent and improves readability across this hook.
- const [_layout, setLayout] = useBookerStoreContext((state) => [state.layout, state.setLayout], shallow); + const [storeLayout, setLayout] = useBookerStoreContext((state) => [state.layout, state.setLayout], shallow); @@ - const layout = isEmbed ? (isMobile ? "mobile" : validateLayout(embedUiConfig.layout) || _layout) : _layout; + const layout = isEmbed ? (isMobile ? "mobile" : validateLayout(embedUiConfig.layout) || storeLayout) : storeLayout; @@ - layout !== _layout + layout !== storeLayout ) { - const validLayout = bookerLayouts.enabledLayouts.find((userLayout) => userLayout === layout); - validLayout && setLayout(validLayout); + const validLayout = bookerLayouts.enabledLayouts.find((userLayout) => userLayout === layout); + validLayout && setLayout(validLayout); } - }, [bookerLayouts, setLayout, _layout, isEmbed, isMobile]); + }, [bookerLayouts, setLayout, storeLayout, isEmbed, isMobile]);Also applies to: 28-28, 56-61
packages/features/bookings/components/AvailableTimesHeader.tsx (1)
31-31
: Simplify selector; shallow array is unnecessary for a primitivelayout is a primitive; using an array with shallow equality adds overhead without benefit. Select the primitive directly.
Apply this diff:
-import { shallow } from "zustand/shallow"; @@ - const [layout] = useBookerStoreContext((state) => [state.layout], shallow); + const layout = useBookerStoreContext((state) => state.layout);packages/features/bookings/components/AvailableTimes.tsx (5)
115-116
: Reduce re-renders: consolidate store selections with shallowYou’re creating three subscriptions. Combine them into a single selector with shallow to avoid redundant renders.
Apply this diff:
+import { shallow } from "zustand/shallow"; @@ - const bookingData = useBookerStoreContext((state) => state.bookingData); - const layout = useBookerStoreContext((state) => state.layout); + const [bookingData, layout, selectedTimeslot] = useBookerStoreContext( + (state) => [state.bookingData, state.layout, state.selectedTimeslot], + shallow + ); @@ - const selectedTimeslot = useBookerStoreContext((state) => state.selectedTimeslot);Also applies to: 130-130
247-249
: Localize user-facing text per guidelines"Busy" should use t() for localization.
Apply this diff:
- <p>Busy</p> + <p>{t("busy")}</p>
208-210
: Prefer enum over string literal for layout checksUsing the typed enum improves safety against typos and future refactors.
Apply this diff and add the import:
+import { BookerLayouts } from "@calcom/prisma/zod-utils"; @@ - variant={layout === "column_view" ? "icon" : "button"} - StartIcon={layout === "column_view" ? "chevron-right" : undefined} + variant={layout === BookerLayouts.COLUMN_VIEW ? "icon" : "button"} + StartIcon={layout === BookerLayouts.COLUMN_VIEW ? "chevron-right" : undefined}
283-291
: Nit: standardize test id attributeUse data-testid (lowercase) consistently to match testing conventions and avoid selector mismatches.
Apply this diff:
- <div - data-testId="no-slots-available" + <div + data-testid="no-slots-available"
118-129
: Watch Day.js usage in hot pathsThis block runs per SlotItem render. If slot counts are high, consider computing nowDate/usersTimezoneDate/offset once in AvailableTimes and passing down, or memoizing where possible.
If you see perf regressions with many slots, hoist:
- const nowDate = dayjs();
- const usersTimezoneDate = nowDate.tz(timezone);
- const offset = (usersTimezoneDate.utcOffset() - nowDate.utcOffset()) / 60;
…to AvailableTimes and pass offset via props.
packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSwitch.tsx (1)
17-25
: Keep Switch controlled: defaultenabled
to a booleanWhen
enabled
isundefined
,checked={switchEnabled}
can make the Switch uncontrolled. Defaulting tofalse
prevents React warnings and ensures consistent behavior.Apply this diff:
-export function OverlayCalendarSwitch({ enabled, hasSession, onStateChange }: OverlayCalendarSwitchProps) { +export function OverlayCalendarSwitch({ enabled = false, hasSession, onStateChange }: OverlayCalendarSwitchProps) { const { t } = useLocale(); const setContinueWithProvider = useOverlayCalendarStore((state) => state.setContinueWithProviderModal); const setCalendarSettingsOverlay = useOverlayCalendarStore( (state) => state.setCalendarSettingsOverlayModal ); - const layout = useBookerStoreContext((state) => state.layout); - const switchEnabled = enabled; + const layout = useBookerStoreContext((state) => state.layout); + const switchEnabled = Boolean(enabled);packages/platform/atoms/availability/AvailabilitySettings.tsx (1)
676-691
: Could not locate or inspectpackages/features/calendars/DatePicker.tsx
to confirm usage of Booker context in override components. Please verify that the DatePicker (and any nested components) consume the Booker store (e.g., viauseBookerStoreContext
or initialization hooks) to justify the Provider wrapper.packages/features/bookings/Booker/components/LargeCalendar.tsx (2)
73-73
: Localize user-facing text (“Busy”)Per guidelines for TSX, wrap user-facing strings with
t()
. Replace the hardcoded "Busy" with a localized string (e.g.,t("busy")
), consistent with the app’s i18n utilities.
42-51
: Consider Day.js overhead in loopsThis map runs per timeslot and calls
dayjs
twice per item. Ifschedule.slots
is large or recomputes frequently, consider micro-optimizing: either precompute with native Date arithmetic or usedayjs.utc()
in hot paths to avoid time zone churn. Not blocking, just a performance note aligned with the repo guideline.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx
(3 hunks)packages/features/bookings/Booker/components/LargeCalendar.tsx
(2 hunks)packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSwitch.tsx
(2 hunks)packages/features/bookings/Booker/components/hooks/useBookerLayout.ts
(2 hunks)packages/features/bookings/components/AvailableTimes.tsx
(3 hunks)packages/features/bookings/components/AvailableTimesHeader.tsx
(2 hunks)packages/platform/atoms/availability/AvailabilitySettings.tsx
(2 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/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx
packages/features/bookings/components/AvailableTimesHeader.tsx
packages/features/bookings/Booker/components/LargeCalendar.tsx
packages/platform/atoms/availability/AvailabilitySettings.tsx
packages/features/bookings/components/AvailableTimes.tsx
packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSwitch.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/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx
packages/features/bookings/components/AvailableTimesHeader.tsx
packages/features/bookings/Booker/components/LargeCalendar.tsx
packages/features/bookings/Booker/components/hooks/useBookerLayout.ts
packages/platform/atoms/availability/AvailabilitySettings.tsx
packages/features/bookings/components/AvailableTimes.tsx
packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSwitch.tsx
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
**/*.ts
: For Prisma queries, only select data you need; never useinclude
, always useselect
Ensure thecredential.key
field is never returned from tRPC endpoints or APIs
Files:
packages/features/bookings/Booker/components/hooks/useBookerLayout.ts
🧬 Code Graph Analysis (7)
packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
packages/features/bookings/components/AvailableTimesHeader.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
packages/features/bookings/Booker/components/LargeCalendar.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
packages/features/bookings/Booker/components/hooks/useBookerLayout.ts (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
packages/platform/atoms/availability/AvailabilitySettings.tsx (2)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
BookerStoreProvider
(15-22)packages/lib/date-ranges.ts (1)
DateOverride
(11-11)
packages/features/bookings/components/AvailableTimes.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSwitch.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Atoms E2E Tests
🔇 Additional comments (13)
packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx (2)
6-6
: LGTM! Correct migration to context-based store.The import change from
useBookerStore
touseBookerStoreContext
aligns with the PR objective to replace the global store with context-based instances for each Booker widget.
47-48
: LGTM! Proper migration to context-based store selectors.The migration from
useBookerStore
touseBookerStoreContext
with proper selector functions maintains the same functionality while enabling per-instance store isolation. The selector pattern(state) => state.selectedTimeslot
and(state) => state.selectedDuration
follows React best practices for state selection.packages/features/bookings/Booker/components/hooks/useBookerLayout.ts (2)
5-5
: Context-scoped store import is the right moveSwitching to useBookerStoreContext aligns with the PR objective and prevents cross-widget state bleed. Good change.
28-35
: Confirm store default ensures valid layoutThe
createBookerStore
initializer sets
layout: BookerLayouts.MONTH_VIEW
anduseInitializeBookerStore
also falls back to MONTH_VIEW (layout || BookerLayouts.MONTH_VIEW
).
As a result,layout
is never undefined, soextraDaysConfig[layout]
is safe.packages/features/bookings/components/AvailableTimesHeader.tsx (1)
5-5
: Good move: switch to context-backed Booker storeUsing useBookerStoreContext aligns with the per-instance store requirement and prevents cross-instance bleeding.
Confirm all render paths for AvailableTimesHeader are wrapped in BookerStoreProvider. The hook will throw if used outside the provider.
packages/features/bookings/components/AvailableTimes.tsx (1)
8-8
: Correct: migrate to useBookerStoreContextThis change is consistent with the instance-scoped Booker store and avoids global store leakage.
Ensure all parents of AvailableTimes are within BookerStoreProvider to avoid runtime errors from the guard in useBookerStoreContext.
packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendarSwitch.tsx (4)
23-23
: LGTM: layout now derived per-widget via contextCorrectly using the context-backed store to avoid global state clashes.
6-7
: LGTM: UI importsImports look correct after the migration; no issues spotted.
57-60
: LGTM: localized label textThe visible text uses
t("overlay_my_calendar")
and complies with localization guidelines.
3-3
: OverlayCalendarSwitch Usage Confirmed Safe
- OverlayCalendarSwitch is only imported and rendered in
packages/features/bookings/Booker/components/OverlayCalendar/OverlayCalendar.tsx- OverlayCalendar itself is used in
packages/features/bookings/Booker/Booker.tsx- The Booker component is always wrapped by BookerStoreProvider in:
• packages/platform/atoms/booker/BookerWebWrapper.tsx
• packages/platform/atoms/booker/BookerPlatformWrapper.tsx
• All booking-related tests use the custom render helper in tests/test-utils.tsxNo changes needed.
packages/platform/atoms/availability/AvailabilitySettings.tsx (1)
16-16
: Import aligns with PR goal to scope Booker state per instanceBringing in BookerStoreProvider here is consistent with isolating state per widget instance. No issues with the import itself.
packages/features/bookings/Booker/components/LargeCalendar.tsx (2)
28-30
: Confirmed:setSelectedTimeslot
accepts a string
The store definessetSelectedTimeslot: (timeslot: string | null) => void
(packages/features/bookings/Booker/store.ts:110–111), so passingdate.toISOString()
is fully compatible. No changes required.
4-4
: Ensure wraps every<LargeCalendar>
usageWe found two standalone uses of
<LargeCalendar>
where no local<BookerStoreProvider>
wrapper exists:
- packages/features/bookings/Booker/Booker.tsx (line 469)
- packages/features/troubleshooter/Troubleshooter.tsx (line 57)
Please verify in your application’s component hierarchy that both of these render paths are enclosed in
<BookerStoreProvider>
(whether locally or at a higher layout level) to prevent the “must be used within BookerStoreProvider” error at runtime.
@@ -18,7 +18,7 @@ export type UseBookerLayoutType = ReturnType<typeof useBookerLayout>; | |||
export const useBookerLayout = ( | |||
profileBookerLayouts: BookerEvent["profile"]["bookerLayouts"] | undefined | null | |||
) => { | |||
const [_layout, setLayout] = useBookerStore((state) => [state.layout, state.setLayout], shallow); | |||
const [_layout, setLayout] = useBookerStoreContext((state) => [state.layout, state.setLayout], shallow); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Correct selector usage with shallow; verify provider wrapping
Using the tuple selector with shallow is idiomatic Zustand. Ensure every consumer of useBookerLayout is rendered under BookerStoreProvider, otherwise this hook will throw at runtime (by design of useBookerStoreContext).
Run the following to locate useBookerLayout consumers and the provider to help double-check composition:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Files importing or using useBookerLayout:"
rg -n --hidden --glob '!node_modules' 'useBookerLayout\(' -A 2
echo
echo "Provider definition and usages:"
rg -n --hidden --glob '!node_modules' 'BookerStoreProvider' -A 3
echo
echo "Check store initialization for a default layout (helps prevent undefined layout downstream):"
rg -n --hidden --glob '!node_modules' 'createBookerStore|BookerStoreProvider|layout' -A 8
Length of output: 280019
Ensure useBookerLayout
is always inside a BookerStoreProvider
The calls to useBookerLayout
in your wrapper components occur before the <BookerStoreProvider>
is mounted, so at runtime useBookerStoreContext
will throw. Hoist the provider to wrap the entire component—including any hook calls in its body—so that all consumers live under its context.
Files needing updates:
-
packages/platform/atoms/booker/BookerWebWrapper.tsx
• Line 57:const bookerLayout = useBookerLayout(...)
happens outside the provider at line 286.
→ Move<BookerStoreProvider>
to encompass this hook call (i.e. wrap the component from its top-level so hooks fire inside). -
packages/platform/atoms/booker/BookerPlatformWrapper.tsx
• Line 164:const bookerLayout = useBookerLayout(...)
occurs before the provider at line 586.
→ Hoist<BookerStoreProvider>
to wrap the component definition so the hook runs within its context.
🤖 Prompt for AI Agents
In packages/features/bookings/Booker/components/hooks/useBookerLayout.ts around
line 21 the hook consumes Booker store context; update the two wrapper
components so this hook always runs inside a BookerStoreProvider: in
packages/platform/atoms/booker/BookerWebWrapper.tsx (hook used at line 57,
provider currently at line 286) and in
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (hook used at line 164,
provider currently at line 586) hoist the <BookerStoreProvider> to wrap the
top-level of the wrapper component (move the provider above where
useBookerLayout is called) so that the provider is mounted before any calls to
useBookerLayout; ensure you remove any duplicate providers and keep provider
props intact.
@@ -13,6 +13,7 @@ import React, { | |||
import { Controller, useFieldArray, useForm, useFormContext, useWatch } from "react-hook-form"; | |||
|
|||
import dayjs from "@calcom/dayjs"; | |||
import { BookerStoreProvider } from "@calcom/features/bookings/Booker/BookerStoreProvider"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Cross-package dependency check: platform → features
This platform atom now depends on features. Ensure this is an allowed layering in your workspace and won’t introduce circular deps or bundling bloat. If layering is a concern, consider re-exporting the provider in a shared package consumed by both platform and features.
To sanity-check for cycles and declared deps:
🏁 Script executed:
#!/bin/bash
# Where is this file?
fd package.json packages | sort
echo "Check if platform package declares dependency on features (or path alias exists)"
rg -n '"@calcom/features"' packages/platform/package.json
echo "Look for tsconfig path alias for @calcom/features"
rg -n '"@calcom/features"' tsconfig*.json packages/**/tsconfig*.json
Length of output: 6476
🏁 Script executed:
#!/bin/bash
# Check if platform atoms package.json declares dependency on @calcom/features
echo "Inspecting packages/platform/atoms/package.json for @calcom/features"
rg -n '"@calcom/features"' packages/platform/atoms/package.json || echo "No dependency declaration found."
# Check for tsconfig path alias in packages/tsconfig
echo "Inspecting packages/tsconfig/tsconfig.json for @calcom/features alias"
rg -n '"@calcom/features"' packages/tsconfig/tsconfig.json || echo "No alias found in packages/tsconfig/tsconfig.json."
Length of output: 680
🏁 Script executed:
#!/bin/bash
set -eo pipefail
# Check for a root tsconfig.json
echo "Root tsconfig.json present?"
if [ -f tsconfig.json ]; then
echo " ✔ tsconfig.json at repo root"
else
echo " ✖ no root tsconfig.json"
fi
# List all tsconfig.json under packages/
echo "All tsconfig.json files in the repo:"
find . -type f -name "tsconfig.json" | sed 's/^/ - /'
# Look for @calcom/features path alias in each tsconfig.json
echo "Searching for @calcom/features path mappings:"
for cfg in $(find . -type f -name "tsconfig.json"); do
echo "==> $cfg"
rg -n '"@calcom/features"' "$cfg" || echo " (none)"
done
# Inspect dependencies in platform/atoms package
echo "Inspecting dependencies in packages/platform/atoms/package.json"
rg -n '"dependencies"' -A10 packages/platform/atoms/package.json || echo " (no dependencies block found)"
rg -n '"peerDependencies"' -A5 packages/platform/atoms/package.json || echo " (no peerDependencies block found)"
rg -n '"devDependencies"' -A5 packages/platform/atoms/package.json || echo " (no devDependencies block found)"
Length of output: 9513
Add missing @calcom/features dependency & verify layering
It looks like you’re importing from @calcom/features
via the TS path alias defined in packages/platform/atoms/tsconfig.json
(line 10), but the platform/atoms
package.json does not list @calcom/features
in its dependencies. Without that entry, consumers outside your workspace will break at install time, and bundlers won’t automatically include the code.
• In packages/platform/atoms/package.json
, under "dependencies"
, add:
"dependencies": {
+ "@calcom/features": "workspace:*",
"@radix-ui/react-dialog-atoms": "npm:@radix-ui/react-dialog@^1.0.4",
"@radix-ui/react-slot": "^1.0.2",
…
}
• Run a quick search to ensure there’s no circular import (e.g. rg '@calcom/platform' packages/features
)
• If you’d rather avoid direct coupling, consider re-exporting the provider from a new shared package consumed by both platform and features.
📝 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.
import { BookerStoreProvider } from "@calcom/features/bookings/Booker/BookerStoreProvider"; | |
{ | |
"name": "platform-atoms", | |
"version": "1.0.0", | |
"dependencies": { | |
+ "@calcom/features": "workspace:*", | |
"@radix-ui/react-dialog-atoms": "npm:@radix-ui/react-dialog@^1.0.4", | |
"@radix-ui/react-slot": "^1.0.2", | |
… | |
}, | |
… | |
} |
🤖 Prompt for AI Agents
In packages/platform/atoms/AvailabilitySettings.tsx around line 16 you import
BookerStoreProvider from @calcom/features but
packages/platform/atoms/package.json does not list @calcom/features as a
dependency; add "@calcom/features" (pinned to the workspace or exact version
used by your monorepo) to the "dependencies" section of
packages/platform/atoms/package.json, run a quick project-wide search (e.g. rg
'@calcom/platform' packages/features and rg '@calcom/features' packages) to
ensure no circular import is introduced, and if you want to avoid direct
coupling instead create or use a shared package that re-exports the provider and
have both platform/atoms and features depend on that shared package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
packages/features/bookings/Booker/Booker.tsx (1)
231-305
: Missing useMemo dependencies cause stale timeslot and props in BookEventFormEventBooker depends on selectedTimeslot (passed to BookEventForm and used to compute isTimeslotUnavailable), confirmButtonDisabled, customClassNames, isInstantMeeting, and schedule (used in onCancel) but these aren’t in the dependency array. This can keep the form from updating when selecting a different slot and leave other props stale.
Add the missing dependencies:
}, [ bookerFormErrorRef, instantVideoMeetingUrl, bookerState, bookingForm, errors, event, expiryTime, extraOptions, formErrors, handleBookEvent, handleVerifyEmail, key, loadingStates, onGoBackInstantMeeting, renderConfirmNotVerifyEmailButtonCond, seatedEventData, setSeatedEventData, setSelectedTimeslot, isPlatform, shouldRenderCaptcha, isVerificationCodeSending, unavailableTimeSlots, + selectedTimeslot, + confirmButtonDisabled, + customClassNames, + isInstantMeeting, + schedule, ]);
♻️ Duplicate comments (6)
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (3)
9-13
: Remove legacy store imports; use only context APIsYou're now using the context-based store. Keep imports from BookerStoreProvider and drop the legacy singleton imports to avoid accidental cross-instance coupling and unused imports.
Apply:
import { BookerStoreProvider, useInitializeBookerStoreContext, useBookerStoreContext, } from "@calcom/features/bookings/Booker/BookerStoreProvider"; -import { useBookerStore, useInitializeBookerStore } from "@calcom/features/bookings/Booker/store"; +import type { BookerStore } from "@calcom/features/bookings/Booker/store";#!/bin/bash # Verify no lingering legacy singleton imports remain across the repo rg -n --no-heading $'@calcom/features/bookings/Booker/store' | rg -n --no-heading 'useBookerStore\\(|useInitializeBookerStore\\(' || true # Verify all context-based imports are used within a Provider rg -n --no-heading $'useBookerStoreContext\\(|useInitializeBookerStoreContext\\(' -A 2 -B 2 rg -n --no-heading $'BookerStoreProvider'Also applies to: 17-17
182-197
: Double initialization: remove legacy useInitializeBookerStore to prevent cross-widget leaksYou correctly initialize via useInitializeBookerStoreContext here. However, you still call the legacy useInitializeBookerStore above. That reintroduces the global singleton and can leak state between widgets.
Action:
- Delete the legacy initializer block above (Lines 166–181).
- With the import cleanup suggested earlier, this avoids initializing the global store entirely.
Remove the following block (shown for clarity):
useInitializeBookerStore({ ...props, teamMemberEmail, crmAppSlug, crmOwnerRecordType, crmRecordId: props.crmRecordId, eventId: event?.data?.id, rescheduleUid: props.rescheduleUid ?? null, bookingUid: props.bookingUid ?? null, layout: layout, org: props.entity?.orgSlug, username, bookingData, isPlatform: true, allowUpdatingUrlParams, });
72-75
: Stop subscribing to the legacy global store in onBookerStateChange; scope it to the context storeThe effect still uses useBookerStore.subscribe/getState and types getStateValues off the legacy store, which breaks isolation across widgets. Select from the context-scoped store instead.
Follow-up changes:
- Type the selector against the store type.
- Drive the callback using a context selector so it only reacts to this instance’s state.
Add the type import (already suggested in imports comment):
import type { BookerStore } from "@calcom/features/bookings/Booker/store";Update the selector typing:
const getStateValues = useCallback((state: BookerStore): BookerStoreValues => { return Object.fromEntries(Object.entries(state).filter(([_, v]) => typeof v !== "function")) as BookerStoreValues; }, []);Replace the effect with a context-scoped subscription:
const scopedStateValues = useBookerStoreContext(getStateValues); useEffect(() => { if (!onBookerStateChange) return; // Initial call and subsequent debounced updates scoped to this provider debouncedStateChange(scopedStateValues, onBookerStateChange); return () => { debouncedStateChange.cancel(); }; }, [scopedStateValues, onBookerStateChange, debouncedStateChange]);Also applies to: 90-96, 112-129
packages/features/embed/Embed.tsx (2)
14-19
: Don’t mix context-based reader with legacy initializer; remove useInitializeBookerStore importYou’ve added BookerStoreProvider and context hooks (good), but still import the legacy useInitializeBookerStore. This can mutate a different, global store and break isolation.
Apply:
import { BookerStoreProvider, useInitializeBookerStoreContext, useBookerStoreContext, } from "@calcom/features/bookings/Booker/BookerStoreProvider"; -import { useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
268-275
: Initialize via context only (correct)Using useInitializeBookerStoreContext with username/eventSlug/eventId/layout/org/isTeamEvent is the right approach. Remove the legacy initializer call above (Lines 260–267) to avoid double-initialization.
Apply removal inside EmailEmbed:
- useInitializeBookerStore({ - username, - eventSlug: eventType?.slug ?? "", - eventId: eventType?.id, - layout: BookerLayouts.MONTH_VIEW, - org: orgSlug, - isTeamEvent, - });packages/features/bookings/Booker/Booker.tsx (1)
408-414
: Confirm EventMeta accepts and consumes selectedTimeslotYou’re passing selectedTimeslot to EventMeta. Ensure its props include selectedTimeslot?: string | null and that it’s used inside the component as intended.
Run:
#!/bin/bash # Check EventMeta props for `selectedTimeslot` rg -n 'type .*EventMeta.*Props|interface .*EventMeta.*Props' -A15 packages/features/bookings/Booker/components/EventMeta.tsx rg -n --fixed-strings 'selectedTimeslot' packages/features/bookings/Booker/components/EventMeta.tsx
🧹 Nitpick comments (4)
packages/features/bookings/Booker/components/hooks/useBookerTime.ts (1)
9-9
: Simplify the selector: select the primitive directly and drop shallow.You’re selecting a single primitive; the extra array allocation and shallow compare are unnecessary. Zustand’s default equality for primitives is sufficient.
Apply this diff within the current line range:
- const [timezoneFromBookerStore] = useBookerStoreContext((state) => [state.timezone], shallow); + const timezoneFromBookerStore = useBookerStoreContext((state) => state.timezone);And remove the (now unused) import at the top of this file:
// Remove this import import { shallow } from "zustand/shallow";packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1)
198-199
: Nit: avoid selecting unused setter to reduce re-render noiseYou select setDayCount but never use it. Select only dayCount.
-const [dayCount] = useBookerStoreContext((state) => [state.dayCount, state.setDayCount], shallow); +const dayCount = useBookerStoreContext((state) => state.dayCount);packages/features/bookings/Booker/Booker.tsx (2)
93-94
: Avoid double subscriptions by selecting value and setter togetherYou’re subscribing twice to the store for selectedDate and setSelectedDate. Combine them into a single selector tuple with shallow to reduce subscriptions.
Apply this diff:
-const selectedDate = useBookerStoreContext((state) => state.selectedDate); -const setSelectedDate = useBookerStoreContext((state) => state.setSelectedDate); +const [selectedDate, setSelectedDate] = useBookerStoreContext( + (state) => [state.selectedDate, state.setSelectedDate], + shallow +);
195-198
: Stabilize the time-slot select handler with useCallbackonAvailableTimeSlotSelect is recreated on every render which can cause extra rerenders downstream. Wrap it in useCallback.
Apply this diff:
- const onAvailableTimeSlotSelect = (time: string) => { - setSelectedTimeslot(time); - }; + const onAvailableTimeSlotSelect = useCallback((time: string) => { + setSelectedTimeslot(time); + }, [setSelectedTimeslot]);Add this import at the top (outside the selected range):
import { useCallback, useEffect, useMemo, useRef } from "react";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/features/bookings/Booker/Booker.tsx
(7 hunks)packages/features/bookings/Booker/components/DatePicker.tsx
(2 hunks)packages/features/bookings/Booker/components/hooks/useBookerTime.ts
(1 hunks)packages/features/calendars/DatePicker.tsx
(3 hunks)packages/features/embed/Embed.tsx
(4 hunks)packages/platform/atoms/booker/BookerPlatformWrapper.tsx
(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/features/calendars/DatePicker.tsx
- packages/features/bookings/Booker/components/DatePicker.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
**/*.ts
: For Prisma queries, only select data you need; never useinclude
, always useselect
Ensure thecredential.key
field is never returned from tRPC endpoints or APIs
Files:
packages/features/bookings/Booker/components/hooks/useBookerTime.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/bookings/Booker/components/hooks/useBookerTime.ts
packages/features/bookings/Booker/Booker.tsx
packages/platform/atoms/booker/BookerPlatformWrapper.tsx
packages/features/embed/Embed.tsx
**/*.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/bookings/Booker/Booker.tsx
packages/platform/atoms/booker/BookerPlatformWrapper.tsx
packages/features/embed/Embed.tsx
🧬 Code Graph Analysis (4)
packages/features/bookings/Booker/components/hooks/useBookerTime.ts (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
packages/features/bookings/Booker/Booker.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (2)
packages/features/bookings/Booker/BookerStoreProvider.tsx (3)
useBookerStoreContext
(24-35)useInitializeBookerStoreContext
(37-113)BookerStoreProvider
(15-22)packages/platform/atoms/booker/types.ts (2)
BookerPlatformWrapperAtomPropsForIndividual
(92-96)BookerPlatformWrapperAtomPropsForTeam
(98-103)
packages/features/embed/Embed.tsx (3)
packages/features/bookings/Booker/BookerStoreProvider.tsx (3)
useInitializeBookerStoreContext
(37-113)useBookerStoreContext
(24-35)BookerStoreProvider
(15-22)packages/embeds/embed-core/src/types.ts (1)
BookerLayouts
(6-6)packages/features/embed/types/index.d.ts (1)
EmbedType
(6-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Atoms E2E Tests
🔇 Additional comments (10)
packages/features/bookings/Booker/components/hooks/useBookerTime.ts (1)
3-3
: Verify BookerStoreProvider wraps all useBookerTime call sitesI ran the repo-wide search you suggested. Summary:
useBookerTime is used across multiple places (examples):
- packages/platform/atoms/hooks/bookings/useHandleBookEvent.ts
- packages/features/embed/Embed.tsx
- packages/features/calendars/weeklyview/components/event/Empty.tsx
- packages/features/calendars/weeklyview/components/currentTime/index.tsx
- packages/features/bookings/components/AvailableTimes.tsx
- packages/features/bookings/Booker/components/EventMeta.tsx
- packages/features/bookings/Booker/utils/event.ts
- packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx
BookerStoreProvider placements found (wrap roots / common wrappers / tests):
- packages/features/embed/Embed.tsx
- packages/platform/atoms/booker/BookerWebWrapper.tsx
- packages/platform/atoms/booker/BookerPlatformWrapper.tsx
- packages/platform/atoms/availability/AvailabilitySettings.tsx (modal)
- tests (e.g. packages/features/calendars/tests/DatePicker.test.tsx)
Items needing explicit attention:
- Files that appear to mix the legacy store API and the new context-based API (please confirm intentional):
- packages/features/bookings/components/event-meta/Occurences.tsx (uses useBookerStore and useBookerTime)
- packages/features/bookings/Booker/components/hooks/useBookings.ts (imports useBookerStore from store)
- Double-check platform atom hooks (e.g. packages/platform/atoms/hooks/useSlots.ts, useHandleBookEvent.ts) are only invoked within components wrapped by BookerStoreProvider in the platform wrappers.
If you'd like, I can run a follow-up script that cross-references each useBookerTime file with known provider-wrapping entrypoints to try to flag any call sites that are likely rendered outside a provider.
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1)
584-592
: Provider wrapping looks correctWrapping the internal component with BookerStoreProvider at the public boundary ensures per-instance isolation. Good move.
packages/features/embed/Embed.tsx (3)
277-291
: Context-based reads/writes LGTMReading month/selectedDate/selectedDatesAndTimes and writing setSelectedDate/setMonth/setSelectedDatesAndTimes/setSelectedTimeslot/setTimezone via useBookerStoreContext is aligned with the per-instance store design.
702-705
: Scoped selection LGTMSelecting [month, selectedDatesAndTimes] from the context store is correct and avoids cross-widget leakage.
1385-1413
: Correct: wrap the embed dialog tree with BookerStoreProviderThis ensures EmailEmbed and preview consumers have a scoped store instance. Solid.
packages/features/bookings/Booker/Booker.tsx (5)
88-91
: Good use of context selector with shallow equalitySwitching to useBookerStoreContext and selecting [state, setState] with shallow prevents unnecessary rerenders. LGTM.
108-116
: LGTM on store usage for seatedEventData and dayCountConsistent tuple selection with shallow for seatedEventData and dayCount looks correct and performant.
235-235
: Verified — BookEventForm props includetimeslot: string | null
; no change requiredBookEventFormProps declares timeslot as
string | null
and the component guards for a missing timeslot, sotimeslot={selectedTimeslot}
is aligned.
- packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx —
type BookEventFormProps
includestimeslot: string | null
(declaration and usage around lines 27/46 and checks at ~94–100).- packages/features/bookings/Booker/Booker.tsx — passing prop at line ~235: timeslot={selectedTimeslot}
Snippet:
timeslot={selectedTimeslot}
495-495
: AvailableTimeSlots: onAvailableTimeSlotSelect is present and required — no change neededAvailableTimeSlotsProps already declares onAvailableTimeSlotSelect: (time: string) => void and the handler in Booker.tsx matches and is passed through, so the original suggestion to make it optional is incorrect.
- packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
- line 52:
onAvailableTimeSlotSelect: (time: string) => void;
- lines 76 / 151 / 167: destructure/use/call the prop
- packages/features/bookings/Booker/Booker.tsx
- line 195:
const onAvailableTimeSlotSelect = (time: string) => { ... }
- line 495:
onAvailableTimeSlotSelect={onAvailableTimeSlotSelect}
Note: The “Timeslot” vs “TimeSlot” naming inconsistency is a minor, non-blocking style issue you may want to standardize later.
Likely an incorrect or invalid review comment.
13-13
: Verified — Booker is always rendered inside BookerStoreProvider (or mocked in tests)I searched the repo and confirmed there are no direct, unwrapped production usages of . Key locations:
- packages/platform/atoms/booker/BookerPlatformWrapper.tsx — BookerPlatformWrapper exports the component wrapped in .
- packages/platform/atoms/booker/BookerWebWrapper.tsx — BookerWebWrapper exports the component wrapped in .
- packages/features/embed/Embed.tsx — EmbedDialog content is wrapped in .
- packages/features/bookings/Booker/tests/test-utils.tsx — tests use renderWithBookerStore which provides BookStoreContext.Provider (mock store) when rendering .
Note: packages/features/bookings/Booker/Booker.tsx exports Booker without embedding a provider (expected). Consumers must continue to use the provided wrappers (or the test helper) when rendering Booker.
No fixes required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (2)
packages/features/troubleshooter/Troubleshooter.tsx (2)
31-33
: CSS variable name typo breaks layout on non-lg breakpointsThe CSS custom property is misspelled as
--troublehooster-meta-width
but referenced as--troubleshooter-meta-width
(and correctly set forlg:
only). This causesgridTemplateColumns
to reference an undefined variable on mobile/tablet, likely breaking the layout.Apply this diff to fix the variable name consistently:
- ? "[--troublehooster-meta-width:0px]" - : "[--troublehooster-meta-width:250px] lg:[--troubleshooter-meta-width:430px]" + ? "[--troubleshooter-meta-width:0px]" + : "[--troubleshooter-meta-width:250px] lg:[--troubleshooter-meta-width:430px]"
57-59
: Invalid Tailwind arbitrary value class: useml-[-1px]
ml[-1px]
is invalid Tailwind syntax; it should beml-[-1px]
. This class currently won’t apply.Apply this diff:
- <div className="ml[-1px] border-subtle sticky top-0 [grid-area:main]"> + <div className="ml-[-1px] border-subtle sticky top-0 [grid-area:main]">
🧹 Nitpick comments (1)
packages/features/troubleshooter/Troubleshooter.tsx (1)
21-23
: Nit: fix hook name typo for consistencyConsider renaming
useInitalizeTroubleshooterStore
touseInitializeTroubleshooterStore
for clarity and consistency across the codebase. If you proceed, update the import on Line 8 and all call sites.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/features/troubleshooter/Troubleshooter.tsx
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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/Troubleshooter.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/features/troubleshooter/Troubleshooter.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Atoms E2E Tests
🔇 Additional comments (2)
packages/features/troubleshooter/Troubleshooter.tsx (2)
1-1
: Import of BookerStoreProvider aligns with per-instance store objectiveThis import is correct and consistent with the PR goal to scope Booker store per subtree.
67-71
: Unconditional BookerStoreProvider is OK here — no nested Booker stores foundRepo scan shows Troubleshooter is only mounted from the dedicated page and not inside an existing Booker provider, so the unconditional provider does not create a double-wrap in current usage.
- packages/features/troubleshooter/Troubleshooter.tsx — wraps TroubleshooterComponent with (lines ~66–71).
- apps/web/modules/availability/troubleshoot/troubleshoot-view.tsx — dynamically imports and renders the Troubleshooter (TroubleshooterClientOnly); there is no upstream BookerStoreProvider at that integration point.
- BookerStoreProvider is used in other places (e.g. packages/platform/atoms/booker/BookerWebWrapper.tsx, packages/platform/atoms/booker/BookerPlatformWrapper.tsx) but those files do not render Troubleshooter.
Conclusion: No change required for this PR. If you plan to render Troubleshooter inside an already-wrapped Booker subtree in the future, consider a provider-if-missing / boundary pattern in a follow-up.
There was a problem hiding this 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
🧹 Nitpick comments (1)
packages/features/bookings/Booker/components/Section.tsx (1)
47-54
: Rename local variable to avoid confusion with framer-motion’slayout
propUsing
layout
as a local variable while also setting thelayout
prop on<m.div>
is easy to misread. Rename the local variable for clarity.- const layout = useBookerStoreContext((state) => state.layout); + const currentLayout = useBookerStoreContext((state) => state.layout); @@ - gridClassName = gridAreaClassNameMap[area[layout] || area.default]; + gridClassName = gridAreaClassNameMap[area[currentLayout] || area.default];
📜 Review details
Configuration used: CodeRabbit UI
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 settings in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/features/bookings/Booker/components/Section.tsx
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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/bookings/Booker/components/Section.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/features/bookings/Booker/components/Section.tsx
🧬 Code Graph Analysis (1)
packages/features/bookings/Booker/components/Section.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Production builds / Build Web App
- GitHub Check: Production builds / Build Docs
- GitHub Check: Production builds / Build Atoms
- GitHub Check: Production builds / Build API v2
- GitHub Check: Production builds / Build API v1
- GitHub Check: Tests / Unit
- GitHub Check: Type check / check-types
- GitHub Check: Linters / lint
- GitHub Check: Atoms E2E Tests
@@ -2,9 +2,9 @@ import type { MotionProps } from "framer-motion"; | |||
import { m } from "framer-motion"; | |||
import { forwardRef } from "react"; | |||
|
|||
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Context store migration looks good; verify all usages are wrapped in BookerStoreProvider
Switching to useBookerStoreContext
is correct for isolating store instances. However, this hook throws if no provider is present (see BookerStoreProvider.tsx Lines 23-34). Please verify every render path of BookerSection
sits under BookerStoreProvider
to avoid runtime errors in downstream apps, embeds, and examples.
Run this script to surface candidate files that reference BookerSection
without any obvious BookerStoreProvider
import/usage in the same file (heuristic; may include false positives if the provider is higher up the tree):
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find files that import or use BookerSection
files=$(rg -n --glob '!**/node_modules/**' -e 'import\s+{?\s*BookerSection\b' -e '<BookerSection\b' --no-heading | cut -d: -f1 | sort -u)
echo "Files referencing BookerSection:"
echo "$files" | sed 's/^/ - /'
echo
echo "Candidates to review (no explicit BookerStoreProvider reference in the same file):"
while read -r f; do
if [[ -z "$f" ]]; then continue; fi
if rg -q 'BookerStoreProvider|<BookerStoreProvider' "$f"; then
echo "[OK] $f"
else
echo "[CHECK] $f"
fi
done <<< "$files"
Length of output: 301
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find files that reference BookerSection (import, JSX usage, or any mention)
mapfile -t files < <(rg --hidden --glob '!**/node_modules/**' -n -l 'BookerSection' || true)
if [ ${#files[@]} -eq 0 ]; then
echo "No files referencing BookerSection found."
exit 0
fi
echo "Files referencing BookerSection:"
for f in "${files[@]}"; do
echo " - $f"
done
echo
echo "Candidates to review (no explicit BookerStoreProvider reference in the same file):"
for f in "${files[@]}"; do
if rg -n --hidden --glob '!**/node_modules/**' -e 'BookerStoreProvider' -e '<BookerStoreProvider' -- "$f" >/dev/null; then
echo "[OK] $f"
else
echo "[CHECK] $f"
fi
done
Length of output: 211
Run a robust search for BookerSection usages and whether BookerStoreProvider is present
Previous attempt hit a shell /dev/fd issue — run the script below to re-check.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find files that reference BookerSection (import, JSX usage, or any mention)
files=$(rg --hidden --glob '!**/node_modules/**' -n -l 'BookerSection' || true)
if [ -z "$files" ]; then
echo "No files referencing BookerSection found."
exit 0
fi
echo "Files referencing BookerSection:"
echo "$files" | sed 's/^/ - /'
echo
echo "Candidates to review (no explicit BookerStoreProvider reference in the same file):"
echo "$files" | while IFS= read -r f; do
if [ -z "$f" ]; then continue; fi
if rg -n --hidden --glob '!**/node_modules/**' -e 'BookerStoreProvider' -e '<BookerStoreProvider' -- "$f" >/dev/null 2>&1; then
echo "[OK] $f"
else
echo "[CHECK] $f"
fi
done
Length of output: 2407
Wrap or mock BookerSection usages outside BookerStoreProvider — mock needs a fix
I searched the repo for BookerSection usages. Results:
- packages/features/bookings/Booker/Booker.tsx — [OK] (contains BookerStoreProvider)
- packages/features/bookings/Booker/components/Section.tsx — [OK]
- packages/features/bookings/Booker/components/mocks/Section.tsx — [CHECK] (no BookerStoreProvider found)
Action required: update the mock at packages/features/bookings/Booker/components/mocks/Section.tsx to either wrap the mock export with BookerStoreProvider or mock useBookerStoreContext (so the hook cannot throw during tests/renders).
🤖 Prompt for AI Agents
In packages/features/bookings/Booker/components/__mocks__/Section.tsx around
line 5, the mock for BookerSection can call useBookerStoreContext outside a
BookerStoreProvider causing the hook to throw in tests; update the mock to
either wrap the mocked component export with BookerStoreProvider (so consumers
render inside the provider) or replace the mock implementation to stub
useBookerStoreContext (e.g., jest.mock the hook to return a safe default) so the
hook is never invoked against a missing provider during tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested the web app booker, and everything is still working as it should
What does this PR do?
Booker
atom wherein previously we were not able to use multiple instance of theBooker
widget on the same page. If we would use multipleBooker
widgets the state would change in both of them even though one of them would be toggledBooker
atom widget uses a store of its own instead of sharing a global instance that all the widgets can access which was the previous approachVisual Demo:
Screen.Recording.2025-08-11.at.6.14.02.PM.mov
Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
For the web app these are the steps:
For platform these are the steps:
booking.tsx
in the examples app and copy the code for Booker atom here and paste it just under to make another instance of the sameBooker
widget appearBooker
atom widgets you need to either comment out the prop allowUpdatingUrlParams or set it to false