This creates the alternative hotels page. It is mostly a copy of the select hotel page, and most of the contents of the pages lives under the same component in /components.

Merged in feat/sw-397-alternative-hotels (pull request #1211)

Feat/sw 397 alternative hotels

* fix(SW-397): create alternative hotels page

* update types

* Adapt to new changes for fetching data

* Make bookingcode optional

* Code review fixes


Approved-by: Simon.Emanuelsson
This commit is contained in:
Niclas Edenvin
2025-01-28 12:08:40 +00:00
parent 4247e37667
commit ef22fc4627
28 changed files with 693 additions and 105 deletions

View File

@@ -3,18 +3,20 @@ import { notFound } from "next/navigation"
import { Suspense } from "react"
import {
alternativeHotels,
alternativeHotelsMap,
selectHotel,
selectHotelMap,
} from "@/constants/routes/hotelReservation"
import {
fetchAlternativeHotels,
fetchAvailableHotels,
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 Alert from "@/components/TempDesignSystem/Alert"
import Breadcrumbs from "@/components/TempDesignSystem/Breadcrumbs"
import Button from "@/components/TempDesignSystem/Button"
import Link from "@/components/TempDesignSystem/Link"
@@ -29,6 +31,7 @@ import HotelCount from "./HotelCount"
import HotelFilter from "./HotelFilter"
import HotelSorter from "./HotelSorter"
import MobileMapButtonContainer from "./MobileMapButtonContainer"
import NoAvailabilityAlert from "./NoAvailabilityAlert"
import styles from "./selectHotel.module.css"
@@ -41,20 +44,23 @@ import {
type TrackingSDKHotelInfo,
type TrackingSDKPageData,
} from "@/types/components/tracking"
import { AlertTypeEnum } from "@/types/enums/alert"
export default async function SelectHotel({
params,
searchParams,
isAlternativeHotels,
}: SelectHotelProps) {
const intl = await getIntl()
const getHotelSearchDetailsPromise = safeTry(
getHotelSearchDetails({
searchParams: searchParams as SelectHotelSearchParams & {
[key: string]: string
getHotelSearchDetails(
{
searchParams: searchParams as SelectHotelSearchParams & {
[key: string]: string
},
},
})
isAlternativeHotels
)
)
const [searchDetails] = await getHotelSearchDetailsPromise
@@ -67,19 +73,29 @@ export default async function SelectHotel({
adultsInRoom,
childrenInRoomString,
childrenInRoom,
hotel: isAlternativeFor,
} = searchDetails
if (!city) return notFound()
const hotelsPromise = safeTry(
fetchAvailableHotels({
cityId: city.id,
roomStayStartDate: selectHotelParams.fromDate,
roomStayEndDate: selectHotelParams.toDate,
adults: adultsInRoom,
children: childrenInRoomString,
})
)
const hotelsPromise = isAlternativeFor
? safeTry(
fetchAlternativeHotels(isAlternativeFor.id, {
roomStayStartDate: selectHotelParams.fromDate,
roomStayEndDate: selectHotelParams.toDate,
adults: adultsInRoom,
children: childrenInRoomString,
})
)
: safeTry(
fetchAvailableHotels({
cityId: city.id,
roomStayStartDate: selectHotelParams.fromDate,
roomStayEndDate: selectHotelParams.toDate,
adults: adultsInRoom,
children: childrenInRoomString,
})
)
const [hotels] = await hotelsPromise
@@ -105,32 +121,50 @@ export default async function SelectHotel({
href: `/${params.lang}/hotelreservation`,
uid: "hotel-reservation",
},
{
title: intl.formatMessage({ id: "Select hotel" }),
href: `${selectHotel(params.lang)}/?${convertedSearchParams}`,
uid: "select-hotel",
},
{
title: city.name,
uid: city.id,
},
isAlternativeFor
? {
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",
},
isAlternativeFor
? {
title: isAlternativeFor.name,
uid: isAlternativeFor.id,
}
: {
title: city.name,
uid: city.id,
},
]
const isAllUnavailable = hotels?.every((hotel) => hotel.price === undefined)
const isAllUnavailable =
hotels?.every((hotel) => hotel.price === undefined) || false
const pageTrackingData: TrackingSDKPageData = {
pageId: "select-hotel",
pageId: isAlternativeFor ? "alternative-hotels" : "select-hotel",
domainLanguage: params.lang,
channel: TrackingChannelEnum["hotelreservation"],
pageName: "hotelreservation|select-hotel",
siteSections: "hotelreservation|select-hotel",
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: selectHotelParams.city,
searchTerm: isAlternativeFor
? selectHotelParams.hotelId
: (selectHotelParams.city as string),
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
departureDate: format(departureDate, "yyyy-MM-dd"),
noOfAdults: adultsInRoom,
@@ -155,7 +189,11 @@ export default async function SelectHotel({
<Breadcrumbs breadcrumbs={breadcrumbs} />
<div className={styles.title}>
<div className={styles.cityInformation}>
<Subtitle>{city.name}</Subtitle>
<Subtitle>
{isAlternativeFor
? `${intl.formatMessage({ id: "Alternatives for" })} ${isAlternativeFor.name}`
: city.name}
</Subtitle>
<HotelCount />
</div>
<div className={styles.sorter}>
@@ -171,18 +209,22 @@ export default async function SelectHotel({
<Link
className={styles.link}
color="burgundy"
href={selectHotelMap(params.lang)}
href={
isAlternativeFor
? alternativeHotelsMap(params.lang)
: selectHotelMap(params.lang)
}
keepSearchParams
>
<div className={styles.mapContainer}>
<StaticMap
city={selectHotelParams.city}
city={city.name}
country={isCityWithCountry(city) ? city.country : undefined}
width={340}
height={180}
zoomLevel={11}
mapType="roadmap"
altText={`Map of ${selectHotelParams.city} city center`}
altText={`Map of ${city.name} city center`}
/>
<Button wrapping size="medium" intent="text" theme="base">
{intl.formatMessage({ id: "See map" })}
@@ -197,27 +239,23 @@ export default async function SelectHotel({
) : (
<div className={styles.mapContainer}>
<StaticMap
city={selectHotelParams.city}
city={city.name}
width={340}
height={180}
zoomLevel={11}
mapType="roadmap"
altText={`Map of ${selectHotelParams.city} city center`}
altText={`Map of ${city.name} city center`}
/>
</div>
)}
<HotelFilter filters={filterList} className={styles.filter} />
</div>
<div className={styles.hotelList}>
{isAllUnavailable && (
<Alert
type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No availability" })}
text={intl.formatMessage({
id: "There are no rooms available that match your request.",
})}
/>
)}
<NoAvailabilityAlert
isAlternative={!!isAlternativeFor}
hotels={validHotels}
isAllUnavailable={isAllUnavailable}
/>
<HotelCardListing hotelData={validHotels} />
</div>
</main>