"use client" import { useSession } from "next-auth/react" import { createElement } from "react" import { useIntl } from "react-intl" import { useRatesStore } from "@/stores/select-rate" import ToggleSidePeek from "@/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek" import { getRates } from "@/components/HotelReservation/SelectRate/utils" import { getIconForFeatureCode } from "@/components/HotelReservation/utils" import { ErrorCircleIcon } from "@/components/Icons" import ImageGallery from "@/components/ImageGallery" import Caption from "@/components/TempDesignSystem/Text/Caption" import Footnote from "@/components/TempDesignSystem/Text/Footnote" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import { useRoomContext } from "@/contexts/Room" import { isValidClientSession } from "@/utils/clientSession" import { mapApiImagesToGalleryImages } from "@/utils/imageGallery" import { cardVariants } from "./cardVariants" import FlexibilityOption from "./FlexibilityOption" import RoomSize from "./RoomSize" import styles from "./roomCard.module.css" import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel" import type { RoomCardProps } from "@/types/components/hotelReservation/selectRate/roomCard" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" import { HotelTypeEnum } from "@/types/enums/hotelType" import type { Product } from "@/types/trpc/routers/hotel/roomAvailability" function getBreakfastMessage( publicBreakfastIncluded: boolean, memberBreakfastIncluded: boolean, hotelType: string | undefined, userIsLoggedIn: boolean, msgs: Record<"included" | "noSelection" | "scandicgo" | "notIncluded", string> ) { if (hotelType === HotelTypeEnum.ScandicGo) { return msgs.scandicgo } if (userIsLoggedIn && memberBreakfastIncluded) { return msgs.included } if (publicBreakfastIncluded && memberBreakfastIncluded) { return msgs.included } /** selected and rate does not include breakfast */ if (false) { return msgs.notIncluded } if (!publicBreakfastIncluded && !memberBreakfastIncluded) { return msgs.notIncluded } return msgs.noSelection } export default function RoomCard({ roomConfiguration }: RoomCardProps) { const { data: session } = useSession() const isUserLoggedIn = isValidClientSession(session) const intl = useIntl() const lessThanFiveRoomsLeft = roomConfiguration.roomsLeft > 0 && roomConfiguration.roomsLeft < 5 const { hotelId, hotelType, petRoomPackage, rateDefinitions, roomCategories, } = useRatesStore((state) => ({ hotelId: state.booking.hotelId, hotelType: state.hotelType, petRoomPackage: state.petRoomPackage, rateDefinitions: state.roomsAvailability?.rateDefinitions, roomCategories: state.roomCategories, })) const { selectedPackage, selectedRate } = useRoomContext() const classNames = cardVariants({ availability: roomConfiguration.status === AvailabilityEnum.NotAvailable ? "noAvailability" : "default", }) const breakfastMessages = { included: intl.formatMessage({ id: "Breakfast is included." }), notIncluded: intl.formatMessage({ id: "Breakfast selection in next step.", }), noSelection: intl.formatMessage({ id: "Select a rate" }), scandicgo: intl.formatMessage({ id: "Breakfast deal can be purchased at the hotel.", }), } const breakfastMessage = getBreakfastMessage( roomConfiguration.breakfastIncludedInAllRatesPublic, roomConfiguration.breakfastIncludedInAllRatesMember, hotelType, isUserLoggedIn, breakfastMessages ) if (!rateDefinitions) { return null } const rates = getRates(rateDefinitions) const petRoomPackageSelected = (selectedPackage === RoomPackageCodeEnum.PET_ROOM && petRoomPackage) || undefined const selectedRoom = roomCategories.find((roomCategory) => roomCategory.roomTypes.find( (roomType) => roomType.code === roomConfiguration.roomTypeCode ) ) const { name, roomSize, totalOccupancy, images } = selectedRoom || {} const galleryImages = mapApiImagesToGalleryImages(images || []) const freeCancelation = intl.formatMessage({ id: "Free cancellation" }) const nonRefundable = intl.formatMessage({ id: "Non-refundable" }) const freeBooking = intl.formatMessage({ id: "Free rebooking" }) const payLater = intl.formatMessage({ id: "Pay later" }) const payNow = intl.formatMessage({ id: "Pay now" }) function getRate(rateCode: string) { switch (rateCode) { case "change": return { isFlex: false, notAvailable: false, title: freeBooking, } case "flex": return { isFlex: true, notAvailable: false, title: freeCancelation, } case "save": return { isFlex: false, notAvailable: false, title: nonRefundable, } default: throw new Error( `Unknown key for rate, should be "change", "flex" or "save", but got ${rateCode}` ) } } function getRateInfo(product: Product) { if ( !product.productType.public.rateCode && !product.productType.member?.rateCode ) { const possibleRate = getRate(product.productType.public.rate) if (possibleRate) { return { ...possibleRate, notAvailable: true, } } return { isFlex: false, notAvailable: true, title: "", } } const publicRate = Object.keys(rates).find((k) => rates[k as keyof typeof rates].find( (a) => a.rateCode === product.productType.public.rateCode ) ) let memberRate if (product.productType.member) { memberRate = Object.keys(rates).find((k) => rates[k as keyof typeof rates].find( (a) => a.rateCode === product.productType.member!.rateCode ) ) } if (!publicRate || !memberRate) { throw new Error("We should never make it where without rateCodes") } const key = isUserLoggedIn ? memberRate : publicRate return getRate(key) } return (
  • {lessThanFiveRoomsLeft ? ( {intl.formatMessage( { id: "{amount, number} left" }, { amount: roomConfiguration.roomsLeft } )} ) : null} {roomConfiguration.features .filter((feature) => selectedPackage === feature.code) .map((feature) => ( {createElement(getIconForFeatureCode(feature.code), { color: "burgundy", height: 16, width: 16, })} ))}
    {totalOccupancy && ( {intl.formatMessage( { id: "Max {max, plural, one {{range} guest} other {{range} guests}}", }, { max: totalOccupancy.max, range: totalOccupancy.range } )} )}
    {roomConfiguration.roomTypeCode && ( )}
    {name} {/* Out of scope for now {descriptions?.short} */}
    {roomConfiguration.status === AvailabilityEnum.NotAvailable ? (
    {intl.formatMessage({ id: "This room is not available", })}
    ) : ( <> {breakfastMessage} {roomConfiguration.products.map((product) => { const rate = getRateInfo(product) const isSelectedRateCode = selectedRate?.product.productType.public.rateCode === product.productType.public.rateCode || selectedRate?.product.productType.member?.rateCode === product.productType.member?.rateCode return ( ) })} )}
  • ) }