feat(SW-1717): rewrite select-rate to show all variants of rate-cards

This commit is contained in:
Simon Emanuelsson
2025-03-25 11:25:44 +01:00
committed by Michael Zetterberg
parent adde77eaa9
commit ebaea78fb3
118 changed files with 4601 additions and 4374 deletions

View File

@@ -80,156 +80,187 @@ export function subtract(...nums: (number | string | undefined)[]) {
}
export function getCurrency(roomRate: RoomRate) {
const requestedCurrency = <CurrencyEnum>(
(roomRate.publicRate?.requestedPrice?.currency ??
(roomRate.chequeRate && CurrencyEnum.CC))
)
const localCurrency = <CurrencyEnum>(
(roomRate.publicRate?.localPrice.currency ??
(roomRate.voucherRate && CurrencyEnum.Voucher) ??
(roomRate.chequeRate && CurrencyEnum.CC))
)
if ("corporateCheque" in roomRate) {
return {
localCurrency: CurrencyEnum.CC,
requestedCurrency: CurrencyEnum.CC,
}
} else if ("redemption" in roomRate) {
return {
localCurrency: CurrencyEnum.POINTS,
requestedCurrency: CurrencyEnum.POINTS,
}
} else if ("voucher" in roomRate) {
return {
localCurrency: CurrencyEnum.Voucher,
requestedCurrency: CurrencyEnum.Voucher,
}
} else if ("public" in roomRate && roomRate.public) {
return {
localCurrency: roomRate.public.localPrice.currency,
requestedCurrency: roomRate.public.requestedPrice?.currency,
}
} else if ("member" in roomRate && roomRate.member) {
return {
localCurrency: roomRate.member.localPrice.currency,
requestedCurrency: roomRate.member.requestedPrice?.currency,
}
}
return {
requestedCurrency,
localCurrency,
localCurrency: CurrencyEnum.Unknown,
requestedCurrency: CurrencyEnum.Unknown,
}
}
export function getRoomPrice(roomRate: RoomRate, isMember: boolean) {
if (isMember && roomRate.memberRate) {
if (isMember && "member" in roomRate && roomRate.member) {
return {
perNight: {
requested: roomRate.memberRate.requestedPrice && {
currency: roomRate.memberRate.requestedPrice.currency,
price: roomRate.memberRate.requestedPrice.pricePerNight,
},
requested: roomRate.member.requestedPrice
? {
currency: roomRate.member.requestedPrice.currency,
price: roomRate.member.requestedPrice.pricePerNight,
}
: undefined,
local: {
currency: roomRate.memberRate.localPrice.currency,
price: roomRate.memberRate.localPrice.pricePerNight,
currency: roomRate.member.localPrice.currency,
price: roomRate.member.localPrice.pricePerNight,
},
},
perStay: {
requested: roomRate.memberRate.requestedPrice && {
currency: roomRate.memberRate.requestedPrice.currency,
price: roomRate.memberRate.requestedPrice.pricePerStay,
},
requested: roomRate.member.requestedPrice
? {
currency: roomRate.member.requestedPrice.currency,
price: roomRate.member.requestedPrice.pricePerStay,
}
: undefined,
local: {
currency: roomRate.memberRate.localPrice.currency,
price: roomRate.memberRate.localPrice.pricePerStay,
currency: roomRate.member.localPrice.currency,
price: roomRate.member.localPrice.pricePerStay,
},
},
}
}
if (roomRate.publicRate) {
if ("public" in roomRate && roomRate.public) {
return {
perNight: {
requested: roomRate.publicRate.requestedPrice && {
currency: roomRate.publicRate.requestedPrice.currency,
price: roomRate.publicRate.requestedPrice.pricePerNight,
},
requested: roomRate.public.requestedPrice
? {
currency: roomRate.public.requestedPrice.currency,
price: roomRate.public.requestedPrice.pricePerNight,
}
: undefined,
local: {
currency: roomRate.publicRate.localPrice.currency,
price: roomRate.publicRate.localPrice.pricePerNight,
regularPrice: roomRate.publicRate.localPrice.regularPricePerNight,
currency: roomRate.public.localPrice.currency,
price: roomRate.public.localPrice.pricePerNight,
regularPrice: roomRate.public.localPrice.regularPricePerNight,
},
},
perStay: {
requested: roomRate.publicRate.requestedPrice && {
currency: roomRate.publicRate.requestedPrice.currency,
price: roomRate.publicRate.requestedPrice.pricePerStay,
},
requested: roomRate.public.requestedPrice
? {
currency: roomRate.public.requestedPrice.currency,
price: roomRate.public.requestedPrice.pricePerStay,
}
: undefined,
local: {
currency: roomRate.publicRate.localPrice.currency,
price: roomRate.publicRate.localPrice.pricePerStay,
regularPrice: roomRate.publicRate.localPrice.regularPricePerStay,
currency: roomRate.public.localPrice.currency,
price: roomRate.public.localPrice.pricePerStay,
regularPrice: roomRate.public.localPrice.regularPricePerStay,
},
},
}
}
if (roomRate.chequeRate) {
if ("corporateCheque" in roomRate) {
return {
perNight: {
requested: roomRate.chequeRate.requestedPrice && {
currency: CurrencyEnum.CC,
price: roomRate.chequeRate.requestedPrice.numberOfBonusCheques,
additionalPrice:
roomRate.chequeRate.requestedPrice.additionalPricePerStay,
additionalPriceCurrency: roomRate.chequeRate.requestedPrice.currency,
},
requested: roomRate.corporateCheque.requestedPrice
? {
currency: CurrencyEnum.CC,
price: roomRate.corporateCheque.requestedPrice.numberOfCheques,
additionalPrice:
roomRate.corporateCheque.requestedPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.corporateCheque.requestedPrice.currency ?? undefined,
}
: undefined,
local: {
currency: CurrencyEnum.CC,
price: roomRate.chequeRate.localPrice.numberOfBonusCheques,
price: roomRate.corporateCheque.localPrice.numberOfCheques,
additionalPrice:
roomRate.chequeRate.localPrice.additionalPricePerStay,
additionalPriceCurrency: roomRate.chequeRate.localPrice.currency,
roomRate.corporateCheque.localPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.corporateCheque.localPrice.currency ?? undefined,
},
},
perStay: {
requested: roomRate.chequeRate.requestedPrice && {
currency: CurrencyEnum.CC,
price: roomRate.chequeRate.requestedPrice.numberOfBonusCheques,
additionalPrice:
roomRate.chequeRate.requestedPrice.additionalPricePerStay,
additionalPriceCurrency: roomRate.chequeRate.requestedPrice.currency,
},
requested: roomRate.corporateCheque.requestedPrice
? {
currency: CurrencyEnum.CC,
price: roomRate.corporateCheque.requestedPrice.numberOfCheques,
additionalPrice:
roomRate.corporateCheque.requestedPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.corporateCheque.requestedPrice.currency ?? undefined,
}
: undefined,
local: {
currency: CurrencyEnum.CC,
price: roomRate.chequeRate.localPrice.numberOfBonusCheques,
price: roomRate.corporateCheque.localPrice.numberOfCheques,
additionalPrice:
roomRate.chequeRate.localPrice.additionalPricePerStay,
additionalPriceCurrency: roomRate.chequeRate.localPrice.currency,
roomRate.corporateCheque.localPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.corporateCheque.localPrice.currency ?? undefined,
},
},
}
}
if (roomRate.voucherRate) {
if ("voucher" in roomRate) {
return {
perNight: {
requested: undefined,
local: {
currency: CurrencyEnum.Voucher,
price: roomRate.voucherRate.numberOfVouchers,
price: roomRate.voucher.numberOfVouchers,
},
},
perStay: {
requested: undefined,
local: {
currency: CurrencyEnum.Voucher,
price: roomRate.voucherRate.numberOfVouchers,
price: roomRate.voucher.numberOfVouchers,
},
},
}
}
if (roomRate.redemptionRate) {
if ("redemption" in roomRate) {
return {
// ToDo Handle perNight as undefined
perNight: {
requested: undefined,
local: {
currency:
roomRate.redemptionRate.localPrice.currency ?? CurrencyEnum.POINTS,
price: roomRate.redemptionRate.localPrice.pointsPerStay,
currency: CurrencyEnum.POINTS,
price: roomRate.redemption.localPrice.pointsPerStay,
additionalPrice:
roomRate.redemptionRate.localPrice.additionalPricePerStay,
roomRate.redemption.localPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.redemptionRate.localPrice.additionalPriceCurrency,
roomRate.redemption.localPrice.currency ?? undefined,
},
},
perStay: {
requested: undefined,
local: {
currency:
roomRate.redemptionRate.localPrice.currency ?? CurrencyEnum.POINTS,
price: roomRate.redemptionRate.localPrice.pointsPerStay,
currency: CurrencyEnum.POINTS,
price: roomRate.redemption.localPrice.pointsPerStay,
additionalPrice:
roomRate.redemptionRate.localPrice.additionalPricePerStay,
roomRate.redemption.localPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.redemptionRate.localPrice.additionalPriceCurrency,
roomRate.redemption.localPrice.currency ?? undefined,
},
},
}
@@ -243,58 +274,64 @@ export function getRoomPrice(roomRate: RoomRate, isMember: boolean) {
export function getTotalPrice(roomRates: RoomRate[], isMember: boolean) {
return roomRates.reduce<Price>(
(total, roomRate, idx) => {
const isFirstRoom = idx === 0
const rate =
isFirstRoom && isMember && roomRate.memberRate
? roomRate.memberRate
: roomRate.publicRate
const isMainRoom = idx === 0
let rate
if (isMainRoom && isMember && "member" in roomRate && roomRate.member) {
rate = roomRate.member
} else if ("public" in roomRate && roomRate.public) {
rate = roomRate.public
}
// TODO: Handle other products?
if (!rate) {
return total
}
return {
requested: rate.requestedPrice
? {
currency: rate.requestedPrice.currency,
price: add(
total.requested?.price ?? 0,
rate.requestedPrice.pricePerStay
),
}
: total.requested,
local: {
currency: rate.localPrice.currency,
price: add(total.local.price ?? 0, rate.localPrice.pricePerStay),
regularPrice: rate.localPrice.regularPricePerStay
? add(total.local.regularPrice, rate.localPrice.regularPricePerStay)
: total.local.regularPrice,
},
total.local.currency = rate.localPrice.currency
total.local.price = add(total.local.price, rate.localPrice.pricePerStay)
if (rate.localPrice.regularPricePerStay) {
total.local.regularPrice = add(
total.local.regularPrice,
rate.localPrice.regularPricePerStay
)
}
if (rate.requestedPrice) {
if (total.requested) {
total.requested.price = add(
total.requested.price,
rate.requestedPrice.pricePerStay
)
} else {
total.requested = {
currency: rate.requestedPrice.currency,
price: rate.requestedPrice.pricePerStay,
}
}
}
return total
},
{
requested: undefined,
local: {
currency: (roomRates[0].publicRate?.localPrice.currency ||
roomRates[0].memberRate?.localPrice.currency)!,
currency: CurrencyEnum.Unknown,
price: 0,
},
requested: undefined,
}
)
}
export const calculateVoucherPrice = (roomRates: RoomRate[]) => {
export function calculateVoucherPrice(roomRates: RoomRate[]) {
return roomRates.reduce<Price>(
(total, room) => {
const rate = room.voucherRate
if (!rate) {
if (!("voucher" in room)) {
return total
}
return <Price>{
return {
local: {
currency: total.local.currency,
price: total.local.price + rate.numberOfVouchers,
price: total.local.price + room.voucher.numberOfVouchers,
},
requested: undefined,
}
@@ -309,50 +346,56 @@ export const calculateVoucherPrice = (roomRates: RoomRate[]) => {
)
}
export const calculateChequePrice = (roomRates: RoomRate[]) => {
export function calculateCorporateChequePrice(roomRates: RoomRate[]) {
return roomRates.reduce<Price>(
(total, room) => {
const rate = room.chequeRate
if (!rate) {
if (!("corporateCheque" in room)) {
return total
}
const price = total.local.price + rate.localPrice.numberOfBonusCheques
const rate = room.corporateCheque
const additionalPrice =
rate.localPrice.numberOfBonusCheques &&
(total.local.additionalPrice ?? 0) +
(rate.localPrice.additionalPricePerStay ?? 0)
const additionalPriceCurrency = (rate.localPrice.numberOfBonusCheques &&
rate.localPrice.currency)!
total.local.price = add(
total.local.price,
rate.localPrice.numberOfCheques
)
const requestedPrice = rate.requestedPrice?.numberOfBonusCheques
? (total.requested?.price ?? 0) +
rate.requestedPrice?.numberOfBonusCheques
: total.requested?.price
const requestedAdditionalPrice =
rate.requestedPrice?.additionalPricePerStay &&
(total.requested?.additionalPrice ??
0 + rate.requestedPrice?.additionalPricePerStay ??
0)
return <Price>{
local: {
currency: CurrencyEnum.CC,
price,
additionalPrice,
additionalPriceCurrency,
},
requested: rate.requestedPrice
? {
currency: CurrencyEnum.CC,
price: requestedPrice,
additionalPrice: requestedAdditionalPrice,
additionalPriceCurrency: rate.requestedPrice?.currency,
}
: undefined,
if (rate.localPrice.additionalPricePerStay) {
total.local.additionalPrice = add(
total.local.additionalPrice,
rate.localPrice.additionalPricePerStay
)
}
if (rate.localPrice.currency) {
total.local.additionalPriceCurrency = rate.localPrice.currency
}
if (rate.requestedPrice) {
if (total.requested) {
total.requested.price = add(
total.requested.price,
rate.requestedPrice.numberOfCheques
)
} else {
total.requested = {
currency: CurrencyEnum.CC,
price: rate.requestedPrice.numberOfCheques,
}
}
if (rate.requestedPrice.additionalPricePerStay) {
total.requested.additionalPrice = add(
total.requested.additionalPrice,
rate.requestedPrice.additionalPricePerStay
)
}
if (rate.requestedPrice.currency) {
total.requested.additionalPriceCurrency = rate.requestedPrice.currency
}
}
return total
},
{
local: {
@@ -437,8 +480,10 @@ export function calcTotalPrice(
breakfastLocalPrice * room.adults * nights,
roomFeaturesTotal?.local ?? 0
),
additionalPriceCurrency:
roomPrice.perStay.local.additionalPriceCurrency,
additionalPriceCurrency: roomPrice.perStay.local
.additionalPriceCurrency
? roomPrice.perStay.local.additionalPriceCurrency
: undefined,
},
}

View File

@@ -11,7 +11,7 @@ import { DetailsContext } from "@/contexts/Details"
import {
add,
calcTotalPrice,
calculateChequePrice,
calculateCorporateChequePrice,
calculateVoucherPrice,
checkRoomProgress,
extractGuestFromUser,
@@ -23,9 +23,8 @@ import {
} from "./helpers"
import type { BreakfastPackages } from "@/types/components/hotelReservation/breakfast"
import {
PointsPriceSchema,
type Price} from "@/types/components/hotelReservation/price";
import type { Price } from "@/types/components/hotelReservation/price"
import { CurrencyEnum } from "@/types/enums/currency"
import { StepEnum } from "@/types/enums/step"
import type {
DetailsState,
@@ -58,20 +57,36 @@ export function createDetailsStore(
const isRedemption =
new URLSearchParams(searchParams).get("searchtype") === REDEMPTION
const isVoucher = initialState.rooms.some((room) => room.roomRate.voucherRate)
const isCorpChq = initialState.rooms.some((room) => room.roomRate.chequeRate)
const isVoucher = initialState.rooms.some(
(room) => "voucher" in room.roomRate
)
const isCorpChq = initialState.rooms.some(
(room) => "corporateCheque" in room.roomRate
)
let initialTotalPrice: Price
if (isRedemption && initialState.rooms[0].roomRate.redemptionRate) {
initialTotalPrice = PointsPriceSchema.parse(
initialState.rooms[0].roomRate.redemptionRate
)
const roomOneRoomRate = initialState.rooms[0].roomRate
if (isRedemption && "redemption" in roomOneRoomRate) {
initialTotalPrice = {
local: {
currency: CurrencyEnum.POINTS,
price: roomOneRoomRate.redemption.localPrice.pointsPerStay,
},
}
if (roomOneRoomRate.redemption.localPrice.currency) {
initialTotalPrice.local.additionalPriceCurrency =
roomOneRoomRate.redemption.localPrice.currency
}
if (roomOneRoomRate.redemption.localPrice.additionalPricePerStay) {
initialTotalPrice.local.additionalPrice =
roomOneRoomRate.redemption.localPrice.additionalPricePerStay
}
} else if (isVoucher) {
initialTotalPrice = calculateVoucherPrice(
initialState.rooms.map((r) => r.roomRate)
)
} else if (isCorpChq) {
initialTotalPrice = calculateChequePrice(
initialTotalPrice = calculateCorporateChequePrice(
initialState.rooms.map((r) => r.roomRate)
)
} else {
@@ -98,56 +113,6 @@ export function createDetailsStore(
}
})
const rooms: RoomState[] = initialState.rooms.map((room, idx) => {
const steps: RoomState["steps"] = {
[StepEnum.selectBed]: {
step: StepEnum.selectBed,
isValid: !!room.bedType,
},
[StepEnum.breakfast]: {
step: StepEnum.breakfast,
isValid: false,
},
[StepEnum.details]: {
step: StepEnum.details,
isValid: false,
},
}
if (room.breakfastIncluded || !breakfastPackages?.length) {
delete steps[StepEnum.breakfast]
}
const currentStep =
Object.values(steps).find((step) => !step.isValid)?.step ??
StepEnum.selectBed
return {
room: {
...room,
adults: initialState.booking.rooms[idx].adults,
childrenInRoom: initialState.booking.rooms[idx].childrenInRoom,
bedType: room.bedType,
breakfast:
!breakfastPackages?.length || room.breakfastIncluded
? false
: undefined,
guest:
isMember && idx === 0
? deepmerge(defaultGuestState, extractGuestFromUser(user))
: defaultGuestState,
roomPrice: getRoomPrice(room.roomRate, isMember && idx === 0),
specialRequest: {
comment: "",
},
},
currentStep,
isComplete: false,
steps,
}
})
return create<DetailsState>()((set) => ({
activeRoom: 0,
booking: initialState.booking,
@@ -156,74 +121,386 @@ export function createDetailsStore(
isSubmittingDisabled: false,
isSummaryOpen: false,
lastRoom: initialState.booking.rooms.length - 1,
rooms,
rooms: initialState.rooms.map((room, idx) => {
const steps: RoomState["steps"] = {
[StepEnum.selectBed]: {
step: StepEnum.selectBed,
isValid: !!room.bedType,
},
[StepEnum.breakfast]: {
step: StepEnum.breakfast,
isValid: false,
},
[StepEnum.details]: {
step: StepEnum.details,
isValid: false,
},
}
if (room.breakfastIncluded || !breakfastPackages?.length) {
delete steps[StepEnum.breakfast]
}
const currentStep =
Object.values(steps).find((step) => !step.isValid)?.step ??
StepEnum.selectBed
return {
actions: {
setStep(step) {
return set(
produce((state: DetailsState) => {
const isSameRoom = idx === state.activeRoom
const room = state.rooms[idx]
if (isSameRoom) {
// Closed same accordion as was open
if (step === room.currentStep) {
if (room.isComplete) {
// Room is complete, move to next room or payment
const nextRoomIdx = state.rooms.findIndex(
(r) => !r.isComplete
)
state.activeRoom = nextRoomIdx
// Done, proceed to payment
if (nextRoomIdx === -1) {
room.currentStep = null
} else {
const nextRoom = state.rooms[nextRoomIdx]
const nextInvalidStep = findNextInvalidStep(nextRoom)
nextRoom.currentStep = nextInvalidStep
}
} else {
room.currentStep = findNextInvalidStep(room)
}
} else {
if (room.steps[step]?.isValid) {
room.currentStep = step
} else {
room.currentStep = findNextInvalidStep(room)
}
}
} else {
const arePreviousRoomsCompleted = state.rooms
.slice(0, idx)
.every((room) => room.isComplete)
if (arePreviousRoomsCompleted) {
state.activeRoom = idx
if (room.steps[step]?.isValid) {
room.currentStep = step
} else {
room.currentStep = findNextInvalidStep(room)
}
} else {
const firstIncompleteRoom = state.rooms.findIndex(
(r) => !r.isComplete
)
state.activeRoom = firstIncompleteRoom
if (firstIncompleteRoom === -1) {
// All rooms are done, proceed to payment
room.currentStep = null
} else {
const nextRoom = state.rooms[firstIncompleteRoom]
nextRoom.currentStep = findNextInvalidStep(nextRoom)
}
}
}
})
)
},
updateBedType(bedType) {
return set(
produce((state: DetailsState) => {
state.rooms[idx].steps[StepEnum.selectBed].isValid = true
state.rooms[idx].room.bedType = bedType
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
handleStepProgression(state.rooms[idx], state)
writeToSessionStorage({
activeRoom: state.activeRoom,
booking: state.booking,
rooms: state.rooms,
})
})
)
},
updateBreakfast(breakfast) {
return set(
produce((state: DetailsState) => {
const currentRoom = state.rooms[idx]
if (currentRoom.steps[StepEnum.breakfast]) {
currentRoom.steps[StepEnum.breakfast].isValid = true
}
const currentTotalPriceRequested = state.totalPrice.requested
let stateTotalRequestedPrice = 0
if (currentTotalPriceRequested) {
stateTotalRequestedPrice =
currentTotalPriceRequested.price ?? 0
}
const stateTotalLocalPrice = state.totalPrice.local.price
const stateTotalLocalRegularPrice =
state.totalPrice.local.regularPrice
const addToTotalPrice =
(currentRoom.room.breakfast === undefined ||
currentRoom.room.breakfast === false) &&
!!breakfast
const subtractFromTotalPrice =
currentRoom.room.breakfast && breakfast === false
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
if (addToTotalPrice) {
const breakfastTotalRequestedPrice =
breakfast.requestedPrice.price *
currentRoom.room.adults *
nights
const breakfastTotalPrice =
breakfast.localPrice.price *
currentRoom.room.adults *
nights
state.totalPrice = {
requested: state.totalPrice.requested && {
currency: state.totalPrice.requested.currency,
price:
stateTotalRequestedPrice + breakfastTotalRequestedPrice,
},
local: {
currency: breakfast.localPrice.currency,
price: stateTotalLocalPrice ?? 0 + breakfastTotalPrice,
regularPrice: stateTotalLocalRegularPrice
? stateTotalLocalRegularPrice + breakfastTotalPrice
: undefined,
},
}
}
if (subtractFromTotalPrice) {
let currency = state.totalPrice.local.currency
let currentBreakfastTotalPrice = 0
let currentBreakfastTotalRequestedPrice = 0
if (currentRoom.room.breakfast) {
currentBreakfastTotalPrice =
currentRoom.room.breakfast.localPrice.price *
currentRoom.room.adults *
nights
currentBreakfastTotalRequestedPrice =
currentRoom.room.breakfast.requestedPrice.totalPrice *
currentRoom.room.adults *
nights
currency = currentRoom.room.breakfast.localPrice.currency
}
let requestedPrice =
stateTotalRequestedPrice -
currentBreakfastTotalRequestedPrice
if (requestedPrice < 0) {
requestedPrice = 0
}
let localPrice =
stateTotalLocalPrice - currentBreakfastTotalPrice
if (localPrice < 0) {
localPrice = 0
}
let regularPrice = stateTotalLocalRegularPrice
? stateTotalLocalRegularPrice - currentBreakfastTotalPrice
: undefined
state.totalPrice = {
requested: state.totalPrice.requested && {
currency: state.totalPrice.requested.currency,
price: requestedPrice,
},
local: {
currency,
price: localPrice,
regularPrice,
},
}
}
currentRoom.room.breakfast = breakfast
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
handleStepProgression(currentRoom, state)
writeToSessionStorage({
activeRoom: state.activeRoom,
booking: state.booking,
rooms: state.rooms,
})
})
)
},
updateDetails(data) {
return set(
produce((state: DetailsState) => {
state.rooms[idx].steps[StepEnum.details].isValid = true
const currentRoom = state.rooms[idx].room
currentRoom.guest.countryCode = data.countryCode
currentRoom.guest.email = data.email
currentRoom.guest.firstName = data.firstName
currentRoom.guest.join = data.join
currentRoom.guest.lastName = data.lastName
if (data.specialRequest?.comment) {
currentRoom.specialRequest.comment =
data.specialRequest.comment
}
if (data.join) {
currentRoom.guest.membershipNo = undefined
} else {
currentRoom.guest.membershipNo = data.membershipNo
}
currentRoom.guest.phoneNumber = data.phoneNumber
// Only valid for room 1
if (idx === 0 && data.join && !isMember) {
if ("dateOfBirth" in currentRoom.guest) {
currentRoom.guest.dateOfBirth = data.dateOfBirth
}
if ("zipCode" in currentRoom.guest) {
currentRoom.guest.zipCode = data.zipCode
}
}
currentRoom.roomPrice = getRoomPrice(
currentRoom.roomRate,
Boolean(data.join || data.membershipNo || isMember)
)
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = calcTotalPrice(
state.rooms,
state.totalPrice.local.currency,
isMember,
nights
)
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
handleStepProgression(state.rooms[idx], state)
writeToSessionStorage({
activeRoom: state.activeRoom,
booking: state.booking,
rooms: state.rooms,
})
})
)
},
updateMultiroomDetails(data) {
return set(
produce((state: DetailsState) => {
state.rooms[idx].steps[StepEnum.details].isValid = true
state.rooms[idx].room.guest.countryCode = data.countryCode
state.rooms[idx].room.guest.email = data.email
state.rooms[idx].room.guest.firstName = data.firstName
state.rooms[idx].room.guest.join = data.join
state.rooms[idx].room.guest.lastName = data.lastName
if (data.join) {
state.rooms[idx].room.guest.membershipNo = undefined
} else {
state.rooms[idx].room.guest.membershipNo = data.membershipNo
}
state.rooms[idx].room.guest.phoneNumber = data.phoneNumber
const getMemberPrice = Boolean(data.join || data.membershipNo)
state.rooms[idx].room.roomPrice = getRoomPrice(
state.rooms[idx].room.roomRate,
getMemberPrice
)
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = calcTotalPrice(
state.rooms,
state.totalPrice.local.currency,
getMemberPrice,
nights
)
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
handleStepProgression(state.rooms[idx], state)
writeToSessionStorage({
activeRoom: state.activeRoom,
booking: state.booking,
rooms: state.rooms,
})
})
)
},
},
room: {
...room,
adults: initialState.booking.rooms[idx].adults,
childrenInRoom: initialState.booking.rooms[idx].childrenInRoom,
bedType: room.bedType,
breakfast:
!breakfastPackages?.length || room.breakfastIncluded
? false
: undefined,
guest:
isMember && idx === 0
? deepmerge(defaultGuestState, extractGuestFromUser(user))
: defaultGuestState,
roomPrice: getRoomPrice(room.roomRate, isMember && idx === 0),
specialRequest: {
comment: "",
},
},
currentStep,
isComplete: false,
steps,
}
}),
searchParamString: searchParams,
totalPrice: initialTotalPrice,
vat: initialState.vat,
actions: {
setStep(idx) {
return function (step) {
return set(
produce((state: DetailsState) => {
const isSameRoom = idx === state.activeRoom
const room = state.rooms[idx]
if (isSameRoom) {
// Closed same accordion as was open
if (step === room.currentStep) {
if (room.isComplete) {
// Room is complete, move to next room or payment
const nextRoomIdx = state.rooms.findIndex(
(r) => !r.isComplete
)
state.activeRoom = nextRoomIdx
// Done, proceed to payment
if (nextRoomIdx === -1) {
room.currentStep = null
} else {
const nextRoom = state.rooms[nextRoomIdx]
const nextInvalidStep = findNextInvalidStep(nextRoom)
nextRoom.currentStep = nextInvalidStep
}
} else {
room.currentStep = findNextInvalidStep(room)
}
} else {
if (room.steps[step]?.isValid) {
room.currentStep = step
} else {
room.currentStep = findNextInvalidStep(room)
}
}
} else {
const arePreviousRoomsCompleted = state.rooms
.slice(0, idx)
.every((room) => room.isComplete)
if (arePreviousRoomsCompleted) {
state.activeRoom = idx
if (room.steps[step]?.isValid) {
room.currentStep = step
} else {
room.currentStep = findNextInvalidStep(room)
}
} else {
const firstIncompleteRoom = state.rooms.findIndex(
(r) => !r.isComplete
)
state.activeRoom = firstIncompleteRoom
if (firstIncompleteRoom === -1) {
// All rooms are done, proceed to payment
room.currentStep = null
} else {
const nextRoom = state.rooms[firstIncompleteRoom]
nextRoom.currentStep = findNextInvalidStep(nextRoom)
}
}
}
})
)
}
},
setIsSubmittingDisabled(isSubmittingDisabled) {
return set(
produce((state: DetailsState) => {
@@ -246,273 +523,6 @@ export function createDetailsStore(
})
)
},
updateBedType(idx) {
return function (bedType) {
return set(
produce((state: DetailsState) => {
state.rooms[idx].steps[StepEnum.selectBed].isValid = true
state.rooms[idx].room.bedType = bedType
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
handleStepProgression(state.rooms[idx], state)
writeToSessionStorage({
activeRoom: state.activeRoom,
booking: state.booking,
rooms: state.rooms,
})
})
)
}
},
updateBreakfast(idx) {
return function (breakfast) {
return set(
produce((state: DetailsState) => {
const currentRoom = state.rooms[idx]
if (currentRoom.steps[StepEnum.breakfast]) {
currentRoom.steps[StepEnum.breakfast].isValid = true
}
const currentTotalPriceRequested = state.totalPrice.requested
let stateTotalRequestedPrice = 0
if (currentTotalPriceRequested) {
stateTotalRequestedPrice = currentTotalPriceRequested.price ?? 0
}
const stateTotalLocalPrice = state.totalPrice.local.price
const stateTotalLocalRegularPrice =
state.totalPrice.local.regularPrice
const addToTotalPrice =
(currentRoom.room.breakfast === undefined ||
currentRoom.room.breakfast === false) &&
!!breakfast
const subtractFromTotalPrice =
currentRoom.room.breakfast && breakfast === false
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
if (addToTotalPrice) {
const breakfastTotalRequestedPrice =
breakfast.requestedPrice.price *
currentRoom.room.adults *
nights
const breakfastTotalPrice =
breakfast.localPrice.price * currentRoom.room.adults * nights
state.totalPrice = {
requested: state.totalPrice.requested && {
currency: state.totalPrice.requested.currency,
price:
stateTotalRequestedPrice + breakfastTotalRequestedPrice,
},
local: {
currency: breakfast.localPrice.currency,
price: stateTotalLocalPrice ?? 0 + breakfastTotalPrice,
regularPrice: stateTotalLocalRegularPrice
? stateTotalLocalRegularPrice + breakfastTotalPrice
: undefined,
},
}
}
if (subtractFromTotalPrice) {
let currency = state.totalPrice.local.currency
let currentBreakfastTotalPrice = 0
let currentBreakfastTotalRequestedPrice = 0
if (currentRoom.room.breakfast) {
currentBreakfastTotalPrice =
currentRoom.room.breakfast.localPrice.price *
currentRoom.room.adults *
nights
currentBreakfastTotalRequestedPrice =
currentRoom.room.breakfast.requestedPrice.totalPrice *
currentRoom.room.adults *
nights
currency = currentRoom.room.breakfast.localPrice.currency
}
let requestedPrice =
stateTotalRequestedPrice - currentBreakfastTotalRequestedPrice
if (requestedPrice < 0) {
requestedPrice = 0
}
let localPrice =
stateTotalLocalPrice - currentBreakfastTotalPrice
if (localPrice < 0) {
localPrice = 0
}
let regularPrice = stateTotalLocalRegularPrice
? stateTotalLocalRegularPrice - currentBreakfastTotalPrice
: undefined
state.totalPrice = {
requested: state.totalPrice.requested && {
currency: state.totalPrice.requested.currency,
price: requestedPrice,
},
local: {
currency,
price: localPrice,
regularPrice,
},
}
}
currentRoom.room.breakfast = breakfast
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
handleStepProgression(currentRoom, state)
writeToSessionStorage({
activeRoom: state.activeRoom,
booking: state.booking,
rooms: state.rooms,
})
})
)
}
},
updateDetails(idx) {
return function (data) {
return set(
produce((state: DetailsState) => {
state.rooms[idx].steps[StepEnum.details].isValid = true
const currentRoom = state.rooms[idx].room
currentRoom.guest.countryCode = data.countryCode
currentRoom.guest.email = data.email
currentRoom.guest.firstName = data.firstName
currentRoom.guest.join = data.join
currentRoom.guest.lastName = data.lastName
if (data.specialRequest?.comment) {
currentRoom.specialRequest.comment = data.specialRequest.comment
}
if (data.join) {
currentRoom.guest.membershipNo = undefined
} else {
currentRoom.guest.membershipNo = data.membershipNo
}
currentRoom.guest.phoneNumber = data.phoneNumber
// Only valid for room 1
if (idx === 0 && data.join && !isMember) {
if ("dateOfBirth" in currentRoom.guest) {
currentRoom.guest.dateOfBirth = data.dateOfBirth
}
if ("zipCode" in currentRoom.guest) {
currentRoom.guest.zipCode = data.zipCode
}
}
currentRoom.roomPrice = getRoomPrice(
currentRoom.roomRate,
Boolean(data.join || data.membershipNo || isMember)
)
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = calcTotalPrice(
state.rooms,
state.totalPrice.local.currency,
isMember,
nights
)
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
handleStepProgression(state.rooms[idx], state)
writeToSessionStorage({
activeRoom: state.activeRoom,
booking: state.booking,
rooms: state.rooms,
})
})
)
}
},
updateMultiroomDetails(idx) {
return function (data) {
return set(
produce((state: DetailsState) => {
state.rooms[idx].steps[StepEnum.details].isValid = true
state.rooms[idx].room.guest.countryCode = data.countryCode
state.rooms[idx].room.guest.email = data.email
state.rooms[idx].room.guest.firstName = data.firstName
state.rooms[idx].room.guest.join = data.join
state.rooms[idx].room.guest.lastName = data.lastName
if (data.join) {
state.rooms[idx].room.guest.membershipNo = undefined
} else {
state.rooms[idx].room.guest.membershipNo = data.membershipNo
}
state.rooms[idx].room.guest.phoneNumber = data.phoneNumber
const getMemberPrice = Boolean(data.join || data.membershipNo)
state.rooms[idx].room.roomPrice = getRoomPrice(
state.rooms[idx].room.roomRate,
getMemberPrice
)
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = calcTotalPrice(
state.rooms,
state.totalPrice.local.currency,
getMemberPrice,
nights
)
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
handleStepProgression(state.rooms[idx], state)
writeToSessionStorage({
activeRoom: state.activeRoom,
booking: state.booking,
rooms: state.rooms,
})
})
)
}
},
updateSeachParamString(searchParamString) {
return set(
produce((state: DetailsState) => {

View File

@@ -0,0 +1,80 @@
import type { AvailabilityError } from "@/types/stores/rates"
import type {
Product,
RoomConfiguration,
} from "@/types/trpc/routers/hotel/roomAvailability"
export function findProduct(rateCode: string, product: Product) {
if ("corporateCheque" in product) {
return product.corporateCheque.rateCode === rateCode
}
if ("redemption" in product) {
return product.redemption.rateCode === rateCode
}
if ("voucher" in product) {
return product.voucher.rateCode === rateCode
}
if ("public" in product && product.public) {
return product.public.rateCode === rateCode
}
if ("member" in product && product.member) {
return product.member.rateCode === rateCode
}
return null
}
export function findProductInRoom(rateCode: string, room: RoomConfiguration) {
if (room.campaign.length) {
const campaignProduct = room.campaign.find((product) =>
findProduct(rateCode, product)
)
if (campaignProduct) {
return campaignProduct
}
}
if (room.code.length) {
const codeProduct = room.code.find((product) =>
findProduct(rateCode, product)
)
if (codeProduct) {
return codeProduct
}
}
if (room.redemptions.length) {
const redemptionProduct = room.redemptions.find((product) =>
findProduct(rateCode, product)
)
if (redemptionProduct) {
return redemptionProduct
}
}
if (room.regular.length) {
const regularProduct = room.regular.find((product) =>
findProduct(rateCode, product)
)
if (regularProduct) {
return regularProduct
}
}
}
export function findSelectedRate(
rateCode: string,
roomTypeCode: string,
rooms: RoomConfiguration[] | AvailabilityError
) {
if (!Array.isArray(rooms)) {
return null
}
return rooms.find((room) => {
if (room.roomTypeCode !== roomTypeCode) {
return false
}
return findProductInRoom(rateCode, room)
})
}

View File

@@ -5,35 +5,13 @@ import { create, useStore } from "zustand"
import { RatesContext } from "@/contexts/Rates"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { RateTypeEnum } from "@/types/enums/rateType"
import type {
AvailabilityError,
InitialState,
RatesState,
} from "@/types/stores/rates"
import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
import { findProductInRoom, findSelectedRate } from "./helpers"
function findSelectedRate(
rateCode: string,
roomTypeCode: string,
rooms: RoomConfiguration[] | AvailabilityError
) {
if (!Array.isArray(rooms)) {
return null
}
return rooms.find(
(room) =>
room.roomTypeCode === roomTypeCode &&
room.products.find(
(product) =>
product.public?.rateCode === rateCode ||
product.member?.rateCode === rateCode ||
product.bonusCheque?.rateCode === rateCode ||
product.voucher?.rateCode === rateCode
)
)
}
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter"
import { RateTypeEnum } from "@/types/enums/rateType"
import type { InitialState, RatesState } from "@/types/stores/rates"
import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
export function createRatesStore({
booking,
@@ -84,60 +62,31 @@ export function createRatesStore({
}
const rateSummary: RatesState["rateSummary"] = []
booking.rooms.forEach((room, idx) => {
for (const [idx, room] of booking.rooms.entries()) {
if (room.rateCode && room.roomTypeCode) {
const roomConfiguration = roomConfigurations?.[idx]
const selectedRoom = roomConfiguration.find(
(rc) =>
rc.roomTypeCode === room.roomTypeCode &&
rc.products.find(
(product) =>
product.public?.rateCode === room.rateCode ||
product.member?.rateCode === room.rateCode ||
product.redemptions?.find(
(redemption) => redemption?.rateCode === room.rateCode
) ||
product.bonusCheque?.rateCode === room.rateCode ||
product.voucher?.rateCode === room.rateCode
)
const selectedRoom = findSelectedRate(
room.rateCode,
room.roomTypeCode,
roomConfiguration
)
const redemptionProduct = selectedRoom?.products[0].redemptions?.find(
(r) => r?.rateCode === room.rateCode
)
const product = selectedRoom?.products.find(
(p) =>
p.public?.rateCode === room.rateCode ||
p.member?.rateCode === room.rateCode ||
p.bonusCheque?.rateCode === room.rateCode ||
p.voucher?.rateCode === room.rateCode
)
if (selectedRoom && product) {
if (!selectedRoom) {
continue
}
const product = findProductInRoom(room.rateCode, selectedRoom)
if (product) {
rateSummary[idx] = {
features: selectedRoom.features,
redemption: undefined,
product,
rate: product.rate,
roomType: selectedRoom.roomType,
roomTypeCode: selectedRoom.roomTypeCode,
}
if (product.member || product.public) {
rateSummary[idx].member = product.member
rateSummary[idx].public = product.public
} else if (product.bonusCheque) {
rateSummary[idx].bonusCheque = product.bonusCheque
} else if (product.voucher) {
rateSummary[idx].voucher = product.voucher
}
} else if (selectedRoom && redemptionProduct) {
rateSummary[idx] = {
features: selectedRoom.features,
redemption: redemptionProduct,
rate: selectedRoom?.products[0].rate,
roomType: selectedRoom.roomType,
roomTypeCode: selectedRoom.roomTypeCode,
}
}
}
})
}
let activeRoom = rateSummary.length
if (searchParams.has("modifyRateIndex")) {
@@ -148,320 +97,264 @@ export function createRatesStore({
activeRoom = -1
}
return create<RatesState>()((set) => ({
actions: {
closeSection(idx) {
return function () {
return set(
produce((state: RatesState) => {
if (state.rateSummary.length === state.booking.rooms.length) {
state.activeRoom = -1
} else {
state.activeRoom = idx + 1
}
})
return create<RatesState>()((set) => {
return {
activeRoom,
booking,
filterOptions,
hotelType,
isUserLoggedIn,
packages,
pathname,
petRoomPackage: packages.find(
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
),
rateSummary,
roomConfigurations,
rooms: booking.rooms.map((room, idx) => {
const roomConfiguration = roomConfigurations[idx]
const selectedRate =
findSelectedRate(
room.rateCode,
room.roomTypeCode,
roomConfiguration
) ?? null
let product = null
if (selectedRate) {
product = findProductInRoom(room.rateCode, selectedRate)
}
const selectedPackage = room.packages?.[0]
let rooms: RoomConfiguration[] = roomConfiguration
if (selectedPackage) {
rooms = roomConfiguration.filter((r) =>
r.features.find((f) => f.code === selectedPackage)
)
}
},
modifyRate(idx) {
return function () {
return set(
produce((state: RatesState) => {
state.activeRoom = idx
})
)
}
},
selectFilter(idx) {
return function (code) {
return set(
produce((state: RatesState) => {
state.rooms[idx].selectedPackage = code
const roomConfiguration = state.roomConfigurations[idx]
if (roomConfiguration) {
const searchParams = new URLSearchParams(state.searchParams)
if (code) {
state.rooms[idx].rooms = roomConfiguration.filter((room) =>
room.features.find((feat) => feat.code === code)
return {
actions: {
appendRegularRates(roomConfigurations) {
return set(
produce((state: RatesState) => {
const rooms = state.rooms[idx].rooms
const updatedRooms = rooms.map((currentRoom) => {
const incomingRoom = roomConfigurations.find(
(room) =>
room.roomType === currentRoom.roomType &&
room.roomTypeCode === currentRoom.roomTypeCode
)
if (incomingRoom) {
return {
...currentRoom,
campaign: [
...currentRoom.campaign,
...incomingRoom.campaign,
],
products: [
...currentRoom.products,
...incomingRoom.products,
],
regular: incomingRoom.regular,
}
}
return currentRoom
})
state.rooms[idx].rooms = updatedRooms
})
)
},
closeSection() {
return set(
produce((state: RatesState) => {
if (state.rateSummary.length === state.booking.rooms.length) {
state.activeRoom = -1
} else {
state.activeRoom = idx + 1
}
})
)
},
modifyRate() {
return set(
produce((state: RatesState) => {
state.activeRoom = idx
})
)
},
selectFilter(filter) {
return set(
produce((state: RatesState) => {
state.rooms[idx].selectedFilter = filter
})
)
},
selectPackage(code) {
return set(
produce((state: RatesState) => {
state.rooms[idx].selectedPackage = code
const roomConfiguration = state.roomConfigurations[idx]
if (roomConfiguration) {
const searchParams = new URLSearchParams(state.searchParams)
if (code) {
state.rooms[idx].rooms = roomConfiguration.filter(
(room) =>
room.features.find((feat) => feat.code === code)
)
searchParams.set(`room[${idx}].packages`, code)
if (state.rateSummary[idx]) {
state.rateSummary[idx].package = code
}
} else {
state.rooms[idx].rooms = roomConfiguration
searchParams.delete(`room[${idx}].packages`)
if (state.rateSummary[idx]) {
state.rateSummary[idx].package = undefined
}
}
state.searchParams = new ReadonlyURLSearchParams(
searchParams
)
window.history.pushState(
{},
"",
`${state.pathname}?${searchParams}`
)
}
})
)
},
selectRate(selectedRate) {
return set(
produce((state: RatesState) => {
if (!selectedRate.product) {
return
}
state.rooms[idx].selectedRate = selectedRate
state.rateSummary[idx] = {
features: selectedRate.features,
package: state.rooms[idx].selectedPackage,
product: selectedRate.product,
rate: selectedRate.product.rate,
roomType: selectedRate.roomType,
roomTypeCode: selectedRate.roomTypeCode,
}
const roomNr = idx + 1
const isMainRoom = roomNr === 1
let productRateCode = ""
if ("corporateCheque" in selectedRate.product) {
productRateCode =
selectedRate.product.corporateCheque.rateCode
}
if ("redemption" in selectedRate.product) {
productRateCode = selectedRate.product.redemption.rateCode
}
if ("voucher" in selectedRate.product) {
productRateCode = selectedRate.product.voucher.rateCode
}
let isRegularRate = false
if (
"public" in selectedRate.product &&
selectedRate.product.public
) {
isRegularRate =
selectedRate.product.public.rateType ===
RateTypeEnum.Regular
productRateCode = selectedRate.product.public.rateCode
}
let hasMemberRate = false
let memberRateCode = ""
if (
"member" in selectedRate.product &&
selectedRate.product.member
) {
hasMemberRate = true
memberRateCode = selectedRate.product.member.rateCode
}
const isMemberRate =
isUserLoggedIn &&
isMainRoom &&
hasMemberRate &&
isRegularRate
const searchParams = new URLSearchParams(state.searchParams)
const counterratecode = isMemberRate
? productRateCode
: memberRateCode
if (counterratecode) {
searchParams.set(
`room[${idx}].counterratecode`,
counterratecode
)
}
const rateCode = isMemberRate
? memberRateCode
: productRateCode
if (rateCode) {
searchParams.set(`room[${idx}].ratecode`, rateCode)
}
searchParams.set(
`room[${idx}].roomtype`,
selectedRate.roomTypeCode
)
searchParams.set(`room[${idx}].packages`, code)
if (state.rateSummary[idx]) {
state.rateSummary[idx].package = code
if (state.rateSummary.length === state.booking.rooms.length) {
state.activeRoom = -1
} else {
state.activeRoom = idx + 1
}
} else {
state.rooms[idx].rooms = roomConfiguration
searchParams.delete(`room[${idx}].packages`)
if (state.rateSummary[idx]) {
state.rateSummary[idx].package = undefined
}
state.searchParams = new ReadonlyURLSearchParams(searchParams)
window.history.pushState(
{},
"",
`${state.pathname}?${searchParams}`
)
})
)
},
},
bookingRoom: room,
rooms,
selectedFilter: booking.bookingCode
? BookingCodeFilterEnum.Discounted
: BookingCodeFilterEnum.All,
selectedPackage,
selectedRate:
selectedRate && product
? {
features: selectedRate.features,
product,
roomType: selectedRate.roomType,
roomTypeCode: selectedRate.roomTypeCode,
}
state.searchParams = new ReadonlyURLSearchParams(searchParams)
window.history.pushState(
{},
"",
`${state.pathname}?${searchParams}`
)
}
})
)
: null,
}
},
selectRate(idx) {
return function (selectedRate) {
return set(
produce((state: RatesState) => {
const memberRate = selectedRate.product.member
const publicRate = selectedRate.product.public
if (!memberRate && !publicRate) {
return
}
state.rooms[idx].selectedRate = selectedRate
state.rateSummary[idx] = {
features: selectedRate.features,
member: selectedRate.product.member,
package: state.rooms[idx].selectedPackage,
rate: selectedRate.product.rate,
public: selectedRate.product.public,
redemption: undefined,
roomType: selectedRate.roomType,
roomTypeCode: selectedRate.roomTypeCode,
}
const isBookingCodeRate =
selectedRate.product.public?.rateType !== RateTypeEnum.Regular
const roomNr = idx + 1
const isMainRoom = roomNr + 1
const isMemberRate =
isUserLoggedIn && isMainRoom && memberRate && !isBookingCodeRate
const searchParams = new URLSearchParams(state.searchParams)
const counterratecode = isMemberRate
? (publicRate?.rateCode ?? "")
: (memberRate?.rateCode ?? "")
if (counterratecode) {
searchParams.set(
`room[${idx}].counterratecode`,
counterratecode
)
}
const rateCode = isMemberRate
? memberRate.rateCode
: (publicRate?.rateCode ?? "")
if (rateCode) {
searchParams.set(`room[${idx}].ratecode`, rateCode)
}
searchParams.set(
`room[${idx}].roomtype`,
selectedRate.roomTypeCode
)
if (state.rateSummary.length === state.booking.rooms.length) {
state.activeRoom = -1
} else {
state.activeRoom = idx + 1
}
state.searchParams = new ReadonlyURLSearchParams(searchParams)
window.history.pushState(
{},
"",
`${state.pathname}?${searchParams}`
)
})
)
}
},
selectRateRedemption(idx) {
return function (selectedRate, selectedRateCode?: string) {
return set(
produce((state: RatesState) => {
const redemptionRate = selectedRate.product.redemptions?.find(
(r) => r?.rateCode === selectedRateCode
)
if (!redemptionRate) {
return
}
state.rooms[idx].selectedRate = selectedRate
state.rateSummary[idx] = {
features: selectedRate.features,
package: state.rooms[idx].selectedPackage,
rate: selectedRate.product.rate,
roomType: selectedRate.roomType,
roomTypeCode: selectedRate.roomTypeCode,
redemption: redemptionRate,
}
const searchParams = new URLSearchParams(state.searchParams)
if (redemptionRate.rateCode) {
searchParams.set(
`room[${idx}].ratecode`,
redemptionRate.rateCode
)
}
searchParams.set(
`room[${idx}].roomtype`,
selectedRate.roomTypeCode
)
state.searchParams = new ReadonlyURLSearchParams(searchParams)
window.history.pushState(
{},
"",
`${state.pathname}?${searchParams}`
)
})
)
}
},
selectRateVoucher() {
return function (selectedRate) {
return set(
produce((state: RatesState) => {
const voucherRate = selectedRate.product.voucher
if (!voucherRate) {
return
}
state.rooms[0].selectedRate = selectedRate
state.rateSummary[0] = {
features: selectedRate.features,
voucher: selectedRate.product.voucher,
bonusCheque: undefined,
package: state.rooms[0].selectedPackage,
rate: selectedRate.product.rate,
roomType: selectedRate.roomType,
roomTypeCode: selectedRate.roomTypeCode,
}
const searchParams = new URLSearchParams(state.searchParams)
searchParams.set(`room[0].ratecode`, voucherRate.rateCode)
searchParams.set(`room[0].roomtype`, selectedRate.roomTypeCode)
if (state.rateSummary.length === state.booking.rooms.length) {
state.activeRoom = -1
} else {
state.activeRoom = 1
}
state.searchParams = new ReadonlyURLSearchParams(searchParams)
window.history.pushState(
{},
"",
`${state.pathname}?${searchParams}`
)
})
)
}
},
selectRateCheque(idx) {
return function (selectedRate) {
return set(
produce((state: RatesState) => {
const chequeRate = selectedRate.product.bonusCheque
if (!chequeRate) {
return
}
state.rooms[idx].selectedRate = selectedRate
state.rateSummary[idx] = {
features: selectedRate.features,
package: state.rooms[idx].selectedPackage,
rate: selectedRate.product.rate,
voucher: undefined,
bonusCheque: chequeRate,
roomType: selectedRate.roomType,
roomTypeCode: selectedRate.roomTypeCode,
}
const searchParams = new URLSearchParams(state.searchParams)
searchParams.set(`room[${idx}].ratecode`, chequeRate.rateCode)
searchParams.set(
`room[${idx}].roomtype`,
selectedRate.roomTypeCode
)
if (state.rateSummary.length === state.booking.rooms.length) {
state.activeRoom = -1
} else {
state.activeRoom = idx + 1
}
state.searchParams = new ReadonlyURLSearchParams(searchParams)
window.history.pushState(
{},
"",
`${state.pathname}?${searchParams}`
)
})
)
}
},
},
activeRoom,
booking,
filterOptions,
hotelType,
isUserLoggedIn,
packages,
pathname,
petRoomPackage: packages.find(
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
),
rateSummary,
roomConfigurations,
rooms: booking.rooms.map((room, idx) => {
const roomConfiguration = roomConfigurations[idx]
const selectedRate =
findSelectedRate(room.rateCode, room.roomTypeCode, roomConfiguration) ??
null
const product = selectedRate?.products.find(
(prd) =>
prd.public?.rateCode === room.rateCode ||
prd.member?.rateCode === room.rateCode ||
prd.bonusCheque?.rateCode === room.rateCode ||
prd.voucher?.rateCode === room.rateCode
)
const selectedPackage = room.packages?.[0]
let rooms: RoomConfiguration[] = roomConfiguration
if (selectedPackage) {
rooms = roomConfiguration.filter((r) =>
r.features.find((f) => f.code === selectedPackage)
)
}
return {
bookingRoom: room,
rooms,
selectedPackage,
selectedRate:
selectedRate && product
? {
features: selectedRate.features,
product,
roomType: selectedRate.roomType,
roomTypeCode: selectedRate.roomTypeCode,
}
: null,
}
}),
roomCategories,
roomsAvailability,
searchParams,
vat,
}))
}),
roomCategories,
roomsAvailability,
searchParams,
vat,
}
})
}
export function useRatesStore<T>(selector: (store: RatesState) => T) {