Merged in feat/SW-613-refactor-hotelreservation-sidepeek (pull request #805)
Feat/SW-613 refactor hotelreservation sidepeek * feat(SW-613): move sidepeek paralell route to apply for all of hotelreservation * feat(SW-613): refactor sidepeek logic to a unified approach for hotelreservation flow * feat(SW-613): fix issue where room was not selected properly in sidepeek * fix(SW-613): move back preload to layout * fix(SW-613): move preload to dedicated file * fix(SW-613): refactor sidepeek to work with hotel page * feat(SW-613): added sidepeek button for room card Approved-by: Simon.Emanuelsson
This commit is contained in:
23
components/SidePeeks/HotelSidePeek/hotelSidePeek.module.css
Normal file
23
components/SidePeeks/HotelSidePeek/hotelSidePeek.module.css
Normal file
@@ -0,0 +1,23 @@
|
||||
.spacing {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.amenity {
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
/* padding set to align with AccordionItem which has a different composition */
|
||||
padding: var(--Spacing-x2)
|
||||
calc(var(--Spacing-x1) + var(--Spacing-x-one-and-half));
|
||||
}
|
||||
|
||||
.list {
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
list-style: inside;
|
||||
}
|
||||
90
components/SidePeeks/HotelSidePeek/index.tsx
Normal file
90
components/SidePeeks/HotelSidePeek/index.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Contact from "@/components/HotelReservation/Contact"
|
||||
import Accordion from "@/components/TempDesignSystem/Accordion"
|
||||
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
|
||||
import styles from "./hotelSidePeek.module.css"
|
||||
|
||||
import { HotelSidePeekProps } from "@/types/components/hotelReservation/hotelSidePeek"
|
||||
import { ParkingProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
import { Amenities, Hotel } from "@/types/hotel"
|
||||
|
||||
function getAmenitiesList(hotel: Hotel) {
|
||||
const detailedAmenities: Amenities = hotel.detailedFacilities.filter(
|
||||
// Remove Parking facilities since parking accordion is based on hotel.parking
|
||||
(facility) => !facility.name.startsWith("Parking") && facility.public
|
||||
)
|
||||
return detailedAmenities
|
||||
}
|
||||
|
||||
export default function HotelSidePeek({
|
||||
hotel,
|
||||
activeSidePeek,
|
||||
close,
|
||||
}: HotelSidePeekProps) {
|
||||
const intl = useIntl()
|
||||
const amenitiesList = getAmenitiesList(hotel)
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
title={hotel.name}
|
||||
isOpen={activeSidePeek === SidePeekEnum.hotelDetails}
|
||||
handleClose={close}
|
||||
>
|
||||
<div className={styles.content}>
|
||||
<Subtitle>
|
||||
{intl.formatMessage({ id: "Practical information" })}
|
||||
</Subtitle>
|
||||
<Contact hotel={hotel} />
|
||||
<Accordion>
|
||||
{/* parking */}
|
||||
{hotel.parking.length ? (
|
||||
<AccordionItem title={intl.formatMessage({ id: "Parking" })}>
|
||||
{hotel.parking.map((p) => (
|
||||
<Parking key={p.name} parking={p} />
|
||||
))}
|
||||
</AccordionItem>
|
||||
) : null}
|
||||
<AccordionItem title={intl.formatMessage({ id: "Accessibility" })}>
|
||||
TODO: What content should be in the accessibility section?
|
||||
</AccordionItem>
|
||||
{amenitiesList.map((amenity) => {
|
||||
return (
|
||||
<div key={amenity.id} className={styles.amenity}>
|
||||
{amenity.name}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
{/* TODO: handle linking to Hotel Page */}
|
||||
<Button theme={"base"}>To the hotel</Button>
|
||||
</div>
|
||||
</SidePeek>
|
||||
)
|
||||
}
|
||||
|
||||
function Parking({ parking }: ParkingProps) {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<div>
|
||||
<Body>{`${intl.formatMessage({ id: parking.type })} (${parking.name})`}</Body>
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
{`${intl.formatMessage({
|
||||
id: "Number of charging points for electric cars",
|
||||
})}: ${parking.numberOfChargingSpaces}`}
|
||||
</li>
|
||||
<li>{`${intl.formatMessage({ id: "Parking can be reserved in advance" })}: ${parking.canMakeReservation ? intl.formatMessage({ id: "Yes" }) : intl.formatMessage({ id: "No" })}`}</li>
|
||||
<li>{`${intl.formatMessage({ id: "Number of parking spots" })}: ${parking.numberOfParkingSpots}`}</li>
|
||||
<li>{`${intl.formatMessage({ id: "Distance to hotel" })}: ${parking.distanceToHotel} m`}</li>
|
||||
<li>{`${intl.formatMessage({ id: "Address" })}: ${parking.address}`}</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { ChevronRightSmallIcon } from "@/components/Icons"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
@@ -12,10 +10,14 @@ import { getFacilityIcon } from "./facilityIcon"
|
||||
|
||||
import styles from "./roomSidePeek.module.css"
|
||||
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
import type { RoomSidePeekProps } from "@/types/components/sidePeeks/roomSidePeek"
|
||||
|
||||
export default function RoomSidePeek({ room, buttonSize }: RoomSidePeekProps) {
|
||||
const [isSidePeekOpen, setIsSidePeekOpen] = useState(false)
|
||||
export default function RoomSidePeek({
|
||||
room,
|
||||
activeSidePeek,
|
||||
close,
|
||||
}: RoomSidePeekProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const roomSize = room.roomSize
|
||||
@@ -24,84 +26,70 @@ export default function RoomSidePeek({ room, buttonSize }: RoomSidePeekProps) {
|
||||
const images = room.images
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
intent="text"
|
||||
type="button"
|
||||
size={buttonSize}
|
||||
theme="base"
|
||||
className={styles.button}
|
||||
onClick={() => setIsSidePeekOpen(true)}
|
||||
>
|
||||
{intl.formatMessage({ id: "See room details" })}
|
||||
<ChevronRightSmallIcon color="burgundy" width={20} height={20} />
|
||||
</Button>
|
||||
|
||||
<SidePeek
|
||||
title={room.name}
|
||||
isOpen={isSidePeekOpen}
|
||||
handleClose={() => setIsSidePeekOpen(false)}
|
||||
>
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.mainContent}>
|
||||
<Body color="baseTextMediumContrast">
|
||||
{roomSize.min === roomSize.max
|
||||
? roomSize.min
|
||||
: `${roomSize.min} - ${roomSize.max}`}
|
||||
m².{" "}
|
||||
{intl.formatMessage(
|
||||
{ id: "booking.accommodatesUpTo" },
|
||||
{ nrOfGuests: occupancy }
|
||||
)}
|
||||
</Body>
|
||||
{images && (
|
||||
<div className={styles.imageContainer}>
|
||||
<ImageGallery images={images} title={room.name} />
|
||||
</div>
|
||||
<SidePeek
|
||||
title={room.name}
|
||||
isOpen={activeSidePeek === SidePeekEnum.roomDetails}
|
||||
handleClose={close}
|
||||
>
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.mainContent}>
|
||||
<Body color="baseTextMediumContrast">
|
||||
{roomSize.min === roomSize.max
|
||||
? roomSize.min
|
||||
: `${roomSize.min} - ${roomSize.max}`}
|
||||
m².{" "}
|
||||
{intl.formatMessage(
|
||||
{ id: "booking.accommodatesUpTo" },
|
||||
{ nrOfGuests: occupancy }
|
||||
)}
|
||||
<Body color="uiTextHighContrast">{roomDescription}</Body>
|
||||
</div>
|
||||
<div className={styles.listContainer}>
|
||||
<Subtitle type="two" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "booking.thisRoomIsEquippedWith" })}
|
||||
</Subtitle>
|
||||
<ul className={styles.facilityList}>
|
||||
{room.roomFacilities
|
||||
.sort((a, b) => a.sortOrder - b.sortOrder)
|
||||
.map((facility) => {
|
||||
const Icon = getFacilityIcon(facility.name)
|
||||
return (
|
||||
<li key={facility.name}>
|
||||
{Icon && <Icon color="uiTextMediumContrast" />}
|
||||
<Body
|
||||
asChild
|
||||
className={!Icon ? styles.noIcon : undefined}
|
||||
color="uiTextMediumContrast"
|
||||
>
|
||||
<span>{facility.name}</span>
|
||||
</Body>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div className={styles.listContainer}>
|
||||
<Subtitle type="two" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "booking.bedOptions" })}
|
||||
</Subtitle>
|
||||
<Body color="grey">
|
||||
{intl.formatMessage({ id: "booking.basedOnAvailability" })}
|
||||
</Body>
|
||||
{/* TODO: Get data for bed options */}
|
||||
</div>
|
||||
</Body>
|
||||
{images && (
|
||||
<div className={styles.imageContainer}>
|
||||
<ImageGallery images={images} title={room.name} />
|
||||
</div>
|
||||
)}
|
||||
<Body color="uiTextHighContrast">{roomDescription}</Body>
|
||||
</div>
|
||||
<div className={styles.buttonContainer}>
|
||||
<Button fullWidth theme="base" intent="primary">
|
||||
{intl.formatMessage({ id: "booking.selectRoom" })}
|
||||
{/* TODO: Implement logic for select room */}
|
||||
</Button>
|
||||
<div className={styles.listContainer}>
|
||||
<Subtitle type="two" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "booking.thisRoomIsEquippedWith" })}
|
||||
</Subtitle>
|
||||
<ul className={styles.facilityList}>
|
||||
{room.roomFacilities
|
||||
.sort((a, b) => a.sortOrder - b.sortOrder)
|
||||
.map((facility) => {
|
||||
const Icon = getFacilityIcon(facility.name)
|
||||
return (
|
||||
<li key={facility.name}>
|
||||
{Icon && <Icon color="uiTextMediumContrast" />}
|
||||
<Body
|
||||
asChild
|
||||
className={!Icon ? styles.noIcon : undefined}
|
||||
color="uiTextMediumContrast"
|
||||
>
|
||||
<span>{facility.name}</span>
|
||||
</Body>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</SidePeek>
|
||||
</div>
|
||||
<div className={styles.listContainer}>
|
||||
<Subtitle type="two" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "booking.bedOptions" })}
|
||||
</Subtitle>
|
||||
<Body color="grey">
|
||||
{intl.formatMessage({ id: "booking.basedOnAvailability" })}
|
||||
</Body>
|
||||
{/* TODO: Get data for bed options */}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.buttonContainer}>
|
||||
<Button fullWidth theme="base" intent="primary">
|
||||
{intl.formatMessage({ id: "booking.selectRoom" })}
|
||||
{/* TODO: Implement logic for select room */}
|
||||
</Button>
|
||||
</div>
|
||||
</SidePeek>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user