fix(SW-1241): Adjusted amenities sidepeek on hotel pages and booking flow

Approved-by: Michael Zetterberg
Approved-by: Matilda Landström
This commit is contained in:
Erik Tiekstra
2025-04-23 08:41:04 +00:00
parent c23a32cd10
commit 8152aea649
46 changed files with 654 additions and 731 deletions

View File

@@ -1,5 +0,0 @@
.wrapper {
display: flex;
flex-direction: column;
gap: var(--Spacing-x3);
}

View File

@@ -1,46 +0,0 @@
import { IconName } from "@/components/Icons/iconName"
import OpeningHours from "@/components/OpeningHours"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Body from "@/components/TempDesignSystem/Text/Body"
import { getIntl } from "@/i18n"
import type { BreakfastAmenityProps } from "@/types/components/hotelPage/sidepeek/amenities"
import { HotelTypeEnum } from "@/types/enums/hotelType"
export default async function BreakfastAmenity({
openingHours,
alternateOpeningHours,
hotelType,
}: BreakfastAmenityProps) {
const intl = await getIntl()
const accordionContent =
hotelType === HotelTypeEnum.ScandicGo ? (
<Body>
{intl.formatMessage({
defaultMessage: "All-day breakfast",
})}
</Body>
) : (
<OpeningHours
openingHours={openingHours!}
alternateOpeningHours={alternateOpeningHours!}
heading={intl.formatMessage({
defaultMessage: "Opening hours",
})}
/>
)
return (
<AccordionItem
title={intl.formatMessage({
defaultMessage: "Breakfast",
})}
iconName={IconName.CoffeeAlt}
variant="sidepeek"
trackingId="amenities:breakfast"
>
{accordionContent}
</AccordionItem>
)
}

View File

@@ -1,47 +0,0 @@
import { IconName } from "@/components/Icons/iconName"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Body from "@/components/TempDesignSystem/Text/Body"
import { getIntl } from "@/i18n"
import type { CheckInAmenityProps } from "@/types/components/hotelPage/sidepeek/checkIn"
export default async function CheckInAmenity({
checkInInformation,
}: CheckInAmenityProps) {
const intl = await getIntl()
const { checkInTime, checkOutTime } = checkInInformation
return (
<AccordionItem
title={`${intl.formatMessage({
defaultMessage: "Check-in",
})}/${intl.formatMessage({
defaultMessage: "Check-out",
})}`}
iconName={IconName.Business}
variant="sidepeek"
trackingId="amenities:check-in"
>
<Body textTransform="bold">
{intl.formatMessage({
defaultMessage: "Times",
})}
</Body>
<Body color="uiTextHighContrast">
{intl.formatMessage(
{
defaultMessage: "Check in from: {checkInTime}",
},
{ checkInTime }
)}
</Body>
<Body color="uiTextHighContrast">
{intl.formatMessage(
{
defaultMessage: "Check out at latest: {checkOutTime}",
},
{ checkOutTime }
)}
</Body>
</AccordionItem>
)
}

View File

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

View File

@@ -1,4 +0,0 @@
export { default as AccessibilityAmenity } from "./Accessibility"
export { default as BreakfastAmenity } from "./Breakfast"
export { default as CheckInAmenity } from "./CheckIn"
export { default as ParkingAmenity } from "./Parking"

View File

@@ -1,44 +0,0 @@
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { FacilityToIcon } from "../../../data"
import styles from "./filteredAmenities.module.css"
import type { FilteredAmenitiesProps } from "@/types/components/hotelPage/sidepeek/amenities"
export default function FilteredAmenities({
filteredAmenities,
}: FilteredAmenitiesProps) {
return (
<>
{filteredAmenities?.map((amenity) => {
const Icon = (
<FacilityToIcon
id={amenity.id}
color="Icon/Interactive/Default"
size={24}
/>
)
return (
<li key={amenity.name} className={styles.wrapper}>
<div className={styles.amenity}>
{Icon ? (
Icon
) : (
<MaterialIcon
icon="favorite"
color="Icon/Interactive/Default"
/>
)}
<Subtitle color="burgundy" type="two">
{amenity.name}
</Subtitle>
</div>
</li>
)
})}
</>
)
}

View File

