Files
web/apps/scandic-web/components/SidePeeks/BookedRoomSidePeek/index.tsx
Hrishikesh Vaipurkar 73cb423c95 Merged in feat/SW-2078-update-confirmation-page-vouchers (pull request #1731)
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
2025-04-08 07:27:40 +00:00

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>
)
}