Merged in feat/SW-3289-replace-sidepeek-hotel-reservation (pull request #2686)
feat(SW-3289): replace sidepeek * fix(SW-3289): replace sidepeek * fix(SW-3289): add wrapping prop and change prop name to buttonVariant * fix(SW-3289): replace body with typography * fix(SW-3289): fix intl message Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -59,9 +59,3 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: underline;
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
color: var(--Text-Interactive-Secondary);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
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"
|
||||
@@ -13,6 +12,7 @@ import useLang from "../../hooks/useLang"
|
||||
import styles from "./contact.module.css"
|
||||
|
||||
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
interface ContactProps {
|
||||
hotel: Hotel
|
||||
@@ -30,55 +30,67 @@ export default function Contact({ hotel }: ContactProps) {
|
||||
<address className={styles.address}>
|
||||
<ul className={styles.contactInfo}>
|
||||
<li>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Address",
|
||||
})}
|
||||
</Body>
|
||||
<Body>
|
||||
{addressStr}
|
||||
<br />
|
||||
{cityStr}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Address",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{addressStr}
|
||||
<br />
|
||||
{cityStr}
|
||||
</p>
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Driving directions",
|
||||
})}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Driving directions",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<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>
|
||||
<Typography variant="Body/Underline/md">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Google Maps",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Contact us",
|
||||
})}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Contact us",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Link href={`tel:${hotel.contactInformation.phoneNumber}`}>
|
||||
<span className={styles.link}>
|
||||
{hotel.contactInformation.phoneNumber}
|
||||
</span>
|
||||
<Typography variant="Body/Underline/md">
|
||||
<p>{hotel.contactInformation.phoneNumber}</p>
|
||||
</Typography>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
{(hotel.socialMedia.facebook || hotel.socialMedia.instagram) && (
|
||||
<>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Follow us",
|
||||
})}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Follow us",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<div className={styles.soMeIcons}>
|
||||
{hotel.socialMedia.instagram && (
|
||||
<Link href={hotel.socialMedia.instagram} target="_blank">
|
||||
@@ -95,15 +107,17 @@ export default function Contact({ hotel }: ContactProps) {
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Email",
|
||||
})}
|
||||
</Body>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Email",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Link href={`mailto:${hotel.contactInformation.email}`}>
|
||||
<span className={styles.link}>
|
||||
{hotel.contactInformation.email}
|
||||
</span>
|
||||
<Typography variant="Body/Underline/md">
|
||||
<p>{hotel.contactInformation.email}</p>
|
||||
</Typography>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.spacing {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect } from "react"
|
||||
|
||||
import { trpc } from "@scandic-hotels/trpc/client"
|
||||
|
||||
import useLang from "../../hooks/useLang"
|
||||
import useSidePeekStore from "../../stores/sidepeek"
|
||||
import HotelSidePeek from "../HotelSidePeek"
|
||||
import RoomSidePeek from "../RoomSidePeek"
|
||||
|
||||
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,5 +0,0 @@
|
||||
.content {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
color: var(--Text-Default);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Accordion from "@scandic-hotels/design-system/Accordion"
|
||||
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 SidePeek from "@scandic-hotels/design-system/SidePeek"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { SidePeekEnum } from "../../stores/sidepeek"
|
||||
import { useTrackingContext } from "../../trackingContext"
|
||||
import AdditionalAmenities from "../AdditionalAmenities"
|
||||
import Contact from "../Contact"
|
||||
import BreakfastAccordionItem from "../SidePeekAccordions/BreakfastAccordionItem"
|
||||
import CheckInCheckOutAccordionItem from "../SidePeekAccordions/CheckInCheckOutAccordionItem"
|
||||
import ParkingAccordionItem from "../SidePeekAccordions/ParkingAccordionItem"
|
||||
|
||||
import styles from "./hotelSidePeek.module.css"
|
||||
|
||||
import type {
|
||||
AdditionalData,
|
||||
Hotel,
|
||||
Restaurant,
|
||||
} from "@scandic-hotels/trpc/types/hotel"
|
||||
|
||||
type HotelSidePeekProps = {
|
||||
hotel: Hotel & { url: string | null }
|
||||
restaurants: Restaurant[]
|
||||
additionalHotelData: AdditionalData | undefined
|
||||
activeSidePeek: SidePeekEnum
|
||||
close: () => void
|
||||
showCTA: boolean
|
||||
}
|
||||
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
type AccessibilityAccordionItemProps = {
|
||||
elevatorPitch?: string
|
||||
}
|
||||
|
||||
function AccessibilityAccordionItem({
|
||||
elevatorPitch,
|
||||
}: AccessibilityAccordionItemProps) {
|
||||
const intl = useIntl()
|
||||
const tracking = useTrackingContext()
|
||||
|
||||
if (!elevatorPitch) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<AccordionItem
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Accessibility",
|
||||
})}
|
||||
iconName={IconName.Accessibility}
|
||||
className={styles.accordionItem}
|
||||
variant="sidepeek"
|
||||
onOpen={() => tracking.trackAccordionItemOpen("amenities:accessibility")}
|
||||
>
|
||||
<div className={styles.accessibilityContent}>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>{elevatorPitch}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
"use client"
|
||||
import { useEffect } from "react"
|
||||
|
||||
import { Button } from "@scandic-hotels/design-system/Button"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
|
||||
import useSidePeekStore, { type SidePeekEnum } from "../../stores/sidepeek"
|
||||
import { useTrackingContext } from "../../trackingContext"
|
||||
|
||||
interface OpenSidePeekButtonProps {
|
||||
label: string
|
||||
hotelId: string
|
||||
showCTA: boolean
|
||||
sidePeekKey: SidePeekEnum
|
||||
}
|
||||
|
||||
export default function OpenSidePeekButton({
|
||||
label,
|
||||
hotelId,
|
||||
showCTA,
|
||||
sidePeekKey,
|
||||
}: OpenSidePeekButtonProps) {
|
||||
const tracking = useTrackingContext()
|
||||
const { openSidePeek, closeSidePeek } = useSidePeekStore((state) => ({
|
||||
openSidePeek: state.openSidePeek,
|
||||
closeSidePeek: state.closeSidePeek,
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
closeSidePeek()
|
||||
}
|
||||
}, [closeSidePeek])
|
||||
|
||||
return (
|
||||
<Button
|
||||
onPress={() => {
|
||||
openSidePeek({ key: sidePeekKey, hotelId, showCTA })
|
||||
tracking.trackOpenSidePeek({
|
||||
name: sidePeekKey,
|
||||
hotelId,
|
||||
includePathname: true,
|
||||
})
|
||||
}}
|
||||
variant="Text"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
>
|
||||
{label}
|
||||
<MaterialIcon icon="chevron_right" size={24} color="CurrentColor" />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { FacilityIcon } from "@scandic-hotels/design-system/Icons/FacilityIcon"
|
||||
import ImageGallery from "@scandic-hotels/design-system/ImageGallery"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { BED_TYPE_ICONS, type BedTypes } from "../../../misc/bedTypeIcons"
|
||||
|
||||
import styles from "./roomSidePeekContent.module.css"
|
||||
|
||||
import type { ApiImage, 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>
|
||||
)
|
||||
}
|
||||
|
||||
function mapApiImagesToGalleryImages(apiImages: ApiImage[]) {
|
||||
return apiImages.map((apiImage) => ({
|
||||
src: apiImage.imageSizes.medium,
|
||||
alt:
|
||||
apiImage.metaData.altText ||
|
||||
apiImage.metaData.altText_En ||
|
||||
apiImage.metaData.title ||
|
||||
apiImage.metaData.title_En,
|
||||
caption: apiImage.metaData.title || apiImage.metaData.title_En,
|
||||
smallSrc: apiImage.imageSizes.small,
|
||||
}))
|
||||
}
|
||||
@@ -1,62 +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 > span:nth-child(2) {
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.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,35 +0,0 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import SidePeek from "@scandic-hotels/design-system/SidePeek"
|
||||
|
||||
import { SidePeekEnum } from "../../stores/sidepeek"
|
||||
import { RoomSidePeekContent } from "./RoomSidePeekContent"
|
||||
|
||||
import type { Room } from "@scandic-hotels/trpc/types/hotel"
|
||||
|
||||
export type RoomSidePeekProps = {
|
||||
room: Room
|
||||
activeSidePeek: SidePeekEnum | null
|
||||
close: () => void
|
||||
}
|
||||
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { create } from "zustand"
|
||||
|
||||
export enum SidePeekEnum {
|
||||
hotelDetails = "hotel-detail-side-peek",
|
||||
roomDetails = "room-detail-side-peek",
|
||||
bookedRoomDetails = "booked-room-detail-side-peek",
|
||||
}
|
||||
|
||||
interface SidePeekState {
|
||||
activeSidePeek: SidePeekEnum | null
|
||||
hotelId: string | null
|
||||
roomTypeCode: string | null
|
||||
showCTA: boolean
|
||||
confirmationNumber: string
|
||||
openSidePeek: ({
|
||||
key,
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
showCTA,
|
||||
confirmationNumber,
|
||||
}: {
|
||||
key: SidePeekEnum | null
|
||||
hotelId: string
|
||||
roomTypeCode?: string
|
||||
showCTA?: boolean
|
||||
confirmationNumber?: string
|
||||
}) => void
|
||||
closeSidePeek: () => void
|
||||
}
|
||||
|
||||
const useSidePeekStore = create<SidePeekState>((set) => ({
|
||||
activeSidePeek: null,
|
||||
hotelId: null,
|
||||
roomTypeCode: null,
|
||||
showCTA: true,
|
||||
user: null,
|
||||
confirmationNumber: "",
|
||||
openSidePeek: ({
|
||||
key,
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
showCTA,
|
||||
confirmationNumber,
|
||||
}) => {
|
||||
set({
|
||||
activeSidePeek: key,
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
showCTA,
|
||||
confirmationNumber,
|
||||
})
|
||||
},
|
||||
closeSidePeek: () =>
|
||||
set({
|
||||
activeSidePeek: null,
|
||||
hotelId: null,
|
||||
roomTypeCode: null,
|
||||
confirmationNumber: "",
|
||||
}),
|
||||
}))
|
||||
|
||||
export default useSidePeekStore
|
||||
@@ -22,8 +22,8 @@
|
||||
"./searchType": "./lib/misc/searchType.ts",
|
||||
"./bedTypeIcons": "./lib/misc/bedTypeIcons.ts",
|
||||
"./stores/bookingCode-filter": "./lib/stores/bookingCode-filter.ts",
|
||||
"./stores/sidepeek": "./lib/stores/sidepeek.ts",
|
||||
"./components/TripAdvisorChip": "./lib/components/TripAdvisorChip/index.tsx",
|
||||
"./components/Contact": "./lib/components/Contact/index.tsx",
|
||||
"./components/AdditionalAmenities": "./lib/components/AdditionalAmenities/index.tsx",
|
||||
"./components/HotelReservationSidePeek": "./lib/components/HotelReservationSidePeek/index.tsx",
|
||||
"./components/RoomSidePeekContent": "./lib/components/RoomSidePeek/RoomSidePeekContent/index.tsx",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { BookingConfirmation } from "../../types/bookingConfirmation"
|
||||
import type { Room } from "../../types/hotel"
|
||||
|
||||
export function getBookedHotelRoom(
|
||||
export function getHotelRoom(
|
||||
rooms: Room[],
|
||||
roomTypeCode: BookingConfirmation["booking"]["roomTypeCode"]
|
||||
) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { getHotel } from "../../routers/hotels/utils"
|
||||
import { toApiLang } from "../../utils"
|
||||
import { encrypt } from "../../utils/encryption"
|
||||
import { isValidSession } from "../../utils/session"
|
||||
import { getBookedHotelRoom } from "./helpers"
|
||||
import { getHotelRoom } from "./helpers"
|
||||
import {
|
||||
createRefIdInput,
|
||||
findBookingInput,
|
||||
@@ -85,10 +85,7 @@ export const bookingQueryRouter = router({
|
||||
return {
|
||||
...hotelData,
|
||||
booking,
|
||||
room: getBookedHotelRoom(
|
||||
hotelData.roomCategories,
|
||||
booking.roomTypeCode
|
||||
),
|
||||
room: getHotelRoom(hotelData.roomCategories, booking.roomTypeCode),
|
||||
}
|
||||
}),
|
||||
findBooking: safeProtectedServiceProcedure
|
||||
@@ -158,10 +155,7 @@ export const bookingQueryRouter = router({
|
||||
return {
|
||||
...hotelData,
|
||||
booking,
|
||||
room: getBookedHotelRoom(
|
||||
hotelData.roomCategories,
|
||||
booking.roomTypeCode
|
||||
),
|
||||
room: getHotelRoom(hotelData.roomCategories, booking.roomTypeCode),
|
||||
}
|
||||
}),
|
||||
linkedReservations: safeProtectedServiceProcedure
|
||||
|
||||
Reference in New Issue
Block a user