Merged in feat/SW-1719-strikethrough-rates (pull request #2266)
Feat/SW-1719 strikethrough rates * feat(SW-1719): Strikethrough rate if logged in on regular rate cards * feat(SW-1719): Strikethrough rate if logged in on rate summary * feat(SW-1719): Strikethrough rate if logged in on mobile rate summary * feat(SW-1719): Strikethrough rate if logged in on enter details * feat(SW-1719): Strikethrough rate support for multiple rooms * feat(SW-1719): booking receipt fixes on confirmation page * feat(SW-1719): improve initial total price calculation * feat: harmonize enter details total price to use one and the same function Approved-by: Michael Zetterberg
This commit is contained in:
committed by
Michael Zetterberg
parent
e1ede52014
commit
85acd3453d
@@ -1,3 +1,5 @@
|
||||
import { cx } from "class-variance-authority"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import styles from "./row.module.css"
|
||||
@@ -5,9 +7,16 @@ import styles from "./row.module.css"
|
||||
interface RowProps {
|
||||
label: string
|
||||
value: string
|
||||
regularValue?: string
|
||||
isDiscounted?: boolean
|
||||
}
|
||||
|
||||
export default function BoldRow({ label, value }: RowProps) {
|
||||
export default function BoldRow({
|
||||
label,
|
||||
value,
|
||||
regularValue,
|
||||
isDiscounted = false,
|
||||
}: RowProps) {
|
||||
return (
|
||||
<tr className={styles.row}>
|
||||
<td>
|
||||
@@ -16,8 +25,15 @@ export default function BoldRow({ label, value }: RowProps) {
|
||||
</Typography>
|
||||
</td>
|
||||
<td className={styles.price}>
|
||||
{isDiscounted && regularValue ? (
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<s className={styles.strikeThroughRate}>{regularValue}</s>
|
||||
</Typography>
|
||||
) : null}
|
||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||
<span>{value}</span>
|
||||
<span className={cx({ [styles.discounted]: isDiscounted })}>
|
||||
{value}
|
||||
</span>
|
||||
</Typography>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import styles from "./row.module.css"
|
||||
|
||||
import type { CurrencyEnum } from "@/types/enums/currency"
|
||||
import type { Package } from "@/types/requests/packages"
|
||||
|
||||
interface DiscountedRegularPriceRowProps {
|
||||
currency: CurrencyEnum
|
||||
packages: Package[]
|
||||
regularPrice?: number
|
||||
}
|
||||
|
||||
export default function DiscountedRegularPriceRow({
|
||||
currency,
|
||||
packages,
|
||||
regularPrice,
|
||||
}: DiscountedRegularPriceRowProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
if (!regularPrice) {
|
||||
return null
|
||||
}
|
||||
|
||||
const totalPackagesPrice = packages.reduce(
|
||||
(total, pkg) => total + pkg.localPrice.totalPrice,
|
||||
0
|
||||
)
|
||||
|
||||
const price = formatPrice(intl, regularPrice + totalPackagesPrice, currency)
|
||||
|
||||
return (
|
||||
<tr className={styles.row}>
|
||||
<td></td>
|
||||
<td className={styles.price}>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<span>
|
||||
<s>{price}</s>
|
||||
</span>
|
||||
</Typography>
|
||||
<Caption color="uiTextMediumContrast" striked></Caption>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +1,66 @@
|
||||
import { cx } from "class-variance-authority"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import styles from "./row.module.css"
|
||||
|
||||
import type { Price } from "@/types/components/hotelReservation/price"
|
||||
|
||||
interface RowProps {
|
||||
allPricesIsDiscounted: boolean
|
||||
label: string
|
||||
value: string
|
||||
price: Price
|
||||
}
|
||||
|
||||
export default function LargeRow({ label, value }: RowProps) {
|
||||
export default function LargeRow({
|
||||
allPricesIsDiscounted,
|
||||
label,
|
||||
price,
|
||||
}: RowProps) {
|
||||
const intl = useIntl()
|
||||
const totalPrice = formatPrice(
|
||||
intl,
|
||||
price.local.price,
|
||||
price.local.currency,
|
||||
price.local.additionalPrice,
|
||||
price.local.additionalPriceCurrency
|
||||
)
|
||||
const regularPrice = price.local.regularPrice
|
||||
? formatPrice(
|
||||
intl,
|
||||
price.local.regularPrice,
|
||||
price.local.currency,
|
||||
price.local.additionalPrice,
|
||||
price.local.additionalPriceCurrency
|
||||
)
|
||||
: null
|
||||
|
||||
const isDiscounted =
|
||||
allPricesIsDiscounted ||
|
||||
(price.local.regularPrice !== undefined &&
|
||||
price.local.regularPrice > price.local.price)
|
||||
return (
|
||||
<tr className={styles.row}>
|
||||
<td>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<tr className={styles.row}>
|
||||
<td>
|
||||
<span>{label}</span>
|
||||
</Typography>
|
||||
</td>
|
||||
<td className={styles.price}>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<span>{value}</span>
|
||||
</Typography>
|
||||
</td>
|
||||
</tr>
|
||||
</td>
|
||||
<td className={styles.price}>
|
||||
{isDiscounted && regularPrice ? (
|
||||
<>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<s className={styles.strikeThroughRate}>{regularPrice}</s>
|
||||
</Typography>
|
||||
</>
|
||||
) : null}
|
||||
<span className={cx({ [styles.discounted]: isDiscounted })}>
|
||||
{totalPrice}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,15 +16,18 @@ export interface RegularPriceType {
|
||||
currency: CurrencyEnum
|
||||
pricePerNight: number
|
||||
pricePerStay: number
|
||||
regularPricePerStay: number
|
||||
}
|
||||
}
|
||||
|
||||
interface RegularPriceProps extends SharedPriceRowProps {
|
||||
isMemberRate: boolean
|
||||
price: RegularPriceType["regular"]
|
||||
}
|
||||
|
||||
export default function RegularPrice({
|
||||
bedType,
|
||||
isMemberRate,
|
||||
nights,
|
||||
packages,
|
||||
price,
|
||||
@@ -47,11 +50,21 @@ export default function RegularPrice({
|
||||
|
||||
const roomCharge = formatPrice(intl, price.pricePerStay, price.currency)
|
||||
|
||||
const regularPriceIsHigherThanPrice =
|
||||
price.regularPricePerStay > price.pricePerStay
|
||||
let regularCharge = undefined
|
||||
if (regularPriceIsHigherThanPrice) {
|
||||
regularCharge = formatPrice(intl, price.regularPricePerStay, price.currency)
|
||||
}
|
||||
const isDiscounted = isMemberRate || regularPriceIsHigherThanPrice
|
||||
|
||||
return (
|
||||
<>
|
||||
<BoldRow
|
||||
label={intl.formatMessage({ defaultMessage: "Room charge" })}
|
||||
value={roomCharge}
|
||||
regularValue={regularCharge}
|
||||
isDiscounted={isDiscounted}
|
||||
/>
|
||||
{nights > 1 ? (
|
||||
<RegularRow label={averagePriceTitle} value={avgeragePricePerNight} />
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: var(--Text-Default);
|
||||
}
|
||||
|
||||
.price {
|
||||
text-align: end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Space-x1);
|
||||
}
|
||||
|
||||
.discounted {
|
||||
color: var(--Text-Accent-Primary);
|
||||
}
|
||||
|
||||
.price .strikeThroughRate {
|
||||
text-decoration: line-through;
|
||||
color: var(--Text-Secondary);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,8 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import BookingCodeRow from "./Row/BookingCode"
|
||||
import DiscountedRegularPriceRow from "./Row/DiscountedRegularPrice"
|
||||
import HeaderRow from "./Row/Header"
|
||||
import LargeRow from "./Row/Large"
|
||||
import CorporateChequePrice, {
|
||||
@@ -32,7 +30,8 @@ import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDet
|
||||
import type { Price } from "@/types/components/hotelReservation/price"
|
||||
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
import type { CurrencyEnum } from "@/types/enums/currency"
|
||||
import type { Package, Packages } from "@/types/requests/packages"
|
||||
import type { Packages } from "@/types/requests/packages"
|
||||
import type { RateDefinition } from "@/types/trpc/routers/hotel/roomAvailability"
|
||||
|
||||
type RoomPrice =
|
||||
| CorporateChequePriceType
|
||||
@@ -49,6 +48,7 @@ export interface Room {
|
||||
childrenInRoom: Child[] | undefined
|
||||
packages: Packages | null
|
||||
price: RoomPrice
|
||||
rateDefinition: Pick<RateDefinition, "isMemberRate">
|
||||
roomType: string
|
||||
}
|
||||
|
||||
@@ -86,11 +86,22 @@ export default function PriceDetailsTable({
|
||||
const departue = dt(toDate).locale(lang).format("ddd, D MMM")
|
||||
const duration = ` ${arrival} - ${departue} (${nightsMsg})`
|
||||
|
||||
const allRoomsPackages: Package[] = rooms
|
||||
.flatMap((r) => r.packages)
|
||||
.filter((r): r is Package => !!r)
|
||||
|
||||
const isAllBreakfastIncluded = rooms.every((room) => room.breakfastIncluded)
|
||||
|
||||
const allPricesIsDiscounted = rooms.every((room) => {
|
||||
if (!("regular" in room.price)) {
|
||||
return false
|
||||
}
|
||||
if (room.rateDefinition.isMemberRate) {
|
||||
return true
|
||||
}
|
||||
if (!room.price.regular) {
|
||||
return false
|
||||
}
|
||||
|
||||
return room.price.regular.pricePerStay > room.price.regular.pricePerStay
|
||||
})
|
||||
|
||||
return (
|
||||
<table className={styles.priceDetailsTable}>
|
||||
{rooms.map((room, idx) => {
|
||||
@@ -104,10 +115,12 @@ export default function PriceDetailsTable({
|
||||
}
|
||||
}
|
||||
|
||||
let isMemberRate = false
|
||||
let price: RegularPriceType["regular"] | undefined
|
||||
if ("regular" in room.price && room.price.regular) {
|
||||
price = room.price.regular
|
||||
currency = room.price.regular.currency
|
||||
isMemberRate = room.rateDefinition.isMemberRate
|
||||
}
|
||||
|
||||
let redemptionPrice: RedemptionPriceType["redemption"] | undefined
|
||||
@@ -153,6 +166,7 @@ export default function PriceDetailsTable({
|
||||
<RegularPrice
|
||||
bedType={room.bedType}
|
||||
packages={room.packages}
|
||||
isMemberRate={isMemberRate}
|
||||
nights={nights}
|
||||
price={price}
|
||||
/>
|
||||
@@ -197,20 +211,9 @@ export default function PriceDetailsTable({
|
||||
<VatRow totalPrice={totalPrice} vat={vat} />
|
||||
|
||||
<LargeRow
|
||||
allPricesIsDiscounted={allPricesIsDiscounted}
|
||||
label={intl.formatMessage({ defaultMessage: "Price including VAT" })}
|
||||
value={formatPrice(
|
||||
intl,
|
||||
totalPrice.local.price,
|
||||
totalPrice.local.currency,
|
||||
totalPrice.local.additionalPrice,
|
||||
totalPrice.local.additionalPriceCurrency
|
||||
)}
|
||||
/>
|
||||
|
||||
<DiscountedRegularPriceRow
|
||||
currency={totalPrice.local.currency}
|
||||
packages={allRoomsPackages}
|
||||
regularPrice={totalPrice.local.regularPrice}
|
||||
price={totalPrice}
|
||||
/>
|
||||
|
||||
<BookingCodeRow
|
||||
|
||||
Reference in New Issue
Block a user