fix: unite all price details modals to one and align on ui

This commit is contained in:
Simon Emanuelsson
2025-04-15 15:04:11 +02:00
committed by Michael Zetterberg
parent 8152aea649
commit 1f94c581ae
54 changed files with 1926 additions and 746 deletions

View File

@@ -4,6 +4,10 @@ import { useIntl } from "react-intl"
import CampaignRateCard from "@scandic-hotels/design-system/CampaignRateCard"
import NoRateAvailableCard from "@scandic-hotels/design-system/NoRateAvailableCard"
import {
sumPackages,
sumPackagesRequestedPrice,
} from "@/components/HotelReservation/utils"
import { useRoomContext } from "@/contexts/SelectRate/Room"
import useRateTitles from "@/hooks/booking/useRateTitles"
@@ -22,11 +26,15 @@ export default function Campaign({
campaign,
handleSelectRate,
nights,
petRoomPackage,
roomTypeCode,
}: CampaignProps) {
const intl = useIntl()
const { roomNr, selectedFilter, selectedRate } = useRoomContext()
const {
roomNr,
selectedFilter,
selectedPackages,
selectedRate,
} = useRoomContext()
const rateTitles = useRateTitles()
const isCampaignRate = campaign.some(
@@ -52,6 +60,9 @@ export default function Campaign({
campaign = campaign.filter((product) => !product.bookingCode)
}
const pkgsSum = sumPackages(selectedPackages)
const pkgsSumRequested = sumPackagesRequestedPrice(selectedPackages)
return campaign.map((product) => {
if (!product.public) {
return (
@@ -67,21 +78,21 @@ export default function Campaign({
const rateTermDetails = product.rateDefinitionMember
? [
{
title: product.rateDefinition.title,
terms: product.rateDefinition.generalTerms,
},
{
title: product.rateDefinitionMember.title,
terms: product.rateDefinition.generalTerms,
},
]
{
title: product.rateDefinition.title,
terms: product.rateDefinition.generalTerms,
},
{
title: product.rateDefinitionMember.title,
terms: product.rateDefinition.generalTerms,
},
]
: [
{
title: product.rateDefinition.title,
terms: product.rateDefinition.generalTerms,
},
]
{
title: product.rateDefinition.title,
terms: product.rateDefinition.generalTerms,
},
]
const isSelected = isSelectedPriceProduct(
product,
@@ -110,16 +121,18 @@ export default function Campaign({
product.public.localPrice.pricePerNight,
product.public.requestedPrice?.pricePerNight,
nights,
petRoomPackage
pkgsSum.price,
pkgsSumRequested.price
)
const pricePerNightMember = product.member
? calculatePricePerNightPriceProduct(
product.member.localPrice.pricePerNight,
product.member.requestedPrice?.pricePerNight,
nights,
petRoomPackage
)
product.member.localPrice.pricePerNight,
product.member.requestedPrice?.pricePerNight,
nights,
pkgsSum.price,
pkgsSumRequested.price
)
: undefined
let approximateRatePrice = undefined
@@ -135,12 +148,12 @@ export default function Campaign({
const approximateRate =
approximateRatePrice && product.public.requestedPrice
? {
label: intl.formatMessage({
defaultMessage: "Approx.",
}),
price: approximateRatePrice,
unit: product.public.requestedPrice.currency,
}
label: intl.formatMessage({
defaultMessage: "Approx.",
}),
price: approximateRatePrice,
unit: product.public.requestedPrice.currency,
}
: undefined
return (
@@ -154,12 +167,12 @@ export default function Campaign({
memberRate={
pricePerNightMember
? {
label: intl.formatMessage({
defaultMessage: "Member price",
}),
price: pricePerNightMember.totalPrice,
unit: `${product.member!.localPrice.currency}/${night}`,
}
label: intl.formatMessage({
defaultMessage: "Member price",
}),
price: pricePerNightMember.totalPrice,
unit: `${product.member!.localPrice.currency}/${night}`,
}
: undefined
}
name={`rateCode-${roomNr}-${product.public.rateCode}`}
@@ -173,15 +186,15 @@ export default function Campaign({
omnibusRate={
product.public.localPrice.omnibusPricePerNight
? {
label: intl
.formatMessage({
defaultMessage: "Lowest price (last 30 days)",
})
.toUpperCase(),
price:
product.public.localPrice.omnibusPricePerNight.toString(),
unit: product.public.localPrice.currency,
}
label: intl
.formatMessage({
defaultMessage: "Lowest price (last 30 days)",
})
.toUpperCase(),
price:
product.public.localPrice.omnibusPricePerNight.toString(),
unit: product.public.localPrice.currency,
}
: undefined
}
rateTermDetails={rateTermDetails}

View File

@@ -6,6 +6,10 @@ import CodeRateCard from "@scandic-hotels/design-system/CodeRateCard"
import { useRatesStore } from "@/stores/select-rate"
import {
sumPackages,
sumPackagesRequestedPrice,
} from "@/components/HotelReservation/utils"
import { useRoomContext } from "@/contexts/SelectRate/Room"
import useRateTitles from "@/hooks/booking/useRateTitles"
@@ -28,11 +32,11 @@ export default function Code({
code,
handleSelectRate,
nights,
petRoomPackage,
roomTypeCode,
}: CodeProps) {
const intl = useIntl()
const { roomNr, selectedFilter, selectedRate } = useRoomContext()
const { roomNr, selectedFilter, selectedPackages, selectedRate } =
useRoomContext()
const bookingCode = useRatesStore((state) => state.booking.bookingCode)
const rateTitles = useRateTitles()
const night = intl
@@ -74,11 +78,16 @@ export default function Code({
},
]
const pkgsSum = sumPackages(selectedPackages)
const pkgsSumRequested = sumPackagesRequestedPrice(selectedPackages)
if ("corporateCheque" in product) {
const { localPrice, rateCode } = product.corporateCheque
let price = `${localPrice.numberOfCheques} CC`
if (localPrice.additionalPricePerStay) {
price = `${price} + ${localPrice.additionalPricePerStay}`
price = `${price} + ${localPrice.additionalPricePerStay + pkgsSum.price}`
} else if (pkgsSum.price) {
price = `${price} + ${pkgsSum.price}`
}
const isSelected = isSelectedCorporateCheque(
@@ -87,6 +96,8 @@ export default function Code({
roomTypeCode
)
const currency = localPrice.currency ?? pkgsSum.currency?.toString() ?? ""
return (
<CodeRateCard
key={product.rate}
@@ -98,7 +109,7 @@ export default function Code({
rate={{
label: product.rateDefinition?.title,
price,
unit: localPrice.currency ?? "",
unit: currency,
}}
rateTitle={rateTitles[product.rate].title}
rateTermDetails={rateTermDetails}
@@ -140,7 +151,8 @@ export default function Code({
localPrice.pricePerNight,
requestedPrice?.pricePerNight,
nights,
petRoomPackage
pkgsSum.price,
pkgsSumRequested.price
)
const approximateRate = pricePerNight.totalRequestedPrice
@@ -157,7 +169,8 @@ export default function Code({
localPrice.regularPricePerNight,
requestedPrice?.regularPricePerNight,
nights,
petRoomPackage
pkgsSum.price,
pkgsSumRequested.price
)
const comparisonRate =

View File

@@ -3,6 +3,7 @@ import { useIntl } from "react-intl"
import PointsRateCard from "@scandic-hotels/design-system/PointsRateCard"
import { sumPackages } from "@/components/HotelReservation/utils"
import { useRoomContext } from "@/contexts/SelectRate/Room"
import useRateTitles from "@/hooks/booking/useRateTitles"
@@ -21,7 +22,7 @@ export default function Redemptions({
}: RedemptionsProps) {
const intl = useIntl()
const rateTitles = useRateTitles()
const { selectedFilter, selectedRate } = useRoomContext()
const { selectedFilter, selectedPackages, selectedRate } = useRoomContext()
if (
selectedFilter === BookingCodeFilterEnum.Discounted ||
@@ -34,6 +35,8 @@ export default function Redemptions({
const rewardNight = intl.formatMessage({
defaultMessage: "Reward night",
})
const pkgsSum = sumPackages(selectedPackages)
const breakfastIncluded = intl.formatMessage({
defaultMessage: "Breakfast included",
})
@@ -58,20 +61,34 @@ export default function Redemptions({
}
}
const rates = redemptions.map((r) => ({
additionalPrice:
r.redemption.localPrice.additionalPricePerStay &&
r.redemption.localPrice.currency
? {
currency: r.redemption.localPrice.currency,
price: r.redemption.localPrice.additionalPricePerStay.toString(),
const rates = redemptions.map((r) => {
let additionalPrice
if (r.redemption.localPrice.additionalPricePerStay) {
additionalPrice =
r.redemption.localPrice.additionalPricePerStay + pkgsSum.price
} else if (pkgsSum.price) {
additionalPrice = pkgsSum.price
}
let additionalPriceCurrency
if (r.redemption.localPrice.currency) {
additionalPriceCurrency = r.redemption.localPrice.currency
} else if (pkgsSum.currency) {
additionalPriceCurrency = pkgsSum.currency
}
return {
additionalPrice:
additionalPrice && additionalPriceCurrency
? {
currency: additionalPriceCurrency,
price: additionalPrice.toString(),
}
: undefined,
currency: "PTS",
isDisabled: !r.redemption.hasEnoughPoints,
points: r.redemption.localPrice.pointsPerStay.toString(),
rateCode: r.redemption.rateCode,
}))
: undefined,
currency: "PTS",
isDisabled: !r.redemption.hasEnoughPoints,
points: r.redemption.localPrice.pointsPerStay.toString(),
rateCode: r.redemption.rateCode,
}
})
const notEnoughPoints = rates.every((rate) => rate.isDisabled)
const firstRedemption = redemptions[0]

View File

@@ -6,6 +6,10 @@ import RegularRateCard from "@scandic-hotels/design-system/RegularRateCard"
import { useRatesStore } from "@/stores/select-rate"
import {
sumPackages,
sumPackagesRequestedPrice,
} from "@/components/HotelReservation/utils"
import { useRoomContext } from "@/contexts/SelectRate/Room"
import useRateTitles from "@/hooks/booking/useRateTitles"
@@ -34,13 +38,13 @@ interface RegularProps extends SharedRateCardProps {
export default function Regular({
handleSelectRate,
nights,
petRoomPackage,
regular,
roomTypeCode,
}: RegularProps) {
const intl = useIntl()
const rateTitles = useRateTitles()
const { isMainRoom, roomNr, selectedFilter, selectedRate } = useRoomContext()
const { isMainRoom, roomNr, selectedFilter, selectedPackages, selectedRate } =
useRoomContext()
const isUserLoggedIn = useRatesStore((state) => state.isUserLoggedIn)
if (selectedFilter === BookingCodeFilterEnum.Discounted) {
@@ -52,6 +56,8 @@ export default function Regular({
defaultMessage: "night",
})
.toUpperCase()
const pkgsSum = sumPackages(selectedPackages)
const pkgsSumRequested = sumPackagesRequestedPrice(selectedPackages)
return regular.map((product) => {
const { member, public: standard } = product
@@ -81,19 +87,21 @@ export default function Regular({
const memberPricePerNight = member
? calculatePricePerNightPriceProduct(
member.localPrice.pricePerNight,
member.requestedPrice?.pricePerNight,
nights,
petRoomPackage
)
member.localPrice.pricePerNight,
member.requestedPrice?.pricePerNight,
nights,
pkgsSum.price,
pkgsSumRequested.price
)
: undefined
const standardPricePerNight = standard
? calculatePricePerNightPriceProduct(
standard.localPrice.pricePerNight,
standard.requestedPrice?.pricePerNight,
nights,
petRoomPackage
)
standard.localPrice.pricePerNight,
standard.requestedPrice?.pricePerNight,
nights,
pkgsSum.price,
pkgsSumRequested.price
)
: undefined
let approximateMemberRatePrice = null
@@ -141,12 +149,12 @@ export default function Regular({
const approximateRate =
approximatePrice && requestedCurrency
? {
label: intl.formatMessage({
defaultMessage: "Approx.",
}),
price: approximatePrice,
unit: requestedCurrency,
}
label: intl.formatMessage({
defaultMessage: "Approx.",
}),
price: approximatePrice,
unit: requestedCurrency,
}
: undefined
const isSelected = isSelectedPriceProduct(
@@ -157,21 +165,21 @@ export default function Regular({
const rateTermDetails = product.rateDefinitionMember
? [
{
title: product.rateDefinition.title,
terms: product.rateDefinition.generalTerms,
},
{
title: product.rateDefinitionMember.title,
terms: product.rateDefinition.generalTerms,
},
]
{
title: product.rateDefinition.title,
terms: product.rateDefinition.generalTerms,
},
{
title: product.rateDefinitionMember.title,
terms: product.rateDefinition.generalTerms,
},
]
: [
{
title: product.rateDefinition.title,
terms: product.rateDefinition.generalTerms,
},
]
{
title: product.rateDefinition.title,
terms: product.rateDefinition.generalTerms,
},
]
return (
<RegularRateCard

View File

@@ -14,7 +14,6 @@ import Redemptions from "./Redemptions"
import Regular from "./Regular"
import type { RatesProps } from "@/types/components/hotelReservation/selectRate/rates"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter"
import type { Product } from "@/types/trpc/routers/hotel/roomAvailability"
@@ -35,7 +34,6 @@ export default function Rates({
actions: { selectRate },
isFetchingAdditionalRate,
selectedFilter,
selectedPackages,
} = useRoomContext()
const nights = useRatesStore((state) =>
dt(state.booking.toDate).diff(state.booking.fromDate, "days")
@@ -44,14 +42,9 @@ export default function Rates({
selectRate({ features, product, roomType, roomTypeCode })
}
const petRoomPackageSelected = selectedPackages.find(
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
)
const sharedProps = {
handleSelectRate,
nights,
petRoomPackage: petRoomPackageSelected,
roomTypeCode,
}
const showAllRates = selectedFilter === BookingCodeFilterEnum.All

View File

@@ -1,20 +1,19 @@
import type { Package } from "@/types/requests/packages"
export function calculatePricePerNightPriceProduct(
pricePerNight: number,
requestedPricePerNight: number | undefined,
nights: number,
petRoomPackage?: Package
packagesSumLocal: number,
packagesSumRequested: number
) {
const totalPrice = petRoomPackage?.localPrice
? Math.floor(pricePerNight + petRoomPackage.localPrice.price / nights)
const totalPrice = packagesSumLocal
? Math.floor(pricePerNight + packagesSumLocal / nights)
: Math.floor(pricePerNight)
let totalRequestedPrice = undefined
if (requestedPricePerNight) {
if (petRoomPackage?.requestedPrice) {
if (packagesSumRequested) {
totalRequestedPrice = Math.floor(
requestedPricePerNight + petRoomPackage.requestedPrice.price / nights
requestedPricePerNight + packagesSumRequested / nights
)
} else {
totalRequestedPrice = Math.floor(requestedPricePerNight)