263 lines
7.9 KiB
TypeScript
263 lines
7.9 KiB
TypeScript
import { produce } from "immer"
|
|
import { ReadonlyURLSearchParams } from "next/navigation"
|
|
import { useContext } from "react"
|
|
import { create, useStore } from "zustand"
|
|
|
|
import { filterDuplicateRoomTypesByLowestPrice } from "@/stores/select-rate/helper"
|
|
|
|
import { RatesContext } from "@/contexts/Rates"
|
|
|
|
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
|
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
|
import type { InitialState, RatesState } from "@/types/stores/rates"
|
|
import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
|
|
|
|
const statusLookup = {
|
|
[AvailabilityEnum.Available]: 1,
|
|
[AvailabilityEnum.NotAvailable]: 2,
|
|
}
|
|
|
|
function findSelectedRate(
|
|
rateCode: string,
|
|
roomTypeCode: string,
|
|
rooms: RoomConfiguration[]
|
|
) {
|
|
return rooms.find(
|
|
(room) =>
|
|
room.roomTypeCode === roomTypeCode &&
|
|
room.products.find(
|
|
(product) =>
|
|
product.productType.public.rateCode === rateCode ||
|
|
product.productType.member?.rateCode === rateCode
|
|
)
|
|
)
|
|
}
|
|
|
|
export function createRatesStore({
|
|
booking,
|
|
hotelType,
|
|
isUserLoggedIn,
|
|
labels,
|
|
packages,
|
|
pathname,
|
|
roomCategories,
|
|
roomsAvailability,
|
|
searchParams,
|
|
vat,
|
|
}: InitialState) {
|
|
const filterOptions = [
|
|
{
|
|
code: RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
|
|
description: labels.accessibilityRoom,
|
|
itemCode: packages.find(
|
|
(pkg) => pkg.code === RoomPackageCodeEnum.ACCESSIBILITY_ROOM
|
|
)?.itemCode,
|
|
},
|
|
{
|
|
code: RoomPackageCodeEnum.ALLERGY_ROOM,
|
|
description: labels.allergyRoom,
|
|
itemCode: packages.find(
|
|
(pkg) => pkg.code === RoomPackageCodeEnum.ALLERGY_ROOM
|
|
)?.itemCode,
|
|
},
|
|
{
|
|
code: RoomPackageCodeEnum.PET_ROOM,
|
|
description: labels.petRoom,
|
|
itemCode: packages.find(
|
|
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
|
|
)?.itemCode,
|
|
},
|
|
]
|
|
|
|
let allRooms: RoomConfiguration[] = []
|
|
if (roomsAvailability?.roomConfigurations) {
|
|
allRooms = filterDuplicateRoomTypesByLowestPrice(
|
|
roomsAvailability.roomConfigurations
|
|
).sort(
|
|
// @ts-expect-error - array indexing
|
|
(a, b) => statusLookup[a.status] - statusLookup[b.status]
|
|
)
|
|
}
|
|
|
|
const rateSummary: RatesState["rateSummary"] = []
|
|
booking.rooms.forEach((room, idx) => {
|
|
if (room.rateCode && room.roomTypeCode) {
|
|
const selectedRoom = roomsAvailability?.roomConfigurations.find(
|
|
(roomConf) =>
|
|
roomConf.roomTypeCode === room.roomTypeCode &&
|
|
roomConf.products.find(
|
|
(product) =>
|
|
product.productType.public.rateCode === room.rateCode ||
|
|
product.productType.member?.rateCode === room.rateCode
|
|
)
|
|
)
|
|
|
|
const product = selectedRoom?.products.find(
|
|
(p) =>
|
|
p.productType.public.rateCode === room.rateCode ||
|
|
p.productType.member?.rateCode === room.rateCode
|
|
)
|
|
if (selectedRoom && product) {
|
|
rateSummary[idx] = {
|
|
features: selectedRoom.features,
|
|
member: product.productType.member,
|
|
public: product.productType.public,
|
|
roomType: selectedRoom.roomType,
|
|
roomTypeCode: selectedRoom.roomTypeCode,
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
return create<RatesState>()((set) => ({
|
|
actions: {
|
|
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 searchParams = new URLSearchParams(state.searchParams)
|
|
if (code) {
|
|
state.rooms[idx].rooms = state.allRooms.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 = state.allRooms
|
|
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(idx) {
|
|
return function (selectedRate) {
|
|
return set(
|
|
produce((state: RatesState) => {
|
|
state.rooms[idx].selectedRate = selectedRate
|
|
state.rateSummary[idx] = {
|
|
features: selectedRate.features,
|
|
member: selectedRate.product.productType.member,
|
|
package: state.rooms[idx].selectedPackage,
|
|
public: selectedRate.product.productType.public,
|
|
roomType: selectedRate.roomType,
|
|
roomTypeCode: selectedRate.roomTypeCode,
|
|
}
|
|
|
|
const searchParams = new URLSearchParams(state.searchParams)
|
|
searchParams.set(
|
|
`room[${idx}].counterratecode`,
|
|
isUserLoggedIn && selectedRate.product.productType.member
|
|
? selectedRate.product.productType.public.rateCode
|
|
: selectedRate.product.productType.member?.rateCode ?? ""
|
|
)
|
|
searchParams.set(
|
|
`room[${idx}].ratecode`,
|
|
isUserLoggedIn && selectedRate.product.productType.member
|
|
? selectedRate.product.productType.member.rateCode
|
|
: selectedRate.product.productType.public.rateCode
|
|
)
|
|
searchParams.set(
|
|
`room[${idx}].roomtype`,
|
|
selectedRate.roomTypeCode
|
|
)
|
|
|
|
state.activeRoom =
|
|
idx + 1 < state.booking.rooms.length
|
|
? idx + 1
|
|
: state.booking.rooms.length
|
|
|
|
state.searchParams = new ReadonlyURLSearchParams(searchParams)
|
|
window.history.pushState(
|
|
{},
|
|
"",
|
|
`${state.pathname}?${searchParams}`
|
|
)
|
|
})
|
|
)
|
|
}
|
|
},
|
|
},
|
|
activeRoom: rateSummary.length,
|
|
allRooms,
|
|
booking,
|
|
filterOptions,
|
|
hotelType,
|
|
packages,
|
|
pathname,
|
|
petRoomPackage: packages.find(
|
|
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
|
|
),
|
|
rateSummary,
|
|
rooms: booking.rooms.map((room) => {
|
|
const selectedRate =
|
|
findSelectedRate(room.rateCode, room.roomTypeCode, allRooms) ?? null
|
|
|
|
const product = selectedRate?.products.find(
|
|
(prd) =>
|
|
prd.productType.public.rateCode === room.rateCode ||
|
|
prd.productType.member?.rateCode === room.rateCode
|
|
)
|
|
|
|
const selectedPackage = room.packages?.[0]
|
|
|
|
return {
|
|
bookingRoom: room,
|
|
rooms: selectedPackage
|
|
? allRooms.filter((r) =>
|
|
r.features.find((f) => f.code === selectedPackage)
|
|
)
|
|
: allRooms,
|
|
selectedPackage,
|
|
selectedRate:
|
|
selectedRate && product
|
|
? {
|
|
features: selectedRate.features,
|
|
product,
|
|
roomType: selectedRate.roomType,
|
|
roomTypeCode: selectedRate.roomTypeCode,
|
|
}
|
|
: null,
|
|
}
|
|
}),
|
|
roomCategories,
|
|
roomsAvailability,
|
|
searchParams,
|
|
vat,
|
|
}))
|
|
}
|
|
|
|
export function useRatesStore<T>(selector: (store: RatesState) => T) {
|
|
const store = useContext(RatesContext)
|
|
|
|
if (!store) {
|
|
throw new Error("useRatesStore must be used within RatesProvider")
|
|
}
|
|
|
|
return useStore(store, selector)
|
|
}
|