import deepmerge from "deepmerge" import isEqual from "fast-deep-equal" import { Lang } from "@/constants/languages" import { getLang } from "@/i18n/serverContext" import { arrayMerge } from "@/utils/merge" import { detailsStorageName } from "." import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate" import { CurrencyEnum } from "@/types/enums/currency" import type { StepEnum } from "@/types/enums/step" import type { DetailsState, PersistedState, PersistedStatePart, RoomRate, } from "@/types/stores/enter-details" import type { SafeUser } from "@/types/user" export function langToCurrency() { const lang = getLang() switch (lang) { case Lang.da: return CurrencyEnum.DKK case Lang.de: case Lang.en: case Lang.fi: return CurrencyEnum.EUR case Lang.no: return CurrencyEnum.NOK case Lang.sv: return CurrencyEnum.SEK default: throw new Error(`Unexpected lang: ${lang}`) } } 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 navigate(step: StepEnum, searchParams: string) { window.history.pushState({ step }, "", `${step}?${searchParams}`) } 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 getInitialRoomPrice(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, }, }, } } export function getInitialTotalPrice(roomRate: RoomRate, isMember: boolean) { if (isMember && roomRate.memberRate) { return { 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 { 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, }, } } export function calcTotalMemberPrice(state: DetailsState) { if (!state.roomRate.memberRate) { return { roomPrice: state.roomPrice, totalPrice: state.totalPrice, } } return calcTotalPrice({ breakfast: state.breakfast, packages: state.packages, roomPrice: state.roomPrice, totalPrice: state.totalPrice, ...state.roomRate.memberRate, }) } export function calcTotalPublicPrice(state: DetailsState) { return calcTotalPrice({ breakfast: state.breakfast, packages: state.packages, roomPrice: state.roomPrice, totalPrice: state.totalPrice, ...state.roomRate.publicRate, }) } export function calcTotalPrice( state: Pick< DetailsState, "breakfast" | "packages" | "roomPrice" | "totalPrice" > & DetailsState["roomRate"]["publicRate"] ) { // state is sometimes read-only, thus we // need to create a deep copy of the values const roomAndTotalPrice = { roomPrice: { perNight: { local: { ...state.roomPrice.perNight.local }, requested: state.roomPrice.perNight.requested ? { ...state.roomPrice.perNight.requested } : state.roomPrice.perNight.requested, }, perStay: { local: { ...state.roomPrice.perStay.local }, requested: state.roomPrice.perStay.requested ? { ...state.roomPrice.perStay.requested } : state.roomPrice.perStay.requested, }, }, totalPrice: { local: { ...state.totalPrice.local }, requested: state.totalPrice.requested ? { ...state.totalPrice.requested } : state.totalPrice.requested, }, } if (state.requestedPrice?.pricePerStay) { roomAndTotalPrice.roomPrice.perStay.requested = { currency: state.requestedPrice.currency, price: state.requestedPrice.pricePerStay, } let totalPriceRequested = state.requestedPrice.pricePerStay if (state.breakfast) { totalPriceRequested = add( totalPriceRequested, state.breakfast.requestedPrice.totalPrice ) } if (state.packages) { totalPriceRequested = state.packages.reduce((total, pkg) => { if (pkg.requestedPrice.totalPrice) { total = add(total, pkg.requestedPrice.totalPrice) } return total }, totalPriceRequested) } roomAndTotalPrice.totalPrice.requested = { currency: state.requestedPrice.currency, price: totalPriceRequested, } } const roomPriceLocal = state.localPrice roomAndTotalPrice.roomPrice.perStay.local = { currency: roomPriceLocal.currency, price: roomPriceLocal.pricePerStay, } let totalPriceLocal = roomPriceLocal.pricePerStay if (state.breakfast) { totalPriceLocal = add( totalPriceLocal, state.breakfast.localPrice.totalPrice ) } if (state.packages) { totalPriceLocal = state.packages.reduce((total, pkg) => { if (pkg.localPrice.totalPrice) { total = add(total, pkg.localPrice.totalPrice) } return total }, totalPriceLocal) } roomAndTotalPrice.totalPrice.local = { currency: roomPriceLocal.currency, price: totalPriceLocal, } return roomAndTotalPrice } export function writeToSessionStorage(part: PersistedStatePart) { const unparsedData = sessionStorage.getItem(detailsStorageName) if (unparsedData) { const data: PersistedState = JSON.parse(unparsedData) // @ts-expect-error - deepmerge is not to happy with // the part type const updated = deepmerge(data, part, { arrayMerge }) sessionStorage.setItem(detailsStorageName, JSON.stringify(updated)) } else { sessionStorage.setItem(detailsStorageName, JSON.stringify(part)) } }