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
This commit is contained in:
Simon.Emanuelsson
2025-03-03 08:28:55 +00:00
committed by Linus Flood
parent 3f01266a75
commit c3e3fa62ec
30 changed files with 487 additions and 573 deletions

View File

@@ -25,6 +25,7 @@ import styles from "./summary.module.css"
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
import type { RoomRate } from "@/types/components/hotelReservation/enterDetails/details"
import type { SelectRateSummaryProps } from "@/types/components/hotelReservation/summary"
import { RateTypeEnum } from "@/types/enums/rateType"
export default function Summary({
booking,
@@ -56,6 +57,11 @@ export default function Summary({
const memberPrice = getMemberPrice(rooms[0].roomRate)
const containsBookingCodeRate = rooms.find(
(room) => room.roomRate.publicRate?.rateType !== RateTypeEnum.Regular
)
const showDiscounted = containsBookingCodeRate || isMember
return (
<section className={styles.summary}>
<header className={styles.header}>
@@ -103,6 +109,9 @@ export default function Summary({
const memberPrice = getMemberPrice(room.roomRate)
const showMemberPrice = !!(isMember && memberPrice && roomNumber === 1)
const isBookingCodeRate =
room.roomRate.publicRate?.rateType !== RateTypeEnum.Regular
const showDiscounted = isBookingCodeRate || showMemberPrice
const adultsMsg = intl.formatMessage(
{ id: "{totalAdults, plural, one {# adult} other {# adults}}" },
@@ -134,7 +143,7 @@ export default function Summary({
) : null}
<div className={styles.entry}>
<Body color="uiTextHighContrast">{room.roomType}</Body>
<Body color={showMemberPrice ? "red" : "uiTextHighContrast"}>
<Body color={showDiscounted ? "red" : "uiTextHighContrast"}>
{formatPrice(
intl,
room.roomPrice.perStay.local.price,
@@ -249,7 +258,11 @@ export default function Summary({
/>
</div>
<div>
<Body textTransform="bold" data-testid="total-price">
<Body
color={showDiscounted ? "red" : "uiTextHighContrast"}
textTransform="bold"
data-testid="total-price"
>
{formatPrice(
intl,
totalPrice.local.price,

View File

@@ -14,6 +14,7 @@ import Summary from "./Summary"
import styles from "./mobileSummary.module.css"
import type { MobileSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary"
import { RateTypeEnum } from "@/types/enums/rateType"
export default function MobileSummary({
isAllRoomsSelected,
@@ -71,19 +72,24 @@ export default function MobileSummary({
roomPrice: {
perNight: {
local: {
price: room.public.localPrice.pricePerNight,
currency: room.public.localPrice.currency,
price: (room.public?.localPrice.pricePerNight ||
room.member?.localPrice.pricePerNight)!,
currency: (room.public?.localPrice.currency ||
room.member?.localPrice.currency)!,
},
requested: undefined,
},
perStay: {
local: {
price: room.public.localPrice.pricePerStay,
currency: room.public.localPrice.currency,
price: (room.public?.localPrice.pricePerStay ||
room.member?.localPrice.pricePerStay)!,
currency: (room.public?.localPrice.currency ||
room.member?.localPrice.currency)!,
},
requested: undefined,
},
currency: room.public.localPrice.currency,
currency: (room.public?.localPrice.currency ||
room.member?.localPrice.currency)!,
},
roomRate: {
...room.public,
@@ -91,13 +97,23 @@ export default function MobileSummary({
publicRate: room.public,
},
rateDetails: rateDefinitions.find(
(rate) => rate.rateCode === room.public.rateCode
(rate) =>
rate.rateCode === room.public?.rateCode ||
rate.rateCode === room.member?.rateCode
)?.generalTerms,
cancellationText:
rateDefinitions.find((rate) => rate.rateCode === room.public.rateCode)
?.cancellationText ?? "",
rateDefinitions.find(
(rate) =>
rate.rateCode === room.public?.rateCode ||
rate.rateCode === room.member?.rateCode
)?.cancellationText ?? "",
}))
const containsBookingCodeRate = rateSummary.find(
(rate) => rate.public?.rateType !== RateTypeEnum.Regular
)
const showDiscounted = containsBookingCodeRate || isUserLoggedIn
return (
<div className={styles.wrapper} data-open={isSummaryOpen}>
<div className={styles.content}>
@@ -122,7 +138,7 @@ export default function MobileSummary({
className={styles.priceDetailsButton}
>
<Caption>{intl.formatMessage({ id: "Total price" })}</Caption>
<Subtitle>
<Subtitle color={showDiscounted ? "red" : "uiTextHighContrast"}>
{formatPrice(
intl,
totalPriceToShow.local.price,

View File

@@ -6,7 +6,6 @@ import { useIntl } from "react-intl"
import { dt } from "@/lib/dt"
import { useRatesStore } from "@/stores/select-rate"
import { getRates } from "@/components/HotelReservation/SelectRate/utils"
import SignupPromoDesktop from "@/components/HotelReservation/SignupPromo/Desktop"
import SignupPromoMobile from "@/components/HotelReservation/SignupPromo/Mobile"
import Button from "@/components/TempDesignSystem/Button"
@@ -23,6 +22,8 @@ import styles from "./rateSummary.module.css"
import type { RateSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate"
import { RateTypeEnum } from "@/types/enums/rateType"
export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
const {
@@ -86,19 +87,13 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
const hasMemberRates = rateSummary.some((room) => room.member)
const showMemberDiscountBanner = hasMemberRates && !isUserLoggedIn
const rates = getRates(roomsAvailability.rateDefinitions)
const freeCancelation = intl.formatMessage({ id: "Free cancellation" })
const nonRefundable = intl.formatMessage({ id: "Non-refundable" })
const freeBooking = intl.formatMessage({ id: "Free rebooking" })
const payLater = intl.formatMessage({ id: "Pay later" })
const payNow = intl.formatMessage({ id: "Pay now" })
function getRateDetails(rateCode: string) {
const rate = Object.keys(rates).find((k) =>
rates[k as keyof typeof rates].find((a) => a.rateCode === rateCode)
)
function getRateDetails(rate: Rate["rate"]) {
switch (rate) {
case "change":
return `${freeBooking}, ${payNow}`
@@ -122,6 +117,11 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
return null
}
const isBookingCodeRate = rateSummary.some(
(rate) => rate.public?.rateType !== RateTypeEnum.Regular
)
const showDiscounted = isUserLoggedIn || isBookingCodeRate
const totalPriceToShow = calculateTotalPrice(
rateSummary,
isUserLoggedIn,
@@ -134,7 +134,6 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
<div className={styles.content}>
<div className={styles.summaryText}>
{rateSummary.map((room, index) => {
const isMainRoom = index + 1 === 1
return (
<div key={index} className={styles.roomSummary}>
{rateSummary.length > 1 ? (
@@ -147,11 +146,7 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
</Subtitle>
<Body color="uiTextMediumContrast">{room.roomType}</Body>
<Caption color="uiTextMediumContrast">
{getRateDetails(
isUserLoggedIn && room.member && isMainRoom
? room.member?.rateCode
: room.public.rateCode
)}
{getRateDetails(room.rate)}
</Caption>
</>
) : (
@@ -160,11 +155,7 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
{room.roomType}
</Subtitle>
<Body color="uiTextMediumContrast">
{getRateDetails(
isUserLoggedIn && room.member && isMainRoom
? room.member?.rateCode
: room.public.rateCode
)}
{getRateDetails(room.rate)}
</Body>
</>
)}
@@ -206,9 +197,8 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
: 0
return total + memberPrice + petRoomPrice
}, 0),
currency:
rateSummary[0].member?.localPrice.currency ??
rateSummary[0].public.localPrice.currency,
currency: (rateSummary[0].member?.localPrice.currency ??
rateSummary[0].public?.localPrice.currency)!,
}}
/>
</div>
@@ -225,7 +215,7 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
<div className={styles.summaryPrice}>
<div className={styles.summaryPriceTextDesktop}>
<Subtitle
color={isUserLoggedIn ? "red" : "uiTextHighContrast"}
color={showDiscounted ? "red" : "uiTextHighContrast"}
textAlign="right"
>
{formatPrice(
@@ -253,7 +243,7 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
<Caption color="uiTextHighContrast">
{intl.formatMessage({ id: "Total price" })}
</Caption>
<Subtitle color={isUserLoggedIn ? "red" : "uiTextHighContrast"}>
<Subtitle color={showDiscounted ? "red" : "uiTextHighContrast"}>
{formatPrice(
intl,
totalPriceToShow.local.price,

View File

@@ -12,10 +12,15 @@ export const calculateTotalPrice = (
) => {
return selectedRateSummary.reduce<Price>(
(total, room, idx) => {
const priceToUse =
const rate =
isUserLoggedIn && room.member && idx + 1 === 1
? room.member
: room.public
if (!rate) {
return total
}
const isPetRoom = room.features.find(
(feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
)
@@ -31,18 +36,16 @@ export const calculateTotalPrice = (
return {
local: {
currency: priceToUse.localPrice.currency,
currency: rate.localPrice.currency,
price:
total.local.price +
priceToUse.localPrice.pricePerStay +
petRoomPrice,
total.local.price + rate.localPrice.pricePerStay + petRoomPrice,
},
requested: priceToUse.requestedPrice
requested: rate.requestedPrice
? {
currency: priceToUse.requestedPrice.currency,
currency: rate.requestedPrice.currency,
price:
(total.requested?.price ?? 0) +
priceToUse.requestedPrice.pricePerStay +
rate.requestedPrice.pricePerStay +
petRoomPrice,
}
: undefined,
@@ -50,7 +53,8 @@ export const calculateTotalPrice = (
},
{
local: {
currency: selectedRateSummary[0].public.localPrice.currency,
currency: (selectedRateSummary[0].public?.localPrice.currency ||
selectedRateSummary[0].member?.localPrice.currency)!,
price: 0,
},
requested: undefined,