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

View File

@@ -146,56 +146,40 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
const payLater = intl.formatMessage({ id: "Pay later" }) const payLater = intl.formatMessage({ id: "Pay later" })
const payNow = intl.formatMessage({ id: "Pay now" }) const payNow = intl.formatMessage({ id: "Pay now" })
function getRate(rateCode: string, rateDefinition?: RateDefinition) { // Possible undefined rate definition carried from roomsAvailability possibility of null
const rateObj = { function getRate(rateCode: string) {
terms: rateDefinition?.generalTerms,
rateTitle:
rateDefinition?.rateType !== RateTypeEnum.Regular
? rateDefinition?.title
: undefined,
}
switch (rateCode) { switch (rateCode) {
case "change": case "change":
return { return {
isFlex: false, isFlex: false,
notAvailable: false, notAvailable: false,
title: freeBooking, title: freeBooking,
...rateObj,
} }
case "flex": case "flex":
return { return {
isFlex: true, isFlex: true,
notAvailable: false, notAvailable: false,
title: freeCancelation, title: freeCancelation,
...rateObj,
} }
case "save": case "save":
return { return {
isFlex: false, isFlex: false,
notAvailable: false, notAvailable: false,
title: nonRefundable, title: nonRefundable,
...rateObj,
} }
default: default:
throw new Error( 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) { function getRateInfo(product: Product) {
const rateDefinition = rateDefinitions?.filter(
(rateDefinition) =>
rateDefinition.rateCode === product.productType.public.rateCode
)[0]
if ( if (
!product.productType.public.rateCode && !product.productType.public.rateCode &&
!product.productType.member?.rateCode !product.productType.member?.rateCode
) { ) {
const possibleRate = getRate( const possibleRate = getRate(product.productType.public.rate)
product.productType.public.rate,
rateDefinition
)
if (possibleRate) { if (possibleRate) {
return { return {
...possibleRate, ...possibleRate,
@@ -206,8 +190,6 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
isFlex: false, isFlex: false,
notAvailable: true, notAvailable: true,
title: "", 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) { if (!publicRate && !memberRate) {
throw new Error( throw new Error(
"We should never make it here without any single available rateCode" "We should never make it here without any single available rateCode"
) )
} }
const specialRate = publicRate || memberRate
if (product.productType.public.rateType !== "Regular" && specialRate) { // Booking code scenario which has various rate types in which only
return getRate(specialRate, rateDefinition) // 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) { if (!publicRate || !memberRate) {
throw new Error( throw new Error(
"We should never make it here without both public and member rateCodes" "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 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 && bookingCode &&
roomConfiguration.products.every((item) => { roomConfiguration.products.every((item) => {
return item.productType.public.rateType !== RateTypeEnum.Regular return item.productType.public.rateType !== RateTypeEnum.Regular
@@ -335,7 +344,7 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
<span> <span>
<Caption color="uiTextHighContrast">{breakfastMessage}</Caption> <Caption color="uiTextHighContrast">{breakfastMessage}</Caption>
{bookingCode ? ( {bookingCode ? (
<span className={!isSpecialRate ? styles.strikedText : ""}> <span className={!isBookingCodeRate ? styles.strikedText : ""}>
<PriceTagIcon /> <PriceTagIcon />
{bookingCode} {bookingCode}
</span> </span>
@@ -343,19 +352,20 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
</span> </span>
{roomConfiguration.products.map((product) => { {roomConfiguration.products.map((product) => {
const rate = getRateInfo(product) const rate = getRateInfo(product)
const rateDefinition = getPartialRateDefinition(
product,
rateDefinitions
)
const isSelectedRateCode = const isSelectedRateCode =
selectedRate?.product.productType.public.rateCode === selectedRate?.product.productType.public.rateCode ===
product.productType.public.rateCode || product.productType.public.rateCode ||
(selectedRate?.product.productType.member?.rateCode === (selectedRate?.product.productType.member?.rateCode ===
product.productType.member?.rateCode && product.productType.member?.rateCode &&
// to handle undefined === undefined scenarios in booking code rates
product.productType.member?.rateCode !== undefined) product.productType.member?.rateCode !== undefined)
return ( return (
<FlexibilityOption <FlexibilityOption
key={ key={product.productType.public.rateCode}
product.productType.public.rateCode +
"_" +
product.productType.public.rate
}
features={roomConfiguration.features} features={roomConfiguration.features}
isSelected={ isSelected={
isSelectedRateCode && isSelectedRateCode &&
@@ -368,8 +378,8 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) {
roomType={roomConfiguration.roomType} roomType={roomConfiguration.roomType}
roomTypeCode={roomConfiguration.roomTypeCode} roomTypeCode={roomConfiguration.roomTypeCode}
title={rate.title} title={rate.title}
priceInformation={rate.terms} priceInformation={rateDefinition.terms}
rateTitle={rate.rateTitle} rateTitle={rateDefinition.rateTitle}
/> />
) )
})} })}

View File

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

View File

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

View File

@@ -44,7 +44,7 @@ export const roomConfigurationSchema = z
return product 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) { if (product.productType.public.rateType !== RateTypeEnum.Regular) {
return product 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 * 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. * 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 * TODO: (Maybe) notify somewhere that this happened
*/ */