fix: allow rates that only have either of member or public to be selectable * fix: allow rates that only have either of member or public to be selectable Approved-by: Michael Zetterberg
170 lines
6.0 KiB
TypeScript
170 lines
6.0 KiB
TypeScript
"use client"
|
|
import { useSearchParams } from "next/navigation"
|
|
import { useEffect } from "react"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { alternativeHotels } from "@/constants/routes/hotelReservation"
|
|
import { useBookingCodeFilterStore } from "@/stores/bookingCode-filter"
|
|
import { useRatesStore } from "@/stores/select-rate"
|
|
|
|
import BookingCodeFilter from "@/components/HotelReservation/SelectHotel/BookingCodeFilter"
|
|
import Alert from "@/components/TempDesignSystem/Alert"
|
|
import { useRoomContext } from "@/contexts/SelectRate/Room"
|
|
import useLang from "@/hooks/useLang"
|
|
|
|
import RoomCard from "./RoomCard"
|
|
import RoomTypeFilter from "./RoomTypeFilter"
|
|
|
|
import styles from "./roomSelectionPanel.module.css"
|
|
|
|
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
|
import { AlertTypeEnum } from "@/types/enums/alert"
|
|
import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter"
|
|
import { RateTypeEnum } from "@/types/enums/rateType"
|
|
|
|
export default function RoomSelectionPanel() {
|
|
const { rooms } = useRoomContext()
|
|
const isSingleRoomAndHasSelection = useRatesStore(
|
|
(state) => state.booking.rooms.length === 1 && !!state.rateSummary.length
|
|
)
|
|
const searchParams = useSearchParams()
|
|
const bookingCode = searchParams.get("bookingCode")
|
|
const intl = useIntl()
|
|
const lang = useLang()
|
|
const noAvailableRooms = rooms.every(
|
|
(roomConfig) => roomConfig.status === AvailabilityEnum.NotAvailable
|
|
)
|
|
const activeCodeFilter = useBookingCodeFilterStore(
|
|
(state) => state.activeCodeFilter
|
|
)
|
|
|
|
// Regular Rates (Save, Change and Flex) always should send both public and member rates
|
|
// so we can check public rates for availability
|
|
const isRegularRatesAvailableWithCode =
|
|
bookingCode &&
|
|
rooms.some(
|
|
(room) =>
|
|
room.status === AvailabilityEnum.Available &&
|
|
room.products.some(
|
|
(product) => product.public?.rateType === RateTypeEnum.Regular
|
|
)
|
|
)
|
|
|
|
// Booking codes rate comes with various rate types but Regular is reserved
|
|
// for non-booking code rates (Save, Change & Flex)
|
|
// With Booking code rates we will always obtain public rate and maybe a member rate
|
|
// so we check for public rate and ignore member rate
|
|
const isBookingCodeRatesAvailable =
|
|
bookingCode &&
|
|
rooms.some(
|
|
(room) =>
|
|
room.status === AvailabilityEnum.Available &&
|
|
room.products.some(
|
|
(product) => product.public?.rateType !== RateTypeEnum.Regular
|
|
)
|
|
)
|
|
|
|
// Show all rooms if either booking code rates or regular rates are not available
|
|
// or filter selection is All rooms
|
|
const showAllRooms =
|
|
!isBookingCodeRatesAvailable ||
|
|
!isRegularRatesAvailableWithCode ||
|
|
activeCodeFilter === BookingCodeFilterEnum.All
|
|
const bookingCodeDiscountedRooms = rooms.filter(
|
|
(room) =>
|
|
room.status === AvailabilityEnum.Available &&
|
|
room.products.every(
|
|
(product) => product.public?.rateType !== RateTypeEnum.Regular
|
|
)
|
|
)
|
|
const regularRateRooms = rooms.filter(
|
|
(room) =>
|
|
room.status === AvailabilityEnum.Available &&
|
|
room.products.every(
|
|
(product) => product.public?.rateType === RateTypeEnum.Regular
|
|
)
|
|
)
|
|
// Show booking code filter when both of the booking code rates or regular rates are available
|
|
const showBookingCodeFilter =
|
|
isRegularRatesAvailableWithCode && isBookingCodeRatesAvailable
|
|
|
|
useEffect(() => {
|
|
if (isSingleRoomAndHasSelection) {
|
|
// Required to prevent the history.pushState on the first selection
|
|
// to scroll user back to top
|
|
requestAnimationFrame(() => {
|
|
const SCROLL_OFFSET = 100
|
|
const selectedInputRoomCard = document.querySelector(
|
|
`.${styles.roomList} li:has(input[type=radio]:checked)`
|
|
)
|
|
if (selectedInputRoomCard) {
|
|
const elementPosition =
|
|
selectedInputRoomCard.getBoundingClientRect().top
|
|
const offsetPosition =
|
|
elementPosition + window.scrollY - SCROLL_OFFSET
|
|
|
|
window.scrollTo({
|
|
top: offsetPosition,
|
|
behavior: "instant",
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}, [isSingleRoomAndHasSelection])
|
|
|
|
return (
|
|
<>
|
|
{noAvailableRooms || (bookingCode && !isBookingCodeRatesAvailable) ? (
|
|
<div className={styles.hotelAlert}>
|
|
<Alert
|
|
type={AlertTypeEnum.Info}
|
|
heading={intl.formatMessage({ id: "No availability" })}
|
|
text={
|
|
bookingCode && !isBookingCodeRatesAvailable
|
|
? intl.formatMessage(
|
|
{
|
|
id: "We found no available rooms using this booking code ({bookingCode}). See available rates below.",
|
|
},
|
|
{ bookingCode }
|
|
)
|
|
: intl.formatMessage({
|
|
id: "There are no rooms available that match your request.",
|
|
})
|
|
}
|
|
link={{
|
|
title: intl.formatMessage({ id: "See alternative hotels" }),
|
|
url: `${alternativeHotels(lang)}`,
|
|
keepSearchParams: true,
|
|
}}
|
|
/>
|
|
</div>
|
|
) : null}
|
|
<RoomTypeFilter />
|
|
{showBookingCodeFilter ? <BookingCodeFilter /> : null}
|
|
<ul className={styles.roomList}>
|
|
{/* Show either Booking code filtered rooms or all the rooms */}
|
|
{showAllRooms
|
|
? rooms.map((roomConfiguration) => (
|
|
<RoomCard
|
|
key={roomConfiguration.roomTypeCode}
|
|
roomConfiguration={roomConfiguration}
|
|
/>
|
|
))
|
|
: activeCodeFilter === BookingCodeFilterEnum.Discounted
|
|
? bookingCodeDiscountedRooms.map((roomConfiguration) => (
|
|
<RoomCard
|
|
key={roomConfiguration.roomTypeCode}
|
|
roomConfiguration={roomConfiguration}
|
|
/>
|
|
))
|
|
: regularRateRooms.map((roomConfiguration) => (
|
|
<RoomCard
|
|
key={roomConfiguration.roomTypeCode}
|
|
roomConfiguration={roomConfiguration}
|
|
/>
|
|
))}
|
|
</ul>
|
|
</>
|
|
)
|
|
}
|