feat: SW-602 Implemented no availability states

This commit is contained in:
Hrishikesh Vaipurkar
2024-11-14 23:20:04 +01:00
parent dc3516f4e1
commit a17010a431
14 changed files with 144 additions and 38 deletions

View File

@@ -84,25 +84,33 @@ export default async function SelectRatePage({
getProfileSafely(),
])
if (!roomsAvailability) {
return "No rooms found" // TODO: Add a proper error message
}
if (!hotelData) {
return "No hotel data found" // TODO: Add a proper error message
}
const roomCategories = hotelData?.included
const noRoomsAvailable = roomsAvailability?.roomConfigurations.reduce(
(acc, room) => {
return acc && room.status === "NotAvailable"
},
true
)
return (
<>
<HotelInfoCard hotelData={hotelData} />
<Rooms
roomsAvailability={roomsAvailability}
roomCategories={roomCategories ?? []}
user={user}
packages={packages ?? []}
<HotelInfoCard
hotelData={hotelData}
noAvailability={!roomsAvailability || !!noRoomsAvailable}
/>
{roomsAvailability ? (
<Rooms
roomsAvailability={roomsAvailability}
roomCategories={roomCategories ?? []}
user={user}
packages={packages ?? []}
/>
) : null}
</>
)
}

View File

@@ -15,8 +15,12 @@ import TripAdvisorChip from "../../TripAdvisorChip"
import styles from "./hotelInfoCard.module.css"
import type { HotelInfoCardProps } from "@/types/components/hotelReservation/selectRate/hotelInfoCardProps"
import { AlertTypeEnum } from "@/types/enums/alert"
export default function HotelInfoCard({ hotelData }: HotelInfoCardProps) {
export default function HotelInfoCard({
hotelData,
noAvailability = false,
}: HotelInfoCardProps) {
const hotelAttributes = hotelData?.data.attributes
const intl = useIntl()
@@ -97,6 +101,16 @@ export default function HotelInfoCard({ hotelData }: HotelInfoCardProps) {
</div>
)
})}
{noAvailability ? (
<div className={styles.hotelAlert}>
<Alert
type={AlertTypeEnum.Info}
text={intl.formatMessage({
id: "There are no rooms available that match your request",
})}
/>
</div>
) : null}
</article>
)
}

View File

