Merged in chore/SW-2878-extract-booking-confirmation-pag (pull request #2779)
Chore/SW-2878 extract booking confirmation pag * chore(SW-2878): Moved booking confirmation page to booking-flow package * chore(SW-2878): Fixed promo styles as per design * chore(SW-2878): Kept tiny duplicate function to avoid export from booking-flow package Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
|
||||
|
||||
import styles from "./linkedReservationCardSkeleton.module.css"
|
||||
|
||||
export function LinkedReservationCardSkeleton() {
|
||||
return (
|
||||
<div className={styles.card}>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.img}>
|
||||
<SkeletonShimmer height={"204px"} width={"100%"} />
|
||||
</div>
|
||||
<div className={styles.roomDetails}>
|
||||
<div className={styles.roomName}>
|
||||
<SkeletonShimmer height={"24px"} width={"130px"} />
|
||||
<SkeletonShimmer height={"20px"} width={"140px"} />
|
||||
</div>
|
||||
<div className={styles.details}>
|
||||
<SkeletonShimmer height={"20px"} width={"300px"} />
|
||||
<SkeletonShimmer height={"20px"} width={"300px"} />
|
||||
<SkeletonShimmer height={"20px"} width={"300px"} />
|
||||
<SkeletonShimmer height={"20px"} width={"300px"} />
|
||||
</div>
|
||||
<div className={styles.guest}>
|
||||
<SkeletonShimmer height={"20px"} width={"100px"} />
|
||||
<SkeletonShimmer height={"20px"} width={"200px"} />
|
||||
<SkeletonShimmer height={"20px"} width={"150px"} />
|
||||
<SkeletonShimmer height={"20px"} width={"300px"} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import styles from "./retry.module.css"
|
||||
|
||||
export interface RetryProps {
|
||||
handleRefetch: () => void
|
||||
}
|
||||
|
||||
export default function Retry({ handleRefetch }: RetryProps) {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<div className={styles.retry}>
|
||||
<Typography variant={"Body/Paragraph/mdRegular"}>
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Something went wrong!",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
<Button size={"Small"} onPress={handleRefetch}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Try again",
|
||||
})}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||
import { trpc } from "@scandic-hotels/trpc/client"
|
||||
|
||||
import useLang from "../../../../hooks/useLang"
|
||||
import { useBookingConfirmationStore } from "../../../../stores/booking-confirmation"
|
||||
import { mapRoomState } from "../../utils"
|
||||
import { Room } from "../Room"
|
||||
import { LinkedReservationCardSkeleton } from "./LinkedReservationCardSkeleton"
|
||||
import Retry from "./Retry"
|
||||
|
||||
export interface LinkedReservationProps {
|
||||
checkInTime: string
|
||||
checkOutTime: string
|
||||
refId: string
|
||||
roomIndex: number
|
||||
}
|
||||
|
||||
export function LinkedReservation({
|
||||
checkInTime,
|
||||
checkOutTime,
|
||||
refId,
|
||||
roomIndex,
|
||||
}: LinkedReservationProps) {
|
||||
const lang = useLang()
|
||||
const { data, refetch, isLoading } = trpc.booking.get.useQuery({
|
||||
refId,
|
||||
lang,
|
||||
})
|
||||
const {
|
||||
setRoom,
|
||||
setFormattedTotalCost,
|
||||
currencyCode,
|
||||
totalBookingPrice,
|
||||
totalBookingCheques,
|
||||
} = useBookingConfirmationStore((state) => ({
|
||||
setRoom: state.actions.setRoom,
|
||||
setFormattedTotalCost: state.actions.setFormattedTotalCost,
|
||||
currencyCode: state.currencyCode,
|
||||
totalBookingPrice: state.totalBookingPrice,
|
||||
totalBookingCheques: state.totalBookingCheques,
|
||||
}))
|
||||
const intl = useIntl()
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.room) {
|
||||
const roomData = mapRoomState(data.booking, data.room, intl)
|
||||
setRoom(roomData, roomIndex)
|
||||
|
||||
const formattedTotalCost = totalBookingCheques
|
||||
? formatPrice(
|
||||
intl,
|
||||
totalBookingCheques,
|
||||
CurrencyEnum.CC,
|
||||
totalBookingPrice,
|
||||
currencyCode
|
||||
)
|
||||
: formatPrice(intl, totalBookingPrice, currencyCode)
|
||||
|
||||
setFormattedTotalCost(formattedTotalCost)
|
||||
}
|
||||
}, [
|
||||
data,
|
||||
roomIndex,
|
||||
intl,
|
||||
setRoom,
|
||||
totalBookingCheques,
|
||||
totalBookingPrice,
|
||||
currencyCode,
|
||||
setFormattedTotalCost,
|
||||
])
|
||||
|
||||
if (isLoading) {
|
||||
return <LinkedReservationCardSkeleton />
|
||||
}
|
||||
|
||||
if (!data?.room) {
|
||||
return <Retry handleRefetch={refetch} />
|
||||
}
|
||||
|
||||
return (
|
||||
<Room
|
||||
booking={data.booking}
|
||||
checkInTime={checkInTime}
|
||||
checkOutTime={checkOutTime}
|
||||
img={data.room.images[0]}
|
||||
roomName={data.room.name}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: var(--Background-Primary);
|
||||
border-radius: var(--Corner-radius-lg);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x2) var(--Spacing-x2) var(--Spacing-x3)
|
||||
var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.img {
|
||||
border-radius: var(--Corner-radius-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.roomDetails {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.roomName {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x1);
|
||||
grid-column: 1/-1;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x1);
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.guest {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x1);
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.content {
|
||||
gap: var(--Spacing-x3);
|
||||
grid-template-columns: auto 1fr;
|
||||
padding: var(--Spacing-x2) var(--Spacing-x3) var(--Spacing-x2)
|
||||
var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.img {
|
||||
min-width: 306px;
|
||||
}
|
||||
.roomDetails {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.guest {
|
||||
align-items: flex-end;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.retry {
|
||||
background-color: var(--Background-Primary);
|
||||
border-radius: var(--Corner-radius-lg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x2) var(--Spacing-x2) var(--Spacing-x3)
|
||||
var(--Spacing-x2);
|
||||
align-items: center;
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { CancellationRuleEnum } from "@scandic-hotels/common/constants/booking"
|
||||
import {
|
||||
changeOrCancelDateFormat,
|
||||
longDateFormat,
|
||||
} from "@scandic-hotels/common/constants/dateFormats"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import Image from "@scandic-hotels/design-system/Image"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { getHotelRoom } from "@scandic-hotels/trpc/routers/booking/helpers"
|
||||
|
||||
import { RoomDetailsSidePeek } from "../../../../components/RoomDetailsSidePeek"
|
||||
import useLang from "../../../../hooks/useLang"
|
||||
import { useBookingConfirmationStore } from "../../../../stores/booking-confirmation"
|
||||
|
||||
import styles from "./room.module.css"
|
||||
|
||||
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
|
||||
|
||||
export interface RoomProps {
|
||||
booking: BookingConfirmation["booking"]
|
||||
checkInTime: string
|
||||
checkOutTime: string
|
||||
img?: NonNullable<BookingConfirmation["room"]>["images"][number]
|
||||
roomName: NonNullable<BookingConfirmation["room"]>["name"]
|
||||
}
|
||||
|
||||
export function Room({
|
||||
booking,
|
||||
checkInTime,
|
||||
checkOutTime,
|
||||
img,
|
||||
roomName,
|
||||
}: RoomProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const { roomCategories } = useBookingConfirmationStore((state) => ({
|
||||
roomCategories: state.roomCategories,
|
||||
}))
|
||||
const room = getHotelRoom(roomCategories, booking.roomTypeCode)
|
||||
|
||||
const guestName = `${booking.guest.firstName} ${booking.guest.lastName}`
|
||||
const fromDate = dt(booking.checkInDate).locale(lang)
|
||||
const toDate = dt(booking.checkOutDate).locale(lang)
|
||||
|
||||
const isFlexBooking =
|
||||
booking.rateDefinition.cancellationRule ===
|
||||
CancellationRuleEnum.CancellableBefore6PM
|
||||
const isChangeBooking =
|
||||
booking.rateDefinition.cancellationRule === CancellationRuleEnum.Changeable
|
||||
return (
|
||||
<article className={styles.room}>
|
||||
<header className={styles.header}>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<h2>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Booking number {value}",
|
||||
},
|
||||
{ value: booking.confirmationNumber }
|
||||
)}
|
||||
</h2>
|
||||
</Typography>
|
||||
{booking.rateDefinition.isMemberRate ? (
|
||||
<div className={styles.benefits}>
|
||||
<>
|
||||
<MaterialIcon
|
||||
color="Icon/Feedback/Success"
|
||||
icon="check_circle"
|
||||
size={20}
|
||||
/>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Membership benefits applied",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</>
|
||||
</div>
|
||||
) : null}
|
||||
{booking.guaranteeInfo && (
|
||||
<div className={styles.benefits}>
|
||||
<MaterialIcon
|
||||
icon="check_circle"
|
||||
color="Icon/Feedback/Success"
|
||||
size={20}
|
||||
/>
|
||||
<Typography
|
||||
variant="Body/Supporting text (caption)/smBold"
|
||||
className={styles.guaranteeText}
|
||||
>
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Booking guaranteed.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
{/* eslint-disable formatjs/no-literal-string-in-jsx */}{" "}
|
||||
{/* eslint-enable formatjs/no-literal-string-in-jsx */}
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage:
|
||||
"Your room will remain available for check-in even after 18:00.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
<div className={styles.booking}>
|
||||
<Image
|
||||
alt={img?.metaData.altText ?? ""}
|
||||
className={styles.img}
|
||||
focalPoint={{ x: 50, y: 50 }}
|
||||
height={204}
|
||||
src={img?.imageSizes.medium ?? ""}
|
||||
style={{ borderRadius: "var(--Corner-radius-md)" }}
|
||||
title={img?.metaData.title || img?.metaData.title_En || ""}
|
||||
width={204}
|
||||
/>
|
||||
<div className={styles.roomDetails}>
|
||||
<div className={styles.roomName}>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<h2>{roomName}</h2>
|
||||
</Typography>
|
||||
{room && (
|
||||
<RoomDetailsSidePeek
|
||||
hotelId={booking.hotelId}
|
||||
room={room}
|
||||
roomTypeCode={booking.roomTypeCode}
|
||||
buttonVariant="primary"
|
||||
triggerLabel={intl.formatMessage({
|
||||
defaultMessage: "View room details",
|
||||
})}
|
||||
wrapping={false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<ul className={styles.details}>
|
||||
<li className={styles.listItem}>
|
||||
<p className={styles.label}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Check-in",
|
||||
})}
|
||||
</p>
|
||||
<p>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "{checkInDate} from {checkInTime}",
|
||||
},
|
||||
{
|
||||
checkInDate: fromDate.format(longDateFormat[lang]),
|
||||
checkInTime: checkInTime,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</li>
|
||||
<li className={styles.listItem}>
|
||||
<p className={styles.label}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Check-out",
|
||||
})}
|
||||
</p>
|
||||
<p>
|
||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||
{`${toDate.format(longDateFormat[lang])}, ${checkOutTime}`}
|
||||
</p>
|
||||
</li>
|
||||
<li className={styles.listItem}>
|
||||
<p className={styles.label}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Cancellation policy",
|
||||
})}
|
||||
</p>
|
||||
<p>{booking.rateDefinition.cancellationText}</p>
|
||||
</li>
|
||||
{isFlexBooking || isChangeBooking ? (
|
||||
<li className={styles.listItem}>
|
||||
<p className={styles.label}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Change or cancel",
|
||||
})}
|
||||
</p>
|
||||
<p>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Until {time}, {date}",
|
||||
},
|
||||
{
|
||||
time: "18:00",
|
||||
date: fromDate.format(changeOrCancelDateFormat[lang]),
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</Typography>
|
||||
<div className={styles.guest}>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.label}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Guest",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p data-hj-suppress>{guestName}</p>
|
||||
</Typography>
|
||||
{booking.guest.membershipNumber ? (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p data-hj-suppress>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Friend no. {value}",
|
||||
},
|
||||
{
|
||||
value: booking.guest.membershipNumber,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
) : null}
|
||||
{booking.guest.phoneNumber ? (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p data-hj-suppress>{booking.guest.phoneNumber}</p>
|
||||
</Typography>
|
||||
) : null}
|
||||
{booking.guest.email ? (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p data-hj-suppress>{booking.guest.email}</p>
|
||||
</Typography>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
.room {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.benefits {
|
||||
align-items: center;
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
border-radius: var(--Corner-radius-md);
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
||||
width: min(max-content, 100%);
|
||||
}
|
||||
|
||||
.guaranteeText {
|
||||
color: var(--Text-Feedback-Succes-Accent);
|
||||
}
|
||||
|
||||
.booking {
|
||||
background-color: var(--Background-Primary);
|
||||
border-radius: var(--Corner-radius-lg);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x2) var(--Spacing-x2) var(--Spacing-x3)
|
||||
var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.roomDetails {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.roomName {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--Spacing-x-half);
|
||||
grid-column: 1/-1;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x-half) var(--Spacing-x3);
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.listItem {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
justify-content: space-between;
|
||||
gap: var(--Space-x3);
|
||||
}
|
||||
|
||||
.guest {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--Text-Tertiary);
|
||||
}
|
||||
|
||||
.details p:nth-of-type(even) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1366px) {
|
||||
.details {
|
||||
padding-bottom: var(--Space-x1);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.header {
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
|
||||
.details p:nth-of-type(even) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 204px;
|
||||
}
|
||||
|
||||
.booking {
|
||||
gap: var(--Spacing-x3);
|
||||
grid-template-columns: auto 1fr;
|
||||
padding: var(--Spacing-x2) var(--Spacing-x3) var(--Spacing-x2)
|
||||
var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.roomDetails {
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
|
||||
.guest {
|
||||
align-items: flex-end;
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { LinkedReservation } from "./LinkedReservation"
|
||||
import { Room } from "./Room"
|
||||
|
||||
import styles from "./rooms.module.css"
|
||||
|
||||
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
|
||||
import type { Room as RoomProp } from "@scandic-hotels/trpc/types/hotel"
|
||||
|
||||
export interface BookingConfirmationRoomsProps
|
||||
extends Pick<BookingConfirmation, "booking"> {
|
||||
mainRoom: RoomProp & {
|
||||
bedType: RoomProp["roomTypes"][number]
|
||||
}
|
||||
checkInTime: string
|
||||
checkOutTime: string
|
||||
}
|
||||
|
||||
export function Rooms({
|
||||
booking,
|
||||
checkInTime,
|
||||
checkOutTime,
|
||||
mainRoom,
|
||||
}: BookingConfirmationRoomsProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
return (
|
||||
<section className={styles.rooms}>
|
||||
<div className={styles.room}>
|
||||
{booking.linkedReservations.length ? (
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<h2 className={styles.roomTitle}>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Room {roomIndex}",
|
||||
},
|
||||
{ roomIndex: 1 }
|
||||
)}
|
||||
</h2>
|
||||
</Typography>
|
||||
) : null}
|
||||
<Room
|
||||
booking={booking}
|
||||
checkInTime={checkInTime}
|
||||
checkOutTime={checkOutTime}
|
||||
img={mainRoom.images[0]}
|
||||
roomName={mainRoom.name}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{booking.linkedReservations.map((reservation, idx) => (
|
||||
<div className={styles.room} key={reservation.confirmationNumber}>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<h2 className={styles.roomTitle}>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Room {roomIndex}",
|
||||
},
|
||||
{ roomIndex: idx + 2 }
|
||||
)}
|
||||
</h2>
|
||||
</Typography>
|
||||
<LinkedReservation
|
||||
checkInTime={checkInTime}
|
||||
checkOutTime={checkOutTime}
|
||||
refId={reservation.refId}
|
||||
roomIndex={idx + 1}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
.rooms {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x5);
|
||||
}
|
||||
|
||||
.room {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Space-x025);
|
||||
}
|
||||
|
||||
.roomTitle {
|
||||
color: var(--Text-Tertiary);
|
||||
}
|
||||
Reference in New Issue
Block a user