From 4f92d1da0878b367676bf7922d11ff3513b2eaa5 Mon Sep 17 00:00:00 2001 From: Pontus Dreij Date: Wed, 11 Dec 2024 13:39:15 +0100 Subject: [PATCH 1/5] feat(SW-977): Scenario 1 --- components/BookingWidget/Client.tsx | 4 ++-- .../FormContent/Search/index.tsx | 23 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/components/BookingWidget/Client.tsx b/components/BookingWidget/Client.tsx index 3c17caf8e..40eb5ccd7 100644 --- a/components/BookingWidget/Client.tsx +++ b/components/BookingWidget/Client.tsx @@ -79,8 +79,8 @@ export default function BookingWidgetClient({ const selectedLocation = bookingWidgetSearchData ? getLocationObj( - (bookingWidgetSearchData.city ?? - bookingWidgetSearchData.hotel) as string + (bookingWidgetSearchData.hotel ?? + bookingWidgetSearchData.city) as string ) : undefined diff --git a/components/Forms/BookingWidget/FormContent/Search/index.tsx b/components/Forms/BookingWidget/FormContent/Search/index.tsx index 28b53bb1b..607fb709b 100644 --- a/components/Forms/BookingWidget/FormContent/Search/index.tsx +++ b/components/Forms/BookingWidget/FormContent/Search/index.tsx @@ -28,16 +28,19 @@ import type { Location } from "@/types/trpc/routers/hotel/locations" const name = "search" export default function Search({ locations, handlePressEnter }: SearchProps) { - const { register, setValue, unregister } = + const { register, setValue, unregister, getValues } = useFormContext() const intl = useIntl() const value = useWatch({ name }) + const locationString = getValues("location") + const location = locationString + ? JSON.parse(decodeURIComponent(locationString)) + : null const [state, dispatch] = useReducer( reducer, { defaultLocations: locations }, init ) - const handleMatchLocations = useCallback( function (searchValue: string) { return locations.filter((location) => { @@ -157,6 +160,16 @@ export default function Search({ locations, handlePressEnter }: SearchProps) { } }, [stayType, stayValue, unregister, setValue]) + function getLocationLabel(): string { + if (state.searchData?.type === "hotels") { + return state.searchData?.relationships?.city?.name || "" + } + if (location?.type === "hotels") { + return location?.relationships?.city?.name || "" + } + return intl.formatMessage({ id: "Where to" }) + } + return ( - - {state.searchData?.type === "hotels" - ? state.searchData?.relationships?.city?.name - : intl.formatMessage({ id: "Where to" })} - + {getLocationLabel()}
From c635a8b072e8743c63c79fd0ab40d45a81f024f5 Mon Sep 17 00:00:00 2001 From: Pontus Dreij Date: Wed, 11 Dec 2024 14:53:51 +0100 Subject: [PATCH 2/5] feat(SW-977): Scenario 2 & 4 --- .../[contentType]/[uid]/[...paths]/page.tsx | 1 + .../[contentType]/[uid]/page.tsx | 31 +++++++++++++++++++ .../FormContent/Search/index.tsx | 13 ++++++-- 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/[...paths]/page.tsx create mode 100644 app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx diff --git a/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/[...paths]/page.tsx b/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/[...paths]/page.tsx new file mode 100644 index 000000000..03a82e5f5 --- /dev/null +++ b/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/[...paths]/page.tsx @@ -0,0 +1 @@ +export { default } from "../page" diff --git a/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx b/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx new file mode 100644 index 000000000..043da7c54 --- /dev/null +++ b/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx @@ -0,0 +1,31 @@ +import { env } from "@/env/server" +import { getHotelData, getHotelPage } from "@/lib/trpc/memoizedRequests" + +import BookingWidget, { preload } from "@/components/BookingWidget" +import { getLang } from "@/i18n/serverContext" + +import { ContentTypeParams, PageArgs } from "@/types/params" + +export default async function BookingWidgetPage({ + params, +}: PageArgs) { + if (!env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH) return null + + preload() + + if (params.contentType === "hotel-page") { + const hotelPageData = await getHotelPage() + + const hotelData = await getHotelData({ + hotelId: hotelPageData?.hotel_page_id || "", + language: getLang(), + }) + const params = new URLSearchParams() + params.set("hotel", hotelData?.data?.id || "") + params.set("city", hotelData?.data?.attributes?.cityName || "") + + return + } + + return +} diff --git a/components/Forms/BookingWidget/FormContent/Search/index.tsx b/components/Forms/BookingWidget/FormContent/Search/index.tsx index 607fb709b..92b2edfc4 100644 --- a/components/Forms/BookingWidget/FormContent/Search/index.tsx +++ b/components/Forms/BookingWidget/FormContent/Search/index.tsx @@ -124,10 +124,12 @@ export default function Search({ locations, handlePressEnter }: SearchProps) { typeof window !== "undefined" ? sessionStorage.getItem(sessionStorageKey) : undefined + const searchHistory = typeof window !== "undefined" ? localStorage.getItem(localStorageKey) : null + if (searchData || searchHistory) { dispatch({ payload: { @@ -160,13 +162,18 @@ export default function Search({ locations, handlePressEnter }: SearchProps) { } }, [stayType, stayValue, unregister, setValue]) + useEffect(() => { + sessionStorage.setItem(sessionStorageKey, locationString) + }, [locationString]) + function getLocationLabel(): string { - if (state.searchData?.type === "hotels") { - return state.searchData?.relationships?.city?.name || "" - } if (location?.type === "hotels") { return location?.relationships?.city?.name || "" } + if (state.searchData?.type === "hotels") { + return state.searchData?.relationships?.city?.name || "" + } + return intl.formatMessage({ id: "Where to" }) } From 237147825c65b7a869232178c4d263766f960c8a Mon Sep 17 00:00:00 2001 From: Pontus Dreij Date: Tue, 17 Dec 2024 14:21:48 +0100 Subject: [PATCH 3/5] feat(SW-977) check if json is valid --- .../Forms/BookingWidget/FormContent/Search/index.tsx | 12 +++++++++--- utils/isValidJson.ts | 9 +++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 utils/isValidJson.ts diff --git a/components/Forms/BookingWidget/FormContent/Search/index.tsx b/components/Forms/BookingWidget/FormContent/Search/index.tsx index 92b2edfc4..2fc7f0bb7 100644 --- a/components/Forms/BookingWidget/FormContent/Search/index.tsx +++ b/components/Forms/BookingWidget/FormContent/Search/index.tsx @@ -13,6 +13,7 @@ import { useIntl } from "react-intl" import SkeletonShimmer from "@/components/SkeletonShimmer" import Caption from "@/components/TempDesignSystem/Text/Caption" +import isValidJson from "@/utils/isValidJson" import Input from "../Input" import { init, localStorageKey, reducer, sessionStorageKey } from "./reducer" @@ -129,12 +130,17 @@ export default function Search({ locations, handlePressEnter }: SearchProps) { typeof window !== "undefined" ? localStorage.getItem(localStorageKey) : null - if (searchData || searchHistory) { dispatch({ payload: { - searchData: searchData ? JSON.parse(searchData) : undefined, - searchHistory: searchHistory ? JSON.parse(searchHistory) : null, + searchData: + isValidJson(searchData) && searchData + ? JSON.parse(searchData) + : undefined, + searchHistory: + isValidJson(searchHistory) && searchHistory + ? JSON.parse(searchHistory) + : null, }, type: ActionType.SET_STORAGE_DATA, }) diff --git a/utils/isValidJson.ts b/utils/isValidJson.ts new file mode 100644 index 000000000..498cff121 --- /dev/null +++ b/utils/isValidJson.ts @@ -0,0 +1,9 @@ +export default function isValidJson(value: string | null | undefined): boolean { + if (!value || value === "undefined") return false + try { + JSON.parse(value) + return true + } catch { + return false + } +} From 3cd893e088c44f908039fd85ce93ff40667c9fcc Mon Sep 17 00:00:00 2001 From: Pontus Dreij Date: Tue, 17 Dec 2024 14:30:25 +0100 Subject: [PATCH 4/5] fix: build error --- app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx b/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx index 043da7c54..9e88b95f2 100644 --- a/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx +++ b/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx @@ -4,7 +4,7 @@ import { getHotelData, getHotelPage } from "@/lib/trpc/memoizedRequests" import BookingWidget, { preload } from "@/components/BookingWidget" import { getLang } from "@/i18n/serverContext" -import { ContentTypeParams, PageArgs } from "@/types/params" +import type { ContentTypeParams, PageArgs } from "@/types/params" export default async function BookingWidgetPage({ params, From 24ef1e98e0b4d24b924ced0bd5a7aa8b17e81ecf Mon Sep 17 00:00:00 2001 From: Pontus Dreij Date: Tue, 17 Dec 2024 15:29:20 +0100 Subject: [PATCH 5/5] feat(SW-977): Add more controls if Json is valid --- .../@bookingwidget/[contentType]/[uid]/page.tsx | 15 ++++++++------- components/BookingWidget/Client.tsx | 4 +++- .../BookingWidget/MobileToggleButton/index.tsx | 11 +++++------ .../BookingWidget/FormContent/Search/index.tsx | 7 ++++--- components/Forms/BookingWidget/schema.ts | 7 ++++++- types/components/bookingWidget/index.ts | 12 +++++------- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx b/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx index 9e88b95f2..fc57012f6 100644 --- a/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx +++ b/app/[lang]/(live)/@bookingwidget/[contentType]/[uid]/page.tsx @@ -8,11 +8,14 @@ import type { ContentTypeParams, PageArgs } from "@/types/params" export default async function BookingWidgetPage({ params, -}: PageArgs) { + searchParams, +}: PageArgs) { if (!env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH) return null preload() + const urlParams = new URLSearchParams() + if (params.contentType === "hotel-page") { const hotelPageData = await getHotelPage() @@ -20,12 +23,10 @@ export default async function BookingWidgetPage({ hotelId: hotelPageData?.hotel_page_id || "", language: getLang(), }) - const params = new URLSearchParams() - params.set("hotel", hotelData?.data?.id || "") - params.set("city", hotelData?.data?.attributes?.cityName || "") + urlParams.set("hotel", hotelData?.data?.id || "") + urlParams.set("city", hotelData?.data?.attributes?.cityName || "") - return + return } - - return + return } diff --git a/components/BookingWidget/Client.tsx b/components/BookingWidget/Client.tsx index 40eb5ccd7..017d9c76a 100644 --- a/components/BookingWidget/Client.tsx +++ b/components/BookingWidget/Client.tsx @@ -13,6 +13,7 @@ import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema" import { CloseLargeIcon } from "@/components/Icons" import useStickyPosition from "@/hooks/useStickyPosition" import { debounce } from "@/utils/debounce" +import isValidJson from "@/utils/isValidJson" import { getFormattedUrlQueryParams } from "@/utils/url" import MobileToggleButton, { @@ -153,8 +154,9 @@ export default function BookingWidgetClient({ typeof window !== "undefined" ? sessionStorage.getItem("searchData") : undefined + const initialSelectedLocation: Location | undefined = - sessionStorageSearchData + sessionStorageSearchData && isValidJson(sessionStorageSearchData) ? JSON.parse(sessionStorageSearchData) : undefined diff --git a/components/BookingWidget/MobileToggleButton/index.tsx b/components/BookingWidget/MobileToggleButton/index.tsx index 99e691359..ce4513d58 100644 --- a/components/BookingWidget/MobileToggleButton/index.tsx +++ b/components/BookingWidget/MobileToggleButton/index.tsx @@ -1,10 +1,8 @@ "use client" -import { useEffect, useMemo, useRef, useState } from "react" import { useWatch } from "react-hook-form" import { useIntl } from "react-intl" import { dt } from "@/lib/dt" -import { StickyElementNameEnum } from "@/stores/sticky-position" import { EditIcon, SearchIcon } from "@/components/Icons" import SkeletonShimmer from "@/components/SkeletonShimmer" @@ -12,7 +10,7 @@ import Divider from "@/components/TempDesignSystem/Divider" import Body from "@/components/TempDesignSystem/Text/Body" import Caption from "@/components/TempDesignSystem/Text/Caption" import useLang from "@/hooks/useLang" -import useStickyPosition from "@/hooks/useStickyPosition" +import isValidJson from "@/utils/isValidJson" import styles from "./button.module.css" @@ -31,9 +29,10 @@ export default function MobileToggleButton({ const location = useWatch({ name: "location" }) const rooms: BookingWidgetSchema["rooms"] = useWatch({ name: "rooms" }) - const parsedLocation: Location | null = location - ? JSON.parse(decodeURIComponent(location)) - : null + const parsedLocation: Location | null = + location && isValidJson(location) + ? JSON.parse(decodeURIComponent(location)) + : null const nights = dt(d.toDate).diff(dt(d.fromDate), "days") diff --git a/components/Forms/BookingWidget/FormContent/Search/index.tsx b/components/Forms/BookingWidget/FormContent/Search/index.tsx index 2fc7f0bb7..bc6c10152 100644 --- a/components/Forms/BookingWidget/FormContent/Search/index.tsx +++ b/components/Forms/BookingWidget/FormContent/Search/index.tsx @@ -34,9 +34,10 @@ export default function Search({ locations, handlePressEnter }: SearchProps) { const intl = useIntl() const value = useWatch({ name }) const locationString = getValues("location") - const location = locationString - ? JSON.parse(decodeURIComponent(locationString)) - : null + const location = + locationString && isValidJson(locationString) + ? JSON.parse(decodeURIComponent(locationString)) + : null const [state, dispatch] = useReducer( reducer, { defaultLocations: locations }, diff --git a/components/Forms/BookingWidget/schema.ts b/components/Forms/BookingWidget/schema.ts index a5e62a989..c8beb98d5 100644 --- a/components/Forms/BookingWidget/schema.ts +++ b/components/Forms/BookingWidget/schema.ts @@ -45,7 +45,10 @@ export const bookingWidgetSchema = z }), location: z.string().refine( (value) => { - if (value) { + if (!value) { + return false + } + try { const parsedValue: Location = JSON.parse(decodeURIComponent(value)) switch (parsedValue?.type) { case "cities": @@ -54,6 +57,8 @@ export const bookingWidgetSchema = z default: return false } + } catch (error) { + return false } }, { message: "Required" } diff --git a/types/components/bookingWidget/index.ts b/types/components/bookingWidget/index.ts index c668617da..7e9c79d16 100644 --- a/types/components/bookingWidget/index.ts +++ b/types/components/bookingWidget/index.ts @@ -1,12 +1,10 @@ -import { VariantProps } from "class-variance-authority" -import { z } from "zod" - -import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema" -import { bookingWidgetVariants } from "@/components/Forms/BookingWidget/variants" - -import { GuestsRoom } from "./guestsRoomsPicker" +import type { VariantProps } from "class-variance-authority" +import type { z } from "zod" import type { Locations } from "@/types/trpc/routers/hotel/locations" +import type { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema" +import type { bookingWidgetVariants } from "@/components/Forms/BookingWidget/variants" +import type { GuestsRoom } from "./guestsRoomsPicker" export type BookingWidgetSchema = z.output