@@ -1,5 +1,5 @@
.card,
.disabledCard {
.noPricesCard {
border-radius: var(--Corner-radius-Large);
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
background-color: var(--Base-Surface-Secondary-light-Normal);
@@ -10,11 +10,11 @@
gap: var(--Spacing-x-half);
}
.disabledCard {
opacity: 0.6;
.noPricesCard {
gap: var(--Spacing-x2);
}
.disabledCard:hover {
.noPricesCard:hover {
cursor: not-allowed;
}
@@ -62,9 +62,16 @@ input[type="radio"]:checked + .card .checkIcon {
outline: none;
}
.noPricesLabel {
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
text-align: center;
background-color: var(--Base-Surface-Subtle-Normal);
border-radius: var(--Corner-radius-Rounded);
margin: 0 auto var(--Spacing-x2);
}
.popover {
background-color: var(--Main-Grey-White);
border-radius: var(--Corner-radius-Medium);
left: 0px;
max-height: 400px;

View File

@@ -1,8 +1,10 @@
"use client"
import { useRef, useState } from "react"
import { Button } from "react-aria-components"
import { useIntl } from "react-intl"
import { CheckIcon, InfoCircleIcon } from "@/components/Icons"
import Label from "@/components/TempDesignSystem/Form/Label"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import PricePopover from "./Popover"
@@ -27,6 +29,7 @@ export default function FlexibilityOption({
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
let triggerRef = useRef<HTMLButtonElement>(null)
const buttonClickedRef = useRef(false)
const intl = useIntl()
function setRef(node: Element | null) {
if (node) {
@@ -36,13 +39,17 @@ export default function FlexibilityOption({
if (!product) {
return (
<div className={styles.disabledCard}>
<div className={styles.noPricesCard}>
<div className={styles.header}>
<InfoCircleIcon width={16} height={16} color="uiTextMediumContrast" />
<Caption color="disabled">{name}</Caption>
<Caption color="disabled">({paymentTerm})</Caption>
<Caption>{name}</Caption>
<Caption color="uiTextPlaceholder">({paymentTerm})</Caption>
</div>
<PriceTable />
<Label size="regular" className={styles.noPricesLabel}>
<Caption color="uiTextHighContrast" type="bold">
{intl.formatMessage({ id: "No Prices available" })}
</Caption>
</Label>
</div>
)
}

View File

@@ -0,0 +1,12 @@
import { cva } from "class-variance-authority"
import styles from "./roomCard.module.css"
export const cardVariants = cva(styles.card, {
variants: {
availability: {
noAvailability: styles.noAvailability,
default: "",
},
},
})

View File

@@ -7,12 +7,14 @@ import { RateDefinition } from "@/server/routers/hotels/output"
import ToggleSidePeek from "@/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek"
import FlexibilityOption from "@/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption"
import { ErrorCircleIcon } from "@/components/Icons"
import ImageGallery from "@/components/ImageGallery"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { getIconForFeatureCode } from "../../utils"
import { cardVariants } from "./cardVariants"
import styles from "./roomCard.module.css"
@@ -89,8 +91,15 @@ export default function RoomCard({
[freeCancelation, freeBooking, nonRefundable]
)
const classNames = cardVariants({
availability:
roomConfiguration.status === "NotAvailable"
? "noAvailability"
: "default",
})
return (
<div className={styles.card}>
<div className={classNames}>
<div>
{mainImage && (
<div className={styles.imageContainer}>
@@ -163,23 +172,36 @@ export default function RoomCard({
id: "Breakfast selection in next step.",
})}
</Caption>
<div className={styles.flexibilityOptions}>
{Object.entries(rates).map(([key, rate]) => (
<FlexibilityOption
key={key}
name={rateKey(key)}
value={key.toLowerCase()}
paymentTerm={key === "flexRate" ? payLater : payNow}
product={findProductForRate(rate)}
priceInformation={getPriceInformationForRate(rate)}
handleSelectRate={handleSelectRate}
roomType={roomConfiguration.roomType}
roomTypeCode={roomConfiguration.roomTypeCode}
features={roomConfiguration.features}
petRoomPackage={petRoomPackage}
/>
))}
</div>
{roomConfiguration.status === "NotAvailable" ? (
<div className={styles.noRoomsContainer}>
<div className={styles.noRooms}>
<ErrorCircleIcon color="red" width={16} />
<Caption color="uiTextHighContrast" type="bold">
{intl.formatMessage({
id: "This room is not available",
})}
</Caption>
</div>
</div>
) : (
<div className={styles.flexibilityOptions}>
{Object.entries(rates).map(([key, rate]) => (
<FlexibilityOption
key={key}
name={rateKey(key)}
value={key.toLowerCase()}
paymentTerm={key === "flexRate" ? payLater : payNow}
product={findProductForRate(rate)}
priceInformation={getPriceInformationForRate(rate)}
handleSelectRate={handleSelectRate}
roomType={roomConfiguration.roomType}
roomTypeCode={roomConfiguration.roomTypeCode}
features={roomConfiguration.features}
petRoomPackage={petRoomPackage}
/>
))}
</div>
)}
</div>
</div>
)

View File

@@ -10,6 +10,22 @@
justify-content: space-between;
}
.card.noAvailability {
justify-content: flex-start;
}
.card.noAvailability:before {
background-color: rgba(0, 0, 0, 40%);
content: "";
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 2;
}
.specification {
display: flex;
flex-direction: row;
@@ -79,3 +95,16 @@
min-height: 190px;
position: relative;
}
.noRoomsContainer {
padding: var(--Spacing-x2);
background-color: var(--Base-Surface-Secondary-light-Normal);
border-radius: var(--Corner-radius-Medium);
margin: 0;
width: 100%;
}
.noRooms {
display: flex;
gap: var(--Spacing-x1);
}

View File

@@ -339,6 +339,7 @@
"There are no rooms available that match your request.": "Der er ingen værelser tilgængelige, der matcher din forespørgsel.",
"There are no transactions to display": "Der er ingen transaktioner at vise",
"Things nearby HOTEL_NAME": "Ting i nærheden af {hotelName}",
"This room is not available": "Dette værelse er ikke tilgængeligt",
"To get the member price <span>{amount} {currency}</span>, log in or join when completing the booking.": "For at få medlemsprisen <span>{amount} {currency}</span>, log ind eller tilmeld dig, når du udfylder bookingen.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "For at sikre din reservation, beder vi om at du giver os dine betalingsoplysninger. Du kan så være sikker på, at ingen gebyrer vil blive opkrævet på dette tidspunkt.",
"Total Points": "Samlet antal point",

View File

@@ -338,6 +338,7 @@
"There are no rooms available that match your request.": "Es sind keine Zimmer verfügbar, die Ihrer Anfrage entsprechen.",
"There are no transactions to display": "Es sind keine Transaktionen zum Anzeigen vorhanden",
"Things nearby HOTEL_NAME": "Dinge in der Nähe von {hotelName}",
"This room is not available": "Dieses Zimmer ist nicht verfügbar",
"To get the member price <span>{amount} {currency}</span>, log in or join when completing the booking.": "Um den Mitgliederpreis von <span>{amount} {currency}</span> zu erhalten, loggen Sie sich ein oder treten Sie Scandic Friends bei, wenn Sie die Buchung abschließen.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "Um Ihre Reservierung zu sichern, bitten wir Sie, Ihre Zahlungskarteninformationen zu geben. Sie können sicher sein, dass keine Gebühren zu diesem Zeitpunkt erhoben werden.",
"Total Points": "Gesamtpunktzahl",

View File

@@ -368,6 +368,7 @@
"There are no rooms available that match your request.": "There are no rooms available that match your request.",
"There are no transactions to display": "There are no transactions to display",
"Things nearby HOTEL_NAME": "Things nearby {hotelName}",
"This room is not available": "This room is not available",
"To get the member price <span>{amount} {currency}</span>, log in or join when completing the booking.": "To get the member price <span>{amount} {currency}</span>, log in or join when completing the booking.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.",
"Total Points": "Total Points",

View File

@@ -340,6 +340,7 @@
"There are no rooms available that match your request.": "Ei huoneita saatavilla, jotka vastaavat pyyntöäsi.",
"There are no transactions to display": "Näytettäviä tapahtumia ei ole",
"Things nearby HOTEL_NAME": "Lähellä olevia asioita {hotelName}",
"This room is not available": "Tämä huone ei ole käytettävissä",
"To get the member price <span>{amount} {currency}</span>, log in or join when completing the booking.": "Jäsenhintaan saavat sisäänkirjautuneet tai liittyneet jäsenet.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "Varmistaaksesi varauksen, pyydämme sinua antamaan meille maksukortin tiedot. Varmista, että ei veloiteta maksusi tällä hetkellä.",
"Total Points": "Kokonaispisteet",

View File

@@ -337,6 +337,7 @@
"There are no rooms available that match your request.": "Det er ingen rom tilgjengelige som matcher din forespørsel.",
"There are no transactions to display": "Det er ingen transaksjoner å vise",
"Things nearby HOTEL_NAME": "Ting i nærheten av {hotelName}",
"This room is not available": "Dette rommet er ikke tilgjengelig",
"To get the member price <span>{amount} {currency}</span>, log in or join when completing the booking.": "For å få medlemsprisen <span>{amount} {currency}</span>, logg inn eller bli med når du fullfører bestillingen.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "For å sikre din reservasjon, ber vi om at du gir oss dine betalingskortdetaljer. Vær sikker på at ingen gebyrer vil bli belastet på dette tidspunktet.",
"Total Points": "Totale poeng",

View File

@@ -337,6 +337,7 @@
"There are no rooms available that match your request.": "Det finns inga rum tillgängliga som matchar din begäran.",
"There are no transactions to display": "Det finns inga transaktioner att visa",
"Things nearby HOTEL_NAME": "Saker i närheten av {hotelName}",
"This room is not available": "Detta rum är inte tillgängligt",
"To get the member price <span>{amount} {currency}</span>, log in or join when completing the booking.": "För att få medlemsprisen <span>{amount} {currency}</span>, logga in eller bli medlem när du slutför bokningen.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "För att säkra din bokning ber vi om att du ger oss dina betalkortdetaljer. Välj säker på att ingen avgifter kommer att debiteras just nu.",
"Total Points": "Poäng totalt",

View File

@@ -2,4 +2,5 @@ import type { HotelData } from "@/types/hotel"
export type HotelInfoCardProps = {
hotelData: HotelData | null
noAvailability: boolean
}