Merged in feat/SW-1879-aa-tracking-bed-breakfastpayment (pull request #1789)

feat: SW-1879 Tracking enter-details sections

* feat: SW-1879 Tracking enter-details sections

* feat: SW-1879 removed onSelect to trigger in onSubmit

* feat: SW-1879 Removed onSelect and triggered inside onSubmit

* feat: SW-1879 Optimized to remove unnecessary useEffect triggers in every re-render

* feat: SW-1879 Updated breakfast package typings

* feat: SW-1879 Reverted RadioCardProps

* feat: SW-1879 Optimised code


Approved-by: Tobias Johansson
Approved-by: Christian Andolf
This commit is contained in:
Hrishikesh Vaipurkar
2025-04-16 14:06:15 +00:00
parent 4076f8d6d8
commit 8c0597727b
13 changed files with 127 additions and 27 deletions

View File

@@ -100,7 +100,7 @@ export default async function DetailsPage({
booking,
hotel,
rooms,
!!breakfastPackages?.length,
!!breakfastPackages.length,
searchParams.city,
!!user,
lang

View File

@@ -12,6 +12,7 @@ import {
import RadioCard from "@/components/TempDesignSystem/Form/RadioCard"
import { useRoomContext } from "@/contexts/Details/Room"
import { trackBedSelection } from "@/utils/tracking"
import { bedTypeFormSchema } from "./schema"
@@ -47,18 +48,17 @@ export default function BedType() {
roomTypeCode: matchingRoom.value,
}
updateBedType(bedType)
trackBedSelection(bedType.roomTypeCode)
}
},
[bedTypes, updateBedType]
)
const selectedBedType = methods.watch("bedType")
const handleSubmit = methods.handleSubmit
useEffect(() => {
if (methods.formState.isSubmitting) {
return
}
methods.watch(() => methods.handleSubmit(onSubmit)())
}, [methods, onSubmit])
handleSubmit(onSubmit)()
}, [selectedBedType, handleSubmit, onSubmit])
return (
<FormProvider {...methods}>

View File

@@ -14,6 +14,7 @@ import RadioCard from "@/components/TempDesignSystem/Form/RadioCard"
import Body from "@/components/TempDesignSystem/Text/Body"
import { useRoomContext } from "@/contexts/Details/Room"
import { formatPrice } from "@/utils/numberFormatting"
import { trackBreakfastSelection } from "@/utils/tracking"
import { breakfastFormSchema } from "./schema"
@@ -25,6 +26,7 @@ import { BreakfastPackageEnum } from "@/types/enums/breakfast"
export default function Breakfast() {
const intl = useIntl()
const packages = useEnterDetailsStore((state) => state.breakfastPackages)
const hotelId = useEnterDetailsStore((state) => state.booking.hotelId)
const {
actions: { updateBreakfast },
room,
@@ -49,22 +51,26 @@ export default function Breakfast() {
const onSubmit = useCallback(
(values: BreakfastFormSchema) => {
const pkg = packages?.find((p) => p.code === values.breakfast)
const pkg = packages.find((p) => p.code === values.breakfast)
if (pkg) {
updateBreakfast(pkg)
} else {
updateBreakfast(false)
}
trackBreakfastSelection({
breakfastPackage: pkg ?? packages[0],
hotelId,
units: pkg ? room.adults : 0,
})
},
[packages, updateBreakfast]
[packages, hotelId, room.adults, updateBreakfast]
)
const selectedBreakfast = methods.watch("breakfast")
const handleSubmit = methods.handleSubmit
useEffect(() => {
if (methods.formState.isSubmitting) {
return
}
methods.watch(() => methods.handleSubmit(onSubmit)())
}, [methods, onSubmit])
handleSubmit(onSubmit)()
}, [selectedBreakfast, handleSubmit, onSubmit])
return (
<FormProvider {...methods}>

View File

@@ -13,6 +13,7 @@ import { PaymentMethodEnum } from "@/constants/booking"
import Modal from "@/components/Modal"
import Divider from "@/components/TempDesignSystem/Divider"
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import { trackPaymentSectionOpen } from "@/utils/tracking/booking"
import MySavedCards from "../Payment/MySavedCards"
import PaymentOption from "../Payment/PaymentOption"
@@ -40,7 +41,16 @@ export default function ConfirmBooking({
<div className={styles.guaranteeContainer}>
<div className={styles.title}>
<div className={styles.checkbox}>
<Checkbox name="guarantee" />
<Checkbox
name="guarantee"
registerOptions={{
onChange: (e) => {
if (e.target.value) {
trackPaymentSectionOpen()
}
},
}}
/>
<Typography variant="Body/Paragraph/mdBold">
<p>
{intl.formatMessage({

View File

@@ -13,7 +13,9 @@ import Input from "@/components/TempDesignSystem/Form/Input"
import Phone from "@/components/TempDesignSystem/Form/Phone"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import { useRoomContext } from "@/contexts/Details/Room"
import { trackPaymentSectionOpen } from "@/utils/tracking/booking"
import { hasPrepaidRate } from "../../Payment/helpers"
import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
import { multiroomDetailsSchema } from "./schema"
@@ -25,10 +27,13 @@ const formID = "enter-details"
export default function Details() {
const intl = useIntl()
const { canProceedToPayment, lastRoom } = useEnterDetailsStore((state) => ({
canProceedToPayment: state.canProceedToPayment,
lastRoom: state.lastRoom,
}))
const { canProceedToPayment, lastRoom, rooms } = useEnterDetailsStore(
(state) => ({
canProceedToPayment: state.canProceedToPayment,
lastRoom: state.lastRoom,
rooms: state.rooms,
})
)
const {
actions: { updateDetails },
@@ -61,6 +66,8 @@ export default function Details() {
const guestIsGoingToJoin = methods.watch("join")
const guestIsMember = methods.watch("membershipNo")
const hasPrepaidRates = rooms.some(hasPrepaidRate)
return (
<FormProvider {...methods}>
<form
@@ -144,6 +151,11 @@ export default function Details() {
typography="Body/Paragraph/mdBold"
size="Medium"
type="submit"
onPress={
isPaymentNext && canProceedToPayment && hasPrepaidRates
? trackPaymentSectionOpen
: undefined
}
>
{isPaymentNext
? intl.formatMessage({

View File

@@ -14,6 +14,7 @@ import Input from "@/components/TempDesignSystem/Form/Input"
import Phone from "@/components/TempDesignSystem/Form/Phone"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import { useRoomContext } from "@/contexts/Details/Room"
import { trackPaymentSectionOpen } from "@/utils/tracking/booking"
import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
import MemberPriceModal from "./MemberPriceModal"
@@ -162,6 +163,11 @@ export default function Details({ user }: DetailsProps) {
typography="Body/Paragraph/mdBold"
size="Medium"
type="submit"
onPress={
isPaymentNext && canProceedToPayment && !room.isFlexRate
? trackPaymentSectionOpen
: undefined
}
>
{isPaymentNext
? intl.formatMessage({

View File

@@ -26,7 +26,7 @@ export default function Multiroom() {
}))
const showBreakfastStep =
!room.breakfastIncluded && !!breakfastPackages?.length
!room.breakfastIncluded && !!breakfastPackages.length
const arePreviousRoomsValid = rooms.slice(0, idx).every((r) => r.isComplete)

View File

@@ -40,7 +40,7 @@ export default function RoomOne({ user }: { user: SafeUser }) {
)
const showBreakfastStep =
!room.breakfastIncluded && !!breakfastPackages?.length
!room.breakfastIncluded && !!breakfastPackages.length
return (
<section>

View File

@@ -49,7 +49,7 @@ export function createDetailsStore(
initialState: InitialState,
searchParams: string,
user: SafeUser,
breakfastPackages: BreakfastPackages | null
breakfastPackages: BreakfastPackages
) {
const isMember = !!user
const isRedemption =
@@ -134,7 +134,7 @@ export function createDetailsStore(
},
}
if (room.breakfastIncluded || !breakfastPackages?.length) {
if (room.breakfastIncluded || !breakfastPackages.length) {
delete steps[StepEnum.breakfast]
}
@@ -318,7 +318,7 @@ export function createDetailsStore(
childrenInRoom: initialState.booking.rooms[idx].childrenInRoom,
bedType: room.bedType,
breakfast:
!breakfastPackages?.length || room.breakfastIncluded
!breakfastPackages.length || room.breakfastIncluded
? false
: undefined,
guest:

View File

@@ -5,7 +5,7 @@ import type { SelectRateSearchParams } from "../components/hotelReservation/sele
export interface DetailsProviderProps extends React.PropsWithChildren {
booking: SelectRateSearchParams
breakfastPackages: BreakfastPackages | null
breakfastPackages: BreakfastPackages
rooms: Room[]
searchParamsStr: string
user: SafeUser

View File

@@ -87,7 +87,7 @@ export interface DetailsState {
updateSeachParamString: (searchParamString: string) => void
}
booking: SelectRateSearchParams
breakfastPackages: BreakfastPackages | null
breakfastPackages: BreakfastPackages
canProceedToPayment: boolean
isSubmittingDisabled: boolean
isSummaryOpen: boolean

View File

@@ -1,5 +1,6 @@
import { trackEvent } from "./base"
import type { BreakfastPackages } from "@/types/components/hotelReservation/breakfast"
import type { LowestRoomPriceEvent } from "@/types/components/tracking"
export function trackLowestRoomPrice(event: LowestRoomPriceEvent) {
@@ -16,3 +17,64 @@ export function trackLowestRoomPrice(event: LowestRoomPriceEvent) {
},
})
}
// Tracking for sections of booking flow enter-details page
export function trackBedSelection(bedType: string) {
trackEvent({
event: "bedSelection",
selection: {
name: "bed options selection click",
bedType: bedType,
},
pageInfo: {
pageName: "hotelreservation|bed",
pageType: "bookingbedtypepage",
},
})
}
export function trackBreakfastSelection({
breakfastPackage,
hotelId,
units,
}: {
breakfastPackage: BreakfastPackages[number]
hotelId: string
units: number
}) {
trackEvent({
event: "breakfastSelection",
selection: {
name: "breakfast options selection click",
},
ancillaries: [
{
hotelId: hotelId,
productCategory: "",
productId: breakfastPackage.code,
productUnits: units,
productPrice: breakfastPackage.localPrice.price,
productPoints: 0,
productType: "food",
productName: breakfastPackage.packageType,
},
],
pageInfo: {
pageName: "hotelreservation|breakfast",
pageType: "bookingbreakfastpage",
},
})
}
export function trackPaymentSectionOpen() {
trackEvent({
event: "paymentSectionOpen",
selection: {
name: "payment section open",
},
pageInfo: {
pageName: "hotelreservation|payment",
pageType: "bookingpaymentpage",
},
})
}

View File

@@ -1,5 +1,9 @@
export { trackClick } from "./base"
export { trackLowestRoomPrice } from "./booking"
export {
trackBedSelection,
trackBreakfastSelection,
trackLowestRoomPrice,
} from "./booking"
export { trackAccordionClick, trackOpenSidePeekEvent } from "./componentEvents"
export { trackHotelMapClick, trackHotelTabClick } from "./hotelPage"
export { trackCancelStay, trackMyStayPageLink } from "./myStay"