diff --git a/components/HotelReservation/SelectRate/RateSummary/index.tsx b/components/HotelReservation/SelectRate/RateSummary/index.tsx index bf4b6f9f3..42c2fd05b 100644 --- a/components/HotelReservation/SelectRate/RateSummary/index.tsx +++ b/components/HotelReservation/SelectRate/RateSummary/index.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react" import { useIntl } from "react-intl" import { dt } from "@/lib/dt" +import { useRateSelectionStore } from "@/stores/rate-selection" import SignupPromoDesktop from "@/components/HotelReservation/SignupPromo/Desktop" import SignupPromoMobile from "@/components/HotelReservation/SignupPromo/Mobile" @@ -16,9 +17,9 @@ import styles from "./rateSummary.module.css" import type { RateSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" +import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate" export default function RateSummary({ - rateSummary, isUserLoggedIn, packages, roomsAvailability, @@ -26,12 +27,19 @@ export default function RateSummary({ const intl = useIntl() const [isVisible, setIsVisible] = useState(false) + const { rateSummary } = useRateSelectionStore() + useEffect(() => { const timer = setTimeout(() => setIsVisible(true), 0) return () => clearTimeout(timer) }, []) if (rateSummary.length === 0) return null + + const selectedRateSummary = rateSummary.filter( + (summary): summary is Rate => summary !== null + ) + const { member, public: publicRate, @@ -39,7 +47,7 @@ export default function RateSummary({ roomType, priceName, priceTerm, - } = rateSummary[0] // TODO: Support multiple rooms + } = selectedRateSummary[0] // TODO: Support multiple rooms const isPetRoomSelected = features.some( (feature) => feature.code === RoomPackageCodeEnum.PET_ROOM diff --git a/components/HotelReservation/SelectRate/Rooms/RoomsContainer.tsx b/components/HotelReservation/SelectRate/Rooms/RoomsContainer.tsx index 0c74eb61d..751f03e58 100644 --- a/components/HotelReservation/SelectRate/Rooms/RoomsContainer.tsx +++ b/components/HotelReservation/SelectRate/Rooms/RoomsContainer.tsx @@ -75,6 +75,8 @@ export async function RoomsContainer({ const [roomsAvailability, roomsAvailabilityError] = await roomsAvailabilityPromise + console.log("roomsAvailability_", roomsAvailability) + if (packagesError) { // TODO: Log packages error console.error("[RoomsContainer] unable to fetch packages") diff --git a/components/HotelReservation/SelectRate/Rooms/index.tsx b/components/HotelReservation/SelectRate/Rooms/index.tsx index d880e5f8d..8ebb52089 100644 --- a/components/HotelReservation/SelectRate/Rooms/index.tsx +++ b/components/HotelReservation/SelectRate/Rooms/index.tsx @@ -7,7 +7,6 @@ import { useIntl } from "react-intl" import { useRateSelectionStore } from "@/stores/rate-selection" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" -import { useRateSummary } from "@/hooks/selectRate/useRateSummary" import { useRoomFiltering } from "@/hooks/selectRate/useRoomFiltering" import { trackLowestRoomPrice } from "@/utils/tracking" import { convertObjToSearchParams, convertSearchParamsToObj } from "@/utils/url" @@ -24,10 +23,7 @@ import { RoomPackageCodeEnum, } from "@/types/components/hotelReservation/selectRate/roomFilter" import type { SelectRateProps } from "@/types/components/hotelReservation/selectRate/roomSelection" -import type { - Rate, - SelectRateSearchParams, -} from "@/types/components/hotelReservation/selectRate/selectRate" +import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate" import type { RoomConfiguration } from "@/server/routers/hotels/output" export default function Rooms({ @@ -45,8 +41,13 @@ export default function Rooms({ const arrivalDate = searchParams.get("fromDate") const departureDate = searchParams.get("toDate") - const { modifyRate, selectedRates, setSelectedRates } = - useRateSelectionStore() + const { + modifyRate, + selectedRates, + rateSummary, + calculateRateSummary, + initializeRates, + } = useRateSelectionStore() const bookingWidgetSearchData = useMemo( () => @@ -61,10 +62,8 @@ export default function Rooms({ const intl = useIntl() useEffect(() => { - setSelectedRates( - new Array(bookingWidgetSearchData.rooms.length).fill(undefined) - ) - }, [setSelectedRates, bookingWidgetSearchData.rooms.length]) + initializeRates(bookingWidgetSearchData.rooms.length) + }, [initializeRates, bookingWidgetSearchData.rooms.length]) const visibleRooms: RoomConfiguration[] = useMemo(() => { const deduped = filterDuplicateRoomTypesByLowestPrice( @@ -117,14 +116,20 @@ export default function Rooms({ const { selectedPackagesByRoom, getRooms, handleFilter, getFilteredRooms } = useRoomFiltering({ roomsAvailability }) - const rateSummary = useRateSummary({ - searchedRoomsAndGuests: bookingWidgetSearchData.rooms, - selectedRates, + useEffect(() => { + calculateRateSummary({ + getFilteredRooms, + availablePackages, + roomCategories, + selectedPackagesByRoom, + }) + }, [ getFilteredRooms, - selectedPackagesByRoom, availablePackages, roomCategories, - }) + selectedPackagesByRoom, + calculateRateSummary, + ]) useEffect(() => { if (!rateSummary?.some((rate) => rate === null)) return @@ -180,13 +185,6 @@ export default function Rooms({ router.push(`select-bed?${queryParams}`) } - const handleModify = useCallback( - (index: number) => () => { - modifyRate(index) - }, - [modifyRate] - ) - const handleFilterForRoom = useCallback( (index: number) => (filter: Record) => { @@ -251,9 +249,7 @@ export default function Rooms({
@@ -291,9 +287,6 @@ export default function Rooms({ onSubmit={handleSubmit} > summary !== null - )} isUserLoggedIn={isUserLoggedIn} packages={availablePackages} roomsAvailability={roomsAvailability} diff --git a/components/HotelReservation/SelectRate/SelectedRoomPanel/index.tsx b/components/HotelReservation/SelectRate/SelectedRoomPanel/index.tsx index 0307bcf62..7f12f4a02 100644 --- a/components/HotelReservation/SelectRate/SelectedRoomPanel/index.tsx +++ b/components/HotelReservation/SelectRate/SelectedRoomPanel/index.tsx @@ -1,6 +1,9 @@ "use client" +import { useCallback } from "react" import { useIntl } from "react-intl" +import { useRateSelectionStore } from "@/stores/rate-selection" + import { EditIcon } from "@/components/Icons" import Image from "@/components/Image" import Button from "@/components/TempDesignSystem/Button" @@ -9,34 +12,33 @@ import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import styles from "./selectedRoomPanel.module.css" -import type { - Rate, - Room, -} from "@/types/components/hotelReservation/selectRate/selectRate" +import type { Room } from "@/types/components/hotelReservation/selectRate/selectRate" import type { RoomData } from "@/types/hotel" interface SelectedRoomPanelProps { roomIndex: number room: Room - selectedRate: Rate | null roomCategories: RoomData[] - handleModify: () => void } export default function SelectedRoomPanel({ roomIndex, room, - selectedRate, roomCategories, - handleModify, }: SelectedRoomPanelProps) { const intl = useIntl() + const { rateSummary, modifyRate } = useRateSelectionStore() + const selectedRate = rateSummary[roomIndex] const images = roomCategories.find((roomCategory) => roomCategory.roomTypes.some( (roomType) => roomType.code === selectedRate?.roomTypeCode ) )?.images + const handleModify = useCallback(() => { + modifyRate(roomIndex) + }, [modifyRate, roomIndex]) + return (
diff --git a/hooks/selectRate/useRateSummary.ts b/hooks/selectRate/useRateSummary.ts index 6b0e40ed5..2dd4da7f4 100644 --- a/hooks/selectRate/useRateSummary.ts +++ b/hooks/selectRate/useRateSummary.ts @@ -10,7 +10,7 @@ import type { } from "@/types/components/hotelReservation/selectRate/selectRate" import type { RoomConfiguration } from "@/server/routers/hotels/output" -interface UseRateSummaryProps { +interface UseRateSummary { searchedRoomsAndGuests: Array<{ adults: number; children?: any[] }> selectedRates: (RateCode | undefined)[] getFilteredRooms: (roomIndex: number) => RoomConfiguration[] @@ -26,7 +26,7 @@ export function useRateSummary({ selectedPackagesByRoom, availablePackages, roomCategories, -}: UseRateSummaryProps) { +}: UseRateSummary) { return useMemo(() => { const summaries: (Rate | null)[] = [] diff --git a/stores/rate-selection.ts b/stores/rate-selection.ts index cca028a37..02e0675ad 100644 --- a/stores/rate-selection.ts +++ b/stores/rate-selection.ts @@ -1,17 +1,40 @@ import { create } from "zustand" -import type { RateCode } from "@/types/components/hotelReservation/selectRate/selectRate" +import type { + RoomPackageCodeEnum, + RoomPackageData} from "@/types/components/hotelReservation/selectRate/roomFilter"; +import type { RoomParam } from "@/types/components/hotelReservation/selectRate/section" +import type { + Rate, + RateCode, +} from "@/types/components/hotelReservation/selectRate/selectRate" +import type { RoomConfiguration } from "@/server/routers/hotels/output" +interface RateSummaryParams { + getFilteredRooms: (roomIndex: number) => RoomConfiguration[] + availablePackages: RoomPackageData + roomCategories: Array<{ name: string; roomTypes: Array<{ code: string }> }> + selectedPackagesByRoom: Record +} interface RateSelectionState { selectedRates: (RateCode | undefined)[] - setSelectedRates: (rates: (RateCode | undefined)[]) => void + roomsAndGuests: RoomParam[] + rateSummary: (Rate | null)[] modifyRate: (index: number) => void selectRate: (index: number, rate: RateCode | undefined) => void + setRoomsAndGuests: (rooms: RoomParam[]) => void + initializeRates: (count: number) => void + calculateRateSummary: ({ + getFilteredRooms, + availablePackages, + roomCategories, + }: RateSummaryParams) => void } -export const useRateSelectionStore = create((set) => ({ +export const useRateSelectionStore = create((set, get) => ({ selectedRates: [], - setSelectedRates: (rates) => set({ selectedRates: rates }), + roomsAndGuests: [], + rateSummary: [], modifyRate: (index) => set((state) => { const newRates = [...state.selectedRates] @@ -24,4 +47,64 @@ export const useRateSelectionStore = create((set) => ({ newRates[index] = rate return { selectedRates: newRates } }), + initializeRates: (count) => + set({ selectedRates: new Array(count).fill(undefined) }), + setRoomsAndGuests: (rooms) => set({ roomsAndGuests: rooms }), + calculateRateSummary: ({ + getFilteredRooms, + availablePackages, + roomCategories, + selectedPackagesByRoom, + }) => { + const state = get() + const summaries = state.roomsAndGuests.map((_, roomIndex) => { + const selectedRate = state.selectedRates[roomIndex] + const filteredRooms = getFilteredRooms(roomIndex) + const selectedPackages = selectedPackagesByRoom[roomIndex] || [] + + const room = filteredRooms.find( + (room) => room.roomTypeCode === selectedRate?.roomTypeCode + ) + + if (!room) return null + + const product = room.products.find( + (product) => + product.productType.public.rateCode === selectedRate?.publicRateCode + ) + + if (!product) return null + + const petRoomPackage = + (selectedPackages.includes(RoomPackageCodeEnum.PET_ROOM) && + availablePackages.find( + (pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM + )) || + undefined + + const features = filteredRooms.find((room) => + room.features.some( + (feature) => feature.code === RoomPackageCodeEnum.PET_ROOM + ) + )?.features + + const roomType = roomCategories.find((roomCategory) => + roomCategory.roomTypes.some( + (roomType) => roomType.code === room.roomTypeCode + ) + ) + + return { + features: petRoomPackage && features ? features : [], + priceName: selectedRate?.name, + priceTerm: selectedRate?.paymentTerm, + public: product.productType.public, + member: product.productType.member, + roomType: roomType?.name ?? room.roomType, + roomTypeCode: room.roomTypeCode, + } + }) + + set({ rateSummary: summaries }) + }, })) diff --git a/types/components/hotelReservation/selectRate/rateSummary.ts b/types/components/hotelReservation/selectRate/rateSummary.ts index 64aea92af..28fed15d7 100644 --- a/types/components/hotelReservation/selectRate/rateSummary.ts +++ b/types/components/hotelReservation/selectRate/rateSummary.ts @@ -3,7 +3,6 @@ import type { RoomPackageData } from "./roomFilter" import type { Rate } from "./selectRate" export interface RateSummaryProps { - rateSummary: Rate[] isUserLoggedIn: boolean packages: RoomPackageData | undefined roomsAvailability: RoomsAvailability diff --git a/types/components/hotelReservation/selectRate/section.ts b/types/components/hotelReservation/selectRate/section.ts index 95dd0e532..c36fe45ed 100644 --- a/types/components/hotelReservation/selectRate/section.ts +++ b/types/components/hotelReservation/selectRate/section.ts @@ -1,5 +1,6 @@ import type { CreditCard, SafeUser } from "@/types/user" import type { PaymentMethodEnum } from "@/constants/booking" +import type { Child } from "./selectRate" export interface SectionProps { nextPath: string @@ -43,7 +44,7 @@ export interface PaymentClientProps export interface RoomParam { adults: number - children?: { age: number; bed: number }[] + children?: Child[] } export interface SectionPageProps {