feat: translate Voucher everywhere during booking flow

This commit is contained in:
Simon Emanuelsson
2025-06-27 15:03:24 +02:00
committed by Simon.Emanuelsson
parent a0b0ed2544
commit 59f4a27cb1
10 changed files with 71 additions and 32 deletions

View File

@@ -57,7 +57,7 @@ export function mapRoomState(
formattedRoomCost = formatPrice( formattedRoomCost = formatPrice(
intl, intl,
booking.vouchers, booking.vouchers,
CurrencyEnum.Voucher intl.formatMessage({ defaultMessage: "Voucher" })
) )
} }

View File

@@ -102,6 +102,30 @@ export default function Room({
const guests = guestsParts.join(", ") const guests = guestsParts.join(", ")
const zeroPrice = formatPrice(intl, 0, defaultCurrency) const zeroPrice = formatPrice(intl, 0, defaultCurrency)
let price = showMemberPrice
? formatPrice(intl, memberPrice.amount, memberPrice.currency)
: formatPrice(
intl,
room.roomPrice.perStay.local.price,
room.roomPrice.perStay.local.currency,
room.roomPrice.perStay.local.additionalPrice,
room.roomPrice.perStay.local.additionalPriceCurrency
)
let currency: string = room.roomPrice.perStay.local.currency
const voucherCurrency = intl.formatMessage({ defaultMessage: "Voucher" })
const isVoucher = "voucher" in room.roomRate
if (isVoucher) {
currency = voucherCurrency
price = formatPrice(
intl,
room.roomPrice.perStay.local.price,
voucherCurrency,
room.roomPrice.perStay.local.additionalPrice,
room.roomPrice.perStay.local.additionalPriceCurrency
)
}
return ( return (
<> <>
<div className={styles.room} data-testid={`summary-room-${roomNumber}`}> <div className={styles.room} data-testid={`summary-room-${roomNumber}`}>
@@ -139,19 +163,7 @@ export default function Room({
[styles.discounted]: showDiscounted, [styles.discounted]: showDiscounted,
})} })}
> >
{showMemberPrice {price}
? formatPrice(
intl,
memberPrice.amount,
memberPrice.currency
)
: formatPrice(
intl,
room.roomPrice.perStay.local.price,
room.roomPrice.perStay.local.currency,
room.roomPrice.perStay.local.additionalPrice,
room.roomPrice.perStay.local.additionalPriceCurrency
)}
</p> </p>
{showDiscounted && publicPrice ? ( {showDiscounted && publicPrice ? (
<s className={styles.strikeThroughRate}> <s className={styles.strikeThroughRate}>
@@ -262,7 +274,7 @@ export default function Room({
</div> </div>
<div className={styles.prices}> <div className={styles.prices}>
<span className={styles.price}> <span className={styles.price}>
{formatPrice(intl, 0, room.roomPrice.perStay.local.currency)} {formatPrice(intl, 0, currency)}
</span> </span>
</div> </div>
</div> </div>
@@ -284,7 +296,7 @@ export default function Room({
</p> </p>
<div className={styles.prices}> <div className={styles.prices}>
<span className={styles.price}> <span className={styles.price}>
{formatPrice(intl, 0, room.roomPrice.perStay.local.currency)} {formatPrice(intl, 0, currency)}
</span> </span>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,7 @@ import { cx } from "class-variance-authority"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { useMediaQuery } from "usehooks-ts" import { useMediaQuery } from "usehooks-ts"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { dt } from "@scandic-hotels/common/dt" import { dt } from "@scandic-hotels/common/dt"
import { Divider } from "@scandic-hotels/design-system/Divider" import { Divider } from "@scandic-hotels/design-system/Divider"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
@@ -66,11 +67,12 @@ export default function SummaryUI({
const roomOneMemberPrice = getMemberPrice(rooms[0].room.roomRate) const roomOneMemberPrice = getMemberPrice(rooms[0].room.roomRate)
const roomOneRoomRate = rooms[0].room.roomRate const roomOneRoomRate = rooms[0].room.roomRate
const isVoucherRate = "voucher" in roomOneRoomRate
// In case of Redemption, voucher and Corporate cheque do not show approx price // In case of Redemption, voucher and Corporate cheque do not show approx price
const isSpecialRate = const isSpecialRate =
"corporateCheque" in roomOneRoomRate || "corporateCheque" in roomOneRoomRate ||
"redemption" in roomOneRoomRate || "redemption" in roomOneRoomRate ||
"voucher" in roomOneRoomRate isVoucherRate
const priceDetailsRooms = mapToPrice(rooms, isMember) const priceDetailsRooms = mapToPrice(rooms, isMember)
const isAllCampaignRate = rooms.every( const isAllCampaignRate = rooms.every(
@@ -84,6 +86,16 @@ export default function SummaryUI({
) )
const showDiscounted = containsBookingCodeRate || isMember const showDiscounted = containsBookingCodeRate || isMember
const totalCurrency = isVoucherRate
? intl.formatMessage({ defaultMessage: "Voucher" })
: totalPrice.local.currency
if (isVoucherRate && defaultCurrency === CurrencyEnum.Voucher) {
defaultCurrency = intl.formatMessage({
defaultMessage: "Voucher",
}) as CurrencyEnum
}
return ( return (
<section className={styles.summary}> <section className={styles.summary}>
<header <header
@@ -179,7 +191,7 @@ export default function SummaryUI({
{formatPrice( {formatPrice(
intl, intl,
totalPrice.local.price, totalPrice.local.price,
totalPrice.local.currency, totalCurrency,
totalPrice.local.additionalPrice, totalPrice.local.additionalPrice,
totalPrice.local.additionalPriceCurrency totalPrice.local.additionalPriceCurrency
)} )}

View File

@@ -1,12 +1,13 @@
"use client" "use client"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import SkeletonShimmer from "@/components/SkeletonShimmer" import SkeletonShimmer from "@/components/SkeletonShimmer"
import { formatPrice } from "@/utils/numberFormatting" import { formatPrice } from "@/utils/numberFormatting"
import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
export default function Vouchers({ export default function Vouchers({
currencyCode, currencyCode,
isCancelled, isCancelled,
@@ -27,7 +28,7 @@ export default function Vouchers({
const totalPrice = formatPrice( const totalPrice = formatPrice(
intl, intl,
vouchers, vouchers,
CurrencyEnum.Voucher, intl.formatMessage({ defaultMessage: "Voucher" }),
price, price,
currencyCode currencyCode
) )

View File

@@ -1,6 +1,7 @@
import { cx } from "class-variance-authority" import { cx } from "class-variance-authority"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { formatPrice } from "@/utils/numberFormatting" import { formatPrice } from "@/utils/numberFormatting"
@@ -21,10 +22,14 @@ export default function LargeRow({
price, price,
}: RowProps) { }: RowProps) {
const intl = useIntl() const intl = useIntl()
const isVoucherRate = price.local.currency === CurrencyEnum.Voucher
const currency = isVoucherRate
? intl.formatMessage({ defaultMessage: "Voucher" })
: price.local.currency
const totalPrice = formatPrice( const totalPrice = formatPrice(
intl, intl,
price.local.price, price.local.price,
price.local.currency, currency,
price.local.additionalPrice, price.local.additionalPrice,
price.local.additionalPriceCurrency price.local.additionalPriceCurrency
) )

View File

@@ -1,8 +1,6 @@
"use client" "use client"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { formatPrice } from "@/utils/numberFormatting" import { formatPrice } from "@/utils/numberFormatting"
import BoldRow from "../Bold" import BoldRow from "../Bold"
@@ -37,17 +35,19 @@ export default function VoucherPrice({
return null return null
} }
const voucherCurrency = intl.formatMessage({ defaultMessage: "Voucher" })
const averagePriceTitle = intl.formatMessage({ const averagePriceTitle = intl.formatMessage({
defaultMessage: "Average price per night", defaultMessage: "Average price per night",
}) })
const averagePricePerNight = `${price.numberOfVouchers / nights} ${CurrencyEnum.Voucher}` const averagePricePerNight = `${price.numberOfVouchers / nights} ${voucherCurrency}`
return ( return (
<> <>
<BoldRow <BoldRow
label={intl.formatMessage({ defaultMessage: "Room charge" })} label={intl.formatMessage({ defaultMessage: "Room charge" })}
value={formatPrice(intl, price.numberOfVouchers, CurrencyEnum.Voucher)} value={formatPrice(intl, price.numberOfVouchers, voucherCurrency)}
/> />
{nights > 1 ? ( {nights > 1 ? (
<RegularRow label={averagePriceTitle} value={averagePricePerNight} /> <RegularRow label={averagePriceTitle} value={averagePricePerNight} />

View File

@@ -167,7 +167,8 @@ export default function RateSummary() {
const totalPriceToShow = getTotalPrice( const totalPriceToShow = getTotalPrice(
mainRoomProduct, mainRoomProduct,
rateSummary, rateSummary,
isUserLoggedIn isUserLoggedIn,
intl
) )
const rateProduct = rateSummary.find((rate) => rate?.product)?.product const rateProduct = rateSummary.find((rate) => rate?.product)?.product

View File

@@ -5,6 +5,7 @@ import { sumPackages } from "@/components/HotelReservation/utils"
import type { Packages } from "@scandic-hotels/trpc/types/packages" import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type { RedemptionProduct } from "@scandic-hotels/trpc/types/roomAvailability" import type { RedemptionProduct } from "@scandic-hotels/trpc/types/roomAvailability"
import type { IntlShape } from "react-intl"
import type { Price } from "@/types/components/hotelReservation/price" import type { Price } from "@/types/components/hotelReservation/price"
import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate" import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate"
@@ -218,7 +219,8 @@ export function calculateCorporateChequePrice(selectedRateSummary: Rate[]) {
export function getTotalPrice( export function getTotalPrice(
mainRoomProduct: Rate | null, mainRoomProduct: Rate | null,
rateSummary: Array<Rate | null>, rateSummary: Array<Rate | null>,
isUserLoggedIn: boolean isUserLoggedIn: boolean,
intl: IntlShape
): Price | null { ): Price | null {
const summaryArray = rateSummary.filter((rate): rate is Rate => rate !== null) const summaryArray = rateSummary.filter((rate): rate is Rate => rate !== null)
@@ -237,7 +239,11 @@ export function getTotalPrice(
return calculateRedemptionTotalPrice(product.redemption, packages) return calculateRedemptionTotalPrice(product.redemption, packages)
} }
if ("voucher" in product) { if ("voucher" in product) {
return calculateVoucherPrice(summaryArray) const voucherPrice = calculateVoucherPrice(summaryArray)
voucherPrice.local.currency = intl.formatMessage({
defaultMessage: "Voucher",
}) as CurrencyEnum
return voucherPrice
} }
return calculateTotalPrice(summaryArray, isUserLoggedIn) return calculateTotalPrice(summaryArray, isUserLoggedIn)

View File

@@ -3,13 +3,14 @@
import { useRef } from "react" import { useRef } from "react"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { createBookingConfirmationStore } from "@/stores/booking-confirmation" import { createBookingConfirmationStore } from "@/stores/booking-confirmation"
import { BookingConfirmationContext } from "@/contexts/BookingConfirmation" import { BookingConfirmationContext } from "@/contexts/BookingConfirmation"
import { formatPrice } from "@/utils/numberFormatting" import { formatPrice } from "@/utils/numberFormatting"
import type { BookingConfirmationStore } from "@/types/contexts/booking-confirmation" import type { BookingConfirmationStore } from "@/types/contexts/booking-confirmation"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import type { BookingConfirmationProviderProps } from "@/types/providers/booking-confirmation" import type { BookingConfirmationProviderProps } from "@/types/providers/booking-confirmation"
export default function BookingConfirmationProvider({ export default function BookingConfirmationProvider({
@@ -62,6 +63,7 @@ export default function BookingConfirmationProvider({
) )
} else if (totalBookingVouchers) { } else if (totalBookingVouchers) {
const room = rooms?.[0] const room = rooms?.[0]
const voucherCurrency = intl.formatMessage({ defaultMessage: "Voucher" })
if (room?.packages) { if (room?.packages) {
const pkgsSum = room.packages.reduce( const pkgsSum = room.packages.reduce(
(total, pkg) => total + pkg.totalPrice, (total, pkg) => total + pkg.totalPrice,
@@ -72,7 +74,7 @@ export default function BookingConfirmationProvider({
formattedTotalCost = formatPrice( formattedTotalCost = formatPrice(
intl, intl,
totalBookingVouchers, totalBookingVouchers,
CurrencyEnum.Voucher, voucherCurrency,
pkgsSum, pkgsSum,
currency currency
) )
@@ -81,7 +83,7 @@ export default function BookingConfirmationProvider({
formattedTotalCost = formatPrice( formattedTotalCost = formatPrice(
intl, intl,
totalBookingVouchers, totalBookingVouchers,
CurrencyEnum.Voucher voucherCurrency
) )
} }
} }

View File

@@ -52,7 +52,7 @@ export function calculateTotalPrice(
} }
if (totals.vouchers) { if (totals.vouchers) {
const appendTotalPrice = totalPrice ? `${totalPrice} + ` : "" const appendTotalPrice = totalPrice ? `${totalPrice} + ` : ""
totalPrice = `${appendTotalPrice}${totals.vouchers} ${CurrencyEnum.Voucher}` totalPrice = `${appendTotalPrice}${totals.vouchers} ${intl.formatMessage({ defaultMessage: "Voucher" })}`
} }
if (totals.cash) { if (totals.cash) {
const appendTotalPrice = totalPrice ? `${totalPrice} + ` : "" const appendTotalPrice = totalPrice ? `${totalPrice} + ` : ""