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

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

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


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

View File

@@ -13,7 +13,7 @@ export default function FooterMainNav({ mainLinks }: FooterMainNavProps) {
<ul className={styles.mainNavigationList}>
{mainLinks.map((link) => (
<li key={link.title} className={styles.mainNavigationItem}>
<Subtitle color="baseTextMediumContrast" type="two" asChild>
<Subtitle color="baseTextHighContrast" type="two" asChild>
<Link
color="burgundy"
href={link.url}
@@ -40,7 +40,7 @@ export function FooterMainNavSkeleton() {
<ul className={styles.mainNavigationList}>
{items.map((x) => (
<li key={x} className={styles.mainNavigationItem}>
<Subtitle color="baseTextMediumContrast" type="two" asChild>
<Subtitle color="baseTextHighContrast" type="two" asChild>
<span className={styles.mainNavigationLink}>
<SkeletonShimmer width="80%" />
<ArrowRightIcon color="peach80" />

View File

@@ -22,10 +22,6 @@
flex-direction: column;
}
.heading {
font-weight: 500;
}
.soMeIcons {
display: flex;
gap: var(--Spacing-x-one-and-half);
@@ -35,14 +31,20 @@
display: flex;
align-items: center;
column-gap: var(--Spacing-x-one-and-half);
grid-column: 2 / 3;
grid-row: 3 / 4;
grid-column: 1 / 3;
grid-row: 4 / 4;
font-size: var(--typography-Footnote-Regular-fontSize);
line-height: ();
margin-bottom: var(--Spacing-x1);
}
.ecoLabel img {
flex-shrink: 0;
}
.ecoLabelText {
display: flex;
color: var(--UI-Text-Medium-contrast);
flex-direction: column;
justify-content: center;
}

View File

@@ -5,6 +5,7 @@ import { useIntl } from "react-intl"
import { FacebookIcon, InstagramIcon } from "@/components/Icons"
import Image from "@/components/Image"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import useLang from "@/hooks/useLang"
import styles from "./contact.module.css"
@@ -20,17 +21,17 @@ export default function Contact({ hotel }: ContactProps) {
<address className={styles.address}>
<ul className={styles.contactInfo}>
<li>
<span className={styles.heading}>
<Body textTransform="bold">
{intl.formatMessage({ id: "Address" })}
</span>
<span>
</Body>
<Body>
{`${hotel.address.streetAddress}, ${hotel.address.city}`}
</span>
</Body>
</li>
<li>
<span className={styles.heading}>
<Body textTransform="bold">
{intl.formatMessage({ id: "Driving directions" })}
</span>
</Body>
<a
href={`https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}`}
className={styles.googleMaps}
@@ -40,20 +41,9 @@ export default function Contact({ hotel }: ContactProps) {
</a>
</li>
<li>
<span className={styles.heading}>
{intl.formatMessage({ id: "Email" })}
</span>
<Link
href={`mailto:${hotel.contactInformation.email}`}
color="peach80"
>
{hotel.contactInformation.email}
</Link>
</li>
<li>
<span className={styles.heading}>
<Body textTransform="bold">
{intl.formatMessage({ id: "Contact us" })}
</span>
</Body>
<Link
href={`tel:${hotel.contactInformation.phoneNumber}`}
color="peach80"
@@ -62,25 +52,44 @@ export default function Contact({ hotel }: ContactProps) {
</Link>
</li>
<li>
<span className={styles.heading}>
{intl.formatMessage({ id: "Follow us" })}
</span>
<div className={styles.soMeIcons}>
<Link href="#" target="_blank">
<InstagramIcon color="burgundy" />
</Link>
<Link href="#" target="_blank">
<FacebookIcon color="burgundy" />
</Link>
</div>
{(hotel.socialMedia.facebook || hotel.socialMedia.instagram) && (
<>
<Body textTransform="bold">
{intl.formatMessage({ id: "Follow us" })}
</Body>
<div className={styles.soMeIcons}>
{hotel.socialMedia.instagram && (
<Link href={hotel.socialMedia.instagram} target="_blank">
<InstagramIcon color="burgundy" />
</Link>
)}
{hotel.socialMedia.facebook && (
<Link href={hotel.socialMedia.facebook} target="_blank">
<FacebookIcon color="burgundy" />
</Link>
)}
</div>
</>
)}
</li>
<li>
<Body textTransform="bold">
{intl.formatMessage({ id: "Email" })}
</Body>
<Link
href={`mailto:${hotel.contactInformation.email}`}
color="peach80"
>
{hotel.contactInformation.email}
</Link>
</li>
</ul>
</address>
{hotel.hotelFacts.ecoLabels.nordicEcoLabel ? (
{hotel.hotelFacts.ecoLabels?.nordicEcoLabel ? (
<div className={styles.ecoLabel}>
<Image
height={38}
width={38}
width={43}
alt={intl.formatMessage({ id: "Nordic Swan Ecolabel" })}
src={`/_static/img/icons/swan-eco/swan_eco_dark_${lang}.png`}
/>

View File

@@ -0,0 +1,27 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function FilledHeartIcon({
className,
color,
...props
}: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M8.00091 13.4171L7.23424 12.7255C6.1424 11.7524 5.24166 10.9189 4.53203 10.2249C3.82239 9.53081 3.26322 8.91489 2.85451 8.37709C2.44579 7.8393 2.16246 7.34774 2.00451 6.90241C1.84656 6.45708 1.76758 6.00088 1.76758 5.53379C1.76758 4.57813 2.09533 3.77255 2.75083 3.11704C3.40634 2.46154 4.21192 2.13379 5.16758 2.13379C5.70411 2.13379 6.22436 2.25046 6.72831 2.48379C7.23227 2.71712 7.65647 3.05046 8.00091 3.48379C8.36202 3.05046 8.7898 2.71712 9.28425 2.48379C9.77869 2.25046 10.2954 2.13379 10.8342 2.13379C11.7899 2.13379 12.5955 2.46154 13.251 3.11704C13.9065 3.77255 14.2342 4.57813 14.2342 5.53379C14.2342 6.00088 14.158 6.45153 14.0056 6.88574C13.8533 7.31996 13.5727 7.80319 13.164 8.33542C12.7553 8.86767 12.1933 9.48637 11.4781 10.1915C10.7629 10.8967 9.84831 11.7524 8.73425 12.7588L8.00091 13.4171Z"
fill="#B05B65"
/>
</svg>
)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,8 @@ import { useRef } from "react"
import { ChevronDownIcon } from "@/components/Icons"
import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name"
import Body from "../../Text/Body"
import Subtitle from "../../Text/Subtitle"
import { accordionItemVariants } from "./variants"
import styles from "./accordionItem.module.css"
@@ -49,7 +51,23 @@ export default function AccordionItem({
<details ref={detailsRef} onToggle={toggleAccordion}>
<summary className={styles.summary}>
{IconComp && <IconComp className={styles.icon} color="burgundy" />}
<span className={styles.title}>{title}</span>
{variant === "card" ? (
<Body
textTransform="bold"
color="baseTextHighContrast"
className={styles.title}
>
{title}
</Body>
) : (
<Subtitle
className={styles.title}
type="two"
color="baseTextHighContrast"
>
{title}
</Subtitle>
)}
<ChevronDownIcon
className={styles.chevron}
color="burgundy"

View File

@@ -63,7 +63,7 @@
color: var(--Scandic-Brand-Pale-Peach);
}
.baseTextMediumContrast {
.baseTextHighContrast {
color: var(--Base-Text-High-contrast);
}

View File

@@ -9,7 +9,7 @@ const config = {
burgundy: styles.burgundy,
baseTextDisabled: styles.baseTextDisabled,
pale: styles.pale,
baseTextMediumContrast: styles.baseTextMediumContrast,
baseTextHighContrast: styles.baseTextHighContrast,
uiTextHighContrast: styles.uiTextHighContrast,
uiTextMediumContrast: styles.uiTextMediumContrast,
uiTextPlaceholder: styles.uiTextPlaceholder,

View File

@@ -64,6 +64,7 @@
"Check out": "Check ud",
"Check out at latest": "Udtjekning senest",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tjek de kreditkort, der er gemt på din profil. Betal med et gemt kort, når du er logget ind for en mere jævn weboplevelse.",
"Check-in/Check-out": "Indtjekning/Udtjekning",
"Children": "børn",
"Choose room": "Vælg rum",
"Cities": "Byer",
@@ -164,6 +165,7 @@
"Hotel surroundings": "Hotel omgivelser",
"Hotel(s)": "{amount} {amount, plural, one {hotel} other {hoteller}}",
"Hotels": "Hoteller",
"Hours": "Tider",
"How do you want to sleep?": "Hvordan vil du sove?",
"How it works": "Hvordan det virker",
"Hurry up and use them before they expire!": "Skynd dig og brug dem, før de udløber!",
@@ -257,6 +259,7 @@
"Open menu": "Åbn menuen",
"Open my pages menu": "Åbn mine sider menuen",
"Opening Hours": "Åbningstider",
"Outdoor": "Udendørs",
"OutdoorPool": "Udendørs pool",
"Overview": "Oversigt",
"PETR": "Kæledyr",
@@ -305,7 +308,7 @@
"Relax": "Slap af",
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
"Request bedtype": "Anmod om sengetype",
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
"Restaurant": "{count, plural, one {Restaurant} other {Restauranter}}",
"Restaurant & Bar": "Restaurant & Bar",
"Restaurants & Bars": "Restaurants & Bars",
"Retype new password": "Gentag den nye adgangskode",

View File

@@ -64,6 +64,7 @@
"Check out": "Auschecken",
"Check out at latest": "Check-out spätestens",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sehen Sie sich die in Ihrem Profil gespeicherten Kreditkarten an. Bezahlen Sie mit einer gespeicherten Karte, wenn Sie angemeldet sind, für ein reibungsloseres Web-Erlebnis.",
"Check-in/Check-out": "Einchecken/Auschecken",
"Children": "Kinder",
"Choose room": "Zimmer wählen",
"Cities": "Städte",
@@ -164,6 +165,7 @@
"Hotel surroundings": "Umgebung des Hotels",
"Hotel(s)": "{amount} {amount, plural, one {hotel} other {hotels}}",
"Hotels": "Hotels",
"Hours": "Zeiten",
"How do you want to sleep?": "Wie möchtest du schlafen?",
"How it works": "Wie es funktioniert",
"Hurry up and use them before they expire!": "Beeilen Sie sich und nutzen Sie sie, bevor sie ablaufen!",
@@ -256,6 +258,7 @@
"Open menu": "Menü öffnen",
"Open my pages menu": "Meine Seiten Menü öffnen",
"Opening Hours": "Öffnungszeiten",
"Outdoor": "Im Freien",
"OutdoorPool": "Außenpool",
"Overview": "Übersicht",
"PETR": "Haustier",
@@ -304,7 +307,7 @@
"Relax": "Entspannen",
"Remove card from member profile": "Karte aus dem Mitgliedsprofil entfernen",
"Request bedtype": "Bettentyp anfragen",
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
"Restaurant": "{count, plural, one {Restaurant} other {Restaurants}}",
"Restaurant & Bar": "Restaurant & Bar",
"Restaurants & Bars": "Restaurants & Bars",
"Retype new password": "Neues Passwort erneut eingeben",

View File

@@ -72,6 +72,7 @@
"Check out at latest": "Check out at latest",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.",
"Check-in": "Check-in",
"Check-in/Check-out": "Check-in/Check-out",
"Check-out": "Check-out",
"Child age is required": "Child age is required",
"Children": "Children",
@@ -182,6 +183,7 @@
"Hotel surroundings": "Hotel surroundings",
"Hotel(s)": "{amount} {amount, plural, one {hotel} other {hotels}}",
"Hotels": "Hotels",
"Hours": "Hours",
"How do you want to sleep?": "How do you want to sleep?",
"How it works": "How it works",
"Hurry up and use them before they expire!": "Hurry up and use them before they expire!",
@@ -284,6 +286,7 @@
"Open menu": "Open menu",
"Open my pages menu": "Open my pages menu",
"Opening Hours": "Opening Hours",
"Outdoor": "Outdoor",
"OutdoorPool": "Outdoor pool",
"Overview": "Overview",
"PETR": "Pet",
@@ -344,7 +347,7 @@
"Request bedtype": "Request bedtype",
"Reservation number": "Reservation number",
"Reservation policy": "Reservation policy",
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
"Restaurant": "{count, plural, one {Restaurant} other {Restaurants}}",
"Restaurant & Bar": "Restaurant & Bar",
"Restaurants & Bars": "Restaurants & Bars",
"Retype new password": "Retype new password",

View File

@@ -64,6 +64,7 @@
"Check out": "Uloskirjautuminen",
"Check out at latest": "Uloskirjautuminen viimeistään",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tarkista profiiliisi tallennetut luottokortit. Maksa tallennetulla kortilla kirjautuneena, jotta verkkokokemus on sujuvampi.",
"Check-in/Check-out": "Sisäänkirjautuminen/Uloskirjautuminen",
"Children": "Lasta",
"Choose room": "Valitse huone",
"Cities": "Kaupungit",
@@ -164,6 +165,7 @@
"Hotel surroundings": "Hotellin ympäristö",
"Hotel(s)": "{amount} {amount, plural, one {hotelli} other {hotellit}}",
"Hotels": "Hotellit",
"Hours": "Ajat",
"How do you want to sleep?": "Kuinka haluat nukkua?",
"How it works": "Kuinka se toimii",
"Hurry up and use them before they expire!": "Ole nopea ja käytä ne ennen kuin ne vanhenevat!",
@@ -257,6 +259,7 @@
"Open menu": "Avaa valikko",
"Open my pages menu": "Avaa omat sivut -valikko",
"Opening Hours": "Aukioloajat",
"Outdoor": "Ulkona",
"OutdoorPool": "Ulkouima-allas",
"Overview": "Yleiskatsaus",
"PETR": "Lemmikki",
@@ -305,7 +308,7 @@
"Relax": "Rentoutua",
"Remove card from member profile": "Poista kortti jäsenprofiilista",
"Request bedtype": "Pyydä sänkytyyppiä",
"Restaurant": "{count, plural, one {#Ravintola} other {#Restaurants}}",
"Restaurant": "{count, plural, one {Ravintola} other {Ravintolat}}",
"Restaurant & Bar": "Ravintola & Baari",
"Restaurants & Bars": "Restaurants & Bars",
"Retype new password": "Kirjoita uusi salasana uudelleen",

View File

@@ -64,6 +64,7 @@
"Check out": "Sjekk ut",
"Check out at latest": "Utsjekking senest",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sjekk ut kredittkortene som er lagret på profilen din. Betal med et lagret kort når du er pålogget for en jevnere nettopplevelse.",
"Check-in/Check-out": "Innsjekking/Utsjekking",
"Children": "Barn",
"Choose room": "Velg rom",
"Cities": "Byer",
@@ -163,6 +164,7 @@
"Hotel surroundings": "Hotellomgivelser",
"Hotel(s)": "{amount} {amount, plural, one {hotell} other {hoteller}}",
"Hotels": "Hoteller",
"Hours": "Tider",
"How do you want to sleep?": "Hvordan vil du sove?",
"How it works": "Hvordan det fungerer",
"Hurry up and use them before they expire!": "Skynd deg og bruk dem før de utløper!",
@@ -255,6 +257,7 @@
"Open menu": "Åpne menyen",
"Open my pages menu": "Åpne mine sider menyen",
"Opening Hours": "Åpningstider",
"Outdoor": "Utendørs",
"OutdoorPool": "Utendørs basseng",
"Overview": "Oversikt",
"PETR": "Kjæledyr",
@@ -303,7 +306,7 @@
"Relax": "Slappe av",
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
"Request bedtype": "Be om sengetype",
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
"Restaurant": "{count, plural, one {Restaurant} other {Restauranter}}",
"Restaurant & Bar": "Restaurant & Bar",
"Restaurants & Bars": "Restaurants & Bars",
"Retype new password": "Skriv inn nytt passord på nytt",

View File

@@ -64,6 +64,7 @@
"Check out": "Checka ut",
"Check out at latest": "Utcheckning senast",
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Kolla in kreditkorten som sparats i din profil. Betala med ett sparat kort när du är inloggad för en smidigare webbupplevelse.",
"Check-in/Check-out": "Inchecking/Utcheckning",
"Children": "Barn",
"Choose room": "Välj rum",
"Cities": "Städer",
@@ -163,6 +164,7 @@
"Hotel surroundings": "Hotellomgivning",
"Hotel(s)": "{amount} hotell",
"Hotels": "Hotell",
"Hours": "Tider",
"How do you want to sleep?": "Hur vill du sova?",
"How it works": "Hur det fungerar",
"Hurry up and use them before they expire!": "Skynda dig och använd dem innan de går ut!",
@@ -255,6 +257,7 @@
"Open menu": "Öppna menyn",
"Open my pages menu": "Öppna mina sidor menyn",
"Opening Hours": "Öppettider",
"Outdoor": "Utomhus",
"OutdoorPool": "Utomhuspool",
"Overview": "Översikt",
"PETR": "Husdjur",
@@ -303,7 +306,7 @@
"Relax": "Koppla av",
"Remove card from member profile": "Ta bort kortet från medlemsprofilen",
"Request bedtype": "Request bedtype",
"Restaurant": "{count, plural, one {#Restaurang} other {#Restauranger}}",
"Restaurant": "{count, plural, one {Restaurang} other {Restauranger}}",
"Restaurant & Bar": "Restaurang & Bar",
"Restaurants & Bars": "Restaurants & Bars",
"Retype new password": "Upprepa nytt lösenord",

View File

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

@@ -56,7 +56,7 @@ const contactInformationSchema = z.object({
websiteUrl: z.string(),
})
const checkinSchema = z.object({
export const checkinSchema = z.object({
checkInTime: z.string(),
checkOutTime: z.string(),
onlineCheckOutAvailableFrom: z.string().nullable().optional(),
@@ -110,6 +110,12 @@ const hotelContentSchema = z.object({
short: z.string(),
medium: z.string(),
}),
meetingDescription: z
.object({
short: z.string().optional(),
medium: z.string().optional(),
})
.optional(),
}),
restaurantsOverviewPage: z.object({
restaurantsOverviewPageLinkText: z.string().optional(),

View File

@@ -1,4 +1,4 @@
import { Hotel, ParkingData } from "@/types/hotel"
import { CheckInData, Hotel, ParkingData } from "@/types/hotel"
export enum AvailabilityEnum {
Available = "Available",
@@ -17,5 +17,21 @@ export interface ContactProps {
}
export interface ParkingProps {
parking: ParkingData
parking: ParkingData[]
}
export interface AccessibilityProps {
accessibilityElevatorPitchText: string
}
export interface RestaurantProps {
restaurantsContentDescriptionMedium: string
}
export interface CheckInCheckOutProps {
checkin: CheckInData
}
export interface MeetingsAndConferencesProps {
meetingDescription: string
}

View File

@@ -1,6 +1,7 @@
import { z } from "zod"
import {
checkinSchema,
facilitySchema,
getHotelDataSchema,
imageSchema,
@@ -23,6 +24,7 @@ export type HotelTripAdvisor =
export type RoomData = z.infer<typeof roomSchema>
export type GalleryImage = z.infer<typeof imageSchema>
export type CheckInData = z.infer<typeof checkinSchema>
export type PointOfInterest = z.output<typeof pointOfInterestSchema>