Merged in feat/BOOK-131-tracking-no-availability (pull request #2886)
feat(BOOK-131): add no availability tracking * feat(BOOK-131): add no availability tracking * feat(BOOK-131): add no availability tracking * feat(BOOK-131): extract noAvailability function * feat(BOOK-131): fix every render problem * feat(BOOK-131): noavailability handle return in function Approved-by: Erik Tiekstra Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -142,6 +142,11 @@ export function getErrorMessage(
|
|||||||
defaultMessage:
|
defaultMessage:
|
||||||
"Membership number can't be the same for two different rooms",
|
"Membership number can't be the same for two different rooms",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case undefined:
|
||||||
|
case null:
|
||||||
|
case "":
|
||||||
|
return errorCode
|
||||||
default:
|
default:
|
||||||
logger.warn("Error code not supported:", errorCode)
|
logger.warn("Error code not supported:", errorCode)
|
||||||
return errorCode
|
return errorCode
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { usePathname, useSearchParams } from "next/navigation"
|
import { usePathname, useSearchParams } from "next/navigation"
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useCallback, useEffect, useRef, useState } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
||||||
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
||||||
import useStickyPosition from "@scandic-hotels/common/hooks/useStickyPosition"
|
import useStickyPosition from "@scandic-hotels/common/hooks/useStickyPosition"
|
||||||
import { Alert } from "@scandic-hotels/design-system/Alert"
|
import { Alert } from "@scandic-hotels/design-system/Alert"
|
||||||
|
import { trackNoAvailability } from "@scandic-hotels/tracking/NoAvailabilityTracking"
|
||||||
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
|
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
|
||||||
|
|
||||||
import useLang from "../../../../hooks/useLang"
|
import useLang from "../../../../hooks/useLang"
|
||||||
import { useEnterDetailsStore } from "../../../../stores/enter-details"
|
import { useEnterDetailsStore } from "../../../../stores/enter-details"
|
||||||
|
import { mapPackageToLabel } from "../../../../utils/getRoomFeatureDescription"
|
||||||
|
|
||||||
import styles from "./bookingAlert.module.css"
|
import styles from "./bookingAlert.module.css"
|
||||||
|
|
||||||
@@ -19,6 +21,7 @@ function useBookingErrorAlert() {
|
|||||||
const updateSearchParams = useEnterDetailsStore(
|
const updateSearchParams = useEnterDetailsStore(
|
||||||
(state) => state.actions.updateSeachParamString
|
(state) => state.actions.updateSeachParamString
|
||||||
)
|
)
|
||||||
|
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
@@ -102,6 +105,12 @@ export default function BookingAlert({ isVisible = false }: BookingAlertProps) {
|
|||||||
selectRateReturnUrl,
|
selectRateReturnUrl,
|
||||||
} = useBookingErrorAlert()
|
} = useBookingErrorAlert()
|
||||||
|
|
||||||
|
const trackNoAvailabilityError = useNoAvailabilityTracking()
|
||||||
|
|
||||||
|
const isAvailabilityError =
|
||||||
|
errorCode === BookingErrorCodeEnum.AvailabilityError ||
|
||||||
|
errorCode === BookingErrorCodeEnum.NoAvailabilityForRateAndRoomType
|
||||||
|
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
const { getTopOffset } = useStickyPosition()
|
const { getTopOffset } = useStickyPosition()
|
||||||
|
|
||||||
@@ -122,11 +131,13 @@ export default function BookingAlert({ isVisible = false }: BookingAlertProps) {
|
|||||||
}
|
}
|
||||||
}, [showAlert, getTopOffset])
|
}, [showAlert, getTopOffset])
|
||||||
|
|
||||||
if (!showAlert) return null
|
useEffect(() => {
|
||||||
|
if (showAlert && isAvailabilityError) {
|
||||||
|
trackNoAvailabilityError()
|
||||||
|
}
|
||||||
|
}, [showAlert, isAvailabilityError, trackNoAvailabilityError])
|
||||||
|
|
||||||
const isAvailabilityError =
|
if (!showAlert) return null
|
||||||
errorCode === BookingErrorCodeEnum.AvailabilityError ||
|
|
||||||
errorCode === BookingErrorCodeEnum.NoAvailabilityForRateAndRoomType
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper} ref={ref}>
|
<div className={styles.wrapper} ref={ref}>
|
||||||
@@ -149,3 +160,48 @@ export default function BookingAlert({ isVisible = false }: BookingAlertProps) {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useNoAvailabilityTracking() {
|
||||||
|
const { fromDate, toDate, hotelId, bookingCode, searchType, rooms } =
|
||||||
|
useEnterDetailsStore((state) => state.booking)
|
||||||
|
const lang = useLang()
|
||||||
|
|
||||||
|
const specialRoomType = rooms
|
||||||
|
?.map((room) => {
|
||||||
|
const packages = room.packages
|
||||||
|
?.map((pkg) => mapPackageToLabel(pkg))
|
||||||
|
.join(",")
|
||||||
|
return packages ?? ""
|
||||||
|
})
|
||||||
|
.join("|")
|
||||||
|
|
||||||
|
const track = useCallback(
|
||||||
|
() =>
|
||||||
|
trackNoAvailability({
|
||||||
|
specialRoomType,
|
||||||
|
lang,
|
||||||
|
searchTerm: hotelId,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
hotelId,
|
||||||
|
noOfRooms: rooms.length,
|
||||||
|
searchType,
|
||||||
|
bookingCode: bookingCode ?? "n/a",
|
||||||
|
pageId: "details",
|
||||||
|
pageName: "hotelreservation|details",
|
||||||
|
pageType: "bookingenterdetailspage",
|
||||||
|
siteSections: "hotelreservation|details",
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
specialRoomType,
|
||||||
|
lang,
|
||||||
|
hotelId,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
rooms.length,
|
||||||
|
searchType,
|
||||||
|
bookingCode,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return track
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,29 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { usePathname, useSearchParams } from "next/navigation"
|
import { usePathname, useSearchParams } from "next/navigation"
|
||||||
import { useEffect } from "react"
|
import { useCallback, useEffect } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { toast } from "@scandic-hotels/design-system/Toast"
|
import { toast } from "@scandic-hotels/design-system/Toast"
|
||||||
|
import { trackNoAvailability } from "@scandic-hotels/tracking/NoAvailabilityTracking"
|
||||||
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
|
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
|
||||||
|
|
||||||
export default function AvailabilityError() {
|
import useLang from "../../hooks/useLang"
|
||||||
|
import { mapPackageToLabel } from "../../utils/getRoomFeatureDescription"
|
||||||
|
|
||||||
|
import type { SelectRateBooking } from "../../types/components/selectRate/selectRate"
|
||||||
|
|
||||||
|
type AvailabilityErrorProps = {
|
||||||
|
booking: SelectRateBooking
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AvailabilityError({ booking }: AvailabilityErrorProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
|
const lang = useLang()
|
||||||
|
|
||||||
|
const { rooms, fromDate, toDate, hotelId, bookingCode, searchType } = booking
|
||||||
|
|
||||||
const errorCode = searchParams.get("errorCode")
|
const errorCode = searchParams.get("errorCode")
|
||||||
const hasAvailabilityError =
|
const hasAvailabilityError =
|
||||||
@@ -21,15 +34,51 @@ export default function AvailabilityError() {
|
|||||||
"Unfortunately, one of the rooms you selected is sold out. Please choose another room to proceed.",
|
"Unfortunately, one of the rooms you selected is sold out. Please choose another room to proceed.",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const noAvailabilityTracking = useCallback(() => {
|
||||||
|
const specialRoomType = rooms
|
||||||
|
?.map((room) => {
|
||||||
|
const packages = room.packages
|
||||||
|
?.map((pkg) => {
|
||||||
|
mapPackageToLabel(pkg)
|
||||||
|
})
|
||||||
|
.join(",")
|
||||||
|
|
||||||
|
return packages ?? ""
|
||||||
|
})
|
||||||
|
.join("|")
|
||||||
|
|
||||||
|
trackNoAvailability({
|
||||||
|
specialRoomType,
|
||||||
|
searchTerm: hotelId,
|
||||||
|
lang,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
noOfRooms: rooms.length,
|
||||||
|
searchType,
|
||||||
|
hotelId,
|
||||||
|
bookingCode,
|
||||||
|
pageId: "select-rate",
|
||||||
|
pageName: "hotelreservation|select-rate",
|
||||||
|
pageType: "bookingroomsandratespage",
|
||||||
|
siteSections: "hotelreservation|select-rate",
|
||||||
|
})
|
||||||
|
}, [rooms, hotelId, lang, fromDate, toDate, searchType, bookingCode])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasAvailabilityError) {
|
if (hasAvailabilityError) {
|
||||||
toast.error(errorMessage)
|
toast.error(errorMessage)
|
||||||
|
noAvailabilityTracking()
|
||||||
const newParams = new URLSearchParams(searchParams.toString())
|
const newParams = new URLSearchParams(searchParams.toString())
|
||||||
newParams.delete("errorCode")
|
newParams.delete("errorCode")
|
||||||
window.history.replaceState({}, "", `${pathname}?${newParams.toString()}`)
|
window.history.replaceState({}, "", `${pathname}?${newParams.toString()}`)
|
||||||
}
|
}
|
||||||
}, [errorMessage, hasAvailabilityError, pathname, searchParams])
|
}, [
|
||||||
|
errorMessage,
|
||||||
|
hasAvailabilityError,
|
||||||
|
noAvailabilityTracking,
|
||||||
|
pathname,
|
||||||
|
searchParams,
|
||||||
|
])
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { NoAvailabilityTracking } from "@scandic-hotels/tracking/NoAvailabilityTracking"
|
||||||
|
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
||||||
|
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||||
|
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
|
||||||
|
|
||||||
|
import { useSelectRateContext } from "../../../contexts/SelectRate/SelectRateContext"
|
||||||
|
import useLang from "../../../hooks/useLang"
|
||||||
|
import { mapPackageToLabel } from "../../../utils/getRoomFeatureDescription"
|
||||||
|
import { getValidDates } from "../getValidDates"
|
||||||
|
import { getSelectRateTracking } from "./tracking"
|
||||||
|
|
||||||
|
import type { RouterOutput } from "@scandic-hotels/trpc/client"
|
||||||
|
|
||||||
|
import type { SelectRateBooking } from "../../../types/components/selectRate/selectRate"
|
||||||
|
|
||||||
|
interface TrackingProps {
|
||||||
|
hotelData: NonNullable<RouterOutput["hotel"]["get"]>
|
||||||
|
booking: SelectRateBooking
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SelectRateTracking({ hotelData, booking }: TrackingProps) {
|
||||||
|
const lang = useLang()
|
||||||
|
const { availability, input } = useSelectRateContext()
|
||||||
|
|
||||||
|
const { fromDate, toDate } = getValidDates(booking.fromDate, booking.toDate)
|
||||||
|
|
||||||
|
const { rooms, searchType, bookingCode, city: paramCity } = booking
|
||||||
|
|
||||||
|
const arrivalDate = fromDate.toDate()
|
||||||
|
const departureDate = toDate.toDate()
|
||||||
|
|
||||||
|
const specialRoomType = input.data?.booking.rooms
|
||||||
|
?.map((room) => {
|
||||||
|
const packages = room.packages
|
||||||
|
?.map((pkg) => mapPackageToLabel(pkg))
|
||||||
|
.join(",")
|
||||||
|
return packages ?? ""
|
||||||
|
})
|
||||||
|
.join("|")
|
||||||
|
const { hotelsTrackingData, pageTrackingData } = getSelectRateTracking({
|
||||||
|
lang,
|
||||||
|
arrivalDate,
|
||||||
|
departureDate,
|
||||||
|
hotelId: hotelData.hotel.id,
|
||||||
|
hotelName: hotelData.hotel.name,
|
||||||
|
country: hotelData.hotel.address.country,
|
||||||
|
hotelCity: hotelData.hotel.address.city,
|
||||||
|
paramCity,
|
||||||
|
bookingCode,
|
||||||
|
isRedemption: searchType === SEARCH_TYPE_REDEMPTION,
|
||||||
|
specialRoomType,
|
||||||
|
rooms,
|
||||||
|
})
|
||||||
|
|
||||||
|
let shouldTrackNoAvailability = false
|
||||||
|
if (availability && !availability.isFetching && availability.data) {
|
||||||
|
shouldTrackNoAvailability = availability.data.some((room) => {
|
||||||
|
if (!room || "error" in room) return false
|
||||||
|
|
||||||
|
const allRoomsNotAvailable = room.roomConfigurations.every(
|
||||||
|
(roomConfig) => roomConfig.status === AvailabilityEnum.NotAvailable
|
||||||
|
)
|
||||||
|
|
||||||
|
const isPublicPromotionWithCode = room.roomConfigurations.some((r) => {
|
||||||
|
const filteredCampaigns = r.campaign.filter(Boolean)
|
||||||
|
return filteredCampaigns.length
|
||||||
|
? filteredCampaigns.every(
|
||||||
|
(product) => !!product.rateDefinition?.isCampaignRate
|
||||||
|
)
|
||||||
|
: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const noAvailableBookingCodeRooms =
|
||||||
|
!isPublicPromotionWithCode &&
|
||||||
|
room.roomConfigurations.every(
|
||||||
|
(r) => r.status === AvailabilityEnum.NotAvailable || !r.code.length
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
allRoomsNotAvailable ||
|
||||||
|
(input.bookingCode && noAvailableBookingCodeRooms)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NoAvailabilityTracking
|
||||||
|
lang={lang}
|
||||||
|
shouldTrackNoAvailability={shouldTrackNoAvailability}
|
||||||
|
hotelsTrackingData={hotelsTrackingData}
|
||||||
|
pageTrackingData={pageTrackingData}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TrackingSDK hotelInfo={hotelsTrackingData} pageData={pageTrackingData} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
type TrackingSDKPageData,
|
type TrackingSDKPageData,
|
||||||
} from "@scandic-hotels/tracking/types"
|
} from "@scandic-hotels/tracking/types"
|
||||||
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
||||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
|
||||||
|
|
||||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||||
import type { Child } from "@scandic-hotels/trpc/types/child"
|
import type { Child } from "@scandic-hotels/trpc/types/child"
|
||||||
@@ -28,6 +27,7 @@ type SelectRateTrackingInput = {
|
|||||||
paramCity: string | undefined
|
paramCity: string | undefined
|
||||||
bookingCode?: string
|
bookingCode?: string
|
||||||
isRedemption?: boolean
|
isRedemption?: boolean
|
||||||
|
specialRoomType?: string
|
||||||
rooms?: Room[]
|
rooms?: Room[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@ export function getSelectRateTracking({
|
|||||||
paramCity,
|
paramCity,
|
||||||
bookingCode,
|
bookingCode,
|
||||||
isRedemption = false,
|
isRedemption = false,
|
||||||
|
specialRoomType,
|
||||||
rooms = [],
|
rooms = [],
|
||||||
}: SelectRateTrackingInput) {
|
}: SelectRateTrackingInput) {
|
||||||
const pageTrackingData: TrackingSDKPageData = {
|
const pageTrackingData: TrackingSDKPageData = {
|
||||||
@@ -83,25 +84,7 @@ export function getSelectRateTracking({
|
|||||||
searchType: "hotel",
|
searchType: "hotel",
|
||||||
bookingCode: bookingCode ?? "n/a",
|
bookingCode: bookingCode ?? "n/a",
|
||||||
rewardNight: isRedemption ? "yes" : "no",
|
rewardNight: isRedemption ? "yes" : "no",
|
||||||
specialRoomType: rooms
|
specialRoomType,
|
||||||
?.map((room) => {
|
|
||||||
const packages = room.packages
|
|
||||||
?.map((pkg) => {
|
|
||||||
if (pkg === RoomPackageCodeEnum.ACCESSIBILITY_ROOM) {
|
|
||||||
return "accessibility"
|
|
||||||
} else if (pkg === RoomPackageCodeEnum.ALLERGY_ROOM) {
|
|
||||||
return "allergy friendly"
|
|
||||||
} else if (pkg === RoomPackageCodeEnum.PET_ROOM) {
|
|
||||||
return "pet room"
|
|
||||||
} else {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.join(",")
|
|
||||||
|
|
||||||
return packages ?? ""
|
|
||||||
})
|
|
||||||
.join("|"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export async function SelectRate({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<AvailabilityError />
|
<AvailabilityError booking={booking} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { notFound } from "next/navigation"
|
|||||||
import { Suspense } from "react"
|
import { Suspense } from "react"
|
||||||
|
|
||||||
import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAndFriends"
|
import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAndFriends"
|
||||||
|
import { NoAvailabilityTracking } from "@scandic-hotels/tracking/NoAvailabilityTracking"
|
||||||
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
||||||
|
|
||||||
import { AlternativeHotelsPageTitle } from "../components/AlternativeHotelsPageTitle"
|
import { AlternativeHotelsPageTitle } from "../components/AlternativeHotelsPageTitle"
|
||||||
@@ -110,6 +111,10 @@ export async function AlternativeHotelsPage({
|
|||||||
|
|
||||||
const suspenseKey = stringify(searchParams)
|
const suspenseKey = stringify(searchParams)
|
||||||
|
|
||||||
|
const shouldTrackNoAvailability = !!(
|
||||||
|
hotels.every((hotel) => hotel.availability.status !== "Available") ||
|
||||||
|
(booking.bookingCode && hotels.length > 0 && !isBookingCodeRateAvailable)
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SelectHotel
|
<SelectHotel
|
||||||
@@ -128,6 +133,12 @@ export async function AlternativeHotelsPage({
|
|||||||
hotelInfo={hotelsTrackingData}
|
hotelInfo={hotelsTrackingData}
|
||||||
pageData={pageTrackingData}
|
pageData={pageTrackingData}
|
||||||
/>
|
/>
|
||||||
|
<NoAvailabilityTracking
|
||||||
|
lang={lang}
|
||||||
|
hotelsTrackingData={hotelsTrackingData}
|
||||||
|
pageTrackingData={pageTrackingData}
|
||||||
|
shouldTrackNoAvailability={shouldTrackNoAvailability}
|
||||||
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { notFound } from "next/navigation"
|
|||||||
import { Suspense } from "react"
|
import { Suspense } from "react"
|
||||||
|
|
||||||
import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAndFriends"
|
import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAndFriends"
|
||||||
|
import { NoAvailabilityTracking } from "@scandic-hotels/tracking/NoAvailabilityTracking"
|
||||||
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
||||||
|
|
||||||
import FnFNotAllowedAlert from "../components/FnFNotAllowedAlert"
|
import FnFNotAllowedAlert from "../components/FnFNotAllowedAlert"
|
||||||
@@ -85,7 +86,7 @@ export async function SelectHotelPage({
|
|||||||
arrivalDate,
|
arrivalDate,
|
||||||
departureDate,
|
departureDate,
|
||||||
hotelsResult: hotels?.length ?? 0,
|
hotelsResult: hotels?.length ?? 0,
|
||||||
searchTerm: booking.hotelId,
|
searchTerm: city.name,
|
||||||
country: hotels?.[0]?.hotel.address.country,
|
country: hotels?.[0]?.hotel.address.country,
|
||||||
hotelCity: hotels?.[0]?.hotel.address.city,
|
hotelCity: hotels?.[0]?.hotel.address.city,
|
||||||
bookingCode: booking.bookingCode,
|
bookingCode: booking.bookingCode,
|
||||||
@@ -96,6 +97,11 @@ export async function SelectHotelPage({
|
|||||||
|
|
||||||
const suspenseKey = stringify(searchParams)
|
const suspenseKey = stringify(searchParams)
|
||||||
|
|
||||||
|
const shouldTrackNoAvailability = !!(
|
||||||
|
hotels.every((hotel) => hotel.availability.status !== "Available") ||
|
||||||
|
(booking.bookingCode && hotels.length > 0 && !isBookingCodeRateAvailable)
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SelectHotel
|
<SelectHotel
|
||||||
@@ -111,6 +117,12 @@ export async function SelectHotelPage({
|
|||||||
hotelInfo={hotelsTrackingData}
|
hotelInfo={hotelsTrackingData}
|
||||||
pageData={pageTrackingData}
|
pageData={pageTrackingData}
|
||||||
/>
|
/>
|
||||||
|
<NoAvailabilityTracking
|
||||||
|
lang={lang}
|
||||||
|
hotelsTrackingData={hotelsTrackingData}
|
||||||
|
pageTrackingData={pageTrackingData}
|
||||||
|
shouldTrackNoAvailability={shouldTrackNoAvailability}
|
||||||
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { notFound } from "next/navigation"
|
import { notFound } from "next/navigation"
|
||||||
|
|
||||||
import { logger } from "@scandic-hotels/common/logger"
|
import { logger } from "@scandic-hotels/common/logger"
|
||||||
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
|
||||||
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||||
|
|
||||||
import { SelectRate } from "../components/SelectRate"
|
import { SelectRate } from "../components/SelectRate"
|
||||||
import { getValidDates } from "../components/SelectRate/getValidDates"
|
import { SelectRateTracking } from "../components/SelectRate/Tracking/SelectRateTracking"
|
||||||
import { getSelectRateTracking } from "../components/SelectRate/Tracking/tracking"
|
|
||||||
import { SelectRateProvider } from "../contexts/SelectRate/SelectRateContext"
|
import { SelectRateProvider } from "../contexts/SelectRate/SelectRateContext"
|
||||||
import { getHotel } from "../trpc/memoizedRequests"
|
import { getHotel } from "../trpc/memoizedRequests"
|
||||||
import { parseSelectRateSearchParams } from "../utils/url"
|
import { parseSelectRateSearchParams } from "../utils/url"
|
||||||
@@ -72,33 +70,12 @@ export async function SelectRatePage({
|
|||||||
notFound()
|
notFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
const { fromDate, toDate } = getValidDates(booking.fromDate, booking.toDate)
|
|
||||||
|
|
||||||
const { rooms, searchType, bookingCode, city: paramCity } = booking
|
|
||||||
|
|
||||||
const arrivalDate = fromDate.toDate()
|
|
||||||
const departureDate = toDate.toDate()
|
|
||||||
|
|
||||||
const { hotelsTrackingData, pageTrackingData } = getSelectRateTracking({
|
|
||||||
lang,
|
|
||||||
arrivalDate,
|
|
||||||
departureDate,
|
|
||||||
hotelId: hotelData.hotel.id,
|
|
||||||
hotelName: hotelData.hotel.name,
|
|
||||||
country: hotelData.hotel.address.country,
|
|
||||||
hotelCity: hotelData.hotel.address.city,
|
|
||||||
paramCity,
|
|
||||||
bookingCode,
|
|
||||||
isRedemption: searchType === SEARCH_TYPE_REDEMPTION,
|
|
||||||
rooms,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SelectRateProvider hotelData={hotelData}>
|
<SelectRateProvider hotelData={hotelData}>
|
||||||
<SelectRate hotelData={hotelData} booking={booking} />
|
<SelectRate hotelData={hotelData} booking={booking} />
|
||||||
|
<SelectRateTracking hotelData={hotelData} booking={booking} />
|
||||||
</SelectRateProvider>
|
</SelectRateProvider>
|
||||||
<TrackingSDK hotelInfo={hotelsTrackingData} pageData={pageTrackingData} />
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||||
|
|
||||||
|
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
|
||||||
import type { IntlShape } from "react-intl"
|
import type { IntlShape } from "react-intl"
|
||||||
|
|
||||||
export function getRoomFeatureDescription(
|
export function getRoomFeatureDescription(
|
||||||
@@ -21,3 +22,16 @@ export function getRoomFeatureDescription(
|
|||||||
|
|
||||||
return roomFeatureDescriptions[code] ?? description
|
return roomFeatureDescriptions[code] ?? description
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mapPackageToLabel(pkgCode: PackageEnum): string {
|
||||||
|
switch (pkgCode) {
|
||||||
|
case RoomPackageCodeEnum.ACCESSIBILITY_ROOM:
|
||||||
|
return "accessibility"
|
||||||
|
case RoomPackageCodeEnum.ALLERGY_ROOM:
|
||||||
|
return "allergy friendly"
|
||||||
|
case RoomPackageCodeEnum.PET_ROOM:
|
||||||
|
return "pet room"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
128
packages/tracking/lib/NoAvailabilityTracking.tsx
Normal file
128
packages/tracking/lib/NoAvailabilityTracking.tsx
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { differenceInCalendarDays, isWeekend } from "date-fns"
|
||||||
|
import { useEffect } from "react"
|
||||||
|
|
||||||
|
import {
|
||||||
|
TrackingChannelEnum,
|
||||||
|
type TrackingSDKHotelInfo,
|
||||||
|
type TrackingSDKPageData,
|
||||||
|
} from "@scandic-hotels/tracking/types"
|
||||||
|
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||||
|
|
||||||
|
import { trackEvent } from "./base"
|
||||||
|
|
||||||
|
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||||
|
|
||||||
|
type NoAvailabilityTrackingProps = {
|
||||||
|
lang: Lang
|
||||||
|
shouldTrackNoAvailability: boolean
|
||||||
|
hotelsTrackingData: TrackingSDKHotelInfo
|
||||||
|
pageTrackingData: TrackingSDKPageData
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NoAvailabilityTracking({
|
||||||
|
lang,
|
||||||
|
shouldTrackNoAvailability,
|
||||||
|
hotelsTrackingData,
|
||||||
|
pageTrackingData,
|
||||||
|
}: NoAvailabilityTrackingProps) {
|
||||||
|
useEffect(() => {
|
||||||
|
if (!shouldTrackNoAvailability) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
searchTerm,
|
||||||
|
searchType,
|
||||||
|
hotelID,
|
||||||
|
leadTime,
|
||||||
|
noOfRooms,
|
||||||
|
bookingCode,
|
||||||
|
rewardNight,
|
||||||
|
bookingTypeofDay,
|
||||||
|
duration,
|
||||||
|
specialRoomType,
|
||||||
|
} = hotelsTrackingData
|
||||||
|
|
||||||
|
trackEvent({
|
||||||
|
event: "noRoomsAvailable",
|
||||||
|
hotelInfo: {
|
||||||
|
searchTerm,
|
||||||
|
searchType,
|
||||||
|
noRoomsAvailable: "yes",
|
||||||
|
hotelID,
|
||||||
|
leadTime,
|
||||||
|
noOfRooms,
|
||||||
|
bookingCode,
|
||||||
|
rewardNight,
|
||||||
|
bookingTypeofDay,
|
||||||
|
duration,
|
||||||
|
specialRoomType,
|
||||||
|
},
|
||||||
|
pageInfo: pageTrackingData,
|
||||||
|
})
|
||||||
|
}, [lang, hotelsTrackingData, pageTrackingData])
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrackNoAvailabilityParams = {
|
||||||
|
specialRoomType: string
|
||||||
|
fromDate: string
|
||||||
|
toDate: string
|
||||||
|
hotelId: string
|
||||||
|
noOfRooms: number
|
||||||
|
searchType?: string
|
||||||
|
bookingCode?: string
|
||||||
|
searchTerm: string
|
||||||
|
pageId: string
|
||||||
|
pageName: string
|
||||||
|
pageType: string
|
||||||
|
siteSections: string
|
||||||
|
lang: Lang
|
||||||
|
}
|
||||||
|
|
||||||
|
export function trackNoAvailability({
|
||||||
|
specialRoomType,
|
||||||
|
lang,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
hotelId,
|
||||||
|
noOfRooms,
|
||||||
|
searchType,
|
||||||
|
bookingCode,
|
||||||
|
searchTerm,
|
||||||
|
pageId,
|
||||||
|
pageName,
|
||||||
|
pageType,
|
||||||
|
siteSections,
|
||||||
|
}: TrackNoAvailabilityParams) {
|
||||||
|
const arrivalDate = new Date(fromDate)
|
||||||
|
const departureDate = new Date(toDate)
|
||||||
|
|
||||||
|
trackEvent({
|
||||||
|
event: "noRoomsAvailable",
|
||||||
|
hotelInfo: {
|
||||||
|
searchTerm,
|
||||||
|
searchType,
|
||||||
|
noRoomsAvailable: "yes",
|
||||||
|
hotelId,
|
||||||
|
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
|
||||||
|
noOfRooms,
|
||||||
|
bookingCode: bookingCode ?? "n/a",
|
||||||
|
rewardNight: searchType === SEARCH_TYPE_REDEMPTION ? "yes" : "no",
|
||||||
|
bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
|
||||||
|
duration: differenceInCalendarDays(departureDate, arrivalDate),
|
||||||
|
specialRoomType,
|
||||||
|
},
|
||||||
|
pageInfo: {
|
||||||
|
channel: TrackingChannelEnum.hotelreservation,
|
||||||
|
domain: "www.scandichotels.com",
|
||||||
|
domainLanguage: lang,
|
||||||
|
pageId,
|
||||||
|
pageName,
|
||||||
|
pageType,
|
||||||
|
siteSections,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -12,7 +12,8 @@
|
|||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
"./*": "./lib/*.ts",
|
"./*": "./lib/*.ts",
|
||||||
"./TrackingSDK": "./lib/TrackingSDK.tsx"
|
"./TrackingSDK": "./lib/TrackingSDK.tsx",
|
||||||
|
"./NoAvailabilityTracking": "./lib/NoAvailabilityTracking.tsx"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scandic-hotels/common": "workspace:*",
|
"@scandic-hotels/common": "workspace:*",
|
||||||
|
|||||||
Reference in New Issue
Block a user