Merged in fix/allow-single-rateCode (pull request #1438)

fix: allow rates that only have either of member or public to be selectable

* fix: allow rates that only have either of member or public to be selectable


Approved-by: Michael Zetterberg
This commit is contained in:
Simon.Emanuelsson
2025-03-03 08:28:55 +00:00
committed by Linus Flood
parent 3f01266a75
commit c3e3fa62ec
30 changed files with 487 additions and 573 deletions
@@ -25,13 +25,6 @@ export function extractGuestFromUser(user: NonNullable<SafeUser>) {
}
}
export function checkIsSameBedTypes(
storedBedTypes: string,
bedTypesData: string
) {
return storedBedTypes === bedTypesData
}
export function checkIsSameBooking(
prev: SelectRateSearchParams,
next: SelectRateSearchParams
@@ -111,28 +104,34 @@ export function getRoomPrice(roomRate: RoomRate, isMember: boolean) {
}
}
return {
perNight: {
requested: roomRate.publicRate.requestedPrice && {
currency: roomRate.publicRate.requestedPrice.currency,
price: roomRate.publicRate.requestedPrice.pricePerNight,
if (roomRate.publicRate) {
return {
perNight: {
requested: roomRate.publicRate.requestedPrice && {
currency: roomRate.publicRate.requestedPrice.currency,
price: roomRate.publicRate.requestedPrice.pricePerNight,
},
local: {
currency: roomRate.publicRate.localPrice.currency,
price: roomRate.publicRate.localPrice.pricePerNight,
},
},
local: {
currency: roomRate.publicRate.localPrice.currency,
price: roomRate.publicRate.localPrice.pricePerNight,
perStay: {
requested: roomRate.publicRate.requestedPrice && {
currency: roomRate.publicRate.requestedPrice.currency,
price: roomRate.publicRate.requestedPrice.pricePerStay,
},
local: {
currency: roomRate.publicRate.localPrice.currency,
price: roomRate.publicRate.localPrice.pricePerStay,
},
},
},
perStay: {
requested: roomRate.publicRate.requestedPrice && {
currency: roomRate.publicRate.requestedPrice.currency,
price: roomRate.publicRate.requestedPrice.pricePerStay,
},
local: {
currency: roomRate.publicRate.localPrice.currency,
price: roomRate.publicRate.localPrice.pricePerStay,
},
},
}
}
throw new Error(
`Unable to calculate RoomPrice since user is neither a member or memberRate is missing, or publicRate is missing`
)
}
type TotalPrice = {
@@ -149,6 +148,10 @@ export function getTotalPrice(roomRates: RoomRate[], isMember: boolean) {
? roomRate.memberRate
: roomRate.publicRate
if (!rate) {
return total
}
return {
requested: rate.requestedPrice
? {
@@ -168,7 +171,8 @@ export function getTotalPrice(roomRates: RoomRate[], isMember: boolean) {
{
requested: undefined,
local: {
currency: roomRates[0].publicRate.localPrice.currency,
currency: (roomRates[0].publicRate?.localPrice.currency ||
roomRates[0].memberRate?.localPrice.currency)!,
price: 0,
},
}
@@ -191,6 +195,10 @@ export function calcTotalPrice(
isFirstRoomAndMember || join
)
if (!roomPrice) {
return acc
}
const breakfastRequestedPrice = room.breakfast
? parseInt(room.breakfast.requestedPrice?.price ?? 0)
: 0
+15 -2
View File
@@ -25,7 +25,6 @@ import type {
DetailsState,
InitialState,
RoomState,
RoomStatus,
} from "@/types/stores/enter-details"
import type { SafeUser } from "@/types/user"
@@ -74,7 +73,7 @@ export function createDetailsStore(
})
const rooms: RoomState[] = initialState.rooms.map((room, idx) => {
const steps: RoomStatus["steps"] = {
const steps: RoomState["steps"] = {
[StepEnum.selectBed]: {
step: StepEnum.selectBed,
isValid: !!room.bedType,
@@ -225,6 +224,13 @@ export function createDetailsStore(
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({
@@ -331,6 +337,13 @@ export function createDetailsStore(
currentRoom.room.breakfast = breakfast
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
handleStepProgression(currentRoom, state)
writeToSessionStorage({
+8 -79
View File
@@ -1,70 +1,6 @@
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import {
RoomPackageCodeEnum,
type RoomPackages,
} from "@/types/components/hotelReservation/selectRate/roomFilter"
import type {
Rate,
RateCode,
} from "@/types/components/hotelReservation/selectRate/selectRate"
import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
interface CalculateRoomSummaryParams {
availablePackages: RoomPackages
getFilteredRooms: (roomIndex: number) => RoomConfiguration[]
roomCategories: Array<{ name: string; roomTypes: Array<{ code: string }> }>
selectedPackagesByRoom: Record<number, RoomPackageCodeEnum[]>
selectedRate: RateCode
roomIndex: number
}
export function calculateRoomSummary({
selectedRate,
roomIndex,
getFilteredRooms,
availablePackages,
roomCategories,
selectedPackagesByRoom,
}: CalculateRoomSummaryParams): Rate | null {
const filteredRooms = getFilteredRooms(roomIndex)
const selectedPackages = selectedPackagesByRoom[roomIndex] || []
const room = filteredRooms.find(
(room) => room.roomTypeCode === selectedRate.roomTypeCode
)
if (!room) return null
const product = room.products.find(
(product) =>
product.productType.public.rateCode === selectedRate.publicRateCode
)
if (!product) return null
const petRoomPackage = selectedPackages.includes(RoomPackageCodeEnum.PET_ROOM)
? availablePackages.find((pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM)
: undefined
const features = filteredRooms.find((room) =>
room.features.some(
(feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
)
)?.features
const roomType = roomCategories.find((roomCategory) =>
roomCategory.roomTypes.some((type) => type.code === room.roomTypeCode)
)
return {
features: petRoomPackage && features ? features : [],
priceName: selectedRate.name,
priceTerm: selectedRate.paymentTerm,
public: product.productType.public,
member: product.productType.member,
roomType: roomType?.name ?? room.roomType,
roomTypeCode: room.roomTypeCode,
}
}
/**
* Get the lowest priced room for each room type that appears more than once.
*/
@@ -119,12 +55,11 @@ export function filterDuplicateRoomTypesByLowestPrice(
if (previousRoom) {
products.forEach((product) => {
const { productType } = product
const publicProduct = productType.public || {
const publicProduct = product?.public || {
requestedPrice: null,
localPrice: null,
}
const memberProduct = productType.member || {
const memberProduct = product?.member || {
requestedPrice: null,
localPrice: null,
}
@@ -154,34 +89,28 @@ export function filterDuplicateRoomTypesByLowestPrice(
currentRequestedPrice <
Math.min(
Number(
previousLowest.products[0].productType.public.requestedPrice
?.pricePerNight
previousLowest.products[0].public?.requestedPrice?.pricePerNight
) ?? Infinity,
Number(
previousLowest.products[0].productType.member?.requestedPrice
?.pricePerNight
previousLowest.products[0].member?.requestedPrice?.pricePerNight
) ?? Infinity
) ||
(currentRequestedPrice ===
Math.min(
Number(
previousLowest.products[0].productType.public.requestedPrice
?.pricePerNight
previousLowest.products[0].public?.requestedPrice?.pricePerNight
) ?? Infinity,
Number(
previousLowest.products[0].productType.member?.requestedPrice
?.pricePerNight
previousLowest.products[0].member?.requestedPrice?.pricePerNight
) ?? Infinity
) &&
currentLocalPrice <
Math.min(
Number(
previousLowest.products[0].productType.public.localPrice
?.pricePerNight
previousLowest.products[0].public?.localPrice?.pricePerNight
) ?? Infinity,
Number(
previousLowest.products[0].productType.member?.localPrice
?.pricePerNight
previousLowest.products[0].member?.localPrice?.pricePerNight
) ?? Infinity
))
) {
+50 -35
View File
@@ -9,6 +9,7 @@ import { RatesContext } from "@/contexts/Rates"
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { RateTypeEnum } from "@/types/enums/rateType"
import type { InitialState, RatesState } from "@/types/stores/rates"
import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
@@ -27,8 +28,8 @@ function findSelectedRate(
room.roomTypeCode === roomTypeCode &&
room.products.find(
(product) =>
product.productType.public.rateCode === rateCode ||
product.productType.member?.rateCode === rateCode
product.public?.rateCode === rateCode ||
product.member?.rateCode === rateCode
)
)
}
@@ -87,21 +88,22 @@ export function createRatesStore({
roomConf.roomTypeCode === room.roomTypeCode &&
roomConf.products.find(
(product) =>
product.productType.public.rateCode === room.rateCode ||
product.productType.member?.rateCode === room.rateCode
product.public?.rateCode === room.rateCode ||
product.member?.rateCode === room.rateCode
)
)
const product = selectedRoom?.products.find(
(p) =>
p.productType.public.rateCode === room.rateCode ||
p.productType.member?.rateCode === room.rateCode
p.public?.rateCode === room.rateCode ||
p.member?.rateCode === room.rateCode
)
if (selectedRoom && product) {
rateSummary[idx] = {
features: selectedRoom.features,
member: product.productType.member,
public: product.productType.public,
member: product.member,
public: product.public,
rate: product.rate,
roomType: selectedRoom.roomType,
roomTypeCode: selectedRoom.roomTypeCode,
}
@@ -180,35 +182,48 @@ export function createRatesStore({
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.productType.member,
member: selectedRate.product.member,
package: state.rooms[idx].selectedPackage,
public: selectedRate.product.productType.public,
rate: selectedRate.product.rate,
public: selectedRate.product.public,
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 &&
roomNr === 1 &&
selectedRate.product.productType.member
isUserLoggedIn && isMainRoom && memberRate && !isBookingCodeRate
const searchParams = new URLSearchParams(state.searchParams)
searchParams.set(
`room[${idx}].counterratecode`,
isMemberRate
? selectedRate.product.productType.public.rateCode
: (selectedRate.product.productType.member?.rateCode ?? "")
)
searchParams.set(
`room[${idx}].ratecode`,
isMemberRate
? // already checked in isMemberRate
selectedRate.product.productType.member!.rateCode
: selectedRate.product.productType.public.rateCode
)
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
@@ -249,8 +264,8 @@ export function createRatesStore({
const product = selectedRate?.products.find(
(prd) =>
prd.productType.public.rateCode === room.rateCode ||
prd.productType.member?.rateCode === room.rateCode
prd.public?.rateCode === room.rateCode ||
prd.member?.rateCode === room.rateCode
)
const selectedPackage = room.packages?.[0]
@@ -259,18 +274,18 @@ export function createRatesStore({
bookingRoom: room,
rooms: selectedPackage
? allRooms.filter((r) =>
r.features.find((f) => f.code === selectedPackage)
)
r.features.find((f) => f.code === selectedPackage)
)
: allRooms,
selectedPackage,
selectedRate:
selectedRate && product
? {
features: selectedRate.features,
product,
roomType: selectedRate.roomType,
roomTypeCode: selectedRate.roomTypeCode,
}
features: selectedRate.features,
product,
roomType: selectedRate.roomType,
roomTypeCode: selectedRate.roomTypeCode,
}
: null,
}
}),