Merged in feat/sw-2873-move-selecthotel-to-booking-flow (pull request #2727)

feat(SW-2873): Move select-hotel to booking flow

* crude setup of select-hotel in partner-sas

* wip

* Fix linting

* restructure tracking files

* Remove dependency on trpc in tracking hooks

* Move pageview tracking to common

* Fix some lint and import issues

* Add AlternativeHotelsPage

* Add SelectHotelMapPage

* Add AlternativeHotelsMapPage

* remove next dependency in tracking store

* Remove dependency on react in tracking hooks

* move isSameBooking to booking-flow

* Inject searchParamsComparator into tracking store

* Move useTrackHardNavigation to common

* Move useTrackSoftNavigation to common

* Add TrackingSDK to partner-sas

* call serverclient in layout

* Remove unused css

* Update types

* Move HotelPin type

* Fix todos

* Merge branch 'master' into feat/sw-2873-move-selecthotel-to-booking-flow

* Merge branch 'master' into feat/sw-2873-move-selecthotel-to-booking-flow

* Fix component


Approved-by: Joakim Jäderberg
This commit is contained in:
Anton Gunnarsson
2025-09-01 08:37:00 +00:00
parent 93a90bef9d
commit 87402a2092
157 changed files with 2026 additions and 1376 deletions

View File

@@ -0,0 +1,74 @@
import { safeTry } from "@scandic-hotels/common/utils/safeTry"
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import {
type HotelLocation,
isHotelLocation,
type Location,
} from "@scandic-hotels/trpc/types/locations"
import { getLocations } from "../trpc/memoizedRequests/getLocations"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { BookingSearchType } from "./searchType"
interface HotelSearchDetails {
city: Location | null
cityIdentifier: string | undefined
hotel: HotelLocation | null
redemption?: boolean
}
export async function getHotelSearchDetails(params: {
hotelId?: string
city?: string
rooms?: {
adults: number
childrenInRoom?: Child[]
}[]
searchType?: BookingSearchType
isAlternativeHotels?: boolean
lang: Lang
}): Promise<HotelSearchDetails | null> {
const [locations, error] = await safeTry(getLocations(params.lang))
if (!locations || error) {
return null
}
const hotel = params.hotelId
? ((locations.find(
(location) =>
isHotelLocation(location) &&
"operaId" in location &&
location.operaId === params.hotelId
) as HotelLocation | undefined) ?? null)
: null
if (params.isAlternativeHotels && !hotel) {
return null
}
const cityIdentifier = params.isAlternativeHotels
? hotel?.relationships.city.cityIdentifier
: params.city
const city = cityIdentifier
? (locations.find(
(location) =>
"cityIdentifier" in location &&
location.cityIdentifier?.toLowerCase() ===
cityIdentifier.toLowerCase()
) ?? null)
: null
if (!city && !hotel) return null
if (params.isAlternativeHotels && (!city || !hotel)) return null
return {
city,
cityIdentifier,
hotel,
redemption: params.searchType === SEARCH_TYPE_REDEMPTION,
}
}

View File

@@ -0,0 +1,16 @@
import type { ApiImage } from "@scandic-hotels/trpc/types/hotel"
export function mapApiImagesToGalleryImages(apiImages: ApiImage[]) {
return apiImages.map((apiImage) => {
return {
src: apiImage.imageSizes.medium,
alt:
apiImage.metaData.altText ||
apiImage.metaData.altText_En ||
apiImage.metaData.title ||
apiImage.metaData.title_En,
caption: apiImage.metaData.title || apiImage.metaData.title_En,
smallSrc: apiImage.imageSizes.small,
}
})
}

View File

@@ -0,0 +1,104 @@
import { differenceInCalendarDays, format, isWeekend } from "date-fns"
import {
TrackingChannelEnum,
type TrackingSDKHotelInfo,
type TrackingSDKPageData,
} from "@scandic-hotels/common/tracking/types"
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { SelectHotelBooking } from "../utils/url"
type ChildrenInRoom = (Child[] | null)[] | null
type SelectHotelTrackingInput = {
lang: Lang
pageId: string
pageName: string
siteSections: string
arrivalDate: Date
departureDate: Date
rooms: SelectHotelBooking["rooms"]
hotelsResult: number
country: string | undefined
hotelCity: string | undefined
bookingCode?: string
searchTerm?: string
isBookingCodeRateAvailable?: boolean
isRedemption?: boolean
isRedemptionAvailable?: boolean
}
export function getSelectHotelTracking({
lang,
pageId,
pageName,
siteSections,
arrivalDate,
departureDate,
rooms,
hotelsResult,
country,
hotelCity,
searchTerm,
bookingCode,
isBookingCodeRateAvailable = false,
isRedemption = false,
isRedemptionAvailable = false,
}: SelectHotelTrackingInput): {
hotelsTrackingData: TrackingSDKHotelInfo
pageTrackingData: TrackingSDKPageData
} {
const pageTrackingData: TrackingSDKPageData = {
channel: TrackingChannelEnum["hotelreservation"],
domainLanguage: lang,
pageId,
pageName,
pageType: "bookinghotelspage",
siteSections,
siteVersion: "new-web",
}
let adultsInRoom: number[] = []
let childrenInRoom: ChildrenInRoom = null
if (rooms?.length) {
adultsInRoom = rooms.map((room) => room.adults ?? 0)
childrenInRoom = rooms.map((room) => room.childrenInRoom ?? null)
}
const hotelsTrackingData: TrackingSDKHotelInfo = {
ageOfChildren: childrenInRoom
?.map((c) => c?.map((k) => k.age).join(",") ?? "")
.join("|"),
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
availableResults: hotelsResult,
bookingCode: bookingCode ?? "n/a",
bookingCodeAvailability: bookingCode
? isBookingCodeRateAvailable.toString()
: undefined,
bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
childBedPreference: childrenInRoom
?.map((c) => c?.map((k) => ChildBedMapEnum[k.bed]).join(",") ?? "")
.join("|"),
country,
departureDate: format(departureDate, "yyyy-MM-dd"),
duration: differenceInCalendarDays(departureDate, arrivalDate),
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
noOfAdults: adultsInRoom.join(","),
noOfChildren: childrenInRoom?.map((kids) => kids?.length ?? 0).join(","),
noOfRooms: rooms?.length ?? 0,
region: hotelCity,
rewardNight: isRedemption ? "yes" : "no",
rewardNightAvailability: isRedemptionAvailable.toString(),
searchTerm,
searchType: "destination",
}
return {
hotelsTrackingData,
pageTrackingData,
}
}

View File

@@ -0,0 +1,6 @@
export const enum SortOrder {
Distance = "distance",
Name = "name",
Price = "price",
TripAdvisorRating = "tripadvisor",
}