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

@@ -1,4 +1,4 @@
import { differenceInCalendarDays, format, isWeekend } from "date-fns"
import stringify from "json-stable-stringify-without-jsonify"
import { notFound } from "next/navigation"
import { Suspense } from "react"
@@ -9,13 +9,6 @@ import {
selectHotelMap,
} from "@/constants/routes/hotelReservation"
import {
fetchAlternativeHotels,
fetchAvailableHotels,
fetchBookingCodeAvailableHotels,
getFiltersFromHotels,
} from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils"
import { getHotelSearchDetails } from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/utils"
import { ChevronRightIcon } from "@/components/Icons"
import StaticMap from "@/components/Maps/StaticMap"
import Breadcrumbs from "@/components/TempDesignSystem/Breadcrumbs"
@@ -24,28 +17,23 @@ import Link from "@/components/TempDesignSystem/Link"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import TrackingSDK from "@/components/TrackingSDK"
import { getIntl } from "@/i18n"
import { safeTry } from "@/utils/safeTry"
import { getHotelSearchDetails } from "@/utils/hotelSearchDetails"
import { convertObjToSearchParams } from "@/utils/url"
import HotelCardListing from "../HotelCardListing"
import BookingCodeFilter from "./BookingCodeFilter"
import { getFiltersFromHotels, getHotels } from "./helpers"
import HotelCount from "./HotelCount"
import HotelFilter from "./HotelFilter"
import HotelSorter from "./HotelSorter"
import MobileMapButtonContainer from "./MobileMapButtonContainer"
import NoAvailabilityAlert from "./NoAvailabilityAlert"
import { getTracking } from "./tracking"
import styles from "./selectHotel.module.css"
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
import type { SelectHotelProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
import {
TrackingChannelEnum,
type TrackingSDKHotelInfo,
type TrackingSDKPageData,
} from "@/types/components/tracking"
export default async function SelectHotel({
params,
@@ -54,78 +42,44 @@ export default async function SelectHotel({
}: SelectHotelProps) {
const intl = await getIntl()
const getHotelSearchDetailsPromise = safeTry(
getHotelSearchDetails(
{
searchParams: searchParams as SelectHotelSearchParams & {
[key: string]: string
},
const searchDetails = await getHotelSearchDetails(
{
searchParams: searchParams as SelectHotelSearchParams & {
[key: string]: string
},
isAlternativeHotels
)
},
isAlternativeHotels
)
const [searchDetails] = await getHotelSearchDetailsPromise
if (!searchDetails) return notFound()
const {
city,
selectHotelParams,
adultsInRoom,
childrenInRoomString,
childrenInRoom,
hotel: isAlternativeFor,
bookingCode,
childrenInRoom,
city,
hotel: isAlternativeFor,
noOfRooms,
redemption,
selectHotelParams,
} = searchDetails
if (!city) return notFound()
const hotelsPromise = isAlternativeFor
? safeTry(
fetchAlternativeHotels(isAlternativeFor.id, {
roomStayStartDate: selectHotelParams.fromDate,
roomStayEndDate: selectHotelParams.toDate,
adults: adultsInRoom[0],
children: childrenInRoomString,
bookingCode,
redemption,
})
)
: bookingCode
? safeTry(
fetchBookingCodeAvailableHotels({
cityId: city.id,
roomStayStartDate: selectHotelParams.fromDate,
roomStayEndDate: selectHotelParams.toDate,
adults: adultsInRoom[0],
children: childrenInRoomString,
bookingCode,
})
)
: safeTry(
fetchAvailableHotels({
cityId: city.id,
roomStayStartDate: selectHotelParams.fromDate,
roomStayEndDate: selectHotelParams.toDate,
adults: adultsInRoom[0],
children: childrenInRoomString,
redemption,
})
)
const [hotels] = await hotelsPromise
const hotels = await getHotels(
selectHotelParams,
isAlternativeFor,
bookingCode,
city,
!!redemption,
)
const arrivalDate = new Date(selectHotelParams.fromDate)
const departureDate = new Date(selectHotelParams.toDate)
const isCityWithCountry = (city: any): city is { country: string } =>
"country" in city
const validHotels =
hotels?.filter((hotel): hotel is HotelData => hotel?.hotelData !== null) ||
[]
const filterList = getFiltersFromHotels(validHotels)
const filterList = getFiltersFromHotels(hotels)
const convertedSearchParams = convertObjToSearchParams(selectHotelParams)
const breadcrumbs = [
@@ -141,65 +95,44 @@ export default async function SelectHotel({
},
isAlternativeFor
? {
title: intl.formatMessage({ id: "Alternative hotels" }),
href: `${alternativeHotels(params.lang)}/?${convertedSearchParams}`,
uid: "alternative-hotels",
}
title: intl.formatMessage({ id: "Alternative hotels" }),
href: `${alternativeHotels(params.lang)}/?${convertedSearchParams}`,
uid: "alternative-hotels",
}
: {
title: intl.formatMessage({ id: "Select hotel" }),
href: `${selectHotel(params.lang)}/?${convertedSearchParams}`,
uid: "select-hotel",
},
title: intl.formatMessage({ id: "Select hotel" }),
href: `${selectHotel(params.lang)}/?${convertedSearchParams}`,
uid: "select-hotel",
},
isAlternativeFor
? {
title: isAlternativeFor.name,
uid: isAlternativeFor.id,
}
title: isAlternativeFor.name,
uid: isAlternativeFor.id,
}
: {
title: city.name,
uid: city.id,
},
title: city.name,
uid: city.id,
},
]
const isAllUnavailable =
hotels?.every((hotel) => hotel.price === undefined) || false
const isAllUnavailable = !hotels.length
const pageTrackingData: TrackingSDKPageData = {
pageId: isAlternativeFor ? "alternative-hotels" : "select-hotel",
domainLanguage: params.lang,
channel: TrackingChannelEnum["hotelreservation"],
pageName: isAlternativeFor
? "hotelreservation|alternative-hotels"
: "hotelreservation|select-hotel",
siteSections: isAlternativeFor
? "hotelreservation|alternative-hotels"
: "hotelreservation|select-hotel",
pageType: "bookinghotelspage",
siteVersion: "new-web",
}
const hotelsTrackingData: TrackingSDKHotelInfo = {
availableResults: validHotels.length,
searchTerm: isAlternativeFor
? selectHotelParams.hotelId
: (selectHotelParams.city as string),
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
departureDate: format(departureDate, "yyyy-MM-dd"),
noOfAdults: adultsInRoom[0], // TODO: Handle multiple rooms,
noOfChildren: childrenInRoom?.length,
ageOfChildren: childrenInRoom?.map((c) => c.age).join(","),
childBedPreference: childrenInRoom
?.map((c) => ChildBedMapEnum[c.bed])
.join("|"),
noOfRooms: 1, // // TODO: Handle multiple rooms
duration: differenceInCalendarDays(departureDate, arrivalDate),
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
searchType: "destination",
bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
country: validHotels?.[0]?.hotelData.address.country,
region: validHotels?.[0]?.hotelData.address.city,
}
const { hotelsTrackingData, pageTrackingData } = getTracking(
params.lang,
!!isAlternativeFor,
arrivalDate,
departureDate,
adultsInRoom,
childrenInRoom,
hotels.length,
selectHotelParams.hotelId,
noOfRooms,
hotels?.[0]?.hotel.address.country,
hotels?.[0]?.hotel.address.city,
selectHotelParams.city
)
const suspenseKey = stringify(searchParams)
return (
<>
<header className={styles.header}>
@@ -229,7 +162,7 @@ export default async function SelectHotel({
<main className={styles.main}>
{bookingCode ? <BookingCodeFilter /> : null}
<div className={styles.sideBar}>
{hotels && hotels.length > 0 ? ( // TODO: Temp fix until API returns hotels that are not available
{hotels.length ? (
<Link
className={styles.link}
color="burgundy"
@@ -276,14 +209,15 @@ export default async function SelectHotel({
</div>
<div className={styles.hotelList}>
<NoAvailabilityAlert
hotelsLength={hotels.length}
isAlternative={!!isAlternativeFor}
hotels={validHotels}
isAllUnavailable={isAllUnavailable}
operaId={hotels?.[0]?.hotel.operaId}
/>
<HotelCardListing hotelData={validHotels} />
<HotelCardListing hotelData={hotels} />
</div>
</main>
<Suspense fallback={null}>
<Suspense key={`${suspenseKey}-tracking`} fallback={null}>
<TrackingSDK
pageData={pageTrackingData}
hotelInfo={hotelsTrackingData}