260 lines
11 KiB
TypeScript
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>
|
|
)
|
|
}
|