feat: add multiroom tracking to booking flow
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
"use client"
|
||||
|
||||
import { useBookingConfirmationStore } from "@/stores/booking-confirmation"
|
||||
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import { getTracking } from "./tracking"
|
||||
|
||||
import type { Room } from "@/types/stores/booking-confirmation"
|
||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||
|
||||
export default function Tracking({
|
||||
bookingConfirmation,
|
||||
}: {
|
||||
bookingConfirmation: BookingConfirmation
|
||||
}) {
|
||||
const lang = useLang()
|
||||
const bookingRooms = useBookingConfirmationStore((state) => state.rooms)
|
||||
if (!bookingRooms.every(Boolean)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const rooms = bookingRooms.filter((room): room is Room => !!room)
|
||||
|
||||
const { hotelsTrackingData, pageTrackingData, paymentInfo } = getTracking(
|
||||
lang,
|
||||
bookingConfirmation.booking,
|
||||
bookingConfirmation.hotel,
|
||||
rooms
|
||||
)
|
||||
|
||||
return (
|
||||
<TrackingSDK
|
||||
pageData={pageTrackingData}
|
||||
hotelInfo={hotelsTrackingData}
|
||||
paymentInfo={paymentInfo}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import { differenceInCalendarDays, format, isWeekend } from "date-fns"
|
||||
|
||||
import { getSpecialRoomType } from "@/utils/specialRoomType"
|
||||
|
||||
import { invertedBedTypeMap } from "../../utils"
|
||||
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKHotelInfo,
|
||||
type TrackingSDKPageData,
|
||||
type TrackingSDKPaymentInfo,
|
||||
} from "@/types/components/tracking"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
import type { Room } from "@/types/stores/booking-confirmation"
|
||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||
import type { RateDefinition } from "@/types/trpc/routers/hotel/roomAvailability"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
function getRate(cancellationRule: RateDefinition["cancellationRule"] | null) {
|
||||
switch (cancellationRule) {
|
||||
case "CancellableBefore6PM":
|
||||
return "flex"
|
||||
case "Changeable":
|
||||
return "change"
|
||||
case "NotCancellable":
|
||||
return "save"
|
||||
default:
|
||||
return "-"
|
||||
}
|
||||
}
|
||||
|
||||
function findBreakfastPackage(
|
||||
packages: BookingConfirmation["booking"]["packages"]
|
||||
) {
|
||||
return packages.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
|
||||
)
|
||||
}
|
||||
|
||||
function mapBreakfastPackage(
|
||||
breakfastPackage: BookingConfirmation["booking"]["packages"][number],
|
||||
adults: number,
|
||||
operaId: string
|
||||
) {
|
||||
return {
|
||||
hotelid: operaId,
|
||||
productCategory: "", // TODO: Add category
|
||||
productId: breakfastPackage.code!, // Is not found unless code exists
|
||||
productName: "BreakfastAdult",
|
||||
productPoints: 0,
|
||||
productPrice: +breakfastPackage.unitPrice,
|
||||
productType: "food",
|
||||
productUnits: adults,
|
||||
}
|
||||
}
|
||||
|
||||
export function getTracking(
|
||||
lang: Lang,
|
||||
booking: BookingConfirmation["booking"],
|
||||
hotel: BookingConfirmation["hotel"],
|
||||
rooms: Room[]
|
||||
) {
|
||||
const arrivalDate = new Date(booking.checkInDate)
|
||||
const departureDate = new Date(booking.checkOutDate)
|
||||
|
||||
const pageTrackingData: TrackingSDKPageData = {
|
||||
channel: TrackingChannelEnum.hotelreservation,
|
||||
domainLanguage: lang,
|
||||
pageId: "booking-confirmation",
|
||||
pageName: `hotelreservation|confirmation`,
|
||||
pageType: "confirmation",
|
||||
siteSections: `hotelreservation|confirmation`,
|
||||
siteVersion: "new-web",
|
||||
}
|
||||
|
||||
const noOfAdults = rooms.map((r) => r.adults).join(",")
|
||||
const noOfChildren = rooms.map((r) => r.children ?? 0).join(",")
|
||||
const noOfRooms = rooms.length
|
||||
|
||||
const hotelsTrackingData: TrackingSDKHotelInfo = {
|
||||
ageOfChildren: rooms.map((r) => r.childrenAges?.join(",") ?? "-").join("|"),
|
||||
analyticsRateCode: rooms
|
||||
.map((r) => getRate(r.rateDefinition.cancellationRule))
|
||||
.join("|"),
|
||||
ancillaries: rooms
|
||||
.filter((r) => findBreakfastPackage(r.packages))
|
||||
.map((r) => {
|
||||
return mapBreakfastPackage(
|
||||
findBreakfastPackage(r.packages)!,
|
||||
r.adults,
|
||||
hotel.operaId
|
||||
)
|
||||
}),
|
||||
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
|
||||
bedType: rooms
|
||||
.map((r) => r.bedDescription)
|
||||
.join(",")
|
||||
.toLowerCase(),
|
||||
bnr: rooms.map((r) => r.confirmationNumber).join(","),
|
||||
bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
|
||||
breakfastOption: rooms
|
||||
.map((r) => {
|
||||
if (r.breakfastIncluded || r.breakfast) {
|
||||
return "breakfast buffet"
|
||||
}
|
||||
return "no breakfast"
|
||||
})
|
||||
.join(","),
|
||||
childBedPreference: rooms
|
||||
.map(
|
||||
(r) =>
|
||||
r.childBedPreferences
|
||||
.map((cbp) =>
|
||||
Array(cbp.quantity).fill(invertedBedTypeMap[cbp.bedType])
|
||||
)
|
||||
.join(",") ?? "-"
|
||||
)
|
||||
.join("|"),
|
||||
country: hotel?.address.country,
|
||||
departureDate: format(departureDate, "yyyy-MM-dd"),
|
||||
duration: differenceInCalendarDays(departureDate, arrivalDate),
|
||||
hotelID: hotel.operaId,
|
||||
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
|
||||
noOfAdults,
|
||||
noOfChildren,
|
||||
noOfRooms,
|
||||
rateCode: rooms.map((r) => r.rateDefinition.rateCode).join(","),
|
||||
rateCodeCancellationRule: rooms
|
||||
.map((r) => r.rateDefinition.cancellationText)
|
||||
.join(",")
|
||||
.toLowerCase(),
|
||||
rateCodeName: rooms
|
||||
.map((r) => r.rateDefinition.title)
|
||||
.join(",")
|
||||
.toLowerCase(),
|
||||
//rateCodeType: , //TODO: Add when available in API. "regular, promotion, corporate etx",
|
||||
region: hotel?.address.city,
|
||||
revenueCurrencyCode: rooms.map((r) => r.currencyCode).join(","),
|
||||
roomPrice: rooms.map((r) => r.roomPrice).join(","),
|
||||
roomTypeCode: rooms.map((r) => r.roomTypeCode ?? "-").join(","),
|
||||
searchType: "hotel",
|
||||
specialRoomType: rooms
|
||||
.map((room) => getSpecialRoomType(room.packages))
|
||||
.join(","),
|
||||
totalPrice: rooms.map((r) => r.totalPrice).join(","),
|
||||
}
|
||||
|
||||
const paymentInfo: TrackingSDKPaymentInfo = {
|
||||
paymentStatus: "confirmed",
|
||||
}
|
||||
|
||||
return {
|
||||
hotelsTrackingData,
|
||||
pageTrackingData,
|
||||
paymentInfo,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { differenceInCalendarDays, format, isWeekend } from "date-fns"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests"
|
||||
@@ -10,30 +9,20 @@ import Receipt from "@/components/HotelReservation/BookingConfirmation/Receipt"
|
||||
import Rooms from "@/components/HotelReservation/BookingConfirmation/Rooms"
|
||||
import SidePanel from "@/components/HotelReservation/SidePanel"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import BookingConfirmationProvider from "@/providers/BookingConfirmationProvider"
|
||||
|
||||
import { invertedBedTypeMap } from "../utils"
|
||||
import Alerts from "./Alerts"
|
||||
import Confirmation from "./Confirmation"
|
||||
import Tracking from "./Tracking"
|
||||
import { mapRoomState } from "./utils"
|
||||
|
||||
import styles from "./bookingConfirmation.module.css"
|
||||
|
||||
import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation"
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKHotelInfo,
|
||||
type TrackingSDKPageData,
|
||||
type TrackingSDKPaymentInfo,
|
||||
} from "@/types/components/tracking"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
|
||||
export default async function BookingConfirmation({
|
||||
confirmationNumber,
|
||||
}: BookingConfirmationProps) {
|
||||
const lang = getLang()
|
||||
const bookingConfirmation = await getBookingConfirmation(confirmationNumber)
|
||||
|
||||
if (!bookingConfirmation) {
|
||||
@@ -44,74 +33,6 @@ export default async function BookingConfirmation({
|
||||
return notFound()
|
||||
}
|
||||
|
||||
const arrivalDate = new Date(booking.checkInDate)
|
||||
const departureDate = new Date(booking.checkOutDate)
|
||||
|
||||
const selectedBreakfast = booking.packages.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
|
||||
)
|
||||
|
||||
const breakfastAncillary = selectedBreakfast && {
|
||||
hotelid: hotel.operaId,
|
||||
productName: "BreakfastAdult",
|
||||
productCategory: "", // TODO: Add category
|
||||
productId: selectedBreakfast.code ?? "",
|
||||
productPrice: +selectedBreakfast.unitPrice,
|
||||
productUnits: booking.adults,
|
||||
productPoints: 0,
|
||||
productType: "food",
|
||||
}
|
||||
|
||||
const initialPageTrackingData: TrackingSDKPageData = {
|
||||
pageId: "booking-confirmation",
|
||||
domainLanguage: lang,
|
||||
channel: TrackingChannelEnum["hotelreservation"],
|
||||
pageName: `hotelreservation|confirmation`,
|
||||
siteSections: `hotelreservation|confirmation`,
|
||||
pageType: "confirmation",
|
||||
siteVersion: "new-web",
|
||||
}
|
||||
|
||||
const initialHotelsTrackingData: TrackingSDKHotelInfo = {
|
||||
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
|
||||
departureDate: format(departureDate, "yyyy-MM-dd"),
|
||||
noOfAdults: booking.adults,
|
||||
noOfChildren: booking.childrenAges?.length,
|
||||
ageOfChildren: booking.childrenAges?.join(","),
|
||||
childBedPreference: booking?.childBedPreferences
|
||||
?.flatMap((c) => Array(c.quantity).fill(invertedBedTypeMap[c.bedType]))
|
||||
.join("|"),
|
||||
noOfRooms: 1, // // TODO: Handle multiple rooms
|
||||
duration: differenceInCalendarDays(departureDate, arrivalDate),
|
||||
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
|
||||
searchType: "hotel",
|
||||
bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
|
||||
country: hotel?.address.country,
|
||||
hotelID: hotel.operaId,
|
||||
region: hotel?.address.city,
|
||||
rateCode: booking.rateDefinition.rateCode ?? undefined,
|
||||
//rateCodeType: , //TODO: Add when available in API. "regular, promotion, corporate etx",
|
||||
rateCodeName: booking.rateDefinition.title ?? undefined,
|
||||
rateCodeCancellationRule:
|
||||
booking.rateDefinition?.cancellationText ?? undefined,
|
||||
revenueCurrencyCode: booking.currencyCode,
|
||||
breakfastOption: booking.rateDefinition.breakfastIncluded
|
||||
? "breakfast buffet"
|
||||
: "no breakfast",
|
||||
totalPrice: booking.totalPrice,
|
||||
//specialRoomType: getSpecialRoomType(booking.packages), TODO: Add
|
||||
//roomTypeName: booking.roomTypeCode ?? undefined, TODO: Do we get the name?
|
||||
bedType: room?.bedType.name,
|
||||
roomTypeCode: booking.roomTypeCode ?? undefined,
|
||||
roomPrice: booking.roomPrice,
|
||||
bnr: booking.confirmationNumber ?? undefined,
|
||||
ancillaries: breakfastAncillary ? [breakfastAncillary] : [],
|
||||
}
|
||||
|
||||
const paymentInfo: TrackingSDKPaymentInfo = {
|
||||
paymentStatus: "confirmed",
|
||||
}
|
||||
|
||||
return (
|
||||
<BookingConfirmationProvider
|
||||
bookingCode={booking.bookingCode}
|
||||
@@ -151,11 +72,7 @@ export default async function BookingConfirmation({
|
||||
</SidePanel>
|
||||
</aside>
|
||||
</Confirmation>
|
||||
<TrackingSDK
|
||||
pageData={initialPageTrackingData}
|
||||
hotelInfo={initialHotelsTrackingData}
|
||||
paymentInfo={paymentInfo}
|
||||
/>
|
||||
<Tracking bookingConfirmation={bookingConfirmation} />
|
||||
</BookingConfirmationProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,13 +19,17 @@ export function mapRoomState(
|
||||
breakfast,
|
||||
breakfastIncluded,
|
||||
children: booking.childrenAges.length,
|
||||
childrenAges: booking.childrenAges,
|
||||
childBedPreferences: booking.childBedPreferences,
|
||||
confirmationNumber: booking.confirmationNumber,
|
||||
currencyCode: booking.currencyCode,
|
||||
fromDate: booking.checkInDate,
|
||||
name: room.name,
|
||||
packages: booking.packages,
|
||||
rateDefinition: booking.rateDefinition,
|
||||
roomFeatures: booking.packages.filter((p) => p.type === "RoomFeature"),
|
||||
roomPrice: booking.roomPrice,
|
||||
roomTypeCode: booking.roomTypeCode,
|
||||
toDate: booking.checkOutDate,
|
||||
totalPrice: booking.totalPrice,
|
||||
totalPriceExVat: booking.totalPriceExVat,
|
||||
|
||||
Reference in New Issue
Block a user