Merged in fix/tracking-fixes (pull request #1930)

Fix/tracking fixes

* fix: remove hotelInfo and paymentInfo when user reloads page on confirmation page

* fix: clean session storage item on unmount

* fix: commented out hard navigation hook

* fix: update price calculation on room ancillary in tracking

* fix: update discount calculation

* fix: add space between fns

* fix: allow useSoftNavigation to fire pageview again on same pathname

* fix: prevent bedSelection and breakfastSelection from tracking twice


Approved-by: Hrishikesh Vaipurkar
This commit is contained in:
Tobias Johansson
2025-05-03 07:27:54 +00:00
parent 0c7836fa59
commit 71f1e9fe2c
6 changed files with 84 additions and 68 deletions

View File

@@ -1,5 +1,7 @@
"use client" "use client"
import { useEffect, useState } from "react"
import { useBookingConfirmationStore } from "@/stores/booking-confirmation" import { useBookingConfirmationStore } from "@/stores/booking-confirmation"
import TrackingSDK from "@/components/TrackingSDK" import TrackingSDK from "@/components/TrackingSDK"
@@ -17,6 +19,22 @@ export default function Tracking({
}) { }) {
const lang = useLang() const lang = useLang()
const bookingRooms = useBookingConfirmationStore((state) => state.rooms) const bookingRooms = useBookingConfirmationStore((state) => state.rooms)
const [hasLoadedBookingConfirmation] = useState(() => {
if (typeof window !== "undefined") {
return sessionStorage.getItem("hasLoadedBookingConfirmation")
}
return null
})
useEffect(() => {
sessionStorage.setItem("hasLoadedBookingConfirmation", "true")
return () => {
sessionStorage.removeItem("hasLoadedBookingConfirmation")
}
}, [])
if (!bookingRooms.every(Boolean)) { if (!bookingRooms.every(Boolean)) {
return null return null
} }
@@ -34,8 +52,8 @@ export default function Tracking({
return ( return (
<TrackingSDK <TrackingSDK
pageData={pageTrackingData} pageData={pageTrackingData}
hotelInfo={hotelsTrackingData} hotelInfo={hasLoadedBookingConfirmation ? undefined : hotelsTrackingData}
paymentInfo={paymentInfo} paymentInfo={hasLoadedBookingConfirmation ? undefined : paymentInfo}
ancillaries={ancillaries} ancillaries={ancillaries}
/> />
) )

View File

@@ -57,7 +57,9 @@ export default function BedType() {
const selectedBedType = methods.watch("bedType") const selectedBedType = methods.watch("bedType")
const handleSubmit = methods.handleSubmit const handleSubmit = methods.handleSubmit
useEffect(() => { useEffect(() => {
if (selectedBedType) {
handleSubmit(onSubmit)() handleSubmit(onSubmit)()
}
}, [selectedBedType, handleSubmit, onSubmit]) }, [selectedBedType, handleSubmit, onSubmit])
return ( return (

View File

@@ -69,7 +69,9 @@ export default function Breakfast() {
const selectedBreakfast = methods.watch("breakfast") const selectedBreakfast = methods.watch("breakfast")
const handleSubmit = methods.handleSubmit const handleSubmit = methods.handleSubmit
useEffect(() => { useEffect(() => {
if (selectedBreakfast) {
handleSubmit(onSubmit)() handleSubmit(onSubmit)()
}
}, [selectedBreakfast, handleSubmit, onSubmit]) }, [selectedBreakfast, handleSubmit, onSubmit])
return ( return (

View File

@@ -93,8 +93,7 @@ export function getTracking(
.join("|"), .join("|"),
country: hotel?.address.country, country: hotel?.address.country,
departureDate: format(departureDate, "yyyy-MM-dd"), departureDate: format(departureDate, "yyyy-MM-dd"),
discount: rooms discount: rooms.reduce((total, room, idx) => {
.map((room, idx) => {
const isMainRoom = idx === 0 const isMainRoom = idx === 0
if ( if (
hasMemberPrice(room.roomRate) && hasMemberPrice(room.roomRate) &&
@@ -104,11 +103,17 @@ export function getTracking(
) { ) {
const memberPrice = room.roomRate.member.localPrice.pricePerStay const memberPrice = room.roomRate.member.localPrice.pricePerStay
const publicPrice = room.roomRate.public.localPrice.pricePerStay const publicPrice = room.roomRate.public.localPrice.pricePerStay
return publicPrice - memberPrice total += publicPrice - memberPrice
} else if (
hasPublicPrice(room.roomRate) &&
room.roomRate.public.localPrice.regularPricePerStay
) {
const publicPrice = room.roomRate.public.localPrice.pricePerStay
const regularPrice = room.roomRate.public.localPrice.regularPricePerStay
total += regularPrice - publicPrice
} }
return 0 return total
}) }, 0),
.join(","),
duration: differenceInCalendarDays(departureDate, arrivalDate), duration: differenceInCalendarDays(departureDate, arrivalDate),
hotelID: hotel?.operaId, hotelID: hotel?.operaId,
leadTime: differenceInCalendarDays(arrivalDate, new Date()), leadTime: differenceInCalendarDays(arrivalDate, new Date()),
@@ -194,19 +199,18 @@ export function getTracking(
const ancillaries: TrackingSDKAncillaries = roomsWithPetRoom const ancillaries: TrackingSDKAncillaries = roomsWithPetRoom
.slice(0, 1) // should only be one item .slice(0, 1) // should only be one item
.map((room) => { .map((room) => {
const petRoomPackage = room.packages.find( return room.packages
(p) => p.code === RoomPackageCodeEnum.PET_ROOM .filter((p) => p.code === RoomPackageCodeEnum.PET_ROOM)
) .map((pkg) => ({
return {
hotelId: hotel.operaId, hotelId: hotel.operaId,
productId: petRoomPackage?.code!, // property is guaranteed at this point productId: pkg.code,
productUnits: roomsWithPetRoom.length, productUnits: roomsWithPetRoom.length,
productPoints: 0, productPoints: 0,
productPrice: petRoomPackage?.localPrice.totalPrice ?? 0, productPrice: pkg.localPrice.totalPrice * roomsWithPetRoom.length,
productType: "room preference", productType: "room preference",
productName: "pet room", productName: "pet room",
productCategory: "", productCategory: "",
} }))[0]
}) })
return { return {
@@ -215,6 +219,7 @@ export function getTracking(
ancillaries, ancillaries,
} }
} }
function hasPublicPrice( function hasPublicPrice(
roomRate: Product roomRate: Product
): roomRate is PriceProduct & { public: NonNullable<PriceProduct["public"]> } { ): roomRate is PriceProduct & { public: NonNullable<PriceProduct["public"]> } {
@@ -223,6 +228,7 @@ function hasPublicPrice(
} }
return false return false
} }
function hasMemberPrice( function hasMemberPrice(
roomRate: Product roomRate: Product
): roomRate is PriceProduct & { member: NonNullable<PriceProduct["member"]> } { ): roomRate is PriceProduct & { member: NonNullable<PriceProduct["member"]> } {
@@ -231,6 +237,7 @@ function hasMemberPrice(
} }
return false return false
} }
function hasPetRoom( function hasPetRoom(
room: Room room: Room
): room is Room & { packages: NonNullable<Room["packages"]> } { ): room is Room & { packages: NonNullable<Room["packages"]> } {
@@ -239,6 +246,7 @@ function hasPetRoom(
} }
return room.packages.some((p) => p.code === RoomPackageCodeEnum.PET_ROOM) return room.packages.some((p) => p.code === RoomPackageCodeEnum.PET_ROOM)
} }
function calcTotalPrice(rooms: Room[], isMember: boolean) { function calcTotalPrice(rooms: Room[], isMember: boolean) {
const totalRoomPrice = calcTotalRoomPrice(rooms, isMember) const totalRoomPrice = calcTotalRoomPrice(rooms, isMember)
const totalPackageSum = rooms.reduce((total, room) => { const totalPackageSum = rooms.reduce((total, room) => {
@@ -247,6 +255,7 @@ function calcTotalPrice(rooms: Room[], isMember: boolean) {
}, 0) }, 0)
return totalRoomPrice + totalPackageSum return totalRoomPrice + totalPackageSum
} }
function calcTotalRoomPrice(rooms: Room[], isMember: boolean) { function calcTotalRoomPrice(rooms: Room[], isMember: boolean) {
return rooms.reduce((total, room, idx) => { return rooms.reduce((total, room, idx) => {
// When it comes special rates, only redemption has additional price and that should be added // When it comes special rates, only redemption has additional price and that should be added

View File

@@ -1,13 +1,7 @@
"use client" "use client"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { import { startTransition, useEffect, useRef, useState } from "react"
startTransition,
useEffect,
useOptimistic,
useRef,
useState,
} from "react"
import { trpc } from "@/lib/trpc/client" import { trpc } from "@/lib/trpc/client"
import useRouterTransitionStore from "@/stores/router-transition" import useRouterTransitionStore from "@/stores/router-transition"
@@ -105,7 +99,6 @@ export const useTrackSoftNavigation = ({
isError, isError,
} = trpc.user.userTrackingInfo.useQuery() } = trpc.user.userTrackingInfo.useQuery()
const [loading, setLoading] = useOptimistic(false)
const [status, setStatus] = useState<TransitionStatus>( const [status, setStatus] = useState<TransitionStatus>(
TransitionStatusEnum.NotRun TransitionStatusEnum.NotRun
) )
@@ -125,22 +118,17 @@ export const useTrackSoftNavigation = ({
if (isTransitioning && status === TransitionStatusEnum.NotRun) { if (isTransitioning && status === TransitionStatusEnum.NotRun) {
startTransition(() => { startTransition(() => {
setStatus(TransitionStatusEnum.Running) setStatus(TransitionStatusEnum.Running)
setLoading(true)
}) })
return return
} }
if ( if (isTransitioning && status === TransitionStatusEnum.Running) {
!loading &&
isTransitioning &&
status === TransitionStatusEnum.Running
) {
setStatus(TransitionStatusEnum.Done) setStatus(TransitionStatusEnum.Done)
stopRouterTransition() stopRouterTransition()
return return
} }
if (!loading && !isTransitioning && status === TransitionStatusEnum.Done) { if (!isTransitioning && status === TransitionStatusEnum.Done) {
const pageLoadTime = getPageLoadTime() const pageLoadTime = getPageLoadTime()
const trackingData = { const trackingData = {
...pageData, ...pageData,
@@ -149,7 +137,6 @@ export const useTrackSoftNavigation = ({
pageLoadTime: pageLoadTime, pageLoadTime: pageLoadTime,
} }
const pageObject = createSDKPageObject(trackingData) const pageObject = createSDKPageObject(trackingData)
if (previousPathname.current !== pathName) {
const userData: TrackingSDKUserData = isError const userData: TrackingSDKUserData = isError
? { loginStatus: "Error" } ? { loginStatus: "Error" }
: userTrackingData : userTrackingData
@@ -162,16 +149,15 @@ export const useTrackSoftNavigation = ({
paymentInfo, paymentInfo,
ancillaries, ancillaries,
}) })
}
setStatus(TransitionStatusEnum.NotRun) // Reset status
previousPathname.current = pathName // Update for next render previousPathname.current = pathName // Update for next render
} }
}, [ }, [
isError, isError,
isPending, isPending,
isTransitioning, isTransitioning,
loading,
status, status,
setLoading,
stopRouterTransition, stopRouterTransition,
pageData, pageData,
pathName, pathName,

View File

@@ -1,9 +1,6 @@
"use client" "use client"
import { import { useTrackSoftNavigation } from "@/components/TrackingSDK/hooks"
useTrackHardNavigation,
useTrackSoftNavigation,
} from "@/components/TrackingSDK/hooks"
import type { import type {
TrackingSDKAncillaries, TrackingSDKAncillaries,
@@ -23,7 +20,9 @@ export default function TrackingSDK({
paymentInfo?: TrackingSDKPaymentInfo paymentInfo?: TrackingSDKPaymentInfo
ancillaries?: TrackingSDKAncillaries ancillaries?: TrackingSDKAncillaries
}) { }) {
useTrackHardNavigation({ pageData, hotelInfo, paymentInfo, ancillaries }) // This hook doesnt seem to be needed. Soft navigation hook seems to fire
// on both hard and soft navigations
// useTrackHardNavigation({ pageData, hotelInfo, paymentInfo, ancillaries })
useTrackSoftNavigation({ pageData, hotelInfo, paymentInfo, ancillaries }) useTrackSoftNavigation({ pageData, hotelInfo, paymentInfo, ancillaries })
return null return null