246 lines
7.8 KiB
TypeScript
246 lines
7.8 KiB
TypeScript
"use client"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { dt } from "@/lib/dt"
|
|
import { useEnterDetailsStore } from "@/stores/enter-details"
|
|
|
|
import { ArrowRightIcon, ChevronDownSmallIcon } from "@/components/Icons"
|
|
import Button from "@/components/TempDesignSystem/Button"
|
|
import Divider from "@/components/TempDesignSystem/Divider"
|
|
import Link from "@/components/TempDesignSystem/Link"
|
|
import Popover from "@/components/TempDesignSystem/Popover"
|
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
|
import useLang from "@/hooks/useLang"
|
|
|
|
import styles from "./ui.module.css"
|
|
|
|
import type { SummaryProps } from "@/types/components/hotelReservation/summary"
|
|
import { CurrencyEnum } from "@/types/enums/currency"
|
|
import type { DetailsState } from "@/types/stores/enter-details"
|
|
|
|
export function storeSelector(state: DetailsState) {
|
|
return {
|
|
bedType: state.bedType,
|
|
booking: state.booking,
|
|
breakfast: state.breakfast,
|
|
join: state.guest.join,
|
|
membershipNo: state.guest.membershipNo,
|
|
packages: state.packages,
|
|
roomRate: state.roomRate,
|
|
roomPrice: state.roomPrice,
|
|
toggleSummaryOpen: state.actions.toggleSummaryOpen,
|
|
totalPrice: state.totalPrice,
|
|
}
|
|
}
|
|
|
|
export default function SummaryUI({
|
|
cancellationText,
|
|
isMember,
|
|
rateDetails,
|
|
roomType,
|
|
}: SummaryProps) {
|
|
const intl = useIntl()
|
|
const lang = useLang()
|
|
|
|
const {
|
|
bedType,
|
|
booking,
|
|
breakfast,
|
|
join,
|
|
membershipNo,
|
|
packages,
|
|
roomPrice,
|
|
roomRate,
|
|
toggleSummaryOpen,
|
|
totalPrice,
|
|
} = useEnterDetailsStore(storeSelector)
|
|
|
|
const adults = booking.rooms[0].adults
|
|
const children = booking.rooms[0].children
|
|
|
|
const showMemberPrice = !!(
|
|
(isMember || join || membershipNo) &&
|
|
roomRate.memberRate
|
|
)
|
|
|
|
const diff = dt(booking.toDate).diff(booking.fromDate, "days")
|
|
|
|
const nights = intl.formatMessage(
|
|
{ id: "booking.nights" },
|
|
{ totalNights: diff }
|
|
)
|
|
|
|
function handleToggleSummary() {
|
|
if (toggleSummaryOpen) {
|
|
toggleSummaryOpen()
|
|
}
|
|
}
|
|
|
|
return (
|
|
<section className={styles.summary}>
|
|
<header className={styles.header}>
|
|
<Subtitle className={styles.title} type="two">
|
|
{intl.formatMessage({ id: "Summary" })}
|
|
</Subtitle>
|
|
<Body className={styles.date} color="baseTextMediumContrast">
|
|
{dt(booking.fromDate).locale(lang).format("ddd, D MMM")}
|
|
<ArrowRightIcon color="peach80" height={15} width={15} />
|
|
{dt(booking.toDate).locale(lang).format("ddd, D MMM")} ({nights})
|
|
</Body>
|
|
<Button
|
|
intent="text"
|
|
size="small"
|
|
className={styles.chevronButton}
|
|
onClick={handleToggleSummary}
|
|
>
|
|
<ChevronDownSmallIcon height="20" width="20" />
|
|
</Button>
|
|
</header>
|
|
<Divider color="primaryLightSubtle" />
|
|
<div className={styles.addOns}>
|
|
<div>
|
|
<div className={styles.entry}>
|
|
<Body color="uiTextHighContrast">{roomType}</Body>
|
|
<Caption color={showMemberPrice ? "red" : "uiTextHighContrast"}>
|
|
{intl.formatNumber(roomPrice.local.price, {
|
|
currency: roomPrice.local.currency,
|
|
style: "currency",
|
|
})}
|
|
</Caption>
|
|
</div>
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage(
|
|
{ id: "booking.adults" },
|
|
{ totalAdults: adults }
|
|
)}
|
|
</Caption>
|
|
{children?.length ? (
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage(
|
|
{ id: "booking.children" },
|
|
{ totalChildren: children.length }
|
|
)}
|
|
</Caption>
|
|
) : null}
|
|
<Caption color="uiTextMediumContrast">{cancellationText}</Caption>
|
|
<Popover
|
|
placement="bottom left"
|
|
triggerContent={
|
|
<Caption color="burgundy" type="underline">
|
|
{intl.formatMessage({ id: "Rate details" })}
|
|
</Caption>
|
|
}
|
|
>
|
|
<aside className={styles.rateDetailsPopover}>
|
|
<header>
|
|
<Caption type="bold">{cancellationText}</Caption>
|
|
</header>
|
|
{rateDetails?.map((detail, idx) => (
|
|
<Caption key={`rateDetails-${idx}`}>{detail}</Caption>
|
|
))}
|
|
</aside>
|
|
</Popover>
|
|
</div>
|
|
{packages
|
|
? packages.map((roomPackage) => (
|
|
<div className={styles.entry} key={roomPackage.code}>
|
|
<div>
|
|
<Body color="uiTextHighContrast">
|
|
{roomPackage.description}
|
|
</Body>
|
|
</div>
|
|
|
|
<Caption color="uiTextHighContrast">
|
|
{intl.formatNumber(parseInt(roomPackage.localPrice.price), {
|
|
currency: roomPackage.localPrice.currency,
|
|
style: "currency",
|
|
})}
|
|
</Caption>
|
|
</div>
|
|
))
|
|
: null}
|
|
{bedType ? (
|
|
<div className={styles.entry}>
|
|
<div>
|
|
<Body color="uiTextHighContrast">{bedType.description}</Body>
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage({ id: "Based on availability" })}
|
|
</Caption>
|
|
</div>
|
|
|
|
<Caption color="uiTextHighContrast">
|
|
{intl.formatNumber(0, {
|
|
currency: roomPrice.local.currency,
|
|
style: "currency",
|
|
})}
|
|
</Caption>
|
|
</div>
|
|
) : null}
|
|
|
|
{breakfast === false ? (
|
|
<div className={styles.entry}>
|
|
<Body color="uiTextHighContrast">
|
|
{intl.formatMessage({ id: "No breakfast" })}
|
|
</Body>
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatNumber(0, {
|
|
currency: roomPrice.local.currency,
|
|
style: "currency",
|
|
})}
|
|
</Caption>
|
|
</div>
|
|
) : null}
|
|
{breakfast ? (
|
|
<div className={styles.entry}>
|
|
<Body color="uiTextHighContrast">
|
|
{intl.formatMessage({ id: "Breakfast buffet" })}
|
|
</Body>
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatNumber(parseInt(breakfast.localPrice.totalPrice), {
|
|
currency: breakfast.localPrice.currency,
|
|
style: "currency",
|
|
})}
|
|
</Caption>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
<Divider color="primaryLightSubtle" />
|
|
<div className={styles.total}>
|
|
<div className={styles.entry}>
|
|
<div>
|
|
<Body>
|
|
{intl.formatMessage<React.ReactNode>(
|
|
{ id: "<b>Total price</b> (incl VAT)" },
|
|
{ b: (str) => <b>{str}</b> }
|
|
)}
|
|
</Body>
|
|
<Link color="burgundy" href="" variant="underscored" size="small">
|
|
{intl.formatMessage({ id: "Price details" })}
|
|
</Link>
|
|
</div>
|
|
<div>
|
|
<Body textTransform="bold">
|
|
{intl.formatNumber(totalPrice.local.price, {
|
|
currency: totalPrice.local.currency,
|
|
style: "currency",
|
|
})}
|
|
</Body>
|
|
{totalPrice.requested && (
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage({ id: "Approx." })}{" "}
|
|
{intl.formatNumber(totalPrice.requested.price, {
|
|
currency: totalPrice.requested.currency,
|
|
style: "currency",
|
|
})}
|
|
</Caption>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<Divider className={styles.bottomDivider} color="primaryLightSubtle" />
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|