Merged in feat/SW-1051-amenities-in-sidepeek (pull request #1007)

feat(SW-1051): fix sidepeek select hotel page

* feat(SW-1051): fix sidepeek select hotel page


Approved-by: Niclas Edenvin
This commit is contained in:
Bianca Widstam
2024-12-02 13:45:31 +00:00
parent 403e1e7cf8
commit 4f871a3220
27 changed files with 359 additions and 138 deletions

View File

@@ -0,0 +1,21 @@
import { useIntl } from "react-intl"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Body from "@/components/TempDesignSystem/Text/Body"
import { AccessibilityProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { IconName } from "@/types/components/icon"
export default function Accessibility({
accessibilityElevatorPitchText,
}: AccessibilityProps) {
const intl = useIntl()
return (
<AccordionItem
title={intl.formatMessage({ id: "Accessibility" })}
icon={IconName.Accessibility}
>
<Body>{accessibilityElevatorPitchText}</Body>
</AccordionItem>
)
}

View File

@@ -0,0 +1,22 @@
import { useIntl } from "react-intl"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Body from "@/components/TempDesignSystem/Text/Body"
import { CheckInCheckOutProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { IconName } from "@/types/components/icon"
export default function CheckinCheckOut({ checkin }: CheckInCheckOutProps) {
const intl = useIntl()
return (
<AccordionItem
title={intl.formatMessage({ id: "Check-in/Check-out" })}
icon={IconName.Calendar}
>
<Body textTransform="bold">{intl.formatMessage({ id: "Hours" })}</Body>
<Body>{`${intl.formatMessage({ id: "Check in from" })}: ${checkin.checkInTime}`}</Body>
<Body>{`${intl.formatMessage({ id: "Check out at latest" })}: ${checkin.checkOutTime}`}</Body>
</AccordionItem>
)
}

View File

@@ -0,0 +1,21 @@
import { useIntl } from "react-intl"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Body from "@/components/TempDesignSystem/Text/Body"
import { MeetingsAndConferencesProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { IconName } from "@/types/components/icon"
export default function MeetingsAndConferences({
meetingDescription,
}: MeetingsAndConferencesProps) {
const intl = useIntl()
return (
<AccordionItem
title={intl.formatMessage({ id: "Meetings & Conferences" })}
icon={IconName.Business}
>
<Body>{meetingDescription}</Body>
</AccordionItem>
)
}

View File

@@ -0,0 +1,55 @@
import { useIntl } from "react-intl"
import FilledHeartIcon from "@/components/Icons/FilledHeart"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import styles from "./sidePeekAccordion.module.css"
import { ParkingProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { IconName } from "@/types/components/icon"
export default function Parking({ parking }: ParkingProps) {
const intl = useIntl()
return (
<AccordionItem
title={intl.formatMessage({ id: "Parking" })}
icon={IconName.Parking}
className={styles.parking}
>
{parking.map((p) => (
<div key={p.name}>
<Subtitle type="two">
{`${intl.formatMessage({ id: p.type })} ${p?.name ? ` (${p.name})` : ""}`}
</Subtitle>
<ul className={styles.list}>
{p?.address && (
<li>
<FilledHeartIcon color="baseIconLowContrast" />
{`${intl.formatMessage({ id: "Address" })}: ${p.address}`}
</li>
)}
{p?.numberOfParkingSpots !== undefined && (
<li>
<FilledHeartIcon color="baseIconLowContrast" />
{intl.formatMessage(
{ id: "Number of parking spots" },
{ number: p.numberOfParkingSpots }
)}
</li>
)}
{p?.numberOfChargingSpaces !== undefined && (
<li>
<FilledHeartIcon color="baseIconLowContrast" />
{intl.formatMessage(
{ id: "Number of charging points for electric cars" },
{ number: p.numberOfChargingSpaces }
)}
</li>
)}
</ul>
</div>
))}
</AccordionItem>
)
}

View File

@@ -0,0 +1,22 @@
import { useIntl } from "react-intl"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Body from "@/components/TempDesignSystem/Text/Body"
import { RestaurantProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { IconName } from "@/types/components/icon"
export default function Restaurant({
restaurantsContentDescriptionMedium,
}: RestaurantProps) {
const intl = useIntl()
return (
<AccordionItem
title={intl.formatMessage({ id: "Restaurant" }, { count: 1 })}
icon={IconName.Restaurant}
>
<Body>{restaurantsContentDescriptionMedium}</Body>
</AccordionItem>
)
}

View File

@@ -0,0 +1,23 @@
.list {
font-family: var(--typography-Body-Regular-fontFamily);
list-style-position: inside;
list-style-type: none;
margin-top: var(--Spacing-x-one-and-half);
}
.list li {
display: flex;
align-items: center;
gap: var(--Spacing-x1);
padding-left: var(--Spacing-x1);
}
.list li svg {
flex-shrink: 0;
}
.parking details > div {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
}

View File

@@ -22,8 +22,3 @@
.noIcon {
margin-left: var(--Spacing-x4);
}
.list {
font-family: var(--typography-Body-Regular-fontFamily);
list-style: inside;
}

View File

@@ -2,29 +2,21 @@ import { useIntl } from "react-intl"
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
import Contact from "@/components/HotelReservation/Contact"
import { AccessibilityIcon } from "@/components/Icons"
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 Accessibility from "./Accordions/Accessibility"
import CheckinCheckOut from "./Accordions/CheckInCheckOut"
import MeetingsAndConferences from "./Accordions/MeetingsAndConferences"
import Parking from "./Accordions/Parking"
import Restaurant from "./Accordions/Restaurant"
import styles from "./hotelSidePeek.module.css"
import type { HotelSidePeekProps } from "@/types/components/hotelReservation/hotelSidePeek"
import type { ParkingProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
import { IconName } from "@/types/components/icon"
import type { 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,
@@ -33,7 +25,12 @@ export default function HotelSidePeek({
showCTA,
}: HotelSidePeekProps) {
const intl = useIntl()
const amenitiesList = getAmenitiesList(hotel)
const amenitiesList = hotel.detailedFacilities.filter(
(facility) => facility.public
)
const parking = hotel.parking.filter(
(p) => p?.numberOfParkingSpots || p?.numberOfChargingSpaces || p?.address
)
return (
<SidePeek
@@ -42,30 +39,38 @@ export default function HotelSidePeek({
handleClose={close}
>
<div className={styles.content}>
<Subtitle>
<Subtitle color="baseTextHighContrast">
{intl.formatMessage({ id: "Practical information" })}
</Subtitle>
<Contact hotel={hotel} />
<Accordion>
{/* parking */}
{hotel.parking.length ? (
<AccordionItem
title={intl.formatMessage({ id: "Parking" })}
icon={IconName.Parking}
>
{hotel.parking.map((p) => (
<Parking key={p.name} parking={p} />
))}
</AccordionItem>
) : null}
<div className={styles.amenity}>
<AccessibilityIcon
width={24}
height={24}
color="uiTextMediumContrast"
{parking?.length > 0 && <Parking parking={parking} />}
{hotel.hotelContent?.restaurantsOverviewPage
?.restaurantsContentDescriptionMedium && (
<Restaurant
restaurantsContentDescriptionMedium={
hotel.hotelContent.restaurantsOverviewPage
.restaurantsContentDescriptionMedium
}
/>
{intl.formatMessage({ id: "Accessibility" })}
</div>
)}
{hotel?.accessibilityElevatorPitchText && (
<Accessibility
accessibilityElevatorPitchText={
hotel.accessibilityElevatorPitchText
}
/>
)}
{hotel.hotelFacts?.checkin && (
<CheckinCheckOut checkin={hotel.hotelFacts.checkin} />
)}
{hotel.hotelContent.texts?.meetingDescription?.medium && (
<MeetingsAndConferences
meetingDescription={
hotel.hotelContent.texts.meetingDescription.medium
}
/>
)}
{amenitiesList.map((amenity) => {
const Icon = mapFacilityToIcon(amenity.id)
return (
@@ -84,54 +89,13 @@ export default function HotelSidePeek({
)
})}
</Accordion>
{showCTA && (
/* TODO: handle linking to Hotel Page */
<Button theme={"base"}>To the hotel</Button>
)}
{/* TODO: handle linking to Hotel Page */}
{/* {showCTA && (
<Button theme="base" intent="secondary" size="large">
Read more about the hotel
</Button>
)} */}
</div>
</SidePeek>
)
}
function Parking({ parking }: ParkingProps) {
const intl = useIntl()
return (
<div>
<Body>
{`${intl.formatMessage({ id: parking.type })}${parking?.name ? ` (${parking.name})` : ""}`}
</Body>
<ul className={styles.list}>
{parking?.numberOfChargingSpaces !== undefined && (
<li>
{intl.formatMessage(
{ id: "Number of charging points for electric cars" },
{ number: parking.numberOfChargingSpaces }
)}
</li>
)}
{parking?.canMakeReservation && (
<li>{`${intl.formatMessage({ id: "Parking can be reserved in advance" })}: ${parking.canMakeReservation ? intl.formatMessage({ id: "Yes" }) : intl.formatMessage({ id: "No" })}`}</li>
)}
{parking?.numberOfParkingSpots !== undefined && (
<li>
{intl.formatMessage(
{ id: "Number of parking spots" },
{ number: parking.numberOfParkingSpots }
)}
</li>
)}
{parking?.distanceToHotel !== undefined && (
<li>
{intl.formatMessage(
{ id: "Distance to hotel" },
{ distance: parking.distanceToHotel }
)}
</li>
)}
{parking?.address && (
<li>{`${intl.formatMessage({ id: "Address" })}: ${parking.address}`}</li>
)}
</ul>
</div>
)
}