Files
web/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/index.tsx
Simon.Emanuelsson c3e3fa62ec Merged in fix/allow-single-rateCode (pull request #1438)
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
2025-03-03 08:28:55 +00:00

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>
</>
)
}