refactor: url management in hotel reservation flow
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
.page {
|
.page {
|
||||||
background-color: var(--Base-Background-Primary-Normal);
|
background-color: var(--Base-Background-Primary-Normal);
|
||||||
min-height: 50dvh;
|
min-height: 50dvh;
|
||||||
max-width: var(--max-width);
|
max-width: var(--max-width-page);
|
||||||
|
margin: 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -20,8 +20,13 @@ export default async function SelectHotelMapPage({
|
|||||||
setLang(params.lang)
|
setLang(params.lang)
|
||||||
const searchDetails = await getHotelSearchDetails({ searchParams })
|
const searchDetails = await getHotelSearchDetails({ searchParams })
|
||||||
if (!searchDetails) return notFound()
|
if (!searchDetails) return notFound()
|
||||||
const { city, adultsInRoom, childrenInRoom, childrenInRoomArray } =
|
const {
|
||||||
searchDetails
|
city,
|
||||||
|
adultsInRoom,
|
||||||
|
childrenInRoom,
|
||||||
|
childrenInRoomArray,
|
||||||
|
selectHotelParams,
|
||||||
|
} = searchDetails
|
||||||
|
|
||||||
if (!city) return notFound()
|
if (!city) return notFound()
|
||||||
|
|
||||||
@@ -34,7 +39,7 @@ export default async function SelectHotelMapPage({
|
|||||||
>
|
>
|
||||||
<SelectHotelMapContainer
|
<SelectHotelMapContainer
|
||||||
city={city}
|
city={city}
|
||||||
searchParams={searchParams}
|
selectHotelParams={selectHotelParams}
|
||||||
adultsInRoom={adultsInRoom}
|
adultsInRoom={adultsInRoom}
|
||||||
childrenInRoom={childrenInRoom}
|
childrenInRoom={childrenInRoom}
|
||||||
child={childrenInRoomArray}
|
child={childrenInRoomArray}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Suspense } from "react"
|
|||||||
|
|
||||||
import SelectHotel from "@/components/HotelReservation/SelectHotel"
|
import SelectHotel from "@/components/HotelReservation/SelectHotel"
|
||||||
import { SelectHotelSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelSkeleton"
|
import { SelectHotelSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelSkeleton"
|
||||||
import { getHotelReservationQueryParams } from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
|
|
||||||
import { setLang } from "@/i18n/serverContext"
|
import { setLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
import { getHotelSearchDetails } from "../utils"
|
import { getHotelSearchDetails } from "../utils"
|
||||||
@@ -20,7 +19,7 @@ export default async function SelectHotelPage({
|
|||||||
if (!searchDetails) return notFound()
|
if (!searchDetails) return notFound()
|
||||||
const {
|
const {
|
||||||
city,
|
city,
|
||||||
urlSearchParams,
|
selectHotelParams,
|
||||||
adultsInRoom,
|
adultsInRoom,
|
||||||
childrenInRoom,
|
childrenInRoom,
|
||||||
childrenInRoomArray,
|
childrenInRoomArray,
|
||||||
@@ -29,8 +28,7 @@ export default async function SelectHotelPage({
|
|||||||
if (!city) return notFound()
|
if (!city) return notFound()
|
||||||
|
|
||||||
const reservationParams = {
|
const reservationParams = {
|
||||||
selectHotelParams: urlSearchParams,
|
selectHotelParams,
|
||||||
searchParams,
|
|
||||||
adultsInRoom,
|
adultsInRoom,
|
||||||
childrenInRoom,
|
childrenInRoom,
|
||||||
childrenInRoomArray,
|
childrenInRoomArray,
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export default async function SelectRatePage({
|
|||||||
setLang(params.lang)
|
setLang(params.lang)
|
||||||
const searchDetails = await getHotelSearchDetails({ searchParams })
|
const searchDetails = await getHotelSearchDetails({ searchParams })
|
||||||
if (!searchDetails) return notFound()
|
if (!searchDetails) return notFound()
|
||||||
const { hotel, adultsInRoom, childrenInRoomArray } = searchDetails
|
const { hotel, adultsInRoom, childrenInRoomArray, selectHotelParams } =
|
||||||
|
searchDetails
|
||||||
|
|
||||||
if (!hotel) return notFound()
|
if (!hotel) return notFound()
|
||||||
|
|
||||||
@@ -39,12 +40,12 @@ export default async function SelectRatePage({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const { fromDate, toDate } = getValidDates(
|
const { fromDate, toDate } = getValidDates(
|
||||||
searchParams.fromDate,
|
selectHotelParams.fromDate,
|
||||||
searchParams.toDate
|
selectHotelParams.toDate
|
||||||
)
|
)
|
||||||
|
|
||||||
const arrivalDate = new Date(searchParams.fromDate)
|
const arrivalDate = fromDate.toDate()
|
||||||
const departureDate = new Date(searchParams.toDate)
|
const departureDate = toDate.toDate()
|
||||||
|
|
||||||
const pageTrackingData: TrackingSDKPageData = {
|
const pageTrackingData: TrackingSDKPageData = {
|
||||||
pageId: "select-rate",
|
pageId: "select-rate",
|
||||||
@@ -57,7 +58,7 @@ export default async function SelectRatePage({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hotelsTrackingData: TrackingSDKHotelInfo = {
|
const hotelsTrackingData: TrackingSDKHotelInfo = {
|
||||||
searchTerm: searchParams.city ?? hotel?.name,
|
searchTerm: selectHotelParams.city ?? hotel?.name,
|
||||||
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
|
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
|
||||||
departureDate: format(departureDate, "yyyy-MM-dd"),
|
departureDate: format(departureDate, "yyyy-MM-dd"),
|
||||||
noOfAdults: adultsInRoom,
|
noOfAdults: adultsInRoom,
|
||||||
|
|||||||
@@ -21,21 +21,19 @@ import SectionAccordion from "@/components/HotelReservation/EnterDetails/Section
|
|||||||
import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom"
|
import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom"
|
||||||
import DesktopSummary from "@/components/HotelReservation/EnterDetails/Summary/Desktop"
|
import DesktopSummary from "@/components/HotelReservation/EnterDetails/Summary/Desktop"
|
||||||
import MobileSummary from "@/components/HotelReservation/EnterDetails/Summary/Mobile"
|
import MobileSummary from "@/components/HotelReservation/EnterDetails/Summary/Mobile"
|
||||||
import {
|
import { generateChildrenString } from "@/components/HotelReservation/utils"
|
||||||
generateChildrenString,
|
|
||||||
getQueryParamsForEnterDetails,
|
|
||||||
} from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
|
|
||||||
import TrackingSDK from "@/components/TrackingSDK"
|
import TrackingSDK from "@/components/TrackingSDK"
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
import { setLang } from "@/i18n/serverContext"
|
import { setLang } from "@/i18n/serverContext"
|
||||||
import EnterDetailsProvider from "@/providers/EnterDetailsProvider"
|
import EnterDetailsProvider from "@/providers/EnterDetailsProvider"
|
||||||
|
import { convertSearchParamsToObj } from "@/utils/url"
|
||||||
|
|
||||||
import EnterDetailsTracking from "./enterDetailsTracking"
|
import EnterDetailsTracking from "./enterDetailsTracking"
|
||||||
|
|
||||||
import styles from "./page.module.css"
|
import styles from "./page.module.css"
|
||||||
|
|
||||||
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
||||||
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
|
import { type SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||||
import {
|
import {
|
||||||
TrackingChannelEnum,
|
TrackingChannelEnum,
|
||||||
type TrackingSDKHotelInfo,
|
type TrackingSDKHotelInfo,
|
||||||
@@ -61,10 +59,10 @@ export default async function StepPage({
|
|||||||
const selectRoomParams = new URLSearchParams(searchParams)
|
const selectRoomParams = new URLSearchParams(searchParams)
|
||||||
// Deleting step to avoid double searchparams after rewrite
|
// Deleting step to avoid double searchparams after rewrite
|
||||||
selectRoomParams.delete("step")
|
selectRoomParams.delete("step")
|
||||||
const booking = getQueryParamsForEnterDetails(selectRoomParams)
|
const booking = convertSearchParamsToObj<SelectRateSearchParams>(searchParams)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
hotel: hotelId,
|
hotelId,
|
||||||
rooms: [
|
rooms: [
|
||||||
{ adults, children, roomTypeCode, rateCode, packages: packageCodes },
|
{ adults, children, roomTypeCode, rateCode, packages: packageCodes },
|
||||||
], // TODO: Handle multiple rooms
|
], // TODO: Handle multiple rooms
|
||||||
@@ -153,8 +151,8 @@ export default async function StepPage({
|
|||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const arrivalDate = new Date(searchParams.fromDate)
|
const arrivalDate = new Date(fromDate)
|
||||||
const departureDate = new Date(searchParams.toDate)
|
const departureDate = new Date(toDate)
|
||||||
const hotelAttributes = hotelData?.data.attributes
|
const hotelAttributes = hotelData?.data.attributes
|
||||||
|
|
||||||
const initialPageTrackingData: TrackingSDKPageData = {
|
const initialPageTrackingData: TrackingSDKPageData = {
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ import { notFound } from "next/navigation"
|
|||||||
|
|
||||||
import { getLocations } from "@/lib/trpc/memoizedRequests"
|
import { getLocations } from "@/lib/trpc/memoizedRequests"
|
||||||
|
|
||||||
import {
|
import { generateChildrenString } from "@/components/HotelReservation/utils"
|
||||||
generateChildrenString,
|
import { convertSearchParamsToObj } from "@/utils/url"
|
||||||
getHotelReservationQueryParams,
|
|
||||||
} from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
|
|
||||||
|
|
||||||
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
|
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
|
||||||
import type {
|
import type {
|
||||||
@@ -14,62 +12,59 @@ import type {
|
|||||||
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||||
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
||||||
|
|
||||||
interface HotelSearchDetails {
|
interface HotelSearchDetails<T> {
|
||||||
city: Location | null
|
city: Location | null
|
||||||
hotel: Location | null
|
hotel: Location | null
|
||||||
urlSearchParams?: URLSearchParams
|
selectHotelParams: T
|
||||||
adultsInRoom: number
|
adultsInRoom: number
|
||||||
childrenInRoom?: string
|
childrenInRoom?: string
|
||||||
childrenInRoomArray?: Child[]
|
childrenInRoomArray?: Child[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getHotelSearchDetails({
|
export async function getHotelSearchDetails<
|
||||||
|
T extends SelectHotelSearchParams | SelectRateSearchParams,
|
||||||
|
>({
|
||||||
searchParams,
|
searchParams,
|
||||||
}: {
|
}: {
|
||||||
searchParams:
|
searchParams: T & {
|
||||||
| (SelectHotelSearchParams & {
|
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
})
|
}
|
||||||
| (SelectRateSearchParams & {
|
}): Promise<HotelSearchDetails<T> | null> {
|
||||||
[key: string]: string
|
const selectHotelParams = convertSearchParamsToObj<T>(searchParams)
|
||||||
})
|
|
||||||
}): Promise<HotelSearchDetails | null> {
|
|
||||||
const locations = await getLocations()
|
const locations = await getLocations()
|
||||||
|
|
||||||
if (!locations || "error" in locations) return null
|
if (!locations || "error" in locations) return null
|
||||||
|
|
||||||
const city = locations.data.find(
|
const city = locations.data.find(
|
||||||
(location) =>
|
(location) =>
|
||||||
location.name.toLowerCase() === searchParams.city?.toLowerCase()
|
location.name.toLowerCase() === selectHotelParams.city?.toLowerCase()
|
||||||
)
|
)
|
||||||
const hotel = locations.data.find(
|
const hotel = locations.data.find(
|
||||||
(location) =>
|
(location) =>
|
||||||
"operaId" in location && location.operaId == searchParams.hotel
|
"operaId" in location && location.operaId == selectHotelParams.hotelId
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!city && !hotel) return notFound()
|
if (!city && !hotel) return notFound()
|
||||||
|
|
||||||
const urlSearchParams = new URLSearchParams(searchParams)
|
|
||||||
const searchParamsObject = getHotelReservationQueryParams(urlSearchParams)
|
|
||||||
|
|
||||||
let adultsInRoom = 1
|
let adultsInRoom = 1
|
||||||
let childrenInRoom: string | undefined = undefined
|
let childrenInRoom: string | undefined = undefined
|
||||||
let childrenInRoomArray: Child[] | undefined = undefined
|
let childrenInRoomArray: Child[] | undefined = undefined
|
||||||
|
|
||||||
if (searchParamsObject.room && searchParamsObject.room.length > 0) {
|
const { rooms } = selectHotelParams
|
||||||
adultsInRoom = searchParamsObject.room[0].adults // TODO: Handle multiple rooms
|
|
||||||
childrenInRoom = searchParamsObject.room[0].child
|
if (rooms && rooms.length > 0) {
|
||||||
? generateChildrenString(searchParamsObject.room[0].child)
|
adultsInRoom = rooms[0].adults // TODO: Handle multiple rooms
|
||||||
: undefined // TODO: Handle multiple rooms
|
childrenInRoom = rooms[0].children
|
||||||
childrenInRoomArray = searchParamsObject.room[0].child
|
? generateChildrenString(rooms[0].children)
|
||||||
? searchParamsObject.room[0].child
|
|
||||||
: undefined // TODO: Handle multiple rooms
|
: undefined // TODO: Handle multiple rooms
|
||||||
|
childrenInRoomArray = rooms[0].children ? rooms[0].children : undefined // TODO: Handle multiple rooms
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
city: city ?? null,
|
city: city ?? null,
|
||||||
hotel: hotel ?? null,
|
hotel: hotel ?? null,
|
||||||
urlSearchParams,
|
selectHotelParams,
|
||||||
adultsInRoom,
|
adultsInRoom,
|
||||||
childrenInRoom,
|
childrenInRoom,
|
||||||
childrenInRoomArray,
|
childrenInRoomArray,
|
||||||
|
|||||||
@@ -4,21 +4,20 @@ import { getHotelData, getHotelPage } from "@/lib/trpc/memoizedRequests"
|
|||||||
import BookingWidget, { preload } from "@/components/BookingWidget"
|
import BookingWidget, { preload } from "@/components/BookingWidget"
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
|
||||||
import type { ContentTypeParams, PageArgs } from "@/types/params"
|
import type { ContentTypeParams, PageArgs } from "@/types/params"
|
||||||
import { PageContentTypeEnum } from "@/types/requests/contentType"
|
import { PageContentTypeEnum } from "@/types/requests/contentType"
|
||||||
|
|
||||||
export default async function BookingWidgetPage({
|
export default async function BookingWidgetPage({
|
||||||
params,
|
params,
|
||||||
searchParams,
|
searchParams,
|
||||||
}: PageArgs<ContentTypeParams, URLSearchParams>) {
|
}: PageArgs<ContentTypeParams, BookingWidgetSearchData>) {
|
||||||
if (!env.ENABLE_BOOKING_WIDGET) {
|
if (!env.ENABLE_BOOKING_WIDGET) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
preload()
|
preload()
|
||||||
|
|
||||||
const urlParams = new URLSearchParams()
|
|
||||||
|
|
||||||
if (params.contentType === PageContentTypeEnum.hotelPage) {
|
if (params.contentType === PageContentTypeEnum.hotelPage) {
|
||||||
const hotelPageData = await getHotelPage()
|
const hotelPageData = await getHotelPage()
|
||||||
|
|
||||||
@@ -26,10 +25,14 @@ export default async function BookingWidgetPage({
|
|||||||
hotelId: hotelPageData?.hotel_page_id || "",
|
hotelId: hotelPageData?.hotel_page_id || "",
|
||||||
language: getLang(),
|
language: getLang(),
|
||||||
})
|
})
|
||||||
urlParams.set("hotel", hotelData?.data?.id || "")
|
|
||||||
urlParams.set("city", hotelData?.data?.attributes?.cityName || "")
|
|
||||||
|
|
||||||
return <BookingWidget searchParams={urlParams} />
|
const hotelPageParams = {
|
||||||
|
hotel: hotelData?.data?.id || "",
|
||||||
|
city: hotelData?.data?.attributes?.cityName || "",
|
||||||
}
|
}
|
||||||
return <BookingWidget searchParams={searchParams} />
|
|
||||||
|
return <BookingWidget bookingWidgetSearchParams={hotelPageParams} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <BookingWidget bookingWidgetSearchParams={searchParams} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ import { env } from "@/env/server"
|
|||||||
|
|
||||||
import BookingWidget, { preload } from "@/components/BookingWidget"
|
import BookingWidget, { preload } from "@/components/BookingWidget"
|
||||||
|
|
||||||
import type { PageArgs } from "@/types/params"
|
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
|
||||||
|
import type { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
export default async function BookingWidgetPage({
|
export default async function BookingWidgetPage({
|
||||||
searchParams,
|
searchParams,
|
||||||
}: PageArgs<{}, URLSearchParams>) {
|
}: PageArgs<LangParams, BookingWidgetSearchData>) {
|
||||||
if (!env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH) {
|
if (!env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
preload()
|
preload()
|
||||||
|
|
||||||
return <BookingWidget searchParams={searchParams} />
|
return <BookingWidget bookingWidgetSearchParams={searchParams} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { serverClient } from "@/lib/trpc/server"
|
|||||||
|
|
||||||
import BookingWidget, { preload } from "@/components/BookingWidget"
|
import BookingWidget, { preload } from "@/components/BookingWidget"
|
||||||
|
|
||||||
|
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
|
||||||
import type { PageArgs } from "@/types/params"
|
import type { PageArgs } from "@/types/params"
|
||||||
|
|
||||||
export default async function BookingWidgetPage({
|
export default async function BookingWidgetPage({
|
||||||
searchParams,
|
searchParams,
|
||||||
}: PageArgs<{}, URLSearchParams>) {
|
}: PageArgs<{}, BookingWidgetSearchData>) {
|
||||||
if (!env.ENABLE_BOOKING_WIDGET) {
|
if (!env.ENABLE_BOOKING_WIDGET) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -22,5 +23,5 @@ export default async function BookingWidgetPage({
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <BookingWidget searchParams={searchParams} />
|
return <BookingWidget bookingWidgetSearchParams={searchParams} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import "@scandic-hotels/design-system/style.css"
|
|||||||
import Script from "next/script"
|
import Script from "next/script"
|
||||||
|
|
||||||
import TokenRefresher from "@/components/Auth/TokenRefresher"
|
import TokenRefresher from "@/components/Auth/TokenRefresher"
|
||||||
import BookingWidget from "@/components/BookingWidget"
|
|
||||||
import CookieBotConsent from "@/components/CookieBot"
|
import CookieBotConsent from "@/components/CookieBot"
|
||||||
import AdobeScript from "@/components/Current/AdobeScript"
|
import AdobeScript from "@/components/Current/AdobeScript"
|
||||||
import Footer from "@/components/Current/Footer"
|
import Footer from "@/components/Current/Footer"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ 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 isValidJson from "@/utils/isValidJson"
|
||||||
import { getFormattedUrlQueryParams } from "@/utils/url"
|
import { convertSearchParamsToObj } from "@/utils/url"
|
||||||
|
|
||||||
import MobileToggleButton, {
|
import MobileToggleButton, {
|
||||||
MobileToggleButtonSkeleton,
|
MobileToggleButtonSkeleton,
|
||||||
@@ -25,14 +25,14 @@ import styles from "./bookingWidget.module.css"
|
|||||||
import type {
|
import type {
|
||||||
BookingWidgetClientProps,
|
BookingWidgetClientProps,
|
||||||
BookingWidgetSchema,
|
BookingWidgetSchema,
|
||||||
BookingWidgetSearchParams,
|
BookingWidgetSearchData,
|
||||||
} from "@/types/components/bookingWidget"
|
} from "@/types/components/bookingWidget"
|
||||||
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
||||||
|
|
||||||
export default function BookingWidgetClient({
|
export default function BookingWidgetClient({
|
||||||
locations,
|
locations,
|
||||||
type,
|
type,
|
||||||
searchParams,
|
bookingWidgetSearchParams,
|
||||||
}: BookingWidgetClientProps) {
|
}: BookingWidgetClientProps) {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const bookingWidgetRef = useRef(null)
|
const bookingWidgetRef = useRef(null)
|
||||||
@@ -41,13 +41,10 @@ export default function BookingWidgetClient({
|
|||||||
name: StickyElementNameEnum.BOOKING_WIDGET,
|
name: StickyElementNameEnum.BOOKING_WIDGET,
|
||||||
})
|
})
|
||||||
|
|
||||||
const bookingWidgetSearchData: BookingWidgetSearchParams | undefined =
|
const bookingWidgetSearchData = bookingWidgetSearchParams
|
||||||
searchParams
|
? convertSearchParamsToObj<BookingWidgetSearchData>(
|
||||||
? getFormattedUrlQueryParams(new URLSearchParams(searchParams), {
|
bookingWidgetSearchParams
|
||||||
adults: "number",
|
)
|
||||||
age: "number",
|
|
||||||
bed: "number",
|
|
||||||
})
|
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const getLocationObj = (destination: string): Location | undefined => {
|
const getLocationObj = (destination: string): Location | undefined => {
|
||||||
@@ -85,13 +82,13 @@ export default function BookingWidgetClient({
|
|||||||
)
|
)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const defaultRoomsData = bookingWidgetSearchData?.room?.map((room) => ({
|
const defaultRoomsData = bookingWidgetSearchData?.rooms?.map((room) => ({
|
||||||
adults: room.adults,
|
adults: room.adults,
|
||||||
child: room.child ?? [],
|
children: room.children ?? [],
|
||||||
})) ?? [
|
})) ?? [
|
||||||
{
|
{
|
||||||
adults: 1,
|
adults: 1,
|
||||||
child: [],
|
children: [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ export default function MobileToggleButton({
|
|||||||
return acc
|
return acc
|
||||||
}, 0)
|
}, 0)
|
||||||
const totalChildren = rooms.reduce((acc, room) => {
|
const totalChildren = rooms.reduce((acc, room) => {
|
||||||
if (room.child) {
|
if (room.children) {
|
||||||
acc = acc + room.child.length
|
acc = acc + room.children.length
|
||||||
}
|
}
|
||||||
return acc
|
return acc
|
||||||
}, 0)
|
}, 0)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export function preload() {
|
|||||||
|
|
||||||
export default async function BookingWidget({
|
export default async function BookingWidget({
|
||||||
type,
|
type,
|
||||||
searchParams,
|
bookingWidgetSearchParams,
|
||||||
}: BookingWidgetProps) {
|
}: BookingWidgetProps) {
|
||||||
const locations = await getLocations()
|
const locations = await getLocations()
|
||||||
const siteConfig = await getSiteConfig()
|
const siteConfig = await getSiteConfig()
|
||||||
@@ -23,7 +23,7 @@ export default async function BookingWidget({
|
|||||||
<BookingWidgetClient
|
<BookingWidgetClient
|
||||||
locations={locations.data}
|
locations={locations.data}
|
||||||
type={type}
|
type={type}
|
||||||
searchParams={searchParams}
|
bookingWidgetSearchParams={bookingWidgetSearchParams}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
@media screen and (max-width: 767px) {
|
||||||
|
.section {
|
||||||
|
max-width: var(--max-width-page);
|
||||||
|
}
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useFormContext } from "react-hook-form"
|
|||||||
import { selectHotel, selectRate } from "@/constants/routes/hotelReservation"
|
import { selectHotel, selectRate } from "@/constants/routes/hotelReservation"
|
||||||
|
|
||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
|
import { convertObjToSearchParams } from "@/utils/url"
|
||||||
|
|
||||||
import FormContent, { BookingWidgetFormContentSkeleton } from "./FormContent"
|
import FormContent, { BookingWidgetFormContentSkeleton } from "./FormContent"
|
||||||
import { bookingWidgetVariants } from "./variants"
|
import { bookingWidgetVariants } from "./variants"
|
||||||
@@ -37,26 +38,14 @@ export default function Form({
|
|||||||
|
|
||||||
const bookingFlowPage =
|
const bookingFlowPage =
|
||||||
locationData.type == "cities" ? selectHotel(lang) : selectRate(lang)
|
locationData.type == "cities" ? selectHotel(lang) : selectRate(lang)
|
||||||
const bookingWidgetParams = new URLSearchParams(data.date)
|
const bookingWidgetParams = convertObjToSearchParams({
|
||||||
|
rooms: data.rooms,
|
||||||
if (locationData.type == "cities")
|
...data.date,
|
||||||
bookingWidgetParams.set("city", locationData.name)
|
...(locationData.type == "cities"
|
||||||
else bookingWidgetParams.set("hotel", locationData.operaId || "")
|
? { city: locationData.name }
|
||||||
|
: { hotel: locationData.operaId || "" }),
|
||||||
data.rooms.forEach((room, index) => {
|
|
||||||
bookingWidgetParams.set(`room[${index}].adults`, room.adults.toString())
|
|
||||||
|
|
||||||
room.child.forEach((child, childIndex) => {
|
|
||||||
bookingWidgetParams.set(
|
|
||||||
`room[${index}].child[${childIndex}].age`,
|
|
||||||
child.age.toString()
|
|
||||||
)
|
|
||||||
bookingWidgetParams.set(
|
|
||||||
`room[${index}].child[${childIndex}].bed`,
|
|
||||||
child.bed.toString()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onClose()
|
onClose()
|
||||||
router.push(`${bookingFlowPage}?${bookingWidgetParams.toString()}`)
|
router.push(`${bookingFlowPage}?${bookingWidgetParams.toString()}`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { Location } from "@/types/trpc/routers/hotel/locations"
|
|||||||
export const guestRoomSchema = z
|
export const guestRoomSchema = z
|
||||||
.object({
|
.object({
|
||||||
adults: z.number().default(1),
|
adults: z.number().default(1),
|
||||||
child: z
|
children: z
|
||||||
.array(
|
.array(
|
||||||
z.object({
|
z.object({
|
||||||
age: z.number().min(0, "Age is required"),
|
age: z.number().min(0, "Age is required"),
|
||||||
@@ -16,11 +16,11 @@ export const guestRoomSchema = z
|
|||||||
.default([]),
|
.default([]),
|
||||||
})
|
})
|
||||||
.superRefine((value, ctx) => {
|
.superRefine((value, ctx) => {
|
||||||
const childrenInAdultsBed = value.child.filter(
|
const childrenInAdultsBed = value.children.filter(
|
||||||
(c) => c.bed === ChildBedMapEnum.IN_ADULTS_BED
|
(c) => c.bed === ChildBedMapEnum.IN_ADULTS_BED
|
||||||
)
|
)
|
||||||
if (value.adults < childrenInAdultsBed.length) {
|
if (value.adults < childrenInAdultsBed.length) {
|
||||||
const lastAdultBedIndex = value.child
|
const lastAdultBedIndex = value.children
|
||||||
.map((c) => c.bed)
|
.map((c) => c.bed)
|
||||||
.lastIndexOf(ChildBedMapEnum.IN_ADULTS_BED)
|
.lastIndexOf(ChildBedMapEnum.IN_ADULTS_BED)
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export default function ChildInfoSelector({
|
|||||||
index = 0,
|
index = 0,
|
||||||
roomIndex = 0,
|
roomIndex = 0,
|
||||||
}: ChildInfoSelectorProps) {
|
}: ChildInfoSelectorProps) {
|
||||||
const ageFieldName = `rooms.${roomIndex}.child.${index}.age`
|
const ageFieldName = `rooms.${roomIndex}.children.${index}.age`
|
||||||
const bedFieldName = `rooms.${roomIndex}.child.${index}.bed`
|
const bedFieldName = `rooms.${roomIndex}.children.${index}.bed`
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const ageLabel = intl.formatMessage({ id: "Age" })
|
const ageLabel = intl.formatMessage({ id: "Age" })
|
||||||
const bedLabel = intl.formatMessage({ id: "Bed" })
|
const bedLabel = intl.formatMessage({ id: "Bed" })
|
||||||
@@ -38,11 +38,11 @@ export default function ChildInfoSelector({
|
|||||||
const { setValue, formState } = useFormContext()
|
const { setValue, formState } = useFormContext()
|
||||||
|
|
||||||
function updateSelectedBed(bed: number) {
|
function updateSelectedBed(bed: number) {
|
||||||
setValue(`rooms.${roomIndex}.child.${index}.bed`, bed)
|
setValue(`rooms.${roomIndex}.children.${index}.bed`, bed)
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSelectedAge(age: number) {
|
function updateSelectedAge(age: number) {
|
||||||
setValue(`rooms.${roomIndex}.child.${index}.age`, age)
|
setValue(`rooms.${roomIndex}.children.${index}.age`, age)
|
||||||
const availableBedTypes = getAvailableBeds(age)
|
const availableBedTypes = getAvailableBeds(age)
|
||||||
updateSelectedBed(availableBedTypes[0].value)
|
updateSelectedBed(availableBedTypes[0].value)
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ export default function ChildInfoSelector({
|
|||||||
}
|
}
|
||||||
|
|
||||||
//@ts-expect-error: formState is typed with FormValues
|
//@ts-expect-error: formState is typed with FormValues
|
||||||
const roomErrors = formState.errors.rooms?.[roomIndex]?.child?.[index]
|
const roomErrors = formState.errors.rooms?.[roomIndex]?.children?.[index]
|
||||||
|
|
||||||
const ageError = roomErrors?.age
|
const ageError = roomErrors?.age
|
||||||
const bedError = roomErrors?.bed
|
const bedError = roomErrors?.bed
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default function ChildSelector({
|
|||||||
|
|
||||||
function increaseChildrenCount(roomIndex: number) {
|
function increaseChildrenCount(roomIndex: number) {
|
||||||
if (currentChildren.length < 5) {
|
if (currentChildren.length < 5) {
|
||||||
setValue(`rooms.${roomIndex}.child.${currentChildren.length}`, {
|
setValue(`rooms.${roomIndex}.children.${currentChildren.length}`, {
|
||||||
age: undefined,
|
age: undefined,
|
||||||
bed: undefined,
|
bed: undefined,
|
||||||
})
|
})
|
||||||
@@ -33,7 +33,7 @@ export default function ChildSelector({
|
|||||||
function decreaseChildrenCount(roomIndex: number) {
|
function decreaseChildrenCount(roomIndex: number) {
|
||||||
if (currentChildren.length > 0) {
|
if (currentChildren.length > 0) {
|
||||||
currentChildren.pop()
|
currentChildren.pop()
|
||||||
setValue(`rooms.${roomIndex}.child`, currentChildren)
|
setValue(`rooms.${roomIndex}.children`, currentChildren)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default function GuestsRoomsPickerDialog({
|
|||||||
}, [trigger, onClose])
|
}, [trigger, onClose])
|
||||||
|
|
||||||
const handleAddRoom = useCallback(() => {
|
const handleAddRoom = useCallback(() => {
|
||||||
setValue("rooms", [...roomsValue, { adults: 1, child: [] }], {
|
setValue("rooms", [...roomsValue, { adults: 1, children: [] }], {
|
||||||
shouldValidate: true,
|
shouldValidate: true,
|
||||||
})
|
})
|
||||||
}, [roomsValue, setValue])
|
}, [roomsValue, setValue])
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export function GuestsRoom({
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const roomLabel = intl.formatMessage({ id: "Room" })
|
const roomLabel = intl.formatMessage({ id: "Room" })
|
||||||
|
|
||||||
const childrenInAdultsBed = room.child.filter(
|
const childrenInAdultsBed = room.children.filter(
|
||||||
(child) => child.bed === ChildBedMapEnum.IN_ADULTS_BED
|
(child) => child.bed === ChildBedMapEnum.IN_ADULTS_BED
|
||||||
).length
|
).length
|
||||||
|
|
||||||
@@ -38,13 +38,13 @@ export function GuestsRoom({
|
|||||||
<AdultSelector
|
<AdultSelector
|
||||||
roomIndex={index}
|
roomIndex={index}
|
||||||
currentAdults={room.adults}
|
currentAdults={room.adults}
|
||||||
currentChildren={room.child}
|
currentChildren={room.children}
|
||||||
childrenInAdultsBed={childrenInAdultsBed}
|
childrenInAdultsBed={childrenInAdultsBed}
|
||||||
/>
|
/>
|
||||||
<ChildSelector
|
<ChildSelector
|
||||||
roomIndex={index}
|
roomIndex={index}
|
||||||
currentAdults={room.adults}
|
currentAdults={room.adults}
|
||||||
currentChildren={room.child}
|
currentChildren={room.children}
|
||||||
childrenInAdultsBed={childrenInAdultsBed}
|
childrenInAdultsBed={childrenInAdultsBed}
|
||||||
/>
|
/>
|
||||||
{index !== 0 && (
|
{index !== 0 && (
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ export default function GuestsRoomsPickerForm() {
|
|||||||
const [isDesktop, setIsDesktop] = useState(true)
|
const [isDesktop, setIsDesktop] = useState(true)
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const [containerHeight, setContainerHeight] = useState(0)
|
const [containerHeight, setContainerHeight] = useState(0)
|
||||||
const childCount = rooms[0] ? rooms[0].child.length : 0 // ToDo Update for multiroom later
|
const childCount =
|
||||||
|
rooms[0] && rooms[0].children ? rooms[0].children.length : 0 // ToDo Update for multiroom later
|
||||||
|
|
||||||
const htmlElement =
|
const htmlElement =
|
||||||
typeof window !== "undefined" ? document.querySelector("body") : null
|
typeof window !== "undefined" ? document.querySelector("body") : null
|
||||||
@@ -153,7 +154,7 @@ function Trigger({
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (rooms.some((room) => room.child.length > 0)) {
|
if (rooms.some((room) => room.children.length > 0)) {
|
||||||
parts.push(
|
parts.push(
|
||||||
intl.formatMessage(
|
intl.formatMessage(
|
||||||
{
|
{
|
||||||
@@ -161,7 +162,7 @@ function Trigger({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
totalChildren: rooms.reduce(
|
totalChildren: rooms.reduce(
|
||||||
(acc, room) => acc + room.child.length,
|
(acc, room) => acc + room.children.length,
|
||||||
0
|
0
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests"
|
|||||||
import TrackingSDK from "@/components/TrackingSDK"
|
import TrackingSDK from "@/components/TrackingSDK"
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
import { invertedBedTypeMap } from "../SelectRate/RoomSelection/utils"
|
import { invertedBedTypeMap } from "../utils"
|
||||||
import Confirmation from "./Confirmation"
|
import Confirmation from "./Confirmation"
|
||||||
|
|
||||||
import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation"
|
import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation"
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import { useEffect } from "react"
|
|||||||
|
|
||||||
import { detailsStorageName } from "@/stores/enter-details"
|
import { detailsStorageName } from "@/stores/enter-details"
|
||||||
|
|
||||||
import { createQueryParamsForEnterDetails } from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
|
|
||||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||||
import { trackPaymentEvent } from "@/utils/tracking"
|
import { trackPaymentEvent } from "@/utils/tracking"
|
||||||
|
import { convertObjToSearchParams } from "@/utils/url"
|
||||||
|
|
||||||
import type { PersistedState } from "@/types/stores/enter-details"
|
import type { PersistedState } from "@/types/stores/enter-details"
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export default function PaymentCallback({
|
|||||||
|
|
||||||
if (bookingData) {
|
if (bookingData) {
|
||||||
const detailsStorage: PersistedState = JSON.parse(bookingData)
|
const detailsStorage: PersistedState = JSON.parse(bookingData)
|
||||||
const searchParams = createQueryParamsForEnterDetails(
|
const searchParams = convertObjToSearchParams(
|
||||||
detailsStorage.booking,
|
detailsStorage.booking,
|
||||||
searchObject
|
searchObject
|
||||||
)
|
)
|
||||||
@@ -37,13 +37,13 @@ export default function PaymentCallback({
|
|||||||
if (status === "cancel") {
|
if (status === "cancel") {
|
||||||
trackPaymentEvent({
|
trackPaymentEvent({
|
||||||
event: "paymentCancel",
|
event: "paymentCancel",
|
||||||
hotelId: detailsStorage.booking.hotel,
|
hotelId: detailsStorage.booking.hotelId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (status === "error") {
|
if (status === "error") {
|
||||||
trackPaymentEvent({
|
trackPaymentEvent({
|
||||||
event: "paymentFail",
|
event: "paymentFail",
|
||||||
hotelId: detailsStorage.booking.hotel,
|
hotelId: detailsStorage.booking.hotelId,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { usePaymentFailedToast } from "@/hooks/booking/usePaymentFailedToast"
|
|||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
import { trackPaymentEvent } from "@/utils/tracking"
|
import { trackPaymentEvent } from "@/utils/tracking"
|
||||||
|
|
||||||
import { bedTypeMap } from "../../SelectRate/RoomSelection/utils"
|
import { bedTypeMap } from "../../utils"
|
||||||
import PriceChangeDialog from "../PriceChangeDialog"
|
import PriceChangeDialog from "../PriceChangeDialog"
|
||||||
import GuaranteeDetails from "./GuaranteeDetails"
|
import GuaranteeDetails from "./GuaranteeDetails"
|
||||||
import PaymentOption from "./PaymentOption"
|
import PaymentOption from "./PaymentOption"
|
||||||
@@ -87,7 +87,7 @@ export default function PaymentClient({
|
|||||||
newPrice: number
|
newPrice: number
|
||||||
} | null>()
|
} | null>()
|
||||||
|
|
||||||
const { toDate, fromDate, rooms, hotel } = booking
|
const { toDate, fromDate, rooms, hotelId } = booking
|
||||||
|
|
||||||
usePaymentFailedToast()
|
usePaymentFailedToast()
|
||||||
|
|
||||||
@@ -171,14 +171,14 @@ export default function PaymentClient({
|
|||||||
|
|
||||||
trackPaymentEvent({
|
trackPaymentEvent({
|
||||||
event: "paymentFail",
|
event: "paymentFail",
|
||||||
hotelId: hotel,
|
hotelId,
|
||||||
method: currentPaymentMethod,
|
method: currentPaymentMethod,
|
||||||
isSavedCreditCard,
|
isSavedCreditCard,
|
||||||
smsEnable,
|
smsEnable,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[intl, methods, savedCreditCards, hotel]
|
[intl, methods, savedCreditCards, hotelId]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -226,7 +226,7 @@ export default function PaymentClient({
|
|||||||
|
|
||||||
trackPaymentEvent({
|
trackPaymentEvent({
|
||||||
event: "paymentAttemptStart",
|
event: "paymentAttemptStart",
|
||||||
hotelId: hotel,
|
hotelId,
|
||||||
method: paymentMethod,
|
method: paymentMethod,
|
||||||
isSavedCreditCard: !!savedCreditCard,
|
isSavedCreditCard: !!savedCreditCard,
|
||||||
smsEnable: data.smsConfirmation,
|
smsEnable: data.smsConfirmation,
|
||||||
@@ -234,7 +234,7 @@ export default function PaymentClient({
|
|||||||
|
|
||||||
initiateBooking.mutate({
|
initiateBooking.mutate({
|
||||||
language: lang,
|
language: lang,
|
||||||
hotelId: hotel,
|
hotelId,
|
||||||
checkInDate: fromDate,
|
checkInDate: fromDate,
|
||||||
checkOutDate: toDate,
|
checkOutDate: toDate,
|
||||||
rooms: rooms.map((room) => ({
|
rooms: rooms.map((room) => ({
|
||||||
@@ -294,7 +294,7 @@ export default function PaymentClient({
|
|||||||
savedCreditCards,
|
savedCreditCards,
|
||||||
lang,
|
lang,
|
||||||
initiateBooking,
|
initiateBooking,
|
||||||
hotel,
|
hotelId,
|
||||||
fromDate,
|
fromDate,
|
||||||
toDate,
|
toDate,
|
||||||
rooms,
|
rooms,
|
||||||
@@ -347,7 +347,7 @@ export default function PaymentClient({
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
cardNumber={savedCreditCard.truncatedNumber}
|
cardNumber={savedCreditCard.truncatedNumber}
|
||||||
hotelId={hotel}
|
hotelId={hotelId}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -364,7 +364,7 @@ export default function PaymentClient({
|
|||||||
name="paymentMethod"
|
name="paymentMethod"
|
||||||
value={PaymentMethodEnum.card}
|
value={PaymentMethodEnum.card}
|
||||||
label={intl.formatMessage({ id: "Credit card" })}
|
label={intl.formatMessage({ id: "Credit card" })}
|
||||||
hotelId={hotel}
|
hotelId={hotelId}
|
||||||
/>
|
/>
|
||||||
{availablePaymentOptions.map((paymentMethod) => (
|
{availablePaymentOptions.map((paymentMethod) => (
|
||||||
<PaymentOption
|
<PaymentOption
|
||||||
@@ -374,7 +374,7 @@ export default function PaymentClient({
|
|||||||
label={
|
label={
|
||||||
PAYMENT_METHOD_TITLES[paymentMethod as PaymentMethodEnum]
|
PAYMENT_METHOD_TITLES[paymentMethod as PaymentMethodEnum]
|
||||||
}
|
}
|
||||||
hotelId={hotel}
|
hotelId={hotelId}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
getFiltersFromHotels,
|
getFiltersFromHotels,
|
||||||
} from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils"
|
} from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils"
|
||||||
import TrackingSDK from "@/components/TrackingSDK"
|
import TrackingSDK from "@/components/TrackingSDK"
|
||||||
|
import { getLang } from "@/i18n/serverContext"
|
||||||
import { safeTry } from "@/utils/safeTry"
|
import { safeTry } from "@/utils/safeTry"
|
||||||
|
|
||||||
import { getHotelPins } from "../../HotelCardDialogListing/utils"
|
import { getHotelPins } from "../../HotelCardDialogListing/utils"
|
||||||
@@ -33,19 +34,20 @@ function isValidHotelData(hotel: NullableHotelData): hotel is HotelData {
|
|||||||
|
|
||||||
export async function SelectHotelMapContainer({
|
export async function SelectHotelMapContainer({
|
||||||
city,
|
city,
|
||||||
searchParams,
|
selectHotelParams,
|
||||||
adultsInRoom,
|
adultsInRoom,
|
||||||
childrenInRoom,
|
childrenInRoom,
|
||||||
child,
|
child,
|
||||||
}: SelectHotelMapContainerProps) {
|
}: SelectHotelMapContainerProps) {
|
||||||
|
const lang = getLang()
|
||||||
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
|
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
|
||||||
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
|
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
|
||||||
|
|
||||||
const fetchAvailableHotelsPromise = safeTry(
|
const fetchAvailableHotelsPromise = safeTry(
|
||||||
fetchAvailableHotels({
|
fetchAvailableHotels({
|
||||||
cityId: city.id,
|
cityId: city.id,
|
||||||
roomStayStartDate: searchParams.fromDate,
|
roomStayStartDate: selectHotelParams.fromDate,
|
||||||
roomStayEndDate: searchParams.toDate,
|
roomStayEndDate: selectHotelParams.toDate,
|
||||||
adults: adultsInRoom,
|
adults: adultsInRoom,
|
||||||
children: childrenInRoom,
|
children: childrenInRoom,
|
||||||
})
|
})
|
||||||
@@ -62,12 +64,12 @@ export async function SelectHotelMapContainer({
|
|||||||
hotel: { address: hotels?.[0]?.hotelData?.address.streetAddress },
|
hotel: { address: hotels?.[0]?.hotelData?.address.streetAddress },
|
||||||
})
|
})
|
||||||
|
|
||||||
const arrivalDate = new Date(searchParams.fromDate)
|
const arrivalDate = new Date(selectHotelParams.fromDate)
|
||||||
const departureDate = new Date(searchParams.toDate)
|
const departureDate = new Date(selectHotelParams.toDate)
|
||||||
|
|
||||||
const pageTrackingData: TrackingSDKPageData = {
|
const pageTrackingData: TrackingSDKPageData = {
|
||||||
pageId: "select-hotel",
|
pageId: "select-hotel",
|
||||||
domainLanguage: searchParams.lang as Lang,
|
domainLanguage: lang,
|
||||||
channel: TrackingChannelEnum["hotelreservation"],
|
channel: TrackingChannelEnum["hotelreservation"],
|
||||||
pageName: "hotelreservation|select-hotel|mapview",
|
pageName: "hotelreservation|select-hotel|mapview",
|
||||||
siteSections: "hotelreservation|select-hotel|mapview",
|
siteSections: "hotelreservation|select-hotel|mapview",
|
||||||
@@ -77,7 +79,7 @@ export async function SelectHotelMapContainer({
|
|||||||
|
|
||||||
const hotelsTrackingData: TrackingSDKHotelInfo = {
|
const hotelsTrackingData: TrackingSDKHotelInfo = {
|
||||||
availableResults: validHotels.length,
|
availableResults: validHotels.length,
|
||||||
searchTerm: searchParams.city,
|
searchTerm: selectHotelParams.city,
|
||||||
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
|
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
|
||||||
departureDate: format(departureDate, "yyyy-MM-dd"),
|
departureDate: format(departureDate, "yyyy-MM-dd"),
|
||||||
noOfAdults: adultsInRoom,
|
noOfAdults: adultsInRoom,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
|||||||
import TrackingSDK from "@/components/TrackingSDK"
|
import TrackingSDK from "@/components/TrackingSDK"
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
import { safeTry } from "@/utils/safeTry"
|
import { safeTry } from "@/utils/safeTry"
|
||||||
|
import { convertObjToSearchParams } from "@/utils/url"
|
||||||
|
|
||||||
import HotelCardListing from "../HotelCardListing"
|
import HotelCardListing from "../HotelCardListing"
|
||||||
import HotelCount from "./HotelCount"
|
import HotelCount from "./HotelCount"
|
||||||
@@ -46,7 +47,6 @@ export default async function SelectHotel({
|
|||||||
}: SelectHotelProps) {
|
}: SelectHotelProps) {
|
||||||
const {
|
const {
|
||||||
selectHotelParams,
|
selectHotelParams,
|
||||||
searchParams,
|
|
||||||
adultsInRoom,
|
adultsInRoom,
|
||||||
childrenInRoom,
|
childrenInRoom,
|
||||||
childrenInRoomArray,
|
childrenInRoomArray,
|
||||||
@@ -57,8 +57,8 @@ export default async function SelectHotel({
|
|||||||
const hotelsPromise = safeTry(
|
const hotelsPromise = safeTry(
|
||||||
fetchAvailableHotels({
|
fetchAvailableHotels({
|
||||||
cityId: city.id,
|
cityId: city.id,
|
||||||
roomStayStartDate: searchParams.fromDate,
|
roomStayStartDate: selectHotelParams.fromDate,
|
||||||
roomStayEndDate: searchParams.toDate,
|
roomStayEndDate: selectHotelParams.toDate,
|
||||||
adults: adultsInRoom,
|
adults: adultsInRoom,
|
||||||
children: childrenInRoom,
|
children: childrenInRoom,
|
||||||
})
|
})
|
||||||
@@ -66,8 +66,8 @@ export default async function SelectHotel({
|
|||||||
|
|
||||||
const [hotels] = await hotelsPromise
|
const [hotels] = await hotelsPromise
|
||||||
|
|
||||||
const arrivalDate = new Date(searchParams.fromDate)
|
const arrivalDate = new Date(selectHotelParams.fromDate)
|
||||||
const departureDate = new Date(searchParams.toDate)
|
const departureDate = new Date(selectHotelParams.toDate)
|
||||||
|
|
||||||
const isCityWithCountry = (city: any): city is { country: string } =>
|
const isCityWithCountry = (city: any): city is { country: string } =>
|
||||||
"country" in city
|
"country" in city
|
||||||
@@ -76,6 +76,8 @@ export default async function SelectHotel({
|
|||||||
hotels?.filter((hotel): hotel is HotelData => hotel !== null) || []
|
hotels?.filter((hotel): hotel is HotelData => hotel !== null) || []
|
||||||
|
|
||||||
const filterList = getFiltersFromHotels(validHotels)
|
const filterList = getFiltersFromHotels(validHotels)
|
||||||
|
|
||||||
|
const searchParams = convertObjToSearchParams(selectHotelParams)
|
||||||
const breadcrumbs = [
|
const breadcrumbs = [
|
||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: "Home" }),
|
title: intl.formatMessage({ id: "Home" }),
|
||||||
@@ -89,7 +91,7 @@ export default async function SelectHotel({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: "Select hotel" }),
|
title: intl.formatMessage({ id: "Select hotel" }),
|
||||||
href: `${selectHotel(params.lang)}/?${selectHotelParams}`,
|
href: `${selectHotel(params.lang)}/?${searchParams.toString()}`,
|
||||||
uid: "select-hotel",
|
uid: "select-hotel",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -112,7 +114,7 @@ export default async function SelectHotel({
|
|||||||
|
|
||||||
const hotelsTrackingData: TrackingSDKHotelInfo = {
|
const hotelsTrackingData: TrackingSDKHotelInfo = {
|
||||||
availableResults: validHotels.length,
|
availableResults: validHotels.length,
|
||||||
searchTerm: searchParams.city,
|
searchTerm: selectHotelParams.city,
|
||||||
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
|
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
|
||||||
departureDate: format(departureDate, "yyyy-MM-dd"),
|
departureDate: format(departureDate, "yyyy-MM-dd"),
|
||||||
noOfAdults: adultsInRoom,
|
noOfAdults: adultsInRoom,
|
||||||
@@ -158,13 +160,13 @@ export default async function SelectHotel({
|
|||||||
>
|
>
|
||||||
<div className={styles.mapContainer}>
|
<div className={styles.mapContainer}>
|
||||||
<StaticMap
|
<StaticMap
|
||||||
city={searchParams.city}
|
city={selectHotelParams.city}
|
||||||
country={isCityWithCountry(city) ? city.country : undefined}
|
country={isCityWithCountry(city) ? city.country : undefined}
|
||||||
width={340}
|
width={340}
|
||||||
height={180}
|
height={180}
|
||||||
zoomLevel={11}
|
zoomLevel={11}
|
||||||
mapType="roadmap"
|
mapType="roadmap"
|
||||||
altText={`Map of ${searchParams.city} city center`}
|
altText={`Map of ${selectHotelParams.city} city center`}
|
||||||
/>
|
/>
|
||||||
<Button wrapping size="medium" intent="text" theme="base">
|
<Button wrapping size="medium" intent="text" theme="base">
|
||||||
{intl.formatMessage({ id: "See map" })}
|
{intl.formatMessage({ id: "See map" })}
|
||||||
@@ -179,12 +181,12 @@ export default async function SelectHotel({
|
|||||||
) : (
|
) : (
|
||||||
<div className={styles.mapContainer}>
|
<div className={styles.mapContainer}>
|
||||||
<StaticMap
|
<StaticMap
|
||||||
city={searchParams.city}
|
city={selectHotelParams.city}
|
||||||
width={340}
|
width={340}
|
||||||
height={180}
|
height={180}
|
||||||
zoomLevel={11}
|
zoomLevel={11}
|
||||||
mapType="roadmap"
|
mapType="roadmap"
|
||||||
altText={`Map of ${searchParams.city} city center`}
|
altText={`Map of ${selectHotelParams.city} city center`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Alert from "@/components/TempDesignSystem/Alert"
|
|||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
import { safeTry } from "@/utils/safeTry"
|
import { safeTry } from "@/utils/safeTry"
|
||||||
|
|
||||||
import { generateChildrenString } from "../RoomSelection/utils"
|
import { generateChildrenString } from "../../utils"
|
||||||
|
|
||||||
import styles from "./NoRoomsAlert.module.css"
|
import styles from "./NoRoomsAlert.module.css"
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ import { useIntl } from "react-intl"
|
|||||||
import { useMediaQuery } from "usehooks-ts"
|
import { useMediaQuery } from "usehooks-ts"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
|
import { getIconForFeatureCode } from "@/components/HotelReservation/utils"
|
||||||
import { InfoCircleIcon } from "@/components/Icons"
|
import { InfoCircleIcon } from "@/components/Icons"
|
||||||
import CheckboxChip from "@/components/TempDesignSystem/Form/FilterChip/Checkbox"
|
import CheckboxChip from "@/components/TempDesignSystem/Form/FilterChip/Checkbox"
|
||||||
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 { Tooltip } from "@/components/TempDesignSystem/Tooltip"
|
import { Tooltip } from "@/components/TempDesignSystem/Tooltip"
|
||||||
|
|
||||||
import { getIconForFeatureCode } from "../utils"
|
|
||||||
|
|
||||||
import styles from "./roomFilter.module.css"
|
import styles from "./roomFilter.module.css"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import ToggleSidePeek from "@/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek"
|
import ToggleSidePeek from "@/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek"
|
||||||
import FlexibilityOption from "@/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption"
|
import FlexibilityOption from "@/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption"
|
||||||
|
import { getIconForFeatureCode } from "@/components/HotelReservation/utils"
|
||||||
import { ErrorCircleIcon } from "@/components/Icons"
|
import { ErrorCircleIcon } from "@/components/Icons"
|
||||||
import ImageGallery from "@/components/ImageGallery"
|
import ImageGallery from "@/components/ImageGallery"
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
|
||||||
import { getIconForFeatureCode } from "../../utils"
|
|
||||||
import { cardVariants } from "./cardVariants"
|
import { cardVariants } from "./cardVariants"
|
||||||
|
|
||||||
import styles from "./roomCard.module.css"
|
import styles from "./roomCard.module.css"
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
|
|
||||||
|
import { convertObjToSearchParams } from "@/utils/url"
|
||||||
|
|
||||||
import RateSummary from "./RateSummary"
|
import RateSummary from "./RateSummary"
|
||||||
import RoomCard from "./RoomCard"
|
import RoomCard from "./RoomCard"
|
||||||
import { getHotelReservationQueryParams } from "./utils"
|
|
||||||
|
|
||||||
import styles from "./roomSelection.module.css"
|
import styles from "./roomSelection.module.css"
|
||||||
|
|
||||||
@@ -26,28 +27,22 @@ export default function RoomSelection({
|
|||||||
const { roomConfigurations, rateDefinitions } = roomsAvailability
|
const { roomConfigurations, rateDefinitions } = roomsAvailability
|
||||||
|
|
||||||
const queryParams = useMemo(() => {
|
const queryParams = useMemo(() => {
|
||||||
const params = new URLSearchParams(searchParams)
|
// TODO: handle multiple rooms
|
||||||
const searchParamsObject = getHotelReservationQueryParams(searchParams)
|
const newSearchParams = convertObjToSearchParams(
|
||||||
|
{
|
||||||
searchParamsObject.room.forEach((item, index) => {
|
rooms: [
|
||||||
if (rateSummary?.roomTypeCode) {
|
{
|
||||||
params.set(`room[${index}].roomtype`, rateSummary.roomTypeCode)
|
roomTypeCode: rateSummary?.roomTypeCode,
|
||||||
}
|
rateCode: rateSummary?.public.rateCode,
|
||||||
if (rateSummary?.public?.rateCode) {
|
counterRateCode: rateSummary?.member?.rateCode,
|
||||||
params.set(`room[${index}].ratecode`, rateSummary.public.rateCode)
|
packages: selectedPackages,
|
||||||
}
|
},
|
||||||
if (rateSummary?.member?.rateCode) {
|
],
|
||||||
params.set(
|
},
|
||||||
`room[${index}].counterratecode`,
|
searchParams
|
||||||
rateSummary.member.rateCode
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
selectedPackages.length > 0
|
|
||||||
? params.set(`room[${index}].packages`, selectedPackages.join(","))
|
|
||||||
: params.delete(`room[${index}].packages`)
|
|
||||||
})
|
|
||||||
|
|
||||||
return params
|
return newSearchParams
|
||||||
}, [searchParams, rateSummary, selectedPackages])
|
}, [searchParams, rateSummary, selectedPackages])
|
||||||
|
|
||||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
import { ChildBedTypeEnum } from "@/constants/booking"
|
|
||||||
|
|
||||||
import { getFormattedUrlQueryParams } from "@/utils/url"
|
|
||||||
|
|
||||||
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
|
||||||
import type { BookingData } from "@/types/components/hotelReservation/enterDetails/bookingData"
|
|
||||||
import type { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
|
||||||
import type {
|
|
||||||
Child,
|
|
||||||
SelectRateSearchParams,
|
|
||||||
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
|
||||||
|
|
||||||
export function getHotelReservationQueryParams(searchParams: URLSearchParams) {
|
|
||||||
return getFormattedUrlQueryParams(searchParams, {
|
|
||||||
adults: "number",
|
|
||||||
age: "number",
|
|
||||||
bed: ChildBedMapEnum,
|
|
||||||
}) as SelectRateSearchParams
|
|
||||||
}
|
|
||||||
|
|
||||||
export const bedTypeMap: Record<number, ChildBedTypeEnum> = {
|
|
||||||
[ChildBedMapEnum.IN_ADULTS_BED]: ChildBedTypeEnum.ParentsBed,
|
|
||||||
[ChildBedMapEnum.IN_CRIB]: ChildBedTypeEnum.Crib,
|
|
||||||
[ChildBedMapEnum.IN_EXTRA_BED]: ChildBedTypeEnum.ExtraBed,
|
|
||||||
[ChildBedMapEnum.UNKNOWN]: ChildBedTypeEnum.Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const invertedBedTypeMap: Record<ChildBedTypeEnum, string> = {
|
|
||||||
[ChildBedTypeEnum.ParentsBed]: ChildBedMapEnum[ChildBedMapEnum.IN_ADULTS_BED],
|
|
||||||
[ChildBedTypeEnum.Crib]: ChildBedMapEnum[ChildBedMapEnum.IN_CRIB],
|
|
||||||
[ChildBedTypeEnum.ExtraBed]: ChildBedMapEnum[ChildBedMapEnum.IN_EXTRA_BED],
|
|
||||||
[ChildBedTypeEnum.Unknown]: ChildBedMapEnum[ChildBedMapEnum.UNKNOWN],
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateChildrenString(children: Child[]): string {
|
|
||||||
return `[${children
|
|
||||||
.map((child) => {
|
|
||||||
const age = child.age
|
|
||||||
const bedType = bedTypeMap[parseInt(child.bed.toString())]
|
|
||||||
return `${age}:${bedType}`
|
|
||||||
})
|
|
||||||
.join(",")}]`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getQueryParamsForEnterDetails(
|
|
||||||
searchParams: URLSearchParams
|
|
||||||
): BookingData {
|
|
||||||
const selectRoomParamsObject = getHotelReservationQueryParams(searchParams)
|
|
||||||
|
|
||||||
const { room } = selectRoomParamsObject
|
|
||||||
return {
|
|
||||||
fromDate: selectRoomParamsObject.fromDate,
|
|
||||||
toDate: selectRoomParamsObject.toDate,
|
|
||||||
hotel: selectRoomParamsObject.hotel,
|
|
||||||
rooms: room?.map((room) => ({
|
|
||||||
adults: room.adults, // TODO: Handle multiple rooms
|
|
||||||
children: room.child, // TODO: Handle multiple rooms and children
|
|
||||||
roomTypeCode: room.roomtype,
|
|
||||||
rateCode: room.ratecode,
|
|
||||||
packages: room.packages?.split(",") as RoomPackageCodeEnum[],
|
|
||||||
counterRateCode: room.counterratecode,
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createQueryParamsForEnterDetails(
|
|
||||||
bookingData: BookingData,
|
|
||||||
intitalSearchParams: URLSearchParams
|
|
||||||
) {
|
|
||||||
const { hotel, fromDate, toDate, rooms } = bookingData
|
|
||||||
|
|
||||||
const bookingSearchParams = new URLSearchParams({ hotel, fromDate, toDate })
|
|
||||||
const searchParams = new URLSearchParams([
|
|
||||||
...intitalSearchParams,
|
|
||||||
...bookingSearchParams,
|
|
||||||
])
|
|
||||||
|
|
||||||
searchParams.set(`hotel`, hotel)
|
|
||||||
|
|
||||||
rooms.forEach((item, index) => {
|
|
||||||
if (item?.adults) {
|
|
||||||
searchParams.set(`room[${index}].adults`, item.adults.toString())
|
|
||||||
}
|
|
||||||
if (item?.children) {
|
|
||||||
item.children.forEach((child, childIndex) => {
|
|
||||||
searchParams.set(
|
|
||||||
`room[${index}].child[${childIndex}].age`,
|
|
||||||
child.age.toString()
|
|
||||||
)
|
|
||||||
searchParams.set(
|
|
||||||
`room[${index}].child[${childIndex}].bed`,
|
|
||||||
child.bed.toString()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (item?.roomTypeCode) {
|
|
||||||
searchParams.set(`room[${index}].roomtype`, item.roomTypeCode)
|
|
||||||
}
|
|
||||||
if (item?.rateCode) {
|
|
||||||
searchParams.set(`room[${index}].ratecode`, item.rateCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item?.counterRateCode) {
|
|
||||||
searchParams.set(`room[${index}].counterratecode`, item.counterRateCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.packages && item.packages.length > 0) {
|
|
||||||
searchParams.set(`room[${index}].packages`, item.packages.join(","))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return searchParams
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ import { auth } from "@/auth"
|
|||||||
import { safeTry } from "@/utils/safeTry"
|
import { safeTry } from "@/utils/safeTry"
|
||||||
import { isValidSession } from "@/utils/session"
|
import { isValidSession } from "@/utils/session"
|
||||||
|
|
||||||
import { generateChildrenString } from "../RoomSelection/utils"
|
import { generateChildrenString } from "../../utils"
|
||||||
import Rooms from "."
|
import Rooms from "."
|
||||||
|
|
||||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { AllergyIcon, PetsIcon, WheelchairIcon } from "@/components/Icons"
|
|
||||||
|
|
||||||
import {
|
|
||||||
RoomPackageCodeEnum,
|
|
||||||
type RoomPackageCodes,
|
|
||||||
} from "@/types/components/hotelReservation/selectRate/roomFilter"
|
|
||||||
|
|
||||||
export function getIconForFeatureCode(featureCode: RoomPackageCodes) {
|
|
||||||
switch (featureCode) {
|
|
||||||
case RoomPackageCodeEnum.ACCESSIBILITY_ROOM:
|
|
||||||
return WheelchairIcon
|
|
||||||
case RoomPackageCodeEnum.ALLERGY_ROOM:
|
|
||||||
return AllergyIcon
|
|
||||||
case RoomPackageCodeEnum.PET_ROOM:
|
|
||||||
return PetsIcon
|
|
||||||
default:
|
|
||||||
return PetsIcon
|
|
||||||
}
|
|
||||||
}
|
|
||||||
47
components/HotelReservation/utils.ts
Normal file
47
components/HotelReservation/utils.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { ChildBedTypeEnum } from "@/constants/booking"
|
||||||
|
|
||||||
|
import { AllergyIcon, PetsIcon, WheelchairIcon } from "@/components/Icons"
|
||||||
|
|
||||||
|
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
||||||
|
import {
|
||||||
|
RoomPackageCodeEnum,
|
||||||
|
type RoomPackageCodes,
|
||||||
|
} from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||||
|
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||||
|
|
||||||
|
export function getIconForFeatureCode(featureCode: RoomPackageCodes) {
|
||||||
|
switch (featureCode) {
|
||||||
|
case RoomPackageCodeEnum.ACCESSIBILITY_ROOM:
|
||||||
|
return WheelchairIcon
|
||||||
|
case RoomPackageCodeEnum.ALLERGY_ROOM:
|
||||||
|
return AllergyIcon
|
||||||
|
case RoomPackageCodeEnum.PET_ROOM:
|
||||||
|
return PetsIcon
|
||||||
|
default:
|
||||||
|
return PetsIcon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const bedTypeMap: Record<number, ChildBedTypeEnum> = {
|
||||||
|
[ChildBedMapEnum.IN_ADULTS_BED]: ChildBedTypeEnum.ParentsBed,
|
||||||
|
[ChildBedMapEnum.IN_CRIB]: ChildBedTypeEnum.Crib,
|
||||||
|
[ChildBedMapEnum.IN_EXTRA_BED]: ChildBedTypeEnum.ExtraBed,
|
||||||
|
[ChildBedMapEnum.UNKNOWN]: ChildBedTypeEnum.Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const invertedBedTypeMap: Record<ChildBedTypeEnum, string> = {
|
||||||
|
[ChildBedTypeEnum.ParentsBed]: ChildBedMapEnum[ChildBedMapEnum.IN_ADULTS_BED],
|
||||||
|
[ChildBedTypeEnum.Crib]: ChildBedMapEnum[ChildBedMapEnum.IN_CRIB],
|
||||||
|
[ChildBedTypeEnum.ExtraBed]: ChildBedMapEnum[ChildBedMapEnum.IN_EXTRA_BED],
|
||||||
|
[ChildBedTypeEnum.Unknown]: ChildBedMapEnum[ChildBedMapEnum.UNKNOWN],
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateChildrenString(children: Child[]): string {
|
||||||
|
return `[${children
|
||||||
|
.map((child) => {
|
||||||
|
const age = child.age
|
||||||
|
const bedType = bedTypeMap[parseInt(child.bed.toString())]
|
||||||
|
return `${age}:${bedType}`
|
||||||
|
})
|
||||||
|
.join(",")}]`
|
||||||
|
}
|
||||||
@@ -23,14 +23,14 @@ export const middleware: NextMiddleware = (request) => {
|
|||||||
const { searchParams } = url
|
const { searchParams } = url
|
||||||
|
|
||||||
if (
|
if (
|
||||||
legacyDatePattern.test(searchParams.get("fromDate")!) ||
|
legacyDatePattern.test(searchParams.get("fromdate")!) ||
|
||||||
legacyDatePattern.test(searchParams.get("toDate")!)
|
legacyDatePattern.test(searchParams.get("todate")!)
|
||||||
) {
|
) {
|
||||||
const fromDate = searchParams.get("fromDate")!
|
const fromDate = searchParams.get("fromdate")!
|
||||||
url.searchParams.set("fromDate", normalizeDate(fromDate))
|
url.searchParams.set("fromdate", normalizeDate(fromDate))
|
||||||
|
|
||||||
const toDate = searchParams.get("toDate")!
|
const toDate = searchParams.get("todate")!
|
||||||
url.searchParams.set("toDate", normalizeDate(toDate))
|
url.searchParams.set("todate", normalizeDate(toDate))
|
||||||
return NextResponse.redirect(url)
|
return NextResponse.redirect(url)
|
||||||
} else {
|
} else {
|
||||||
const headers = new Headers(request.headers)
|
const headers = new Headers(request.headers)
|
||||||
@@ -43,5 +43,5 @@ export const middleware: NextMiddleware = (request) => {
|
|||||||
|
|
||||||
export const matcher: MiddlewareMatcher = (request) => {
|
export const matcher: MiddlewareMatcher = (request) => {
|
||||||
const { searchParams } = request.nextUrl
|
const { searchParams } = request.nextUrl
|
||||||
return searchParams.has("fromDate") || searchParams.has("toDate")
|
return searchParams.has("fromdate") || searchParams.has("todate")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,14 +112,14 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// fromDate param missing
|
// fromdate param missing
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
source:
|
source:
|
||||||
"/:lang/hotelreservation/(select-bed|breakfast|details|payment)",
|
"/:lang/hotelreservation/(select-bed|breakfast|details|payment)",
|
||||||
destination: "/:lang/hotelreservation/select-rate",
|
destination: "/:lang/hotelreservation/select-rate",
|
||||||
missing: [
|
missing: [
|
||||||
{
|
{
|
||||||
key: "fromDate",
|
key: "fromdate",
|
||||||
type: "query",
|
type: "query",
|
||||||
value: undefined,
|
value: undefined,
|
||||||
},
|
},
|
||||||
@@ -128,14 +128,14 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// fromDate param has to be a date
|
// fromdate param has to be a date
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
source:
|
source:
|
||||||
"/:lang/hotelreservation/(select-bed|breakfast|details|payment)",
|
"/:lang/hotelreservation/(select-bed|breakfast|details|payment)",
|
||||||
destination: "/:lang/hotelreservation/select-rate",
|
destination: "/:lang/hotelreservation/select-rate",
|
||||||
missing: [
|
missing: [
|
||||||
{
|
{
|
||||||
key: "fromDate",
|
key: "fromdate",
|
||||||
type: "query",
|
type: "query",
|
||||||
value: "^([12]\\d{3}-(0[1-9]|1[0-2])-(0?[1-9]|[12]\\d|3[01]))$",
|
value: "^([12]\\d{3}-(0[1-9]|1[0-2])-(0?[1-9]|[12]\\d|3[01]))$",
|
||||||
},
|
},
|
||||||
@@ -144,14 +144,14 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// toDate param missing
|
// todate param missing
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
source:
|
source:
|
||||||
"/:lang/hotelreservation/(select-bed|breakfast|details|payment)",
|
"/:lang/hotelreservation/(select-bed|breakfast|details|payment)",
|
||||||
destination: "/:lang/hotelreservation/select-rate",
|
destination: "/:lang/hotelreservation/select-rate",
|
||||||
missing: [
|
missing: [
|
||||||
{
|
{
|
||||||
key: "toDate",
|
key: "todate",
|
||||||
type: "query",
|
type: "query",
|
||||||
value: undefined,
|
value: undefined,
|
||||||
},
|
},
|
||||||
@@ -160,14 +160,14 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// toDate param has to be a date
|
// todate param has to be a date
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
source:
|
source:
|
||||||
"/:lang/hotelreservation/(select-bed|breakfast|details|payment)",
|
"/:lang/hotelreservation/(select-bed|breakfast|details|payment)",
|
||||||
destination: "/:lang/hotelreservation/select-rate",
|
destination: "/:lang/hotelreservation/select-rate",
|
||||||
missing: [
|
missing: [
|
||||||
{
|
{
|
||||||
key: "toDate",
|
key: "todate",
|
||||||
type: "query",
|
type: "query",
|
||||||
value: "^([12]\\d{3}-(0[1-9]|1[0-2])-(0?[1-9]|[12]\\d|3[01]))$",
|
value: "^([12]\\d{3}-(0[1-9]|1[0-2])-(0?[1-9]|[12]\\d|3[01]))$",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,15 +8,13 @@ import { arrayMerge } from "@/utils/merge"
|
|||||||
|
|
||||||
import { detailsStorageName } from "."
|
import { detailsStorageName } from "."
|
||||||
|
|
||||||
import type { BookingData } from "@/types/components/hotelReservation/enterDetails/bookingData"
|
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||||
import { CurrencyEnum } from "@/types/enums/currency"
|
import { CurrencyEnum } from "@/types/enums/currency"
|
||||||
import type { StepEnum } from "@/types/enums/step"
|
import type { StepEnum } from "@/types/enums/step"
|
||||||
import type {
|
import type {
|
||||||
DetailsState,
|
DetailsState,
|
||||||
PersistedState,
|
PersistedState,
|
||||||
PersistedStatePart,
|
PersistedStatePart,
|
||||||
Price,
|
|
||||||
RoomPrice,
|
|
||||||
RoomRate,
|
RoomRate,
|
||||||
} from "@/types/stores/enter-details"
|
} from "@/types/stores/enter-details"
|
||||||
import type { SafeUser } from "@/types/user"
|
import type { SafeUser } from "@/types/user"
|
||||||
@@ -55,7 +53,10 @@ export function navigate(step: StepEnum, searchParams: string) {
|
|||||||
window.history.pushState({ step }, "", `${step}?${searchParams}`)
|
window.history.pushState({ step }, "", `${step}?${searchParams}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkIsSameRoom(prev: BookingData, next: BookingData) {
|
export function checkIsSameRoom(
|
||||||
|
prev: SelectRateSearchParams,
|
||||||
|
next: SelectRateSearchParams
|
||||||
|
) {
|
||||||
const { rooms: prevRooms, ...prevBooking } = prev
|
const { rooms: prevRooms, ...prevBooking } = prev
|
||||||
|
|
||||||
const prevRoomsWithoutRateCodes = prevRooms.map(
|
const prevRoomsWithoutRateCodes = prevRooms.map(
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ jest.mock("@/lib/api", () => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const booking = {
|
const booking = {
|
||||||
hotel: "123",
|
hotelId: "123",
|
||||||
fromDate: "2100-01-01",
|
fromDate: "2100-01-01",
|
||||||
toDate: "2100-01-02",
|
toDate: "2100-01-02",
|
||||||
rooms: [
|
rooms: [
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
|
import type { Child, Room } from "../hotelReservation/selectRate/selectRate"
|
||||||
|
|
||||||
export type ChildBed = {
|
export type ChildBed = {
|
||||||
label: string
|
label: string
|
||||||
value: number
|
value: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Child = {
|
export type TGuestsRoom = Required<Pick<Room, "adults" | "children">>
|
||||||
age: number
|
|
||||||
bed: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TGuestsRoom = {
|
|
||||||
adults: number
|
|
||||||
child: Child[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GuestsRoomPickerProps = {
|
export type GuestsRoomPickerProps = {
|
||||||
index: number
|
index: number
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { VariantProps } from "class-variance-authority"
|
import type { VariantProps } from "class-variance-authority"
|
||||||
import type { z } from "zod"
|
import type { z } from "zod"
|
||||||
|
|
||||||
|
import type { SearchParams } from "@/types/params"
|
||||||
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 { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema"
|
||||||
import type { bookingWidgetVariants } from "@/components/Forms/BookingWidget/variants"
|
import type { bookingWidgetVariants } from "@/components/Forms/BookingWidget/variants"
|
||||||
@@ -8,12 +9,12 @@ import type { TGuestsRoom } from "./guestsRoomsPicker"
|
|||||||
|
|
||||||
export type BookingWidgetSchema = z.output<typeof bookingWidgetSchema>
|
export type BookingWidgetSchema = z.output<typeof bookingWidgetSchema>
|
||||||
|
|
||||||
export type BookingWidgetSearchParams = {
|
export type BookingWidgetSearchData = {
|
||||||
city?: string
|
city?: string
|
||||||
hotel?: string
|
hotel?: string
|
||||||
fromDate?: string
|
fromDate?: string
|
||||||
toDate?: string
|
toDate?: string
|
||||||
room?: TGuestsRoom[]
|
rooms?: TGuestsRoom[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BookingWidgetType = VariantProps<
|
export type BookingWidgetType = VariantProps<
|
||||||
@@ -22,13 +23,13 @@ export type BookingWidgetType = VariantProps<
|
|||||||
|
|
||||||
export interface BookingWidgetProps {
|
export interface BookingWidgetProps {
|
||||||
type?: BookingWidgetType
|
type?: BookingWidgetType
|
||||||
searchParams?: URLSearchParams
|
bookingWidgetSearchParams: SearchParams<BookingWidgetSearchData>["searchParams"]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookingWidgetClientProps {
|
export interface BookingWidgetClientProps {
|
||||||
locations: Locations
|
locations: Locations
|
||||||
type?: BookingWidgetType
|
type?: BookingWidgetType
|
||||||
searchParams?: URLSearchParams
|
bookingWidgetSearchParams: SearchParams<BookingWidgetSearchData>["searchParams"]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookingWidgetToggleButtonProps {
|
export interface BookingWidgetToggleButtonProps {
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import type { RoomPackageCodeEnum } from "../selectRate/roomFilter"
|
|
||||||
import type { Child } from "../selectRate/selectRate"
|
|
||||||
|
|
||||||
interface Room {
|
|
||||||
adults: number
|
|
||||||
roomTypeCode: string
|
|
||||||
rateCode: string
|
|
||||||
counterRateCode?: string
|
|
||||||
children?: Child[]
|
|
||||||
packages?: RoomPackageCodeEnum[]
|
|
||||||
}
|
|
||||||
export interface BookingData {
|
|
||||||
hotel: string
|
|
||||||
fromDate: string
|
|
||||||
toDate: string
|
|
||||||
rooms: Room[]
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ import type { z } from "zod"
|
|||||||
import type { Coordinates } from "@/types/components/maps/coordinates"
|
import type { Coordinates } from "@/types/components/maps/coordinates"
|
||||||
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
||||||
import type { imageSchema } from "@/server/routers/hotels/schemas/image"
|
import type { imageSchema } from "@/server/routers/hotels/schemas/image"
|
||||||
import type { Child } from "../../bookingWidget/guestsRoomsPicker"
|
import type { Child } from "../selectRate/selectRate"
|
||||||
import type { HotelData } from "./hotelCardListingProps"
|
import type { HotelData } from "./hotelCardListingProps"
|
||||||
import type { CategorizedFilters, Filter } from "./hotelFilters"
|
import type { CategorizedFilters, Filter } from "./hotelFilters"
|
||||||
import type { SelectHotelSearchParams } from "./selectHotelSearchParams"
|
import type { SelectHotelSearchParams } from "./selectHotelSearchParams"
|
||||||
@@ -66,7 +66,7 @@ export interface HotelCardDialogListingProps {
|
|||||||
|
|
||||||
export type SelectHotelMapContainerProps = {
|
export type SelectHotelMapContainerProps = {
|
||||||
city: Location
|
city: Location
|
||||||
searchParams: SelectHotelSearchParams
|
selectHotelParams: SelectHotelSearchParams
|
||||||
adultsInRoom: number
|
adultsInRoom: number
|
||||||
childrenInRoom: string | undefined
|
childrenInRoom: string | undefined
|
||||||
child: Child[] | undefined
|
child: Child[] | undefined
|
||||||
|
|||||||
@@ -46,8 +46,7 @@ export interface SelectHotelProps {
|
|||||||
lang: Lang
|
lang: Lang
|
||||||
}
|
}
|
||||||
reservationParams: {
|
reservationParams: {
|
||||||
selectHotelParams: URLSearchParams | undefined
|
selectHotelParams: SelectHotelSearchParams
|
||||||
searchParams: SelectHotelSearchParams
|
|
||||||
adultsInRoom: number
|
adultsInRoom: number
|
||||||
childrenInRoom: string | undefined
|
childrenInRoom: string | undefined
|
||||||
childrenInRoomArray: Child[] | undefined
|
childrenInRoomArray: Child[] | undefined
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
interface Child {
|
import type { Room } from "../selectRate/selectRate"
|
||||||
bed: string
|
|
||||||
age: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Room {
|
|
||||||
adults: number
|
|
||||||
child?: Child[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SelectHotelSearchParams {
|
export interface SelectHotelSearchParams {
|
||||||
city: string
|
city: string
|
||||||
fromDate: string
|
fromDate: string
|
||||||
toDate: string
|
toDate: string
|
||||||
room: Room[]
|
rooms: Pick<Room, "adults" | "children">[]
|
||||||
[key: string]: string | string[] | Room[]
|
[key: string]: string | string[] | Pick<Room, "adults" | "children">[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
import type { Product, RoomConfiguration } from "@/server/routers/hotels/output"
|
import type { Product, RoomConfiguration } from "@/server/routers/hotels/output"
|
||||||
import type { ChildBedMapEnum } from "../../bookingWidget/enums"
|
import type { ChildBedMapEnum } from "../../bookingWidget/enums"
|
||||||
|
import type { RoomPackageCodeEnum } from "./roomFilter"
|
||||||
|
|
||||||
export interface Child {
|
export interface Child {
|
||||||
bed: ChildBedMapEnum
|
bed: ChildBedMapEnum
|
||||||
age: number
|
age: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Room {
|
export interface Room {
|
||||||
adults: number
|
adults: number
|
||||||
roomtype: string
|
roomTypeCode: string
|
||||||
ratecode: string
|
rateCode: string
|
||||||
counterratecode: string
|
counterRateCode: string
|
||||||
child?: Child[]
|
children?: Child[]
|
||||||
packages?: string
|
packages?: RoomPackageCodeEnum[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectRateSearchParams {
|
export interface SelectRateSearchParams {
|
||||||
hotel: string
|
city?: string
|
||||||
|
hotelId: string
|
||||||
fromDate: string
|
fromDate: string
|
||||||
toDate: string
|
toDate: string
|
||||||
room: Room[]
|
rooms: Room[]
|
||||||
[key: string]: string | string[] | Room[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Rate {
|
export interface Rate {
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType"
|
import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType"
|
||||||
import type { BookingData } from "@/types/components/hotelReservation/enterDetails/bookingData"
|
|
||||||
import type { BreakfastPackage } from "@/types/components/hotelReservation/enterDetails/breakfast"
|
|
||||||
import type { StepEnum } from "@/types/enums/step"
|
import type { StepEnum } from "@/types/enums/step"
|
||||||
import type { RoomAvailability } from "@/types/trpc/routers/hotel/availability"
|
import type { RoomAvailability } from "@/types/trpc/routers/hotel/availability"
|
||||||
import type { SafeUser } from "@/types/user"
|
import type { SafeUser } from "@/types/user"
|
||||||
|
import type { SelectRateSearchParams } from "../components/hotelReservation/selectRate/selectRate"
|
||||||
import type { Packages } from "../requests/packages"
|
import type { Packages } from "../requests/packages"
|
||||||
|
|
||||||
export interface DetailsProviderProps extends React.PropsWithChildren {
|
export interface DetailsProviderProps extends React.PropsWithChildren {
|
||||||
booking: BookingData
|
booking: SelectRateSearchParams
|
||||||
bedTypes: BedTypeSelection[]
|
bedTypes: BedTypeSelection[]
|
||||||
showBreakfastStep: boolean
|
showBreakfastStep: boolean
|
||||||
packages: Packages | null
|
packages: Packages | null
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
|
import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
|
||||||
import type { BookingData } from "@/types/components/hotelReservation/enterDetails/bookingData"
|
|
||||||
import type { BreakfastPackage } from "@/types/components/hotelReservation/enterDetails/breakfast"
|
import type { BreakfastPackage } from "@/types/components/hotelReservation/enterDetails/breakfast"
|
||||||
import type {
|
import type {
|
||||||
DetailsSchema,
|
DetailsSchema,
|
||||||
SignedInDetailsSchema,
|
SignedInDetailsSchema,
|
||||||
} from "@/types/components/hotelReservation/enterDetails/details"
|
} from "@/types/components/hotelReservation/enterDetails/details"
|
||||||
import type { StepEnum } from "@/types/enums/step"
|
import type { StepEnum } from "@/types/enums/step"
|
||||||
|
import type { SelectRateSearchParams } from "../components/hotelReservation/selectRate/selectRate"
|
||||||
import type { DetailsProviderProps } from "../providers/enter-details"
|
import type { DetailsProviderProps } from "../providers/enter-details"
|
||||||
import type { Packages } from "../requests/packages"
|
import type { Packages } from "../requests/packages"
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ export interface Price {
|
|||||||
|
|
||||||
export interface FormValues {
|
export interface FormValues {
|
||||||
bedType: BedTypeSchema | undefined
|
bedType: BedTypeSchema | undefined
|
||||||
booking: BookingData
|
booking: SelectRateSearchParams
|
||||||
breakfast: BreakfastPackage | false | undefined
|
breakfast: BreakfastPackage | false | undefined
|
||||||
guest: DetailsSchema | SignedInDetailsSchema
|
guest: DetailsSchema | SignedInDetailsSchema
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ export interface DetailsState {
|
|||||||
updateSeachParamString: (searchParamString: string) => void
|
updateSeachParamString: (searchParamString: string) => void
|
||||||
}
|
}
|
||||||
bedType: BedTypeSchema | undefined
|
bedType: BedTypeSchema | undefined
|
||||||
booking: BookingData
|
booking: SelectRateSearchParams
|
||||||
breakfast: BreakfastPackage | false | undefined
|
breakfast: BreakfastPackage | false | undefined
|
||||||
currentStep: StepEnum
|
currentStep: StepEnum
|
||||||
formValues: FormValues
|
formValues: FormValues
|
||||||
|
|||||||
161
utils/url.ts
161
utils/url.ts
@@ -1,3 +1,9 @@
|
|||||||
|
import type { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||||
|
import type {
|
||||||
|
Child,
|
||||||
|
Room,
|
||||||
|
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||||
|
|
||||||
export function removeMultipleSlashes(pathname: string) {
|
export function removeMultipleSlashes(pathname: string) {
|
||||||
return pathname.replaceAll(/\/\/+/g, "/")
|
return pathname.replaceAll(/\/\/+/g, "/")
|
||||||
}
|
}
|
||||||
@@ -10,35 +16,142 @@ export function removeTrailingSlash(pathname: string) {
|
|||||||
return pathname
|
return pathname
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFormattedUrlQueryParams(
|
type PartialRoom = { rooms?: Partial<Room>[] }
|
||||||
searchParams: URLSearchParams,
|
|
||||||
dataTypes: Record<string, unknown>
|
const keyedSearchParams = new Map([
|
||||||
|
["room", "rooms"],
|
||||||
|
["ratecode", "rateCode"],
|
||||||
|
["counterratecode", "counterRateCode"],
|
||||||
|
["roomtype", "roomTypeCode"],
|
||||||
|
["fromdate", "fromDate"],
|
||||||
|
["todate", "toDate"],
|
||||||
|
["hotel", "hotelId"],
|
||||||
|
["child", "children"],
|
||||||
|
])
|
||||||
|
|
||||||
|
export function getKeyFromSearchParam(key: string): string {
|
||||||
|
return keyedSearchParams.get(key) || key
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSearchParamFromKey(key: string): string {
|
||||||
|
for (const [mapKey, mapValue] of keyedSearchParams.entries()) {
|
||||||
|
if (mapValue === key) {
|
||||||
|
return mapKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertSearchParamsToObj<T extends PartialRoom>(
|
||||||
|
searchParams: Record<string, string>
|
||||||
) {
|
) {
|
||||||
const searchParamsObject: Record<string, unknown> = Array.from(
|
const searchParamsObject = Object.entries(searchParams).reduce<
|
||||||
searchParams.entries()
|
T & PartialRoom
|
||||||
).reduce<Record<string, unknown>>(
|
>((acc, [key, value]) => {
|
||||||
(acc, [key, value]) => {
|
// The params are sometimes indexed with a number (for ex: `room[0].adults`),
|
||||||
const keys = key.replace(/\]/g, "").split(/\[|\./) // Split keys by '[' or '.'
|
// so we need to split them by . or []
|
||||||
keys.reduce((nestedAcc, k, i) => {
|
const keys = key.replace(/\]/g, "").split(/\[|\./)
|
||||||
if (i === keys.length - 1) {
|
const firstKey = getKeyFromSearchParam(keys[0]) as keyof T
|
||||||
if (dataTypes[k] == "number") {
|
|
||||||
;(nestedAcc as Record<string, unknown>)[k] = Number(value)
|
// Room is a special case since it is an array, so we need to handle it separately
|
||||||
} else if (dataTypes[k] == "boolean") {
|
if (firstKey === "rooms") {
|
||||||
;(nestedAcc as Record<string, unknown>)[k] =
|
// Rooms are always indexed with a number, so we need to extract the index
|
||||||
value.toLowerCase() === "true"
|
const index = Number(keys[1])
|
||||||
|
const roomObject =
|
||||||
|
acc.rooms && Array.isArray(acc.rooms) ? acc.rooms : (acc.rooms = [])
|
||||||
|
|
||||||
|
const roomObjectKey = getKeyFromSearchParam(keys[2]) as keyof Room
|
||||||
|
|
||||||
|
if (!roomObject[index]) {
|
||||||
|
roomObject[index] = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adults should be converted to a number
|
||||||
|
if (roomObjectKey === "adults") {
|
||||||
|
roomObject[index].adults = Number(value)
|
||||||
|
|
||||||
|
// Child is an array, so we need to handle it separately
|
||||||
|
} else if (roomObjectKey === "children") {
|
||||||
|
const childIndex = Number(keys[3])
|
||||||
|
const childKey = keys[4] as keyof Child
|
||||||
|
|
||||||
|
if (
|
||||||
|
!("children" in roomObject[index]) ||
|
||||||
|
!Array.isArray(roomObject[index].children)
|
||||||
|
) {
|
||||||
|
roomObject[index].children = []
|
||||||
|
}
|
||||||
|
|
||||||
|
roomObject[index].children![childIndex] = {
|
||||||
|
...roomObject[index].children![childIndex],
|
||||||
|
[childKey]: Number(value),
|
||||||
|
}
|
||||||
|
} else if (roomObjectKey === "packages") {
|
||||||
|
roomObject[index].packages = value.split(",") as RoomPackageCodeEnum[]
|
||||||
} else {
|
} else {
|
||||||
;(nestedAcc as Record<string, unknown>)[k] = value
|
roomObject[index][roomObjectKey] = value
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!nestedAcc[k]) {
|
acc[firstKey] = value as T[keyof T]
|
||||||
nestedAcc[k] = isNaN(Number(keys[i + 1])) ? {} : [] // Initialize as object or array
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nestedAcc[k] as Record<string, unknown>
|
|
||||||
}, acc)
|
|
||||||
return acc
|
return acc
|
||||||
},
|
}, {} as T)
|
||||||
{} as Record<string, unknown>
|
|
||||||
)
|
|
||||||
return searchParamsObject
|
return searchParamsObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function convertObjToSearchParams<T>(
|
||||||
|
bookingData: T & PartialRoom,
|
||||||
|
intitalSearchParams = {} as URLSearchParams
|
||||||
|
) {
|
||||||
|
const bookingSearchParams = new URLSearchParams(intitalSearchParams)
|
||||||
|
Object.entries(bookingData).forEach(([key, value]) => {
|
||||||
|
if (key === "rooms") {
|
||||||
|
value.forEach((item, index) => {
|
||||||
|
if (item?.adults) {
|
||||||
|
bookingSearchParams.set(
|
||||||
|
`room[${index}].adults`,
|
||||||
|
item.adults.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (item?.children) {
|
||||||
|
item.children.forEach((child, childIndex) => {
|
||||||
|
bookingSearchParams.set(
|
||||||
|
`room[${index}].child[${childIndex}].age`,
|
||||||
|
child.age.toString()
|
||||||
|
)
|
||||||
|
bookingSearchParams.set(
|
||||||
|
`room[${index}].child[${childIndex}].bed`,
|
||||||
|
child.bed.toString()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (item?.roomTypeCode) {
|
||||||
|
bookingSearchParams.set(`room[${index}].roomtype`, item.roomTypeCode)
|
||||||
|
}
|
||||||
|
if (item?.rateCode) {
|
||||||
|
bookingSearchParams.set(`room[${index}].ratecode`, item.rateCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item?.counterRateCode) {
|
||||||
|
bookingSearchParams.set(
|
||||||
|
`room[${index}].counterratecode`,
|
||||||
|
item.counterRateCode
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.packages && item.packages.length > 0) {
|
||||||
|
bookingSearchParams.set(
|
||||||
|
`room[${index}].packages`,
|
||||||
|
item.packages.join(",")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
bookingSearchParams.set(getSearchParamFromKey(key), value.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return bookingSearchParams
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user