Files
web/components/HotelReservation/SelectRate/Rooms/index.tsx
2025-01-30 11:19:22 +01:00

259 lines
7.8 KiB
TypeScript

"use client"
import { usePathname, useRouter, useSearchParams } from "next/navigation"
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"
import RateSummary from "../RateSummary"
import { RoomSelectionPanel } from "../RoomSelectionPanel"
import { filterDuplicateRoomTypesByLowestPrice, parseRoomParams } from "./utils"
import styles from "./rooms.module.css"
import {
type DefaultFilterOptions,
RoomPackageCodeEnum,
} from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { SelectRateProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
import type {
Rate,
RateCode,
} from "@/types/components/hotelReservation/selectRate/selectRate"
import type { RoomConfiguration } from "@/server/routers/hotels/output"
type SelectedRates = (RateCode | undefined)[]
export default function Rooms({
roomsAvailability,
roomCategories = [],
availablePackages,
hotelType,
isUserLoggedIn,
}: SelectRateProps) {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
const hotelId = searchParams.get("hotel")
const arrivalDate = searchParams.get("fromDate")
const departureDate = searchParams.get("toDate")
const searchedRoomsAndGuests = useMemo(
() => parseRoomParams(searchParams),
[searchParams]
)
const [selectedRates, setSelectedRates] = useState<SelectedRates>(
new Array(searchedRoomsAndGuests.length).fill(undefined)
)
const isMultipleRooms = searchedRoomsAndGuests.length > 1
const intl = useIntl()
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(
() => [
{
code: RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
description: intl.formatMessage({ id: "Accessible Room" }),
itemCode: availablePackages.find(
(pkg) => pkg.code === RoomPackageCodeEnum.ACCESSIBILITY_ROOM
)?.itemCode,
},
{
code: RoomPackageCodeEnum.ALLERGY_ROOM,
description: intl.formatMessage({ id: "Allergy Room" }),
itemCode: availablePackages.find(
(pkg) => pkg.code === RoomPackageCodeEnum.ALLERGY_ROOM
)?.itemCode,
},
{
code: RoomPackageCodeEnum.PET_ROOM,
description: intl.formatMessage({ id: "Pet Room" }),
itemCode: availablePackages.find(
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
)?.itemCode,
},
],
[availablePackages, intl]
)
const { selectedPackagesByRoom, getRooms, handleFilter, getFilteredRooms } =
useRoomFiltering({ roomsAvailability })
const rateSummary = useRateSummary({
searchedRoomsAndGuests,
selectedRates,
getFilteredRooms,
selectedPackagesByRoom,
availablePackages,
roomCategories,
})
useEffect(() => {
if (!rateSummary?.some((rate) => rate === null)) return
const hasAnySelection = selectedRates.some((rate) => rate !== undefined)
if (!hasAnySelection) return
}, [rateSummary, selectedRates])
useEffect(() => {
const pricesWithCurrencies = visibleRooms.flatMap((room) =>
room.products.map((product) => ({
price: product.productType.public.localPrice.pricePerNight,
currency: product.productType.public.localPrice.currency,
}))
)
const lowestPrice = pricesWithCurrencies.reduce(
(minPrice, { price }) => Math.min(minPrice, price),
Infinity
)
const currency = pricesWithCurrencies[0]?.currency
trackLowestRoomPrice({
hotelId,
arrivalDate,
departureDate,
lowestPrice: lowestPrice,
currency: currency,
})
}, [arrivalDate, departureDate, hotelId, visibleRooms])
const queryParams = useMemo(() => {
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, selectedPackagesByRoom])
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
window.history.replaceState(
null,
"",
`${pathname}?${queryParams.toString()}`
)
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} className={styles.roomContainer}>
<Subtitle>
{intl.formatMessage(
{
id: room.children?.length
? "Room {roomIndex}, {adults} adults, {children} children"
: "Room {roomIndex}, {adults} adults",
},
{
roomIndex: index + 1,
adults: room.adults,
children: room.children?.length,
}
)}
</Subtitle>
<RoomSelectionPanel
rooms={getRooms(index)}
roomCategories={roomCategories}
availablePackages={availablePackages}
selectedPackages={selectedPackagesByRoom[index]}
setSelectedRate={setSelectedRateForRoom(index)}
hotelType={hotelType}
handleFilter={handleFilterForRoom(index)}
defaultPackages={defaultPackages}
roomListIndex={index}
/>
</div>
))
) : (
<RoomSelectionPanel
rooms={getRooms(0)}
roomCategories={roomCategories}
availablePackages={availablePackages}
selectedPackages={selectedPackagesByRoom[0]}
setSelectedRate={setSelectedRateForRoom(0)}
hotelType={hotelType}
handleFilter={handleFilterForRoom(0)}
defaultPackages={defaultPackages}
roomListIndex={0}
/>
)}
{rateSummary && (
<form
method="GET"
action={`select-bed?${searchParams}`}
onSubmit={handleSubmit}
>
<RateSummary
rateSummary={rateSummary.filter(
(summary): summary is Rate => summary !== null
)}
isUserLoggedIn={isUserLoggedIn}
packages={availablePackages}
roomsAvailability={roomsAvailability}
/>
</form>
)}
</div>
)
}