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

@@ -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) {