Merged in feat/SW-1062-parking-page (pull request #1242)

Feat/SW-1062 parking page

* feat(SW-1062): Added parking sub page


Approved-by: Christian Andolf
Approved-by: Fredrik Thorsson
This commit is contained in:
Erik Tiekstra
2025-02-04 11:11:51 +00:00
parent 2e311be924
commit c4eafa419b
21 changed files with 393 additions and 87 deletions

View File

@@ -1,18 +1,12 @@
import { parkingSubPage } from "@/constants/routes/hotelSubpages"
import { OpenInNewIcon } from "@/components/Icons"
import ParkingInformation from "@/components/ParkingInformation"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Button from "@/components/TempDesignSystem/Button"
import Divider from "@/components/TempDesignSystem/Divider"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import ParkingList from "./ParkingList"
import ParkingPrices from "./ParkingPrices"
import styles from "./parkingAmenity.module.css"
import type { ParkingAmenityProps } from "@/types/components/hotelPage/sidepeek/parking"
@@ -36,58 +30,7 @@ export default async function ParkingAmenity({
<div className={styles.wrapper}>
{parkingElevatorPitch}
{parking.map((data) => (
<div key={data.type} className={styles.information}>
<div className={styles.list}>
<Body textTransform="bold">{data.type}</Body>
<ParkingList
numberOfChargingSpaces={data.numberOfChargingSpaces}
canMakeReservation={data.canMakeReservation}
numberOfParkingSpots={data.numberOfParkingSpots}
distanceToHotel={data.distanceToHotel}
address={data.address}
/>
</div>
<div className={styles.prices}>
<Body textTransform="bold">
{intl.formatMessage({ id: "Prices" })}
</Body>
<div className={styles.weekday}>
<Caption color="uiTextMediumContrast" textTransform="uppercase">
{intl.formatMessage({ id: "Weekday prices" })}
</Caption>
<Divider color="baseSurfaceSubtleHover" />
<ParkingPrices
pricing={data.pricing.localCurrency?.ordinary}
currency={data.pricing.localCurrency?.currency}
freeParking={data.pricing.freeParking}
/>
</div>
<div className={styles.weekend}>
<Caption color="uiTextMediumContrast" textTransform="uppercase">
{intl.formatMessage({ id: "Weekend prices" })}
</Caption>
<Divider color="baseSurfaceSubtleHover" />
<ParkingPrices
pricing={data.pricing.localCurrency?.weekend}
currency={data.pricing.localCurrency?.currency}
freeParking={data.pricing.freeParking}
/>
</div>
</div>
{data.externalParkingUrl && (
<Button theme="base" intent="primary" asChild>
<Link
href={data.externalParkingUrl}
color="white"
weight="bold"
target="_blank"
>
{intl.formatMessage({ id: "Book parking" })}
<OpenInNewIcon color="white" />
</Link>
</Button>
)}
</div>
<ParkingInformation key={data.type} parking={data} />
))}
{hasExtraParkingPage && (
<Button

View File

@@ -1,24 +1,4 @@
.wrapper,
.information {
.wrapper {
display: grid;
gap: var(--Spacing-x3);
}
.list,
.prices {
display: grid;
gap: var(--Spacing-x-one-and-half);
}
.weekday,
.weekend {
background-color: var(--Base-Surface-Subtle-Normal);
border-radius: var(--Corner-radius-Medium);
padding: var(--Spacing-x2) var(--Spacing-x3);
display: grid;
gap: var(--Spacing-x1);
}
.parkingPageLink {
margin-top: var(--Spacing-x2);
}

View File

@@ -0,0 +1,27 @@
import ParkingInformation from "@/components/ParkingInformation"
import styles from "./additionalContent.module.css"
import type { Hotel } from "@/types/hotel"
interface ParkingAdditionalContentProps {
hotel: Hotel
}
export default async function ParkingAdditionalContent({
hotel,
}: ParkingAdditionalContentProps) {
const parking = hotel.parking
return (
<div className={styles.additionalContent}>
{parking.map((data) => (
<ParkingInformation
key={data.type}
parking={data}
showExternalParkingButton={false}
/>
))}
</div>
)
}

View File

@@ -0,0 +1,4 @@
.additionalContent {
display: grid;
gap: var(--Spacing-x4);
}

View File

@@ -1,7 +1,12 @@
import { wellnessSubPage } from "@/constants/routes/hotelSubpages"
import {
parkingSubPage,
wellnessSubPage,
} from "@/constants/routes/hotelSubpages"
import { getLang } from "@/i18n/serverContext"
import ParkingAdditionalContent from "./ParkingAdditionalContent"
import type { Hotel } from "@/types/hotel"
interface HotelSubpageAdditionalContentProps {
@@ -16,6 +21,8 @@ export default function HotelSubpageAdditionalContent({
const lang = getLang()
switch (subpage) {
case parkingSubPage[lang]:
return <ParkingAdditionalContent hotel={hotel} />
case wellnessSubPage[lang]:
return null
default:

View File

@@ -0,0 +1,64 @@
import NextLink from "next/link"
import { OpenInNewIcon } from "@/components/Icons"
import Button from "@/components/TempDesignSystem/Button"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n"
import styles from "./sidebar.module.css"
import type { Hotel } from "@/types/hotel"
interface HotelSidebarProps {
hotel: Hotel
}
export default async function ParkingSidebar({ hotel }: HotelSidebarProps) {
const intl = await getIntl()
const parking = hotel.parking
.map((parking) => ({ url: parking.externalParkingUrl, type: parking.type }))
.filter(
(parking): parking is { type: string; url: string } => !!parking.url
)
return (
<aside className={styles.sidebar}>
<Title level="h3" as="h4">
{intl.formatMessage({ id: "Address" })}
</Title>
<div>
<Body color="uiTextHighContrast">{hotel.address.streetAddress}</Body>
<Body color="uiTextHighContrast">
{hotel.address.zipCode} {hotel.address.city}
</Body>
<Body color="uiTextHighContrast">{hotel.address.country}</Body>
</div>
<Title level="h3" as="h4">
{intl.formatMessage({ id: "Contact us" })}
</Title>
<Link
href={`tel:${hotel.contactInformation.phoneNumber}`}
color="peach80"
textDecoration="underline"
>
{hotel.contactInformation.phoneNumber}
</Link>
{parking.map(({ url, type }) => (
<Button key={type} theme="base" intent="primary" variant="icon" asChild>
<NextLink href={url} target="_blank">
{intl.formatMessage(
{ id: "Book {type} parking" },
{ type: type.toLowerCase() }
)}
<OpenInNewIcon />
</NextLink>
</Button>
))}
</aside>
)
}

View File

@@ -1,7 +1,11 @@
import { wellnessSubPage } from "@/constants/routes/hotelSubpages"
import {
parkingSubPage,
wellnessSubPage,
} from "@/constants/routes/hotelSubpages"
import { getLang } from "@/i18n/serverContext"
import ParkingSidebar from "./ParkingSidebar"
import WellnessSidebar from "./WellnessSidebar"
import type { Hotel } from "@/types/hotel"
@@ -17,6 +21,8 @@ export default function HotelSubpageSidebar({
}: HotelSubpageSidebarProps) {
const lang = getLang()
switch (subpage) {
case parkingSubPage[lang]:
return <ParkingSidebar hotel={hotel} />
case wellnessSubPage[lang]:
return <WellnessSidebar hotel={hotel} />
default:

View File

@@ -1,4 +1,7 @@
import { wellnessSubPage } from "@/constants/routes/hotelSubpages"
import {
parkingSubPage,
wellnessSubPage,
} from "@/constants/routes/hotelSubpages"
import { getLang } from "@/i18n/serverContext"
@@ -15,17 +18,29 @@ export function getSubpageData(
const additionalData = hotelData.additionalData
const hotel = hotelData.hotel
switch (subpage) {
case parkingSubPage[lang]:
const parkingImage = additionalData.parkingImages?.heroImages[0]
return {
...additionalData.hotelParking,
heading: intl.formatMessage({ id: "Parking" }),
heroImage: parkingImage
? {
src: parkingImage.imageSizes.medium,
alt: parkingImage.metaData.altText || "",
}
: null,
}
case wellnessSubPage[lang]:
const heroImage = hotel.healthFacilities.find(
const wellnessImage = hotel.healthFacilities.find(
(fac) => fac.content.images.length
)?.content.images[0]
return {
...additionalData.healthAndFitness,
heading: intl.formatMessage({ id: "Wellness & Exercise" }),
heroImage: heroImage
heroImage: wellnessImage
? {
src: heroImage.imageSizes.medium,
alt: heroImage.metaData.altText || "",
src: wellnessImage.imageSizes.medium,
alt: wellnessImage.metaData.altText || "",
}
: null,
}