fix: ensure isBookingDryRun prop works for booking creation (#22182)

* fix: ensure isBookingDryRun prop works for booking creation

- Add isBookingDryRun prop to useHandleBookEvent hook
- Pass prop through BookerPlatformWrapper and useBookings
- Override _isDryRun flag when prop is provided
- Fixes issue where dry run banner showed but booking creation ignored prop

Co-Authored-By: rajiv@cal.com <rajiv@cal.com>

* fixup

* if dry run prop is pass we use that, otherwise we use params

* add changesets for PR

* replace minor with patch changeset

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: rajiv@cal.com <rajiv@cal.com>
Co-authored-by: Ryukemeister <sahalrajiv6900@gmail.com>
Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
This commit is contained in:
devin-ai-integration[bot]
2025-07-11 16:45:30 +00:00
committed by GitHub
co-authored by rajiv@cal.com Ryukemeister Rajiv Sahal Morgan
parent 94a537bfda
commit 8b3ff0b789
5 changed files with 23 additions and 3 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@calcom/atoms": patch
---
This change ensures that the booking dry run flow never reserves an actual booking slot.
@@ -53,6 +53,7 @@ export interface IUseBookings {
bookingForm: UseBookingFormReturnType["bookingForm"];
metadata: Record<string, string>;
teamMemberEmail?: string | null;
isBookingDryRun?: boolean;
}
const getBookingSuccessfulEventPayload = (booking: {
@@ -107,7 +108,14 @@ const storeInLocalStorage = ({
localStorage.setItem(STORAGE_KEY, value);
};
export const useBookings = ({ event, hashedLink, bookingForm, metadata, teamMemberEmail }: IUseBookings) => {
export const useBookings = ({
event,
hashedLink,
bookingForm,
metadata,
teamMemberEmail,
isBookingDryRun,
}: IUseBookings) => {
const router = useRouter();
const eventSlug = useBookerStore((state) => state.eventSlug);
const eventTypeId = useBookerStore((state) => state.eventId);
@@ -399,6 +407,7 @@ export const useBookings = ({ event, hashedLink, bookingForm, metadata, teamMemb
handleInstantBooking: createInstantBookingMutation.mutate,
handleRecBooking: createRecurringBookingMutation.mutate,
handleBooking: createBookingMutation.mutate,
isBookingDryRun,
});
const errors = {
@@ -29,6 +29,7 @@ export type BookingOptions = {
crmAppSlug?: string | null;
orgSlug?: string;
routingFormSearchParams?: RoutingFormSearchParams;
isDryRunProp?: boolean;
};
export const mapBookingToMutationInput = ({
@@ -50,6 +51,7 @@ export const mapBookingToMutationInput = ({
crmAppSlug,
orgSlug,
routingFormSearchParams,
isDryRunProp,
}: BookingOptions): BookingCreateBody => {
const searchParams = new URLSearchParams(routingFormSearchParams ?? window.location.search);
const routedTeamMemberIds = getRoutedTeamMemberIdsFromSearchParams(searchParams);
@@ -57,7 +59,7 @@ export const mapBookingToMutationInput = ({
const routingFormResponseId = routingFormResponseIdParam ? Number(routingFormResponseIdParam) : undefined;
const skipContactOwner = searchParams.get("cal.skipContactOwner") === "true";
const reroutingFormResponses = searchParams.get("cal.reroutingFormResponses");
const _isDryRun = isBookingDryRun(searchParams);
const _isDryRun = isDryRunProp !== undefined ? isDryRunProp : isBookingDryRun(searchParams);
const _cacheParam = searchParams?.get("cal.cache");
const _shouldServeCache = _cacheParam ? _cacheParam === "true" : undefined;
const dub_id = searchParams?.get("dub_id");
@@ -353,7 +353,7 @@ export const BookerPlatformWrapper = (
onReserveSlotError: props.onReserveSlotError,
onDeleteSlotSuccess: props.onDeleteSlotSuccess,
onDeleteSlotError: props.onDeleteSlotError,
isBookingDryRun: routingParams?.isBookingDryRun,
isBookingDryRun: props.isBookingDryRun ? props.isBookingDryRun : routingParams?.isBookingDryRun,
handleSlotReservation,
});
@@ -393,6 +393,7 @@ export const BookerPlatformWrapper = (
handleRecBooking: createRecBooking,
locationUrl: props.locationUrl,
routingFormSearchParams,
isBookingDryRun: isBookingDryRun ?? routingParams?.isBookingDryRun,
});
const onOverlaySwitchStateChange = useCallback(
@@ -30,6 +30,7 @@ type UseHandleBookingProps = {
handleRecBooking: (input: BookingCreateBody[], callbacks?: Callbacks) => void;
locationUrl?: string;
routingFormSearchParams?: RoutingFormSearchParams;
isBookingDryRun?: boolean;
};
export const useHandleBookEvent = ({
@@ -42,6 +43,7 @@ export const useHandleBookEvent = ({
handleRecBooking,
locationUrl,
routingFormSearchParams,
isBookingDryRun,
}: UseHandleBookingProps) => {
const isPlatform = useIsPlatform();
const setFormValues = useBookerStore((state) => state.setFormValues);
@@ -108,6 +110,7 @@ export const useHandleBookEvent = ({
crmAppSlug,
orgSlug: orgSlug ? orgSlug : undefined,
routingFormSearchParams,
isDryRunProp: isBookingDryRun,
};
const tracking = getUtmTrackingParameters(searchParams);