diff --git a/components/HotelReservation/SelectRate/RateSummary/index.tsx b/components/HotelReservation/SelectRate/RateSummary/index.tsx
index bac210d79..cbded87de 100644
--- a/components/HotelReservation/SelectRate/RateSummary/index.tsx
+++ b/components/HotelReservation/SelectRate/RateSummary/index.tsx
@@ -1,3 +1,5 @@
+"use client"
+
import { useEffect, useState } from "react"
import { useIntl } from "react-intl"
@@ -13,6 +15,8 @@ import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { formatPrice } from "@/utils/numberFormatting"
+import { calculateTotalPrice } from "./utils"
+
import styles from "./rateSummary.module.css"
import type { RateSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary"
@@ -22,6 +26,7 @@ export default function RateSummary({
isUserLoggedIn,
packages,
roomsAvailability,
+ rooms,
}: RateSummaryProps) {
const intl = useIntl()
const [isVisible, setIsVisible] = useState(false)
@@ -34,87 +39,107 @@ export default function RateSummary({
}, [])
const selectedRateSummary = getSelectedRateSummary()
-
+ const totalRoomsRequired = rooms?.length || 1
if (selectedRateSummary.length === 0) return null
- const {
- member,
- public: publicRate,
- features,
- roomType,
- priceName,
- priceTerm,
- } = selectedRateSummary[0] // TODO: Support multiple rooms
-
- const isPetRoomSelected = features.some(
- (feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
- )
-
const petRoomPackage = packages?.find(
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
)
- const petRoomLocalPrice =
- isPetRoomSelected && petRoomPackage?.localPrice.totalPrice
- ? Number(petRoomPackage?.localPrice.totalPrice)
- : 0
- const petRoomRequestedPrice =
- isPetRoomSelected && petRoomPackage?.requestedPrice.totalPrice
- ? Number(petRoomPackage?.requestedPrice.totalPrice)
- : 0
-
- const priceToShow = isUserLoggedIn && member ? member : publicRate
-
- const totalPriceToShow = {
- localPrice: {
- currency: priceToShow.localPrice.currency,
- price: priceToShow.localPrice.pricePerStay + petRoomLocalPrice,
- },
- requestedPrice: !priceToShow.requestedPrice
- ? undefined
- : {
- currency: priceToShow.requestedPrice.currency,
- price:
- priceToShow.requestedPrice.pricePerStay + petRoomRequestedPrice,
- },
- }
+ const totalPriceToShow = calculateTotalPrice(
+ selectedRateSummary,
+ isUserLoggedIn,
+ petRoomPackage
+ )
+ const isAllRoomsSelected = selectedRateSummary.length === totalRoomsRequired
const checkInDate = new Date(roomsAvailability.checkInDate)
const checkOutDate = new Date(roomsAvailability.checkOutDate)
const nights = dt(checkOutDate).diff(dt(checkInDate), "days")
- const showMemberDiscountBanner = member && !isUserLoggedIn
+ const hasMemberRates = selectedRateSummary.some((room) => room.member)
+
+ const showMemberDiscountBanner = hasMemberRates && !isUserLoggedIn
const summaryPriceText = `${intl.formatMessage(
{ id: "{totalNights, plural, one {# night} other {# nights}}" },
{ totalNights: nights }
)}, ${intl.formatMessage(
{ id: "{totalAdults, plural, one {# adult} other {# adults}}" },
- { totalAdults: roomsAvailability.occupancy?.adults }
+ { totalAdults: rooms.reduce((acc, room) => acc + room.adults, 0) }
)}${
- roomsAvailability.occupancy?.children?.length
+ rooms.some((room) => room.childrenInRoom?.length)
? `, ${intl.formatMessage(
{ id: "{totalChildren, plural, one {# child} other {# children}}" },
- { totalChildren: roomsAvailability.occupancy.children.length }
+ {
+ totalChildren: rooms.reduce(
+ (acc, room) => acc + (room.childrenInRoom?.length ?? 0),
+ 0
+ ),
+ }
)}`
: ""
- }`
+ }, ${intl.formatMessage(
+ { id: "{totalRooms, plural, one {# room} other {# rooms}}" },
+ {
+ totalRooms: rooms.length,
+ }
+ )}`
return (
{showMemberDiscountBanner &&
}
-
{roomType}
- {`${priceName}, ${priceTerm}`}
+ {selectedRateSummary.map((room, index) => (
+
+
+ {intl.formatMessage(
+ { id: "Room {roomIndex}" },
+ { roomIndex: index + 1 }
+ )}
+
+ {room.roomType}
+
{`${room.priceName}, ${room.priceTerm}`}
+
+ ))}
+ {/* Render unselected rooms */}
+ {Array.from({
+ length: totalRoomsRequired - selectedRateSummary.length,
+ }).map((_, index) => (
+
+
+ {intl.formatMessage(
+ { id: "Room {roomIndex}" },
+ { roomIndex: selectedRateSummary.length + index + 1 }
+ )}
+
+
+ {intl.formatMessage({ id: "Select room" })}
+
+
+ ))}
{showMemberDiscountBanner && (
{
+ const memberPrice =
+ room.member?.localPrice.pricePerStay ?? 0
+ const isPetRoom = room.features.some(
+ (feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
+ )
+ const petRoomPrice =
+ isPetRoom && petRoomPackage
+ ? Number(petRoomPackage.localPrice.totalPrice || 0)
+ : 0
+ return total + memberPrice + petRoomPrice
+ }, 0),
+ currency:
+ selectedRateSummary[0].member?.localPrice.currency ??
+ selectedRateSummary[0].public.localPrice.currency,
}}
/>
@@ -177,6 +202,7 @@ export default function RateSummary({
type="submit"
theme="base"
className={styles.continueButton}
+ disabled={!isAllRoomsSelected}
>
{intl.formatMessage({ id: "Continue" })}
diff --git a/components/HotelReservation/SelectRate/RateSummary/rateSummary.module.css b/components/HotelReservation/SelectRate/RateSummary/rateSummary.module.css
index 9f910233b..fac373840 100644
--- a/components/HotelReservation/SelectRate/RateSummary/rateSummary.module.css
+++ b/components/HotelReservation/SelectRate/RateSummary/rateSummary.module.css
@@ -79,10 +79,13 @@
}
.petInfo,
.promoContainer,
- .summaryText,
.summaryPriceTextDesktop {
display: block;
}
+ .summaryText {
+ display: flex;
+ gap: var(--Spacing-x2);
+ }
.summaryPriceTextMobile {
display: none;
}
diff --git a/components/HotelReservation/SelectRate/RateSummary/utils.ts b/components/HotelReservation/SelectRate/RateSummary/utils.ts
new file mode 100644
index 000000000..93319dfa6
--- /dev/null
+++ b/components/HotelReservation/SelectRate/RateSummary/utils.ts
@@ -0,0 +1,58 @@
+import {
+ type RoomPackage,
+ RoomPackageCodeEnum,
+} from "@/types/components/hotelReservation/selectRate/roomFilter"
+import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate"
+
+interface TotalPrice {
+ localPrice: { currency: string; price: number }
+ requestedPrice?: { currency: string; price: number }
+}
+
+export const calculateTotalPrice = (
+ selectedRateSummary: Rate[],
+ isUserLoggedIn: boolean,
+ petRoomPackage: RoomPackage | undefined
+) => {
+ return selectedRateSummary.reduce
(
+ (total, room) => {
+ const priceToUse =
+ isUserLoggedIn && room.member ? room.member : room.public
+ const isPetRoom = room.features.some(
+ (feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
+ )
+ const petRoomPrice =
+ isPetRoom && petRoomPackage
+ ? isUserLoggedIn
+ ? Number(petRoomPackage.localPrice.totalPrice || 0)
+ : Number(petRoomPackage.requestedPrice.totalPrice || 0)
+ : 0
+
+ return {
+ localPrice: {
+ currency: priceToUse.localPrice.currency,
+ price:
+ total.localPrice.price +
+ priceToUse.localPrice.pricePerStay +
+ petRoomPrice,
+ },
+ requestedPrice: priceToUse.requestedPrice
+ ? {
+ currency: priceToUse.requestedPrice.currency,
+ price:
+ (total.requestedPrice?.price ?? 0) +
+ priceToUse.requestedPrice.pricePerStay +
+ petRoomPrice,
+ }
+ : undefined,
+ }
+ },
+ {
+ localPrice: {
+ currency: selectedRateSummary[0].public.localPrice.currency,
+ price: 0,
+ },
+ requestedPrice: undefined,
+ }
+ )
+}
diff --git a/components/HotelReservation/SelectRate/Rooms/index.tsx b/components/HotelReservation/SelectRate/Rooms/index.tsx
index aa3d46b06..a09f99946 100644
--- a/components/HotelReservation/SelectRate/Rooms/index.tsx
+++ b/components/HotelReservation/SelectRate/Rooms/index.tsx
@@ -14,7 +14,6 @@ import { convertObjToSearchParams, convertSearchParamsToObj } from "@/utils/url"
import RateSummary from "../RateSummary"
import { RoomSelectionPanel } from "../RoomSelectionPanel"
import SelectedRoomPanel from "../SelectedRoomPanel"
-import { filterDuplicateRoomTypesByLowestPrice } from "./utils"
import { roomSelectionPanelVariants } from "./variants"
import styles from "./rooms.module.css"
@@ -25,7 +24,6 @@ import {
} from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { SelectRateProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
-import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
export default function Rooms({
availablePackages,
@@ -37,6 +35,7 @@ export default function Rooms({
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
+ const intl = useIntl()
const hotelId = searchParams.get("hotel")
const arrivalDate = searchParams.get("fromDate")
@@ -47,6 +46,7 @@ export default function Rooms({
const {
selectedPackagesByRoom,
+ visibleRooms,
setVisibleRooms,
setRoomsAvailability,
getFilteredRooms,
@@ -62,33 +62,10 @@ export default function Rooms({
const isMultipleRooms = bookingWidgetSearchData.rooms.length > 1
- const intl = useIntl()
-
useEffect(() => {
initializeRates(bookingWidgetSearchData.rooms.length)
}, [initializeRates, bookingWidgetSearchData.rooms.length])
- const visibleRooms: RoomConfiguration[] = useMemo(() => {
- const deduped = filterDuplicateRoomTypesByLowestPrice(
- roomsAvailability.roomConfigurations
- )
-
- const separated = deduped.reduce<{
- available: RoomConfiguration[]
- notAvailable: RoomConfiguration[]
- }>(
- (acc, curr) => {
- if (curr.status === "NotAvailable") {
- return { ...acc, notAvailable: [...acc.notAvailable, curr] }
- }
- return { ...acc, available: [...acc.available, curr] }
- },
- { available: [], notAvailable: [] }
- )
-
- return [...separated.available, ...separated.notAvailable]
- }, [roomsAvailability.roomConfigurations])
-
const defaultPackages: DefaultFilterOptions[] = useMemo(
() => [
{
@@ -197,7 +174,9 @@ export default function Rooms({
const SCROLL_OFFSET = 100
const roomElements = document.querySelectorAll(`.${styles.roomContainer}`)
const index = selectedRates.findIndex((rate) => rate === undefined)
- const selectedRoom = roomElements[index - 1]
+
+ const targetIndex = index === -1 ? selectedRates.length - 1 : index - 1
+ const selectedRoom = roomElements[targetIndex]
if (selectedRoom) {
const elementPosition = selectedRoom.getBoundingClientRect().top
@@ -286,6 +265,7 @@ export default function Rooms({
isUserLoggedIn={isUserLoggedIn}
packages={availablePackages}
roomsAvailability={roomsAvailability}
+ rooms={bookingWidgetSearchData.rooms}
/>
)}
diff --git a/types/components/hotelReservation/selectRate/rateSummary.ts b/types/components/hotelReservation/selectRate/rateSummary.ts
index 470f2bf3d..0a7b55169 100644
--- a/types/components/hotelReservation/selectRate/rateSummary.ts
+++ b/types/components/hotelReservation/selectRate/rateSummary.ts
@@ -1,9 +1,10 @@
import type { RoomsAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
import type { RoomPackages } from "./roomFilter"
-import type { Rate } from "./selectRate"
+import type { Room } from "./selectRate"
export interface RateSummaryProps {
isUserLoggedIn: boolean
packages: RoomPackages | undefined
roomsAvailability: RoomsAvailability
+ rooms: Room[]
}