Files
web/packages/booking-flow/lib/components/EnterDetails/PriceChangeDialog/PriceChangeSummary/index.tsx
Erik Tiekstra 8e08af718c feat(BOOK-743): Replaced deprecated Button component
Approved-by: Bianca Widstam
2026-01-21 09:38:38 +00:00

260 lines
11 KiB
TypeScript

"use client"
import { Fragment, useState } from "react"
import {
Dialog,
DialogTrigger,
Modal,
ModalOverlay,
} from "react-aria-components"
import { useIntl } from "react-intl"
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
import { Button } from "@scandic-hotels/design-system/Button"
import { Divider } from "@scandic-hotels/design-system/Divider"
import { IconButton } from "@scandic-hotels/design-system/IconButton"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { getRoomFeatureDescription } from "../../../../utils/getRoomFeatureDescription"
import styles from "./priceChangeSummary.module.css"
import type { RoomState } from "../../../../stores/enter-details/types"
interface PriceChangeSummaryProps {
rooms: RoomState[]
roomPrices: { prevPrice: number; newPrice?: number }[]
newTotalPrice: { price: number; currency: string }
onAccept: () => void
onCancel: () => void
}
export default function PriceChangeSummary({
rooms,
roomPrices,
newTotalPrice,
onAccept,
onCancel,
}: PriceChangeSummaryProps) {
const intl = useIntl()
const [isOpen, toggleOpen] = useState(false)
return (
<DialogTrigger>
<Button
variant="Text"
size="sm"
onPress={() => toggleOpen((isOpen) => !isOpen)}
trailingIconName="chevron_right"
>
{intl.formatMessage({
id: "enterDetails.priceChangeDialog.seePriceDetailsButton",
defaultMessage: "See price details",
})}
</Button>
<ModalOverlay isOpen={isOpen} onOpenChange={toggleOpen}>
<Modal>
<Dialog className={styles.dialog}>
{({ close }) => (
<div className={styles.content}>
<header className={styles.header}>
<Typography variant="Title/Subtitle/md">
<p>
{intl.formatMessage({
id: "common.priceDetails",
defaultMessage: "Price details",
})}
</p>
</Typography>
<IconButton
iconName="close"
variant="Muted"
emphasis
onPress={close}
className={styles.closeButton}
aria-label={intl.formatMessage({
id: "common.close",
defaultMessage: "Close",
})}
/>
</header>
<section>
<div>
{rooms.map(({ room }, idx) => {
const roomNumber = idx + 1
const newPrice = roomPrices[idx].newPrice
return (
<Fragment key={idx}>
<div className={styles.rowContainer}>
<Typography variant="Body/Paragraph/mdBold">
<p>
{rooms.length > 1
? intl.formatMessage(
{
id: "booking.roomIndex",
defaultMessage: "Room {roomIndex}",
},
{ roomIndex: roomNumber }
)
: intl.formatMessage({
id: "common.room",
defaultMessage: "Room",
})}
</p>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<p>{room.roomType}</p>
</Typography>
<div className={styles.priceRow}>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{intl.formatMessage({
id: "priceDetails.roomCharge",
defaultMessage: "Room charge",
})}
</p>
</Typography>
{newPrice ? (
<div className={styles.updatedPrice}>
<Typography
variant="Body/Supporting text (caption)/smRegular"
className={styles.prevPrice}
>
<p>
{formatPrice(
intl,
room.roomPrice.perStay.local.price,
room.roomPrice.perStay.local.currency
)}
</p>
</Typography>
<Typography variant="Body/Paragraph/mdBold">
<p>
{formatPrice(
intl,
newPrice,
room.roomPrice.perStay.local.currency
)}
</p>
</Typography>
</div>
) : (
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{formatPrice(
intl,
room.roomPrice.perStay.local.price,
room.roomPrice.perStay.local.currency
)}
</p>
</Typography>
)}
</div>
{room.breakfast && (
<div className={styles.priceRow}>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{intl.formatMessage({
id: "enterDetails.priceChangeDialog.breakfastCharge",
defaultMessage: "Breakfast charge",
})}
</p>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{formatPrice(
intl,
Number(
room.breakfast.localPrice.totalPrice
),
room.breakfast.localPrice.currency
)}
</p>
</Typography>
</div>
)}
{room.roomFeatures?.map((feature) => (
<div
className={styles.priceRow}
key={feature.itemCode}
>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{getRoomFeatureDescription(
feature.code,
feature.description,
intl
)}
</p>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{formatPrice(
intl,
Number(feature.localPrice.totalPrice),
feature.localPrice.currency
)}
</p>
</Typography>
</div>
))}
</div>
<Divider color="Border/Divider/Subtle" />
</Fragment>
)
})}
</div>
<div className={styles.rowContainer}>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "common.total",
defaultMessage: "Total",
})}
</p>
</Typography>
<div className={styles.priceRow}>
<Typography variant="Body/Paragraph/mdBold">
<p>
{intl.formatMessage({
id: "booking.priceIncludingVat",
defaultMessage: "Price including VAT",
})}
</p>
</Typography>
<Typography variant="Body/Paragraph/mdBold">
<p>
{formatPrice(
intl,
newTotalPrice.price,
newTotalPrice.currency
)}
</p>
</Typography>
</div>
</div>
</section>
<footer className={styles.footer}>
<Button variant="Secondary" onPress={onCancel}>
{intl.formatMessage({
id: "enterDetails.priceChangeDialog.cancelButton",
defaultMessage: "Back to room selection",
})}
</Button>
<Button variant="Primary" onPress={onAccept}>
{intl.formatMessage({
id: "enterDetails.priceChangeDialog.acceptButton",
defaultMessage: "Continue with new price",
})}
</Button>
</footer>
</div>
)}
</Dialog>
</Modal>
</ModalOverlay>
</DialogTrigger>
)
}