From 71eac4e8baf2d070e061ef85c65029c1ef7ba81e Mon Sep 17 00:00:00 2001 From: Hrishikesh Vaipurkar Date: Thu, 20 Feb 2025 10:34:55 +0100 Subject: [PATCH] feat: SW-1588 Optimized as per review comments --- .../FlexibilityOption/PriceList/index.tsx | 45 +++--- .../RoomSelectionPanel/RoomCard/index.tsx | 80 ++++++----- .../Rooms/RoomSelectionPanel/index.tsx | 134 ++++++++++-------- apps/scandic-web/i18n/dictionaries/en.json | 1 - .../schemas/roomAvailability/configuration.ts | 5 +- 5 files changed, 140 insertions(+), 125 deletions(-) diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/index.tsx b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/index.tsx index 04ce5ab23..a421c93c2 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/index.tsx +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/index.tsx @@ -65,7 +65,7 @@ export default function PriceList({ return (
- {isUserLoggedIn && isMainRoom ? null : ( + {isUserLoggedIn && isMainRoom && memberLocalPrice ? null : (
)} - {memberLocalPrice && !publicLocalPrice.regularPricePerNight ? ( + {memberLocalPrice && (
@@ -128,29 +128,24 @@ export default function PriceList({ )}
- ) : ( - publicLocalPrice.regularPricePerNight && ( -
-
- - {intl.formatMessage({ id: "Regular price" })} - -
-
-
- - {publicLocalPrice.regularPricePerNight} - - - {publicLocalPrice.currency} - - /{intl.formatMessage({ id: "night" })} - - -
-
-
- ) + )} + {publicLocalPrice.regularPricePerNight && ( +
+
+
+
+ + {publicLocalPrice.regularPricePerNight} + + + {publicLocalPrice.currency} + + /{intl.formatMessage({ id: "night" })} + + +
+
+
)} {showRequestedPrice && (
diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/index.tsx b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/index.tsx index e0d66ff63..37377d079 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/index.tsx +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/index.tsx @@ -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) { {breakfastMessage} {bookingCode ? ( - + {bookingCode} @@ -343,19 +352,20 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) { {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 ( ) })} diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/index.tsx b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/index.tsx index 55a4dd0c0..882450e63 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/index.tsx +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/index.tsx @@ -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) ? (
) : null} - {bookingCode && - isRegularRatesAvailableWithCode && - !noAvailabilityWithBookingCode ? ( - - ) : null} + {showBookingCodeFilter ? : null}
    - {filteredRooms.map((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) + )}
) } + +function RoomCardWrap(roomConfiguration: RoomConfiguration) { + return ( + + ) +} diff --git a/apps/scandic-web/i18n/dictionaries/en.json b/apps/scandic-web/i18n/dictionaries/en.json index 88ae9ae83..66b23086a 100644 --- a/apps/scandic-web/i18n/dictionaries/en.json +++ b/apps/scandic-web/i18n/dictionaries/en.json @@ -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", diff --git a/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/configuration.ts b/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/configuration.ts index 48b21766b..56acc0e14 100644 --- a/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/configuration.ts +++ b/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/configuration.ts @@ -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 */