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:
@@ -100,7 +100,7 @@ export default async function DetailsPage({
|
|||||||
booking,
|
booking,
|
||||||
hotel,
|
hotel,
|
||||||
rooms,
|
rooms,
|
||||||
!!breakfastPackages?.length,
|
!!breakfastPackages.length,
|
||||||
searchParams.city,
|
searchParams.city,
|
||||||
!!user,
|
!!user,
|
||||||
lang
|
lang
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
|
|
||||||
import RadioCard from "@/components/TempDesignSystem/Form/RadioCard"
|
import RadioCard from "@/components/TempDesignSystem/Form/RadioCard"
|
||||||
import { useRoomContext } from "@/contexts/Details/Room"
|
import { useRoomContext } from "@/contexts/Details/Room"
|
||||||
|
import { trackBedSelection } from "@/utils/tracking"
|
||||||
|
|
||||||
import { bedTypeFormSchema } from "./schema"
|
import { bedTypeFormSchema } from "./schema"
|
||||||
|
|
||||||
@@ -47,18 +48,17 @@ export default function BedType() {
|
|||||||
roomTypeCode: matchingRoom.value,
|
roomTypeCode: matchingRoom.value,
|
||||||
}
|
}
|
||||||
updateBedType(bedType)
|
updateBedType(bedType)
|
||||||
|
trackBedSelection(bedType.roomTypeCode)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[bedTypes, updateBedType]
|
[bedTypes, updateBedType]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const selectedBedType = methods.watch("bedType")
|
||||||
|
const handleSubmit = methods.handleSubmit
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (methods.formState.isSubmitting) {
|
handleSubmit(onSubmit)()
|
||||||
return
|
}, [selectedBedType, handleSubmit, onSubmit])
|
||||||
}
|
|
||||||
|
|
||||||
methods.watch(() => methods.handleSubmit(onSubmit)())
|
|
||||||
}, [methods, onSubmit])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import RadioCard from "@/components/TempDesignSystem/Form/RadioCard"
|
|||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
import { useRoomContext } from "@/contexts/Details/Room"
|
import { useRoomContext } from "@/contexts/Details/Room"
|
||||||
import { formatPrice } from "@/utils/numberFormatting"
|
import { formatPrice } from "@/utils/numberFormatting"
|
||||||
|
import { trackBreakfastSelection } from "@/utils/tracking"
|
||||||
|
|
||||||
import { breakfastFormSchema } from "./schema"
|
import { breakfastFormSchema } from "./schema"
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
|||||||
export default function Breakfast() {
|
export default function Breakfast() {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const packages = useEnterDetailsStore((state) => state.breakfastPackages)
|
const packages = useEnterDetailsStore((state) => state.breakfastPackages)
|
||||||
|
const hotelId = useEnterDetailsStore((state) => state.booking.hotelId)
|
||||||
const {
|
const {
|
||||||
actions: { updateBreakfast },
|
actions: { updateBreakfast },
|
||||||
room,
|
room,
|
||||||
@@ -49,22 +51,26 @@ export default function Breakfast() {
|
|||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
(values: BreakfastFormSchema) => {
|
(values: BreakfastFormSchema) => {
|
||||||
const pkg = packages?.find((p) => p.code === values.breakfast)
|
const pkg = packages.find((p) => p.code === values.breakfast)
|
||||||
if (pkg) {
|
if (pkg) {
|
||||||
updateBreakfast(pkg)
|
updateBreakfast(pkg)
|
||||||
} else {
|
} else {
|
||||||
updateBreakfast(false)
|
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(() => {
|
useEffect(() => {
|
||||||
if (methods.formState.isSubmitting) {
|
handleSubmit(onSubmit)()
|
||||||
return
|
}, [selectedBreakfast, handleSubmit, onSubmit])
|
||||||
}
|
|
||||||
methods.watch(() => methods.handleSubmit(onSubmit)())
|
|
||||||
}, [methods, onSubmit])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { PaymentMethodEnum } from "@/constants/booking"
|
|||||||
import Modal from "@/components/Modal"
|
import Modal from "@/components/Modal"
|
||||||
import Divider from "@/components/TempDesignSystem/Divider"
|
import Divider from "@/components/TempDesignSystem/Divider"
|
||||||
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
|
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
|
||||||
|
import { trackPaymentSectionOpen } from "@/utils/tracking/booking"
|
||||||
|
|
||||||
import MySavedCards from "../Payment/MySavedCards"
|
import MySavedCards from "../Payment/MySavedCards"
|
||||||
import PaymentOption from "../Payment/PaymentOption"
|
import PaymentOption from "../Payment/PaymentOption"
|
||||||
@@ -40,7 +41,16 @@ export default function ConfirmBooking({
|
|||||||
<div className={styles.guaranteeContainer}>
|
<div className={styles.guaranteeContainer}>
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
<div className={styles.checkbox}>
|
<div className={styles.checkbox}>
|
||||||
<Checkbox name="guarantee" />
|
<Checkbox
|
||||||
|
name="guarantee"
|
||||||
|
registerOptions={{
|
||||||
|
onChange: (e) => {
|
||||||
|
if (e.target.value) {
|
||||||
|
trackPaymentSectionOpen()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Typography variant="Body/Paragraph/mdBold">
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
<p>
|
<p>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import Input from "@/components/TempDesignSystem/Form/Input"
|
|||||||
import Phone from "@/components/TempDesignSystem/Form/Phone"
|
import Phone from "@/components/TempDesignSystem/Form/Phone"
|
||||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||||
import { useRoomContext } from "@/contexts/Details/Room"
|
import { useRoomContext } from "@/contexts/Details/Room"
|
||||||
|
import { trackPaymentSectionOpen } from "@/utils/tracking/booking"
|
||||||
|
|
||||||
|
import { hasPrepaidRate } from "../../Payment/helpers"
|
||||||
import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
|
import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
|
||||||
import { multiroomDetailsSchema } from "./schema"
|
import { multiroomDetailsSchema } from "./schema"
|
||||||
|
|
||||||
@@ -25,10 +27,13 @@ const formID = "enter-details"
|
|||||||
export default function Details() {
|
export default function Details() {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
const { canProceedToPayment, lastRoom } = useEnterDetailsStore((state) => ({
|
const { canProceedToPayment, lastRoom, rooms } = useEnterDetailsStore(
|
||||||
canProceedToPayment: state.canProceedToPayment,
|
(state) => ({
|
||||||
lastRoom: state.lastRoom,
|
canProceedToPayment: state.canProceedToPayment,
|
||||||
}))
|
lastRoom: state.lastRoom,
|
||||||
|
rooms: state.rooms,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
actions: { updateDetails },
|
actions: { updateDetails },
|
||||||
@@ -61,6 +66,8 @@ export default function Details() {
|
|||||||
const guestIsGoingToJoin = methods.watch("join")
|
const guestIsGoingToJoin = methods.watch("join")
|
||||||
const guestIsMember = methods.watch("membershipNo")
|
const guestIsMember = methods.watch("membershipNo")
|
||||||
|
|
||||||
|
const hasPrepaidRates = rooms.some(hasPrepaidRate)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<form
|
<form
|
||||||
@@ -144,6 +151,11 @@ export default function Details() {
|
|||||||
typography="Body/Paragraph/mdBold"
|
typography="Body/Paragraph/mdBold"
|
||||||
size="Medium"
|
size="Medium"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
onPress={
|
||||||
|
isPaymentNext && canProceedToPayment && hasPrepaidRates
|
||||||
|
? trackPaymentSectionOpen
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{isPaymentNext
|
{isPaymentNext
|
||||||
? intl.formatMessage({
|
? intl.formatMessage({
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import Input from "@/components/TempDesignSystem/Form/Input"
|
|||||||
import Phone from "@/components/TempDesignSystem/Form/Phone"
|
import Phone from "@/components/TempDesignSystem/Form/Phone"
|
||||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||||
import { useRoomContext } from "@/contexts/Details/Room"
|
import { useRoomContext } from "@/contexts/Details/Room"
|
||||||
|
import { trackPaymentSectionOpen } from "@/utils/tracking/booking"
|
||||||
|
|
||||||
import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
|
import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
|
||||||
import MemberPriceModal from "./MemberPriceModal"
|
import MemberPriceModal from "./MemberPriceModal"
|
||||||
@@ -162,6 +163,11 @@ export default function Details({ user }: DetailsProps) {
|
|||||||
typography="Body/Paragraph/mdBold"
|
typography="Body/Paragraph/mdBold"
|
||||||
size="Medium"
|
size="Medium"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
onPress={
|
||||||
|
isPaymentNext && canProceedToPayment && !room.isFlexRate
|
||||||
|
? trackPaymentSectionOpen
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{isPaymentNext
|
{isPaymentNext
|
||||||
? intl.formatMessage({
|
? intl.formatMessage({
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function Multiroom() {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const showBreakfastStep =
|
const showBreakfastStep =
|
||||||
!room.breakfastIncluded && !!breakfastPackages?.length
|
!room.breakfastIncluded && !!breakfastPackages.length
|
||||||
|
|
||||||
const arePreviousRoomsValid = rooms.slice(0, idx).every((r) => r.isComplete)
|
const arePreviousRoomsValid = rooms.slice(0, idx).every((r) => r.isComplete)
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default function RoomOne({ user }: { user: SafeUser }) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const showBreakfastStep =
|
const showBreakfastStep =
|
||||||
!room.breakfastIncluded && !!breakfastPackages?.length
|
!room.breakfastIncluded && !!breakfastPackages.length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export function createDetailsStore(
|
|||||||
initialState: InitialState,
|
initialState: InitialState,
|
||||||
searchParams: string,
|
searchParams: string,
|
||||||
user: SafeUser,
|
user: SafeUser,
|
||||||
breakfastPackages: BreakfastPackages | null
|
breakfastPackages: BreakfastPackages
|
||||||
) {
|
) {
|
||||||
const isMember = !!user
|
const isMember = !!user
|
||||||
const isRedemption =
|
const isRedemption =
|
||||||
@@ -134,7 +134,7 @@ export function createDetailsStore(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (room.breakfastIncluded || !breakfastPackages?.length) {
|
if (room.breakfastIncluded || !breakfastPackages.length) {
|
||||||
delete steps[StepEnum.breakfast]
|
delete steps[StepEnum.breakfast]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +318,7 @@ export function createDetailsStore(
|
|||||||
childrenInRoom: initialState.booking.rooms[idx].childrenInRoom,
|
childrenInRoom: initialState.booking.rooms[idx].childrenInRoom,
|
||||||
bedType: room.bedType,
|
bedType: room.bedType,
|
||||||
breakfast:
|
breakfast:
|
||||||
!breakfastPackages?.length || room.breakfastIncluded
|
!breakfastPackages.length || room.breakfastIncluded
|
||||||
? false
|
? false
|
||||||
: undefined,
|
: undefined,
|
||||||
guest:
|
guest:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { SelectRateSearchParams } from "../components/hotelReservation/sele
|
|||||||
|
|
||||||
export interface DetailsProviderProps extends React.PropsWithChildren {
|
export interface DetailsProviderProps extends React.PropsWithChildren {
|
||||||
booking: SelectRateSearchParams
|
booking: SelectRateSearchParams
|
||||||
breakfastPackages: BreakfastPackages | null
|
breakfastPackages: BreakfastPackages
|
||||||
rooms: Room[]
|
rooms: Room[]
|
||||||
searchParamsStr: string
|
searchParamsStr: string
|
||||||
user: SafeUser
|
user: SafeUser
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export interface DetailsState {
|
|||||||
updateSeachParamString: (searchParamString: string) => void
|
updateSeachParamString: (searchParamString: string) => void
|
||||||
}
|
}
|
||||||
booking: SelectRateSearchParams
|
booking: SelectRateSearchParams
|
||||||
breakfastPackages: BreakfastPackages | null
|
breakfastPackages: BreakfastPackages
|
||||||
canProceedToPayment: boolean
|
canProceedToPayment: boolean
|
||||||
isSubmittingDisabled: boolean
|
isSubmittingDisabled: boolean
|
||||||
isSummaryOpen: boolean
|
isSummaryOpen: boolean
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { trackEvent } from "./base"
|
import { trackEvent } from "./base"
|
||||||
|
|
||||||
|
import type { BreakfastPackages } from "@/types/components/hotelReservation/breakfast"
|
||||||
import type { LowestRoomPriceEvent } from "@/types/components/tracking"
|
import type { LowestRoomPriceEvent } from "@/types/components/tracking"
|
||||||
|
|
||||||
export function trackLowestRoomPrice(event: LowestRoomPriceEvent) {
|
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",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
export { trackClick } from "./base"
|
export { trackClick } from "./base"
|
||||||
export { trackLowestRoomPrice } from "./booking"
|
export {
|
||||||
|
trackBedSelection,
|
||||||
|
trackBreakfastSelection,
|
||||||
|
trackLowestRoomPrice,
|
||||||
|
} from "./booking"
|
||||||
export { trackAccordionClick, trackOpenSidePeekEvent } from "./componentEvents"
|
export { trackAccordionClick, trackOpenSidePeekEvent } from "./componentEvents"
|
||||||
export { trackHotelMapClick, trackHotelTabClick } from "./hotelPage"
|
export { trackHotelMapClick, trackHotelTabClick } from "./hotelPage"
|
||||||
export { trackCancelStay, trackMyStayPageLink } from "./myStay"
|
export { trackCancelStay, trackMyStayPageLink } from "./myStay"
|
||||||
|
|||||||
Reference in New Issue
Block a user