Merged in fix/SW-2853-tracking-breakfast-ancillaries (pull request #2188)

fix(SW-2853): tracking for breakfast ancillaries

* fix(SW-2853): tracking for breakfast ancillaries

* fix(SW-2853): viewAncillary event fix for breakfast

* fix(SW-2853): pr comment spread room facilities


Approved-by: Tobias Johansson
This commit is contained in:
Bianca Widstam
2025-05-22 10:05:54 +00:00
parent 818c1a3bcf
commit c4229c2dd2
10 changed files with 183 additions and 48 deletions

View File

@@ -32,6 +32,7 @@ export default function ActionButtons({
selectDeliveryTime,
selectQuantityAndDeliveryTime,
selectedAncillary,
breakfastData,
} = useAddAncillaryStore((state) => ({
currentStep: state.currentStep,
isBreakfast: state.isBreakfast,
@@ -41,6 +42,7 @@ export default function ActionButtons({
selectDeliveryTime: state.selectDeliveryTime,
selectQuantityAndDeliveryTime: state.selectQuantityAndDeliveryTime,
selectedAncillary: state.selectedAncillary,
breakfastData: state.breakfastData,
}))
const isMobile = useMediaQuery("(max-width: 767px)")
const { setError } = useFormContext()
@@ -69,7 +71,8 @@ export default function ActionButtons({
trackAddAncillary(
selectedAncillary,
quantityWithCard,
quantityWithPoints
quantityWithPoints,
breakfastData
)
if (isMobile) {
selectQuantityAndDeliveryTime()

View File

@@ -162,6 +162,7 @@ export default function AddAncillaryFlowModal({
data.deliveryTime,
"ancillary",
selectedAncillary,
breakfastData,
booking.guaranteeInfo?.cardType,
booking.roomTypeCode
)
@@ -180,12 +181,22 @@ export default function AddAncillaryFlowModal({
})
router.refresh()
} else {
trackAncillaryFailed(packages, data.deliveryTime, selectedAncillary)
trackAncillaryFailed(
packages,
data.deliveryTime,
selectedAncillary,
breakfastData
)
toast.error(ancillaryErrorMessage)
}
},
onError: () => {
trackAncillaryFailed(packages, data.deliveryTime, selectedAncillary)
trackAncillaryFailed(
packages,
data.deliveryTime,
selectedAncillary,
breakfastData
)
toast.error(ancillaryErrorMessage)
},
}
@@ -200,7 +211,8 @@ export default function AddAncillaryFlowModal({
savedCreditCard,
packages,
selectedAncillary,
data.deliveryTime
data.deliveryTime,
breakfastData
)
if (booking.refId) {
const card = savedCreditCard
@@ -270,6 +282,7 @@ export default function AddAncillaryFlowModal({
selectedAncillary,
packages: packagesToAdd,
isBreakfast,
breakfastData,
})
const shouldSkipGuarantee =
booking.guaranteeInfo || (data.quantityWithCard ?? 0) <= 0

View File

@@ -13,14 +13,17 @@ export default function WrappedAncillaryCard({
ancillary,
}: WrappedAncillaryProps) {
const { description, ...ancillaryWithoutDescription } = ancillary
const selectAncillary = useAddAncillaryStore((state) => state.selectAncillary)
const { selectAncillary, booking } = useAddAncillaryStore((state) => ({
selectAncillary: state.selectAncillary,
booking: state.booking,
}))
return (
<div
role="button"
onClick={() => {
selectAncillary(ancillary)
trackViewAncillary(ancillary)
trackViewAncillary(ancillary, booking)
}}
>
<AncillaryCard ancillary={ancillaryWithoutDescription} />

View File

@@ -20,12 +20,14 @@ export default function RemoveButton({
title,
booking,
ancillary,
addedBreakfastPackages,
}: {
refId: string
codes: string[]
title?: string
booking: Room
ancillary: PackageSchema
addedBreakfastPackages: PackageSchema[] | undefined
}) {
const lang = useLang()
const intl = useIntl()
@@ -75,7 +77,8 @@ export default function RemoveButton({
trackRemoveAncillary(
ancillary,
booking.hotelId,
booking.ancillary?.deliveryTime
booking.ancillary?.deliveryTime,
addedBreakfastPackages
)
router.refresh()

View File

@@ -137,6 +137,7 @@ export function AddedAncillaries({
title={ancillaryTitle}
booking={booking}
ancillary={ancillary}
addedBreakfastPackages={addedBreakfastPackages}
/>
</div>
) : null}
@@ -205,6 +206,7 @@ export function AddedAncillaries({
title={ancillaryTitle}
booking={booking}
ancillary={ancillary}
addedBreakfastPackages={addedBreakfastPackages}
/>
</div>
) : null}

View File

@@ -47,7 +47,7 @@ export default function GuaranteeAncillaryHandler({
return
}
const { formData, selectedAncillary, packages } = sessionData
const { formData, selectedAncillary, packages, breakfastData } = sessionData
addAncillary.mutate(
{
@@ -67,7 +67,8 @@ export default function GuaranteeAncillaryHandler({
packages,
formData.deliveryTime,
"room + ancillary",
selectedAncillary
selectedAncillary,
breakfastData
)
clearAncillarySessionData()
router.replace(returnUrl)
@@ -75,7 +76,8 @@ export default function GuaranteeAncillaryHandler({
trackAncillaryFailed(
packages,
formData.deliveryTime,
selectedAncillary
selectedAncillary,
breakfastData
)
router.replace(`${returnUrl}&errorCode=AncillaryFailed`)
}
@@ -84,7 +86,8 @@ export default function GuaranteeAncillaryHandler({
trackAncillaryFailed(
packages,
formData.deliveryTime,
selectedAncillary
selectedAncillary,
breakfastData
)
router.replace(`${returnUrl}&errorCode=AncillaryFailed`)
},

View File

@@ -12,7 +12,10 @@ import {
import { getAncillarySessionData } from "@/components/HotelReservation/MyStay/utils/ancillaries"
import LoadingSpinner from "@/components/LoadingSpinner"
import { trackEvent } from "@/utils/tracking/base"
import { buildAncillaries } from "@/utils/tracking/myStay"
import {
buildAncillariesTracking,
buildBreakfastTracking,
} from "@/utils/tracking/myStay"
interface TrackGuaranteeProps {
status: string
@@ -32,7 +35,8 @@ export default function TrackGuarantee({
useEffect(() => {
const trackAncillaryPaymentEvent = (event: string, status: string) => {
const sessionData = getAncillarySessionData()
const { formData, selectedAncillary, packages } = sessionData || {}
const { formData, selectedAncillary, packages, breakfastData } =
sessionData || {}
trackEvent({
event,
@@ -42,11 +46,13 @@ export default function TrackGuarantee({
lateArrivalGuarantee: "yes",
guaranteedProduct: "room + ancillary",
},
ancillaries: buildAncillaries(
packages ?? [],
selectedAncillary,
formData?.deliveryTime
),
ancillaries: breakfastData
? buildBreakfastTracking(breakfastData)
: buildAncillariesTracking(
packages ?? [],
selectedAncillary,
formData?.deliveryTime
),
})
}

View File

@@ -3,6 +3,7 @@ import type {
SelectedAncillary,
} from "@/types/components/myPages/myStay/ancillaries"
import type { AncillaryFormData } from "@/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/schema"
import type { BreakfastData } from "@/stores/my-stay/add-ancillary-flow"
export const generateDeliveryOptions = () => {
const timeSlots = ["16:00-17:00", "17:00-18:00", "18:00-19:00", "19:00-20:00"]
@@ -49,6 +50,7 @@ export const getAncillarySessionData = ():
comment: string | undefined
}[]
isBreakfast: boolean
breakfastData: BreakfastData | null
}
| undefined => {
if (typeof window === "undefined") return undefined
@@ -67,6 +69,7 @@ export function setAncillarySessionData({
selectedAncillary,
packages,
isBreakfast,
breakfastData,
}: {
formData?: AncillaryFormData
selectedAncillary?: Ancillary["ancillaryContent"][number] | null
@@ -76,6 +79,7 @@ export function setAncillarySessionData({
comment: string | undefined
}[]
isBreakfast: boolean
breakfastData: BreakfastData | null
}) {
if (typeof window === "undefined") return
try {
@@ -88,6 +92,7 @@ export function setAncillarySessionData({
selectedAncillary,
packages,
isBreakfast,
breakfastData,
})
)
} catch (error) {

View File

@@ -19,7 +19,7 @@ export default function RoomDetails({
}: RoomDetailsProps) {
const intl = useIntl()
const filteredSortedFacilities = roomFacilities
const filteredSortedFacilities = [...roomFacilities]
.sort((a, b) => a.sortOrder - b.sortOrder)
.map((facility) => {
const Icon = <FacilityIcon name={facility.icon} color="Icon/Default" />

View File

@@ -1,9 +1,12 @@
import { trackEvent } from "./base"
import type { SelectedAncillary } from "@/types/components/myPages/myStay/ancillaries"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
import { CurrencyEnum } from "@/types/enums/currency"
import type { Room } from "@/types/stores/my-stay"
import type { PackageSchema } from "@/types/trpc/routers/booking/confirmation"
import type { CreditCard } from "@/types/user"
import type { BreakfastData } from "@/stores/my-stay/add-ancillary-flow"
export function trackCancelStay(hotelId: string, bnr: string) {
trackEvent({
@@ -45,7 +48,7 @@ export function trackGlaSaveCardAttempt(
})
}
export function buildAncillaries(
export function buildAncillariesTracking(
packages: {
code: string
quantity: number
@@ -57,11 +60,14 @@ export function buildAncillaries(
return packages.map((pkg) => {
const payedWithCard = pkg.code === selectedAncillary?.id
const payedWithPoints = pkg.code === selectedAncillary?.loyaltyCode
const ancillaryDeliveryTime = selectedAncillary?.requiresDeliveryTime
? deliveryTime
: undefined
return {
productId: pkg.code,
productUnits: pkg.quantity,
productDeliveryTime: deliveryTime,
productDeliveryTime: ancillaryDeliveryTime,
productName: selectedAncillary?.title,
productCategory: selectedAncillary?.categoryName,
...(payedWithCard && {
@@ -83,7 +89,8 @@ export function trackGlaAncillaryAttempt(
comment: string | undefined
}[],
selectedAncillary: SelectedAncillary | null,
deliveryTime: string | undefined
deliveryTime: string | undefined,
breakfastData: BreakfastData | null
) {
trackEvent({
event: "GuaranteeAttemptAncillary",
@@ -91,7 +98,9 @@ export function trackGlaAncillaryAttempt(
status: "glacardsaveattempt",
type: savedCreditCard?.cardType,
},
ancillaries: buildAncillaries(packages, selectedAncillary, deliveryTime),
ancillaries: breakfastData
? buildBreakfastTracking(breakfastData, selectedAncillary?.hotelId)
: buildAncillariesTracking(packages, selectedAncillary, deliveryTime),
hotelInfo: {
hotelId: selectedAncillary?.hotelId,
lateArrivalGuarantee: "yes",
@@ -110,6 +119,7 @@ export function trackAncillarySuccess(
deliveryTime: string | null | undefined,
guaranteedProduct: string,
selectedAncillary: SelectedAncillary | null,
breakfastData: BreakfastData | null,
cardType?: string,
roomTypeCode?: string
) {
@@ -125,7 +135,9 @@ export function trackAncillarySuccess(
status: "glacardsaveconfirmed",
type: cardType,
},
ancillaries: buildAncillaries(packages, selectedAncillary, deliveryTime),
ancillaries: breakfastData
? buildBreakfastTracking(breakfastData, selectedAncillary?.hotelId)
: buildAncillariesTracking(packages, selectedAncillary, deliveryTime),
})
}
@@ -136,11 +148,14 @@ export function trackAncillaryFailed(
comment?: string
}[],
deliveryTime: string | null | undefined,
selectedAncillary: SelectedAncillary | null
selectedAncillary: SelectedAncillary | null,
breakfastData: BreakfastData | null
) {
trackEvent({
event: "GuaranteeFailAncillary",
ancillaries: buildAncillaries(packages, selectedAncillary, deliveryTime),
ancillaries: breakfastData
? buildBreakfastTracking(breakfastData, selectedAncillary?.hotelId)
: buildAncillariesTracking(packages, selectedAncillary, deliveryTime),
hotelInfo: {
hotelId: selectedAncillary?.hotelId,
lateArrivalGuarantee: "yes",
@@ -149,47 +164,129 @@ export function trackAncillaryFailed(
})
}
export function trackViewAncillary(ancillary: SelectedAncillary) {
export function buildBreakfastTracking(
breakfastData: BreakfastData,
hotelId?: number
) {
const items = []
if (breakfastData.nrOfAdults) {
items.push({
hotelId,
productId: BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST,
productName: "Breakfast",
productUnits: breakfastData.nrOfAdults * breakfastData.nrOfNights,
productPrice:
breakfastData.priceAdult *
breakfastData.nrOfAdults *
breakfastData.nrOfNights,
currency: breakfastData.currency,
productCategory: "Food",
})
}
if (breakfastData.nrOfPayingChildren) {
items.push({
hotelId,
productId: BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST,
productName: "Breakfast",
productUnits: breakfastData.nrOfPayingChildren * breakfastData.nrOfNights,
productPrice:
breakfastData.priceChild *
breakfastData.nrOfPayingChildren *
breakfastData.nrOfNights,
currency: breakfastData.currency,
productCategory: "Food",
})
}
return items
}
export function trackViewAncillary(
ancillary: SelectedAncillary,
booking: Room
) {
const { hotelId, id, title, categoryName } = ancillary
const isBreakfast = id === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
const hasPayingChildren = booking.childrenAges.some((age) => age >= 4)
const ancillaries = [
{
hotelId,
productId: id,
productName: title,
productCategory: categoryName,
},
]
if (isBreakfast && hasPayingChildren) {
ancillaries.push({
hotelId,
productId: BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST,
productName: title,
productCategory: categoryName,
})
}
trackEvent({
event: "viewAncillary",
ancillaries: [
{
hotelId: ancillary.hotelId,
productId: ancillary.id,
productName: ancillary.title,
productCategory: ancillary.categoryName,
},
],
ancillaries,
})
}
export function trackRemoveAncillary(
ancillary: PackageSchema,
hotelId: string,
deliveryTime?: string
deliveryTime?: string,
addedBreakfastPackages?: PackageSchema[]
) {
const isBreakfastPackage =
ancillary.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
const isPoints = ancillary.currency === CurrencyEnum.POINTS
const packagesWithoutFreeChildBreakfast = addedBreakfastPackages?.filter(
(p) => p.code !== BreakfastPackageEnum.FREE_CHILD_BREAKFAST
)
const ancillaries = isBreakfastPackage
? (packagesWithoutFreeChildBreakfast?.map((pkg) => ({
hotelId,
productId: pkg.code,
productPrice: pkg.totalPrice,
productUnits: pkg.totalUnit,
productType: pkg.type,
})) ?? [])
: [
{
hotelId,
productId: ancillary.code,
productPrice: isPoints ? 0 : ancillary.totalPrice,
productPoints: isPoints ? ancillary.totalPrice : 0,
productUnits: ancillary.totalUnit,
productType: ancillary.type,
productDeliveryTime: deliveryTime,
},
]
trackEvent({
event: "removeAncillary",
ancillaries: [
{
hotelId,
productId: ancillary.code,
productPrice: isPoints ? 0 : ancillary.totalPrice,
productPoints: isPoints ? ancillary.totalPrice : 0,
productUnits: ancillary.totalUnit,
productType: ancillary.type,
productDeliveryTime: deliveryTime,
},
],
ancillaries,
})
}
export function trackAddAncillary(
ancillary: SelectedAncillary | null,
quantityWithCard: number | null,
quantityWithPoints: number | null
quantityWithPoints: number | null,
breakfastData: BreakfastData | null
) {
if (breakfastData) {
return trackEvent({
event: "addAncillary",
ancillaries: buildBreakfastTracking(breakfastData, ancillary?.hotelId),
})
}
const ancillaries = []
if ((quantityWithCard ?? 0) > 0) {
ancillaries.push({