Merged in chore/move-enter-details (pull request #2778)
Chore/move enter details Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
"use client"
|
||||
|
||||
import { cx } from "class-variance-authority"
|
||||
import { useIntl } from "react-intl"
|
||||
import { useMediaQuery } from "usehooks-ts"
|
||||
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { longDateFormat } from "@scandic-hotels/common/constants/dateFormats"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||
import Body from "@scandic-hotels/design-system/Body"
|
||||
import { BookingCodeChip } from "@scandic-hotels/design-system/BookingCodeChip"
|
||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import Subtitle from "@scandic-hotels/design-system/Subtitle"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import useLang from "../../../../hooks/useLang"
|
||||
import PriceDetailsModal from "../../../PriceDetailsModal"
|
||||
import { isBookingCodeRate } from "../../../SelectRate/RoomsContainer/RateSummary/utils"
|
||||
import SignupPromoDesktop from "../../../SignupPromo/Desktop"
|
||||
import { mapToPrice } from "./mapToPrice"
|
||||
import Room from "./Room"
|
||||
import { getMemberPrice } from "./utils"
|
||||
|
||||
import styles from "./ui.module.css"
|
||||
|
||||
import type { RoomState } from "../../../../stores/enter-details/types"
|
||||
import type { Price } from "../../../../types/price"
|
||||
import type { DetailsBooking } from "../../../../utils/url"
|
||||
|
||||
type EnterDetailsSummaryProps = {
|
||||
booking: DetailsBooking
|
||||
isUserLoggedIn: boolean
|
||||
totalPrice: Price
|
||||
vat: number
|
||||
rooms: RoomState[]
|
||||
toggleSummaryOpen: () => void
|
||||
defaultCurrency: CurrencyEnum
|
||||
}
|
||||
|
||||
export default function SummaryUI({
|
||||
booking,
|
||||
rooms,
|
||||
totalPrice,
|
||||
isUserLoggedIn,
|
||||
vat,
|
||||
toggleSummaryOpen,
|
||||
defaultCurrency,
|
||||
}: EnterDetailsSummaryProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const isDesktop = useMediaQuery("(min-width: 1367px)")
|
||||
|
||||
const nights = dt(booking.toDate).diff(booking.fromDate, "days")
|
||||
|
||||
const nightsMsg = intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "{totalNights, plural, one {# night} other {# nights}}",
|
||||
},
|
||||
{ totalNights: nights }
|
||||
)
|
||||
|
||||
function handleToggleSummary() {
|
||||
if (toggleSummaryOpen) {
|
||||
toggleSummaryOpen()
|
||||
}
|
||||
}
|
||||
|
||||
const roomOneGuest = rooms[0].room.guest
|
||||
const showSignupPromo =
|
||||
rooms.length === 1 &&
|
||||
!isUserLoggedIn &&
|
||||
!roomOneGuest.membershipNo &&
|
||||
!roomOneGuest.join
|
||||
|
||||
const roomOneMemberPrice = getMemberPrice(rooms[0].room.roomRate)
|
||||
|
||||
const roomOneRoomRate = rooms[0].room.roomRate
|
||||
const isVoucherRate = "voucher" in roomOneRoomRate
|
||||
|
||||
const priceDetailsRooms = mapToPrice(rooms, isUserLoggedIn)
|
||||
const isAllCampaignRate = rooms.every(
|
||||
(room) => room.room.roomRate.rateDefinition.isCampaignRate
|
||||
)
|
||||
const isAllBreakfastIncluded = rooms.every(
|
||||
(room) => room.room.roomRate.rateDefinition.breakfastIncluded
|
||||
)
|
||||
const containsBookingCodeRate = rooms.find(
|
||||
(r) => r && isBookingCodeRate(r.room.roomRate)
|
||||
)
|
||||
const showDiscounted = containsBookingCodeRate || isUserLoggedIn
|
||||
|
||||
const totalCurrency = isVoucherRate
|
||||
? CurrencyEnum.Voucher
|
||||
: totalPrice.local.currency
|
||||
|
||||
return (
|
||||
<section className={styles.summary}>
|
||||
<header
|
||||
className={styles.header}
|
||||
role="button"
|
||||
onClick={isDesktop ? undefined : handleToggleSummary}
|
||||
>
|
||||
<Subtitle className={styles.title} type="two">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Booking summary",
|
||||
})}
|
||||
</Subtitle>
|
||||
<Body className={styles.date} color="baseTextMediumContrast">
|
||||
{dt(booking.fromDate).locale(lang).format(longDateFormat[lang])}
|
||||
<MaterialIcon
|
||||
icon="arrow_right"
|
||||
color="Icon/Interactive/Secondary"
|
||||
size={15}
|
||||
/>
|
||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||
{dt(booking.toDate).locale(lang).format(longDateFormat[lang])} (
|
||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||
{nightsMsg})
|
||||
</Body>
|
||||
<MaterialIcon
|
||||
className={styles.chevronIcon}
|
||||
icon="keyboard_arrow_down"
|
||||
size={30}
|
||||
color="CurrentColor"
|
||||
/>
|
||||
</header>
|
||||
<Divider color="Border/Divider/Subtle" />
|
||||
{rooms.map(({ room }, idx) => (
|
||||
<Room
|
||||
key={idx}
|
||||
defaultCurrency={defaultCurrency}
|
||||
room={room}
|
||||
roomNumber={idx + 1}
|
||||
roomCount={rooms.length}
|
||||
isUserLoggedIn={isUserLoggedIn}
|
||||
nightsCount={nights}
|
||||
/>
|
||||
))}
|
||||
|
||||
<div>
|
||||
<div className={styles.entry}>
|
||||
<div>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "<b>Total price</b> (incl VAT)",
|
||||
},
|
||||
{
|
||||
b: (str) => (
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<span>{str}</span>
|
||||
</Typography>
|
||||
),
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
{totalPrice.requested ? (
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p className={styles.approxPrice}>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Approx. {value}",
|
||||
},
|
||||
{
|
||||
value: formatPrice(
|
||||
intl,
|
||||
totalPrice.requested.price,
|
||||
totalPrice.requested.currency,
|
||||
totalPrice.requested.additionalPrice,
|
||||
totalPrice.requested.additionalPriceCurrency
|
||||
),
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={styles.prices}>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<span
|
||||
className={cx(styles.price, {
|
||||
[styles.discounted]: showDiscounted,
|
||||
})}
|
||||
data-testid="total-price"
|
||||
>
|
||||
{formatPrice(
|
||||
intl,
|
||||
totalPrice.local.price,
|
||||
totalCurrency,
|
||||
totalPrice.local.additionalPrice,
|
||||
totalPrice.local.additionalPriceCurrency
|
||||
)}
|
||||
</span>
|
||||
</Typography>
|
||||
{showDiscounted && totalPrice.local.regularPrice ? (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
<s className={styles.strikeThroughRate}>
|
||||
{formatPrice(
|
||||
intl,
|
||||
totalPrice.local.regularPrice,
|
||||
totalPrice.local.currency
|
||||
)}
|
||||
</s>
|
||||
</p>
|
||||
</Typography>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.ctaWrapper}>
|
||||
<PriceDetailsModal
|
||||
bookingCode={booking.bookingCode}
|
||||
defaultCurrency={defaultCurrency}
|
||||
fromDate={booking.fromDate}
|
||||
rooms={priceDetailsRooms}
|
||||
toDate={booking.toDate}
|
||||
totalPrice={totalPrice}
|
||||
vat={vat}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<BookingCodeChip
|
||||
isCampaign={isAllCampaignRate}
|
||||
bookingCode={booking.bookingCode}
|
||||
isBreakfastIncluded={isAllBreakfastIncluded}
|
||||
alignCenter
|
||||
/>
|
||||
<Divider className={styles.bottomDivider} color="Border/Divider/Subtle" />
|
||||
{showSignupPromo && roomOneMemberPrice && !isUserLoggedIn ? (
|
||||
<SignupPromoDesktop
|
||||
memberPrice={roomOneMemberPrice}
|
||||
badgeContent={"✌️"}
|
||||
isEnterDetailsPage
|
||||
/>
|
||||
) : null}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user