feat: SW-1588 Optimized as per review comments

This commit is contained in:
Hrishikesh Vaipurkar
2025-02-20 10:34:55 +01:00
parent fef31237c8
commit 71eac4e8ba
5 changed files with 140 additions and 125 deletions

View File

@@ -65,7 +65,7 @@ export default function PriceList({
return (
<dl className={styles.priceList}>
{isUserLoggedIn && isMainRoom ? null : (
{isUserLoggedIn && isMainRoom && memberLocalPrice ? null : (
<div className={styles.priceRow}>
<dt>
<Caption
@@ -101,7 +101,7 @@ export default function PriceList({
</div>
)}
{memberLocalPrice && !publicLocalPrice.regularPricePerNight ? (
{memberLocalPrice && (
<div className={styles.priceRow}>
<dt>
<Caption type="bold" color={memberLocalPrice ? "red" : "disabled"}>
@@ -128,29 +128,24 @@ export default function PriceList({
)}
</dd>
</div>
) : (
publicLocalPrice.regularPricePerNight && (
<div className={styles.priceRow}>
<dt>
<Caption color="uiTextMediumContrast">
{intl.formatMessage({ id: "Regular price" })}
</Caption>
</dt>
<dd>
<div className={styles.priceStriked}>
<Subtitle type="two" color="uiTextHighContrast">
{publicLocalPrice.regularPricePerNight}
</Subtitle>
<Body color="uiTextHighContrast" textTransform="bold">
{publicLocalPrice.currency}
<span className={styles.perNight}>
/{intl.formatMessage({ id: "night" })}
</span>
</Body>
</div>
</dd>
</div>
)
)}
{publicLocalPrice.regularPricePerNight && (
<div className={styles.priceRow}>
<dt></dt>
<dd>
<div className={styles.priceStriked}>
<Subtitle type="two" color="uiTextHighContrast">
{publicLocalPrice.regularPricePerNight}
</Subtitle>
<Body color="uiTextHighContrast" textTransform="bold">
{publicLocalPrice.currency}
<span className={styles.perNight}>
/{intl.formatMessage({ id: "night" })}
</span>
</Body>
</div>
</dd>
</div>
)}
{showRequestedPrice && (
<div className={styles.priceRow}>

View File

@@ -146,56 +146,40 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
const payLater = intl.formatMessage({ id: "Pay later" })
const payNow = intl.formatMessage({ id: "Pay now" })
function getRate(rateCode: string, rateDefinition?: RateDefinition) {
const rateObj = {
terms: rateDefinition?.generalTerms,
rateTitle:
rateDefinition?.rateType !== RateTypeEnum.Regular
? rateDefinition?.title
: undefined,
}
// Possible undefined rate definition carried from roomsAvailability possibility of null
function getRate(rateCode: string) {
switch (rateCode) {
case "change":
return {
isFlex: false,
notAvailable: false,
title: freeBooking,
...rateObj,
}
case "flex":
return {
isFlex: true,
notAvailable: false,
title: freeCancelation,
...rateObj,
}
case "save":
return {
isFlex: false,
notAvailable: false,
title: nonRefundable,
...rateObj,
}
default:
throw new Error(
`Unknown key for rate, should be "change", "flex", "save" or "special", but got ${rateCode}`
`Unknown key for rate, should be "change", "flex", "save", but got ${rateCode}`
)
}
}
function getRateInfo(product: Product) {
const rateDefinition = rateDefinitions?.filter(
(rateDefinition) =>
rateDefinition.rateCode === product.productType.public.rateCode
)[0]
if (
!product.productType.public.rateCode &&
!product.productType.member?.rateCode
) {
const possibleRate = getRate(
product.productType.public.rate,
rateDefinition
)
const possibleRate = getRate(product.productType.public.rate)
if (possibleRate) {
return {
...possibleRate,
@@ -206,8 +190,6 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
isFlex: false,
notAvailable: true,
title: "",
terms: undefined,
rateTitle: undefined,
}
}
@@ -225,15 +207,23 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
)
}
// At least one rate is required to proceed here
if (!publicRate && !memberRate) {
throw new Error(
"We should never make it here without any single available rateCode"
)
}
const specialRate = publicRate || memberRate
if (product.productType.public.rateType !== "Regular" && specialRate) {
return getRate(specialRate, rateDefinition)
// Booking code scenario which has various rate types in which only
// public rate code is allowed/obtained from the API
const isBookingCodeRate =
product.productType.public.rateType !== RateTypeEnum.Regular
if (isBookingCodeRate) {
//@ts-ignore (publicRate || memberRate) types as `string | undefined` instead of just `string`
return getRate(publicRate || memberRate)
}
// Regular rates (Save, Change, Flex) requires both public and member rates availability
if (!publicRate || !memberRate) {
throw new Error(
"We should never make it here without both public and member rateCodes"
@@ -241,10 +231,29 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
}
const key = isUserLoggedIn && isMainRoom ? memberRate : publicRate
return getRate(key, rateDefinition)
return getRate(key)
}
const isSpecialRate =
function getPartialRateDefinition(
product: Product,
rateDefinitions: RateDefinition[]
) {
return rateDefinitions
.filter((rateDefinition) =>
isUserLoggedIn && product.productType.member
? rateDefinition.rateCode === product.productType.member.rateCode
: rateDefinition.rateCode === product.productType.public.rateCode
)
.flatMap((rateDefinition) => ({
terms: rateDefinition.generalTerms,
rateTitle:
rateDefinition.rateType !== RateTypeEnum.Regular
? rateDefinition.title
: undefined,
}))[0]
}
const isBookingCodeRate =
bookingCode &&
roomConfiguration.products.every((item) => {
return item.productType.public.rateType !== RateTypeEnum.Regular
@@ -335,7 +344,7 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
<span>
<Caption color="uiTextHighContrast">{breakfastMessage}</Caption>
{bookingCode ? (
<span className={!isSpecialRate ? styles.strikedText : ""}>
<span className={!isBookingCodeRate ? styles.strikedText : ""}>
<PriceTagIcon />
{bookingCode}
</span>
@@ -343,19 +352,20 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
</span>
{roomConfiguration.products.map((product) => {
const rate = getRateInfo(product)
const rateDefinition = getPartialRateDefinition(
product,
rateDefinitions
)
const isSelectedRateCode =
selectedRate?.product.productType.public.rateCode ===
product.productType.public.rateCode ||
(selectedRate?.product.productType.member?.rateCode ===
product.productType.member?.rateCode &&
// to handle undefined === undefined scenarios in booking code rates
product.productType.member?.rateCode !== undefined)
return (
<FlexibilityOption
key={
product.productType.public.rateCode +
"_" +
product.productType.public.rate
}
key={product.productType.public.rateCode}
features={roomConfiguration.features}
isSelected={
isSelectedRateCode &&
@@ -368,8 +378,8 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
roomType={roomConfiguration.roomType}
roomTypeCode={roomConfiguration.roomTypeCode}
title={rate.title}
priceInformation={rate.terms}
rateTitle={rate.rateTitle}
priceInformation={rateDefinition.terms}
rateTitle={rateDefinition.rateTitle}
/>
)
})}

View File

@@ -19,6 +19,7 @@ import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHote
import { AlertTypeEnum } from "@/types/enums/alert"
import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter"
import { RateTypeEnum } from "@/types/enums/rateType"
import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
export default function RoomSelectionPanel() {
const { rooms } = useRoomContext()
@@ -33,67 +34,67 @@ export default function RoomSelectionPanel() {
(state) => state.activeCodeFilter
)
let filteredRooms = rooms,
isRegularRatesAvailableWithCode: boolean = false,
noAvailabilityWithBookingCode: boolean = false
if (bookingCode) {
isRegularRatesAvailableWithCode = bookingCode
? rooms?.some((room) => {
return (
room.status === AvailabilityEnum.Available &&
room.products.some(
(product) =>
product.productType.public.rateType === RateTypeEnum.Regular
)
)
})
: false
// With Booking code rates we will always obtain public rate and never a member rate,
// so we should ignore it from the logic below.
const isRegularRatesAvailableWithCode =
bookingCode &&
rooms.some(
(room) =>
room.status === AvailabilityEnum.Available &&
room.products.some(
(product) =>
product.productType.public.rateType === RateTypeEnum.Regular
)
)
noAvailabilityWithBookingCode = bookingCode
? !rooms?.some((room) => {
return (
room.status === AvailabilityEnum.Available &&
room.products.some(
(product) =>
product.productType.public.rateType !== RateTypeEnum.Regular
)
)
})
: false
// Booking codes rate comes with various rate types but Regular is reserved
// for non-booking code rates (Save, Change & Flex)
const isBookingCodeRatesAvailable =
bookingCode &&
rooms.some(
(room) =>
room.status === AvailabilityEnum.Available &&
room.products.some(
(product) =>
product.productType.public.rateType !== RateTypeEnum.Regular
)
)
filteredRooms =
noAvailabilityWithBookingCode ||
!isRegularRatesAvailableWithCode ||
activeCodeFilter === BookingCodeFilterEnum.All
? rooms
: rooms.filter((room) => {
return (
room.status === AvailabilityEnum.Available &&
room.products.every(
(product) =>
(activeCodeFilter === BookingCodeFilterEnum.Discounted &&
product.productType.public.rateType !==
RateTypeEnum.Regular) ||
(activeCodeFilter === BookingCodeFilterEnum.Regular &&
product.productType.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.productType.public.rateType !== RateTypeEnum.Regular
)
)
const regularRateRooms = rooms.filter(
(room) =>
room.status === AvailabilityEnum.Available &&
room.products.every(
(product) =>
product.productType.public.rateType === RateTypeEnum.Regular
)
)
// Show booking code filter when both of the booking code rates or regular rates are available
const showBookingCodeFilter =
isRegularRatesAvailableWithCode && isBookingCodeRatesAvailable
return (
<>
{noAvailableRooms ||
(bookingCode &&
isRegularRatesAvailableWithCode &&
noAvailabilityWithBookingCode) ? (
{noAvailableRooms || (bookingCode && !isBookingCodeRatesAvailable) ? (
<div className={styles.hotelAlert}>
<Alert
type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No availability" })}
text={
noAvailabilityWithBookingCode
bookingCode && !isBookingCodeRatesAvailable
? intl.formatMessage(
{
id: "We found no available rooms using this booking code ({bookingCode}). See available rates below.",
@@ -113,19 +114,28 @@ export default function RoomSelectionPanel() {
</div>
) : null}
<RoomTypeFilter />
{bookingCode &&
isRegularRatesAvailableWithCode &&
!noAvailabilityWithBookingCode ? (
<BookingCodeFilter />
) : null}
{showBookingCodeFilter ? <BookingCodeFilter /> : null}
<ul className={styles.roomList}>
{filteredRooms.map((roomConfiguration) => (
<RoomCard
key={roomConfiguration.roomTypeCode}
roomConfiguration={roomConfiguration}
/>
))}
{/* Show either Booking code filtered rooms or all the rooms */}
{showAllRooms
? rooms.map((roomConfiguration) => RoomCardWrap(roomConfiguration))
: activeCodeFilter === BookingCodeFilterEnum.Discounted
? bookingCodeDiscountedRooms.map((roomConfiguration) =>
RoomCardWrap(roomConfiguration)
)
: regularRateRooms.map((roomConfiguration) =>
RoomCardWrap(roomConfiguration)
)}
</ul>
</>
)
}
function RoomCardWrap(roomConfiguration: RoomConfiguration) {
return (
<RoomCard
key={roomConfiguration.roomTypeCode}
roomConfiguration={roomConfiguration}
/>
)
}

View File

@@ -524,7 +524,6 @@
"Reference": "Reference",
"Reference #{bookingNr}": "Reference #{bookingNr}",
"Reference number": "Reference number",
"Regular price": "Regular price",
"Relax": "Relax",
"Remember code": "Remember code",
"Remove card from member profile": "Remove card from member profile",

View File

@@ -44,7 +44,7 @@ export const roomConfigurationSchema = z
return product
}
// Return rate even if single when special rates
// Return rate even if single when booking code rate which can be any one of other rate types
if (product.productType.public.rateType !== RateTypeEnum.Regular) {
return product
}
@@ -73,7 +73,8 @@ export const roomConfigurationSchema = z
/**
* When all products miss at least one rateCode (member or public), we change the status to NotAvailable
* since we cannot as of now (31 january) guarantee the flow with missing rateCodes.
* Exception Special rate (Booking code rates)
* This rule applies to regular rates (Save, Change and Flex)
* Exception Booking code rate
*
* TODO: (Maybe) notify somewhere that this happened
*/