297 lines
9.5 KiB
TypeScript
297 lines
9.5 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { ChevronDown } from "react-feather"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { dt } from "@/lib/dt"
|
|
import { useDetailsStore } from "@/stores/details"
|
|
|
|
import { ArrowRightIcon } from "@/components/Icons"
|
|
import Button from "@/components/TempDesignSystem/Button"
|
|
import Divider from "@/components/TempDesignSystem/Divider"
|
|
import Link from "@/components/TempDesignSystem/Link"
|
|
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 "./summary.module.css"
|
|
|
|
import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
|
|
import type { BreakfastPackage } from "@/types/components/hotelReservation/enterDetails/breakfast"
|
|
import type { SummaryProps } from "@/types/components/hotelReservation/enterDetails/summary"
|
|
import type { DetailsState } from "@/types/stores/details"
|
|
|
|
function storeSelector(state: DetailsState) {
|
|
return {
|
|
fromDate: state.data.booking.fromDate,
|
|
toDate: state.data.booking.toDate,
|
|
bedType: state.data.bedType,
|
|
breakfast: state.data.breakfast,
|
|
toggleSummaryOpen: state.actions.toggleSummaryOpen,
|
|
setTotalPrice: state.actions.setTotalPrice,
|
|
totalPrice: state.totalPrice,
|
|
}
|
|
}
|
|
|
|
export default function Summary({ showMemberPrice, room }: SummaryProps) {
|
|
const [chosenBed, setChosenBed] = useState<BedTypeSchema>()
|
|
const [chosenBreakfast, setChosenBreakfast] = useState<
|
|
BreakfastPackage | false
|
|
>()
|
|
const intl = useIntl()
|
|
const lang = useLang()
|
|
const {
|
|
bedType,
|
|
breakfast,
|
|
fromDate,
|
|
setTotalPrice,
|
|
toDate,
|
|
toggleSummaryOpen,
|
|
totalPrice,
|
|
} = useDetailsStore(storeSelector)
|
|
|
|
const diff = dt(toDate).diff(fromDate, "days")
|
|
|
|
const nights = intl.formatMessage(
|
|
{ id: "booking.nights" },
|
|
{ totalNights: diff }
|
|
)
|
|
|
|
let color: "uiTextHighContrast" | "red" = "uiTextHighContrast"
|
|
if (showMemberPrice) {
|
|
color = "red"
|
|
}
|
|
|
|
const additionalPackageCost = room.packages?.reduce(
|
|
(acc, curr) => {
|
|
acc.local = acc.local + parseInt(curr.localPrice.totalPrice)
|
|
acc.euro = acc.euro + parseInt(curr.requestedPrice.totalPrice)
|
|
return acc
|
|
},
|
|
{ local: 0, euro: 0 }
|
|
) || { local: 0, euro: 0 }
|
|
|
|
const roomsPriceLocal = room.localPrice.price + additionalPackageCost.local
|
|
const roomsPriceEuro = room.euroPrice
|
|
? room.euroPrice.price + additionalPackageCost.euro
|
|
: undefined
|
|
|
|
useEffect(() => {
|
|
setChosenBed(bedType)
|
|
setChosenBreakfast(breakfast)
|
|
|
|
if (breakfast || breakfast === false) {
|
|
setChosenBreakfast(breakfast)
|
|
if (breakfast === false) {
|
|
setTotalPrice({
|
|
local: {
|
|
price: roomsPriceLocal,
|
|
currency: room.localPrice.currency,
|
|
},
|
|
euro:
|
|
room.euroPrice && roomsPriceEuro
|
|
? {
|
|
price: roomsPriceEuro,
|
|
currency: room.euroPrice.currency,
|
|
}
|
|
: undefined,
|
|
})
|
|
} else {
|
|
setTotalPrice({
|
|
local: {
|
|
price: roomsPriceLocal + parseInt(breakfast.localPrice.totalPrice),
|
|
currency: room.localPrice.currency,
|
|
},
|
|
euro:
|
|
room.euroPrice && roomsPriceEuro
|
|
? {
|
|
price:
|
|
roomsPriceEuro +
|
|
parseInt(breakfast.requestedPrice.totalPrice),
|
|
currency: room.euroPrice.currency,
|
|
}
|
|
: undefined,
|
|
})
|
|
}
|
|
}
|
|
}, [
|
|
bedType,
|
|
breakfast,
|
|
roomsPriceLocal,
|
|
room.localPrice.currency,
|
|
room.euroPrice,
|
|
roomsPriceEuro,
|
|
setTotalPrice,
|
|
])
|
|
|
|
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(fromDate).locale(lang).format("ddd, D MMM")}
|
|
<ArrowRightIcon color="peach80" height={15} width={15} />
|
|
{dt(toDate).locale(lang).format("ddd, D MMM")} ({nights})
|
|
</Body>
|
|
<Button
|
|
intent="text"
|
|
size="small"
|
|
className={styles.chevronButton}
|
|
onClick={toggleSummaryOpen}
|
|
>
|
|
<ChevronDown height="20" width="20" />
|
|
</Button>
|
|
</header>
|
|
<Divider color="primaryLightSubtle" />
|
|
<div className={styles.addOns}>
|
|
<div>
|
|
<div className={styles.entry}>
|
|
<Body color="uiTextHighContrast">{room.roomType}</Body>
|
|
<Caption color={color}>
|
|
{intl.formatMessage(
|
|
{ id: "{amount} {currency}" },
|
|
{
|
|
amount: intl.formatNumber(room.localPrice.price),
|
|
currency: room.localPrice.currency,
|
|
}
|
|
)}
|
|
</Caption>
|
|
</div>
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage(
|
|
{ id: "booking.adults" },
|
|
{ totalAdults: room.adults }
|
|
)}
|
|
</Caption>
|
|
{room.children?.length ? (
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage(
|
|
{ id: "booking.children" },
|
|
{ totalChildren: room.children.length }
|
|
)}
|
|
</Caption>
|
|
) : null}
|
|
<Caption color="uiTextMediumContrast">
|
|
{room.cancellationText}
|
|
</Caption>
|
|
<Link color="burgundy" href="#" variant="underscored" size="small">
|
|
{intl.formatMessage({ id: "Rate details" })}
|
|
</Link>
|
|
</div>
|
|
{room.packages
|
|
? room.packages.map((roomPackage) => (
|
|
<div className={styles.entry} key={roomPackage.code}>
|
|
<div>
|
|
<Body color="uiTextHighContrast">
|
|
{roomPackage.description}
|
|
</Body>
|
|
</div>
|
|
|
|
<Caption color="uiTextHighContrast">
|
|
{intl.formatMessage(
|
|
{ id: "{amount} {currency}" },
|
|
{
|
|
amount: roomPackage.localPrice.price,
|
|
currency: roomPackage.localPrice.currency,
|
|
}
|
|
)}
|
|
</Caption>
|
|
</div>
|
|
))
|
|
: null}
|
|
{chosenBed ? (
|
|
<div className={styles.entry}>
|
|
<div>
|
|
<Body color="uiTextHighContrast">{chosenBed.description}</Body>
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage({ id: "Based on availability" })}
|
|
</Caption>
|
|
</div>
|
|
|
|
<Caption color="uiTextHighContrast">
|
|
{intl.formatMessage(
|
|
{ id: "{amount} {currency}" },
|
|
{ amount: "0", currency: room.localPrice.currency }
|
|
)}
|
|
</Caption>
|
|
</div>
|
|
) : null}
|
|
|
|
{chosenBreakfast === false ? (
|
|
<div className={styles.entry}>
|
|
<Body color="uiTextHighContrast">
|
|
{intl.formatMessage({ id: "No breakfast" })}
|
|
</Body>
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage(
|
|
{ id: "{amount} {currency}" },
|
|
{ amount: "0", currency: room.localPrice.currency }
|
|
)}
|
|
</Caption>
|
|
</div>
|
|
) : chosenBreakfast?.code ? (
|
|
<div className={styles.entry}>
|
|
<Body color="uiTextHighContrast">
|
|
{intl.formatMessage({ id: "Breakfast buffet" })}
|
|
</Body>
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage(
|
|
{ id: "{amount} {currency}" },
|
|
{
|
|
amount: chosenBreakfast.localPrice.totalPrice,
|
|
currency: chosenBreakfast.localPrice.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.formatMessage(
|
|
{ id: "{amount} {currency}" },
|
|
{
|
|
amount: intl.formatNumber(totalPrice.local.price),
|
|
currency: totalPrice.local.currency,
|
|
}
|
|
)}
|
|
</Body>
|
|
{totalPrice.euro && (
|
|
<Caption color="uiTextMediumContrast">
|
|
{intl.formatMessage({ id: "Approx." })}{" "}
|
|
{intl.formatMessage(
|
|
{ id: "{amount} {currency}" },
|
|
{
|
|
amount: intl.formatNumber(totalPrice.euro.price),
|
|
currency: totalPrice.euro.currency,
|
|
}
|
|
)}
|
|
</Caption>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<Divider className={styles.bottomDivider} color="primaryLightSubtle" />
|
|
</div>
|
|
</section >
|
|
)
|
|
}
|