"use client" import { useRouter } from "next/navigation" import { useState, useTransition } from "react" import { useIntl } from "react-intl" import { dt } from "@/lib/dt" import { useRatesStore } from "@/stores/select-rate" import SignupPromoDesktop from "@/components/HotelReservation/SignupPromo/Desktop" import SignupPromoMobile from "@/components/HotelReservation/SignupPromo/Mobile" import Button from "@/components/TempDesignSystem/Button" import Body from "@/components/TempDesignSystem/Text/Body" import Caption from "@/components/TempDesignSystem/Text/Caption" import Footnote from "@/components/TempDesignSystem/Text/Footnote" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import { formatPrice } from "@/utils/numberFormatting" import MobileSummary from "./MobileSummary" import { calculateCorporateChequePrice, calculateRedemptionTotalPrice, calculateTotalPrice, calculateVoucherPrice, } from "./utils" import styles from "./rateSummary.module.css" import type { Price } from "@/types/components/hotelReservation/price" import type { RateSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" import { RateEnum } from "@/types/enums/rate" import { RateTypeEnum } from "@/types/enums/rateType" export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) { const { bookingCode, bookingRooms, dates, petRoomPackage, rateSummary, roomsAvailability, searchParams, } = useRatesStore((state) => ({ bookingCode: state.booking.bookingCode, bookingRooms: state.booking.rooms, dates: { checkInDate: state.booking.fromDate, checkOutDate: state.booking.toDate, }, petRoomPackage: state.petRoomPackage, rateSummary: state.rateSummary, roomsAvailability: state.roomsAvailability, searchParams: state.searchParams, })) const [isSubmitting, setIsSubmitting] = useState(false) const intl = useIntl() const router = useRouter() const params = new URLSearchParams(searchParams) const [_, startTransition] = useTransition() if (!roomsAvailability) { return null } const checkInDate = new Date(dates.checkInDate) const checkOutDate = new Date(dates.checkOutDate) const nights = dt(checkOutDate).diff(dt(checkInDate), "days") const totalNights = intl.formatMessage( { id: "{totalNights, plural, one {# night} other {# nights}}" }, { totalNights: nights } ) const totalAdults = intl.formatMessage( { id: "{totalAdults, plural, one {# adult} other {# adults}}" }, { totalAdults: bookingRooms.reduce((acc, room) => acc + room.adults, 0) } ) const childrenInOneOrMoreRooms = bookingRooms.some( (room) => room.childrenInRoom?.length ) const childrenInroom = intl.formatMessage( { id: "{totalChildren, plural, one {# child} other {# children}}" }, { totalChildren: bookingRooms.reduce( (acc, room) => acc + (room.childrenInRoom?.length ?? 0), 0 ), } ) const totalChildren = childrenInOneOrMoreRooms ? `, ${childrenInroom}` : "" const totalRooms = intl.formatMessage( { id: "{totalRooms, plural, one {# room} other {# rooms}}" }, { totalRooms: bookingRooms.length } ) const summaryPriceText = `${totalNights}, ${totalAdults}${totalChildren}, ${totalRooms}` const totalRoomsRequired = bookingRooms.length const isAllRoomsSelected = rateSummary.length === totalRoomsRequired const hasMemberRates = rateSummary.some( (room) => "member" in room.product && room.product.member ) const showMemberDiscountBanner = hasMemberRates && !isUserLoggedIn 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 getRateDetails(rate: RateEnum) { switch (rate) { case RateEnum.change: return `${freeBooking}, ${payNow}` case RateEnum.flex: return `${freeCancelation}, ${payLater}` case RateEnum.save: default: return `${nonRefundable}, ${payNow}` } } function handleSubmit(e: React.FormEvent) { e.preventDefault() setIsSubmitting(true) startTransition(() => { router.push(`details?${params}`) }) } if (!rateSummary.length) { return null } const isBookingCodeRate = rateSummary.some( (rate) => "public" in rate.product && rate.product.public?.rateType !== RateTypeEnum.Regular ) const isVoucherRate = rateSummary.some((rate) => "voucher" in rate.product) const isCorporateChequeRate = rateSummary.some( (rate) => "corporateCheque" in rate.product ) const showDiscounted = isUserLoggedIn || isBookingCodeRate || isVoucherRate || isCorporateChequeRate const mainRoomProduct = rateSummary[0] let totalPriceToShow: Price if ("redemption" in mainRoomProduct.product) { // In case of reward night (redemption) only single room booking is supported by business rules totalPriceToShow = calculateRedemptionTotalPrice( mainRoomProduct.product.redemption ) } else if ("voucher" in mainRoomProduct.product) { totalPriceToShow = calculateVoucherPrice(rateSummary) } else if ("corporateCheque" in mainRoomProduct.product) { totalPriceToShow = calculateCorporateChequePrice(rateSummary) } else { totalPriceToShow = calculateTotalPrice( rateSummary, isUserLoggedIn, petRoomPackage ) } let mainRoomCurrency = "" if ( "member" in mainRoomProduct.product && mainRoomProduct.product.member?.localPrice ) { mainRoomCurrency = mainRoomProduct.product.member.localPrice.currency } if ( !mainRoomCurrency && "public" in mainRoomProduct.product && mainRoomProduct.product.public?.localPrice ) { mainRoomCurrency = mainRoomProduct.product.public.localPrice.currency } return (
{rateSummary.map((room, index) => (
{rateSummary.length > 1 ? ( <> {intl.formatMessage( { id: "Room {roomIndex}" }, { roomIndex: index + 1 } )} {room.roomType} {getRateDetails(room.rate)} ) : ( <> {room.roomType} {getRateDetails(room.rate)} )}
))} {/* Render unselected rooms */} {Array.from({ length: totalRoomsRequired - rateSummary.length, }).map((_, index) => (
{intl.formatMessage( { id: "Room {roomIndex}" }, { roomIndex: rateSummary.length + index + 1 } )} {intl.formatMessage({ id: "Select room" })}
))}
{showMemberDiscountBanner && (
{ const memberExists = "member" in product && product.member const publicExists = "public" in product && product.public if (!memberExists) { if (!publicExists) { return total } } const price = product.member?.localPrice.pricePerStay || product.public?.localPrice.pricePerStay if (!price) { return total } const hasSelectedPetRoom = roomPackages.includes( RoomPackageCodeEnum.PET_ROOM ) if (!hasSelectedPetRoom) { return total + price } const isPetRoom = features.find( (feature) => feature.code === RoomPackageCodeEnum.PET_ROOM ) const petRoomPrice = isPetRoom && petRoomPackage ? Number(petRoomPackage.localPrice.totalPrice) : 0 return total + price + petRoomPrice }, 0 ), currency: mainRoomCurrency, }} />
)}
{intl.formatMessage( { id: "Total price (incl VAT)" }, { b: (str) => {str} } )} {summaryPriceText}
{formatPrice( intl, totalPriceToShow.local.price, totalPriceToShow.local.currency, totalPriceToShow.local.additionalPrice, totalPriceToShow.local.additionalPriceCurrency )} {bookingCode && totalPriceToShow.local.regularPrice && ( {formatPrice( intl, totalPriceToShow.local.regularPrice, totalPriceToShow.local.currency )} )} {totalPriceToShow.requested ? ( {intl.formatMessage( { id: "Approx. {value}" }, { value: formatPrice( intl, totalPriceToShow.requested.price, totalPriceToShow.requested.currency, totalPriceToShow.requested.additionalPrice, totalPriceToShow.requested.additionalPriceCurrency ), } )} ) : null}
{intl.formatMessage({ id: "Total price" })} {formatPrice( intl, totalPriceToShow.local.price, totalPriceToShow.local.currency, totalPriceToShow.local.additionalPrice, totalPriceToShow.local.additionalPriceCurrency )} {summaryPriceText}
{showMemberDiscountBanner ? : null}
) }