feat(SW-718) Fixed filtering with multirooms
This commit is contained in:
@@ -5,6 +5,8 @@ import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
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 } from "@/utils/url"
|
||||
|
||||
@@ -17,7 +19,6 @@ import styles from "./rooms.module.css"
|
||||
import {
|
||||
type DefaultFilterOptions,
|
||||
RoomPackageCodeEnum,
|
||||
type RoomPackageCodes,
|
||||
} from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import type { SelectRateProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
|
||||
import type {
|
||||
@@ -26,6 +27,8 @@ import type {
|
||||
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
import type { RoomConfiguration } from "@/server/routers/hotels/output"
|
||||
|
||||
type SelectedRates = (RateCode | undefined)[]
|
||||
|
||||
export default function Rooms({
|
||||
roomsAvailability,
|
||||
roomCategories = [],
|
||||
@@ -46,6 +49,10 @@ export default function Rooms({
|
||||
[searchParams]
|
||||
)
|
||||
|
||||
const [selectedRates, setSelectedRates] = useState<SelectedRates>(
|
||||
new Array(searchedRoomsAndGuests.length).fill(undefined)
|
||||
)
|
||||
|
||||
const isMultipleRooms = searchedRoomsAndGuests.length > 1
|
||||
|
||||
const intl = useIntl()
|
||||
@@ -71,13 +78,6 @@ export default function Rooms({
|
||||
return [...separated.available, ...separated.notAvailable]
|
||||
}, [roomsAvailability.roomConfigurations])
|
||||
|
||||
const [selectedRate, setSelectedRate] = useState<RateCode | undefined>(
|
||||
undefined
|
||||
)
|
||||
const [selectedPackages, setSelectedPackages] = useState<RoomPackageCodes[]>(
|
||||
[]
|
||||
)
|
||||
|
||||
const defaultPackages: DefaultFilterOptions[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -105,142 +105,60 @@ export default function Rooms({
|
||||
[availablePackages, intl]
|
||||
)
|
||||
|
||||
const handleFilter = useCallback(
|
||||
(filter: Record<RoomPackageCodeEnum, boolean | undefined>) => {
|
||||
const filteredPackages = Object.keys(filter).filter(
|
||||
(key) => filter[key as RoomPackageCodeEnum]
|
||||
) as RoomPackageCodeEnum[]
|
||||
const { selectedPackagesByRoom, getRooms, handleFilter, getFilteredRooms } =
|
||||
useRoomFiltering({ roomsAvailability })
|
||||
|
||||
setSelectedPackages(filteredPackages)
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const filteredRooms = useMemo(() => {
|
||||
return visibleRooms.filter((room) =>
|
||||
selectedPackages.every((filteredPackage) =>
|
||||
room.features.some((feature) => feature.code === filteredPackage)
|
||||
)
|
||||
)
|
||||
}, [visibleRooms, selectedPackages])
|
||||
|
||||
const rooms = useMemo(() => {
|
||||
if (selectedPackages.length === 0) {
|
||||
return {
|
||||
...roomsAvailability,
|
||||
roomConfigurations: visibleRooms,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...roomsAvailability,
|
||||
roomConfigurations: [...filteredRooms],
|
||||
}
|
||||
}, [roomsAvailability, visibleRooms, selectedPackages, filteredRooms])
|
||||
|
||||
const rateSummary: Rate | null = useMemo(() => {
|
||||
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
|
||||
)
|
||||
)
|
||||
|
||||
const rateSummary: Rate = {
|
||||
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,
|
||||
}
|
||||
|
||||
return rateSummary
|
||||
}, [
|
||||
filteredRooms,
|
||||
const rateSummary = useRateSummary({
|
||||
searchedRoomsAndGuests,
|
||||
selectedRates,
|
||||
getFilteredRooms,
|
||||
selectedPackagesByRoom,
|
||||
availablePackages,
|
||||
selectedPackages,
|
||||
selectedRate,
|
||||
roomCategories,
|
||||
])
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (rateSummary) return
|
||||
if (!selectedRate) return
|
||||
if (!rateSummary?.some((rate) => rate === null)) return
|
||||
|
||||
setSelectedRate(undefined)
|
||||
}, [rateSummary, selectedRate])
|
||||
const hasAnySelection = selectedRates.some((rate) => rate !== undefined)
|
||||
if (!hasAnySelection) return
|
||||
}, [rateSummary, selectedRates])
|
||||
|
||||
useEffect(() => {
|
||||
const pricesWithCurrencies = rooms.roomConfigurations.flatMap((room) =>
|
||||
const pricesWithCurrencies = visibleRooms.flatMap((room) =>
|
||||
room.products.map((product) => ({
|
||||
price: product.productType.public.localPrice.pricePerNight,
|
||||
currency: product.productType.public.localPrice.currency,
|
||||
}))
|
||||
)
|
||||
|
||||
const cheapestPrice = pricesWithCurrencies.reduce(
|
||||
const lowestPrice = pricesWithCurrencies.reduce(
|
||||
(minPrice, { price }) => Math.min(minPrice, price),
|
||||
Infinity
|
||||
)
|
||||
|
||||
const currency = pricesWithCurrencies.find(
|
||||
({ price }) => price === cheapestPrice
|
||||
)?.currency
|
||||
const currency = pricesWithCurrencies[0]?.currency
|
||||
|
||||
trackLowestRoomPrice({
|
||||
hotelId,
|
||||
arrivalDate,
|
||||
departureDate,
|
||||
lowestPrice: cheapestPrice,
|
||||
lowestPrice: lowestPrice,
|
||||
currency: currency,
|
||||
})
|
||||
}, [arrivalDate, departureDate, hotelId, rooms.roomConfigurations])
|
||||
}, [arrivalDate, departureDate, hotelId, visibleRooms])
|
||||
|
||||
const queryParams = useMemo(() => {
|
||||
// TODO: handle multiple rooms
|
||||
const newSearchParams = convertObjToSearchParams(
|
||||
{
|
||||
rooms: [
|
||||
{
|
||||
roomTypeCode: rateSummary?.roomTypeCode,
|
||||
rateCode: rateSummary?.public.rateCode,
|
||||
counterRateCode: rateSummary?.member?.rateCode,
|
||||
packages: selectedPackages,
|
||||
},
|
||||
],
|
||||
},
|
||||
searchParams
|
||||
)
|
||||
const rooms = rateSummary.map((rate, index) => ({
|
||||
roomTypeCode: rate?.roomTypeCode,
|
||||
rateCode: rate?.public.rateCode,
|
||||
counterRateCode: rate?.member?.rateCode,
|
||||
packages: selectedPackagesByRoom[index] || [],
|
||||
}))
|
||||
|
||||
const newSearchParams = convertObjToSearchParams({ rooms }, searchParams)
|
||||
|
||||
return newSearchParams
|
||||
}, [searchParams, rateSummary, selectedPackages])
|
||||
}, [searchParams, rateSummary, selectedPackagesByRoom])
|
||||
|
||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault()
|
||||
@@ -253,11 +171,31 @@ export default function Rooms({
|
||||
router.push(`select-bed?${queryParams}`)
|
||||
}
|
||||
|
||||
const setSelectedRateForRoom = useCallback(
|
||||
(index: number) => (value: React.SetStateAction<RateCode | undefined>) => {
|
||||
setSelectedRates((prev) => {
|
||||
const newRates = [...prev]
|
||||
newRates[index] =
|
||||
typeof value === "function" ? value(prev[index]) : value
|
||||
return newRates
|
||||
})
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const handleFilterForRoom = useCallback(
|
||||
(index: number) =>
|
||||
(filter: Record<RoomPackageCodeEnum, boolean | undefined>) => {
|
||||
handleFilter(filter, index)
|
||||
},
|
||||
[handleFilter]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
{isMultipleRooms ? (
|
||||
searchedRoomsAndGuests.map((room, index) => (
|
||||
<div key={index}>
|
||||
<div key={index} className={styles.roomContainer}>
|
||||
<Subtitle>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
@@ -273,27 +211,29 @@ export default function Rooms({
|
||||
)}
|
||||
</Subtitle>
|
||||
<RoomSelectionPanel
|
||||
rooms={rooms}
|
||||
rooms={getRooms(index)}
|
||||
roomCategories={roomCategories}
|
||||
availablePackages={availablePackages}
|
||||
selectedPackages={selectedPackages}
|
||||
setSelectedRate={setSelectedRate}
|
||||
selectedPackages={selectedPackagesByRoom[index]}
|
||||
setSelectedRate={setSelectedRateForRoom(index)}
|
||||
hotelType={hotelType}
|
||||
handleFilter={handleFilter}
|
||||
handleFilter={handleFilterForRoom(index)}
|
||||
defaultPackages={defaultPackages}
|
||||
roomListIndex={index}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<RoomSelectionPanel
|
||||
rooms={rooms}
|
||||
rooms={getRooms(0)}
|
||||
roomCategories={roomCategories}
|
||||
availablePackages={availablePackages}
|
||||
selectedPackages={selectedPackages}
|
||||
setSelectedRate={setSelectedRate}
|
||||
selectedPackages={selectedPackagesByRoom[0]}
|
||||
setSelectedRate={setSelectedRateForRoom(0)}
|
||||
hotelType={hotelType}
|
||||
handleFilter={handleFilter}
|
||||
handleFilter={handleFilterForRoom(0)}
|
||||
defaultPackages={defaultPackages}
|
||||
roomListIndex={0}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -304,7 +244,9 @@ export default function Rooms({
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<RateSummary
|
||||
rateSummary={rateSummary}
|
||||
rateSummary={rateSummary.filter(
|
||||
(summary): summary is Rate => summary !== null
|
||||
)}
|
||||
isUserLoggedIn={isUserLoggedIn}
|
||||
packages={availablePackages}
|
||||
roomsAvailability={roomsAvailability}
|
||||
|
||||
@@ -6,3 +6,12 @@
|
||||
gap: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x2) 0;
|
||||
}
|
||||
|
||||
.roomContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
padding: var(--Spacing-x2) var(--Spacing-x2) 0 var(--Spacing-x2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user