Files
Anton Gunnarsson 800dc5c3c1 Merged in feat/sw-3225-move-parking-information-to-booking-flow (pull request #2614)
feat(SW-3225): Move ParkingInformation to design-system

* Inline ParkingInformation types to remove trpc dependency

* Move ParkingInformation to design-system

* Move numberFormatting to common package

* Add deps to external

* Fix imports and i18n script

* Add common as dependency

* Merge branch 'master' into feat/sw-3225-move-parking-information-to-booking-flow


Approved-by: Linus Flood
2025-08-12 12:36:31 +00:00

179 lines
5.4 KiB
TypeScript

"use client"
import { cx } from "class-variance-authority"
import { useEffect, useRef, useState } from "react"
import { Button as ButtonRAC } 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 { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { useRatesStore } from "@/stores/select-rate"
import SummaryContent from "./Content"
import { mapRate } from "./mapRate"
import { isBookingCodeRate } from "./utils"
import styles from "./mobileSummary.module.css"
import type { RoomsAvailability } from "@scandic-hotels/trpc/types/roomAvailability"
import type { MobileSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary"
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 = ""
document.body.style.width = ""
}
}, [isSummaryOpen])
const roomRateDefinitions = roomsAvailability?.find(
(ra): ra is RoomsAvailability => "rateDefinitions" in ra
)
if (!roomRateDefinitions) {
return null
}
const rooms = rateSummary.map((room, index) =>
room ? mapRate(room, index, bookingRooms, room.packages) : null
)
const containsBookingCodeRate = rateSummary.find(
(r) => r && isBookingCodeRate(r.product)
)
const showDiscounted = containsBookingCodeRate || isUserLoggedIn
const totalRegularPrice = totalPriceToShow.local?.regularPrice
? totalPriceToShow.local.regularPrice
: 0
const showStrikeThroughPrice =
totalRegularPrice > totalPriceToShow.local.price
return (
<div className={styles.wrapper} data-open={isSummaryOpen}>
<div className={styles.content}>
<div className={styles.summaryAccordion}>
<SummaryContent
booking={booking}
rooms={rooms}
isMember={isUserLoggedIn}
totalPrice={totalPriceToShow}
vat={vat}
toggleSummaryOpen={toggleSummaryOpen}
/>
</div>
</div>
<div className={styles.bottomSheet}>
<ButtonRAC
data-open={isSummaryOpen}
onPress={toggleSummaryOpen}
className={styles.priceDetailsButton}
>
<Typography variant="Body/Supporting text (caption)/smRegular">
<span className={styles.priceLabel}>
{intl.formatMessage({
defaultMessage: "Total price",
})}
</span>
</Typography>
<Typography variant="Title/Subtitle/lg">
<span
className={cx(styles.price, {
[styles.discounted]: showDiscounted,
})}
>
{formatPrice(
intl,
totalPriceToShow.local.price,
totalPriceToShow.local.currency,
totalPriceToShow.local.additionalPrice,
totalPriceToShow.local.additionalPriceCurrency
)}
</span>
</Typography>
{showDiscounted &&
showStrikeThroughPrice &&
totalPriceToShow.local.regularPrice ? (
<Typography variant="Body/Paragraph/mdRegular">
<s className={styles.strikeThroughRate}>
{formatPrice(
intl,
totalPriceToShow.local.regularPrice,
totalPriceToShow.local.currency
)}
</s>
</Typography>
) : null}
<Typography variant="Body/Supporting text (caption)/smBold">
<span className={styles.seeDetails}>
<span>
{intl.formatMessage({
defaultMessage: "See details",
})}
</span>
<MaterialIcon
icon="chevron_right"
color="CurrentColor"
size={20}
/>
</span>
</Typography>
</ButtonRAC>
<Button
variant="Primary"
color="Primary"
size="Large"
type="submit"
typography="Body/Paragraph/mdBold"
isDisabled={!isAllRoomsSelected}
>
{intl.formatMessage({
defaultMessage: "Continue",
})}
</Button>
</div>
</div>
)
}