Merged in feature/SW-3516-pass-eurobonus-number-on-booking (pull request #2902)
* feat(SW-3516): Include partnerLoyaltyNumber on bookings - Added user context to BookingFlowProviders for user state management. - Updated booking input and output schemas to accommodate new user data. - Refactored booking mutation logic to include user-related information. - Improved type definitions for better TypeScript support across booking components. Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -1,23 +1,35 @@
|
||||
"use client"
|
||||
|
||||
import { createContext, useContext } from "react"
|
||||
import { createContext } from "react"
|
||||
|
||||
type BaseUser = {
|
||||
firstName: string | null
|
||||
lastName: string | null
|
||||
email: string
|
||||
}
|
||||
|
||||
export type BookingFlowUser =
|
||||
| (BaseUser & {
|
||||
type: "partner-sas"
|
||||
partnerLoyaltyNumber: `EB${string}`
|
||||
})
|
||||
| (BaseUser & {
|
||||
type: "scandic"
|
||||
/**
|
||||
* This will always be null for Scandic Friends members
|
||||
*/
|
||||
partnerLoyaltyNumber: null
|
||||
membershipNumber: string
|
||||
})
|
||||
|
||||
export type BookingFlowContextData = {
|
||||
isLoggedIn: boolean
|
||||
user:
|
||||
| { state: "loading"; data: undefined }
|
||||
| { state: "error"; data: undefined }
|
||||
| { state: "loaded"; data: BookingFlowUser | undefined }
|
||||
}
|
||||
|
||||
export const BookingFlowContext = createContext<
|
||||
BookingFlowContextData | undefined
|
||||
>(undefined)
|
||||
|
||||
export const useBookingFlowContext = (): BookingFlowContextData => {
|
||||
const context = useContext(BookingFlowContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useBookingFlowContext must be used within a BookingFlowContextProvider. Did you forget to use the provider in the consuming app?"
|
||||
)
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
|
||||
import { env } from "../../../../env/client"
|
||||
import { useAvailablePaymentOptions } from "../../../hooks/useAvailablePaymentOptions"
|
||||
import { useBookingFlowContext } from "../../../hooks/useBookingFlowContext"
|
||||
import { useHandleBookingStatus } from "../../../hooks/useHandleBookingStatus"
|
||||
import { useIsLoggedIn } from "../../../hooks/useIsLoggedIn"
|
||||
import useLang from "../../../hooks/useLang"
|
||||
@@ -59,6 +60,7 @@ import TermsAndConditions from "./TermsAndConditions"
|
||||
|
||||
import styles from "./payment.module.css"
|
||||
|
||||
import type { CreateBookingInput } from "@scandic-hotels/trpc/routers/booking/mutation/create/schema"
|
||||
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
||||
|
||||
import type { PriceChangeData } from "../PriceChangeData"
|
||||
@@ -83,6 +85,7 @@ export default function PaymentClient({
|
||||
const searchParams = useSearchParams()
|
||||
const isUserLoggedIn = useIsLoggedIn()
|
||||
const { getTopOffset } = useStickyPosition({})
|
||||
const { user } = useBookingFlowContext()
|
||||
|
||||
const [showBookingAlert, setShowBookingAlert] = useState(false)
|
||||
|
||||
@@ -397,39 +400,33 @@ export default function PaymentClient({
|
||||
}
|
||||
writePaymentInfoToSessionStorage(paymentMethodType, !!savedCreditCard)
|
||||
|
||||
const payload = {
|
||||
const payload: CreateBookingInput = {
|
||||
checkInDate: fromDate,
|
||||
checkOutDate: toDate,
|
||||
hotelId,
|
||||
language: lang,
|
||||
payment,
|
||||
rooms: rooms.map(({ room }, idx) => {
|
||||
const isMainRoom = idx === 0
|
||||
let rateCode = ""
|
||||
if (isMainRoom && isUserLoggedIn) {
|
||||
rateCode = booking.rooms[idx].rateCode
|
||||
} else if (
|
||||
(room.guest.join || room.guest.membershipNo) &&
|
||||
booking.rooms[idx].counterRateCode
|
||||
) {
|
||||
rateCode = booking.rooms[idx].counterRateCode
|
||||
} else {
|
||||
rateCode = booking.rooms[idx].rateCode
|
||||
}
|
||||
rooms: rooms.map(
|
||||
({ room }, idx): CreateBookingInput["rooms"][number] => {
|
||||
const isMainRoom = idx === 0
|
||||
let rateCode = ""
|
||||
if (isMainRoom && isUserLoggedIn) {
|
||||
rateCode = booking.rooms[idx].rateCode
|
||||
} else if (
|
||||
(room.guest.join || room.guest.membershipNo) &&
|
||||
booking.rooms[idx].counterRateCode
|
||||
) {
|
||||
rateCode = booking.rooms[idx].counterRateCode
|
||||
} else {
|
||||
rateCode = booking.rooms[idx].rateCode
|
||||
}
|
||||
|
||||
const phoneNumber = formatPhoneNumber(
|
||||
room.guest.phoneNumber,
|
||||
room.guest.phoneNumberCC
|
||||
)
|
||||
const phoneNumber = formatPhoneNumber(
|
||||
room.guest.phoneNumber,
|
||||
room.guest.phoneNumberCC
|
||||
)
|
||||
|
||||
return {
|
||||
adults: room.adults,
|
||||
bookingCode: room.roomRate.bookingCode,
|
||||
childrenAges: room.childrenInRoom?.map((child) => ({
|
||||
age: child.age,
|
||||
bedType: bedTypeMap[parseInt(child.bed.toString())],
|
||||
})),
|
||||
guest: {
|
||||
const guest: CreateBookingInput["rooms"][number]["guest"] = {
|
||||
becomeMember: room.guest.join,
|
||||
countryCode: room.guest.countryCode,
|
||||
email: room.guest.email,
|
||||
@@ -437,19 +434,24 @@ export default function PaymentClient({
|
||||
lastName: room.guest.lastName,
|
||||
membershipNumber: room.guest.membershipNo,
|
||||
phoneNumber,
|
||||
// Only allowed for room one
|
||||
...(idx === 0 && {
|
||||
dateOfBirth:
|
||||
"dateOfBirth" in room.guest && room.guest.dateOfBirth
|
||||
? room.guest.dateOfBirth
|
||||
: undefined,
|
||||
postalCode:
|
||||
"zipCode" in room.guest && room.guest.zipCode
|
||||
? room.guest.zipCode
|
||||
: undefined,
|
||||
}),
|
||||
},
|
||||
packages: {
|
||||
partnerLoyaltyNumber: null,
|
||||
}
|
||||
|
||||
if (isMainRoom) {
|
||||
// Only valid for main room
|
||||
guest.partnerLoyaltyNumber =
|
||||
user?.data?.partnerLoyaltyNumber || null
|
||||
guest.dateOfBirth =
|
||||
"dateOfBirth" in room.guest && room.guest.dateOfBirth
|
||||
? room.guest.dateOfBirth
|
||||
: undefined
|
||||
guest.postalCode =
|
||||
"zipCode" in room.guest && room.guest.zipCode
|
||||
? room.guest.zipCode
|
||||
: undefined
|
||||
}
|
||||
|
||||
const packages: CreateBookingInput["rooms"][number]["packages"] = {
|
||||
accessibility:
|
||||
room.roomFeatures?.some(
|
||||
(feature) =>
|
||||
@@ -464,47 +466,59 @@ export default function PaymentClient({
|
||||
room.roomFeatures?.some(
|
||||
(feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
|
||||
) ?? false,
|
||||
},
|
||||
rateCode,
|
||||
roomPrice: {
|
||||
memberPrice:
|
||||
"member" in room.roomRate
|
||||
? room.roomRate.member?.localPrice.pricePerStay
|
||||
}
|
||||
|
||||
return {
|
||||
adults: room.adults,
|
||||
bookingCode: room.roomRate.bookingCode,
|
||||
childrenAges: room.childrenInRoom?.map((child) => ({
|
||||
age: child.age,
|
||||
bedType: bedTypeMap[parseInt(child.bed.toString())],
|
||||
})),
|
||||
guest,
|
||||
packages,
|
||||
rateCode,
|
||||
roomPrice: {
|
||||
memberPrice:
|
||||
"member" in room.roomRate
|
||||
? room.roomRate.member?.localPrice.pricePerStay
|
||||
: undefined,
|
||||
publicPrice:
|
||||
"public" in room.roomRate
|
||||
? room.roomRate.public?.localPrice.pricePerStay
|
||||
: undefined,
|
||||
},
|
||||
roomTypeCode: room.bedType!.roomTypeCode, // A selection has been made in order to get to this step.
|
||||
smsConfirmationRequested: data.smsConfirmation,
|
||||
specialRequest: {
|
||||
comment: room.specialRequest.comment
|
||||
? room.specialRequest.comment
|
||||
: undefined,
|
||||
publicPrice:
|
||||
"public" in room.roomRate
|
||||
? room.roomRate.public?.localPrice.pricePerStay
|
||||
: undefined,
|
||||
},
|
||||
roomTypeCode: room.bedType!.roomTypeCode, // A selection has been made in order to get to this step.
|
||||
smsConfirmationRequested: data.smsConfirmation,
|
||||
specialRequest: {
|
||||
comment: room.specialRequest.comment
|
||||
? room.specialRequest.comment
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}),
|
||||
),
|
||||
}
|
||||
|
||||
initiateBooking.mutate(payload)
|
||||
},
|
||||
[
|
||||
setIsSubmitting,
|
||||
preSubmitCallbacks,
|
||||
rooms,
|
||||
getPaymentMethod,
|
||||
savedCreditCards,
|
||||
lang,
|
||||
initiateBooking,
|
||||
hotelId,
|
||||
bookingMustBeGuaranteed,
|
||||
hasOnlyFlexRates,
|
||||
fromDate,
|
||||
toDate,
|
||||
rooms,
|
||||
booking.rooms,
|
||||
getPaymentMethod,
|
||||
hasOnlyFlexRates,
|
||||
bookingMustBeGuaranteed,
|
||||
preSubmitCallbacks,
|
||||
isUserLoggedIn,
|
||||
hotelId,
|
||||
initiateBooking,
|
||||
getTopOffset,
|
||||
setIsSubmitting,
|
||||
isUserLoggedIn,
|
||||
booking.rooms,
|
||||
user?.data?.partnerLoyaltyNumber,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
18
packages/booking-flow/lib/hooks/useBookingFlowContext.ts
Normal file
18
packages/booking-flow/lib/hooks/useBookingFlowContext.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useContext } from "react"
|
||||
|
||||
import {
|
||||
BookingFlowContext,
|
||||
type BookingFlowContextData,
|
||||
} from "../bookingFlowContext"
|
||||
|
||||
export const useBookingFlowContext = (): BookingFlowContextData => {
|
||||
const context = useContext(BookingFlowContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useBookingFlowContext must be used within a BookingFlowContextProvider. Did you forget to use the provider in the consuming app?"
|
||||
)
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useBookingFlowContext } from "../bookingFlowContext"
|
||||
import { useBookingFlowContext } from "./useBookingFlowContext"
|
||||
|
||||
export function useIsLoggedIn() {
|
||||
const data = useBookingFlowContext()
|
||||
|
||||
Reference in New Issue
Block a user