Merged in chore/SW-3321-move-selectratecontext-to- (pull request #2729)
chore(SW-3321): Moved Select rate context to booking-flow package * chore(SW-3321): Moved Select rate context to booking-flow package * chore(SW-3321): Optimised code Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { SelectRateProvider } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { parseSelectRateSearchParams } from "@scandic-hotels/booking-flow/utils/url"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||
@@ -8,7 +9,6 @@ import { combineRegExps, rateTypeRegex } from "@/constants/booking"
|
||||
import { getHotel } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import SelectRate from "@/components/HotelReservation/SelectRate"
|
||||
import { SelectRateProvider } from "@/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import type { LangParams, NextSearchParams, PageArgs } from "@/types/params"
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createHash } from "crypto"
|
||||
import { differenceInCalendarDays, format, isWeekend } from "date-fns"
|
||||
|
||||
import { invertedBedTypeMap } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { RateEnum } from "@scandic-hotels/common/constants/rate"
|
||||
import {
|
||||
@@ -18,8 +19,6 @@ import { CancellationRuleEnum } from "@/constants/booking"
|
||||
import { readPaymentInfoFromSessionStorage } from "@/components/HotelReservation/EnterDetails/Payment/helpers"
|
||||
import { getSpecialRoomType } from "@/utils/specialRoomType"
|
||||
|
||||
import { invertedBedTypeMap } from "../../utils"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
|
||||
import type { RateDefinition } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { filterOverlappingDates } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { Alert } from "@scandic-hotels/design-system/Alert"
|
||||
@@ -16,7 +17,6 @@ import SidePanel from "@/components/HotelReservation/SidePanel"
|
||||
import { getIntl } from "@/i18n"
|
||||
import BookingConfirmationProvider from "@/providers/BookingConfirmationProvider"
|
||||
|
||||
import { filterOverlappingDates } from "../utils"
|
||||
import Confirmation from "./Confirmation"
|
||||
import Tracking from "./Tracking"
|
||||
import { mapRoomState } from "./utils"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
|
||||
import type { RoomState } from "@/types/stores/enter-details"
|
||||
|
||||
export function mapToPrice(rooms: RoomState[], isMember: boolean) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { differenceInCalendarDays, format, isWeekend } from "date-fns"
|
||||
|
||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
@@ -12,7 +13,6 @@ import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
||||
import { PackageTypeEnum } from "@scandic-hotels/trpc/enums/packages"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
import { getSpecialRoomType } from "@/utils/specialRoomType"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
|
||||
import { sumPackages } from "../../utils"
|
||||
|
||||
import { PriceTypeEnum } from "@/types/components/hotelReservation/myStay/myStay"
|
||||
import type { Price } from "@/types/components/hotelReservation/price"
|
||||
import type { Room } from "@/types/stores/my-stay"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { calculateVat } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
|
||||
import { calculateVat } from "@/components/HotelReservation/utils"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./specification.module.css"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import { useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||
import { trpc } from "@scandic-hotels/trpc/client"
|
||||
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import Confirmation from "./Confirmation"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { IconForFeatureCode } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { changeOrCancelDateFormat } from "@scandic-hotels/common/constants/dateFormats"
|
||||
import { RateEnum } from "@scandic-hotels/common/constants/rate"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
@@ -16,7 +17,6 @@ import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
|
||||
import { CancellationRuleEnum } from "@/constants/booking"
|
||||
|
||||
import { IconForFeatureCode } from "@/components/HotelReservation/utils"
|
||||
import useRateTitles from "@/hooks/booking/useRateTitles"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import PriceType from "@/components/HotelReservation/MyStay/PriceType"
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
|
||||
import styles from "./details.module.css"
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import { IconForFeatureCode } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import { IconForFeatureCode } from "@/components/HotelReservation/utils"
|
||||
|
||||
import styles from "./packages.module.css"
|
||||
|
||||
export default function Packages() {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"use client"
|
||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import { sumPackages } from "../../utils"
|
||||
import PriceType from "../PriceType"
|
||||
|
||||
import type { PriceType as _PriceType } from "@/types/components/hotelReservation/myStay/myStay"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { cookies } from "next/headers"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { filterOverlappingDates } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
import * as maskValue from "@scandic-hotels/common/utils/maskValue"
|
||||
@@ -39,8 +40,6 @@ import { getIntl } from "@/i18n"
|
||||
import MyStayProvider from "@/providers/MyStay"
|
||||
import { isLoggedInUser } from "@/utils/isLoggedInUser"
|
||||
|
||||
import { filterOverlappingDates } from "../utils"
|
||||
|
||||
import styles from "./index.module.css"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { calculateVat } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||
|
||||
import { calculateVat } from "@/components/HotelReservation/utils"
|
||||
|
||||
import RegularRow from "./Regular"
|
||||
|
||||
import type { Price } from "@/types/components/hotelReservation/price"
|
||||
|
||||
@@ -15,8 +15,8 @@ import { isBookingCodeRate } from "./utils"
|
||||
|
||||
import styles from "./rateSummary.module.css"
|
||||
|
||||
import type { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import type { SelectedRate } from "@/contexts/SelectRate/types"
|
||||
import type { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import type { SelectedRate } from "@scandic-hotels/booking-flow/contexts/SelectRate/types"
|
||||
|
||||
export function DesktopSummary({
|
||||
input,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { cx } from "class-variance-authority"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { longDateFormat } from "@scandic-hotels/common/constants/dateFormats"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
@@ -13,7 +14,6 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import PriceDetailsModal from "@/components/HotelReservation/PriceDetailsModal"
|
||||
import SignupPromoDesktop from "@/components/HotelReservation/SignupPromo/Desktop"
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import useRateTitles from "@/hooks/booking/useRateTitles"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
@@ -22,7 +22,7 @@ import Room from "../Room"
|
||||
|
||||
import styles from "./summaryContent.module.css"
|
||||
|
||||
import type { Price } from "@/contexts/SelectRate/getTotalPrice"
|
||||
import type { Price } from "@scandic-hotels/booking-flow/contexts/SelectRate/getTotalPrice"
|
||||
|
||||
export type SelectRateSummaryProps = {
|
||||
isMember: boolean
|
||||
|
||||
@@ -4,12 +4,12 @@ import { useEffect, useRef, useState } from "react"
|
||||
import { Button as ButtonRAC } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import { useIsUserLoggedIn } from "@/hooks/useIsUserLoggedIn"
|
||||
|
||||
import { isBookingCodeRate } from "../utils"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
|
||||
import type { Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
|
||||
import type { Price } from "@/types/components/hotelReservation/price"
|
||||
import type {
|
||||
Rate,
|
||||
Room,
|
||||
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
} from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
|
||||
import type { Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
|
||||
import type { Price } from "@/types/components/hotelReservation/price"
|
||||
|
||||
export function mapRate(
|
||||
room: Rate,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type {
|
||||
Rate,
|
||||
Room as SelectRateRoom,
|
||||
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
} from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
|
||||
|
||||
import type { Room } from "@/components/HotelReservation/PriceDetailsModal/PriceDetailsTable"
|
||||
|
||||
export function mapToPrice(
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useState, useTransition } from "react"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import { ErrorBoundary } from "@/components/ErrorBoundary/ErrorBoundary"
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import { DesktopSummary } from "./DesktopSummary"
|
||||
import { MobileSummary } from "./MobileSummary"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { RateTypeEnum } from "@scandic-hotels/common/constants/rateType"
|
||||
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
|
||||
import type { Rate } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
|
||||
import type { Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
import type {
|
||||
Product,
|
||||
@@ -10,7 +10,6 @@ import type {
|
||||
} from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type { Price } from "@/types/components/hotelReservation/price"
|
||||
import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
|
||||
export function calculateTotalPrice(
|
||||
selectedRateSummary: Rate[],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { RateEnum } from "@scandic-hotels/common/constants/rate"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
@@ -12,7 +13,6 @@ import Image from "@scandic-hotels/design-system/Image"
|
||||
import { OldDSButton as Button } from "@scandic-hotels/design-system/OldDSButton"
|
||||
import Subtitle from "@scandic-hotels/design-system/Subtitle"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import { useIsUserLoggedIn } from "@/hooks/useIsUserLoggedIn"
|
||||
|
||||
import styles from "./selectedRoomPanel.module.css"
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useEffect } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import useStickyPosition from "@scandic-hotels/common/hooks/useStickyPosition"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { OldDSButton as Button } from "@scandic-hotels/design-system/OldDSButton"
|
||||
import Subtitle from "@scandic-hotels/design-system/Subtitle"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import { SelectedRoomPanel } from "./SelectedRoomPanel"
|
||||
import { roomSelectionPanelVariants } from "./variants"
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
||||
import { alternativeHotels } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
||||
import { Alert } from "@scandic-hotels/design-system/Alert"
|
||||
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import styles from "./alert.module.css"
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import { RateTypeEnum } from "@scandic-hotels/common/constants/rateType"
|
||||
import { ChipButton } from "@scandic-hotels/design-system/ChipButton"
|
||||
@@ -19,7 +20,6 @@ import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import { useBreakpoint } from "@/hooks/useBreakpoint"
|
||||
|
||||
import styles from "./bookingCodeFilter.module.css"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { BookingCodeChip } from "@scandic-hotels/design-system/BookingCodeChip"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
export function RemoveBookingCodeButton() {
|
||||
const {
|
||||
input: { bookingCode },
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client"
|
||||
import { Button as ButtonRAC } from "react-aria-components"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import { useBreakpoint } from "@/hooks/useBreakpoint"
|
||||
|
||||
import PetRoomMessage from "./Form/Checkboxes/PetRoomMessage"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
|
||||
|
||||
import { ErrorBoundary } from "@/components/ErrorBoundary/ErrorBoundary"
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import { RemoveBookingCodeButton } from "./RemoveBookingCodeButton/RemoveBookingCodeButton"
|
||||
import { RoomPackageFilter } from "./RoomPackageFilter"
|
||||
|
||||
@@ -7,7 +7,7 @@ import RoomSize from "./RoomSize"
|
||||
|
||||
import styles from "./details.module.css"
|
||||
|
||||
import type { RoomInfo } from "@/contexts/SelectRate/types"
|
||||
import type { RoomInfo } from "@scandic-hotels/booking-flow/contexts/SelectRate/types"
|
||||
|
||||
type Props = {
|
||||
roomInfo: RoomInfo
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import { useSession } from "next-auth/react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import { isValidClientSession } from "@/utils/clientSession"
|
||||
|
||||
import { getBreakfastMessage } from "./getBreakfastMessage"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import CampaignRateCard from "@scandic-hotels/design-system/CampaignRateCard"
|
||||
import NoRateAvailableCard from "@scandic-hotels/design-system/NoRateAvailableCard"
|
||||
|
||||
import {
|
||||
sumPackages,
|
||||
sumPackagesRequestedPrice,
|
||||
} from "@/components/HotelReservation/utils"
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
} from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import CampaignRateCard from "@scandic-hotels/design-system/CampaignRateCard"
|
||||
import NoRateAvailableCard from "@scandic-hotels/design-system/NoRateAvailableCard"
|
||||
|
||||
import useRateTitles from "@/hooks/booking/useRateTitles"
|
||||
import { useIsUserLoggedIn } from "@/hooks/useIsUserLoggedIn"
|
||||
|
||||
@@ -18,7 +18,7 @@ import { calculatePricePerNightPriceProduct } from "./totalPricePerNight"
|
||||
import type {
|
||||
AvailabilityWithRoomInfo,
|
||||
Package,
|
||||
} from "@/contexts/SelectRate/types"
|
||||
} from "@scandic-hotels/booking-flow/contexts/SelectRate/types"
|
||||
|
||||
type CampaignProps = {
|
||||
nights: number
|
||||
|
||||
@@ -2,21 +2,20 @@
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import CodeRateCard from "@scandic-hotels/design-system/CodeRateCard"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import {
|
||||
sumPackages,
|
||||
sumPackagesRequestedPrice,
|
||||
} from "@/components/HotelReservation/utils"
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
} from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import CodeRateCard from "@scandic-hotels/design-system/CodeRateCard"
|
||||
|
||||
import useRateTitles from "@/hooks/booking/useRateTitles"
|
||||
|
||||
import { calculatePricePerNightPriceProduct } from "./totalPricePerNight"
|
||||
|
||||
import type { Package } from "@scandic-hotels/booking-flow/contexts/SelectRate/types"
|
||||
import type { CodeProduct } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type { Package } from "@/contexts/SelectRate/types"
|
||||
|
||||
type CodeProps = {
|
||||
nights: number
|
||||
roomTypeCode: string
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import PointsRateCard from "@scandic-hotels/design-system/PointsRateCard"
|
||||
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import useRateTitles from "@/hooks/booking/useRateTitles"
|
||||
|
||||
import type {
|
||||
AvailabilityWithRoomInfo,
|
||||
Package,
|
||||
} from "@/contexts/SelectRate/types"
|
||||
} from "@scandic-hotels/booking-flow/contexts/SelectRate/types"
|
||||
|
||||
type RedemptionsProps = {
|
||||
redemptions: AvailabilityWithRoomInfo["redemptions"]
|
||||
|
||||
@@ -2,24 +2,23 @@
|
||||
import { useSession } from "next-auth/react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import NoRateAvailableCard from "@scandic-hotels/design-system/NoRateAvailableCard"
|
||||
import RegularRateCard from "@scandic-hotels/design-system/RegularRateCard"
|
||||
|
||||
import {
|
||||
sumPackages,
|
||||
sumPackagesRequestedPrice,
|
||||
} from "@/components/HotelReservation/utils"
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
} from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import NoRateAvailableCard from "@scandic-hotels/design-system/NoRateAvailableCard"
|
||||
import RegularRateCard from "@scandic-hotels/design-system/RegularRateCard"
|
||||
|
||||
import useRateTitles from "@/hooks/booking/useRateTitles"
|
||||
import { isValidClientSession } from "@/utils/clientSession"
|
||||
|
||||
import { calculatePricePerNightPriceProduct } from "./totalPricePerNight"
|
||||
|
||||
import type { AvailabilityWithRoomInfo } from "@scandic-hotels/booking-flow/contexts/SelectRate/types"
|
||||
import type { Package } from "@scandic-hotels/trpc/types/packages"
|
||||
|
||||
import type { AvailabilityWithRoomInfo } from "@/contexts/SelectRate/types"
|
||||
|
||||
interface Rate {
|
||||
label: string
|
||||
price: string
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
"use client"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import { BreakfastMessage } from "./BreakfastMessage"
|
||||
import Campaign from "./Campaign"
|
||||
import Code from "./Code"
|
||||
import Redemptions from "./Redemptions"
|
||||
import { RegularRate } from "./Regular"
|
||||
|
||||
import type { AvailabilityWithRoomInfo } from "@scandic-hotels/booking-flow/contexts/SelectRate/types"
|
||||
import type { Package } from "@scandic-hotels/trpc/types/packages"
|
||||
|
||||
import type { AvailabilityWithRoomInfo } from "@/contexts/SelectRate/types"
|
||||
|
||||
export interface RatesProps {
|
||||
roomConfiguration: AvailabilityWithRoomInfo
|
||||
roomIndex: number
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { SelectedRate } from "@scandic-hotels/booking-flow/types/stores/rates"
|
||||
import type {
|
||||
CorporateChequeProduct,
|
||||
PriceProduct,
|
||||
VoucherProduct,
|
||||
} from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type { SelectedRate } from "@/types/stores/rates"
|
||||
|
||||
export function isSelectedPriceProduct(
|
||||
product: PriceProduct,
|
||||
selectedRate: SelectedRate | null,
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import { memo } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { IconForFeatureCode } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import ImageGallery from "@scandic-hotels/design-system/ImageGallery"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { getHotelRoom } from "@scandic-hotels/trpc/routers/booking/helpers"
|
||||
|
||||
import { IconForFeatureCode } from "@/components/HotelReservation/utils"
|
||||
import RoomDetailsSidePeek from "@/components/SidePeeks/RoomDetailsSidePeek"
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
||||
|
||||
import styles from "./image.module.css"
|
||||
|
||||
@@ -8,10 +8,9 @@ import RoomNotAvailable from "./RoomNotAvailable"
|
||||
|
||||
import styles from "./roomListItem.module.css"
|
||||
|
||||
import type { AvailabilityWithRoomInfo } from "@scandic-hotels/booking-flow/contexts/SelectRate/types"
|
||||
import type { Package } from "@scandic-hotels/trpc/types/packages"
|
||||
|
||||
import type { AvailabilityWithRoomInfo } from "@/contexts/SelectRate/types"
|
||||
|
||||
export type RoomListItemProps = {
|
||||
room: AvailabilityWithRoomInfo
|
||||
selectedPackages: Package[]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client"
|
||||
import { useEffect } from "react"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import styles from "./rooms.module.css"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use client"
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import { RoomListItem } from "./RoomListItem"
|
||||
import { RoomsListSkeleton } from "./RoomsListSkeleton"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import { MultiRoomWrapper } from "./MultiRoomWrapper"
|
||||
import NoAvailabilityAlert from "./NoAvailabilityAlert"
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
import { TRPCClientError } from "@trpc/client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useSelectRateContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/SelectRateContext"
|
||||
import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
||||
import { Alert } from "@scandic-hotels/design-system/Alert"
|
||||
|
||||
import { useSelectRateContext } from "@/contexts/SelectRate/SelectRateContext"
|
||||
|
||||
import { RateSummary } from "./RateSummary"
|
||||
import Rooms from "./Rooms"
|
||||
import { RoomsContainerSkeleton } from "./RoomsContainerSkeleton"
|
||||
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
|
||||
import type { Room } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import type { Child } from "@scandic-hotels/trpc/types/child"
|
||||
|
||||
import type { Room } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
|
||||
type ChildrenInRoom = (Child[] | null)[] | null
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { cookies } from "next/headers"
|
||||
|
||||
import FnFNotAllowedAlert from "@scandic-hotels/booking-flow/components/FnFNotAllowedAlert"
|
||||
import { HotelDetailsSidePeek } from "@scandic-hotels/booking-flow/components/HotelDetailsSidePeek"
|
||||
import { hasOverlappingDates } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAndFriends"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { HotelInfoCard } from "@scandic-hotels/design-system/HotelInfoCard"
|
||||
@@ -10,14 +11,12 @@ import { RoomsContainer } from "@/components/HotelReservation/SelectRate/RoomsCo
|
||||
import { getIntl } from "@/i18n"
|
||||
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
||||
|
||||
import { hasOverlappingDates } from "../utils"
|
||||
import AvailabilityError from "./AvailabilityError"
|
||||
import Tracking from "./Tracking"
|
||||
|
||||
import type { SelectRateBooking } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
|
||||
import type { RouterOutput } from "@scandic-hotels/trpc/client"
|
||||
|
||||
import type { SelectRateBooking } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
|
||||
export default async function SelectRatePage({
|
||||
booking,
|
||||
hotelData,
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
|
||||
import { filterOverlappingDates } from "./index"
|
||||
|
||||
import type { specialAlertsSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/hotel/specialAlerts"
|
||||
import type { z } from "zod"
|
||||
|
||||
type Alert = z.infer<typeof specialAlertsSchema>[number]
|
||||
|
||||
function makeAlert(start: string, end: string): Alert {
|
||||
return {
|
||||
id: "test-id",
|
||||
name: "Test Alert",
|
||||
heading: "Test Heading",
|
||||
text: "Some text",
|
||||
type: AlertTypeEnum.Alarm,
|
||||
displayInBookingFlow: true,
|
||||
startDate: start,
|
||||
endDate: end,
|
||||
}
|
||||
}
|
||||
|
||||
describe("filterOverlappingDates", () => {
|
||||
const alert = makeAlert("2025-09-01", "2025-09-10")
|
||||
|
||||
it("shows alert if booking starts inside alert", () => {
|
||||
const result = filterOverlappingDates(
|
||||
[alert],
|
||||
dt("2025-09-05"),
|
||||
dt("2025-09-12")
|
||||
)
|
||||
expect(result).toHaveLength(1)
|
||||
})
|
||||
|
||||
it("shows alert if booking ends inside alert", () => {
|
||||
const result = filterOverlappingDates(
|
||||
[alert],
|
||||
dt("2025-08-28"),
|
||||
dt("2025-09-05")
|
||||
)
|
||||
expect(result).toHaveLength(1)
|
||||
})
|
||||
|
||||
it("shows alert if booking fully contains alert", () => {
|
||||
const result = filterOverlappingDates(
|
||||
[alert],
|
||||
dt("2025-08-28"),
|
||||
dt("2025-09-15")
|
||||
)
|
||||
expect(result).toHaveLength(1)
|
||||
})
|
||||
|
||||
it("shows alert if alert fully contains booking", () => {
|
||||
const result = filterOverlappingDates(
|
||||
[alert],
|
||||
dt("2025-09-03"),
|
||||
dt("2025-09-05")
|
||||
)
|
||||
expect(result).toHaveLength(1)
|
||||
})
|
||||
|
||||
it("does not show alert if no overlap", () => {
|
||||
const result = filterOverlappingDates(
|
||||
[alert],
|
||||
dt("2025-08-01"),
|
||||
dt("2025-08-05")
|
||||
)
|
||||
expect(result).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
@@ -1,143 +0,0 @@
|
||||
import { type Dayjs, dt } from "@scandic-hotels/common/dt"
|
||||
import {
|
||||
MaterialIcon,
|
||||
type MaterialIconSetIconProps,
|
||||
} from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
||||
import { ChildBedTypeEnum } from "@scandic-hotels/trpc/enums/childBedTypeEnum"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
|
||||
import type { Package, Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { type RoomPackageCodes } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
|
||||
interface IconForFeatureCodeProps {
|
||||
featureCode: RoomPackageCodes
|
||||
}
|
||||
export function IconForFeatureCode({
|
||||
featureCode,
|
||||
...props
|
||||
}: IconForFeatureCodeProps & MaterialIconSetIconProps): JSX.Element {
|
||||
switch (featureCode) {
|
||||
case RoomPackageCodeEnum.ACCESSIBILITY_ROOM:
|
||||
return <MaterialIcon icon="accessible" {...props} />
|
||||
case RoomPackageCodeEnum.ALLERGY_ROOM:
|
||||
return <MaterialIcon icon="mode_fan" {...props} />
|
||||
case RoomPackageCodeEnum.PET_ROOM:
|
||||
default:
|
||||
return <MaterialIcon icon="pets" {...props} />
|
||||
}
|
||||
}
|
||||
|
||||
export const invertedBedTypeMap: Record<ChildBedTypeEnum, string> = {
|
||||
[ChildBedTypeEnum.ParentsBed]: ChildBedMapEnum[ChildBedMapEnum.IN_ADULTS_BED],
|
||||
[ChildBedTypeEnum.Crib]: ChildBedMapEnum[ChildBedMapEnum.IN_CRIB],
|
||||
[ChildBedTypeEnum.ExtraBed]: ChildBedMapEnum[ChildBedMapEnum.IN_EXTRA_BED],
|
||||
[ChildBedTypeEnum.Unknown]: ChildBedMapEnum[ChildBedMapEnum.UNKNOWN],
|
||||
}
|
||||
|
||||
export function sumPackages(
|
||||
packages: Pick<Package, "localPrice">[] | undefined | null
|
||||
) {
|
||||
if (!packages || !packages.length) {
|
||||
return {
|
||||
currency: undefined,
|
||||
price: 0,
|
||||
}
|
||||
}
|
||||
return packages.reduce(
|
||||
(total, pkg) => {
|
||||
total.price = total.price + pkg.localPrice.totalPrice
|
||||
return total
|
||||
},
|
||||
{
|
||||
currency: packages[0].localPrice.currency,
|
||||
price: 0,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function sumPackagesRequestedPrice(packages: Packages | null) {
|
||||
if (!packages || !packages.length) {
|
||||
return {
|
||||
currency: undefined,
|
||||
price: 0,
|
||||
}
|
||||
}
|
||||
return packages.reduce(
|
||||
(total, pkg) => {
|
||||
total.price = total.price + pkg.requestedPrice.totalPrice
|
||||
return total
|
||||
},
|
||||
{
|
||||
currency: packages[0].requestedPrice.currency,
|
||||
price: 0,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function calculateVat(priceInclVat: number, vat: number) {
|
||||
const vatPercentage = vat / 100
|
||||
const priceExclVat = priceInclVat / (1 + vatPercentage)
|
||||
const vatAmount = priceInclVat - priceExclVat
|
||||
return {
|
||||
priceExclVat,
|
||||
vatAmount,
|
||||
}
|
||||
}
|
||||
|
||||
export function filterOverlappingDates<
|
||||
T extends {
|
||||
startDate: Date | Dayjs | string | undefined | null
|
||||
endDate: Date | Dayjs | string | undefined | null
|
||||
},
|
||||
>(dateRangeItems: T[], fromDate: Date | Dayjs, toDate: Date | Dayjs) {
|
||||
const startDate = dt(fromDate)
|
||||
const endDate = dt(toDate)
|
||||
|
||||
return dateRangeItems.filter((item) =>
|
||||
hasOverlappingDates(item, startDate, endDate)
|
||||
)
|
||||
}
|
||||
|
||||
export function hasOverlappingDates(
|
||||
dateRangeItem: {
|
||||
startDate: Date | Dayjs | string | undefined | null
|
||||
endDate: Date | Dayjs | string | undefined | null
|
||||
},
|
||||
fromDate: Date | Dayjs,
|
||||
toDate: Date | Dayjs
|
||||
) {
|
||||
const startDate = dt(fromDate)
|
||||
const endDate = dt(toDate)
|
||||
|
||||
if (dateRangeItem.endDate && dateRangeItem.startDate) {
|
||||
const itemStartDate = dt(dateRangeItem.startDate)
|
||||
const itemEndDate = dt(dateRangeItem.endDate)
|
||||
|
||||
const fromDateIsBetweenItemDates = startDate.isBetween(
|
||||
itemStartDate,
|
||||
itemEndDate,
|
||||
"date",
|
||||
"[]"
|
||||
)
|
||||
const toDateIsBetweenItemDates = endDate.isBetween(
|
||||
itemStartDate,
|
||||
itemEndDate,
|
||||
"date",
|
||||
"[]"
|
||||
)
|
||||
|
||||
const itemFullyContained =
|
||||
startDate.isSameOrBefore(itemStartDate, "date") &&
|
||||
endDate.isSameOrAfter(itemEndDate, "date")
|
||||
|
||||
return (
|
||||
fromDateIsBetweenItemDates ||
|
||||
toDateIsBetweenItemDates ||
|
||||
itemFullyContained
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { changeOrCancelDateFormat } from "@scandic-hotels/common/constants/dateFormats"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||
@@ -16,7 +17,6 @@ import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
import GuestDetails from "@/components/HotelReservation/MyStay/GuestDetails"
|
||||
import PriceType from "@/components/HotelReservation/MyStay/PriceType"
|
||||
import { hasModifiableRate } from "@/components/HotelReservation/MyStay/utils"
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
import { getFeatureDescription } from "@/components/HotelReservation/utils/getRoomFeatureDescription"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
|
||||
import { useSelectRateContext } from "./SelectRateContext"
|
||||
import { type SelectRateContext } from "./types"
|
||||
|
||||
export function DebugButton() {
|
||||
const context = useSelectRateContext()
|
||||
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
return null
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
const allRoomAvailability = getAllRoomAvailability(context)
|
||||
const allRoomPackages = getAllRoomPackages(context)
|
||||
console.log("%c SelectRateContext: ", "background: #AD0015; color: #FFF", {
|
||||
...context,
|
||||
...allRoomAvailability,
|
||||
...allRoomPackages,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
style={{
|
||||
position: "fixed",
|
||||
bottom: "1rem",
|
||||
right: "4rem",
|
||||
background: "green",
|
||||
border: "none",
|
||||
padding: "0.5rem 1rem",
|
||||
zIndex: 1000,
|
||||
}}
|
||||
onClick={handleClick}
|
||||
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
|
||||
>
|
||||
DEBUG
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
function getAllRoomAvailability(context: SelectRateContext) {
|
||||
const obj: Record<
|
||||
string,
|
||||
ReturnType<SelectRateContext["getAvailabilityForRoom"]> | null
|
||||
> = {}
|
||||
for (let i = 0; i < context.input.roomCount; i++) {
|
||||
const key = `getAvailabilityForRoom(${i})`
|
||||
const availability = context.getAvailabilityForRoom(i) ?? null
|
||||
obj[key] = availability
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
function getAllRoomPackages(context: SelectRateContext) {
|
||||
const obj: Record<
|
||||
string,
|
||||
ReturnType<SelectRateContext["getPackagesForRoom"]> | null
|
||||
> = {}
|
||||
for (let i = 0; i < context.input.roomCount; i++) {
|
||||
const key = `getPackagesForRoom(${i})`
|
||||
const availability = context.getPackagesForRoom(i) ?? null
|
||||
obj[key] = availability
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { createContext, useContext } from "react"
|
||||
|
||||
import type { RoomContextValue } from "@/types/contexts/select-rate/room"
|
||||
|
||||
export const RoomContext = createContext<RoomContextValue | null>(null)
|
||||
|
||||
export function useRoomContext() {
|
||||
const ctx = useContext(RoomContext)
|
||||
if (!ctx) {
|
||||
throw new Error("Missing context value [RoomContext]")
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
@@ -1,579 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { usePathname, useSearchParams } from "next/navigation"
|
||||
import { parseAsInteger, useQueryState } from "nuqs"
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react"
|
||||
import { type IntlShape, useIntl } from "react-intl"
|
||||
|
||||
import { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import {
|
||||
parseSelectRateSearchParams,
|
||||
searchParamsToRecord,
|
||||
serializeBookingSearchParams,
|
||||
} from "@scandic-hotels/booking-flow/utils/url"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
import { type RouterOutput, trpc } from "@scandic-hotels/trpc/client"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
|
||||
import { selectRateRoomsAvailabilityInputSchema } from "@scandic-hotels/trpc/routers/hotels/input"
|
||||
|
||||
import { useIsUserLoggedIn } from "@/hooks/useIsUserLoggedIn"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import { clearRooms } from "./clearRooms"
|
||||
import { DebugButton } from "./DebugButton"
|
||||
import { findUnavailableSelectedRooms } from "./findUnavailableSelectedRooms"
|
||||
import { getSelectedPackages } from "./getSelectedPackages"
|
||||
import { getTotalPrice, type Price } from "./getTotalPrice"
|
||||
import { includeRoomInfo } from "./includeRoomInfo"
|
||||
import { isRateSelected as isRateSelected_Inner } from "./isRateSelected"
|
||||
|
||||
import type { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
|
||||
|
||||
import type { SelectRateBooking } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
import type {
|
||||
AvailabilityWithRoomInfo,
|
||||
DefaultRoomPackage,
|
||||
Rate,
|
||||
RoomPackage,
|
||||
SelectedRate,
|
||||
SelectRateContext,
|
||||
} from "./types"
|
||||
|
||||
const SelectRateContext = createContext<SelectRateContext>(
|
||||
{} as SelectRateContext
|
||||
)
|
||||
SelectRateContext.displayName = "SelectRateContext"
|
||||
|
||||
type SelectRateContextProps = {
|
||||
children: React.ReactNode
|
||||
hotelData: NonNullable<RouterOutput["hotel"]["get"]>
|
||||
}
|
||||
|
||||
export function SelectRateProvider({
|
||||
children,
|
||||
hotelData,
|
||||
}: SelectRateContextProps) {
|
||||
const lang = useLang()
|
||||
const searchParams = useSearchParams()
|
||||
const updateBooking = useUpdateBooking()
|
||||
const isUserLoggedIn = useIsUserLoggedIn()
|
||||
const intl = useIntl()
|
||||
|
||||
const [activeRoomIndex, setInternalActiveRoomIndex] = useQueryState<number>(
|
||||
"activeRoomIndex",
|
||||
parseAsInteger.withDefault(0)
|
||||
)
|
||||
|
||||
const [_bookingCodeFilter, setBookingCodeFilter] =
|
||||
useState<BookingCodeFilterEnum>(BookingCodeFilterEnum.Discounted)
|
||||
|
||||
const selectRateBooking = parseSelectRateSearchParams(
|
||||
searchParamsToRecord(searchParams)
|
||||
)
|
||||
|
||||
const selectRateInput = selectRateRoomsAvailabilityInputSchema.safeParse({
|
||||
booking: selectRateBooking,
|
||||
lang,
|
||||
})
|
||||
|
||||
const hotelId = selectRateInput.data?.booking.hotelId ?? hotelData.hotel.id
|
||||
|
||||
const hotelQuery = trpc.hotel.get.useQuery(
|
||||
{ hotelId: hotelId!, language: lang, isCardOnlyPayment: false },
|
||||
{ enabled: !!hotelId, initialData: hotelData, refetchOnWindowFocus: false }
|
||||
)
|
||||
|
||||
const availabilityQuery = trpc.hotel.availability.selectRate.rooms.useQuery(
|
||||
selectRateInput.data!,
|
||||
{
|
||||
retry(failureCount, error) {
|
||||
if (error.data?.code === "BAD_REQUEST") {
|
||||
return false
|
||||
}
|
||||
|
||||
return failureCount <= 2
|
||||
},
|
||||
enabled: selectRateInput.success,
|
||||
refetchOnWindowFocus: false,
|
||||
}
|
||||
)
|
||||
|
||||
const availablePackages: (DefaultRoomPackage | RoomPackage)[][] | undefined =
|
||||
useMemo(() => {
|
||||
const defaults = getDefaultRoomPackages(intl)
|
||||
|
||||
return availabilityQuery.data
|
||||
?.filter((x) => "packages" in x)
|
||||
.map((x) => {
|
||||
const p = x.packages.filter((x) => isRoomPackage(x))
|
||||
return [
|
||||
...p,
|
||||
...defaults.filter(
|
||||
(def) => !p.some((pkg) => pkg.code === def.code)
|
||||
),
|
||||
].sort((a, b) => a.description.localeCompare(b.description))
|
||||
})
|
||||
}, [availabilityQuery.data, intl])
|
||||
|
||||
const roomAvailability: (AvailabilityWithRoomInfo | null)[][] =
|
||||
useMemo(() => {
|
||||
return (
|
||||
availabilityQuery.data
|
||||
?.map((x, ix) => {
|
||||
if ("roomConfigurations" in x === false) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const { roomConfigurations } = x
|
||||
return includeRoomInfo({
|
||||
roomConfigurations,
|
||||
roomCategories: hotelQuery.data?.roomCategories ?? [],
|
||||
selectedPackages: getSelectedPackages(
|
||||
availablePackages?.at(ix),
|
||||
selectRateInput.data?.booking.rooms[ix]?.packages ?? []
|
||||
),
|
||||
})
|
||||
})
|
||||
.filter((x) => !!x) ?? []
|
||||
)
|
||||
}, [
|
||||
availabilityQuery.data,
|
||||
hotelQuery.data?.roomCategories,
|
||||
availablePackages,
|
||||
selectRateInput.data?.booking.rooms,
|
||||
])
|
||||
|
||||
const isRateSelected = useCallback(
|
||||
({
|
||||
roomIndex,
|
||||
rate,
|
||||
roomTypeCode,
|
||||
}: {
|
||||
roomIndex: number
|
||||
rate: Rate
|
||||
roomTypeCode: string | null | undefined
|
||||
}) => {
|
||||
const selectedRate = selectRateBooking?.rooms?.[roomIndex].rateCode
|
||||
const selectedRoomTypeCode =
|
||||
selectRateBooking?.rooms?.[roomIndex].roomTypeCode
|
||||
|
||||
const isSelected = isRateSelected_Inner({
|
||||
selectedRateCode: selectedRate,
|
||||
selectedRoomTypeCode,
|
||||
rate,
|
||||
roomTypeCode,
|
||||
})
|
||||
|
||||
return isSelected
|
||||
},
|
||||
[selectRateBooking?.rooms]
|
||||
)
|
||||
|
||||
const roomCount = selectRateInput.data?.booking?.rooms?.length ?? 1
|
||||
|
||||
const selectedRates: SelectedRate[] = useMemo(() => {
|
||||
return (selectRateBooking?.rooms ?? []).map((_, ix) => {
|
||||
const selectedRatesPerRoom = roomAvailability.at(ix)?.flatMap((room) => {
|
||||
if (!room) return undefined
|
||||
|
||||
const allRates: Rate[] = [
|
||||
...room.regular.map((reg) => ({
|
||||
...reg,
|
||||
type: "regular" as const,
|
||||
})),
|
||||
...room.campaign.map((camp) => ({
|
||||
...camp,
|
||||
type: "campaign" as const,
|
||||
})),
|
||||
...room.redemptions.map((red) => ({
|
||||
...red,
|
||||
type: "redemption" as const,
|
||||
})),
|
||||
...room.code.map((cod) => ({
|
||||
...cod,
|
||||
type: "code" as const,
|
||||
})),
|
||||
]
|
||||
|
||||
return allRates
|
||||
.map((rate) => ({
|
||||
...rate,
|
||||
roomInfo: room,
|
||||
isSelected: isRateSelected({
|
||||
roomIndex: ix,
|
||||
rate: rate,
|
||||
roomTypeCode: room.roomTypeCode,
|
||||
}),
|
||||
}))
|
||||
.filter((x) => x.isSelected)
|
||||
})
|
||||
|
||||
if (selectedRatesPerRoom && selectedRatesPerRoom.length > 1) {
|
||||
console.error(`Multiple selected rates found for room index ${ix}:`)
|
||||
}
|
||||
|
||||
const selectedRate = selectedRatesPerRoom?.at(0)
|
||||
return selectedRate
|
||||
})
|
||||
}, [selectRateBooking?.rooms, isRateSelected, roomAvailability])
|
||||
|
||||
const totalPrice = getTotalPrice({
|
||||
selectedRates: selectedRates.map((rate, ix) => ({
|
||||
rate,
|
||||
roomConfiguration: roomAvailability[ix]?.[0],
|
||||
})),
|
||||
useMemberPrices: isUserLoggedIn,
|
||||
})
|
||||
|
||||
const getPriceForRoom = useCallback(
|
||||
(roomIndex: number): Price | null => {
|
||||
if (roomIndex < 0 || roomIndex >= selectedRates.length) {
|
||||
console.warn("Room index out of bounds:", roomIndex)
|
||||
return null
|
||||
}
|
||||
|
||||
const rate = selectedRates[roomIndex]
|
||||
if (!rate) {
|
||||
return null
|
||||
}
|
||||
|
||||
return getTotalPrice({
|
||||
selectedRates: [
|
||||
{ rate, roomConfiguration: roomAvailability[roomIndex]?.[0] },
|
||||
],
|
||||
useMemberPrices: isUserLoggedIn,
|
||||
})
|
||||
},
|
||||
[selectedRates, roomAvailability, isUserLoggedIn]
|
||||
)
|
||||
|
||||
const setActiveRoomIndex = useCallback(
|
||||
(roomIndex: number | "deselect" | "next") => {
|
||||
if (roomIndex === "deselect" || roomIndex == "next") {
|
||||
if (roomCount === 1) {
|
||||
setInternalActiveRoomIndex(0)
|
||||
return
|
||||
}
|
||||
|
||||
const isLastRoom = activeRoomIndex >= roomCount - 1
|
||||
if (isLastRoom) {
|
||||
setInternalActiveRoomIndex(-1)
|
||||
return
|
||||
}
|
||||
|
||||
const nextRoomWithoutRate = selectedRates.findIndex((rate, ix) => {
|
||||
return ix !== activeRoomIndex && (!rate || !rate.isSelected)
|
||||
})
|
||||
|
||||
setInternalActiveRoomIndex(nextRoomWithoutRate)
|
||||
return
|
||||
}
|
||||
|
||||
if (roomIndex < 0 || roomIndex >= roomCount) {
|
||||
logger.warn("Room index out of bounds:", roomIndex)
|
||||
return
|
||||
}
|
||||
|
||||
setInternalActiveRoomIndex(roomIndex)
|
||||
},
|
||||
[roomCount, activeRoomIndex, setInternalActiveRoomIndex, selectedRates]
|
||||
)
|
||||
|
||||
const getPackagesForRoom: SelectRateContext["getPackagesForRoom"] = (
|
||||
roomIndex
|
||||
) => {
|
||||
const availableForRoom = availablePackages?.[roomIndex] ?? []
|
||||
const selectedPackages = getSelectedPackages(
|
||||
availableForRoom,
|
||||
selectRateInput.data?.booking.rooms[roomIndex]?.packages ?? []
|
||||
)
|
||||
|
||||
return {
|
||||
selectedPackages,
|
||||
availablePackages: availableForRoom,
|
||||
}
|
||||
}
|
||||
|
||||
const bookingCodeFilter =
|
||||
_bookingCodeFilter === BookingCodeFilterEnum.Discounted &&
|
||||
!selectRateInput.data?.booking.bookingCode
|
||||
? BookingCodeFilterEnum.All
|
||||
: _bookingCodeFilter
|
||||
|
||||
const roomAvailabilityWithAdjustedRoomCount: (AvailabilityWithRoomInfo | null)[][] =
|
||||
roomAvailability.map((availability, roomIndex) => {
|
||||
if (roomIndex === 0) {
|
||||
return availability
|
||||
}
|
||||
|
||||
return availability.map((room) => {
|
||||
if (!room) {
|
||||
return room
|
||||
}
|
||||
|
||||
const sameRoomTypeSelectedPreviouslyCount =
|
||||
selectRateBooking?.rooms
|
||||
.slice(0, roomIndex)
|
||||
.filter((x) => x.roomTypeCode === room.roomTypeCode).length ?? 0
|
||||
const newRoomsLeft = Math.max(
|
||||
room.roomsLeft - sameRoomTypeSelectedPreviouslyCount,
|
||||
0
|
||||
)
|
||||
|
||||
return {
|
||||
...room,
|
||||
roomsLeft: newRoomsLeft,
|
||||
status:
|
||||
newRoomsLeft === 0
|
||||
? AvailabilityEnum.NotAvailable
|
||||
: AvailabilityEnum.Available,
|
||||
} as typeof room
|
||||
})
|
||||
})
|
||||
|
||||
const roomIndexesToDeselect = findUnavailableSelectedRooms({
|
||||
selectedRates,
|
||||
roomAvailabilityWithAdjustedRoomCount,
|
||||
})
|
||||
|
||||
const cleared = clearRooms({
|
||||
selectRateBooking,
|
||||
roomIndexesToClear: roomIndexesToDeselect,
|
||||
})
|
||||
|
||||
if (cleared.hasUpdated) {
|
||||
updateBooking(cleared.selectRateBooking)
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectRateContext.Provider
|
||||
value={{
|
||||
hotel: {
|
||||
data: hotelQuery.data,
|
||||
isFetching: hotelQuery.isFetching,
|
||||
isError: hotelQuery.isError,
|
||||
isSuccess: hotelQuery.isSuccess,
|
||||
error: hotelQuery.error,
|
||||
},
|
||||
availability: {
|
||||
data: availabilityQuery.data,
|
||||
isFetching: availabilityQuery.isFetching,
|
||||
isError: availabilityQuery.isError,
|
||||
isSuccess: availabilityQuery.isSuccess,
|
||||
error: availabilityQuery.error,
|
||||
},
|
||||
isFetching: availabilityQuery.isFetching || hotelQuery.isFetching,
|
||||
isError: availabilityQuery.isError || hotelQuery.isError,
|
||||
isSuccess: availabilityQuery.isSuccess && hotelQuery.isSuccess,
|
||||
getAvailabilityForRoom: (roomIndex: number) =>
|
||||
getAvailabilityForRoom(
|
||||
roomIndex,
|
||||
roomAvailabilityWithAdjustedRoomCount
|
||||
),
|
||||
isRateSelected,
|
||||
getPackagesForRoom,
|
||||
bookingCodeFilter,
|
||||
input: {
|
||||
data: selectRateInput.data,
|
||||
hasError: !selectRateInput.success,
|
||||
nights: calculateNumberOfNights(
|
||||
selectRateInput.data?.booking.fromDate,
|
||||
selectRateInput.data?.booking.toDate
|
||||
),
|
||||
errorCode: selectRateInput.error?.errors[0].message,
|
||||
bookingCode: selectRateInput.data?.booking.bookingCode,
|
||||
roomCount: roomCount,
|
||||
isMultiRoom: roomCount > 1,
|
||||
},
|
||||
selectedRates: {
|
||||
vat: hotelQuery.data?.hotel.vat ?? 0,
|
||||
rates: selectedRates,
|
||||
totalPrice,
|
||||
getPriceForRoom,
|
||||
rateSelectedForRoom: (roomIndex: number) => {
|
||||
return !!selectedRates[roomIndex]
|
||||
},
|
||||
forRoom: (roomIndex: number) => {
|
||||
return selectedRates[roomIndex]
|
||||
},
|
||||
state:
|
||||
selectedRates.length === 0
|
||||
? "NONE_SELECTED"
|
||||
: selectedRates.every((x) => !!x)
|
||||
? "ALL_SELECTED"
|
||||
: "PARTIALLY_SELECTED",
|
||||
},
|
||||
activeRoomIndex: activeRoomIndex,
|
||||
actions: {
|
||||
setActiveRoom: setActiveRoomIndex,
|
||||
selectPackages: ({ roomIndex, packages }) => {
|
||||
const updatedRoom = selectRateBooking?.rooms?.[roomIndex]
|
||||
if (!updatedRoom) {
|
||||
console.error("No room found at index", roomIndex)
|
||||
// TODO: What to do here?
|
||||
return
|
||||
}
|
||||
|
||||
updatedRoom.packages = packages
|
||||
updateBooking(selectRateBooking)
|
||||
setActiveRoomIndex(roomIndex)
|
||||
},
|
||||
selectBookingCodeFilter: (filter: BookingCodeFilterEnum) => {
|
||||
setBookingCodeFilter(filter)
|
||||
},
|
||||
selectRate: ({
|
||||
roomIndex,
|
||||
rateCode,
|
||||
counterRateCode,
|
||||
roomTypeCode,
|
||||
bookingCode,
|
||||
}) => {
|
||||
const updatedRoom = selectRateBooking?.rooms?.[roomIndex]
|
||||
if (!updatedRoom) {
|
||||
console.error("No room found at index", roomIndex)
|
||||
// TODO: What to do here?
|
||||
return
|
||||
}
|
||||
|
||||
updatedRoom.rateCode = rateCode
|
||||
updatedRoom.roomTypeCode = roomTypeCode
|
||||
updatedRoom.counterRateCode = counterRateCode || null
|
||||
updatedRoom.bookingCode = bookingCode || null
|
||||
|
||||
updateBooking(selectRateBooking)
|
||||
setActiveRoomIndex("next")
|
||||
},
|
||||
removeBookingCode: () => {
|
||||
if (!selectRateInput.data) {
|
||||
return
|
||||
}
|
||||
|
||||
const clearedBooking: SelectRateBooking = {
|
||||
hotelId: selectRateInput.data.booking.hotelId,
|
||||
fromDate: selectRateInput.data.booking.fromDate,
|
||||
toDate: selectRateInput.data.booking.toDate,
|
||||
rooms: selectRateInput.data.booking.rooms.map((room) => ({
|
||||
...room,
|
||||
bookingCode: null,
|
||||
})),
|
||||
}
|
||||
|
||||
updateBooking(clearedBooking)
|
||||
setActiveRoomIndex(0)
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<DebugButton />
|
||||
</SelectRateContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useSelectRateContext = () => useContext(SelectRateContext)
|
||||
export const SelectRateConsumer = SelectRateContext.Consumer
|
||||
|
||||
const getDefaultRoomPackages = (intl: IntlShape): DefaultRoomPackage[] =>
|
||||
[
|
||||
{
|
||||
code: RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
|
||||
description: intl.formatMessage({
|
||||
defaultMessage: "Accessible room",
|
||||
}),
|
||||
},
|
||||
{
|
||||
code: RoomPackageCodeEnum.ALLERGY_ROOM,
|
||||
description: intl.formatMessage({
|
||||
defaultMessage: "Allergy-friendly room",
|
||||
}),
|
||||
},
|
||||
{
|
||||
code: RoomPackageCodeEnum.PET_ROOM,
|
||||
description: intl.formatMessage({
|
||||
defaultMessage: "Pet-friendly room",
|
||||
}),
|
||||
},
|
||||
].map((pkg) => ({
|
||||
...pkg,
|
||||
type: "default",
|
||||
localPrice: { currency: CurrencyEnum.Unknown, price: 0, totalPrice: 0 },
|
||||
requestedPrice: { currency: CurrencyEnum.Unknown, price: 0, totalPrice: 0 },
|
||||
itemCode: "",
|
||||
inventories: [],
|
||||
}))
|
||||
|
||||
function calculateNumberOfNights(
|
||||
fromDate: string | Date | undefined,
|
||||
toDate: string | Date | undefined
|
||||
): number {
|
||||
if (!fromDate || !toDate) return 0
|
||||
|
||||
return dt(toDate).diff(dt(fromDate), "day")
|
||||
}
|
||||
|
||||
function getAvailabilityForRoom(
|
||||
roomIndex: number,
|
||||
roomAvailability: (AvailabilityWithRoomInfo | null)[][] | undefined
|
||||
): AvailabilityWithRoomInfo[] | undefined {
|
||||
if (
|
||||
!roomAvailability ||
|
||||
roomIndex < 0 ||
|
||||
roomIndex >= roomAvailability.length
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return roomAvailability[roomIndex]
|
||||
?.filter((x) => !!x)
|
||||
.sort((a, b) => {
|
||||
if (!a || !b) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (
|
||||
a.status === AvailabilityEnum.NotAvailable &&
|
||||
b.status !== AvailabilityEnum.NotAvailable
|
||||
) {
|
||||
return 1
|
||||
}
|
||||
|
||||
if (
|
||||
a.status !== AvailabilityEnum.NotAvailable &&
|
||||
b.status === AvailabilityEnum.NotAvailable
|
||||
) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
})
|
||||
}
|
||||
|
||||
function useUpdateBooking() {
|
||||
const pathname = usePathname()
|
||||
|
||||
return function updateBooking(booking: SelectRateBooking) {
|
||||
const newUrl = new URL(pathname, window.location.origin)
|
||||
|
||||
// TODO: Handle existing search params
|
||||
newUrl.search = serializeBookingSearchParams(booking).toString()
|
||||
// router.replace(newUrl.toString(), { scroll: false })
|
||||
window.history.replaceState({}, "", newUrl.toString())
|
||||
}
|
||||
}
|
||||
|
||||
function isRoomPackage(x: {
|
||||
code: BreakfastPackageEnum | RoomPackageCodeEnum
|
||||
}): x is { code: RoomPackageCodeEnum } {
|
||||
return Object.values(RoomPackageCodeEnum).includes(
|
||||
x.code as RoomPackageCodeEnum
|
||||
)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { SelectRateBooking } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
|
||||
/**
|
||||
* Removes room data
|
||||
*/
|
||||
export function clearRooms({
|
||||
selectRateBooking,
|
||||
roomIndexesToClear,
|
||||
}: {
|
||||
selectRateBooking: SelectRateBooking | null
|
||||
roomIndexesToClear: number[]
|
||||
}):
|
||||
| { selectRateBooking: SelectRateBooking; hasUpdated: true }
|
||||
| { hasUpdated: false } {
|
||||
if (!selectRateBooking || roomIndexesToClear.length === 0) {
|
||||
return { hasUpdated: false }
|
||||
}
|
||||
|
||||
roomIndexesToClear.forEach((roomIndex) => {
|
||||
if (!selectRateBooking?.rooms?.[roomIndex]) {
|
||||
return
|
||||
}
|
||||
|
||||
selectRateBooking.rooms[roomIndex].roomTypeCode = null
|
||||
selectRateBooking.rooms[roomIndex].rateCode = null
|
||||
selectRateBooking.rooms[roomIndex].counterRateCode = null
|
||||
selectRateBooking.rooms[roomIndex].bookingCode = null
|
||||
})
|
||||
|
||||
return { hasUpdated: true, selectRateBooking: selectRateBooking }
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
|
||||
|
||||
import type { AvailabilityWithRoomInfo, SelectedRate } from "./types"
|
||||
|
||||
export function findUnavailableSelectedRooms({
|
||||
selectedRates,
|
||||
roomAvailabilityWithAdjustedRoomCount,
|
||||
}: {
|
||||
selectedRates: SelectedRate[]
|
||||
roomAvailabilityWithAdjustedRoomCount: (AvailabilityWithRoomInfo | null)[][]
|
||||
}): number[] {
|
||||
const roomIndexesToDeselect: number[] = []
|
||||
for (let roomIndex = selectedRates.length - 1; roomIndex >= 0; roomIndex--) {
|
||||
const rate = selectedRates[roomIndex]
|
||||
if (!rate) continue
|
||||
|
||||
const room = roomAvailabilityWithAdjustedRoomCount[roomIndex].find(
|
||||
(x) => x?.roomTypeCode === rate.roomInfo.roomTypeCode
|
||||
)
|
||||
|
||||
if (!room || room.status === AvailabilityEnum.NotAvailable) {
|
||||
roomIndexesToDeselect.push(roomIndex)
|
||||
}
|
||||
}
|
||||
return roomIndexesToDeselect
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
|
||||
import { getSelectedPackages } from "./getSelectedPackages"
|
||||
|
||||
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
|
||||
|
||||
import type { Package } from "./types"
|
||||
|
||||
type FirstParameter = Parameters<typeof getSelectedPackages>[0]
|
||||
|
||||
const localPrice = { currency: CurrencyEnum.Unknown, price: 0, totalPrice: 0 }
|
||||
describe("getSelectedPackages", () => {
|
||||
const availablePackages: Partial<Package>[] = [
|
||||
{
|
||||
code: "PKG1" as PackageEnum,
|
||||
localPrice,
|
||||
},
|
||||
{ code: "PKG2" as PackageEnum, localPrice },
|
||||
{ code: "PKG3" as PackageEnum, localPrice },
|
||||
]
|
||||
|
||||
it("returns empty array if availablePackages is undefined", () => {
|
||||
const result = getSelectedPackages(undefined, ["PKG1" as PackageEnum])
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
||||
it("returns empty array if selectedPackages is undefined", () => {
|
||||
// @ts-expect-error testing undefined
|
||||
const result = getSelectedPackages(availablePackages, undefined)
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
||||
it("returns empty array if selectedPackages is empty", () => {
|
||||
const result = getSelectedPackages(availablePackages as FirstParameter, [])
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
||||
it("returns only the selected packages", () => {
|
||||
const result = getSelectedPackages(availablePackages as FirstParameter, [
|
||||
"PKG1" as PackageEnum,
|
||||
"PKG3" as PackageEnum,
|
||||
])
|
||||
expect(result).toMatchObject([{ code: "PKG1" }, { code: "PKG3" }])
|
||||
})
|
||||
|
||||
it("returns empty array if no selectedPackages match", () => {
|
||||
const result = getSelectedPackages(availablePackages as FirstParameter, [
|
||||
"PKG4" as PackageEnum,
|
||||
])
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
||||
it("returns all packages if all are selected", () => {
|
||||
const result = getSelectedPackages(availablePackages as FirstParameter, [
|
||||
"PKG1" as PackageEnum,
|
||||
"PKG2" as PackageEnum,
|
||||
"PKG3" as PackageEnum,
|
||||
])
|
||||
expect(result).toMatchObject(availablePackages)
|
||||
})
|
||||
|
||||
it("handles duplicate selectedPackages gracefully", () => {
|
||||
const result = getSelectedPackages(availablePackages as FirstParameter, [
|
||||
"PKG1" as PackageEnum,
|
||||
"PKG1" as PackageEnum,
|
||||
"PKG2" as PackageEnum,
|
||||
])
|
||||
expect(result).toMatchObject([{ code: "PKG1" }, { code: "PKG2" }])
|
||||
})
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
|
||||
|
||||
import type { DefaultRoomPackage, RoomPackage } from "./types"
|
||||
|
||||
export function getSelectedPackages(
|
||||
availablePackages: (DefaultRoomPackage | RoomPackage)[] | undefined,
|
||||
selectedPackages: PackageEnum[]
|
||||
): RoomPackage[] {
|
||||
if (
|
||||
!availablePackages ||
|
||||
!selectedPackages ||
|
||||
selectedPackages.length === 0
|
||||
) {
|
||||
return []
|
||||
}
|
||||
|
||||
return availablePackages.filter((pack) => {
|
||||
const isSelected = selectedPackages.some(
|
||||
(selected) => selected === pack.code
|
||||
)
|
||||
return isSelected
|
||||
})
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { getTotalPrice } from "./getTotalPrice"
|
||||
|
||||
describe("getTotalPrice", () => {
|
||||
it("should return null when no rates are selected", () => {
|
||||
const result = getTotalPrice({
|
||||
selectedRates: [],
|
||||
useMemberPrices: false,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
local: {
|
||||
currency: "Unknown",
|
||||
price: 0,
|
||||
regularPrice: undefined,
|
||||
},
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,273 +0,0 @@
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
|
||||
import type { RedemptionProduct } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type {
|
||||
AvailabilityWithRoomInfo,
|
||||
Rate,
|
||||
RoomPackage,
|
||||
} from "@/contexts/SelectRate/types"
|
||||
|
||||
type TPrice = {
|
||||
additionalPrice?: number
|
||||
additionalPriceCurrency?: CurrencyEnum
|
||||
currency: CurrencyEnum
|
||||
price: number
|
||||
regularPrice?: number
|
||||
}
|
||||
|
||||
export type Price = {
|
||||
requested?: TPrice
|
||||
local: TPrice
|
||||
}
|
||||
|
||||
type SelectedRate = {
|
||||
roomConfiguration: AvailabilityWithRoomInfo | null
|
||||
rate: Rate | undefined
|
||||
}
|
||||
|
||||
export function getTotalPrice({
|
||||
selectedRates,
|
||||
useMemberPrices,
|
||||
}: {
|
||||
selectedRates: Array<SelectedRate | null>
|
||||
useMemberPrices: boolean
|
||||
}): Price | null {
|
||||
const mainRoom = selectedRates[0]
|
||||
const mainRoomRate = mainRoom?.rate
|
||||
const summaryArray = selectedRates.filter(
|
||||
(x): x is OneLevelNonNullable<SelectedRate> => !!x
|
||||
)
|
||||
|
||||
if (summaryArray.some((rate) => "corporateCheque" in rate)) {
|
||||
return calculateCorporateChequePrice(summaryArray)
|
||||
}
|
||||
|
||||
if (!mainRoomRate) {
|
||||
return calculateTotalPrice(summaryArray, useMemberPrices)
|
||||
}
|
||||
|
||||
// In case of reward night (redemption) or voucher only single room booking is supported by business rules
|
||||
if ("redemption" in mainRoomRate) {
|
||||
return calculateRedemptionTotalPrice(
|
||||
mainRoomRate.redemption,
|
||||
mainRoom.roomConfiguration?.selectedPackages.filter(
|
||||
(pkg) => "localPrice" in pkg
|
||||
) ?? null
|
||||
)
|
||||
}
|
||||
if ("voucher" in mainRoomRate) {
|
||||
const voucherPrice = calculateVoucherPrice(summaryArray)
|
||||
return voucherPrice
|
||||
}
|
||||
|
||||
return calculateTotalPrice(summaryArray, useMemberPrices)
|
||||
}
|
||||
|
||||
function calculateTotalPrice(
|
||||
selectedRateSummary: OneLevelNonNullable<SelectedRate>[],
|
||||
useMemberPrices: boolean
|
||||
) {
|
||||
return selectedRateSummary.reduce<Price>(
|
||||
(total, room, idx) => {
|
||||
if (!room.rate || !("member" in room.rate) || !("public" in room.rate)) {
|
||||
return total
|
||||
}
|
||||
|
||||
const roomNr = idx + 1
|
||||
const isMainRoom = roomNr === 1
|
||||
|
||||
const useMemberRate = isMainRoom && useMemberPrices && room.rate.member
|
||||
const rate = useMemberRate ? room.rate.member : room.rate.public
|
||||
|
||||
if (!rate) {
|
||||
return total
|
||||
}
|
||||
|
||||
const packagesPrice = room.roomConfiguration?.selectedPackages.reduce(
|
||||
(total, pkg) => {
|
||||
total.local = total.local + pkg.localPrice.totalPrice
|
||||
if (pkg.requestedPrice.totalPrice) {
|
||||
total.requested = total.requested + pkg.requestedPrice.totalPrice
|
||||
}
|
||||
return total
|
||||
},
|
||||
{ local: 0, requested: 0 }
|
||||
)
|
||||
|
||||
total.local.currency = rate.localPrice.currency
|
||||
total.local.price =
|
||||
total.local.price + rate.localPrice.pricePerStay + packagesPrice.local
|
||||
|
||||
if (rate.localPrice.regularPricePerStay) {
|
||||
total.local.regularPrice =
|
||||
(total.local.regularPrice || 0) +
|
||||
rate.localPrice.regularPricePerStay +
|
||||
packagesPrice.local
|
||||
}
|
||||
|
||||
if (rate.requestedPrice) {
|
||||
if (!total.requested) {
|
||||
total.requested = {
|
||||
currency: rate.requestedPrice.currency,
|
||||
price: 0,
|
||||
}
|
||||
}
|
||||
|
||||
if (!total.requested.currency) {
|
||||
total.requested.currency = rate.requestedPrice.currency
|
||||
}
|
||||
|
||||
total.requested.price =
|
||||
total.requested.price +
|
||||
rate.requestedPrice.pricePerStay +
|
||||
packagesPrice.requested
|
||||
|
||||
if (rate.requestedPrice.regularPricePerStay) {
|
||||
total.requested.regularPrice =
|
||||
(total.requested.regularPrice || 0) +
|
||||
rate.requestedPrice.regularPricePerStay +
|
||||
packagesPrice.requested
|
||||
}
|
||||
}
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: CurrencyEnum.Unknown,
|
||||
price: 0,
|
||||
regularPrice: undefined,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function calculateRedemptionTotalPrice(
|
||||
redemption: RedemptionProduct["redemption"],
|
||||
packages: RoomPackage[] | null
|
||||
) {
|
||||
const pkgsSum = sumPackages(packages)
|
||||
let additionalPrice
|
||||
if (redemption.localPrice.additionalPricePerStay) {
|
||||
additionalPrice =
|
||||
redemption.localPrice.additionalPricePerStay + pkgsSum.price
|
||||
} else if (pkgsSum.price) {
|
||||
additionalPrice = pkgsSum.price
|
||||
}
|
||||
|
||||
let additionalPriceCurrency
|
||||
if (redemption.localPrice.currency) {
|
||||
additionalPriceCurrency = redemption.localPrice.currency
|
||||
} else if (pkgsSum.currency) {
|
||||
additionalPriceCurrency = pkgsSum.currency
|
||||
}
|
||||
return {
|
||||
local: {
|
||||
additionalPrice,
|
||||
additionalPriceCurrency,
|
||||
currency: CurrencyEnum.POINTS,
|
||||
price: redemption.localPrice.pointsPerStay,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function calculateVoucherPrice(
|
||||
selectedRateSummary: OneLevelNonNullable<SelectedRate>[]
|
||||
) {
|
||||
return selectedRateSummary.reduce<Price>(
|
||||
(total, room) => {
|
||||
if (!("voucher" in room.rate)) {
|
||||
return total
|
||||
}
|
||||
const rate = room.rate.voucher
|
||||
|
||||
total.local.price = total.local.price + rate.numberOfVouchers
|
||||
|
||||
const pkgsSum = sumPackages(room.roomConfiguration?.selectedPackages)
|
||||
if (pkgsSum.price && pkgsSum.currency) {
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) + pkgsSum.price
|
||||
total.local.additionalPriceCurrency = pkgsSum.currency
|
||||
}
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: CurrencyEnum.Voucher,
|
||||
price: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type OneLevelNonNullable<T> = {
|
||||
[K in keyof T]-?: NonNullable<T[K]>
|
||||
}
|
||||
|
||||
export function calculateCorporateChequePrice(
|
||||
selectedRates: OneLevelNonNullable<SelectedRate>[]
|
||||
) {
|
||||
return selectedRates.reduce<Price>(
|
||||
(total, room) => {
|
||||
if (!("corporateCheque" in room.rate)) {
|
||||
return total
|
||||
}
|
||||
|
||||
const rate = room.rate.corporateCheque
|
||||
const pkgsSum = sumPackages(
|
||||
selectedRates.flatMap((x) => x.roomConfiguration?.selectedPackages)
|
||||
)
|
||||
|
||||
total.local.price = total.local.price + rate.localPrice.numberOfCheques
|
||||
if (rate.localPrice.additionalPricePerStay) {
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) +
|
||||
rate.localPrice.additionalPricePerStay +
|
||||
pkgsSum.price
|
||||
} else if (pkgsSum.price) {
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) + pkgsSum.price
|
||||
}
|
||||
if (rate.localPrice.currency) {
|
||||
total.local.additionalPriceCurrency = rate.localPrice.currency
|
||||
}
|
||||
|
||||
if (rate.requestedPrice) {
|
||||
if (!total.requested) {
|
||||
total.requested = {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: 0,
|
||||
}
|
||||
}
|
||||
|
||||
total.requested.price =
|
||||
total.requested.price + rate.requestedPrice.numberOfCheques
|
||||
|
||||
if (rate.requestedPrice.additionalPricePerStay) {
|
||||
total.requested.additionalPrice =
|
||||
(total.requested.additionalPrice || 0) +
|
||||
rate.requestedPrice.additionalPricePerStay
|
||||
}
|
||||
|
||||
if (rate.requestedPrice.currency) {
|
||||
total.requested.additionalPriceCurrency = rate.requestedPrice.currency
|
||||
}
|
||||
}
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { includeRoomInfo } from "./includeRoomInfo"
|
||||
|
||||
import type { RoomCategory } from "@scandic-hotels/trpc/types/hotel"
|
||||
import type { RoomConfiguration } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
type DeepPartial<T> = T extends object
|
||||
? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>
|
||||
}
|
||||
: T
|
||||
|
||||
const mockRoomCategories: DeepPartial<RoomCategory>[] = [
|
||||
{
|
||||
id: "cat1",
|
||||
name: "Standard",
|
||||
roomTypes: [{ code: "STD" }, { code: "STD2" }],
|
||||
},
|
||||
{
|
||||
id: "cat2",
|
||||
name: "Deluxe",
|
||||
roomTypes: [{ code: "DLX" }],
|
||||
},
|
||||
]
|
||||
|
||||
describe("includeRoomInfo", () => {
|
||||
it("returns roomConfiguration with roomInfo when roomTypeCode matches", () => {
|
||||
const roomConfigurations: DeepPartial<RoomConfiguration>[] = [
|
||||
{ roomTypeCode: "STD" },
|
||||
{ roomTypeCode: "DLX" },
|
||||
]
|
||||
|
||||
const result = includeRoomInfo({
|
||||
roomConfigurations: roomConfigurations as RoomConfiguration[],
|
||||
roomCategories: mockRoomCategories as RoomCategory[],
|
||||
selectedPackages: [],
|
||||
})
|
||||
|
||||
expect(result[0]).toMatchObject({
|
||||
roomTypeCode: "STD",
|
||||
roomInfo: mockRoomCategories[0],
|
||||
})
|
||||
expect(result[1]).toMatchObject({
|
||||
roomTypeCode: "DLX",
|
||||
roomInfo: mockRoomCategories[1],
|
||||
})
|
||||
})
|
||||
|
||||
it("returns null when no matching roomTypeCode is found", () => {
|
||||
const roomConfigurations: DeepPartial<RoomConfiguration>[] = [
|
||||
{ roomTypeCode: "NOT_FOUND" },
|
||||
]
|
||||
|
||||
const result = includeRoomInfo({
|
||||
roomConfigurations: roomConfigurations as RoomConfiguration[],
|
||||
roomCategories: mockRoomCategories as RoomCategory[],
|
||||
selectedPackages: [],
|
||||
})
|
||||
|
||||
expect(result).toEqual([null])
|
||||
})
|
||||
|
||||
it("handles empty roomConfigurations", () => {
|
||||
const result = includeRoomInfo({
|
||||
roomConfigurations: [],
|
||||
roomCategories: mockRoomCategories as RoomCategory[],
|
||||
selectedPackages: [],
|
||||
})
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
|
||||
it("handles empty roomCategories", () => {
|
||||
const roomConfigurations: DeepPartial<RoomConfiguration>[] = [
|
||||
{ roomTypeCode: "STD" },
|
||||
]
|
||||
const result = includeRoomInfo({
|
||||
roomConfigurations: roomConfigurations as RoomConfiguration[],
|
||||
roomCategories: [],
|
||||
selectedPackages: [],
|
||||
})
|
||||
expect(result).toEqual([null])
|
||||
})
|
||||
|
||||
it("returns correct mapping for multiple configurations with mixed matches", () => {
|
||||
const roomConfigurations: DeepPartial<RoomConfiguration>[] = [
|
||||
{ roomTypeCode: "STD" },
|
||||
{ roomTypeCode: "DLX" },
|
||||
{ roomTypeCode: "UNKNOWN" },
|
||||
]
|
||||
const result = includeRoomInfo({
|
||||
roomConfigurations: roomConfigurations as RoomConfiguration[],
|
||||
roomCategories: mockRoomCategories as RoomCategory[],
|
||||
selectedPackages: [],
|
||||
})
|
||||
expect(result[0]).toMatchObject({
|
||||
roomTypeCode: "STD",
|
||||
roomInfo: mockRoomCategories[0],
|
||||
})
|
||||
expect(result[1]).toMatchObject({
|
||||
roomTypeCode: "DLX",
|
||||
roomInfo: mockRoomCategories[1],
|
||||
})
|
||||
expect(result[2]).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -1,39 +0,0 @@
|
||||
import type { RouterOutput } from "@scandic-hotels/trpc/client"
|
||||
|
||||
import type { AvailabilityWithRoomInfo, RoomPackage } from "./types"
|
||||
|
||||
type RoomConfiguration = Extract<
|
||||
RouterOutput["hotel"]["availability"]["selectRate"]["rooms"][number],
|
||||
{ roomConfigurations: unknown }
|
||||
>["roomConfigurations"][number]
|
||||
type RoomCategory = NonNullable<
|
||||
RouterOutput["hotel"]["get"]
|
||||
>["roomCategories"][number]
|
||||
|
||||
export function includeRoomInfo({
|
||||
roomConfigurations,
|
||||
roomCategories,
|
||||
selectedPackages,
|
||||
}: {
|
||||
roomConfigurations: RoomConfiguration[]
|
||||
roomCategories: RoomCategory[]
|
||||
selectedPackages: RoomPackage[]
|
||||
}): (AvailabilityWithRoomInfo | null)[] {
|
||||
return roomConfigurations.map((roomConfiguration) => {
|
||||
const room = roomCategories.find((roomCategory) =>
|
||||
roomCategory.roomTypes.find(
|
||||
(roomType) => roomType.code === roomConfiguration.roomTypeCode
|
||||
)
|
||||
)
|
||||
|
||||
if (!room) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
...roomConfiguration,
|
||||
roomInfo: room,
|
||||
selectedPackages,
|
||||
} satisfies AvailabilityWithRoomInfo
|
||||
})
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { RateEnum } from "@scandic-hotels/common/constants/rate"
|
||||
|
||||
import { isRateSelected } from "./isRateSelected"
|
||||
|
||||
describe("isRateSelected", () => {
|
||||
it("should return false when selectedRateCode is undefined", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: undefined,
|
||||
selectedRoomTypeCode: "ROOM1",
|
||||
rate: { public: { rateCode: "RATE1" } },
|
||||
roomTypeCode: "ROOM1",
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
it("should return false when selectedRoomTypeCode is null", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: "RATE1",
|
||||
selectedRoomTypeCode: null,
|
||||
rate: { public: { rateCode: "RATE1" } },
|
||||
roomTypeCode: "ROOM1",
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
it("should return false when rateCode is undefined", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: "RATE1",
|
||||
selectedRoomTypeCode: "ROOM1",
|
||||
rate: { public: { rateCode: undefined } },
|
||||
roomTypeCode: "ROOM1",
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
it("should return false when roomTypeCode is null", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: "RATE1",
|
||||
selectedRoomTypeCode: "ROOM1",
|
||||
rate: { public: { rateCode: "RATE1" } },
|
||||
roomTypeCode: null,
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
it("should return false when rate codes don't match", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: "RATE1",
|
||||
selectedRoomTypeCode: "ROOM1",
|
||||
rate: { public: { rateCode: "RATE2" } },
|
||||
roomTypeCode: "ROOM1",
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
it("should return false when room type codes don't match", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: "RATE1",
|
||||
selectedRoomTypeCode: "ROOM1",
|
||||
rate: { public: { rateCode: "RATE1" } },
|
||||
roomTypeCode: "ROOM2",
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
it("should return true when both rate code and room type code match", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: "RATE1",
|
||||
selectedRoomTypeCode: "ROOM1",
|
||||
rate: { public: { rateCode: "RATE1" } },
|
||||
roomTypeCode: "ROOM1",
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(true)
|
||||
})
|
||||
|
||||
it("should handle case insensitivity in rate codes", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: "RATE1",
|
||||
selectedRoomTypeCode: "ROOM1",
|
||||
rate: { public: { rateCode: "rate1" } },
|
||||
roomTypeCode: "ROOM1",
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(true)
|
||||
})
|
||||
|
||||
it("should handle case insensitivity in room type codes", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: "RATE1",
|
||||
selectedRoomTypeCode: "ROOM1",
|
||||
rate: { public: { rateCode: "RATE1" } },
|
||||
roomTypeCode: "room1",
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(true)
|
||||
})
|
||||
|
||||
it("should work with RateEnum values", () => {
|
||||
const result = isRateSelected({
|
||||
selectedRateCode: RateEnum.save,
|
||||
selectedRoomTypeCode: "ROOM1",
|
||||
rate: { public: { rateCode: RateEnum.save } },
|
||||
roomTypeCode: "ROOM1",
|
||||
} as any)
|
||||
|
||||
expect(result).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
import type { RateEnum } from "@scandic-hotels/common/constants/rate"
|
||||
|
||||
import type { Rate } from "./types"
|
||||
|
||||
export function isRateSelected({
|
||||
selectedRateCode,
|
||||
selectedRoomTypeCode,
|
||||
rate,
|
||||
roomTypeCode,
|
||||
}: {
|
||||
selectedRateCode: RateEnum | string | undefined | null
|
||||
selectedRoomTypeCode: RateEnum | string | undefined | null
|
||||
rate: Rate
|
||||
roomTypeCode: string | null | undefined
|
||||
}) {
|
||||
if (!selectedRateCode || !selectedRoomTypeCode || !rate || !roomTypeCode) {
|
||||
return false
|
||||
}
|
||||
|
||||
let rateCodes: string[] = []
|
||||
if ("public" in rate && rate.public) {
|
||||
rateCodes = [...rateCodes, rate.public.rateCode?.toLowerCase()]
|
||||
}
|
||||
if ("member" in rate && rate.member) {
|
||||
rateCodes = [...rateCodes, rate.member.rateCode?.toLowerCase()]
|
||||
}
|
||||
if ("redemption" in rate && rate.redemption) {
|
||||
rateCodes = [...rateCodes, rate.redemption.rateCode?.toLowerCase()]
|
||||
}
|
||||
if ("voucher" in rate && rate.voucher) {
|
||||
rateCodes = [...rateCodes, rate.voucher.rateCode?.toLowerCase()]
|
||||
}
|
||||
if ("corporateCheque" in rate && rate.corporateCheque) {
|
||||
rateCodes = [...rateCodes, rate.corporateCheque.rateCode?.toLowerCase()]
|
||||
}
|
||||
|
||||
return (
|
||||
rateCodes.includes(selectedRateCode.toLowerCase()) &&
|
||||
selectedRoomTypeCode.toLowerCase() === roomTypeCode.toLowerCase()
|
||||
)
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
import { type RouterOutput } from "@scandic-hotels/trpc/client"
|
||||
|
||||
import { type Price } from "./getTotalPrice"
|
||||
|
||||
import type { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import type { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
import type { RoomsAvailabilityOutputSchema } from "@scandic-hotels/trpc/types/availability"
|
||||
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
|
||||
import type { RoomConfiguration } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
export type SelectRateContext = {
|
||||
hotel: QueryData<RouterOutput["hotel"]["get"]>
|
||||
availability: QueryData<
|
||||
RouterOutput["hotel"]["availability"]["selectRate"]["rooms"]
|
||||
>
|
||||
input: {
|
||||
data: RoomsAvailabilityOutputSchema | undefined
|
||||
errorCode?: string
|
||||
hasError: boolean
|
||||
nights: number
|
||||
isMultiRoom: boolean
|
||||
roomCount: number
|
||||
bookingCode: string | undefined
|
||||
}
|
||||
|
||||
isFetching: boolean
|
||||
isError: boolean
|
||||
isSuccess: boolean
|
||||
|
||||
getAvailabilityForRoom: (
|
||||
roomIndex: number
|
||||
) => AvailabilityWithRoomInfo[] | undefined
|
||||
|
||||
getPackagesForRoom: (roomIndex: number) => {
|
||||
selectedPackages: RoomPackage[]
|
||||
availablePackages: (DefaultRoomPackage | RoomPackage)[]
|
||||
}
|
||||
|
||||
isRateSelected: (args: {
|
||||
roomIndex: number
|
||||
rate: Rate
|
||||
roomTypeCode: string
|
||||
}) => boolean
|
||||
|
||||
selectedRates: {
|
||||
vat: number
|
||||
rates: (
|
||||
| WithSelected<Rate & { roomInfo: AvailabilityWithRoomInfo }>
|
||||
| undefined
|
||||
)[]
|
||||
forRoom: (
|
||||
roomIndex: number
|
||||
) => WithSelected<Rate & { roomInfo: AvailabilityWithRoomInfo }> | undefined
|
||||
rateSelectedForRoom: (roomIndex: number) => boolean
|
||||
getPriceForRoom: (roomIndex: number) => Price | null
|
||||
totalPrice: Price | null
|
||||
state: "ALL_SELECTED" | "PARTIALLY_SELECTED" | "NONE_SELECTED"
|
||||
}
|
||||
|
||||
bookingCodeFilter: BookingCodeFilterEnum
|
||||
activeRoomIndex: number
|
||||
actions: {
|
||||
setActiveRoom: (roomIndex: number | "deselect" | "next") => void
|
||||
selectPackages: (args: {
|
||||
roomIndex: number
|
||||
packages: PackageEnum[]
|
||||
}) => void
|
||||
selectBookingCodeFilter: (filter: BookingCodeFilterEnum) => void
|
||||
selectRate: (args: {
|
||||
roomIndex: number
|
||||
rateCode: string
|
||||
counterRateCode?: string
|
||||
roomTypeCode: string
|
||||
bookingCode?: string
|
||||
}) => void
|
||||
removeBookingCode: () => void
|
||||
}
|
||||
}
|
||||
|
||||
type RegularRate = RoomConfiguration["regular"][number] & {
|
||||
type: "regular"
|
||||
}
|
||||
type CampaignRate = RoomConfiguration["campaign"][number] & {
|
||||
type: "campaign"
|
||||
}
|
||||
type RedemptionRate = RoomConfiguration["redemptions"][number] & {
|
||||
type: "redemption"
|
||||
}
|
||||
type CodeRate = RoomConfiguration["code"][number] & { type: "code" }
|
||||
|
||||
export type Rate = RegularRate | CampaignRate | RedemptionRate | CodeRate
|
||||
|
||||
type QueryData<T> = {
|
||||
data?: T
|
||||
isFetching: boolean
|
||||
isError: boolean
|
||||
isSuccess: boolean
|
||||
error: unknown
|
||||
}
|
||||
|
||||
type AvailabilityQueryData = QueryData<
|
||||
RouterOutput["hotel"]["availability"]["selectRate"]["rooms"]
|
||||
>["data"]
|
||||
|
||||
type HotelQueryData = QueryData<RouterOutput["hotel"]["get"]>["data"]
|
||||
|
||||
type WithSelected<T> = T & { isSelected: boolean }
|
||||
|
||||
export type RoomInfo = NonNullable<HotelQueryData>["roomCategories"][number]
|
||||
|
||||
export type AvailabilityWithRoomInfo = Extract<
|
||||
NonNullable<AvailabilityQueryData>[number],
|
||||
{ hotelId: number }
|
||||
>["roomConfigurations"][number] & {
|
||||
roomInfo: RoomInfo | undefined
|
||||
selectedPackages: RoomPackage[]
|
||||
}
|
||||
|
||||
export type SelectedRate =
|
||||
| WithSelected<Rate & { roomInfo: AvailabilityWithRoomInfo }>
|
||||
| undefined
|
||||
|
||||
export type Package = Extract<
|
||||
NonNullable<AvailabilityQueryData>[number],
|
||||
{ hotelId: number }
|
||||
>["packages"][number]
|
||||
|
||||
export type DefaultRoomPackage = RoomPackage & {
|
||||
type: "default"
|
||||
// code: RoomPackageCodeEnum
|
||||
// description: string
|
||||
}
|
||||
|
||||
export type RoomPackage = Package & { code: RoomPackageCodeEnum }
|
||||
@@ -1,13 +1,12 @@
|
||||
"use client"
|
||||
|
||||
import { RoomContext } from "@scandic-hotels/booking-flow/contexts/SelectRate/Room"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
|
||||
import { sortRoomConfigs } from "@scandic-hotels/trpc/utils/sortRoomConfigs"
|
||||
|
||||
import { useRatesStore } from "@/stores/select-rate"
|
||||
|
||||
import { RoomContext } from "@/contexts/SelectRate/Room"
|
||||
|
||||
import type { RoomProviderProps } from "@/types/providers/select-rate/room"
|
||||
|
||||
export default function RoomProvider({
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import isEqual from "fast-deep-equal"
|
||||
import { parsePhoneNumberFromString } from "libphonenumber-js"
|
||||
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { RateTypeEnum } from "@scandic-hotels/common/constants/rateType"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
|
||||
import {
|
||||
sumPackages,
|
||||
sumPackagesRequestedPrice,
|
||||
} from "@/components/HotelReservation/utils"
|
||||
} from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { RateTypeEnum } from "@scandic-hotels/common/constants/rateType"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
|
||||
import { detailsStorageName } from "."
|
||||
|
||||
import type { BookingWidgetSearchData } from "@scandic-hotels/booking-flow/BookingWidget"
|
||||
import type { SelectRateBooking } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
|
||||
import type { Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
import type {
|
||||
CorporateChequeProduct,
|
||||
@@ -24,7 +24,6 @@ import type {
|
||||
import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast"
|
||||
import { type RoomRate } from "@/types/components/hotelReservation/enterDetails/details"
|
||||
import type { Price } from "@/types/components/hotelReservation/price"
|
||||
import type { SelectRateBooking } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
import type { PersistedState, RoomState } from "@/types/stores/enter-details"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import type { AvailabilityError } from "@/types/stores/rates"
|
||||
|
||||
import type { AvailabilityError } from "@scandic-hotels/booking-flow/types/stores/rates"
|
||||
import type {
|
||||
Product,
|
||||
RoomConfiguration,
|
||||
|
||||
@@ -16,11 +16,13 @@ import {
|
||||
findSelectedRate,
|
||||
} from "./helpers"
|
||||
|
||||
import type {
|
||||
InitialState,
|
||||
RatesState,
|
||||
} from "@scandic-hotels/booking-flow/types/stores/rates"
|
||||
import type { Package, Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
import type { PriceProduct } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type { InitialState, RatesState } from "@/types/stores/rates"
|
||||
|
||||
export function createRatesStore({
|
||||
booking,
|
||||
hotelType,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { SelectRateBooking } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
|
||||
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
|
||||
|
||||
import type { SelectRateBooking } from "./selectRate"
|
||||
|
||||
export interface HotelInfoCardProps {
|
||||
booking: SelectRateBooking
|
||||
hotel: Hotel
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import type { packageSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/packages"
|
||||
import type { z } from "zod"
|
||||
|
||||
type RoomPackage = z.output<typeof packageSchema>
|
||||
export type RoomPackageCodes = RoomPackage["code"]
|
||||
@@ -1,50 +0,0 @@
|
||||
import type { BookingSearchType } from "@scandic-hotels/booking-flow/searchType"
|
||||
import type { RateEnum } from "@scandic-hotels/common/constants/rate"
|
||||
import type { Child } from "@scandic-hotels/trpc/types/child"
|
||||
import type { PackageEnum, Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
import type {
|
||||
Product,
|
||||
RoomConfiguration,
|
||||
} from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
export interface Room {
|
||||
adults: number
|
||||
childrenInRoom?: Child[]
|
||||
bookingCode?: string | null
|
||||
counterRateCode?: string | null
|
||||
packages?: PackageEnum[] | null
|
||||
rateCode?: string | null
|
||||
roomTypeCode?: string | null
|
||||
}
|
||||
|
||||
export type SelectRateBooking = {
|
||||
bookingCode?: string
|
||||
city?: string
|
||||
fromDate: string
|
||||
hotelId: string
|
||||
rooms: Room[]
|
||||
searchType?: BookingSearchType
|
||||
toDate: string
|
||||
}
|
||||
|
||||
export type Rate = {
|
||||
features: RoomConfiguration["features"]
|
||||
packages: NonNullable<Packages>
|
||||
priceName?: string
|
||||
priceTerm?: string
|
||||
product: Product
|
||||
rate: RateEnum
|
||||
roomRates?: {
|
||||
rate: Rate
|
||||
roomIndex: number
|
||||
}[]
|
||||
roomType: RoomConfiguration["roomType"]
|
||||
roomTypeCode: RoomConfiguration["roomTypeCode"]
|
||||
}
|
||||
|
||||
export type RateCode = {
|
||||
publicRateCode: string
|
||||
roomTypeCode: string
|
||||
name: string
|
||||
paymentTerm: string
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { SelectRateBooking } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
|
||||
import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import type { Child } from "@scandic-hotels/trpc/types/child"
|
||||
import type { Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
@@ -9,7 +10,6 @@ import type {
|
||||
RoomRate,
|
||||
} from "./enterDetails/details"
|
||||
import type { Price } from "./price"
|
||||
import type { SelectRateBooking } from "./selectRate/selectRate"
|
||||
|
||||
export type RoomsData = {
|
||||
rateDetails: string[] | undefined
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import type { Package } from "@scandic-hotels/trpc/types/packages"
|
||||
|
||||
import type { RatesState, SelectedRoom } from "@/types/stores/rates"
|
||||
|
||||
export interface RoomContextValue extends Omit<SelectedRoom, "actions"> {
|
||||
actions: SelectedRoom["actions"]
|
||||
isActiveRoom: boolean
|
||||
isFetchingAdditionalRate: boolean
|
||||
isMainRoom: boolean
|
||||
petRoomPackage: Package | undefined
|
||||
roomAvailability:
|
||||
| NonNullable<RatesState["roomsAvailability"]>[number]
|
||||
| undefined
|
||||
roomPackages: Package[]
|
||||
roomNr: number
|
||||
totalRooms: number
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { SelectRateBooking } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
|
||||
import type { AvailabilityError } from "@scandic-hotels/booking-flow/types/stores/rates"
|
||||
import type { Room } from "@scandic-hotels/trpc/types/hotel"
|
||||
import type { RoomsAvailability } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type { SelectRateBooking } from "../components/hotelReservation/selectRate/selectRate"
|
||||
import type { AvailabilityError } from "../stores/rates"
|
||||
|
||||
export interface RatesProviderProps extends React.PropsWithChildren {
|
||||
booking: SelectRateBooking
|
||||
hotelType: string | undefined
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SelectedRoom } from "@/types/stores/rates"
|
||||
import type { SelectedRoom } from "@scandic-hotels/booking-flow/types/stores/rates"
|
||||
|
||||
export interface RoomProviderProps extends React.PropsWithChildren {
|
||||
idx: number
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import type { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import type { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
import type { Room } from "@scandic-hotels/trpc/types/hotel"
|
||||
import type { Package, PackageEnum } from "@scandic-hotels/trpc/types/packages"
|
||||
import type {
|
||||
Product,
|
||||
RoomConfiguration,
|
||||
RoomsAvailability,
|
||||
} from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type {
|
||||
Rate,
|
||||
Room as RoomBooking,
|
||||
SelectRateBooking,
|
||||
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
|
||||
export interface AvailabilityError {
|
||||
details: string
|
||||
error: string
|
||||
}
|
||||
|
||||
interface Actions {
|
||||
appendRegularRates: (
|
||||
roomConfigurations: RoomConfiguration[] | undefined
|
||||
) => void
|
||||
closeSection: () => void
|
||||
modifyRate: () => void
|
||||
removeSelectedPackage: (code: PackageEnum) => void
|
||||
removeSelectedPackages: () => void
|
||||
selectFilter: (filter: BookingCodeFilterEnum) => void
|
||||
selectPackages: (codes: PackageEnum[]) => void
|
||||
selectRate: (rate: SelectedRate, isUserLoggedIn: boolean) => void
|
||||
updateRooms: (rooms: RoomConfiguration[] | undefined) => void
|
||||
}
|
||||
|
||||
export interface SelectedRate {
|
||||
features: RoomConfiguration["features"]
|
||||
product: Product
|
||||
roomType: RoomConfiguration["roomType"]
|
||||
roomTypeCode: RoomConfiguration["roomTypeCode"]
|
||||
}
|
||||
|
||||
export interface SelectedRoom {
|
||||
actions: Actions
|
||||
bookingRoom: RoomBooking
|
||||
isFetchingAdditionalRate: boolean
|
||||
isFetchingPackages: boolean
|
||||
rooms: RoomConfiguration[]
|
||||
selectedFilter: BookingCodeFilterEnum | undefined
|
||||
selectedPackages: Package[]
|
||||
selectedRate: SelectedRate | null
|
||||
}
|
||||
|
||||
interface DefaultFilterOptions {
|
||||
code: RoomPackageCodeEnum
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface RatesState {
|
||||
activeRoom: number
|
||||
booking: SelectRateBooking
|
||||
hotelType: string | undefined
|
||||
isRedemptionBooking: boolean
|
||||
packageOptions: DefaultFilterOptions[]
|
||||
rateSummary: Array<Rate | null>
|
||||
rooms: SelectedRoom[]
|
||||
roomCategories: Room[]
|
||||
roomConfigurations: RoomConfiguration[][]
|
||||
roomsPackages: Package[][]
|
||||
roomsAvailability: (RoomsAvailability | AvailabilityError)[] | undefined
|
||||
vat: number
|
||||
defaultCurrency: CurrencyEnum
|
||||
}
|
||||
|
||||
export interface InitialState
|
||||
extends Pick<
|
||||
RatesState,
|
||||
"booking" | "hotelType" | "roomCategories" | "roomsAvailability" | "vat"
|
||||
> {
|
||||
initialActiveRoom?: number
|
||||
pathname: string
|
||||
labels: {
|
||||
accessibilityRoom: string
|
||||
allergyRoom: string
|
||||
petRoom: string
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user