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()((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(selector: (store: RatesState) => T) { const store = useContext(RatesContext) if (!store) { throw new Error("useRatesStore must be used within RatesProvider") } return useStore(store, selector) }