refactor: url management in hotel reservation flow

This commit is contained in:
Christel Westerberg
2025-01-13 14:26:38 +01:00
parent 23ff0970e9
commit b2935114e3
48 changed files with 407 additions and 418 deletions

View File

@@ -14,7 +14,7 @@ import { CloseLargeIcon } from "@/components/Icons"
import useStickyPosition from "@/hooks/useStickyPosition"
import { debounce } from "@/utils/debounce"
import isValidJson from "@/utils/isValidJson"
import { getFormattedUrlQueryParams } from "@/utils/url"
import { convertSearchParamsToObj } from "@/utils/url"
import MobileToggleButton, {
MobileToggleButtonSkeleton,
@@ -25,14 +25,14 @@ import styles from "./bookingWidget.module.css"
import type {
BookingWidgetClientProps,
BookingWidgetSchema,
BookingWidgetSearchParams,
BookingWidgetSearchData,
} from "@/types/components/bookingWidget"
import type { Location } from "@/types/trpc/routers/hotel/locations"
export default function BookingWidgetClient({
locations,
type,
searchParams,
bookingWidgetSearchParams,
}: BookingWidgetClientProps) {
const [isOpen, setIsOpen] = useState(false)
const bookingWidgetRef = useRef(null)
@@ -41,14 +41,11 @@ export default function BookingWidgetClient({
name: StickyElementNameEnum.BOOKING_WIDGET,
})
const bookingWidgetSearchData: BookingWidgetSearchParams | undefined =
searchParams
? getFormattedUrlQueryParams(new URLSearchParams(searchParams), {
adults: "number",
age: "number",
bed: "number",
})
: undefined
const bookingWidgetSearchData = bookingWidgetSearchParams
? convertSearchParamsToObj<BookingWidgetSearchData>(
bookingWidgetSearchParams
)
: undefined
const getLocationObj = (destination: string): Location | undefined => {
if (destination) {
@@ -85,13 +82,13 @@ export default function BookingWidgetClient({
)
: undefined
const defaultRoomsData = bookingWidgetSearchData?.room?.map((room) => ({
const defaultRoomsData = bookingWidgetSearchData?.rooms?.map((room) => ({
adults: room.adults,
child: room.child ?? [],
children: room.children ?? [],
})) ?? [
{
adults: 1,
child: [],
children: [],
},
]

View File

@@ -49,8 +49,8 @@ export default function MobileToggleButton({
return acc
}, 0)
const totalChildren = rooms.reduce((acc, room) => {
if (room.child) {
acc = acc + room.child.length
if (room.children) {
acc = acc + room.children.length
}
return acc
}, 0)

View File

@@ -10,7 +10,7 @@ export function preload() {
export default async function BookingWidget({
type,
searchParams,
bookingWidgetSearchParams,
}: BookingWidgetProps) {
const locations = await getLocations()
const siteConfig = await getSiteConfig()
@@ -23,7 +23,7 @@ export default async function BookingWidget({
<BookingWidgetClient
locations={locations.data}
type={type}
searchParams={searchParams}
bookingWidgetSearchParams={bookingWidgetSearchParams}
/>
)
}

View File

@@ -10,6 +10,10 @@
}
@media screen and (max-width: 767px) {
.section {
max-width: var(--max-width-page);
}
.form {
align-self: flex-start;
}

View File

@@ -6,6 +6,7 @@ import { useFormContext } from "react-hook-form"
import { selectHotel, selectRate } from "@/constants/routes/hotelReservation"
import useLang from "@/hooks/useLang"
import { convertObjToSearchParams } from "@/utils/url"
import FormContent, { BookingWidgetFormContentSkeleton } from "./FormContent"
import { bookingWidgetVariants } from "./variants"
@@ -37,26 +38,14 @@ export default function Form({
const bookingFlowPage =
locationData.type == "cities" ? selectHotel(lang) : selectRate(lang)
const bookingWidgetParams = new URLSearchParams(data.date)
if (locationData.type == "cities")
bookingWidgetParams.set("city", locationData.name)
else bookingWidgetParams.set("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()
)
})
const bookingWidgetParams = convertObjToSearchParams({
rooms: data.rooms,
...data.date,
...(locationData.type == "cities"
? { city: locationData.name }
: { hotel: locationData.operaId || "" }),
})
onClose()
router.push(`${bookingFlowPage}?${bookingWidgetParams.toString()}`)
}

View File

@@ -6,7 +6,7 @@ import type { Location } from "@/types/trpc/routers/hotel/locations"
export const guestRoomSchema = z
.object({
adults: z.number().default(1),
child: z
children: z
.array(
z.object({
age: z.number().min(0, "Age is required"),
@@ -16,11 +16,11 @@ export const guestRoomSchema = z
.default([]),
})
.superRefine((value, ctx) => {
const childrenInAdultsBed = value.child.filter(
const childrenInAdultsBed = value.children.filter(
(c) => c.bed === ChildBedMapEnum.IN_ADULTS_BED
)
if (value.adults < childrenInAdultsBed.length) {
const lastAdultBedIndex = value.child
const lastAdultBedIndex = value.children
.map((c) => c.bed)
.lastIndexOf(ChildBedMapEnum.IN_ADULTS_BED)

View File

@@ -29,8 +29,8 @@ export default function ChildInfoSelector({
index = 0,
roomIndex = 0,
}: ChildInfoSelectorProps) {
const ageFieldName = `rooms.${roomIndex}.child.${index}.age`
const bedFieldName = `rooms.${roomIndex}.child.${index}.bed`
const ageFieldName = `rooms.${roomIndex}.children.${index}.age`
const bedFieldName = `rooms.${roomIndex}.children.${index}.bed`
const intl = useIntl()
const ageLabel = intl.formatMessage({ id: "Age" })
const bedLabel = intl.formatMessage({ id: "Bed" })
@@ -38,11 +38,11 @@ export default function ChildInfoSelector({
const { setValue, formState } = useFormContext()
function updateSelectedBed(bed: number) {
setValue(`rooms.${roomIndex}.child.${index}.bed`, bed)
setValue(`rooms.${roomIndex}.children.${index}.bed`, bed)
}
function updateSelectedAge(age: number) {
setValue(`rooms.${roomIndex}.child.${index}.age`, age)
setValue(`rooms.${roomIndex}.children.${index}.age`, age)
const availableBedTypes = getAvailableBeds(age)
updateSelectedBed(availableBedTypes[0].value)
}
@@ -77,7 +77,7 @@ export default function ChildInfoSelector({
}
//@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 bedError = roomErrors?.bed

View File

@@ -24,7 +24,7 @@ export default function ChildSelector({
function increaseChildrenCount(roomIndex: number) {
if (currentChildren.length < 5) {
setValue(`rooms.${roomIndex}.child.${currentChildren.length}`, {
setValue(`rooms.${roomIndex}.children.${currentChildren.length}`, {
age: undefined,
bed: undefined,
})
@@ -33,7 +33,7 @@ export default function ChildSelector({
function decreaseChildrenCount(roomIndex: number) {
if (currentChildren.length > 0) {
currentChildren.pop()
setValue(`rooms.${roomIndex}.child`, currentChildren)
setValue(`rooms.${roomIndex}.children`, currentChildren)
}
}

View File

@@ -48,7 +48,7 @@ export default function GuestsRoomsPickerDialog({
}, [trigger, onClose])
const handleAddRoom = useCallback(() => {
setValue("rooms", [...roomsValue, { adults: 1, child: [] }], {
setValue("rooms", [...roomsValue, { adults: 1, children: [] }], {
shouldValidate: true,
})
}, [roomsValue, setValue])

View File

@@ -25,7 +25,7 @@ export function GuestsRoom({
const intl = useIntl()
const roomLabel = intl.formatMessage({ id: "Room" })
const childrenInAdultsBed = room.child.filter(
const childrenInAdultsBed = room.children.filter(
(child) => child.bed === ChildBedMapEnum.IN_ADULTS_BED
).length
@@ -38,13 +38,13 @@ export function GuestsRoom({
<AdultSelector
roomIndex={index}
currentAdults={room.adults}
currentChildren={room.child}
currentChildren={room.children}
childrenInAdultsBed={childrenInAdultsBed}
/>
<ChildSelector
roomIndex={index}
currentAdults={room.adults}
currentChildren={room.child}
currentChildren={room.children}
childrenInAdultsBed={childrenInAdultsBed}
/>
{index !== 0 && (

View File

@@ -28,7 +28,8 @@ export default function GuestsRoomsPickerForm() {
const [isDesktop, setIsDesktop] = useState(true)
const [isOpen, setIsOpen] = useState(false)
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 =
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(
intl.formatMessage(
{
@@ -161,7 +162,7 @@ function Trigger({
},
{
totalChildren: rooms.reduce(
(acc, room) => acc + room.child.length,
(acc, room) => acc + room.children.length,
0
),
}

View File

@@ -6,7 +6,7 @@ import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests"
import TrackingSDK from "@/components/TrackingSDK"
import { getLang } from "@/i18n/serverContext"
import { invertedBedTypeMap } from "../SelectRate/RoomSelection/utils"
import { invertedBedTypeMap } from "../utils"
import Confirmation from "./Confirmation"
import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation"

View File

@@ -5,9 +5,9 @@ import { useEffect } from "react"
import { detailsStorageName } from "@/stores/enter-details"
import { createQueryParamsForEnterDetails } from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
import LoadingSpinner from "@/components/LoadingSpinner"
import { trackPaymentEvent } from "@/utils/tracking"
import { convertObjToSearchParams } from "@/utils/url"
import type { PersistedState } from "@/types/stores/enter-details"
@@ -29,7 +29,7 @@ export default function PaymentCallback({
if (bookingData) {
const detailsStorage: PersistedState = JSON.parse(bookingData)
const searchParams = createQueryParamsForEnterDetails(
const searchParams = convertObjToSearchParams(
detailsStorage.booking,
searchObject
)
@@ -37,13 +37,13 @@ export default function PaymentCallback({
if (status === "cancel") {
trackPaymentEvent({
event: "paymentCancel",
hotelId: detailsStorage.booking.hotel,
hotelId: detailsStorage.booking.hotelId,
})
}
if (status === "error") {
trackPaymentEvent({
event: "paymentFail",
hotelId: detailsStorage.booking.hotel,
hotelId: detailsStorage.booking.hotelId,
errorMessage,
})
}

View File

@@ -33,7 +33,7 @@ import { usePaymentFailedToast } from "@/hooks/booking/usePaymentFailedToast"
import useLang from "@/hooks/useLang"
import { trackPaymentEvent } from "@/utils/tracking"
import { bedTypeMap } from "../../SelectRate/RoomSelection/utils"
import { bedTypeMap } from "../../utils"
import PriceChangeDialog from "../PriceChangeDialog"
import GuaranteeDetails from "./GuaranteeDetails"
import PaymentOption from "./PaymentOption"
@@ -87,7 +87,7 @@ export default function PaymentClient({
newPrice: number
} | null>()
const { toDate, fromDate, rooms, hotel } = booking
const { toDate, fromDate, rooms, hotelId } = booking
usePaymentFailedToast()
@@ -171,14 +171,14 @@ export default function PaymentClient({
trackPaymentEvent({
event: "paymentFail",
hotelId: hotel,
hotelId,
method: currentPaymentMethod,
isSavedCreditCard,
smsEnable,
errorMessage,
})
},
[intl, methods, savedCreditCards, hotel]
[intl, methods, savedCreditCards, hotelId]
)
useEffect(() => {
@@ -226,7 +226,7 @@ export default function PaymentClient({
trackPaymentEvent({
event: "paymentAttemptStart",
hotelId: hotel,
hotelId,
method: paymentMethod,
isSavedCreditCard: !!savedCreditCard,
smsEnable: data.smsConfirmation,
@@ -234,7 +234,7 @@ export default function PaymentClient({
initiateBooking.mutate({
language: lang,
hotelId: hotel,
hotelId,
checkInDate: fromDate,
checkOutDate: toDate,
rooms: rooms.map((room) => ({
@@ -294,7 +294,7 @@ export default function PaymentClient({
savedCreditCards,
lang,
initiateBooking,
hotel,
hotelId,
fromDate,
toDate,
rooms,
@@ -347,7 +347,7 @@ export default function PaymentClient({
]
}
cardNumber={savedCreditCard.truncatedNumber}
hotelId={hotel}
hotelId={hotelId}
/>
))}
</div>
@@ -364,7 +364,7 @@ export default function PaymentClient({
name="paymentMethod"
value={PaymentMethodEnum.card}
label={intl.formatMessage({ id: "Credit card" })}
hotelId={hotel}
hotelId={hotelId}
/>
{availablePaymentOptions.map((paymentMethod) => (
<PaymentOption
@@ -374,7 +374,7 @@ export default function PaymentClient({
label={
PAYMENT_METHOD_TITLES[paymentMethod as PaymentMethodEnum]
}
hotelId={hotel}
hotelId={hotelId}
/>
))}
</div>

View File

@@ -9,6 +9,7 @@ import {
getFiltersFromHotels,
} from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils"
import TrackingSDK from "@/components/TrackingSDK"
import { getLang } from "@/i18n/serverContext"
import { safeTry } from "@/utils/safeTry"
import { getHotelPins } from "../../HotelCardDialogListing/utils"
@@ -33,19 +34,20 @@ function isValidHotelData(hotel: NullableHotelData): hotel is HotelData {
export async function SelectHotelMapContainer({
city,
searchParams,
selectHotelParams,
adultsInRoom,
childrenInRoom,
child,
}: SelectHotelMapContainerProps) {
const lang = getLang()
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
const fetchAvailableHotelsPromise = safeTry(
fetchAvailableHotels({
cityId: city.id,
roomStayStartDate: searchParams.fromDate,
roomStayEndDate: searchParams.toDate,
roomStayStartDate: selectHotelParams.fromDate,
roomStayEndDate: selectHotelParams.toDate,
adults: adultsInRoom,
children: childrenInRoom,
})
@@ -62,12 +64,12 @@ export async function SelectHotelMapContainer({
hotel: { address: hotels?.[0]?.hotelData?.address.streetAddress },
})
const arrivalDate = new Date(searchParams.fromDate)
const departureDate = new Date(searchParams.toDate)
const arrivalDate = new Date(selectHotelParams.fromDate)
const departureDate = new Date(selectHotelParams.toDate)
const pageTrackingData: TrackingSDKPageData = {
pageId: "select-hotel",
domainLanguage: searchParams.lang as Lang,
domainLanguage: lang,
channel: TrackingChannelEnum["hotelreservation"],
pageName: "hotelreservation|select-hotel|mapview",
siteSections: "hotelreservation|select-hotel|mapview",
@@ -77,7 +79,7 @@ export async function SelectHotelMapContainer({
const hotelsTrackingData: TrackingSDKHotelInfo = {
availableResults: validHotels.length,
searchTerm: searchParams.city,
searchTerm: selectHotelParams.city,
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
departureDate: format(departureDate, "yyyy-MM-dd"),
noOfAdults: adultsInRoom,

View File

@@ -20,6 +20,7 @@ import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import TrackingSDK from "@/components/TrackingSDK"
import { getIntl } from "@/i18n"
import { safeTry } from "@/utils/safeTry"
import { convertObjToSearchParams } from "@/utils/url"
import HotelCardListing from "../HotelCardListing"
import HotelCount from "./HotelCount"
@@ -46,7 +47,6 @@ export default async function SelectHotel({
}: SelectHotelProps) {
const {
selectHotelParams,
searchParams,
adultsInRoom,
childrenInRoom,
childrenInRoomArray,
@@ -57,8 +57,8 @@ export default async function SelectHotel({
const hotelsPromise = safeTry(
fetchAvailableHotels({
cityId: city.id,
roomStayStartDate: searchParams.fromDate,
roomStayEndDate: searchParams.toDate,
roomStayStartDate: selectHotelParams.fromDate,
roomStayEndDate: selectHotelParams.toDate,
adults: adultsInRoom,
children: childrenInRoom,
})
@@ -66,8 +66,8 @@ export default async function SelectHotel({
const [hotels] = await hotelsPromise
const arrivalDate = new Date(searchParams.fromDate)
const departureDate = new Date(searchParams.toDate)
const arrivalDate = new Date(selectHotelParams.fromDate)
const departureDate = new Date(selectHotelParams.toDate)
const isCityWithCountry = (city: any): city is { country: string } =>
"country" in city
@@ -76,6 +76,8 @@ export default async function SelectHotel({
hotels?.filter((hotel): hotel is HotelData => hotel !== null) || []
const filterList = getFiltersFromHotels(validHotels)
const searchParams = convertObjToSearchParams(selectHotelParams)
const breadcrumbs = [
{
title: intl.formatMessage({ id: "Home" }),
@@ -89,7 +91,7 @@ export default async function SelectHotel({
},
{
title: intl.formatMessage({ id: "Select hotel" }),
href: `${selectHotel(params.lang)}/?${selectHotelParams}`,
href: `${selectHotel(params.lang)}/?${searchParams.toString()}`,
uid: "select-hotel",
},
{
@@ -112,7 +114,7 @@ export default async function SelectHotel({
const hotelsTrackingData: TrackingSDKHotelInfo = {
availableResults: validHotels.length,
searchTerm: searchParams.city,
searchTerm: selectHotelParams.city,
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
departureDate: format(departureDate, "yyyy-MM-dd"),
noOfAdults: adultsInRoom,
@@ -158,13 +160,13 @@ export default async function SelectHotel({
>
<div className={styles.mapContainer}>
<StaticMap
city={searchParams.city}
city={selectHotelParams.city}
country={isCityWithCountry(city) ? city.country : undefined}
width={340}
height={180}
zoomLevel={11}
mapType="roadmap"
altText={`Map of ${searchParams.city} city center`}
altText={`Map of ${selectHotelParams.city} city center`}
/>
<Button wrapping size="medium" intent="text" theme="base">
{intl.formatMessage({ id: "See map" })}
@@ -179,12 +181,12 @@ export default async function SelectHotel({
) : (
<div className={styles.mapContainer}>
<StaticMap
city={searchParams.city}
city={selectHotelParams.city}
width={340}
height={180}
zoomLevel={11}
mapType="roadmap"
altText={`Map of ${searchParams.city} city center`}
altText={`Map of ${selectHotelParams.city} city center`}
/>
</div>
)}

View File

@@ -5,7 +5,7 @@ import Alert from "@/components/TempDesignSystem/Alert"
import { getIntl } from "@/i18n"
import { safeTry } from "@/utils/safeTry"
import { generateChildrenString } from "../RoomSelection/utils"
import { generateChildrenString } from "../../utils"
import styles from "./NoRoomsAlert.module.css"

View File

@@ -8,14 +8,13 @@ import { useIntl } from "react-intl"
import { useMediaQuery } from "usehooks-ts"
import { z } from "zod"
import { getIconForFeatureCode } from "@/components/HotelReservation/utils"
import { InfoCircleIcon } from "@/components/Icons"
import CheckboxChip from "@/components/TempDesignSystem/Form/FilterChip/Checkbox"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import { Tooltip } from "@/components/TempDesignSystem/Tooltip"
import { getIconForFeatureCode } from "../utils"
import styles from "./roomFilter.module.css"
import {

View File

@@ -5,13 +5,13 @@ import { useIntl } from "react-intl"
import ToggleSidePeek from "@/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek"
import FlexibilityOption from "@/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption"
import { getIconForFeatureCode } from "@/components/HotelReservation/utils"
import { ErrorCircleIcon } from "@/components/Icons"
import ImageGallery from "@/components/ImageGallery"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { getIconForFeatureCode } from "../../utils"
import { cardVariants } from "./cardVariants"
import styles from "./roomCard.module.css"

View File

@@ -2,9 +2,10 @@
import { usePathname, useRouter, useSearchParams } from "next/navigation"
import { useMemo } from "react"
import { convertObjToSearchParams } from "@/utils/url"
import RateSummary from "./RateSummary"
import RoomCard from "./RoomCard"
import { getHotelReservationQueryParams } from "./utils"
import styles from "./roomSelection.module.css"
@@ -26,28 +27,22 @@ export default function RoomSelection({
const { roomConfigurations, rateDefinitions } = roomsAvailability
const queryParams = useMemo(() => {
const params = new URLSearchParams(searchParams)
const searchParamsObject = getHotelReservationQueryParams(searchParams)
// TODO: handle multiple rooms
const newSearchParams = convertObjToSearchParams(
{
rooms: [
{
roomTypeCode: rateSummary?.roomTypeCode,
rateCode: rateSummary?.public.rateCode,
counterRateCode: rateSummary?.member?.rateCode,
packages: selectedPackages,
},
],
},
searchParams
)
searchParamsObject.room.forEach((item, index) => {
if (rateSummary?.roomTypeCode) {
params.set(`room[${index}].roomtype`, rateSummary.roomTypeCode)
}
if (rateSummary?.public?.rateCode) {
params.set(`room[${index}].ratecode`, rateSummary.public.rateCode)
}
if (rateSummary?.member?.rateCode) {
params.set(
`room[${index}].counterratecode`,
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])
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {

View File

@@ -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
}

View File

@@ -9,7 +9,7 @@ import { auth } from "@/auth"
import { safeTry } from "@/utils/safeTry"
import { isValidSession } from "@/utils/session"
import { generateChildrenString } from "../RoomSelection/utils"
import { generateChildrenString } from "../../utils"
import Rooms from "."
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"

View File

@@ -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
}
}

View 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(",")}]`
}