Merged in feat/SW-977-update-bookingwidget-with-hotel-name (pull request #1071)
Feat/SW-977 update bookingwidget with hotel name Approved-by: Fredrik Thorsson
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../page"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { env } from "@/env/server"
|
||||||
|
import { getHotelData, getHotelPage } from "@/lib/trpc/memoizedRequests"
|
||||||
|
|
||||||
|
import BookingWidget, { preload } from "@/components/BookingWidget"
|
||||||
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import type { ContentTypeParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function BookingWidgetPage({
|
||||||
|
params,
|
||||||
|
searchParams,
|
||||||
|
}: PageArgs<ContentTypeParams, URLSearchParams>) {
|
||||||
|
if (!env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH) return null
|
||||||
|
|
||||||
|
preload()
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams()
|
||||||
|
|
||||||
|
if (params.contentType === "hotel-page") {
|
||||||
|
const hotelPageData = await getHotelPage()
|
||||||
|
|
||||||
|
const hotelData = await getHotelData({
|
||||||
|
hotelId: hotelPageData?.hotel_page_id || "",
|
||||||
|
language: getLang(),
|
||||||
|
})
|
||||||
|
urlParams.set("hotel", hotelData?.data?.id || "")
|
||||||
|
urlParams.set("city", hotelData?.data?.attributes?.cityName || "")
|
||||||
|
|
||||||
|
return <BookingWidget searchParams={urlParams} />
|
||||||
|
}
|
||||||
|
return <BookingWidget searchParams={searchParams} />
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema"
|
|||||||
import { CloseLargeIcon } from "@/components/Icons"
|
import { CloseLargeIcon } from "@/components/Icons"
|
||||||
import useStickyPosition from "@/hooks/useStickyPosition"
|
import useStickyPosition from "@/hooks/useStickyPosition"
|
||||||
import { debounce } from "@/utils/debounce"
|
import { debounce } from "@/utils/debounce"
|
||||||
|
import isValidJson from "@/utils/isValidJson"
|
||||||
import { getFormattedUrlQueryParams } from "@/utils/url"
|
import { getFormattedUrlQueryParams } from "@/utils/url"
|
||||||
|
|
||||||
import MobileToggleButton, {
|
import MobileToggleButton, {
|
||||||
@@ -79,8 +80,8 @@ export default function BookingWidgetClient({
|
|||||||
|
|
||||||
const selectedLocation = bookingWidgetSearchData
|
const selectedLocation = bookingWidgetSearchData
|
||||||
? getLocationObj(
|
? getLocationObj(
|
||||||
(bookingWidgetSearchData.city ??
|
(bookingWidgetSearchData.hotel ??
|
||||||
bookingWidgetSearchData.hotel) as string
|
bookingWidgetSearchData.city) as string
|
||||||
)
|
)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
@@ -153,8 +154,9 @@ export default function BookingWidgetClient({
|
|||||||
typeof window !== "undefined"
|
typeof window !== "undefined"
|
||||||
? sessionStorage.getItem("searchData")
|
? sessionStorage.getItem("searchData")
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const initialSelectedLocation: Location | undefined =
|
const initialSelectedLocation: Location | undefined =
|
||||||
sessionStorageSearchData
|
sessionStorageSearchData && isValidJson(sessionStorageSearchData)
|
||||||
? JSON.parse(sessionStorageSearchData)
|
? JSON.parse(sessionStorageSearchData)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useMemo, useRef, useState } from "react"
|
|
||||||
import { useWatch } from "react-hook-form"
|
import { useWatch } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { dt } from "@/lib/dt"
|
import { dt } from "@/lib/dt"
|
||||||
import { StickyElementNameEnum } from "@/stores/sticky-position"
|
|
||||||
|
|
||||||
import { EditIcon, SearchIcon } from "@/components/Icons"
|
import { EditIcon, SearchIcon } from "@/components/Icons"
|
||||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||||
@@ -12,7 +10,7 @@ import Divider from "@/components/TempDesignSystem/Divider"
|
|||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
import useStickyPosition from "@/hooks/useStickyPosition"
|
import isValidJson from "@/utils/isValidJson"
|
||||||
|
|
||||||
import styles from "./button.module.css"
|
import styles from "./button.module.css"
|
||||||
|
|
||||||
@@ -31,9 +29,10 @@ export default function MobileToggleButton({
|
|||||||
const location = useWatch({ name: "location" })
|
const location = useWatch({ name: "location" })
|
||||||
const rooms: BookingWidgetSchema["rooms"] = useWatch({ name: "rooms" })
|
const rooms: BookingWidgetSchema["rooms"] = useWatch({ name: "rooms" })
|
||||||
|
|
||||||
const parsedLocation: Location | null = location
|
const parsedLocation: Location | null =
|
||||||
? JSON.parse(decodeURIComponent(location))
|
location && isValidJson(location)
|
||||||
: null
|
? JSON.parse(decodeURIComponent(location))
|
||||||
|
: null
|
||||||
|
|
||||||
const nights = dt(d.toDate).diff(dt(d.fromDate), "days")
|
const nights = dt(d.toDate).diff(dt(d.fromDate), "days")
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
import isValidJson from "@/utils/isValidJson"
|
||||||
|
|
||||||
import Input from "../Input"
|
import Input from "../Input"
|
||||||
import { init, localStorageKey, reducer, sessionStorageKey } from "./reducer"
|
import { init, localStorageKey, reducer, sessionStorageKey } from "./reducer"
|
||||||
@@ -28,16 +29,20 @@ import type { Location } from "@/types/trpc/routers/hotel/locations"
|
|||||||
const name = "search"
|
const name = "search"
|
||||||
|
|
||||||
export default function Search({ locations, handlePressEnter }: SearchProps) {
|
export default function Search({ locations, handlePressEnter }: SearchProps) {
|
||||||
const { register, setValue, unregister } =
|
const { register, setValue, unregister, getValues } =
|
||||||
useFormContext<BookingWidgetSchema>()
|
useFormContext<BookingWidgetSchema>()
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const value = useWatch({ name })
|
const value = useWatch({ name })
|
||||||
|
const locationString = getValues("location")
|
||||||
|
const location =
|
||||||
|
locationString && isValidJson(locationString)
|
||||||
|
? JSON.parse(decodeURIComponent(locationString))
|
||||||
|
: null
|
||||||
const [state, dispatch] = useReducer(
|
const [state, dispatch] = useReducer(
|
||||||
reducer,
|
reducer,
|
||||||
{ defaultLocations: locations },
|
{ defaultLocations: locations },
|
||||||
init
|
init
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMatchLocations = useCallback(
|
const handleMatchLocations = useCallback(
|
||||||
function (searchValue: string) {
|
function (searchValue: string) {
|
||||||
return locations.filter((location) => {
|
return locations.filter((location) => {
|
||||||
@@ -121,6 +126,7 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
|||||||
typeof window !== "undefined"
|
typeof window !== "undefined"
|
||||||
? sessionStorage.getItem(sessionStorageKey)
|
? sessionStorage.getItem(sessionStorageKey)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const searchHistory =
|
const searchHistory =
|
||||||
typeof window !== "undefined"
|
typeof window !== "undefined"
|
||||||
? localStorage.getItem(localStorageKey)
|
? localStorage.getItem(localStorageKey)
|
||||||
@@ -128,8 +134,14 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
|||||||
if (searchData || searchHistory) {
|
if (searchData || searchHistory) {
|
||||||
dispatch({
|
dispatch({
|
||||||
payload: {
|
payload: {
|
||||||
searchData: searchData ? JSON.parse(searchData) : undefined,
|
searchData:
|
||||||
searchHistory: searchHistory ? JSON.parse(searchHistory) : null,
|
isValidJson(searchData) && searchData
|
||||||
|
? JSON.parse(searchData)
|
||||||
|
: undefined,
|
||||||
|
searchHistory:
|
||||||
|
isValidJson(searchHistory) && searchHistory
|
||||||
|
? JSON.parse(searchHistory)
|
||||||
|
: null,
|
||||||
},
|
},
|
||||||
type: ActionType.SET_STORAGE_DATA,
|
type: ActionType.SET_STORAGE_DATA,
|
||||||
})
|
})
|
||||||
@@ -157,6 +169,21 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
|||||||
}
|
}
|
||||||
}, [stayType, stayValue, unregister, setValue])
|
}, [stayType, stayValue, unregister, setValue])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sessionStorage.setItem(sessionStorageKey, locationString)
|
||||||
|
}, [locationString])
|
||||||
|
|
||||||
|
function getLocationLabel(): string {
|
||||||
|
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" })
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Downshift
|
<Downshift
|
||||||
initialSelectedItem={state.searchData}
|
initialSelectedItem={state.searchData}
|
||||||
@@ -187,11 +214,7 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
|||||||
color={isOpen ? "uiTextActive" : "red"}
|
color={isOpen ? "uiTextActive" : "red"}
|
||||||
asChild
|
asChild
|
||||||
>
|
>
|
||||||
<span>
|
<span>{getLocationLabel()}</span>
|
||||||
{state.searchData?.type === "hotels"
|
|
||||||
? state.searchData?.relationships?.city?.name
|
|
||||||
: intl.formatMessage({ id: "Where to" })}
|
|
||||||
</span>
|
|
||||||
</Caption>
|
</Caption>
|
||||||
</label>
|
</label>
|
||||||
<div {...getRootProps({}, { suppressRefError: true })}>
|
<div {...getRootProps({}, { suppressRefError: true })}>
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ export const bookingWidgetSchema = z
|
|||||||
}),
|
}),
|
||||||
location: z.string().refine(
|
location: z.string().refine(
|
||||||
(value) => {
|
(value) => {
|
||||||
if (value) {
|
if (!value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
try {
|
||||||
const parsedValue: Location = JSON.parse(decodeURIComponent(value))
|
const parsedValue: Location = JSON.parse(decodeURIComponent(value))
|
||||||
switch (parsedValue?.type) {
|
switch (parsedValue?.type) {
|
||||||
case "cities":
|
case "cities":
|
||||||
@@ -54,6 +57,8 @@ export const bookingWidgetSchema = z
|
|||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ message: "Required" }
|
{ message: "Required" }
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { VariantProps } from "class-variance-authority"
|
import type { VariantProps } from "class-variance-authority"
|
||||||
import { z } from "zod"
|
import type { z } from "zod"
|
||||||
|
|
||||||
import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema"
|
|
||||||
import { bookingWidgetVariants } from "@/components/Forms/BookingWidget/variants"
|
|
||||||
|
|
||||||
import { GuestsRoom } from "./guestsRoomsPicker"
|
|
||||||
|
|
||||||
import type { Locations } from "@/types/trpc/routers/hotel/locations"
|
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<typeof bookingWidgetSchema>
|
export type BookingWidgetSchema = z.output<typeof bookingWidgetSchema>
|
||||||
|
|
||||||
|
|||||||
9
utils/isValidJson.ts
Normal file
9
utils/isValidJson.ts
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user