feat(BOOK-472): Different placement of no availability alert on mobile viewports

Approved-by: Bianca Widstam
This commit is contained in:
Erik Tiekstra
2025-11-10 12:38:42 +00:00
parent 89262ffb82
commit 69f852a2d3
6 changed files with 107 additions and 66 deletions

View File

@@ -1,4 +1,5 @@
"use client" "use client"
import { cx } from "class-variance-authority"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert" import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
@@ -11,11 +12,15 @@ import useLang from "../../../../../hooks/useLang"
import styles from "./alert.module.css" import styles from "./alert.module.css"
interface NoAvailabilityAlertProps {
roomIndex: number
className?: string
}
export default function NoAvailabilityAlert({ export default function NoAvailabilityAlert({
roomIndex, roomIndex,
}: { className,
roomIndex: number }: NoAvailabilityAlertProps) {
}) {
const lang = useLang() const lang = useLang()
const intl = useIntl() const intl = useIntl()
@@ -51,7 +56,7 @@ export default function NoAvailabilityAlert({
defaultMessage: "There are no rooms available that match your request.", defaultMessage: "There are no rooms available that match your request.",
}) })
return ( return (
<div className={styles.hotelAlert}> <div className={cx(styles.hotelAlert, className)}>
<Alert <Alert
type={AlertTypeEnum.Info} type={AlertTypeEnum.Info}
heading={intl.formatMessage({ heading={intl.formatMessage({
@@ -92,7 +97,7 @@ export default function NoAvailabilityAlert({
) )
return ( return (
<div className={styles.hotelAlert}> <div className={cx(styles.hotelAlert, className)}>
<Alert <Alert
type={AlertTypeEnum.Info} type={AlertTypeEnum.Info}
heading={intl.formatMessage({ heading={intl.formatMessage({

View File

@@ -0,0 +1,3 @@
.availableRooms {
color: var(--Text-Default);
}

View File

@@ -0,0 +1,71 @@
import { cx } from "class-variance-authority"
import { useIntl } from "react-intl"
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
import { useSelectRateContext } from "../../../../../../contexts/SelectRate/SelectRateContext"
import styles from "./availableRoomsCount.module.css"
interface AvailableRoomsCountProps {
roomIndex: number
className?: string
}
export function AvailableRoomsCount({
roomIndex,
className,
}: AvailableRoomsCountProps) {
const intl = useIntl()
const { isFetching, getAvailabilityForRoom } = useSelectRateContext()
const roomAvailability = getAvailabilityForRoom(roomIndex) ?? []
const availableRooms = roomAvailability.filter(
(x) => x.status === AvailabilityEnum.Available
).length
const totalRooms = roomAvailability.length
const notAllRoomsAvailableText = intl.formatMessage(
{
id: "selectRate.availableRooms.partial",
defaultMessage:
"{availableRooms}/{numberOfRooms, plural, one {# room type} other {# room types}} available",
},
{
availableRooms,
numberOfRooms: totalRooms,
}
)
const allRoomsAvailableText = intl.formatMessage(
{
id: "selectRate.availableRooms.all",
defaultMessage:
"{numberOfRooms, plural, one {# room type} other {# room types}} available",
},
{
numberOfRooms: totalRooms,
}
)
if (isFetching) {
return <SkeletonShimmer height="30px" width="25ch" />
}
return (
<Typography
variant="Title/Subtitle/md"
className={cx(styles.availableRooms, className)}
>
<p>
{availableRooms !== totalRooms
? notAllRoomsAvailableText
: allRoomsAvailableText}
</p>
</Typography>
)
}

View File

@@ -1,13 +1,9 @@
"use client" "use client"
import { useIntl } from "react-intl"
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
import { useSelectRateContext } from "../../../../../contexts/SelectRate/SelectRateContext"
import { ErrorBoundary } from "../../../../ErrorBoundary/ErrorBoundary" import { ErrorBoundary } from "../../../../ErrorBoundary/ErrorBoundary"
import NoAvailabilityAlert from "../NoAvailabilityAlert"
import { RemoveBookingCodeButton } from "./RemoveBookingCodeButton/RemoveBookingCodeButton" import { RemoveBookingCodeButton } from "./RemoveBookingCodeButton/RemoveBookingCodeButton"
import { AvailableRoomsCount } from "./AvailableRoomsCount"
import { RoomPackageFilter } from "./RoomPackageFilter" import { RoomPackageFilter } from "./RoomPackageFilter"
import styles from "./roomsHeader.module.css" import styles from "./roomsHeader.module.css"
@@ -24,7 +20,14 @@ export function RoomsHeader({ roomIndex }: { roomIndex: number }) {
function InnerRoomsHeader({ roomIndex }: { roomIndex: number }) { function InnerRoomsHeader({ roomIndex }: { roomIndex: number }) {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<AvailableRoomCount roomIndex={roomIndex} /> <AvailableRoomsCount
roomIndex={roomIndex}
className={styles.availableRoomsCount}
/>
<NoAvailabilityAlert
roomIndex={roomIndex}
className={styles.noAvailabilityAlert}
/>
<div className={styles.filters}> <div className={styles.filters}>
<RemoveBookingCodeButton /> <RemoveBookingCodeButton />
<RoomPackageFilter roomIndex={roomIndex} /> <RoomPackageFilter roomIndex={roomIndex} />
@@ -33,53 +36,3 @@ function InnerRoomsHeader({ roomIndex }: { roomIndex: number }) {
</div> </div>
) )
} }
function AvailableRoomCount({ roomIndex }: { roomIndex: number }) {
const intl = useIntl()
const { isFetching, getAvailabilityForRoom } = useSelectRateContext()
const roomAvailability = getAvailabilityForRoom(roomIndex) ?? []
const availableRooms = roomAvailability.filter(
(x) => x.status === AvailabilityEnum.Available
).length
const totalRooms = roomAvailability.length
const notAllRoomsAvailableText = intl.formatMessage(
{
id: "selectRate.availableRooms.partial",
defaultMessage:
"{availableRooms}/{numberOfRooms, plural, one {# room type} other {# room types}} available",
},
{
availableRooms,
numberOfRooms: totalRooms,
}
)
const allRoomsAvailableText = intl.formatMessage(
{
id: "selectRate.availableRooms.all",
defaultMessage:
"{numberOfRooms, plural, one {# room type} other {# room types}} available",
},
{
numberOfRooms: totalRooms,
}
)
if (isFetching) {
return <SkeletonShimmer height="30px" width="25ch" />
}
return (
<Typography variant="Title/Subtitle/md" className={styles.availableRooms}>
<p>
{availableRooms !== totalRooms
? notAllRoomsAvailableText
: allRoomsAvailableText}
</p>
</Typography>
)
}

View File

@@ -2,13 +2,22 @@
display: grid; display: grid;
gap: var(--Space-x3); gap: var(--Space-x3);
align-items: center; align-items: center;
grid-template-areas:
"availableRoomsCount"
"noAvailabilityAlert"
"filters";
} }
.availableRooms { .availableRoomsCount {
color: var(--Text-Default); grid-area: availableRoomsCount;
}
.noAvailabilityAlert {
grid-area: noAvailabilityAlert;
} }
.filters { .filters {
grid-area: filters;
display: flex; display: flex;
gap: var(--Space-x1); gap: var(--Space-x1);
align-items: flex-start; align-items: flex-start;
@@ -17,5 +26,7 @@
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.container { .container {
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto;
grid-template-areas: "availableRoomsCount filters"
"noAvailabilityAlert noAvailabilityAlert";
} }
} }

View File

@@ -2,7 +2,6 @@
import { useSelectRateContext } from "../../../../contexts/SelectRate/SelectRateContext" import { useSelectRateContext } from "../../../../contexts/SelectRate/SelectRateContext"
import { MultiRoomWrapper } from "./MultiRoomWrapper" import { MultiRoomWrapper } from "./MultiRoomWrapper"
import NoAvailabilityAlert from "./NoAvailabilityAlert"
import { RoomsHeader } from "./RoomsHeader" import { RoomsHeader } from "./RoomsHeader"
import RoomsList from "./RoomsList" import RoomsList from "./RoomsList"
@@ -28,7 +27,6 @@ export default function Rooms() {
isMultiRoom={isMultiRoom} isMultiRoom={isMultiRoom}
> >
<RoomsHeader roomIndex={idx} /> <RoomsHeader roomIndex={idx} />
<NoAvailabilityAlert roomIndex={idx} />
<RoomsList roomIndex={idx} /> <RoomsList roomIndex={idx} />
</MultiRoomWrapper> </MultiRoomWrapper>
) )