@@ -1,19 +1,14 @@
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 Accordion from "@/components/TempDesignSystem/Accordion"
import SidePeek from "@/components/TempDesignSystem/SidePeek"
import { getIntl } from "@/i18n"
import {
AccessibilityAmenity,
BreakfastAmenity,
CheckInAmenity,
ParkingAmenity,
} from "./AccordionAmenities"
import FilteredAmenities from "./FilteredAmenities"
import { SidepeekSlugs } from "@/types/components/hotelPage/hotelPage"
import type { AmenitiesSidePeekProps } from "@/types/components/hotelPage/sidepeek/amenities"
import { FacilityEnum } from "@/types/enums/facilities"
import { HotelTypeEnum } from "@/types/enums/hotelType"
export default async function AmenitiesSidePeek({
amenitiesList,
@@ -25,35 +20,6 @@ export default async function AmenitiesSidePeek({
}: AmenitiesSidePeekProps) {
const intl = await getIntl()
const amenitiesToRemove = [
FacilityEnum.ParkingAdditionalCost,
FacilityEnum.ParkingElectricCharging,
FacilityEnum.ParkingFreeParking,
FacilityEnum.ParkingGarage,
FacilityEnum.ParkingOutdoor,
FacilityEnum.MeetingArea,
FacilityEnum.ServesBreakfastAlwaysIncluded,
FacilityEnum.LateCheckOutUntil1400Guaranteed,
]
const filteredAmenities = amenitiesList.filter(
(amenity) => !amenitiesToRemove.includes(amenity.id)
)
const breakfastOpeningHours = restaurants
?.map((restaurant) => {
const breakfastDetail = restaurant.openingDetails.find(
(details) =>
details.openingHours.name === "Breakfast" ||
details.openingHours.name ===
intl.formatMessage({
defaultMessage: "Breakfast",
})
)
return breakfastDetail
})
.filter(Boolean)[0]
return (
<SidePeek
contentKey={SidepeekSlugs.amenities}
@@ -62,27 +28,21 @@ export default async function AmenitiesSidePeek({
})}
>
<Accordion>
<ParkingAmenity
<ParkingAccordionItem
parking={parking.parking}
parkingElevatorPitch={parking.parkingElevatorPitch}
elevatorPitch={parking.parkingElevatorPitch}
parkingPageUrl={parking.parkingPageUrl}
/>
{(breakfastOpeningHours || hotelType === HotelTypeEnum.ScandicGo) && (
<BreakfastAmenity
openingHours={breakfastOpeningHours?.openingHours}
alternateOpeningHours={breakfastOpeningHours?.alternateOpeningHours}
hotelType={hotelType}
/>
)}
<CheckInAmenity checkInInformation={checkInInformation} />
{(accessibility.elevatorPitch ||
accessibility.accessibilityPageUrl) && (
<AccessibilityAmenity
accessibilityPageUrl={accessibility.accessibilityPageUrl}
elevatorPitch={accessibility.elevatorPitch}
/>
)}
<FilteredAmenities filteredAmenities={filteredAmenities} />
<BreakfastAccordionItem
restaurants={restaurants}
hotelType={hotelType}
/>
<CheckInCheckOutAccordionItem checkInData={checkInInformation} />
<AccessibilityAccordionItem
accessibilityPageUrl={accessibility.accessibilityPageUrl}
elevatorPitch={accessibility.elevatorPitch}
/>
<AdditionalAmenities amenities={amenitiesList} />
</Accordion>
</SidePeek>
)

View File

@@ -38,6 +38,7 @@ import styles from "./hotelCard.module.css"
import { HotelCardListingTypeEnum } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
import type { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps"
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
import { RateTypeEnum } from "@/types/enums/rateType"
import type { Lang } from "@/constants/languages"
@@ -173,6 +174,7 @@ function HotelCard({
hotelId={hotel.operaId}
hotel={hotel}
showCTA={true}
sidePeekKey={SidePeekEnum.hotelDetails}
/>
</section>
<div className={styles.prices}>

View File

@@ -1,30 +1,29 @@
"use client"
import { Button } from "@scandic-hotels/design-system/Button"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import useSidePeekStore from "@/stores/sidepeek"
import Button from "@/components/TempDesignSystem/Button"
import styles from "./readMore.module.css"
import type { ReadMoreProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
export default function ReadMore({ label, hotelId, showCTA }: ReadMoreProps) {
export default function ReadMore({
label,
hotelId,
showCTA,
sidePeekKey,
}: ReadMoreProps) {
const openSidePeek = useSidePeekStore((state) => state.openSidePeek)
return (
<Button
onPress={() => {
openSidePeek({ key: SidePeekEnum.hotelDetails, hotelId, showCTA })
openSidePeek({ key: sidePeekKey, hotelId, showCTA })
}}
intent="text"
theme="base"
wrapping
className={styles.detailsButton}
variant="Text"
typography="Body/Paragraph/mdBold"
>
{label}
<MaterialIcon icon="chevron_right" size={20} color="CurrentColor" />
<MaterialIcon icon="chevron_right" size={24} color="CurrentColor" />
</Button>
)
}

View File

@@ -1,25 +0,0 @@
.detailsButton {
align-self: start;
border-radius: 0;
height: auto;
padding-left: 0;
padding-right: 0;
}
.content {
display: grid;
gap: var(--Spacing-x2);
}
.amenity {
font-family: var(--typography-Body-Regular-fontFamily);
border-bottom: 1px solid var(--Base-Border-Subtle);
/* padding set to align with AccordionItem which has a different composition */
padding: var(--Spacing-x2)
calc(var(--Spacing-x1) + var(--Spacing-x-one-and-half));
}
.list {
font-family: var(--typography-Body-Regular-fontFamily);
list-style: inside;
}

View File

@@ -16,6 +16,7 @@ import TripAdvisorChip from "../../TripAdvisorChip"
import styles from "./hotelInfoCard.module.css"
import type { HotelInfoCardProps } from "@/types/components/hotelReservation/selectRate/hotelInfoCard"
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
export default async function HotelInfoCard({ hotel }: HotelInfoCardProps) {
const intl = await getIntl()
@@ -88,6 +89,7 @@ export default async function HotelInfoCard({ hotel }: HotelInfoCardProps) {
hotelId={hotel.operaId}
hotel={hotel}
showCTA={false}
sidePeekKey={SidePeekEnum.amenities}
/>
</div>
</div>

View File

@@ -3,6 +3,7 @@
import { trpc } from "@/lib/trpc/client"
import useSidePeekStore from "@/stores/sidepeek"
import AmenitiesSidePeek from "@/components/SidePeeks/AmenitiesSidePeek"
import BookedRoomSidePeek from "@/components/SidePeeks/BookedRoomSidePeek"
import HotelSidePeek from "@/components/SidePeeks/HotelSidePeek"
import RoomSidePeek from "@/components/SidePeeks/RoomSidePeek"
@@ -39,13 +40,23 @@ export default function HotelReservationSidePeek() {
return (
<>
{hotelData && (
<HotelSidePeek
additionalHotelData={hotelData.additionalData}
hotel={hotelData.hotel}
activeSidePeek={activeSidePeek}
close={close}
showCTA={showCTA}
/>
<>
<HotelSidePeek
additionalHotelData={hotelData.additionalData}
hotel={hotelData.hotel}
restaurants={hotelData.restaurants}
activeSidePeek={activeSidePeek}
close={close}
showCTA={showCTA}
/>
<AmenitiesSidePeek
hotel={hotelData.hotel}
restaurants={hotelData.restaurants}
additionalHotelData={hotelData.additionalData}
activeSidePeek={activeSidePeek}
close={close}
/>
</>
)}
{selectedRoom && (
<RoomSidePeek

View File

@@ -1,7 +1,10 @@
"use client"
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import Divider from "@/components/TempDesignSystem/Divider"
import { getIntl } from "@/i18n"
import { getGroupedOpeningHours } from "../utils"
@@ -13,10 +16,10 @@ interface AlternateOpeningHoursProps {
alternateOpeningHours: RestaurantOpeningHours
}
export default async function AlternateOpeningHours({
export default function AlternateOpeningHours({
alternateOpeningHours,
}: AlternateOpeningHoursProps) {
const intl = await getIntl()
const intl = useIntl()
const groupedAlternateOpeningHours = alternateOpeningHours
? getGroupedOpeningHours(alternateOpeningHours, intl)
: null

View File

@@ -1,6 +1,8 @@
import { Typography } from "@scandic-hotels/design-system/Typography"
"use client"
import { getIntl } from "@/i18n"
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import Divider from "../TempDesignSystem/Divider"
import AlternateOpeningHours from "./AlternateOpeningHours"
@@ -8,14 +10,20 @@ import { getGroupedOpeningHours } from "./utils"
import styles from "./openingHours.module.css"
import type { OpeningHoursProps } from "@/types/components/hotelPage/sidepeek/openingHours"
import type { RestaurantOpeningHours } from "@/types/hotel"
export default async function OpeningHours({
interface OpeningHoursProps {
openingHours: RestaurantOpeningHours
alternateOpeningHours?: RestaurantOpeningHours
heading?: string
}
export default function OpeningHours({
openingHours,
alternateOpeningHours,
heading,
}: OpeningHoursProps) {
const intl = await getIntl()
const intl = useIntl()
const groupedOpeningHours = getGroupedOpeningHours(openingHours, intl)
return (

View File

@@ -1,18 +1,21 @@
import Body from "@/components/TempDesignSystem/Text/Body"
import { getIntl } from "@/i18n"
"use client"
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import styles from "./parkingList.module.css"
import type { ParkingListProps } from "@/types/components/hotelPage/sidepeek/parking"
export default async function ParkingList({
export default function ParkingList({
numberOfChargingSpaces,
canMakeReservation,
numberOfParkingSpots,
distanceToHotel,
address,
}: ParkingListProps) {
const intl = await getIntl()
const intl = useIntl()
const canMakeReservationYesMsg = intl.formatMessage({
defaultMessage: "Parking can be reserved in advance: Yes",
@@ -22,7 +25,7 @@ export default async function ParkingList({
})
return (
<Body color="uiTextHighContrast" asChild>
<Typography variant="Body/Paragraph/mdRegular">
<ul className={styles.listStyling}>
{numberOfChargingSpaces ? (
<li>
@@ -71,6 +74,6 @@ export default async function ParkingList({
</li>
) : null}
</ul>
</Body>
</Typography>
)
}

View File

@@ -1,5 +1,9 @@
import Body from "@/components/TempDesignSystem/Text/Body"
import { getIntl } from "@/i18n"
"use client"
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { formatPrice } from "@/utils/numberFormatting"
import { getPeriod } from "./utils"
@@ -11,28 +15,28 @@ import {
Periods,
} from "@/types/components/hotelPage/sidepeek/parking"
export default async function ParkingPrices({
export default function ParkingPrices({
currency = "",
freeParking,
pricing,
}: ParkingPricesProps) {
const intl = await getIntl()
const intl = useIntl()
return freeParking ? (
<Body textTransform="bold" color="uiTextHighContrast">
{intl.formatMessage({
defaultMessage: "Free parking",
})}
</Body>
<Typography variant="Body/Paragraph/mdBold">
<p className={styles.wrapper}>
{intl.formatMessage({ defaultMessage: "Free parking" })}
</p>
</Typography>
) : (
<dl className={styles.wrapper}>
{pricing?.map((parking) => (
<div key={parking.period} className={styles.period}>
<div className={styles.information}>
<Body textTransform="bold" color="uiTextHighContrast" asChild>
<Typography variant="Body/Paragraph/mdBold">
<dt>{getPeriod(intl, parking.period)}</dt>
</Body>
<Body color="uiTextHighContrast" asChild>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<dd>
{parking.amount
? formatPrice(intl, parking.amount, currency)
@@ -40,24 +44,17 @@ export default async function ParkingPrices({
defaultMessage: "At a cost",
})}
</dd>
</Body>
</Typography>
</div>
{parking.startTime &&
parking.endTime &&
parking.period !== Periods.allDay && (
<div className={styles.information}>
<Body color="uiTextHighContrast" asChild>
<dt>
{intl.formatMessage({
defaultMessage: "From",
})}
</dt>
</Body>
<Body color="uiTextHighContrast" asChild>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<Typography variant="Body/Paragraph/mdRegular">
<div className={styles.information}>
<dt>{intl.formatMessage({ defaultMessage: "From" })}</dt>
<dd>{`${parking.startTime}-${parking.endTime}`}</dd>
</Body>
</div>
</div>
</Typography>
)}
</div>
))}

View File

@@ -2,6 +2,7 @@
display: grid;
row-gap: var(--Spacing-x1);
margin: 0;
color: var(--Text-Default);
}
.period {
@@ -13,3 +14,7 @@
margin: 0;
flex: 1;
}
.priceHeading {
color: var(--Text-Secondary);
}

View File

@@ -1,13 +1,13 @@
import Link from "next/link"
"use client"
import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { getIntl } from "@/i18n"
import ButtonLink from "@/components/ButtonLink"
import Divider from "@/components/TempDesignSystem/Divider"
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"
@@ -20,17 +20,19 @@ interface ParkingInformationProps {
showExternalParkingButton?: boolean
}
export default async function ParkingInformation({
export default function ParkingInformation({
parking,
showExternalParkingButton = true,
}: ParkingInformationProps) {
const intl = await getIntl()
const intl = useIntl()
const title = `${parking.type}${parking.name ? ` (${parking.name})` : ""}`
return (
<div className={styles.parkingInformation}>
<div className={styles.list}>
<Subtitle type="two" asChild>
<h4>{parking.type}</h4>
</Subtitle>
<Typography variant="Title/Subtitle/md">
<h4 className={styles.heading}>{title}</h4>
</Typography>
<ParkingList
numberOfChargingSpaces={parking.numberOfChargingSpaces}
canMakeReservation={parking.canMakeReservation}
@@ -40,19 +42,17 @@ export default async function ParkingInformation({
/>
</div>
<div className={styles.prices}>
<Subtitle type="two" asChild>
<h5>
{intl.formatMessage({
defaultMessage: "Prices",
})}
<Typography variant="Body/Paragraph/mdBold">
<h5 className={styles.heading}>
{intl.formatMessage({ defaultMessage: "Prices" })}
</h5>
</Subtitle>
</Typography>
<div className={styles.priceWrapper}>
<Caption color="uiTextMediumContrast" textTransform="uppercase">
{intl.formatMessage({
defaultMessage: "Weekday prices",
})}
</Caption>
<Typography variant="Title/Overline/sm">
<h6 className={styles.priceHeading}>
{intl.formatMessage({ defaultMessage: "Weekday prices" })}
</h6>
</Typography>
<Divider color="baseSurfaceSubtleHover" />
{parking.pricing.localCurrency ? (
<ParkingPrices
@@ -63,11 +63,11 @@ export default async function ParkingInformation({
) : null}
</div>
<div className={styles.priceWrapper}>
<Caption color="uiTextMediumContrast" textTransform="uppercase">
{intl.formatMessage({
defaultMessage: "Weekend prices",
})}
</Caption>
<Typography variant="Title/Overline/sm">
<h6 className={styles.priceHeading}>
{intl.formatMessage({ defaultMessage: "Weekend prices" })}
</h6>
</Typography>
<Divider color="baseSurfaceSubtleHover" />
{parking.pricing.localCurrency ? (
<ParkingPrices
@@ -79,14 +79,10 @@ export default async function ParkingInformation({
</div>
</div>
{parking.externalParkingUrl && showExternalParkingButton && (
<Button theme="base" intent="primary" variant="icon" asChild>
<Link href={parking.externalParkingUrl} target="_blank">
{intl.formatMessage({
defaultMessage: "Book parking",
})}
<MaterialIcon icon="open_in_new" color="CurrentColor" />
</Link>
</Button>
<ButtonLink href={parking.externalParkingUrl} target="_blank">
{intl.formatMessage({ defaultMessage: "Book parking" })}
<MaterialIcon icon="open_in_new" color="CurrentColor" />
</ButtonLink>
)}
</div>
)

View File

@@ -16,3 +16,11 @@
display: grid;
gap: var(--Spacing-x1);
}
.heading {
color: var(--Text-Default);
}
.priceHeading {
color: var(--Text-Secondary);
}

View File

@@ -0,0 +1,49 @@
"use client"
import { useIntl } from "react-intl"
import AdditionalAmenities from "@/components/SidePeeks/AmenitiesSidepeekContent/AdditionalAmenities"
import Accordion from "@/components/TempDesignSystem/Accordion"
import SidePeek from "@/components/TempDesignSystem/SidePeek"
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 type { AmenitiesSidePeekProps } from "@/types/components/hotelReservation/amenitiesSidePeek"
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
export default function AmenitiesSidePeek({
hotel,
restaurants,
additionalHotelData,
activeSidePeek,
close,
}: AmenitiesSidePeekProps) {
const intl = useIntl()
return (
<SidePeek
title={intl.formatMessage({ defaultMessage: "Amenities" })}
isOpen={activeSidePeek === SidePeekEnum.amenities}
handleClose={close}
>
<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>
</SidePeek>
)
}

View File

@@ -1,20 +1,26 @@
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
"use client"
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import ButtonLink from "@/components/ButtonLink"
import { IconName } from "@/components/Icons/iconName"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Body from "@/components/TempDesignSystem/Text/Body"
import { getIntl } from "@/i18n"
import styles from "./accessibilityAmenity.module.css"
import styles from "./sidePeekAccordion.module.css"
import type { AccessibilityAmenityProps } from "@/types/components/hotelPage/sidepeek/accessibility"
import type { AccessibilityAccordionItemProps } from "@/types/components/sidePeeks/amenities"
export default async function AccessibilityAmenity({
export default function AccessibilityAccordionItem({
elevatorPitch,
accessibilityPageUrl,
}: AccessibilityAmenityProps) {
const intl = await getIntl()
}: AccessibilityAccordionItemProps) {
const intl = useIntl()
if (!elevatorPitch && !accessibilityPageUrl) {
return null
}
return (
<AccordionItem
@@ -22,13 +28,14 @@ export default async function AccessibilityAmenity({
defaultMessage: "Accessibility",
})}
iconName={IconName.Accessibility}
className={styles.accordionItem}
variant="sidepeek"
trackingId="amenities:accessibility"
>
<div className={styles.wrapper}>
{elevatorPitch && (
<Body color="uiTextHighContrast">{elevatorPitch}</Body>
)}
<div className={styles.accessibilityContent}>
<Typography variant="Body/Paragraph/mdRegular">
<p>{elevatorPitch}</p>
</Typography>
{accessibilityPageUrl && (
<ButtonLink
href={`/${accessibilityPageUrl}`}
@@ -38,11 +45,7 @@ export default async function AccessibilityAmenity({
typography="Body/Paragraph/mdBold"
appendToCurrentPath
>
{intl.formatMessage({
defaultMessage: "About accessibility",
})}
<MaterialIcon icon="arrow_forward" color="CurrentColor" />
{intl.formatMessage({ defaultMessage: "About accessibility" })}
</ButtonLink>
)}
</div>

View File

@@ -0,0 +1,61 @@
"use client"
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { isDefined } from "@/server/utils"
import { IconName } from "@/components/Icons/iconName"
import OpeningHours from "@/components/OpeningHours"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import styles from "./sidePeekAccordion.module.css"
import type { BreakfastAccordionItemProps } from "@/types/components/sidePeeks/amenities"
import { HotelTypeEnum } from "@/types/enums/hotelType"
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}
trackingId="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>
)
}

View File

@@ -0,0 +1,60 @@
"use client"
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { IconName } from "@/components/Icons/iconName"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Divider from "@/components/TempDesignSystem/Divider"
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}
trackingId="amenities:check-in"
>
<div className={styles.checkInCheckOutContent}>
<Typography variant="Title/Overline/sm">
<h4 className={styles.subheading}>
{intl.formatMessage({ defaultMessage: "Hours" })}
</h4>
</Typography>
<Divider color="Border/Divider/Default" />
<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>
)
}

View File

@@ -1,19 +1,24 @@
"use client"
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import ButtonLink from "@/components/ButtonLink"
import { IconName } from "@/components/Icons/iconName"
import ParkingInformation from "@/components/ParkingInformation"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import { getIntl } from "@/i18n"
import styles from "./parkingAmenity.module.css"
import styles from "./sidePeekAccordion.module.css"
import type { ParkingAmenityProps } from "@/types/components/hotelPage/sidepeek/parking"
import type { ParkingAccordionItemProps } from "@/types/components/sidePeeks/amenities"
export default async function ParkingAmenity({
export default function ParkingAccordionItem({
parking,
parkingElevatorPitch,
elevatorPitch,
parkingPageUrl,
}: ParkingAmenityProps) {
const intl = await getIntl()
}: ParkingAccordionItemProps) {
const intl = useIntl()
return (
<AccordionItem
@@ -22,10 +27,15 @@ export default async function ParkingAmenity({
})}
iconName={IconName.Parking}
variant="sidepeek"
className={styles.accordionItem}
trackingId="amenities:parking"
>
<div className={styles.wrapper}>
{parkingElevatorPitch}
<div className={styles.parkingContent}>
{elevatorPitch ? (
<Typography variant="Body/Paragraph/mdRegular">
<p>{elevatorPitch}</p>
</Typography>
) : null}
{parking.map((data) => (
<ParkingInformation key={data.type} parking={data} />
))}

View File

@@ -0,0 +1,26 @@
.accordionItem {
color: var(--Text-Default);
}
.parkingContent,
.accessibilityContent {
display: grid;
gap: var(--Space-x3);
}
.checkInCheckOutContent {
display: grid;
gap: var(--Space-x15);
}
.checkInCheckOutContent {
display: grid;
padding: var(--Space-x2) var(--Space-x3);
gap: var(--Space-x1);
border-radius: var(--Corner-radius-Medium);
background: var(--Surface-Secondary-Default);
}
.subheading {
color: var(--Text-Secondary);
}

View File

@@ -1,6 +1,7 @@
.wrapper {
padding: var(--Spacing-x1) var(--Spacing-x0);
border-bottom: 1px solid var(--Base-Border-Subtle);
color: var(--Text-Interactive-Default);
}
.amenity {

View File

@@ -0,0 +1,42 @@
import { Typography } from "@scandic-hotels/design-system/Typography"
import { FacilityToIcon } from "../../../ContentType/HotelPage/data"
import styles from "./additionalAmenities.module.css"
import type { AdditionalAmenitiesProps } from "@/types/components/sidePeeks/amenities"
import { FacilityEnum } from "@/types/enums/facilities"
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}
/>
{amenity.name}
</div>
</li>
</Typography>
))
}

View File

@@ -1,24 +0,0 @@
import { useIntl } from "react-intl"
import { IconName } from "@/components/Icons/iconName"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Body from "@/components/TempDesignSystem/Text/Body"
import type { AccessibilityProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
export default function Accessibility({
elevatorPitchText,
}: AccessibilityProps) {
const intl = useIntl()
return (
<AccordionItem
title={intl.formatMessage({
defaultMessage: "Accessibility",
})}
iconName={IconName.Accessibility}
variant="sidepeek"
>
<Body>{elevatorPitchText}</Body>
</AccordionItem>
)
}

View File

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

View File

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

View File

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

View File

@@ -1,29 +0,0 @@
import { useIntl } from "react-intl"
import { IconName } from "@/components/Icons/iconName"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Body from "@/components/TempDesignSystem/Text/Body"
import type { RestaurantProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
export default function Restaurant({
restaurantsContentDescriptionMedium,
}: RestaurantProps) {
const intl = useIntl()
return (
<AccordionItem
title={intl.formatMessage(
{
defaultMessage:
"{totalRestaurants, plural, one {Restaurant} other {Restaurants}}",
},
{ totalRestaurants: 1 }
)}
iconName={IconName.Restaurant}
variant="sidepeek"
>
<Body>{restaurantsContentDescriptionMedium}</Body>
</AccordionItem>
)
}

View File

@@ -1,24 +0,0 @@
.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);
justify-items: flex-start;
}
.list li svg {
flex-shrink: 0;
}
.parking details > div {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
}

View File

@@ -1,35 +1,5 @@
.spacing {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
}
.content {
display: grid;
gap: var(--Spacing-x2);
}
.content:last-child {
gap: 0;
}
.content > p {
margin-bottom: var(--Spacing-x-one-and-half);
}
.content > ul > li:first-child {
border-top: 1px solid var(--Base-Border-Subtle);
}
.amenity > p {
border-top: 1px solid var(--Base-Border-Subtle);
padding: calc(var(--Spacing-x-one-and-half) + var(--Spacing-x1))
var(--Spacing-x1);
display: flex;
align-items: center;
gap: var(--Spacing-x1);
}
.noIcon {
margin-left: var(--Spacing-x4);
color: var(--Text-Default);
}

View File

@@ -1,16 +1,18 @@
"use client"
import { useIntl } from "react-intl"
import { FacilityToIcon } from "@/components/ContentType/HotelPage/data"
import { Typography } from "@scandic-hotels/design-system/Typography"
import Contact from "@/components/HotelReservation/Contact"
import AdditionalAmenities from "@/components/SidePeeks/AmenitiesSidepeekContent/AdditionalAmenities"
import Accordion from "@/components/TempDesignSystem/Accordion"
import SidePeek from "@/components/TempDesignSystem/SidePeek"
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 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"
@@ -19,17 +21,12 @@ import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
export default function HotelSidePeek({
hotel,
restaurants,
additionalHotelData,
activeSidePeek,
close,
}: HotelSidePeekProps) {
const intl = useIntl()
const amenitiesList = hotel.detailedFacilities.filter(
(facility) => facility.public
)
const parking = hotel.parking.filter(
(p) => p?.numberOfParkingSpots || p?.numberOfChargingSpaces || p?.address
)
return (
<SidePeek
@@ -38,61 +35,30 @@ export default function HotelSidePeek({
handleClose={close}
>
<div className={styles.content}>
<Subtitle color="baseTextHighContrast">
{intl.formatMessage({
defaultMessage: "Practical information",
})}
</Subtitle>
<Typography variant="Title/Subtitle/lg">
<h3>
{intl.formatMessage({ defaultMessage: "Practical information" })}
</h3>
</Typography>
<Contact hotel={hotel} />
<Accordion>
{parking?.length > 0 && <Parking parking={parking} />}
{additionalHotelData?.restaurantsOverviewPage
?.restaurantsContentDescriptionMedium && (
<Restaurant
restaurantsContentDescriptionMedium={
additionalHotelData.restaurantsOverviewPage
.restaurantsContentDescriptionMedium
}
/>
)}
{additionalHotelData?.hotelSpecialNeeds.elevatorPitch && (
<Accessibility
elevatorPitchText={
additionalHotelData.hotelSpecialNeeds.elevatorPitch
}
/>
)}
{hotel.hotelFacts?.checkin && (
<CheckinCheckOut checkin={hotel.hotelFacts.checkin} />
)}
{hotel.hotelContent.texts?.meetingDescription?.medium && (
<MeetingsAndConferences
meetingDescription={
hotel.hotelContent.texts.meetingDescription.medium
}
/>
)}
</Accordion>
<div className={styles.amenity}>
{amenitiesList.map((amenity) => {
const Icon = (
<FacilityToIcon id={amenity.id} size={24} color="Icon/Intense" />
)
return (
<Subtitle type="two" key={amenity.id} color="uiTextHighContrast">
{Icon && Icon}
{amenity.name}
</Subtitle>
)
})}
</div>
{/* TODO: handle linking to Hotel Page */}
{/* {showCTA && (
<Button theme="base" intent="secondary" size="large">
Read more about the hotel
</Button>
)} */}
<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>
</div>
</SidePeek>
)

View File

@@ -32,7 +32,11 @@ export default function AccordionItem({
const IconComp = icon
? icon
: iconName && (
<IconByIconName iconName={iconName} color="Icon/Interactive/Default" />
<IconByIconName
iconName={iconName}
color="Icon/Interactive/Default"
size={24}
/>
)
function toggleAccordion() {

View File

@@ -1,4 +1,10 @@
{
"+2LakC": [
{
"type": 0,
"value": "Invalid membership number format"
}
],
"+CC2q2": [
{
"type": 0,
@@ -547,8 +553,7 @@
"value": "Please enter the code sent to "
},
{
"children": [
],
"children": [],
"type": 8,
"value": "maskedContactInfo"
},
@@ -901,6 +906,12 @@
"value": "Log in/Join"
}
],
"5cIDjn": [
{
"type": 0,
"value": "Booking code is invalid"
}
],
"5kfntb": [
{
"type": 0,
@@ -1009,6 +1020,12 @@
"value": "Continue with new price"
}
],
"6TXxwM": [
{
"type": 0,
"value": "Last name can't contain any special characters"
}
],
"6U6gjS": [
{
"type": 0,
@@ -1779,8 +1796,7 @@
"value": "Please enter the code sent to "
},
{
"children": [
],
"children": [],
"type": 8,
"value": "maskedContactInfo"
},
@@ -2170,6 +2186,12 @@
"value": "Find"
}
],
"FPcbu0": [
{
"type": 0,
"value": "Please enter a valid phone number"
}
],
"FZI5xl": [
{
"type": 0,
@@ -2420,6 +2442,12 @@
"value": "About accessibility"
}
],
"HRrOrP": [
{
"type": 0,
"value": "Bed choice is required"
}
],
"HW+bLl": [
{
"type": 0,
@@ -2849,6 +2877,12 @@
"value": "points"
}
],
"KaH0vJ": [
{
"type": 0,
"value": "You cannot have more children in adults bed than adults in the room"
}
],
"KgkOn/": [
{
"type": 0,
@@ -3497,6 +3531,12 @@
"value": "See details"
}
],
"RS5Kmw": [
{
"type": 0,
"value": "New password is required"
}
],
"RT3W/7": [
{
"type": 0,
@@ -3641,6 +3681,12 @@
"value": "Go back to My Pages"
}
],
"Seanpx": [
{
"type": 0,
"value": "Required"
}
],
"SjHZ7g": [
{
"type": 0,
@@ -4005,6 +4051,12 @@
"value": "You have cancelled to process to guarantee your booking."
}
],
"Vp6BHv": [
{
"type": 0,
"value": "Must be at least 18 years of age to continue"
}
],
"W1IuVy": [
{
"type": 0,
@@ -4335,8 +4387,7 @@
"value": "Please enter the code sent to "
},
{
"children": [
],
"children": [],
"type": 8,
"value": "maskedContactInfo"
},
@@ -4387,6 +4438,12 @@
"value": "Select a rate"
}
],
"YqklRf": [
{
"type": 0,
"value": "Destination required"
}
],
"Z+d8/1": [
{
"type": 0,
@@ -4697,6 +4754,12 @@
"value": "Where to?"
}
],
"boU1pG": [
{
"type": 0,
"value": "Code and voucher is not available with reward night."
}
],
"bsUwPW": [
{
"type": 0,
@@ -5247,6 +5310,12 @@
"value": " (incl VAT)"
}
],
"gq4AYf": [
{
"type": 0,
"value": "Only digits are allowed"
}
],
"gttxKw": [
{
"type": 0,
@@ -5349,6 +5418,12 @@
"value": "Your transaction"
}
],
"iZMEZ+": [
{
"type": 0,
"value": "Age is required"
}
],
"iwtnXO": [
{
"type": 0,
@@ -5439,6 +5514,12 @@
"value": " cm"
}
],
"jlqOPq": [
{
"type": 0,
"value": "Country is required"
}
],
"jvo0vs": [
{
"type": 0,
@@ -5557,6 +5638,12 @@
"value": "destination"
}
],
"ktyBmE": [
{
"type": 0,
"value": "Current password is required"
}
],
"kvOMtB": [
{
"type": 0,
@@ -6700,6 +6787,12 @@
"value": "Paid"
}
],
"u8II6t": [
{
"type": 0,
"value": "First name can't contain any special characters"
}
],
"uDm4Mt": [
{
"type": 1,
@@ -6740,6 +6833,12 @@
"value": "By adding a card you also guarantee your room booking for late arrival."
}
],
"uiFoFm": [
{
"type": 0,
"value": "Password is required"
}
],
"ujX0MU": [
{
"type": 0,
@@ -6920,6 +7019,12 @@
"value": "View your details"
}
],
"vzqS9m": [
{
"type": 0,
"value": "Phone is required"
}
],
"w0LSDs": [
{
"type": 1,
@@ -6964,6 +7069,12 @@
"value": "Sign up bonus"
}
],
"wRUsWd": [
{
"type": 0,
"value": "Retype new password does not match new password"
}
],
"wSh+Ly": [
{
"type": 1,
@@ -7195,6 +7306,12 @@
"value": "value"
}
],
"yNJhk3": [
{
"type": 0,
"value": "Retype new password is required"
}
],
"yP9SR1": [
{
"type": 0,
@@ -7243,12 +7360,24 @@
"value": "Last name is required"
}
],
"yr+Os9": [
{
"type": 0,
"value": "Date of birth is required"
}
],
"yxguVq": [
{
"type": 0,
"value": "Discard changes"
}
],
"z7+EXa": [
{
"type": 0,
"value": "Zip code is required"
}
],
"zA7LuX": [
{
"type": 0,
@@ -7359,4 +7488,4 @@
"value": "Map"
}
]
}
}

View File

@@ -1,4 +0,0 @@
export type AccessibilityAmenityProps = {
elevatorPitch?: string
accessibilityPageUrl?: string
}

View File

@@ -1,22 +1,19 @@
import type { Hotel, Restaurant, RestaurantOpeningHours } from "@/types/hotel"
import type { AccessibilityAmenityProps } from "./accessibility"
import type {
CheckInData,
DetailedFacility,
Hotel,
Restaurant,
} from "@/types/hotel"
import type { ParkingAmenityProps } from "./parking"
export type AmenitiesSidePeekProps = {
amenitiesList: Hotel["detailedFacilities"]
amenitiesList: DetailedFacility[]
parking: ParkingAmenityProps
checkInInformation: Hotel["hotelFacts"]["checkin"]
accessibility: AccessibilityAmenityProps
checkInInformation: CheckInData
accessibility: {
elevatorPitch?: string
accessibilityPageUrl?: string
}
restaurants: Restaurant[]
hotelType: Hotel["hotelType"]
}
export type FilteredAmenitiesProps = {
filteredAmenities: Hotel["detailedFacilities"]
}
export interface BreakfastAmenityProps {
openingHours?: RestaurantOpeningHours
alternateOpeningHours?: RestaurantOpeningHours
hotelType: Hotel["hotelType"]
}

View File

@@ -1,5 +0,0 @@
import type { Hotel } from "@/types/hotel"
export type CheckInAmenityProps = {
checkInInformation: Hotel["hotelFacts"]["checkin"]
}

View File

@@ -1,7 +0,0 @@
import type { RestaurantOpeningHours } from "@/types/hotel"
export interface OpeningHoursProps {
openingHours: RestaurantOpeningHours
alternateOpeningHours?: RestaurantOpeningHours
heading?: string
}

View File

@@ -0,0 +1,10 @@
import type { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
import type { AdditionalData, Hotel, Restaurant } from "@/types/hotel"
export type AmenitiesSidePeekProps = {
hotel: Hotel
restaurants: Restaurant[]
additionalHotelData: AdditionalData | undefined
activeSidePeek: SidePeekEnum
close: () => void
}

View File

@@ -1,8 +1,9 @@
import type { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
import type { AdditionalData, Hotel } from "@/types/hotel"
import type { AdditionalData, Hotel, Restaurant } from "@/types/hotel"
export type HotelSidePeekProps = {
hotel: Hotel
restaurants: Restaurant[]
additionalHotelData: AdditionalData | undefined
activeSidePeek: SidePeekEnum
close: () => void

View File

@@ -1,5 +1,6 @@
import type { CheckInData, Hotel, Parking } from "@/types/hotel"
import type { Hotel } from "@/types/hotel"
import type { Lang } from "@/constants/languages"
import type { SidePeekEnum } from "../sidePeek"
import type {
AlternativeHotelsSearchParams,
SelectHotelSearchParams,
@@ -15,32 +16,13 @@ export interface ReadMoreProps {
hotelId: string
hotel: Hotel
showCTA: boolean
sidePeekKey: SidePeekEnum
}
export interface ContactProps {
hotel: Hotel
}
export interface ParkingProps {
parking: Parking[]
}
export interface AccessibilityProps {
elevatorPitchText: string
}
export interface RestaurantProps {
restaurantsContentDescriptionMedium: string
}
export interface CheckInCheckOutProps {
checkin: CheckInData
}
export interface MeetingsAndConferencesProps {
meetingDescription: string
}
export interface SelectHotelProps {
params: {
lang: Lang

View File

@@ -1,5 +1,6 @@
export enum SidePeekEnum {
hotelDetails = "hotel-detail-side-peek",
amenities = "amenities-side-peek",
roomDetails = "room-detail-side-peek",
bookedRoomDetails = "booked-room-detail-side-peek",
}

View File

@@ -0,0 +1,30 @@
import type {
CheckInData,
DetailedFacility,
Parking,
Restaurant,
} from "@/types/hotel"
export interface ParkingAccordionItemProps {
parkingPageUrl?: string
parking: Parking[]
elevatorPitch?: string
}
export interface BreakfastAccordionItemProps {
restaurants?: Restaurant[]
hotelType: string
}
export interface CheckInCheckOutAccordionItemProps {
checkInData: CheckInData
}
export interface AccessibilityAccordionItemProps {
elevatorPitch?: string
accessibilityPageUrl?: string
}
export interface AdditionalAmenitiesProps {
amenities: DetailedFacility[]
}