229 lines
7.3 KiB
TypeScript
229 lines
7.3 KiB
TypeScript
"use client"
|
||
|
||
import { cx } from "class-variance-authority"
|
||
import { Button } from "react-aria-components"
|
||
import { useWatch } from "react-hook-form"
|
||
import { useIntl } from "react-intl"
|
||
|
||
import { shortDateFormat } from "@scandic-hotels/common/constants/dateFormats"
|
||
import { dt } from "@scandic-hotels/common/dt"
|
||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
|
||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||
|
||
import useLang from "../../../hooks/useLang"
|
||
|
||
import styles from "./button.module.css"
|
||
|
||
import type { BookingWidgetSchema } from "../Client"
|
||
|
||
type BookingWidgetToggleButtonProps = {
|
||
openMobileSearch: () => void
|
||
isFloating: boolean
|
||
}
|
||
export default function MobileToggleButton({
|
||
openMobileSearch,
|
||
isFloating,
|
||
}: BookingWidgetToggleButtonProps) {
|
||
const intl = useIntl()
|
||
const lang = useLang()
|
||
const date = useWatch<BookingWidgetSchema, "date">({ name: "date" })
|
||
const rooms = useWatch<BookingWidgetSchema, "rooms">({ name: "rooms" })
|
||
const searchTerm = useWatch<BookingWidgetSchema, "search">({ name: "search" })
|
||
const selectedSearchTerm = useWatch<BookingWidgetSchema, "selectedSearch">({
|
||
name: "selectedSearch",
|
||
})
|
||
|
||
const selectedFromDate = dt(date.fromDate)
|
||
.locale(lang)
|
||
.format(shortDateFormat[lang])
|
||
const selectedToDate = dt(date.toDate)
|
||
.locale(lang)
|
||
.format(shortDateFormat[lang])
|
||
|
||
const locationAndDateIsSet = searchTerm && date
|
||
|
||
const totalNights = dt(date.toDate).diff(dt(date.fromDate), "days")
|
||
const totalRooms = rooms.length
|
||
const totalAdults = rooms.reduce((acc, room) => {
|
||
if (room.adults) {
|
||
acc = acc + room.adults
|
||
}
|
||
return acc
|
||
}, 0)
|
||
const totalChildren = rooms.reduce((acc, room) => {
|
||
if (room.childrenInRoom) {
|
||
acc = acc + room.childrenInRoom.length
|
||
}
|
||
return acc
|
||
}, 0)
|
||
|
||
const totalNightsMsg = intl.formatMessage(
|
||
{
|
||
id: "booking.numberOfNights",
|
||
defaultMessage: "{totalNights, plural, one {# night} other {# nights}}",
|
||
},
|
||
{ totalNights }
|
||
)
|
||
|
||
const totalAdultsMsg = intl.formatMessage(
|
||
{
|
||
id: "booking.numberOfAdults",
|
||
defaultMessage: "{adults, plural, one {# adult} other {# adults}}",
|
||
},
|
||
{ adults: totalAdults }
|
||
)
|
||
|
||
const totalChildrenMsg = intl.formatMessage(
|
||
{
|
||
id: "booking.numberOfChildren",
|
||
defaultMessage: "{children, plural, one {# child} other {# children}}",
|
||
},
|
||
{ children: totalChildren }
|
||
)
|
||
|
||
const totalRoomsMsg = intl.formatMessage(
|
||
{
|
||
id: "booking.numberOfRooms",
|
||
defaultMessage: "{totalRooms, plural, one {# room} other {# rooms}}",
|
||
},
|
||
{ totalRooms }
|
||
)
|
||
|
||
const totalDetails = [totalAdultsMsg]
|
||
if (totalChildren > 0) {
|
||
totalDetails.push(totalChildrenMsg)
|
||
}
|
||
totalDetails.push(totalRoomsMsg)
|
||
|
||
return (
|
||
<Button
|
||
className={cx(
|
||
styles.mobileToggleButton,
|
||
locationAndDateIsSet ? styles.complete : styles.partial,
|
||
{ [styles.floating]: isFloating }
|
||
)}
|
||
onPress={openMobileSearch}
|
||
>
|
||
{!locationAndDateIsSet && (
|
||
<>
|
||
<span className={styles.block}>
|
||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||
<span className={styles.blockLabel}>
|
||
{intl.formatMessage({
|
||
id: "bookingWidget.label.whereTo",
|
||
defaultMessage: "Where to?",
|
||
})}
|
||
</span>
|
||
</Typography>
|
||
<Typography variant="Body/Paragraph/mdRegular">
|
||
<span className={styles.placeholder}>
|
||
{searchTerm
|
||
? searchTerm
|
||
: intl.formatMessage({
|
||
id: "bookingWidget.label.destination",
|
||
defaultMessage: "Destination",
|
||
})}
|
||
</span>
|
||
</Typography>
|
||
</span>
|
||
{/* Button can't contain HR elements */}
|
||
<Divider color="Border/Divider/Subtle" variant="vertical" />
|
||
<span className={styles.block}>
|
||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||
<span className={styles.blockLabel}>{totalNightsMsg}</span>
|
||
</Typography>
|
||
<Typography variant="Body/Paragraph/mdRegular">
|
||
<span className={styles.placeholder}>
|
||
{intl.formatMessage(
|
||
{
|
||
id: "booking.selectedDateRange",
|
||
defaultMessage: "{selectedFromDate} – {selectedToDate}",
|
||
},
|
||
{
|
||
selectedFromDate,
|
||
selectedToDate,
|
||
}
|
||
)}
|
||
</span>
|
||
</Typography>
|
||
</span>
|
||
<span className={styles.icon}>
|
||
<MaterialIcon icon="search" color="Icon/Inverted" />
|
||
</span>
|
||
</>
|
||
)}
|
||
|
||
{locationAndDateIsSet && (
|
||
<>
|
||
<span className={styles.block}>
|
||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||
<span className={styles.blockLabel}>{selectedSearchTerm}</span>
|
||
</Typography>
|
||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||
<span className={styles.locationAndDate}>
|
||
{intl.formatMessage(
|
||
{
|
||
id: "bookingWidget.mobile.selectedSummary",
|
||
defaultMessage:
|
||
"{selectedFromDate} – {selectedToDate} ({totalNights}) {details}",
|
||
},
|
||
{
|
||
selectedFromDate,
|
||
selectedToDate,
|
||
totalNights: totalNightsMsg,
|
||
details: totalDetails.join(", "),
|
||
}
|
||
)}
|
||
</span>
|
||
</Typography>
|
||
</span>
|
||
<span className={styles.icon}>
|
||
<MaterialIcon icon="edit_square" color="Icon/Inverted" />
|
||
</span>
|
||
</>
|
||
)}
|
||
</Button>
|
||
)
|
||
}
|
||
|
||
export function MobileToggleButtonSkeleton() {
|
||
const intl = useIntl()
|
||
|
||
return (
|
||
<div className={cx(styles.mobileToggleButton, styles.partial)}>
|
||
<span className={styles.block}>
|
||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||
<span className={styles.blockLabel}>
|
||
{intl.formatMessage({
|
||
id: "bookingWidget.label.whereTo",
|
||
defaultMessage: "Where to?",
|
||
})}
|
||
</span>
|
||
</Typography>
|
||
<SkeletonShimmer display="block" height="20px" width="11ch" />
|
||
</span>
|
||
<Divider color="Border/Divider/Subtle" variant="vertical" />
|
||
<span className={styles.block}>
|
||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||
<span className={styles.blockLabel}>
|
||
{intl.formatMessage(
|
||
{
|
||
id: "booking.numberOfNights",
|
||
defaultMessage:
|
||
"{totalNights, plural, one {# night} other {# nights}}",
|
||
},
|
||
{ totalNights: 0 }
|
||
)}
|
||
</span>
|
||
</Typography>
|
||
<SkeletonShimmer display="block" height="20px" width="13ch" />
|
||
</span>
|
||
<span className={styles.icon}>
|
||
<MaterialIcon icon="search" color="Icon/Inverted" />
|
||
</span>
|
||
</div>
|
||
)
|
||
}
|