feat: add multiroom tracking to booking flow

This commit is contained in:
Simon Emanuelsson
2025-03-05 11:53:05 +01:00
parent 540402b969
commit 1812591903
72 changed files with 2277 additions and 1308 deletions

View File

@@ -0,0 +1,117 @@
import { notFound } from "next/navigation"
import { REDEMPTION } from "@/constants/booking"
import { getLocations } from "@/lib/trpc/memoizedRequests"
import { generateChildrenString } from "@/components/HotelReservation/utils"
import { safeTry } from "@/utils/safeTry"
import { convertSearchParamsToObj, type SelectHotelParams } from "@/utils/url"
import type {
AlternativeHotelsSearchParams,
SelectHotelSearchParams,
} from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
import type {
Child,
SelectRateSearchParams,
} from "@/types/components/hotelReservation/selectRate/selectRate"
import {
type HotelLocation,
isHotelLocation,
type Location,
} from "@/types/trpc/routers/hotel/locations"
export type ChildrenInRoom = (Child[] | null)[] | null
export type ChildrenInRoomString = (string | null)[] | null
interface HotelSearchDetails<T> {
adultsInRoom: number[]
bookingCode?: string
childrenInRoom: ChildrenInRoom
childrenInRoomString: ChildrenInRoomString
city: Location | null
hotel: HotelLocation | null
noOfRooms: number
redemption?: boolean
selectHotelParams: SelectHotelParams<T> & { city: string | undefined }
}
export async function getHotelSearchDetails<
T extends
| SelectHotelSearchParams
| SelectRateSearchParams
| AlternativeHotelsSearchParams,
>(
{
searchParams,
}: {
searchParams: T & {
[key: string]: string
}
},
isAlternativeHotels?: boolean
): Promise<HotelSearchDetails<T> | null> {
const selectHotelParams = convertSearchParamsToObj<T>(searchParams)
const [locations, error] = await safeTry(getLocations())
if (!locations || error) {
return null
}
const hotel =
("hotelId" in selectHotelParams &&
(locations.find(
(location) =>
isHotelLocation(location) &&
"operaId" in location &&
location.operaId === selectHotelParams.hotelId
) as HotelLocation | undefined)) ||
null
if (isAlternativeHotels && !hotel) {
return notFound()
}
const cityName = isAlternativeHotels
? hotel?.relationships.city.name
: "city" in selectHotelParams
? (selectHotelParams.city as string | undefined)
: undefined
const city =
(typeof cityName === "string" &&
locations.find(
(location) => location.name.toLowerCase() === cityName.toLowerCase()
)) ||
null
if (!city && !hotel) return notFound()
if (isAlternativeHotels && (!city || !hotel)) return notFound()
let adultsInRoom: number[] = []
let childrenInRoom: ChildrenInRoom = null
let childrenInRoomString: ChildrenInRoomString = null
const { rooms } = selectHotelParams
if (rooms?.length) {
adultsInRoom = rooms.map((room) => room.adults ?? 0)
childrenInRoom = rooms.map((room) => room.childrenInRoom ?? null)
childrenInRoomString = rooms.map((room) =>
room.childrenInRoom ? generateChildrenString(room.childrenInRoom) : null
)
}
return {
adultsInRoom,
bookingCode: selectHotelParams.bookingCode ?? undefined,
childrenInRoom,
childrenInRoomString,
city,
hotel,
noOfRooms: rooms?.length ?? 0,
redemption: selectHotelParams.searchType === REDEMPTION,
selectHotelParams: { city: cityName, ...selectHotelParams },
}
}

View File

@@ -0,0 +1,20 @@
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { Packages } from "@/types/requests/packages"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
export function getSpecialRoomType(
packages: BookingConfirmation["booking"]["packages"] | Packages | null
) {
const packageCodes = packages
?.filter((pkg) => pkg.code)
.map((pkg) => pkg.code)
if (packageCodes?.includes(RoomPackageCodeEnum.ACCESSIBILITY_ROOM)) {
return "accesibillity"
} else if (packageCodes?.includes(RoomPackageCodeEnum.ALLERGY_ROOM)) {
return "allergy friendly"
} else if (packageCodes?.includes(RoomPackageCodeEnum.PET_ROOM)) {
return "pet room"
} else {
return "-"
}
}