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:
@@ -1,18 +1,12 @@
|
|||||||
import { parkingSubPage } from "@/constants/routes/hotelSubpages"
|
import { parkingSubPage } from "@/constants/routes/hotelSubpages"
|
||||||
|
|
||||||
import { OpenInNewIcon } from "@/components/Icons"
|
import ParkingInformation from "@/components/ParkingInformation"
|
||||||
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
|
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import Divider from "@/components/TempDesignSystem/Divider"
|
|
||||||
import Link from "@/components/TempDesignSystem/Link"
|
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 { getIntl } from "@/i18n"
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
import ParkingList from "./ParkingList"
|
|
||||||
import ParkingPrices from "./ParkingPrices"
|
|
||||||
|
|
||||||
import styles from "./parkingAmenity.module.css"
|
import styles from "./parkingAmenity.module.css"
|
||||||
|
|
||||||
import type { ParkingAmenityProps } from "@/types/components/hotelPage/sidepeek/parking"
|
import type { ParkingAmenityProps } from "@/types/components/hotelPage/sidepeek/parking"
|
||||||
@@ -36,58 +30,7 @@ export default async function ParkingAmenity({
|
|||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
{parkingElevatorPitch}
|
{parkingElevatorPitch}
|
||||||
{parking.map((data) => (
|
{parking.map((data) => (
|
||||||
<div key={data.type} className={styles.information}>
|
<ParkingInformation key={data.type} parking={data} />
|
||||||
<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>
|
|
||||||
))}
|
))}
|
||||||
{hasExtraParkingPage && (
|
{hasExtraParkingPage && (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,24 +1,4 @@
|
|||||||
.wrapper,
|
.wrapper {
|
||||||
.information {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--Spacing-x3);
|
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);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.additionalContent {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x4);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { wellnessSubPage } from "@/constants/routes/hotelSubpages"
|
import {
|
||||||
|
parkingSubPage,
|
||||||
|
wellnessSubPage,
|
||||||
|
} from "@/constants/routes/hotelSubpages"
|
||||||
|
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import ParkingAdditionalContent from "./ParkingAdditionalContent"
|
||||||
|
|
||||||
import type { Hotel } from "@/types/hotel"
|
import type { Hotel } from "@/types/hotel"
|
||||||
|
|
||||||
interface HotelSubpageAdditionalContentProps {
|
interface HotelSubpageAdditionalContentProps {
|
||||||
@@ -16,6 +21,8 @@ export default function HotelSubpageAdditionalContent({
|
|||||||
const lang = getLang()
|
const lang = getLang()
|
||||||
|
|
||||||
switch (subpage) {
|
switch (subpage) {
|
||||||
|
case parkingSubPage[lang]:
|
||||||
|
return <ParkingAdditionalContent hotel={hotel} />
|
||||||
case wellnessSubPage[lang]:
|
case wellnessSubPage[lang]:
|
||||||
return null
|
return null
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
import { wellnessSubPage } from "@/constants/routes/hotelSubpages"
|
import {
|
||||||
|
parkingSubPage,
|
||||||
|
wellnessSubPage,
|
||||||
|
} from "@/constants/routes/hotelSubpages"
|
||||||
|
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import ParkingSidebar from "./ParkingSidebar"
|
||||||
import WellnessSidebar from "./WellnessSidebar"
|
import WellnessSidebar from "./WellnessSidebar"
|
||||||
|
|
||||||
import type { Hotel } from "@/types/hotel"
|
import type { Hotel } from "@/types/hotel"
|
||||||
@@ -17,6 +21,8 @@ export default function HotelSubpageSidebar({
|
|||||||
}: HotelSubpageSidebarProps) {
|
}: HotelSubpageSidebarProps) {
|
||||||
const lang = getLang()
|
const lang = getLang()
|
||||||
switch (subpage) {
|
switch (subpage) {
|
||||||
|
case parkingSubPage[lang]:
|
||||||
|
return <ParkingSidebar hotel={hotel} />
|
||||||
case wellnessSubPage[lang]:
|
case wellnessSubPage[lang]:
|
||||||
return <WellnessSidebar hotel={hotel} />
|
return <WellnessSidebar hotel={hotel} />
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { wellnessSubPage } from "@/constants/routes/hotelSubpages"
|
import {
|
||||||
|
parkingSubPage,
|
||||||
|
wellnessSubPage,
|
||||||
|
} from "@/constants/routes/hotelSubpages"
|
||||||
|
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
@@ -15,17 +18,29 @@ export function getSubpageData(
|
|||||||
const additionalData = hotelData.additionalData
|
const additionalData = hotelData.additionalData
|
||||||
const hotel = hotelData.hotel
|
const hotel = hotelData.hotel
|
||||||
switch (subpage) {
|
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]:
|
case wellnessSubPage[lang]:
|
||||||
const heroImage = hotel.healthFacilities.find(
|
const wellnessImage = hotel.healthFacilities.find(
|
||||||
(fac) => fac.content.images.length
|
(fac) => fac.content.images.length
|
||||||
)?.content.images[0]
|
)?.content.images[0]
|
||||||
return {
|
return {
|
||||||
...additionalData.healthAndFitness,
|
...additionalData.healthAndFitness,
|
||||||
heading: intl.formatMessage({ id: "Wellness & Exercise" }),
|
heading: intl.formatMessage({ id: "Wellness & Exercise" }),
|
||||||
heroImage: heroImage
|
heroImage: wellnessImage
|
||||||
? {
|
? {
|
||||||
src: heroImage.imageSizes.medium,
|
src: wellnessImage.imageSizes.medium,
|
||||||
alt: heroImage.metaData.altText || "",
|
alt: wellnessImage.metaData.altText || "",
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
}
|
}
|
||||||
|
|||||||
64
components/ParkingInformation/ParkingList/index.tsx
Normal file
64
components/ParkingInformation/ParkingList/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
|
|
||||||
|
import styles from "./parkingList.module.css"
|
||||||
|
|
||||||
|
import type { ParkingListProps } from "@/types/components/hotelPage/sidepeek/parking"
|
||||||
|
|
||||||
|
export default async function ParkingList({
|
||||||
|
numberOfChargingSpaces,
|
||||||
|
canMakeReservation,
|
||||||
|
numberOfParkingSpots,
|
||||||
|
distanceToHotel,
|
||||||
|
address,
|
||||||
|
}: ParkingListProps) {
|
||||||
|
const intl = await getIntl()
|
||||||
|
|
||||||
|
const canMakeReservationYesMsg = intl.formatMessage({
|
||||||
|
id: "Parking can be reserved in advance: Yes",
|
||||||
|
})
|
||||||
|
const canMakeReservationNoMsg = intl.formatMessage({
|
||||||
|
id: "Parking can be reserved in advance: No",
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Body color="uiTextHighContrast" asChild>
|
||||||
|
<ul className={styles.listStyling}>
|
||||||
|
{numberOfChargingSpaces ? (
|
||||||
|
<li>
|
||||||
|
{intl.formatMessage(
|
||||||
|
{ id: "Number of charging points for electric cars: {number}" },
|
||||||
|
{ number: numberOfChargingSpaces }
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
<li>
|
||||||
|
{canMakeReservation
|
||||||
|
? canMakeReservationYesMsg
|
||||||
|
: canMakeReservationNoMsg}
|
||||||
|
</li>
|
||||||
|
{numberOfParkingSpots ? (
|
||||||
|
<li>
|
||||||
|
{intl.formatMessage(
|
||||||
|
{ id: "Number of parking spots: {number}" },
|
||||||
|
{ number: numberOfParkingSpots }
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
{distanceToHotel ? (
|
||||||
|
<li>
|
||||||
|
{intl.formatMessage(
|
||||||
|
{ id: "Distance to hotel: {distanceInM} m" },
|
||||||
|
{ distanceInM: distanceToHotel }
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
{address ? (
|
||||||
|
<li>
|
||||||
|
{intl.formatMessage({ id: "Address: {address}" }, { address })}
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
</ul>
|
||||||
|
</Body>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
.listStyling {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listStyling > li::before {
|
||||||
|
content: url("/_static/icons/heart.svg");
|
||||||
|
position: relative;
|
||||||
|
height: 8px;
|
||||||
|
top: 3px;
|
||||||
|
margin-right: var(--Spacing-x1);
|
||||||
|
}
|
||||||
69
components/ParkingInformation/ParkingPrices/index.tsx
Normal file
69
components/ParkingInformation/ParkingPrices/index.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
|
import { formatPrice } from "@/utils/numberFormatting"
|
||||||
|
|
||||||
|
import styles from "./parkingPrices.module.css"
|
||||||
|
|
||||||
|
import {
|
||||||
|
type ParkingPricesProps,
|
||||||
|
Periods,
|
||||||
|
} from "@/types/components/hotelPage/sidepeek/parking"
|
||||||
|
|
||||||
|
export default async function ParkingPrices({
|
||||||
|
currency = "",
|
||||||
|
freeParking,
|
||||||
|
pricing,
|
||||||
|
}: ParkingPricesProps) {
|
||||||
|
const intl = await getIntl()
|
||||||
|
const day = intl.formatMessage({ id: "Price per day" })
|
||||||
|
const night = intl.formatMessage({ id: "Price per night" })
|
||||||
|
const allDay = intl.formatMessage({ id: "Price per 24 hours" })
|
||||||
|
|
||||||
|
function getPeriod(period?: string) {
|
||||||
|
switch (period) {
|
||||||
|
case Periods.day:
|
||||||
|
return day
|
||||||
|
case Periods.night:
|
||||||
|
return night
|
||||||
|
case Periods.allDay:
|
||||||
|
return allDay
|
||||||
|
default:
|
||||||
|
return period
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredPeriods = pricing?.filter((filter) => filter.period !== "Hour")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
{filteredPeriods?.map((parking) => (
|
||||||
|
<div key={parking.period} className={styles.period}>
|
||||||
|
<div className={styles.information}>
|
||||||
|
<Body textTransform="bold" color="uiTextHighContrast">
|
||||||
|
{getPeriod(parking.period)}
|
||||||
|
</Body>
|
||||||
|
<Body color="uiTextHighContrast">
|
||||||
|
{parking.amount
|
||||||
|
? freeParking
|
||||||
|
? intl.formatMessage({ id: "Free parking" })
|
||||||
|
: formatPrice(intl, parking.amount, currency)
|
||||||
|
: intl.formatMessage({ id: "N/A" })}
|
||||||
|
</Body>
|
||||||
|
</div>
|
||||||
|
{parking.startTime &&
|
||||||
|
parking.endTime &&
|
||||||
|
parking.period !== Periods.allDay && (
|
||||||
|
<div className={styles.information}>
|
||||||
|
<Body color="uiTextHighContrast">
|
||||||
|
{intl.formatMessage({ id: "From" })}
|
||||||
|
</Body>
|
||||||
|
<Body color="uiTextHighContrast">
|
||||||
|
{`${parking.startTime}-${parking.endTime}`}
|
||||||
|
</Body>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
.wrapper {
|
||||||
|
display: grid;
|
||||||
|
row-gap: var(--Spacing-x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.period {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--Spacing-x5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.information {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
78
components/ParkingInformation/index.tsx
Normal file
78
components/ParkingInformation/index.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
|
|
||||||
|
import { OpenInNewIcon } from "../Icons"
|
||||||
|
import Button from "../TempDesignSystem/Button"
|
||||||
|
import Divider from "../TempDesignSystem/Divider"
|
||||||
|
import Caption from "../TempDesignSystem/Text/Caption"
|
||||||
|
import Subtitle from "../TempDesignSystem/Text/Subtitle"
|
||||||
|
import ParkingList from "./ParkingList"
|
||||||
|
import ParkingPrices from "./ParkingPrices"
|
||||||
|
|
||||||
|
import styles from "./parkingInformation.module.css"
|
||||||
|
|
||||||
|
import type { Parking } from "@/types/hotel"
|
||||||
|
|
||||||
|
interface ParkingInformationProps {
|
||||||
|
parking: Parking
|
||||||
|
showExternalParkingButton?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function ParkingInformation({
|
||||||
|
parking,
|
||||||
|
showExternalParkingButton = true,
|
||||||
|
}: ParkingInformationProps) {
|
||||||
|
const intl = await getIntl()
|
||||||
|
return (
|
||||||
|
<div className={styles.parkingInformation}>
|
||||||
|
<div className={styles.list}>
|
||||||
|
<Subtitle type="two" asChild>
|
||||||
|
<h4>{parking.type}</h4>
|
||||||
|
</Subtitle>
|
||||||
|
<ParkingList
|
||||||
|
numberOfChargingSpaces={parking.numberOfChargingSpaces}
|
||||||
|
canMakeReservation={parking.canMakeReservation}
|
||||||
|
numberOfParkingSpots={parking.numberOfParkingSpots}
|
||||||
|
distanceToHotel={parking.distanceToHotel}
|
||||||
|
address={parking.address}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.prices}>
|
||||||
|
<Subtitle type="two" asChild>
|
||||||
|
<h5>{intl.formatMessage({ id: "Prices" })}</h5>
|
||||||
|
</Subtitle>
|
||||||
|
<div className={styles.priceWrapper}>
|
||||||
|
<Caption color="uiTextMediumContrast" textTransform="uppercase">
|
||||||
|
{intl.formatMessage({ id: "Weekday prices" })}
|
||||||
|
</Caption>
|
||||||
|
<Divider color="baseSurfaceSubtleHover" />
|
||||||
|
<ParkingPrices
|
||||||
|
pricing={parking.pricing.localCurrency?.ordinary}
|
||||||
|
currency={parking.pricing.localCurrency?.currency}
|
||||||
|
freeParking={parking.pricing.freeParking}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.priceWrapper}>
|
||||||
|
<Caption color="uiTextMediumContrast" textTransform="uppercase">
|
||||||
|
{intl.formatMessage({ id: "Weekend prices" })}
|
||||||
|
</Caption>
|
||||||
|
<Divider color="baseSurfaceSubtleHover" />
|
||||||
|
<ParkingPrices
|
||||||
|
pricing={parking.pricing.localCurrency?.weekend}
|
||||||
|
currency={parking.pricing.localCurrency?.currency}
|
||||||
|
freeParking={parking.pricing.freeParking}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{parking.externalParkingUrl && showExternalParkingButton && (
|
||||||
|
<Button theme="base" intent="primary" variant="icon" asChild>
|
||||||
|
<Link href={parking.externalParkingUrl} target="_blank">
|
||||||
|
{intl.formatMessage({ id: "Book parking" })}
|
||||||
|
<OpenInNewIcon />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
18
components/ParkingInformation/parkingInformation.module.css
Normal file
18
components/ParkingInformation/parkingInformation.module.css
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
.parkingInformation {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list,
|
||||||
|
.prices {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x-one-and-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.priceWrapper {
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -60,6 +60,7 @@
|
|||||||
"Book a table online": "Book et bord online",
|
"Book a table online": "Book et bord online",
|
||||||
"Book parking": "Book parkering",
|
"Book parking": "Book parkering",
|
||||||
"Book reward night": "Book bonusnat",
|
"Book reward night": "Book bonusnat",
|
||||||
|
"Book {type} parking": "Book {type} parkering",
|
||||||
"Booking confirmation": "Booking bekræftelse",
|
"Booking confirmation": "Booking bekræftelse",
|
||||||
"Booking number": "Bookingnummer",
|
"Booking number": "Bookingnummer",
|
||||||
"Breakfast": "Morgenmad",
|
"Breakfast": "Morgenmad",
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"Book a table online": "Tisch online buchen",
|
"Book a table online": "Tisch online buchen",
|
||||||
"Book parking": "Parkplatz buchen",
|
"Book parking": "Parkplatz buchen",
|
||||||
"Book reward night": "Bonusnacht buchen",
|
"Book reward night": "Bonusnacht buchen",
|
||||||
|
"Book {type} parking": "Buchen Sie {type} Parkplatz",
|
||||||
"Booking confirmation": "Buchungsbestätigung",
|
"Booking confirmation": "Buchungsbestätigung",
|
||||||
"Booking number": "Buchungsnummer",
|
"Booking number": "Buchungsnummer",
|
||||||
"Breakfast": "Frühstück",
|
"Breakfast": "Frühstück",
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
"Book parking": "Book parking",
|
"Book parking": "Book parking",
|
||||||
"Book reward night": "Book reward night",
|
"Book reward night": "Book reward night",
|
||||||
"Book your next stay": "Book your next stay",
|
"Book your next stay": "Book your next stay",
|
||||||
|
"Book {type} parking": "Book {type} parking",
|
||||||
"Booking": "Booking",
|
"Booking": "Booking",
|
||||||
"Booking confirmation": "Booking confirmation",
|
"Booking confirmation": "Booking confirmation",
|
||||||
"Booking number": "Booking number",
|
"Booking number": "Booking number",
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"Book a table online": "Varaa pöytä verkossa",
|
"Book a table online": "Varaa pöytä verkossa",
|
||||||
"Book parking": "Varaa pysäköinti",
|
"Book parking": "Varaa pysäköinti",
|
||||||
"Book reward night": "Kirjapalkinto-ilta",
|
"Book reward night": "Kirjapalkinto-ilta",
|
||||||
|
"Book {type} parking": "Varaa {type} pysäköinti",
|
||||||
"Booking confirmation": "Varausvahvistus",
|
"Booking confirmation": "Varausvahvistus",
|
||||||
"Booking number": "Varausnumero",
|
"Booking number": "Varausnumero",
|
||||||
"Breakfast": "Aamiainen",
|
"Breakfast": "Aamiainen",
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"Book a table online": "Bestill bord online",
|
"Book a table online": "Bestill bord online",
|
||||||
"Book parking": "Bestill parkering",
|
"Book parking": "Bestill parkering",
|
||||||
"Book reward night": "Bestill belønningskveld",
|
"Book reward night": "Bestill belønningskveld",
|
||||||
|
"Book {type} parking": "Book {type} parkering",
|
||||||
"Booking confirmation": "Bestillingsbekreftelse",
|
"Booking confirmation": "Bestillingsbekreftelse",
|
||||||
"Booking number": "Bestillingsnummer",
|
"Booking number": "Bestillingsnummer",
|
||||||
"Breakfast": "Frokost",
|
"Breakfast": "Frokost",
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"Book a table online": "Boka ett bord online",
|
"Book a table online": "Boka ett bord online",
|
||||||
"Book parking": "Boka parkering",
|
"Book parking": "Boka parkering",
|
||||||
"Book reward night": "Boka frinatt",
|
"Book reward night": "Boka frinatt",
|
||||||
|
"Book {type} parking": "Boka {type} parkering",
|
||||||
"Booking confirmation": "Bokningsbekräftelse",
|
"Booking confirmation": "Bokningsbekräftelse",
|
||||||
"Booking number": "Bokningsnummer",
|
"Booking number": "Bokningsnummer",
|
||||||
"Breakfast": "Frukost",
|
"Breakfast": "Frukost",
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export const additionalDataSchema = z.object({
|
|||||||
conferencesAndMeetings: facilitySchema.optional(),
|
conferencesAndMeetings: facilitySchema.optional(),
|
||||||
healthAndWellness: facilitySchema.optional(),
|
healthAndWellness: facilitySchema.optional(),
|
||||||
restaurantImages: facilitySchema.optional(),
|
restaurantImages: facilitySchema.optional(),
|
||||||
|
parkingImages: facilitySchema.optional(),
|
||||||
restaurantsOverviewPage: restaurantsOverviewPageSchema,
|
restaurantsOverviewPage: restaurantsOverviewPageSchema,
|
||||||
meetingRooms: extraPageSchema,
|
meetingRooms: extraPageSchema,
|
||||||
healthAndFitness: extraPageSchema,
|
healthAndFitness: extraPageSchema,
|
||||||
|
|||||||
Reference in New Issue
Block a user