From bac2d1f6f0997424d7459ac55181c5437fbcda16 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Wed, 1 Jan 2025 17:10:53 +0530 Subject: [PATCH] feat: Add dry run mode functionality and message display in booking process (#18429) - Introduced a new `DryRunMessage` component to inform users when in dry run mode. - Implemented utility function `isBookingDryRun` to check for dry run status based on URL search parameters. - Updated the `Booker` component to conditionally render the dry run message. - Adjusted booking mutation input mapping to reuse the utility Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> --- apps/web/next.config.js | 7 ++++- apps/web/public/static/locales/en/common.json | 1 + packages/features/bookings/Booker/Booker.tsx | 8 ++++-- .../Booker/components/DryRunMessage.tsx | 26 +++++++++++++++++++ .../bookings/Booker/utils/isBookingDryRun.ts | 3 +++ .../booking-to-mutation-input-mapper.tsx | 5 ++-- 6 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 packages/features/bookings/Booker/components/DryRunMessage.tsx create mode 100644 packages/features/bookings/Booker/utils/isBookingDryRun.ts diff --git a/apps/web/next.config.js b/apps/web/next.config.js index ef21974e92..7bd6e133b0 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -395,6 +395,11 @@ const nextConfig = { value: "cross-origin", }; + const ACCESS_CONTROL_ALLOW_ORIGIN_HEADER = { + key: "Access-Control-Allow-Origin", + value: "*", + }; + return [ { source: "/auth/:path*", @@ -464,7 +469,7 @@ const nextConfig = { }, { source: "/icons/sprite.svg", - headers: [CORP_CROSS_ORIGIN_HEADER], + headers: [CORP_CROSS_ORIGIN_HEADER, ACCESS_CONTROL_ALLOW_ORIGIN_HEADER], }, ], ...(isOrganizationsEnabled diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 328e6b18a3..4ea9d6c659 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -2905,5 +2905,6 @@ "routing_form": "Routing Form", "something_unexpected_occurred": "Something unexpected occurred", "500_error_message": "It's not you, it's us.", + "dry_run_mode_active": "You are doing a test booking.", "ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑" } diff --git a/packages/features/bookings/Booker/Booker.tsx b/packages/features/bookings/Booker/Booker.tsx index 523ca891ab..4cd6355e6e 100644 --- a/packages/features/bookings/Booker/Booker.tsx +++ b/packages/features/bookings/Booker/Booker.tsx @@ -11,13 +11,14 @@ import dayjs from "@calcom/dayjs"; import { getQueryParam } from "@calcom/features/bookings/Booker/utils/query-param"; import { useNonEmptyScheduleDays } from "@calcom/features/schedules"; import classNames from "@calcom/lib/classNames"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { BookerLayouts } from "@calcom/prisma/zod-utils"; import { VerifyCodeDialog } from "../components/VerifyCodeDialog"; import { AvailableTimeSlots } from "./components/AvailableTimeSlots"; import { BookEventForm } from "./components/BookEventForm"; import { BookFormAsModal } from "./components/BookEventForm/BookFormAsModal"; +import { DryRunMessage } from "./components/DryRunMessage"; import { EventMeta } from "./components/EventMeta"; import { HavingTroubleFindingTime } from "./components/HavingTroubleFindingTime"; import { Header } from "./components/Header"; @@ -30,6 +31,7 @@ import { NotFound } from "./components/Unavailable"; import { fadeInLeft, getBookerSizeClassNames, useBookerResizeAnimation } from "./config"; import { useBookerStore } from "./store"; import type { BookerProps, WrappedBookerProps } from "./types"; +import { isBookingDryRun } from "./utils/isBookingDryRun"; const loadFramerFeatures = () => import("./framer-features").then((res) => res.default); const PoweredBy = dynamic(() => import("@calcom/ee/components/PoweredBy").then((mod) => mod.default)); @@ -71,7 +73,7 @@ const BookerComponent = ({ userLocale, hasValidLicense, }: BookerProps & WrappedBookerProps) => { - const { t } = useLocale(); + const searchParams = useCompatSearchParams(); const isPlatformBookerEmbed = useIsPlatformBookerEmbed(); const [bookerState, setBookerState] = useBookerStore((state) => [state.state, state.setState], shallow); const selectedDate = useBookerStore((state) => state.selectedDate); @@ -269,6 +271,8 @@ const BookerComponent = ({ <> {event.data && !isPlatform ? : <>} + {isBookingDryRun(searchParams) && } +
{ + const { t } = useLocale(); + const [isVisible, setIsVisible] = useState(true); + + if (!isVisible) return null; + + return ( +
setIsVisible(false)} + className={`bg-default border-subtle fixed left-1/2 ${ + !isEmbed ? "top-4" : "top-0" + } z-50 -translate-x-1/2 transform cursor-pointer items-center gap-3 rounded-xl border p-3 text-sm shadow-md`}> +
+
+ +
+
{t("dry_run_mode_active")}
+
+
+ ); +}; diff --git a/packages/features/bookings/Booker/utils/isBookingDryRun.ts b/packages/features/bookings/Booker/utils/isBookingDryRun.ts new file mode 100644 index 0000000000..e5c4341639 --- /dev/null +++ b/packages/features/bookings/Booker/utils/isBookingDryRun.ts @@ -0,0 +1,3 @@ +export const isBookingDryRun = (searchParams: URLSearchParams) => { + return searchParams.get("cal.isBookingDryRun") === "true"; +}; diff --git a/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx b/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx index df1eddd867..f2a203b441 100644 --- a/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx +++ b/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx @@ -1,6 +1,7 @@ import { v4 as uuidv4 } from "uuid"; import dayjs from "@calcom/dayjs"; +import { isBookingDryRun } from "@calcom/features/bookings/Booker/utils/isBookingDryRun"; import { getRoutedTeamMemberIdsFromSearchParams } from "@calcom/lib/bookings/getRoutedTeamMemberIdsFromSearchParams"; import { parseRecurringDates } from "@calcom/lib/parse-dates"; @@ -52,7 +53,7 @@ export const mapBookingToMutationInput = ({ const routingFormResponseId = routingFormResponseIdParam ? Number(routingFormResponseIdParam) : undefined; const skipContactOwner = searchParams.get("cal.skipContactOwner") === "true"; const reroutingFormResponses = searchParams.get("cal.reroutingFormResponses"); - const isBookingDryRun = searchParams.get("cal.isBookingDryRun") === "true"; + const _isDryRun = isBookingDryRun(searchParams); const _cacheParam = searchParams?.get("cal.cache"); const _shouldServeCache = _cacheParam ? _cacheParam === "true" : undefined; @@ -84,7 +85,7 @@ export const mapBookingToMutationInput = ({ skipContactOwner, // In case of rerouting, the form responses are actually the responses that we need to update. reroutingFormResponses: reroutingFormResponses ? JSON.parse(reroutingFormResponses) : undefined, - _isDryRun: isBookingDryRun, + _isDryRun, _shouldServeCache, }; };