feat: SW-1356 Reward night bookingflow * feat: SW-1356 Reward night bookingflow * feat: SW-1356 Removed extra param booking call * feat: SW-1356 Optimized as review comments * feat: SW-1356 Schema validation updates * feat: SW-1356 Fix after rebase * feat: SW-1356 Optimised price.redemptions check * feat: SW-1356 Updated Props naming Approved-by: Arvid Norlin
177 lines
5.3 KiB
TypeScript
177 lines
5.3 KiB
TypeScript
"use client"
|
|
import { useEffect, useRef, useState } from "react"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { useRatesStore } from "@/stores/select-rate"
|
|
|
|
import Button from "@/components/TempDesignSystem/Button"
|
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
|
import { formatPriceWithAdditionalPrice } from "@/utils/numberFormatting"
|
|
|
|
import Summary from "./Summary"
|
|
|
|
import styles from "./mobileSummary.module.css"
|
|
|
|
import type { MobileSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary"
|
|
import { RateTypeEnum } from "@/types/enums/rateType"
|
|
import type { RoomsAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
|
|
|
|
export default function MobileSummary({
|
|
isAllRoomsSelected,
|
|
isUserLoggedIn,
|
|
totalPriceToShow,
|
|
}: MobileSummaryProps) {
|
|
const intl = useIntl()
|
|
const scrollY = useRef(0)
|
|
const [isSummaryOpen, setIsSummaryOpen] = useState(false)
|
|
|
|
const { booking, bookingRooms, roomsAvailability, rateSummary, vat } =
|
|
useRatesStore((state) => ({
|
|
booking: state.booking,
|
|
bookingRooms: state.booking.rooms,
|
|
roomsAvailability: state.roomsAvailability,
|
|
rateSummary: state.rateSummary,
|
|
vat: state.vat,
|
|
}))
|
|
|
|
function toggleSummaryOpen() {
|
|
setIsSummaryOpen(!isSummaryOpen)
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (isSummaryOpen) {
|
|
scrollY.current = window.scrollY
|
|
document.body.style.position = "fixed"
|
|
document.body.style.top = `-${scrollY.current}px`
|
|
document.body.style.width = "100%"
|
|
} else {
|
|
document.body.style.position = ""
|
|
document.body.style.top = ""
|
|
document.body.style.width = ""
|
|
window.scrollTo({
|
|
top: scrollY.current,
|
|
left: 0,
|
|
behavior: "instant",
|
|
})
|
|
}
|
|
|
|
return () => {
|
|
document.body.style.position = ""
|
|
document.body.style.top = ""
|
|
}
|
|
}, [isSummaryOpen])
|
|
|
|
const roomRateDefinitions = roomsAvailability?.find(
|
|
(ra): ra is RoomsAvailability => "rateDefinitions" in ra
|
|
)
|
|
if (!roomRateDefinitions) {
|
|
return null
|
|
}
|
|
|
|
const rateDefinitions = roomRateDefinitions.rateDefinitions
|
|
|
|
const rooms = rateSummary.map((room, index) => ({
|
|
adults: bookingRooms[index].adults,
|
|
childrenInRoom: bookingRooms[index].childrenInRoom ?? undefined,
|
|
roomType: room.roomType,
|
|
roomPrice: {
|
|
perNight: {
|
|
local: {
|
|
price: (room.public?.localPrice.pricePerNight ||
|
|
room.member?.localPrice.pricePerNight)!,
|
|
currency: (room.public?.localPrice.currency ||
|
|
room.member?.localPrice.currency)!,
|
|
},
|
|
requested: undefined,
|
|
},
|
|
perStay: {
|
|
local: {
|
|
price: (room.public?.localPrice.pricePerStay ||
|
|
room.member?.localPrice.pricePerStay)!,
|
|
currency: (room.public?.localPrice.currency ||
|
|
room.member?.localPrice.currency)!,
|
|
},
|
|
requested: undefined,
|
|
},
|
|
currency: (room.public?.localPrice.currency ||
|
|
room.member?.localPrice.currency)!,
|
|
},
|
|
roomRate: {
|
|
...room.public,
|
|
memberRate: room.member,
|
|
publicRate: room.public,
|
|
},
|
|
rateDetails: rateDefinitions.find(
|
|
(rate) =>
|
|
rate.rateCode === room.public?.rateCode ||
|
|
rate.rateCode === room.member?.rateCode
|
|
)?.generalTerms,
|
|
cancellationText:
|
|
rateDefinitions.find(
|
|
(rate) =>
|
|
rate.rateCode === room.public?.rateCode ||
|
|
rate.rateCode === room.member?.rateCode
|
|
)?.cancellationText ?? "",
|
|
}))
|
|
|
|
const containsBookingCodeRate = rateSummary.find(
|
|
(rate) => rate.public?.rateType !== RateTypeEnum.Regular
|
|
)
|
|
const showDiscounted = containsBookingCodeRate || isUserLoggedIn
|
|
|
|
return (
|
|
<div className={styles.wrapper} data-open={isSummaryOpen}>
|
|
<div className={styles.content}>
|
|
<div className={styles.summaryAccordion}>
|
|
<Summary
|
|
booking={booking}
|
|
rooms={rooms}
|
|
isMember={isUserLoggedIn}
|
|
totalPrice={totalPriceToShow}
|
|
vat={vat}
|
|
toggleSummaryOpen={toggleSummaryOpen}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className={styles.bottomSheet}>
|
|
<button
|
|
data-open={isSummaryOpen}
|
|
onClick={(e) => {
|
|
e.preventDefault()
|
|
toggleSummaryOpen()
|
|
}}
|
|
className={styles.priceDetailsButton}
|
|
>
|
|
<Caption>{intl.formatMessage({ id: "Total price" })}</Caption>
|
|
<Subtitle
|
|
color={showDiscounted ? "red" : "uiTextHighContrast"}
|
|
className={styles.wrappedText}
|
|
>
|
|
{formatPriceWithAdditionalPrice(
|
|
intl,
|
|
totalPriceToShow.local.price,
|
|
totalPriceToShow.local.currency,
|
|
totalPriceToShow.local.additionalPrice,
|
|
totalPriceToShow.local.additionalPriceCurrency
|
|
)}
|
|
</Subtitle>
|
|
<Caption color="baseTextHighContrast" type="underline">
|
|
{intl.formatMessage({ id: "See details" })}
|
|
</Caption>
|
|
</button>
|
|
<Button
|
|
intent="primary"
|
|
theme="base"
|
|
size="large"
|
|
type="submit"
|
|
fullWidth
|
|
disabled={!isAllRoomsSelected}
|
|
>
|
|
{intl.formatMessage({ id: "Continue" })}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|