198 lines
5.7 KiB
TypeScript
198 lines
5.7 KiB
TypeScript
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.
|
|
*/
|
|
|
|
export function filterDuplicateRoomTypesByLowestPrice(
|
|
roomConfigurations: RoomConfiguration[]
|
|
): RoomConfiguration[] {
|
|
const roomTypeCount = roomConfigurations.reduce<Record<string, number>>(
|
|
(roomTypeTally, currentRoom) => {
|
|
const currentRoomType = currentRoom.roomType
|
|
const currentCount = roomTypeTally[currentRoomType] || 0
|
|
|
|
return {
|
|
...roomTypeTally,
|
|
[currentRoomType]: currentCount + 1,
|
|
}
|
|
},
|
|
{}
|
|
)
|
|
|
|
const duplicateRoomTypes = new Set(
|
|
Object.keys(roomTypeCount).filter((roomType) => roomTypeCount[roomType] > 1)
|
|
)
|
|
|
|
const roomMap = new Map()
|
|
|
|
roomConfigurations.forEach((room) => {
|
|
const { roomType, products, status } = room
|
|
|
|
if (!duplicateRoomTypes.has(roomType)) {
|
|
roomMap.set(roomType, room)
|
|
return
|
|
}
|
|
|
|
const previousRoom = roomMap.get(roomType)
|
|
|
|
// Prioritize 'Available' status
|
|
if (
|
|
status === AvailabilityEnum.Available &&
|
|
previousRoom?.status === AvailabilityEnum.NotAvailable
|
|
) {
|
|
roomMap.set(roomType, room)
|
|
return
|
|
}
|
|
|
|
if (
|
|
status === AvailabilityEnum.NotAvailable &&
|
|
previousRoom?.status === AvailabilityEnum.Available
|
|
) {
|
|
return
|
|
}
|
|
|
|
if (previousRoom) {
|
|
products.forEach((product) => {
|
|
const { productType } = product
|
|
const publicProduct = productType.public || {
|
|
requestedPrice: null,
|
|
localPrice: null,
|
|
}
|
|
const memberProduct = productType.member || {
|
|
requestedPrice: null,
|
|
localPrice: null,
|
|
}
|
|
|
|
const {
|
|
requestedPrice: publicRequestedPrice,
|
|
localPrice: publicLocalPrice,
|
|
} = publicProduct
|
|
const {
|
|
requestedPrice: memberRequestedPrice,
|
|
localPrice: memberLocalPrice,
|
|
} = memberProduct
|
|
|
|
const previousLowest = roomMap.get(roomType)
|
|
|
|
const currentRequestedPrice = Math.min(
|
|
Number(publicRequestedPrice?.pricePerNight) ?? Infinity,
|
|
Number(memberRequestedPrice?.pricePerNight) ?? Infinity
|
|
)
|
|
const currentLocalPrice = Math.min(
|
|
Number(publicLocalPrice?.pricePerNight) ?? Infinity,
|
|
Number(memberLocalPrice?.pricePerNight) ?? Infinity
|
|
)
|
|
|
|
if (
|
|
!previousLowest ||
|
|
currentRequestedPrice <
|
|
Math.min(
|
|
Number(
|
|
previousLowest.products[0].productType.public.requestedPrice
|
|
?.pricePerNight
|
|
) ?? Infinity,
|
|
Number(
|
|
previousLowest.products[0].productType.member?.requestedPrice
|
|
?.pricePerNight
|
|
) ?? Infinity
|
|
) ||
|
|
(currentRequestedPrice ===
|
|
Math.min(
|
|
Number(
|
|
previousLowest.products[0].productType.public.requestedPrice
|
|
?.pricePerNight
|
|
) ?? Infinity,
|
|
Number(
|
|
previousLowest.products[0].productType.member?.requestedPrice
|
|
?.pricePerNight
|
|
) ?? Infinity
|
|
) &&
|
|
currentLocalPrice <
|
|
Math.min(
|
|
Number(
|
|
previousLowest.products[0].productType.public.localPrice
|
|
?.pricePerNight
|
|
) ?? Infinity,
|
|
Number(
|
|
previousLowest.products[0].productType.member?.localPrice
|
|
?.pricePerNight
|
|
) ?? Infinity
|
|
))
|
|
) {
|
|
roomMap.set(roomType, room)
|
|
}
|
|
})
|
|
} else {
|
|
roomMap.set(roomType, room)
|
|
}
|
|
})
|
|
|
|
return Array.from(roomMap.values())
|
|
}
|