Merged in feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow (pull request #2600)
feat(SW-2873): Move HotelReservationSidePeek to booking-flow * Move sidepeek store to booking-flow * Begin move of HotelReservationSidePeek to booking-flow * Copy Link * Update AccessibilityAccordionItem * Split AccessibilityAccordionItem into two components * Fix tracking for Accordion * Duplicate ButtonLink to booking-flow TEMP * AdditionalAmeneties * wip * Move sidepeek accordion items * Remove temp ButtonLink * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow * Fix accordion tracking * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow * Update exports * Fix self-referencing import * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow * Add 'use client' to tracking function * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow * Fix TEMP folder * Refactor sidepeek tracking * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import AdditionalAmenities from "@scandic-hotels/booking-flow/components/AdditionalAmenities"
|
||||
import BreakfastAccordionItem from "@scandic-hotels/booking-flow/components/SidePeekAccordions/BreakfastAccordionItem"
|
||||
import CheckInCheckOutAccordionItem from "@scandic-hotels/booking-flow/components/SidePeekAccordions/CheckInCheckOutAccordionItem"
|
||||
import ParkingAccordionItem from "@scandic-hotels/booking-flow/components/SidePeekAccordions/ParkingAccordionItem"
|
||||
import Accordion from "@scandic-hotels/design-system/Accordion"
|
||||
import SidePeek from "@scandic-hotels/design-system/SidePeek"
|
||||
|
||||
import AccessibilityAccordionItem from "@/components/SidePeeks/AmenitiesSidepeekContent/Accordions/Accessibility"
|
||||
import BreakfastAccordionItem from "@/components/SidePeeks/AmenitiesSidepeekContent/Accordions/Breakfast"
|
||||
import CheckInCheckOutAccordionItem from "@/components/SidePeeks/AmenitiesSidepeekContent/Accordions/CheckInCheckOut"
|
||||
import ParkingAccordionItem from "@/components/SidePeeks/AmenitiesSidepeekContent/Accordions/Parking"
|
||||
import AdditionalAmenities from "@/components/SidePeeks/AmenitiesSidepeekContent/AdditionalAmenities"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { appendSlugToPathname } from "@/utils/appendSlugToPathname"
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import { cx } from "class-variance-authority"
|
||||
import { useRef, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { FacilityIcon } from "@scandic-hotels/design-system/Icons/FacilityIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { FacilityIcon } from "@/components/SidePeeks/RoomSidePeek/facilityIcon"
|
||||
import ShowMoreButton from "@/components/TempDesignSystem/ShowMoreButton"
|
||||
|
||||
import styles from "./roomFacilities.module.css"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { getBedIconName } from "@/components/SidePeeks/RoomSidePeek/bedIcon"
|
||||
import { getBedIconName } from "@/components/utils"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import { getBedDescriptionText } from "../utils"
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function SidePeeks({ hotelId, children }: SidePeeksProps) {
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
function handleOpen(sidePeek: string) {
|
||||
trackOpenSidePeekEvent(sidePeek, hotelId)
|
||||
trackOpenSidePeekEvent({ name: sidePeek, hotelId })
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
import { DialogTrigger } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { RoomSidePeekContent } from "@scandic-hotels/booking-flow/components/RoomSidePeekContent"
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { getBookedHotelRoom } from "@scandic-hotels/trpc/routers/booking/helpers"
|
||||
|
||||
import { useBookingConfirmationStore } from "@/stores/booking-confirmation"
|
||||
|
||||
import { RoomSidePeekContent } from "@/components/SidePeeks/RoomSidePeek/RoomSidePeekContent"
|
||||
import SidePeekSelfControlled from "@/components/TempDesignSystem/SidePeekSelfControlled"
|
||||
|
||||
interface RoomDetailsSidePeekProps {
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
.wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: auto;
|
||||
gap: var(--Spacing-x2);
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
margin-bottom: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.address,
|
||||
.contactInfo {
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
grid-template-rows: subgrid;
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 1 / 4;
|
||||
}
|
||||
|
||||
.contactInfo > li {
|
||||
font-style: normal;
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.soMeIcons {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.ecoLabel {
|
||||
width: 38px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.ecoLabel img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
flex-shrink: 0;
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 4 / 4;
|
||||
}
|
||||
|
||||
.ecoContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: var(--Spacing-x-one-and-half);
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 4 / 4;
|
||||
font-size: var(--typography-Footnote-Regular-fontSize);
|
||||
line-height: ();
|
||||
margin-bottom: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.ecoLabelText {
|
||||
display: flex;
|
||||
color: var(--UI-Text-Medium-contrast);
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: underline;
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
color: var(--Text-Interactive-Secondary);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Body from "@scandic-hotels/design-system/Body"
|
||||
import FacebookIcon from "@scandic-hotels/design-system/Icons/FacebookIcon"
|
||||
import InstagramIcon from "@scandic-hotels/design-system/Icons/InstagramIcon"
|
||||
import Image from "@scandic-hotels/design-system/Image"
|
||||
import Link from "@scandic-hotels/design-system/Link"
|
||||
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import styles from "./contact.module.css"
|
||||
|
||||
import type { ContactProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
|
||||
export default function Contact({ hotel }: ContactProps) {
|
||||
const lang = useLang()
|
||||
const intl = useIntl()
|
||||
|
||||
const addressStr = `${hotel.address.streetAddress}, `
|
||||
const cityStr = hotel.address.city
|
||||
|
||||
return (
|
||||
<section className={styles.wrapper}>
|
||||
<address className={styles.address}>
|
||||
<ul className={styles.contactInfo}>
|
||||
<li>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Address",
|
||||
})}
|
||||
</Body>
|
||||
<Body>
|
||||
{addressStr}
|
||||
<br />
|
||||
{cityStr}
|
||||
</Body>
|
||||
</li>
|
||||
<li>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Driving directions",
|
||||
})}
|
||||
</Body>
|
||||
<Link
|
||||
href={`https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(
|
||||
`${hotel.name}, ${hotel.address.streetAddress}, ${hotel.address.zipCode} ${hotel.address.city}`
|
||||
)}`}
|
||||
>
|
||||
<span className={styles.link}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Google Maps",
|
||||
})}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Contact us",
|
||||
})}
|
||||
</Body>
|
||||
<Link href={`tel:${hotel.contactInformation.phoneNumber}`}>
|
||||
<span className={styles.link}>
|
||||
{hotel.contactInformation.phoneNumber}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
{(hotel.socialMedia.facebook || hotel.socialMedia.instagram) && (
|
||||
<>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Follow us",
|
||||
})}
|
||||
</Body>
|
||||
<div className={styles.soMeIcons}>
|
||||
{hotel.socialMedia.instagram && (
|
||||
<Link href={hotel.socialMedia.instagram} target="_blank">
|
||||
<InstagramIcon color="Icon/Interactive/Default" />
|
||||
</Link>
|
||||
)}
|
||||
{hotel.socialMedia.facebook && (
|
||||
<Link href={hotel.socialMedia.facebook} target="_blank">
|
||||
<FacebookIcon color="Icon/Interactive/Default" />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Email",
|
||||
})}
|
||||
</Body>
|
||||
<Link href={`mailto:${hotel.contactInformation.email}`}>
|
||||
<span className={styles.link}>
|
||||
{hotel.contactInformation.email}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</address>
|
||||
{hotel.hotelFacts.ecoLabels?.nordicEcoLabel ? (
|
||||
<div className={styles.ecoContainer}>
|
||||
<div className={styles.ecoLabel}>
|
||||
<Image
|
||||
height={38}
|
||||
width={38}
|
||||
alt={intl.formatMessage({
|
||||
defaultMessage: "Nordic Swan Ecolabel",
|
||||
})}
|
||||
src={`/_static/img/icons/swan-eco/swan_eco_dark_${lang}.png`}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.ecoLabelText}>
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Nordic Swan Ecolabel",
|
||||
})}
|
||||
</span>
|
||||
<span>
|
||||
{hotel.hotelFacts.ecoLabels.svanenEcoLabelCertificateNumber}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import { FormProvider, useForm } from "react-hook-form"
|
||||
|
||||
import { BED_TYPE_ICONS } from "@scandic-hotels/booking-flow/bedTypeIcons"
|
||||
import RadioCard from "@scandic-hotels/design-system/Form/RadioCard"
|
||||
|
||||
import { BED_TYPE_ICONS } from "@/constants/booking"
|
||||
import { useEnterDetailsStore } from "@/stores/enter-details"
|
||||
|
||||
import { useRoomContext } from "@/contexts/Details/Room"
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import useSidePeekStore, {
|
||||
SidePeekEnum,
|
||||
} from "@scandic-hotels/booking-flow/stores/sidepeek"
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
|
||||
import useSidePeekStore from "@/stores/sidepeek"
|
||||
import { trackOpenSidePeekEvent } from "@/utils/tracking"
|
||||
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
import type { ToggleSidePeekProps } from "@/types/components/hotelReservation/toggleSidePeekProps"
|
||||
|
||||
export default function ToggleSidePeek({ hotelId }: ToggleSidePeekProps) {
|
||||
@@ -16,7 +18,14 @@ export default function ToggleSidePeek({ hotelId }: ToggleSidePeekProps) {
|
||||
|
||||
return (
|
||||
<Button
|
||||
onPress={() => openSidePeek({ key: SidePeekEnum.hotelDetails, hotelId })}
|
||||
onPress={() => {
|
||||
openSidePeek({ key: SidePeekEnum.hotelDetails, hotelId })
|
||||
trackOpenSidePeekEvent({
|
||||
name: SidePeekEnum.hotelDetails,
|
||||
hotelId,
|
||||
includePathname: true,
|
||||
})
|
||||
}}
|
||||
size="Small"
|
||||
variant="Secondary"
|
||||
color="Inverted"
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import useSidePeekStore, {
|
||||
SidePeekEnum,
|
||||
} from "@scandic-hotels/booking-flow/stores/sidepeek"
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
|
||||
import useSidePeekStore from "@/stores/sidepeek"
|
||||
import { trackOpenSidePeekEvent } from "@/utils/tracking"
|
||||
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
import type { ToggleSidePeekProps } from "@/types/components/hotelReservation/toggleSidePeekProps"
|
||||
|
||||
export default function ToggleSidePeek({
|
||||
@@ -20,9 +22,15 @@ export default function ToggleSidePeek({
|
||||
|
||||
return (
|
||||
<Button
|
||||
onPress={() =>
|
||||
onPress={() => {
|
||||
openSidePeek({ key: SidePeekEnum.roomDetails, hotelId, roomTypeCode })
|
||||
}
|
||||
trackOpenSidePeekEvent({
|
||||
name: SidePeekEnum.roomDetails,
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
includePathname: true,
|
||||
})
|
||||
}}
|
||||
size="Small"
|
||||
variant="Text"
|
||||
wrapping
|
||||
|
||||
@@ -11,6 +11,7 @@ import { memo } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import TripAdvisorChip from "@scandic-hotels/booking-flow/components/TripAdvisorChip"
|
||||
import { SidePeekEnum } from "@scandic-hotels/booking-flow/stores/sidepeek"
|
||||
import {
|
||||
alternativeHotelsMap,
|
||||
selectHotelMap,
|
||||
@@ -44,7 +45,6 @@ import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
import { HotelCardListingTypeEnum } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
import type { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps"
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
|
||||
function HotelCard({
|
||||
hotelData: { availability, hotel },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { cookies } from "next/headers"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import SidePeek from "@scandic-hotels/booking-flow/components/HotelReservationSidePeek"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
import * as maskValue from "@scandic-hotels/common/utils/maskValue"
|
||||
@@ -35,7 +36,6 @@ import Promo from "@/components/HotelReservation/MyStay/Promo"
|
||||
import { ReferenceCard } from "@/components/HotelReservation/MyStay/ReferenceCard"
|
||||
import MultiRoom from "@/components/HotelReservation/MyStay/Rooms/MultiRoom"
|
||||
import SingleRoom from "@/components/HotelReservation/MyStay/Rooms/SingleRoom"
|
||||
import SidePeek from "@/components/HotelReservation/SidePeek"
|
||||
import { getIntl } from "@/i18n"
|
||||
import MyStayProvider from "@/providers/MyStay"
|
||||
import { isLoggedInUser } from "@/utils/isLoggedInUser"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"use client"
|
||||
import { useEffect } from "react"
|
||||
|
||||
import useSidePeekStore from "@scandic-hotels/booking-flow/stores/sidepeek"
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
|
||||
import useSidePeekStore from "@/stores/sidepeek"
|
||||
import { trackOpenSidePeekEvent } from "@/utils/tracking"
|
||||
|
||||
import type { ReadMoreProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
|
||||
@@ -29,6 +30,11 @@ export default function ReadMore({
|
||||
<Button
|
||||
onPress={() => {
|
||||
openSidePeek({ key: sidePeekKey, hotelId, showCTA })
|
||||
trackOpenSidePeekEvent({
|
||||
name: sidePeekKey,
|
||||
hotelId,
|
||||
includePathname: true,
|
||||
})
|
||||
}}
|
||||
variant="Text"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useState } from "react"
|
||||
import { Button as ButtonRAC } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { SidePeekEnum } from "@scandic-hotels/booking-flow/stores/sidepeek"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { FacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||
@@ -14,8 +15,6 @@ import styles from "./hotelDescription.module.css"
|
||||
|
||||
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
|
||||
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
|
||||
export default function HotelDescription({
|
||||
description,
|
||||
hotel,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import TripAdvisorChip from "@scandic-hotels/booking-flow/components/TripAdvisorChip"
|
||||
import { SidePeekEnum } from "@scandic-hotels/booking-flow/stores/sidepeek"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { getSingleDecimal } from "@scandic-hotels/common/utils/numberFormatting"
|
||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||
@@ -20,7 +21,6 @@ import styles from "./hotelInfoCard.module.css"
|
||||
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
|
||||
|
||||
import type { SelectRateBooking } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
|
||||
export type HotelInfoCardProps = {
|
||||
booking: SelectRateBooking
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import useSidePeekStore, {
|
||||
SidePeekEnum,
|
||||
} from "@scandic-hotels/booking-flow/stores/sidepeek"
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
|
||||
import useSidePeekStore from "@/stores/sidepeek"
|
||||
import { trackOpenSidePeekEvent } from "@/utils/tracking"
|
||||
|
||||
import styles from "./details.module.css"
|
||||
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
import type { ToggleSidePeekProps } from "@/types/components/hotelReservation/toggleSidePeekProps"
|
||||
|
||||
export default function ToggleSidePeek({
|
||||
@@ -21,13 +23,19 @@ export default function ToggleSidePeek({
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
openSidePeek({
|
||||
key: SidePeekEnum.roomDetails,
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
})
|
||||
}
|
||||
trackOpenSidePeekEvent({
|
||||
name: SidePeekEnum.roomDetails,
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
includePathname: true,
|
||||
})
|
||||
}}
|
||||
size="Small"
|
||||
variant="Text"
|
||||
wrapping
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.spacing {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect } from "react"
|
||||
|
||||
import { trpc } from "@scandic-hotels/trpc/client"
|
||||
|
||||
import useSidePeekStore from "@/stores/sidepeek"
|
||||
|
||||
import HotelSidePeek from "@/components/SidePeeks/HotelSidePeek"
|
||||
import RoomSidePeek from "@/components/SidePeeks/RoomSidePeek"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
export default function HotelReservationSidePeek() {
|
||||
const { activeSidePeek, hotelId, roomTypeCode, showCTA } = useSidePeekStore(
|
||||
(state) => ({
|
||||
activeSidePeek: state.activeSidePeek,
|
||||
hotelId: state.hotelId,
|
||||
roomTypeCode: state.roomTypeCode,
|
||||
showCTA: state.showCTA,
|
||||
})
|
||||
)
|
||||
const closeFn = useSidePeekStore((state) => state.closeSidePeek)
|
||||
const lang = useLang()
|
||||
|
||||
const { data: hotelData } = trpc.hotel.get.useQuery(
|
||||
{
|
||||
hotelId: hotelId ?? "",
|
||||
language: lang,
|
||||
isCardOnlyPayment: false,
|
||||
},
|
||||
{
|
||||
enabled: !!hotelId,
|
||||
}
|
||||
)
|
||||
|
||||
const selectedRoom = hotelData?.roomCategories.find((room) =>
|
||||
room.roomTypes.some((type) => type.code === roomTypeCode)
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (activeSidePeek) {
|
||||
window.history.pushState(null, "", window.location.href)
|
||||
}
|
||||
}, [activeSidePeek])
|
||||
|
||||
useEffect(() => {
|
||||
function handlePopState() {
|
||||
if (activeSidePeek) {
|
||||
closeFn()
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("popstate", handlePopState)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("popstate", handlePopState)
|
||||
}
|
||||
}, [activeSidePeek, closeFn])
|
||||
|
||||
if (activeSidePeek) {
|
||||
return (
|
||||
<>
|
||||
{hotelData && (
|
||||
<HotelSidePeek
|
||||
additionalHotelData={hotelData.additionalData}
|
||||
hotel={{ ...hotelData.hotel, url: hotelData.url }}
|
||||
restaurants={hotelData.restaurants}
|
||||
activeSidePeek={activeSidePeek}
|
||||
close={closeFn}
|
||||
showCTA={showCTA}
|
||||
/>
|
||||
)}
|
||||
{selectedRoom && (
|
||||
<RoomSidePeek
|
||||
room={selectedRoom}
|
||||
activeSidePeek={activeSidePeek}
|
||||
close={closeFn}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { isDefined } from "@scandic-hotels/common/utils/isDefined"
|
||||
import AccordionItem from "@scandic-hotels/design-system/Accordion/AccordionItem"
|
||||
import { IconName } from "@scandic-hotels/design-system/Icons/iconName"
|
||||
import OpeningHours from "@scandic-hotels/design-system/OpeningHours"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { HotelTypeEnum } from "@scandic-hotels/trpc/enums/hotelType"
|
||||
|
||||
import { trackAccordionClick } from "@/utils/tracking"
|
||||
|
||||
import styles from "./sidePeekAccordion.module.css"
|
||||
|
||||
import type { BreakfastAccordionItemProps } from "@/types/components/sidePeeks/amenities"
|
||||
|
||||
export default function BreakfastAccordionItem({
|
||||
restaurants,
|
||||
hotelType,
|
||||
}: BreakfastAccordionItemProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const openingHours = restaurants
|
||||
?.map((restaurant) => {
|
||||
const breakfastDetail = restaurant.openingDetails.find(
|
||||
(details) =>
|
||||
details.openingHours.name === "Breakfast" ||
|
||||
details.openingHours.name ===
|
||||
intl.formatMessage({ defaultMessage: "Breakfast" })
|
||||
)
|
||||
return breakfastDetail
|
||||
})
|
||||
.filter(isDefined)[0]
|
||||
|
||||
if (!openingHours && hotelType !== HotelTypeEnum.ScandicGo) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<AccordionItem
|
||||
title={intl.formatMessage({ defaultMessage: "Breakfast" })}
|
||||
iconName={IconName.CoffeeAlt}
|
||||
variant="sidepeek"
|
||||
className={styles.accordionItem}
|
||||
onOpen={() => trackAccordionClick("amenities:breakfast")}
|
||||
>
|
||||
{openingHours ? (
|
||||
<OpeningHours
|
||||
openingHours={openingHours.openingHours}
|
||||
alternateOpeningHours={openingHours.alternateOpeningHours}
|
||||
heading={intl.formatMessage({ defaultMessage: "Opening hours" })}
|
||||
/>
|
||||
) : (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>{intl.formatMessage({ defaultMessage: "All-day breakfast" })}</p>
|
||||
</Typography>
|
||||
)}
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import AccordionItem from "@scandic-hotels/design-system/Accordion/AccordionItem"
|
||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||
import { IconName } from "@scandic-hotels/design-system/Icons/iconName"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { trackAccordionClick } from "@/utils/tracking"
|
||||
|
||||
import styles from "./sidePeekAccordion.module.css"
|
||||
|
||||
import type { CheckInCheckOutAccordionItemProps } from "@/types/components/sidePeeks/amenities"
|
||||
|
||||
export default function CheckInCheckOutAccordionItem({
|
||||
checkInData,
|
||||
}: CheckInCheckOutAccordionItemProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const { checkInTime, checkOutTime } = checkInData
|
||||
|
||||
return (
|
||||
<AccordionItem
|
||||
title={intl.formatMessage({ defaultMessage: "Check-in/Check-out" })}
|
||||
iconName={IconName.Business}
|
||||
variant="sidepeek"
|
||||
className={styles.accordionItem}
|
||||
onOpen={() => trackAccordionClick("amenities:check-in")}
|
||||
>
|
||||
<div className={styles.checkInCheckOutContent}>
|
||||
<Typography variant="Title/Overline/sm">
|
||||
<h4 className={styles.subheading}>
|
||||
{intl.formatMessage({ defaultMessage: "Hours" })}
|
||||
</h4>
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<div>
|
||||
<p>
|
||||
{intl.formatMessage(
|
||||
{ defaultMessage: "Check in from: {checkInTime}" },
|
||||
{
|
||||
checkInTime,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{intl.formatMessage(
|
||||
{ defaultMessage: "Check out at latest: {checkOutTime}" },
|
||||
{
|
||||
checkOutTime,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</Typography>
|
||||
</div>
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import AccordionItem from "@scandic-hotels/design-system/Accordion/AccordionItem"
|
||||
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
||||
import { IconName } from "@scandic-hotels/design-system/Icons/iconName"
|
||||
import ParkingInformation from "@scandic-hotels/design-system/ParkingInformation"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { trackAccordionClick } from "@/utils/tracking"
|
||||
|
||||
import styles from "./sidePeekAccordion.module.css"
|
||||
|
||||
import type { ParkingAccordionItemProps } from "@/types/components/sidePeeks/amenities"
|
||||
|
||||
export default function ParkingAccordionItem({
|
||||
parking,
|
||||
elevatorPitch,
|
||||
parkingPageHref,
|
||||
}: ParkingAccordionItemProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
if (!parking.length && !elevatorPitch && !parkingPageHref) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<AccordionItem
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Parking",
|
||||
})}
|
||||
iconName={IconName.Parking}
|
||||
variant="sidepeek"
|
||||
className={styles.accordionItem}
|
||||
onOpen={() => trackAccordionClick("amenities:parking")}
|
||||
>
|
||||
<div className={styles.parkingContent}>
|
||||
{elevatorPitch ? (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>{elevatorPitch}</p>
|
||||
</Typography>
|
||||
) : null}
|
||||
{parking.map((data) => (
|
||||
<ParkingInformation key={data.type} parking={data} />
|
||||
))}
|
||||
{parkingPageHref ? (
|
||||
<ButtonLink
|
||||
href={parkingPageHref}
|
||||
variant="Secondary"
|
||||
color="Primary"
|
||||
size="Medium"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "About parking",
|
||||
})}
|
||||
</ButtonLink>
|
||||
) : null}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
.wrapper {
|
||||
padding: var(--Space-x1) var(--Space-x0);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
color: var(--Text-Interactive-Default);
|
||||
}
|
||||
|
||||
.amenity {
|
||||
display: flex;
|
||||
gap: var(--Space-x1);
|
||||
padding: var(--Space-x15) var(--Space-x1);
|
||||
}
|
||||
|
||||
.amenityName {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: var(--Space-x3);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { FacilityEnum } from "@scandic-hotels/trpc/enums/facilities"
|
||||
|
||||
import { FacilityToIcon } from "../../../ContentType/HotelPage/data"
|
||||
|
||||
import styles from "./additionalAmenities.module.css"
|
||||
|
||||
import type { AdditionalAmenitiesProps } from "@/types/components/sidePeeks/amenities"
|
||||
|
||||
export default function AdditionalAmenities({
|
||||
amenities,
|
||||
}: AdditionalAmenitiesProps) {
|
||||
const amenitiesToIgnore = [
|
||||
FacilityEnum.ParkingAdditionalCost,
|
||||
FacilityEnum.ParkingElectricCharging,
|
||||
FacilityEnum.ParkingFreeParking,
|
||||
FacilityEnum.ParkingGarage,
|
||||
FacilityEnum.ParkingOutdoor,
|
||||
FacilityEnum.MeetingArea,
|
||||
FacilityEnum.ServesBreakfastAlwaysIncluded,
|
||||
FacilityEnum.LateCheckOutUntil1400Guaranteed,
|
||||
]
|
||||
|
||||
const filteredAmenities = amenities.filter(
|
||||
(amenity) => !amenitiesToIgnore.includes(amenity.id)
|
||||
)
|
||||
|
||||
return filteredAmenities?.map((amenity) => (
|
||||
<Typography key={amenity.name} variant="Title/Subtitle/md">
|
||||
<li className={styles.wrapper}>
|
||||
<div className={styles.amenity}>
|
||||
<FacilityToIcon
|
||||
id={amenity.id}
|
||||
color="Icon/Interactive/Default"
|
||||
size={24}
|
||||
/>
|
||||
<span className={styles.amenityName}>{amenity.name}</span>
|
||||
</div>
|
||||
</li>
|
||||
</Typography>
|
||||
))
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { FacilityIcon } from "@scandic-hotels/design-system/Icons/FacilityIcon"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { getBedIconName } from "../RoomSidePeek/bedIcon"
|
||||
import { FacilityIcon } from "../RoomSidePeek/facilityIcon"
|
||||
import { getBedIconName } from "@/components/utils"
|
||||
|
||||
import styles from "./bookedRoomSidePeek.module.css"
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.content {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
color: var(--Text-Default);
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Accordion from "@scandic-hotels/design-system/Accordion"
|
||||
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
||||
import SidePeek from "@scandic-hotels/design-system/SidePeek"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import Contact from "@/components/HotelReservation/Contact"
|
||||
import AdditionalAmenities from "@/components/SidePeeks/AmenitiesSidepeekContent/AdditionalAmenities"
|
||||
|
||||
import AccessibilityAccordionItem from "../AmenitiesSidepeekContent/Accordions/Accessibility"
|
||||
import BreakfastAccordionItem from "../AmenitiesSidepeekContent/Accordions/Breakfast"
|
||||
import CheckInCheckOutAccordionItem from "../AmenitiesSidepeekContent/Accordions/CheckInCheckOut"
|
||||
import ParkingAccordionItem from "../AmenitiesSidepeekContent/Accordions/Parking"
|
||||
|
||||
import styles from "./hotelSidePeek.module.css"
|
||||
|
||||
import type { HotelSidePeekProps } from "@/types/components/hotelReservation/hotelSidePeek"
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
|
||||
export default function HotelSidePeek({
|
||||
hotel,
|
||||
restaurants,
|
||||
additionalHotelData,
|
||||
activeSidePeek,
|
||||
close,
|
||||
}: HotelSidePeekProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
title={hotel.name}
|
||||
isOpen={activeSidePeek === SidePeekEnum.hotelDetails}
|
||||
handleClose={close}
|
||||
closeLabel={intl.formatMessage({
|
||||
defaultMessage: "Close",
|
||||
})}
|
||||
>
|
||||
<div className={styles.content}>
|
||||
<Typography variant="Title/Subtitle/lg">
|
||||
<h3>
|
||||
{intl.formatMessage({ defaultMessage: "Practical information" })}
|
||||
</h3>
|
||||
</Typography>
|
||||
<Contact hotel={hotel} />
|
||||
|
||||
<Accordion>
|
||||
<ParkingAccordionItem
|
||||
parking={hotel.parking}
|
||||
elevatorPitch={additionalHotelData?.hotelParking.elevatorPitch}
|
||||
/>
|
||||
<BreakfastAccordionItem
|
||||
restaurants={restaurants}
|
||||
hotelType={hotel.hotelType}
|
||||
/>
|
||||
<CheckInCheckOutAccordionItem
|
||||
checkInData={hotel.hotelFacts.checkin}
|
||||
/>
|
||||
<AccessibilityAccordionItem
|
||||
elevatorPitch={additionalHotelData?.hotelSpecialNeeds.elevatorPitch}
|
||||
/>
|
||||
<AdditionalAmenities amenities={hotel.detailedFacilities} />
|
||||
</Accordion>
|
||||
{hotel.url ? (
|
||||
<ButtonLink
|
||||
href={hotel.url}
|
||||
variant="Secondary"
|
||||
size="Medium"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Read more about the hotel",
|
||||
})}
|
||||
</ButtonLink>
|
||||
) : null}
|
||||
</div>
|
||||
</SidePeek>
|
||||
)
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import ImageGallery from "@scandic-hotels/design-system/ImageGallery"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { BED_TYPE_ICONS, type BedTypes } from "@/constants/booking"
|
||||
|
||||
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
||||
|
||||
import { FacilityIcon } from "../facilityIcon"
|
||||
|
||||
import styles from "./roomSidePeekContent.module.css"
|
||||
|
||||
import type { Room } from "@scandic-hotels/trpc/types/hotel"
|
||||
|
||||
interface RoomSidePeekContentProps {
|
||||
room: Room
|
||||
}
|
||||
|
||||
export function RoomSidePeekContent({ room }: RoomSidePeekContentProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const roomSize = room.roomSize
|
||||
const totalOccupancy = room.totalOccupancy
|
||||
const roomDescription = room.descriptions.medium
|
||||
const galleryImages = mapApiImagesToGalleryImages(room.images)
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.mainContent}>
|
||||
{totalOccupancy && (
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage:
|
||||
"Max. {max, plural, one {{range} guest} other {{range} guests}}",
|
||||
},
|
||||
{
|
||||
max: totalOccupancy.max,
|
||||
range: totalOccupancy.range,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
{roomSize && (
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p>
|
||||
{roomSize.min === roomSize.max
|
||||
? intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "{roomSize} m²",
|
||||
},
|
||||
{
|
||||
roomSize: roomSize.min,
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "{roomSizeMin}–{roomSizeMax} m²",
|
||||
},
|
||||
{
|
||||
roomSizeMin: roomSize.min,
|
||||
roomSizeMax: roomSize.max,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
<div className={styles.imageContainer}>
|
||||
<ImageGallery images={galleryImages} title={room.name} height={280} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.listContainer}>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Room amenities",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<ul className={styles.facilityList}>
|
||||
{room.roomFacilities
|
||||
.sort((a, b) => a.sortOrder - b.sortOrder)
|
||||
.map((facility) => {
|
||||
return (
|
||||
<li key={facility.name}>
|
||||
<FacilityIcon
|
||||
name={facility.icon}
|
||||
size={24}
|
||||
color="Icon/Default"
|
||||
/>
|
||||
<span>
|
||||
{facility.availableInAllRooms
|
||||
? facility.name
|
||||
: intl.formatMessage(
|
||||
{
|
||||
defaultMessage:
|
||||
"{facility} (available in some rooms)",
|
||||
},
|
||||
{
|
||||
facility: facility.name,
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={styles.listContainer}>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Bed options",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Subject to availability",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<ul className={styles.bedOptions}>
|
||||
{room.roomTypes.map((roomType) => {
|
||||
const description =
|
||||
roomType.description || roomType.mainBed.description
|
||||
const MainBedIcon =
|
||||
BED_TYPE_ICONS[roomType.mainBed.type as BedTypes]
|
||||
const ExtraBedIcon = roomType.fixedExtraBed
|
||||
? BED_TYPE_ICONS[roomType.fixedExtraBed.type as BedTypes]
|
||||
: null
|
||||
return (
|
||||
<li key={roomType.code}>
|
||||
{MainBedIcon ? <MainBedIcon height={24} width={24} /> : null}
|
||||
{ExtraBedIcon ? <ExtraBedIcon height={24} width={30} /> : null}
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<span>{description}</span>
|
||||
</Typography>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div className={styles.listContainer}>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "About the hotel",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>{roomDescription}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
position: relative;
|
||||
margin-bottom: calc(
|
||||
var(--Spacing-x4) * 2 + 80px
|
||||
); /* Creates space between the wrapper and buttonContainer */
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
color: var(--Text-Secondary);
|
||||
}
|
||||
|
||||
.mainContent,
|
||||
.listContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
position: relative;
|
||||
border-radius: var(--Corner-radius-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.imageContainer img {
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.facilityList {
|
||||
column-count: 2;
|
||||
column-gap: var(--Spacing-x2);
|
||||
color: var(--Text-Secondary);
|
||||
}
|
||||
|
||||
.facilityList li {
|
||||
display: flex !important; /* Overrides the display none from grids.stackable on Hotel Page */
|
||||
gap: var(--Spacing-x1);
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.bedOptions {
|
||||
color: var(--Text-Secondary);
|
||||
}
|
||||
|
||||
.bedOptions li {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.facilityList li svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
import BathroomCabinetIcon from "@scandic-hotels/design-system/Icons/BathroomCabinetIcon"
|
||||
import BedHotelIcon from "@scandic-hotels/design-system/Icons/BedHotelIcon"
|
||||
import BowlingPinsIcon from "@scandic-hotels/design-system/Icons/BowlingPinsIcon"
|
||||
import BunkBedIcon from "@scandic-hotels/design-system/Icons/BunkBedIcon"
|
||||
import ConferenceRoomIcon from "@scandic-hotels/design-system/Icons/ConferenceRoomIcon"
|
||||
import CoolingIcon from "@scandic-hotels/design-system/Icons/CoolingIcon"
|
||||
import CouchTableIcon from "@scandic-hotels/design-system/Icons/CouchTableIcon"
|
||||
import DoorIcon from "@scandic-hotels/design-system/Icons/DoorIcon"
|
||||
import FootStoolIcon from "@scandic-hotels/design-system/Icons/FootStoolIcon"
|
||||
import HairdresserIcon from "@scandic-hotels/design-system/Icons/HairdresserIcon"
|
||||
import HairdryerIcon from "@scandic-hotels/design-system/Icons/HairdryerIcon"
|
||||
import HandSoapIcon from "@scandic-hotels/design-system/Icons/HandSoapIcon"
|
||||
import IceMachineIcon from "@scandic-hotels/design-system/Icons/IceMachineIcon"
|
||||
import MassageIcon from "@scandic-hotels/design-system/Icons/MassageIcon"
|
||||
import {
|
||||
MaterialIcon,
|
||||
type MaterialIconSetIconProps,
|
||||
} from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import MirrorIcon from "@scandic-hotels/design-system/Icons/MirrorIcon"
|
||||
import MovingBedsIcon from "@scandic-hotels/design-system/Icons/MovingBedsIcon"
|
||||
import RecordPlayerIcon from "@scandic-hotels/design-system/Icons/RecordPlayerIcon"
|
||||
import RoadIcon from "@scandic-hotels/design-system/Icons/RoadIcon"
|
||||
import RugIcon from "@scandic-hotels/design-system/Icons/RugIcon"
|
||||
import SafetyBoxIcon from "@scandic-hotels/design-system/Icons/SafetyBoxIcon"
|
||||
import SlippersIcon from "@scandic-hotels/design-system/Icons/SlippersIcon"
|
||||
import ToiletIcon from "@scandic-hotels/design-system/Icons/ToiletIcon"
|
||||
import TowelIcon from "@scandic-hotels/design-system/Icons/TowelIcon"
|
||||
import UserPoliceIcon from "@scandic-hotels/design-system/Icons/UserPoliceIcon"
|
||||
import ViewIcon from "@scandic-hotels/design-system/Icons/ViewIcon"
|
||||
import WardIcon from "@scandic-hotels/design-system/Icons/WardIcon"
|
||||
import WindowNotAvailableIcon from "@scandic-hotels/design-system/Icons/WindowNotAvailableIcon"
|
||||
import WoodFloorIcon from "@scandic-hotels/design-system/Icons/WoodFloorIcon"
|
||||
|
||||
import type {
|
||||
IconProps,
|
||||
NucleoIconProps,
|
||||
} from "@scandic-hotels/design-system/Icons"
|
||||
import type { MaterialSymbolProps } from "@scandic-hotels/design-system/Icons/MaterialIcon/MaterialSymbol"
|
||||
import type { JSX } from "react"
|
||||
|
||||
interface FacilityIconProps {
|
||||
name: string | undefined
|
||||
}
|
||||
export function FacilityIcon({
|
||||
name,
|
||||
...props
|
||||
}:
|
||||
| (FacilityIconProps & MaterialIconSetIconProps)
|
||||
| NucleoIconProps
|
||||
| IconProps): JSX.Element {
|
||||
if (!name) {
|
||||
return <MaterialIcon icon="favorite" {...props} />
|
||||
}
|
||||
|
||||
const normalizedName = name.toLowerCase()
|
||||
|
||||
const customIcon = CustomIconMappings.find(
|
||||
(icon) => icon.name.toLowerCase() === normalizedName
|
||||
)
|
||||
if (customIcon) {
|
||||
return <customIcon.icon {...props} />
|
||||
}
|
||||
|
||||
const materialSymbolIcon = MaterialIconMappings.find(
|
||||
(icon) => icon.name.toLowerCase() === normalizedName
|
||||
)
|
||||
|
||||
return materialSymbolIcon ? (
|
||||
<MaterialIcon icon={materialSymbolIcon.icon} {...props} />
|
||||
) : (
|
||||
<MaterialIcon icon="favorite" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
const MaterialIconMappings: {
|
||||
icon: MaterialSymbolProps["icon"]
|
||||
name: string
|
||||
}[] = [
|
||||
{ icon: "air_purifier_gen", name: "aircondition" },
|
||||
{ icon: "travel", name: "airport" },
|
||||
{ icon: "balcony", name: "balcony" },
|
||||
{ icon: "deck", name: "balconyorterrace" },
|
||||
{ icon: "balcony", name: "frenchbalcony" },
|
||||
{ icon: "bathtub", name: "bathroomwithbathtub" },
|
||||
{ icon: "bathtub", name: "bathroomwithshowerandbathtub" },
|
||||
{ icon: "bed", name: "setoftwopillows" },
|
||||
{ icon: "chair", name: "armchairbed" },
|
||||
{ icon: "meeting_room", name: "separatebedroom" },
|
||||
{ icon: "pedal_bike", name: "bikeforloan" },
|
||||
{ icon: "bakery_dining", name: "breakfast" },
|
||||
{ icon: "bakery_dining", name: "servesbreakfastalwaysincluded" },
|
||||
{ icon: "business_center", name: "meetingconferencefacilities" },
|
||||
{ icon: "business_center", name: "meetingrooms" },
|
||||
{ icon: "router", name: "internetwithcableintheroom" },
|
||||
{ icon: "chair", name: "armchair" },
|
||||
{ icon: "chair", name: "chair" },
|
||||
{ icon: "meeting_room", name: "connectingrooms" },
|
||||
{ icon: "location_city", name: "viewcityview" },
|
||||
{ icon: "coffee_maker", name: "nespressomachine" },
|
||||
{ icon: "dining", name: "café" },
|
||||
{ icon: "coffee", name: "coffee" },
|
||||
{ icon: "coffee_maker", name: "coffeemachine" },
|
||||
{ icon: "concierge", name: "lifestyleconcierge" },
|
||||
{ icon: "mode_fan", name: "aircooling" },
|
||||
{ icon: "mode_fan", name: "cooler" },
|
||||
{ icon: "desk", name: "deskandchair" },
|
||||
{ icon: "desk", name: "desk" },
|
||||
{ icon: "dining", name: "diningarea" },
|
||||
{ icon: "table_bar", name: "tablefordining" },
|
||||
{ icon: "electric_bike", name: "ebikeschargingstation" },
|
||||
{ icon: "family_restroom", name: "extrafamilyfriendly" },
|
||||
{ icon: "exercise", name: "gym" },
|
||||
{ icon: "health_and_beauty", name: "beautysalon" },
|
||||
{ icon: "bathroom", name: "bathroom2separatebathrooms" },
|
||||
{ icon: "credit_card_heart", name: "cashfree800pmtill0600am" },
|
||||
{ icon: "credit_card_heart", name: "cashfreehotel" },
|
||||
{ icon: "confirmation_number", name: "coffeevoucher" },
|
||||
{ icon: "water_full", name: "complimentarycoldrefreshments" },
|
||||
{ icon: "groups", name: "conventioncentre" },
|
||||
{ icon: "accessible", name: "disabledparking" },
|
||||
{ icon: "charging_station", name: "dockingstationforipodipad" },
|
||||
{ icon: "cool_to_dry", name: "dryingcabinet" },
|
||||
{ icon: "assistant_navigation", name: "easyaccess" },
|
||||
{ icon: "laundry", name: "garmentsteamer" },
|
||||
{ icon: "stairs", name: "highfloor" },
|
||||
{ icon: "hot_tub", name: "jaccuzzi" },
|
||||
{ icon: "hot_tub", name: "jacuzzi" },
|
||||
{ icon: "countertops", name: "kitchen" },
|
||||
{ icon: "countertops", name: "kitchenette" },
|
||||
{ icon: "checked_bag", name: "latecheckoutuntil1400guaranteed" },
|
||||
{ icon: "music_cast", name: "livemusicexhibitions" },
|
||||
{ icon: "liquor", name: "minibarincludedinroomrate" },
|
||||
{ icon: "local_parking", name: "parkingadditionalcost" },
|
||||
{ icon: "sauna", name: "privatesauna" },
|
||||
{ icon: "kitchen", name: "refrigerator" },
|
||||
{ icon: "airline_seat_recline_normal", name: "seatingarea" },
|
||||
{ icon: "scene", name: "separatelivingroom" },
|
||||
{ icon: "chair", name: "sofabed" },
|
||||
{ icon: "chair", name: "sofas" },
|
||||
{ icon: "chair", name: "sofa" },
|
||||
{ icon: "bedroom_parent", name: "spaciousroom" },
|
||||
{ icon: "local_drink", name: "stillandsparklingwater" },
|
||||
{ icon: "table_bar", name: "table" },
|
||||
{ icon: "coffee_maker", name: "tassimocoffeemaker" },
|
||||
{ icon: "deck", name: "terrace" },
|
||||
{ icon: "iron", name: "trouserpress" },
|
||||
{ icon: "tv_guide", name: "tv" },
|
||||
{ icon: "tv_remote", name: "tvwithcomplimentarymoviechannels" },
|
||||
{ icon: "tv_remote", name: "tvwithmoviechannels" },
|
||||
{ icon: "live_tv", name: "tvwithstreamingoption" },
|
||||
{ icon: "hvac", name: "ventilationinroom" },
|
||||
{ icon: "landscape", name: "viewfjordview" },
|
||||
{ icon: "houseboat", name: "viewlakeview" },
|
||||
{ icon: "panorama", name: "viewpanoramicview" },
|
||||
{ icon: "sailing", name: "viewseaview" },
|
||||
{ icon: "ward", name: "wallbed" },
|
||||
{ icon: "pedal_bike", name: "wallmountedcyclerack" },
|
||||
{ icon: "dresser", name: "wardrobe" },
|
||||
{ icon: "spa", name: "wellnessandsaunaentrancefeeadmission16years" },
|
||||
{ icon: "spa", name: "wellnesspoolsaunaentrancefeeadmission16years" },
|
||||
{ icon: "curtains", name: "windownook" },
|
||||
{ icon: "iron", name: "ironandironingboard" },
|
||||
{ icon: "iron", name: "ironingroom" },
|
||||
{ icon: "kayaking", name: "kayaksforloan" },
|
||||
{ icon: "kettle", name: "kettle" },
|
||||
{ icon: "kettle", name: "kettlewithcoffee" },
|
||||
{ icon: "sync_saved_locally", name: "laptopsafe" },
|
||||
{ icon: "computer", name: "laptoptray" },
|
||||
{ icon: "local_laundry_service", name: "laundryservice" },
|
||||
{ icon: "local_bar", name: "bar" },
|
||||
{ icon: "deck", name: "rooftopbar" },
|
||||
{ icon: "local_bar", name: "skybar" },
|
||||
{ icon: "microwave", name: "microwave" },
|
||||
{ icon: "nature", name: "viewparkview" },
|
||||
{ icon: "nightlife", name: "disconightclub" },
|
||||
{ icon: "smoke_free", name: "nonsmoking" },
|
||||
{ icon: "deck", name: "outdoorterrace" },
|
||||
{ icon: "local_parking", name: "parking" },
|
||||
{ icon: "local_parking", name: "parkingfreeparking" },
|
||||
{ icon: "pets", name: "petfriendlyrooms" },
|
||||
{ icon: "phone", name: "directdialphoneandvoicemail" },
|
||||
{ icon: "restaurant", name: "restaurant" },
|
||||
{ icon: "room_service", name: "roomservice" },
|
||||
{ icon: "sauna", name: "sauna" },
|
||||
{ icon: "shower", name: "bathroomwithshower" },
|
||||
{ icon: "shower", name: "rainshower" },
|
||||
{ icon: "shower", name: "sharedbathroomwithshower" },
|
||||
{ icon: "radio", name: "musicplayer" },
|
||||
{ icon: "shopping_bag", name: "shop" },
|
||||
{ icon: "pool", name: "pool" },
|
||||
{ icon: "laundry", name: "handwash" },
|
||||
{ icon: "connected_tv", name: "tvwithchromecast" },
|
||||
{ icon: "wifi", name: "freewifi" },
|
||||
{ icon: "curtains_closed", name: "blackoutcurtains" },
|
||||
{ icon: "liquor", name: "minibar" },
|
||||
{ icon: "yard", name: "viewatriumview" },
|
||||
]
|
||||
|
||||
const CustomIconMappings = [
|
||||
{ icon: BathroomCabinetIcon, name: "bathroomwithshowerorbathtub" },
|
||||
{ icon: MovingBedsIcon, name: "adjustablebeds" },
|
||||
{ icon: MovingBedsIcon, name: "pulloutbed" },
|
||||
{ icon: BedHotelIcon, name: "extrabed" },
|
||||
{ icon: CoolingIcon, name: "coolingcabinet" },
|
||||
{ icon: FootStoolIcon, name: "footstool" },
|
||||
{ icon: HairdryerIcon, name: "hairdryer" },
|
||||
{ icon: HairdresserIcon, name: "hairdresser" },
|
||||
{ icon: HandSoapIcon, name: "toiletries" },
|
||||
{ icon: TowelIcon, name: "bathrobes" },
|
||||
{ icon: HandSoapIcon, name: "bodycareproducts" },
|
||||
{ icon: HandSoapIcon, name: "bodylotion" },
|
||||
{ icon: BowlingPinsIcon, name: "bowling" },
|
||||
{ icon: BunkBedIcon, name: "bunkbed" },
|
||||
{ icon: BunkBedIcon, name: "bunkbed80x188cm" },
|
||||
{ icon: IceMachineIcon, name: "icemachine" },
|
||||
{ icon: IceMachineIcon, name: "icemachinereception" },
|
||||
{ icon: MassageIcon, name: "massage" },
|
||||
{ icon: ConferenceRoomIcon, name: "meetingarea" },
|
||||
{ icon: UserPoliceIcon, name: "overnightsecurity" },
|
||||
{ icon: UserPoliceIcon, name: "security24hoours" },
|
||||
{ icon: UserPoliceIcon, name: "servicesecurity24h" },
|
||||
{ icon: FootStoolIcon, name: "footstool" },
|
||||
{ icon: WindowNotAvailableIcon, name: "nowindow" },
|
||||
{ icon: DoorIcon, name: "luggagelockers" },
|
||||
{ icon: MirrorIcon, name: "cosmeticmirror" },
|
||||
{ icon: WardIcon, name: "wallbed" },
|
||||
{ icon: CouchTableIcon, name: "sofawithtable" },
|
||||
{ icon: SafetyBoxIcon, name: "safetybox" },
|
||||
{ icon: RecordPlayerIcon, name: "modernvinylplayer" },
|
||||
{ icon: RugIcon, name: "carpetingwalltowallcarpet" },
|
||||
{ icon: ToiletIcon, name: "separatetoilet" },
|
||||
{ icon: WoodFloorIcon, name: "woodenfloor" },
|
||||
{ icon: ViewIcon, name: "view" },
|
||||
{ icon: HandSoapIcon, name: "shampoo" },
|
||||
{ icon: HandSoapIcon, name: "conditioner" },
|
||||
{ icon: RoadIcon, name: "viewstreetview" },
|
||||
{ icon: SlippersIcon, name: "slippers" },
|
||||
{ icon: HandSoapIcon, name: "showergel" },
|
||||
{ icon: HandSoapIcon, name: "showerproductsscentbygrandcentral" },
|
||||
]
|
||||
@@ -1,29 +0,0 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import SidePeek from "@scandic-hotels/design-system/SidePeek"
|
||||
|
||||
import { RoomSidePeekContent } from "./RoomSidePeekContent"
|
||||
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
import type { RoomSidePeekProps } from "@/types/components/sidePeeks/roomSidePeek"
|
||||
|
||||
export default function RoomSidePeek({
|
||||
room,
|
||||
activeSidePeek,
|
||||
close,
|
||||
}: RoomSidePeekProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
title={room.name}
|
||||
isOpen={activeSidePeek === SidePeekEnum.roomDetails}
|
||||
handleClose={close}
|
||||
closeLabel={intl.formatMessage({
|
||||
defaultMessage: "Close",
|
||||
})}
|
||||
>
|
||||
<RoomSidePeekContent room={room} />
|
||||
</SidePeek>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user