import { createHash } from "crypto" import { differenceInCalendarDays, format, isWeekend } from "date-fns" import { CancellationRuleEnum } from "@scandic-hotels/common/constants/booking" import { CurrencyEnum } from "@scandic-hotels/common/constants/currency" import { RateEnum } from "@scandic-hotels/common/constants/rate" import { TrackingChannelEnum, type TrackingSDKAncillaries, type TrackingSDKHotelInfo, type TrackingSDKPageData, type TrackingSDKPaymentInfo, } from "@scandic-hotels/tracking/types" import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast" import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter" import { readPaymentInfoFromSessionStorage } from "../../../components/EnterDetails/Payment/helpers" import { invertedBedTypeMap } from "../../../utils/SelectRate" import { getSpecialRoomType } from "../../../utils/specialRoomType" import type { Lang } from "@scandic-hotels/common/constants/language" import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation" import type { RateDefinition } from "@scandic-hotels/trpc/types/roomAvailability" import type { Room } from "../../../types/stores/booking-confirmation" function getRate(cancellationRule: RateDefinition["cancellationRule"] | null) { switch (cancellationRule) { case "CancellableBefore6PM": return RateEnum.flex case "Changeable": return RateEnum.change case "NotCancellable": return RateEnum.save default: return "" } } function mapAncillaryPackage( ancillaryPackage: BookingConfirmation["booking"]["packages"][number], operaId: string ) { const isPoints = ancillaryPackage.currency === CurrencyEnum.POINTS return { hotelid: operaId, productCategory: "", // TODO: Add category productId: ancillaryPackage.code, productName: ancillaryPackage.description, productPoints: isPoints ? ancillaryPackage.totalPrice : 0, productPrice: isPoints ? 0 : ancillaryPackage.totalPrice, productType: ancillaryPackage.code === BreakfastPackageEnum.REGULAR_BREAKFAST ? "food" : "room preference", productUnits: ancillaryPackage.totalUnit, productDeliveryTime: "", } } export function getTracking( lang: Lang, booking: BookingConfirmation["booking"], hotel: BookingConfirmation["hotel"], rooms: Room[], searchTerm?: string ) { const arrivalDate = new Date(booking.checkInDate) const departureDate = new Date(booking.checkOutDate) const paymentInfoSessionData = readPaymentInfoFromSessionStorage() 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.childrenAges?.length ?? 0).join(",") const noOfRooms = rooms.length const isFlexBooking = booking.rateDefinition.cancellationRule === CancellationRuleEnum.CancellableBefore6PM const isGuaranteedFlexBooking = booking.guaranteeInfo && isFlexBooking const ancillaries: TrackingSDKAncillaries = rooms .flatMap((r) => r.packages) .filter( (p) => p.code === RoomPackageCodeEnum.PET_ROOM || p.code === BreakfastPackageEnum.REGULAR_BREAKFAST ) .map((pkg) => mapAncillaryPackage(pkg, hotel.operaId)) const hotelsTrackingData: TrackingSDKHotelInfo = { ageOfChildren: rooms.map((r) => r.childrenAges?.join(",") ?? "").join("|"), analyticsRateCode: rooms .map((r) => getRate(r.rateDefinition.cancellationRule)) .join("|"), arrivalDate: format(arrivalDate, "yyyy-MM-dd"), bedType: rooms .map((r) => r.bedType) .join(",") .toLowerCase(), bnr: rooms.map((r) => r.confirmationNumber).join(","), bookingCode: rooms.map((room) => room.bookingCode ?? "n/a").join(", "), bookingCodeAvailability: booking.bookingCode ? rooms.map((room) => (room.bookingCode ? "true" : "false")).join(", ") : undefined, 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.cancellationRule) .join(",") .toLowerCase(), rateCodeName: rooms.map(constructRateCodeName).join(","), rateCodeType: rooms.map((r) => r.rateCodeType?.toLowerCase()).join(","), region: hotel?.address.city, revenueCurrencyCode: [...new Set(rooms.map((r) => r.currencyCode))].join( "," ), rewardNight: booking.roomPoints > 0 ? "yes" : "no", rewardNightAvailability: booking.roomPoints > 0 ? "true" : "false", points: booking.roomPoints > 0 ? booking.roomPoints : undefined, roomPrice: rooms.reduce((total, room) => total + room.roomPrice, 0), roomTypeCode: rooms.map((r) => r.roomTypeCode ?? "").join(","), searchTerm, searchType: "hotel", specialRoomType: rooms .map((room) => getSpecialRoomType(room.packages)) .join(","), totalPrice: rooms.reduce((total, room) => total + room.totalPrice, 0), lateArrivalGuarantee: booking.rateDefinition.mustBeGuaranteed ? "mandatory" : isFlexBooking ? booking.guaranteeInfo ? "yes" : "no" : "na", guaranteedProduct: isGuaranteedFlexBooking ? "room" : "na", emailId: getSHAHash(booking.guest.email), mobileNumber: getSHAHash(booking.guest.phoneNumber), } const paymentInfo: TrackingSDKPaymentInfo = { paymentStatus: isGuaranteedFlexBooking ? "glacardsaveconfirmed" : "confirmed", type: booking.guaranteeInfo?.cardType ?? paymentInfoSessionData?.paymentMethod, } return { hotelsTrackingData, pageTrackingData, paymentInfo, ancillaries, } } function constructRateCodeName(room: Room) { if (room.cheques) { return "corporate cheque" } else if (room.vouchers) { return "voucher" } else if (room.roomPoints) { return "redemption" } const rate = getRate(room.rateDefinition.cancellationRule) const bookingCodeStr = room.bookingCode ? room.bookingCode.toUpperCase() : "" const breakfastIncludedStr = room.breakfastIncluded ? "incl. breakfast" : "excl. breakfast" return [bookingCodeStr, rate, breakfastIncludedStr] .filter(Boolean) .join(" - ") } function getSHAHash(key: string) { return createHash("sha256").update(key).digest("hex") }