import isEqual from "fast-deep-equal" import { detailsStorageName } from "." import type { RoomRate } from "@/types/components/hotelReservation/enterDetails/details" import type { Price } from "@/types/components/hotelReservation/price" import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate" import { StepEnum } from "@/types/enums/step" import type { DetailsState, PersistedState, RoomState, RoomStatus, } from "@/types/stores/enter-details" import type { SafeUser } from "@/types/user" export function extractGuestFromUser(user: NonNullable) { return { countryCode: user.address.countryCode?.toString(), email: user.email, firstName: user.firstName, lastName: user.lastName, join: false, membershipNo: user.membership?.membershipNumber, phoneNumber: user.phoneNumber ?? "", } } export function checkIsSameRoom( prev: SelectRateSearchParams, next: SelectRateSearchParams ) { const { rooms: prevRooms, ...prevBooking } = prev const prevRoomsWithoutRateCodes = prevRooms.map( ({ rateCode, counterRateCode, ...room }) => room ) const { rooms: nextRooms, ...nextBooking } = next const nextRoomsWithoutRateCodes = nextRooms.map( ({ rateCode, counterRateCode, ...room }) => room ) return isEqual( { ...prevBooking, rooms: prevRoomsWithoutRateCodes, }, { ...nextBooking, rooms: nextRoomsWithoutRateCodes, } ) } export function add(...nums: (number | string | undefined)[]) { return nums.reduce((total: number, num) => { if (typeof num === "undefined") { num = 0 } total = total + parseInt(`${num}`) return total }, 0) } export function subtract(...nums: (number | string | undefined)[]) { return nums.reduce((total: number, num, idx) => { if (typeof num === "undefined") { num = 0 } if (idx === 0) { return parseInt(`${num}`) } total = total - parseInt(`${num}`) if (total < 0) { return 0 } return total }, 0) } export function getRoomPrice(roomRate: RoomRate, isMember: boolean) { if (isMember && roomRate.memberRate) { return { perNight: { requested: roomRate.memberRate.requestedPrice && { currency: roomRate.memberRate.requestedPrice.currency, price: roomRate.memberRate.requestedPrice.pricePerNight, }, local: { currency: roomRate.memberRate.localPrice.currency, price: roomRate.memberRate.localPrice.pricePerNight, }, }, perStay: { requested: roomRate.memberRate.requestedPrice && { currency: roomRate.memberRate.requestedPrice.currency, price: roomRate.memberRate.requestedPrice.pricePerStay, }, local: { currency: roomRate.memberRate.localPrice.currency, price: roomRate.memberRate.localPrice.pricePerStay, }, }, } } 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, }, }, 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, }, }, } } type TotalPrice = { requested: { currency: string; price: number } | undefined local: { currency: string; price: number } } export function getTotalPrice(roomRates: RoomRate[], isMember: boolean) { return roomRates.reduce( (total, roomRate, idx) => { const isFirstRoom = idx === 0 const rate = isFirstRoom && isMember && roomRate.memberRate ? roomRate.memberRate : roomRate.publicRate return { requested: rate.requestedPrice ? { currency: rate.requestedPrice.currency, price: add( total.requested?.price ?? 0, rate.requestedPrice.pricePerStay ), } : undefined, local: { currency: rate.localPrice.currency, price: add(total.local.price ?? 0, rate.localPrice.pricePerStay), }, } }, { requested: undefined, local: { currency: roomRates[0].publicRate.localPrice.currency, price: 0, }, } ) } export function calcTotalPrice( rooms: RoomState[], totalPrice: Price, isMember: boolean ) { return rooms.reduce( (acc, room, index) => { const isFirstRoomAndMember = index === 0 && isMember const join = Boolean(room.guest.join || room.guest.membershipNo) const roomPrice = getRoomPrice( room.roomRate, isFirstRoomAndMember || join ) const breakfastRequestedPrice = room.breakfast ? room.breakfast.requestedPrice?.totalPrice ?? 0 : 0 const breakfastLocalPrice = room.breakfast ? room.breakfast.localPrice?.totalPrice ?? 0 : 0 const roomFeaturesTotal = room.roomFeatures?.reduce((total, pkg) => { if (pkg.requestedPrice.totalPrice) { total = add(total, pkg.requestedPrice.totalPrice) } return total }, 0) const result: Price = { requested: roomPrice.perStay.requested ? { currency: roomPrice.perStay.requested.currency, price: add( acc.requested?.price ?? 0, roomPrice.perStay.requested.price, breakfastRequestedPrice ), } : undefined, local: { currency: roomPrice.perStay.local.currency, price: add( acc.local.price, roomPrice.perStay.local.price, breakfastLocalPrice, roomFeaturesTotal ), }, } return result }, { requested: undefined, local: { currency: totalPrice.local.currency, price: 0 }, } ) } export const selectRoomStatus = (state: DetailsState, index?: number) => state.bookingProgress.roomStatuses[ index ?? state.bookingProgress.currentRoomIndex ] export const selectRoom = (state: DetailsState, index?: number) => state.rooms[index ?? state.bookingProgress.currentRoomIndex] export const selectRoomSteps = (state: DetailsState, index?: number) => state.bookingProgress.roomStatuses[ index ?? state.bookingProgress.currentRoomIndex ].steps export const selectNextStep = (roomStatus: RoomStatus) => { if (!roomStatus.currentStep) { throw new Error("getNextStep: currentStep is undefined") } if (!roomStatus.steps[roomStatus.currentStep]?.isValid) { return roomStatus.currentStep } const stepsArray = Object.values(roomStatus.steps) const currentIndex = stepsArray.findIndex( (step) => step?.step === roomStatus.currentStep ) if (currentIndex === stepsArray.length - 1) { return null } const nextInvalidStep = stepsArray .slice(currentIndex + 1) .find((step) => !step.isValid) return nextInvalidStep?.step ?? null } export const selectBookingProgress = (state: DetailsState) => state.bookingProgress export const checkBookingProgress = (state: DetailsState) => { return state.bookingProgress.roomStatuses.every((r) => r.isComplete) } export const checkRoomProgress = (state: DetailsState) => { const steps = selectRoomSteps(state) return Object.values(steps) .filter(Boolean) .every((step) => step.isValid) } export function handleStepProgression(state: DetailsState) { const isAllRoomsCompleted = checkBookingProgress(state) if (isAllRoomsCompleted) { const roomStatus = selectRoomStatus(state) roomStatus.currentStep = null state.bookingProgress.canProceedToPayment = true return } const roomStatus = selectRoomStatus(state) if (roomStatus.isComplete) { const nextRoomIndex = state.bookingProgress.currentRoomIndex + 1 roomStatus.lastCompletedStep = roomStatus.currentStep ?? undefined roomStatus.currentStep = null const nextRoomStatus = selectRoomStatus(state, nextRoomIndex) nextRoomStatus.currentStep = Object.values(nextRoomStatus.steps).find((step) => !step.isValid)?.step ?? StepEnum.selectBed const nextStep = selectNextStep(nextRoomStatus) nextRoomStatus.currentStep = nextStep state.bookingProgress.currentRoomIndex = nextRoomIndex return } const nextStep = selectNextStep(roomStatus) if (nextStep && roomStatus.currentStep) { roomStatus.lastCompletedStep = roomStatus.currentStep roomStatus.currentStep = nextStep return } } export function readFromSessionStorage(): PersistedState | undefined { if (typeof window === "undefined") { return undefined } try { const storedData = sessionStorage.getItem(detailsStorageName) if (!storedData) { return undefined } const parsedData = JSON.parse(storedData) as PersistedState if ( !parsedData.booking || !parsedData.rooms || !parsedData.bookingProgress ) { return undefined } return parsedData } catch (error) { console.error("Error reading from session storage:", error) return undefined } } export function writeToSessionStorage(state: PersistedState) { if (typeof window === "undefined") { return } try { sessionStorage.setItem(detailsStorageName, JSON.stringify(state)) } catch (error) { console.error("Error writing to session storage:", error) } } export function clearSessionStorage() { if (typeof window === "undefined") { return } sessionStorage.removeItem(detailsStorageName) }