From 79f7a16f627017ddcfad1b7fd51003ec7dd8ffd4 Mon Sep 17 00:00:00 2001 From: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Date: Wed, 22 Mar 2023 15:45:16 +0530 Subject: [PATCH] fix: pass timeFormat to booking page (#7642) * fix: pass timeFormat to booking page Signed-off-by: Udit Takkar * fix: add zod schema, fix type eroor Signed-off-by: Udit Takkar --------- Signed-off-by: Udit Takkar Co-authored-by: Peer Richelsen Co-authored-by: zomars --- .../web/components/booking/AvailableTimes.tsx | 3 +- .../components/booking/pages/BookingPage.tsx | 79 +++++++++---------- apps/web/lib/parseDate.ts | 19 +++-- 3 files changed, 48 insertions(+), 53 deletions(-) diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index ddea5dcb4c..77dc015639 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -94,7 +94,7 @@ const AvailableTimes: FC = ({ slots.map((slot) => { type BookingURL = { pathname: string; - query: Record; + query: Record; }; const bookingUrl: BookingURL = { pathname: router.pathname.endsWith("/embed") ? "../book" : "book", @@ -103,6 +103,7 @@ const AvailableTimes: FC = ({ date: dayjs.utc(slot.time).tz(timeZone()).format(), type: eventTypeId, slug: eventTypeSlug, + timeFormat, /** Treat as recurring only when a count exist and it's not a rescheduling workflow */ count: recurringCount && !rescheduleUid ? recurringCount : undefined, ...(ethSignature ? { ethSignature } : {}), diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx index 92346c5716..221db1365a 100644 --- a/apps/web/components/booking/pages/BookingPage.tsx +++ b/apps/web/components/booking/pages/BookingPage.tsx @@ -4,7 +4,7 @@ import { useSession } from "next-auth/react"; import dynamic from "next/dynamic"; import Head from "next/head"; import { useRouter } from "next/router"; -import { useCallback, useEffect, useMemo, useReducer, useState } from "react"; +import { useEffect, useMemo, useReducer, useState } from "react"; import { useForm, useFormContext } from "react-hook-form"; import { v4 as uuidv4 } from "uuid"; import { z } from "zod"; @@ -15,7 +15,6 @@ import { getEventLocationType, locationKeyToString } from "@calcom/app-store/loc import { createPaymentLink } from "@calcom/app-store/stripepayment/lib/client"; import { getEventTypeAppData } from "@calcom/app-store/utils"; import type { LocationObject } from "@calcom/core/location"; -import type { Dayjs } from "@calcom/dayjs"; import dayjs from "@calcom/dayjs"; import { useEmbedNonStylesConfig, @@ -36,19 +35,19 @@ import classNames from "@calcom/lib/classNames"; import { APP_NAME } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import useTheme from "@calcom/lib/hooks/useTheme"; +import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery"; import { HttpError } from "@calcom/lib/http-error"; import { getEveryFreqFor } from "@calcom/lib/recurringStrings"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; -import type { RecurringEvent } from "@calcom/types/Calendar"; +import { TimeFormat } from "@calcom/lib/timeFormat"; import { Button, Form, Tooltip } from "@calcom/ui"; import { FiAlertTriangle, FiCalendar, FiRefreshCw, FiUser } from "@calcom/ui/components/icon"; -import { asStringOrNull } from "@lib/asStringOrNull"; import { timeZone } from "@lib/clock"; import useRouterQuery from "@lib/hooks/useRouterQuery"; import createBooking from "@lib/mutations/bookings/create-booking"; import createRecurringBooking from "@lib/mutations/bookings/create-recurring-booking"; -import { parseDate, parseRecurringDates } from "@lib/parseDate"; +import { parseRecurringDates, parseDate } from "@lib/parseDate"; import type { Gate, GateState } from "@components/Gates"; import Gates from "@components/Gates"; @@ -187,6 +186,22 @@ const BookingFields = ({ ); }; +const routerQuerySchema = z + .object({ + timeFormat: z.nativeEnum(TimeFormat), + rescheduleUid: z.string().optional(), + date: z + .string() + .optional() + .transform((date) => { + if (date === undefined) { + return null; + } + return date; + }), + }) + .passthrough(); + const BookingPage = ({ eventType, booking, @@ -225,20 +240,6 @@ const BookingPage = ({ duration = Number(queryDuration); } - // This is a workaround for forcing the same time format for both server side rendering and client side rendering - // At initial render, we use the default time format which is 12H - const [withDefaultTimeFormat, setWithDefaultTimeFormat] = useState(true); - const parseDateFunc = useCallback( - (date: string | null | Dayjs) => { - return parseDate(date, i18n, withDefaultTimeFormat); - }, - [withDefaultTimeFormat] - ); - // After intial render on client side, we let parseDateFunc to use the time format from the localStorage - useEffect(() => { - setWithDefaultTimeFormat(false); - }, []); - useEffect(() => { if (top !== window) { //page_view will be collected automatically by _middleware.ts @@ -295,9 +296,12 @@ const BookingPage = ({ }, }); - const rescheduleUid = router.query.rescheduleUid as string; + const { + data: { timeFormat, rescheduleUid, date }, + } = useTypedQuery(routerQuerySchema); + useTheme(profile.theme); - const date = asStringOrNull(router.query.date); + const querySchema = getBookingResponsesPartialSchema({ eventType: { bookingFields: getBookingFieldsWithSystemFields(eventType), @@ -401,26 +405,17 @@ const BookingPage = ({ // Calculate the booking date(s) let recurringStrings: string[] = [], recurringDates: Date[] = []; - const parseRecurringDatesFunc = useCallback( - (date: string | null | Dayjs, recurringEvent: RecurringEvent, recurringCount: number) => { - return parseRecurringDates( - { - startDate: date, - timeZone: timeZone(), - recurringEvent: recurringEvent, - recurringCount: recurringCount, - withDefaultTimeFormat: withDefaultTimeFormat, - }, - i18n - ); - }, - [withDefaultTimeFormat, date, eventType.recurringEvent, recurringEventCount] - ); + if (eventType.recurringEvent?.freq && recurringEventCount !== null) { - [recurringStrings, recurringDates] = parseRecurringDatesFunc( - date, - eventType.recurringEvent, - parseInt(recurringEventCount.toString()) + [recurringStrings, recurringDates] = parseRecurringDates( + { + startDate: date, + timeZone: timeZone(), + recurringEvent: eventType.recurringEvent, + recurringCount: parseInt(recurringEventCount.toString()), + selectedTimeFormat: timeFormat, + }, + i18n ); } @@ -550,7 +545,7 @@ const BookingPage = ({
{isClientTimezoneAvailable && (rescheduleUid || !eventType.recurringEvent?.freq) && - `${parseDateFunc(date)}`} + `${parseDate(date, i18n, timeFormat)}`} {isClientTimezoneAvailable && !rescheduleUid && eventType.recurringEvent?.freq && @@ -580,7 +575,7 @@ const BookingPage = ({ {isClientTimezoneAvailable && typeof booking.startTime === "string" && - parseDateFunc(dayjs(booking.startTime))} + parseDate(dayjs(booking.startTime), i18n, timeFormat)}

)} diff --git a/apps/web/lib/parseDate.ts b/apps/web/lib/parseDate.ts index 54b34fbbaf..b262b5569e 100644 --- a/apps/web/lib/parseDate.ts +++ b/apps/web/lib/parseDate.ts @@ -3,23 +3,22 @@ import { RRule } from "rrule"; import type { Dayjs } from "@calcom/dayjs"; import dayjs from "@calcom/dayjs"; -import { detectBrowserTimeFormat, TimeFormat } from "@calcom/lib/timeFormat"; +import type { TimeFormat } from "@calcom/lib/timeFormat"; +import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat"; import type { RecurringEvent } from "@calcom/types/Calendar"; import { parseZone } from "./parseZone"; -const processDate = (date: string | null | Dayjs, i18n: I18n, withDefaultTimeFormat: boolean) => { +const processDate = (date: string | null | Dayjs, i18n: I18n, selectedTimeFormat?: TimeFormat) => { const parsedZone = parseZone(date); if (!parsedZone?.isValid()) return "Invalid date"; - const formattedTime = parsedZone?.format( - withDefaultTimeFormat ? TimeFormat.TWELVE_HOUR : detectBrowserTimeFormat - ); + const formattedTime = parsedZone?.format(selectedTimeFormat || detectBrowserTimeFormat); return formattedTime + ", " + dayjs(date).toDate().toLocaleString(i18n.language, { dateStyle: "full" }); }; -export const parseDate = (date: string | null | Dayjs, i18n: I18n, withDefaultTimeFormat: boolean) => { +export const parseDate = (date: string | null | Dayjs, i18n: I18n, selectedTimeFormat?: TimeFormat) => { if (!date) return ["No date"]; - return processDate(date, i18n, withDefaultTimeFormat); + return processDate(date, i18n, selectedTimeFormat); }; export const parseRecurringDates = ( @@ -28,13 +27,13 @@ export const parseRecurringDates = ( timeZone, recurringEvent, recurringCount, - withDefaultTimeFormat, + selectedTimeFormat, }: { startDate: string | null | Dayjs; timeZone?: string; recurringEvent: RecurringEvent | null; recurringCount: number; - withDefaultTimeFormat: boolean; + selectedTimeFormat?: TimeFormat; }, i18n: I18n ): [string[], Date[]] => { @@ -54,7 +53,7 @@ export const parseRecurringDates = ( }); const dateStrings = times.map((t) => { // finally; show in local timeZone again - return processDate(t.tz(timeZone), i18n, withDefaultTimeFormat); + return processDate(t.tz(timeZone), i18n, selectedTimeFormat); }); return [dateStrings, times.map((t) => t.toDate())];