import isEqual from "fast-deep-equal" import { sumPackages, sumPackagesRequestedPrice, } from "@/components/HotelReservation/utils" 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 { CurrencyEnum } from "@/types/enums/currency" import { StepEnum } from "@/types/enums/step" import type { PersistedState, RoomState } 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 checkIsSameBooking( prev: SelectRateSearchParams & { errorCode?: string }, next: SelectRateSearchParams & { errorCode?: string } ) { const { rooms: prevRooms, errorCode: prevErrorCode, ...prevBooking } = prev const prevRoomsWithoutRateCodes = prevRooms.map( ({ rateCode, counterRateCode, roomTypeCode, ...room }) => room ) const { rooms: nextRooms, errorCode: nextErrorCode, ...nextBooking } = next const nextRoomsWithoutRateCodes = nextRooms.map( ({ rateCode, counterRateCode, roomTypeCode, ...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 getCurrency(roomRate: RoomRate) { if ("corporateCheque" in roomRate) { return { localCurrency: CurrencyEnum.CC, requestedCurrency: CurrencyEnum.CC, } } else if ("redemption" in roomRate) { return { localCurrency: CurrencyEnum.POINTS, requestedCurrency: CurrencyEnum.POINTS, } } else if ("voucher" in roomRate) { return { localCurrency: CurrencyEnum.Voucher, requestedCurrency: CurrencyEnum.Voucher, } } else if ("public" in roomRate && roomRate.public) { return { localCurrency: roomRate.public.localPrice.currency, requestedCurrency: roomRate.public.requestedPrice?.currency, } } else if ("member" in roomRate && roomRate.member) { return { localCurrency: roomRate.member.localPrice.currency, requestedCurrency: roomRate.member.requestedPrice?.currency, } } return { localCurrency: CurrencyEnum.Unknown, requestedCurrency: CurrencyEnum.Unknown, } } export function getRoomPrice(roomRate: RoomRate, isMember: boolean) { if (isMember && "member" in roomRate && roomRate.member) { return { perNight: { requested: roomRate.member.requestedPrice ? { currency: roomRate.member.requestedPrice.currency, price: roomRate.member.requestedPrice.pricePerNight, } : undefined, local: { currency: roomRate.member.localPrice.currency, price: roomRate.member.localPrice.pricePerNight, }, }, perStay: { requested: roomRate.member.requestedPrice ? { currency: roomRate.member.requestedPrice.currency, price: roomRate.member.requestedPrice.pricePerStay, } : undefined, local: { currency: roomRate.member.localPrice.currency, price: roomRate.member.localPrice.pricePerStay, }, }, } } if ("public" in roomRate && roomRate.public) { return { perNight: { requested: roomRate.public.requestedPrice ? { currency: roomRate.public.requestedPrice.currency, price: roomRate.public.requestedPrice.pricePerNight, } : undefined, local: { currency: roomRate.public.localPrice.currency, price: roomRate.public.localPrice.pricePerNight, regularPrice: roomRate.public.localPrice.regularPricePerNight, }, }, perStay: { requested: roomRate.public.requestedPrice ? { currency: roomRate.public.requestedPrice.currency, price: roomRate.public.requestedPrice.pricePerStay, } : undefined, local: { currency: roomRate.public.localPrice.currency, price: roomRate.public.localPrice.pricePerStay, regularPrice: roomRate.public.localPrice.regularPricePerStay, }, }, } } if ("corporateCheque" in roomRate) { return { perNight: { requested: roomRate.corporateCheque.requestedPrice ? { currency: CurrencyEnum.CC, price: roomRate.corporateCheque.requestedPrice.numberOfCheques, additionalPrice: roomRate.corporateCheque.requestedPrice.additionalPricePerStay, additionalPriceCurrency: roomRate.corporateCheque.requestedPrice.currency ?? undefined, } : undefined, local: { currency: CurrencyEnum.CC, price: roomRate.corporateCheque.localPrice.numberOfCheques, additionalPrice: roomRate.corporateCheque.localPrice.additionalPricePerStay, additionalPriceCurrency: roomRate.corporateCheque.localPrice.currency ?? undefined, }, }, perStay: { requested: roomRate.corporateCheque.requestedPrice ? { currency: CurrencyEnum.CC, price: roomRate.corporateCheque.requestedPrice.numberOfCheques, additionalPrice: roomRate.corporateCheque.requestedPrice.additionalPricePerStay, additionalPriceCurrency: roomRate.corporateCheque.requestedPrice.currency ?? undefined, } : undefined, local: { currency: CurrencyEnum.CC, price: roomRate.corporateCheque.localPrice.numberOfCheques, additionalPrice: roomRate.corporateCheque.localPrice.additionalPricePerStay, additionalPriceCurrency: roomRate.corporateCheque.localPrice.currency ?? undefined, }, }, } } if ("voucher" in roomRate) { return { perNight: { requested: undefined, local: { currency: CurrencyEnum.Voucher, price: roomRate.voucher.numberOfVouchers, }, }, perStay: { requested: undefined, local: { currency: CurrencyEnum.Voucher, price: roomRate.voucher.numberOfVouchers, }, }, } } if ("redemption" in roomRate) { return { // ToDo Handle perNight as undefined perNight: { requested: undefined, local: { currency: CurrencyEnum.POINTS, price: roomRate.redemption.localPrice.pointsPerStay, additionalPrice: roomRate.redemption.localPrice.additionalPricePerStay, additionalPriceCurrency: roomRate.redemption.localPrice.currency ?? undefined, }, }, perStay: { requested: undefined, local: { currency: CurrencyEnum.POINTS, price: roomRate.redemption.localPrice.pointsPerStay, additionalPrice: roomRate.redemption.localPrice.additionalPricePerStay, additionalPriceCurrency: roomRate.redemption.localPrice.currency ?? undefined, }, }, } } throw new Error( `Unable to calculate RoomPrice since user is neither a member or memberRate is missing, or publicRate is missing` ) } export function getTotalPrice(roomRates: RoomRate[], isMember: boolean) { return roomRates.reduce( (total, roomRate, idx) => { const isMainRoom = idx === 0 let rate if (isMainRoom && isMember && "member" in roomRate && roomRate.member) { rate = roomRate.member } else if ("public" in roomRate && roomRate.public) { rate = roomRate.public } // TODO: Handle other products? if (!rate) { return total } total.local.currency = rate.localPrice.currency total.local.price = add(total.local.price, rate.localPrice.pricePerStay) if (rate.localPrice.regularPricePerStay) { total.local.regularPrice = add( total.local.regularPrice, rate.localPrice.regularPricePerStay ) } if (rate.requestedPrice) { if (total.requested) { total.requested.price = add( total.requested.price, rate.requestedPrice.pricePerStay ) } else { total.requested = { currency: rate.requestedPrice.currency, price: rate.requestedPrice.pricePerStay, } } } return total }, { local: { currency: CurrencyEnum.Unknown, price: 0, }, requested: undefined, } ) } export function calculateVoucherPrice(roomRates: RoomRate[]) { return roomRates.reduce( (total, room) => { if (!("voucher" in room)) { return total } return { local: { currency: total.local.currency, price: total.local.price + room.voucher.numberOfVouchers, }, requested: undefined, } }, { local: { currency: CurrencyEnum.Voucher, price: 0, }, requested: undefined, } ) } export function calculateCorporateChequePrice(roomRates: RoomRate[]) { return roomRates.reduce( (total, room) => { if (!("corporateCheque" in room)) { return total } const rate = room.corporateCheque total.local.price = add( total.local.price, rate.localPrice.numberOfCheques ) if (rate.localPrice.additionalPricePerStay) { total.local.additionalPrice = add( total.local.additionalPrice, rate.localPrice.additionalPricePerStay ) } if (rate.localPrice.currency) { total.local.additionalPriceCurrency = rate.localPrice.currency } if (rate.requestedPrice) { if (total.requested) { total.requested.price = add( total.requested.price, rate.requestedPrice.numberOfCheques ) } else { total.requested = { currency: CurrencyEnum.CC, price: rate.requestedPrice.numberOfCheques, } } if (rate.requestedPrice.additionalPricePerStay) { total.requested.additionalPrice = add( total.requested.additionalPrice, rate.requestedPrice.additionalPricePerStay ) } if (rate.requestedPrice.currency) { total.requested.additionalPriceCurrency = rate.requestedPrice.currency } } return total }, { local: { currency: CurrencyEnum.CC, price: 0, }, requested: undefined, } ) } export function calcTotalPrice( rooms: RoomState[], currency: Price["local"]["currency"], isMember: boolean, nights: number ) { 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 ) if (!roomPrice) { return acc } const isSpecialRate = "corporateCheque" in room.roomRate || "redemption" in room.roomRate || "voucher" in room.roomRate const breakfastRequestedPrice = room.breakfast ? (room.breakfast.requestedPrice?.price ?? 0) : 0 const breakfastLocalPrice = room.breakfast ? (room.breakfast.localPrice?.price ?? 0) : 0 const pkgsSum = sumPackages(room.roomFeatures) const pkgsSumRequested = sumPackagesRequestedPrice(room.roomFeatures) const breakfastRequestedTotalPrice = breakfastRequestedPrice * room.adults * nights if (roomPrice.perStay.requested) { if (!acc.requested) { acc.requested = { currency: roomPrice.perStay.requested.currency, price: 0, } } if (isSpecialRate) { acc.requested.price = add( acc.requested.price, roomPrice.perStay.requested.price ) acc.requested.additionalPrice = add( breakfastRequestedTotalPrice, pkgsSumRequested.price ) if (!acc.requested.additionalPriceCurrency) { if (roomPrice.perStay.requested.additionalPriceCurrency) { acc.requested.additionalPriceCurrency = roomPrice.perStay.requested.additionalPriceCurrency } else if (room.breakfast) { acc.requested.additionalPriceCurrency = room.breakfast.localPrice.currency } else if (pkgsSumRequested.currency) { acc.requested.additionalPriceCurrency = pkgsSumRequested.currency } } } else { acc.requested.price = add( acc.requested.price, roomPrice.perStay.requested.price, breakfastRequestedTotalPrice, pkgsSumRequested.price ) } } const breakfastLocalTotalPrice = breakfastLocalPrice * room.adults * nights if (isSpecialRate) { acc.local.price = add(acc.local.price, roomPrice.perStay.local.price) if ( roomPrice.perStay.local.additionalPrice || breakfastLocalTotalPrice || pkgsSum.price ) { acc.local.additionalPrice = add( acc.local.additionalPrice, roomPrice.perStay.local.additionalPrice, breakfastLocalTotalPrice, pkgsSum.price ) } if (!acc.local.additionalPriceCurrency) { if (roomPrice.perStay.local.additionalPriceCurrency) { acc.local.additionalPriceCurrency = roomPrice.perStay.local.additionalPriceCurrency } else if (room.breakfast) { acc.local.additionalPriceCurrency = room.breakfast.localPrice.currency } else if (pkgsSum.currency) { acc.local.additionalPriceCurrency = pkgsSum.currency } } } else { acc.local.price = add( acc.local.price, roomPrice.perStay.local.price, breakfastLocalTotalPrice, pkgsSum.price ) if (roomPrice.perStay.local.regularPrice) { acc.local.regularPrice = add( acc.local.regularPrice, roomPrice.perStay.local.regularPrice, breakfastLocalTotalPrice, pkgsSum.price ) } } return acc }, { requested: undefined, local: { currency, price: 0 }, } ) } export function getFirstInteractiveStepOfRoom(room: RoomState["room"]) { if (!room.bedType) { return StepEnum.selectBed } if (room.breakfast !== false) { return StepEnum.breakfast } return StepEnum.details } export function findNextInvalidStep(roomState: RoomState) { return ( Object.values(roomState.steps).find((stp) => !stp.isValid)?.step ?? getFirstInteractiveStepOfRoom(roomState.room) ) } export const checkRoomProgress = (steps: RoomState["steps"]) => { return Object.values(steps) .filter(Boolean) .every((step) => step.isValid) } 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) { 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) }