Feat/SW-2078 update confirmation page vouchers and Corp Cheques rate * feat: SW-2078 Tablet bookingCode ref forward issue fix (cherry picked from commit 16a6a00fd99b6b6220a98ad74de062d67d35e1c0) * feat: SW-2078 Display Vouchers and Cheques prices on confirmation page (cherry picked from commit a76494de497a7d5e7641cb0036bd7055acf875c1) * feat: SW-2078 Rebase issue fix * feat: SW-2079 Updated rate title in terms modal * feat: SW-2078 Optimized code * feat: SW-2078 Removed extra tags Approved-by: Christian Andolf
341 lines
12 KiB
TypeScript
341 lines
12 KiB
TypeScript
import { useIntl } from "react-intl"
|
|
|
|
import DiscountIcon from "@scandic-hotels/design-system/Icons/DiscountIcon"
|
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
|
|
import { dt } from "@/lib/dt"
|
|
import { useMyStayRoomDetailsStore } from "@/stores/my-stay/myStayRoomDetailsStore"
|
|
|
|
import GuestDetails from "@/components/HotelReservation/MyStay/GuestDetails"
|
|
import Price from "@/components/HotelReservation/MyStay/Price"
|
|
import { hasBreakfastPackageFromBookingFlow } from "@/components/HotelReservation/MyStay/utils/hasBreakfastPackage"
|
|
import ImageGallery from "@/components/ImageGallery"
|
|
import Accordion from "@/components/TempDesignSystem/Accordion"
|
|
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
|
|
import IconChip from "@/components/TempDesignSystem/IconChip"
|
|
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
|
import useLang from "@/hooks/useLang"
|
|
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
|
|
|
import RoomDetails from "./RoomDetails"
|
|
|
|
import styles from "./bookedRoomSidePeek.module.css"
|
|
|
|
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
|
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
|
import type { BookedRoomSidePeekProps } from "@/types/components/sidePeeks/bookedRoomSidePeek"
|
|
|
|
export default function BookedRoomSidePeek({
|
|
room,
|
|
activeSidePeek,
|
|
user,
|
|
confirmationNumber,
|
|
close,
|
|
}: BookedRoomSidePeekProps) {
|
|
const intl = useIntl()
|
|
const lang = useLang()
|
|
const bookedRoom = useMyStayRoomDetailsStore((state) => state.bookedRoom)
|
|
const linkedReservationRooms = useMyStayRoomDetailsStore(
|
|
(state) => state.linkedReservationRooms
|
|
)
|
|
const updateBookedRoom = useMyStayRoomDetailsStore(
|
|
(state) => state.actions.updateBookedRoom
|
|
)
|
|
const updateLinkedReservationRoom = useMyStayRoomDetailsStore(
|
|
(state) => state.actions.updateLinkedReservationRoom
|
|
)
|
|
|
|
const allRooms = [bookedRoom, ...linkedReservationRooms]
|
|
|
|
const matchingRoomBooking = allRooms.find(
|
|
(r) => r.confirmationNumber === confirmationNumber
|
|
)
|
|
|
|
if (!matchingRoomBooking) {
|
|
return null
|
|
}
|
|
|
|
const {
|
|
roomNumber,
|
|
cancellationNumber,
|
|
adults,
|
|
childrenInRoom,
|
|
terms,
|
|
packages,
|
|
bedType,
|
|
checkInDate,
|
|
bookingCode,
|
|
roomPrice,
|
|
isCancelled,
|
|
} = matchingRoomBooking
|
|
|
|
const fromDate = dt(checkInDate).locale(lang)
|
|
|
|
const roomDescription = room.descriptions.medium
|
|
const galleryImages = mapApiImagesToGalleryImages(room.images)
|
|
|
|
const adultsMsg = intl.formatMessage(
|
|
{ id: "{adults, plural, one {# adult} other {# adults}}" },
|
|
{
|
|
adults: adults,
|
|
}
|
|
)
|
|
|
|
const childrenMsg = intl.formatMessage(
|
|
{
|
|
id: "{children, plural, one {# child} other {# children}}",
|
|
},
|
|
{
|
|
children: childrenInRoom.length,
|
|
}
|
|
)
|
|
|
|
const adultsOnlyMsg = adultsMsg
|
|
const adultsAndChildrenMsg = [adultsMsg, childrenMsg].join(", ")
|
|
|
|
return (
|
|
<SidePeek
|
|
title={room.name}
|
|
isOpen={activeSidePeek === SidePeekEnum.bookedRoomDetails}
|
|
handleClose={close}
|
|
>
|
|
<div className={styles.wrapper}>
|
|
<div className={styles.roomHeader}>
|
|
{isCancelled ? (
|
|
<IconChip
|
|
color={"red"}
|
|
icon={
|
|
<MaterialIcon
|
|
icon="cancel"
|
|
size={20}
|
|
color="Icon/Feedback/Error"
|
|
/>
|
|
}
|
|
>
|
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
|
<span>{intl.formatMessage({ id: "Cancelled" })}</span>
|
|
</Typography>
|
|
</IconChip>
|
|
) : (
|
|
<div className={styles.chip}>
|
|
<Typography variant="Tag/sm">
|
|
<span>
|
|
{intl.formatMessage(
|
|
{ id: "Room {roomIndex}" },
|
|
{ roomIndex: roomNumber }
|
|
)}
|
|
</span>
|
|
</Typography>
|
|
</div>
|
|
)}
|
|
<div className={styles.reference}>
|
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
|
{isCancelled ? (
|
|
<span>{intl.formatMessage({ id: "Cancellation no" })}:</span>
|
|
) : (
|
|
<span>{intl.formatMessage({ id: "Reference" })}:</span>
|
|
)}
|
|
</Typography>
|
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
|
{isCancelled ? (
|
|
<span className={styles.cancellationNumber}>
|
|
{cancellationNumber}
|
|
</span>
|
|
) : (
|
|
<span>{confirmationNumber}</span>
|
|
)}
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
<div className={styles.mainContent}>
|
|
<div className={styles.imageContainer}>
|
|
<ImageGallery
|
|
images={galleryImages}
|
|
title={room.name}
|
|
height={280}
|
|
/>
|
|
</div>
|
|
<div className={styles.roomDetails}>
|
|
<div className={styles.row}>
|
|
<span className={styles.rowTitle}>
|
|
<MaterialIcon icon="person" color="Icon/Default" size={20} />
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>{intl.formatMessage({ id: "Guests" })}</p>
|
|
</Typography>
|
|
</span>
|
|
<div className={styles.rowContent}>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p color="uiTextHighContrast">
|
|
{childrenInRoom.length > 0
|
|
? adultsAndChildrenMsg
|
|
: adultsOnlyMsg}
|
|
</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
<div className={styles.row}>
|
|
<span className={styles.rowTitle}>
|
|
<MaterialIcon icon="contract" color="Icon/Default" size={20} />
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>{intl.formatMessage({ id: "Terms" })}</p>
|
|
</Typography>
|
|
</span>
|
|
<div className={styles.rowContent}>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p color="uiTextHighContrast">{terms}</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
<div className={styles.row}>
|
|
<span className={styles.rowTitle}>
|
|
<MaterialIcon icon="refresh" color="Icon/Default" size={20} />
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>{intl.formatMessage({ id: "Modify By" })}</p>
|
|
</Typography>
|
|
</span>
|
|
<div className={styles.rowContent}>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p color="uiTextHighContrast">
|
|
{intl.formatMessage(
|
|
{ id: "Until {time}, {date}" },
|
|
{ time: "18:00", date: fromDate.format("dddd D MMM") }
|
|
)}
|
|
</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
<div className={styles.row}>
|
|
<span className={styles.rowTitle}>
|
|
<MaterialIcon icon="coffee" color="Icon/Default" size={20} />
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>{intl.formatMessage({ id: "Breakfast" })}</p>
|
|
</Typography>
|
|
</span>
|
|
<div className={styles.rowContent}>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p color="uiTextHighContrast">
|
|
{packages &&
|
|
hasBreakfastPackageFromBookingFlow(
|
|
packages.map((pkg) => ({
|
|
code: pkg.code,
|
|
}))
|
|
)
|
|
? intl.formatMessage({ id: "Included" })
|
|
: intl.formatMessage({ id: "Not included" })}
|
|
</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
{packages?.some((item) =>
|
|
Object.values(RoomPackageCodeEnum).includes(
|
|
item.code as RoomPackageCodeEnum
|
|
)
|
|
) && (
|
|
<div className={styles.row}>
|
|
<span className={styles.rowTitle}>
|
|
<MaterialIcon
|
|
icon="meeting_room"
|
|
color="Icon/Default"
|
|
size={20}
|
|
/>
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>{intl.formatMessage({ id: "Room classification" })}</p>
|
|
</Typography>
|
|
</span>
|
|
<div className={styles.rowContent}>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p color="uiTextHighContrast">
|
|
{packages
|
|
?.filter((item) =>
|
|
Object.values(RoomPackageCodeEnum).includes(
|
|
item.code as RoomPackageCodeEnum
|
|
)
|
|
)
|
|
.map((item) => item.description)
|
|
.join(", ")}
|
|
</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className={styles.row}>
|
|
<span className={styles.rowTitle}>
|
|
<MaterialIcon icon="bed" color="Icon/Default" size={20} />
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>{intl.formatMessage({ id: "Bed preference" })}</p>
|
|
</Typography>
|
|
</span>
|
|
<div className={styles.rowContent}>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p color="uiTextHighContrast">{bedType?.description}</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className={styles.bookingInformation}>
|
|
<div className={styles.priceDetails}>
|
|
<div className={styles.price}>
|
|
<Typography variant="Body/Lead text">
|
|
<p color="uiTextHighContrast">
|
|
{intl.formatMessage({ id: "Room total" })}
|
|
</p>
|
|
</Typography>
|
|
|
|
<Price
|
|
price={roomPrice.perStay.local.price}
|
|
variant="Title/Subtitle/md"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{bookingCode && (
|
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
|
<IconChip
|
|
color="blue"
|
|
icon={<DiscountIcon color="Icon/Feedback/Information" />}
|
|
>
|
|
{intl.formatMessage(
|
|
{ id: "<strong>Booking code</strong>: {value}" },
|
|
{
|
|
value: bookingCode,
|
|
strong: (text) => (
|
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
|
<strong>{text}</strong>
|
|
</Typography>
|
|
),
|
|
}
|
|
)}
|
|
</IconChip>
|
|
</Typography>
|
|
)}
|
|
|
|
<GuestDetails
|
|
user={user ?? null}
|
|
booking={matchingRoomBooking}
|
|
updateRoom={
|
|
bookedRoom.confirmationNumber ===
|
|
matchingRoomBooking.confirmationNumber
|
|
? updateBookedRoom
|
|
: updateLinkedReservationRoom
|
|
}
|
|
/>
|
|
</div>
|
|
<Accordion>
|
|
<AccordionItem
|
|
title={intl.formatMessage({ id: "Room details" })}
|
|
variant="sidepeek"
|
|
>
|
|
<RoomDetails
|
|
roomDescription={roomDescription}
|
|
roomFacilities={room.roomFacilities}
|
|
roomTypes={room.roomTypes}
|
|
/>
|
|
</AccordionItem>
|
|
</Accordion>
|
|
</div>
|
|
</SidePeek>
|
|
)
|
|
}
|