Merged in feat/SW-2079-update-booking-page-to-show-points- (pull request #1683)
feat: SW-2079 Show points in confirmation page * feat: SW-2079 Show points in confirmation page * feat: SW-2079 Optimized code * feat: SW-2079 Updated Body to Typography * feat: SW-2079 Multi-room total cost display * feat: SW-2079 Add reward nights condition rate title * feat: SW-2079 Removed extra checks * feat: SW-2079 Optimmized formatPrice function * feat: SW-2079 Typo fix Approved-by: Christian Andolf
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { CancellationRuleEnum, ChildBedTypeEnum } from "@/constants/booking"
|
||||
import { useBookingConfirmationStore } from "@/stores/booking-confirmation"
|
||||
@@ -10,8 +11,6 @@ import { useBookingConfirmationStore } from "@/stores/booking-confirmation"
|
||||
import Modal from "@/components/Modal"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import RoomSkeletonLoader from "./RoomSkeletonLoader"
|
||||
@@ -45,29 +44,37 @@ export default function ReceiptRoom({
|
||||
return (
|
||||
<article className={styles.room}>
|
||||
<header className={styles.roomHeader}>
|
||||
<Body color="uiTextHighContrast">{room.name}</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>{room.name}</p>
|
||||
</Typography>
|
||||
{room.rateDefinition.isMemberRate ? (
|
||||
<div className={styles.memberPrice}>
|
||||
<Body color="red">
|
||||
{formatPrice(intl, room.roomPrice, currencyCode)}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.red}>{room.formattedTotalCost}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
) : (
|
||||
<Body color="uiTextHighContrast">
|
||||
{formatPrice(intl, room.roomPrice, currencyCode)}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{formatPrice(intl, room.roomPrice, currencyCode)}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "{totalAdults, plural, one {# adult} other {# adults}}" },
|
||||
{
|
||||
totalAdults: room.adults,
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{room.rateDefinition.cancellationText}
|
||||
</Caption>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextMediumContrast}>
|
||||
{intl.formatMessage(
|
||||
{ id: "{totalAdults, plural, one {# adult} other {# adults}}" },
|
||||
{
|
||||
totalAdults: room.adults,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextMediumContrast}>
|
||||
{room.rateDefinition.cancellationText}
|
||||
</p>
|
||||
</Typography>
|
||||
<Modal
|
||||
trigger={
|
||||
<Button intent="text" className={styles.termsLink}>
|
||||
@@ -83,7 +90,11 @@ export default function ReceiptRoom({
|
||||
</Link>
|
||||
</Button>
|
||||
}
|
||||
title={room.rateDefinition.cancellationText || ""}
|
||||
title={
|
||||
(room.roomPoints
|
||||
? room.rateDefinition.title
|
||||
: room.rateDefinition.cancellationText) || ""
|
||||
}
|
||||
subtitle={
|
||||
room.rateDefinition.cancellationRule ===
|
||||
CancellationRuleEnum.CancellableBefore6PM
|
||||
@@ -93,19 +104,21 @@ export default function ReceiptRoom({
|
||||
>
|
||||
<div className={styles.terms}>
|
||||
{room.rateDefinition.generalTerms?.map((info) => (
|
||||
<Body
|
||||
<Typography
|
||||
key={info}
|
||||
color="uiTextHighContrast"
|
||||
className={styles.termsText}
|
||||
variant="Body/Paragraph/mdRegular"
|
||||
>
|
||||
<MaterialIcon
|
||||
icon="check"
|
||||
color="Icon/Feedback/Success"
|
||||
size={20}
|
||||
className={styles.termsIcon}
|
||||
/>
|
||||
{info}
|
||||
</Body>
|
||||
<span>
|
||||
<MaterialIcon
|
||||
icon="check"
|
||||
color="Icon/Feedback/Success"
|
||||
size={20}
|
||||
className={styles.termsIcon}
|
||||
/>
|
||||
{info}
|
||||
</span>
|
||||
</Typography>
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
@@ -114,70 +127,98 @@ export default function ReceiptRoom({
|
||||
? room.roomFeatures.map((feature) => (
|
||||
<div className={styles.entry} key={feature.code}>
|
||||
<div>
|
||||
<Body color="uiTextHighContrast">{feature.description}</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{feature.description}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<Body color="uiTextHighContrast">
|
||||
{formatPrice(intl, feature.totalPrice, feature.currency)}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{formatPrice(intl, feature.totalPrice, feature.currency)}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
<div className={styles.entry}>
|
||||
<Body color="uiTextHighContrast">{room.bedDescription}</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{formatPrice(intl, 0, currencyCode)}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>{room.bedDescription}</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{formatPrice(intl, 0, currencyCode)}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
{childBedCrib ? (
|
||||
<div className={styles.entry}>
|
||||
<div>
|
||||
<Body color="uiTextHighContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "Crib (child) × {count}" },
|
||||
{ count: childBedCrib.quantity }
|
||||
)}
|
||||
</Body>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage({ id: "Based on availability" })}
|
||||
</Caption>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{intl.formatMessage(
|
||||
{ id: "Crib (child) × {count}" },
|
||||
{ count: childBedCrib.quantity }
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{intl.formatMessage({ id: "Based on availability" })}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<Body color="uiTextHighContrast">
|
||||
{formatPrice(intl, 0, currencyCode)}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{formatPrice(intl, 0, currencyCode)}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
) : null}
|
||||
{childBedExtraBed ? (
|
||||
<div className={styles.entry}>
|
||||
<div>
|
||||
<Body color="uiTextHighContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "Extra bed (child) × {count}" },
|
||||
{
|
||||
count: childBedExtraBed.quantity,
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{intl.formatMessage(
|
||||
{ id: "Extra bed (child) × {count}" },
|
||||
{
|
||||
count: childBedExtraBed.quantity,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<Body color="uiTextHighContrast">
|
||||
{formatPrice(intl, 0, currencyCode)}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{formatPrice(intl, 0, currencyCode)}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
) : null}
|
||||
{room.breakfast || breakfastIncluded ? (
|
||||
<div className={styles.entry}>
|
||||
<Body>{intl.formatMessage({ id: "Breakfast buffet" })}</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>{intl.formatMessage({ id: "Breakfast buffet" })}</p>
|
||||
</Typography>
|
||||
{breakfastIncluded ? (
|
||||
<Body color="red">{intl.formatMessage({ id: "Included" })}</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.red}>
|
||||
{intl.formatMessage({ id: "Included" })}
|
||||
</p>
|
||||
</Typography>
|
||||
) : null}
|
||||
{room.breakfast && !breakfastIncluded ? (
|
||||
<Body color="uiTextHighContrast">
|
||||
{formatPrice(
|
||||
intl,
|
||||
room.breakfast.totalPrice,
|
||||
room.breakfast.currency
|
||||
)}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.uiTextHighContrast}>
|
||||
{formatPrice(
|
||||
intl,
|
||||
room.breakfast.totalPrice,
|
||||
room.breakfast.currency
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -40,3 +40,15 @@
|
||||
.terms .termsIcon {
|
||||
padding-right: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.red {
|
||||
color: var(--Scandic-Brand-Scandic-Red);
|
||||
}
|
||||
|
||||
.uiTextHighContrast {
|
||||
color: var(--UI-Text-High-contrast);
|
||||
}
|
||||
|
||||
.uiTextMediumContrast {
|
||||
color: var(--UI-Text-Medium-contrast);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useBookingConfirmationStore } from "@/stores/booking-confirmation"
|
||||
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import PriceDetailsModal from "../../PriceDetailsModal"
|
||||
|
||||
@@ -15,29 +15,27 @@ import styles from "./totalPrice.module.css"
|
||||
|
||||
export default function TotalPrice() {
|
||||
const intl = useIntl()
|
||||
const { rooms, currencyCode } = useBookingConfirmationStore((state) => ({
|
||||
rooms: state.rooms,
|
||||
currencyCode: state.currencyCode,
|
||||
}))
|
||||
const { rooms, formattedTotalCost } = useBookingConfirmationStore(
|
||||
(state) => ({
|
||||
rooms: state.rooms,
|
||||
formattedTotalCost: state.formattedTotalCost,
|
||||
})
|
||||
)
|
||||
|
||||
const hasAllRoomsLoaded = rooms.every((room) => room)
|
||||
const grandTotal = rooms.reduce((acc, room) => {
|
||||
const reservationTotalPrice = room?.totalPrice || 0
|
||||
return acc + reservationTotalPrice
|
||||
}, 0)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Divider color="primaryLightSubtle" />
|
||||
<div className={styles.price}>
|
||||
<div className={styles.entry}>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({ id: "Total price" })}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>{intl.formatMessage({ id: "Total price" })}</p>
|
||||
</Typography>
|
||||
{hasAllRoomsLoaded ? (
|
||||
<Body textTransform="bold">
|
||||
{formatPrice(intl, grandTotal, currencyCode)}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>{formattedTotalCost}</p>
|
||||
</Typography>
|
||||
) : (
|
||||
<SkeletonShimmer width={"25%"} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user