Merged in feat/SW-1090-amenities-meetings-sidepeeks (pull request #1114)
Feat/SW-1090: Sidepeek amenities and meetings Approved-by: Erik Tiekstra Approved-by: Fredrik Thorsson
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import OpeningHours from "../../../OpeningHours"
|
||||
|
||||
import type { BreakfastAmenityProps } from "@/types/components/hotelPage/sidepeek/amenities"
|
||||
import { IconName } from "@/types/components/icon"
|
||||
|
||||
export default async function BreakfastAmenity() {
|
||||
export default async function BreakfastAmenity({
|
||||
openingHours,
|
||||
alternateOpeningHours,
|
||||
}: BreakfastAmenityProps) {
|
||||
const intl = await getIntl()
|
||||
return (
|
||||
<AccordionItem
|
||||
@@ -11,7 +17,12 @@ export default async function BreakfastAmenity() {
|
||||
icon={IconName.CoffeeAlt}
|
||||
variant="sidepeek"
|
||||
>
|
||||
{/* TODO: breakfast to be implemented */}
|
||||
<OpeningHours
|
||||
openingHours={openingHours}
|
||||
alternateOpeningHours={alternateOpeningHours}
|
||||
heading={intl.formatMessage({ id: "Opening hours" })}
|
||||
type="amenities"
|
||||
/>
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export default async function AmenitiesSidePeek({
|
||||
parking,
|
||||
checkInInformation,
|
||||
accessibility,
|
||||
restaurants,
|
||||
}: AmenitiesSidePeekProps) {
|
||||
const lang = getLang()
|
||||
const intl = await getIntl()
|
||||
@@ -40,6 +41,17 @@ export default async function AmenitiesSidePeek({
|
||||
(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({ id: "Breakfast" })
|
||||
)
|
||||
return breakfastDetail
|
||||
})
|
||||
.filter(Boolean)[0]
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
contentKey={amenities[lang]}
|
||||
@@ -47,7 +59,13 @@ export default async function AmenitiesSidePeek({
|
||||
>
|
||||
<Accordion>
|
||||
{parking.length ? <ParkingAmenity parking={parking} /> : null}
|
||||
<BreakfastAmenity />
|
||||
|
||||
{breakfastOpeningHours && (
|
||||
<BreakfastAmenity
|
||||
openingHours={breakfastOpeningHours.openingHours}
|
||||
alternateOpeningHours={breakfastOpeningHours.alternateOpeningHours}
|
||||
/>
|
||||
)}
|
||||
<CheckInAmenity checkInInformation={checkInInformation} />
|
||||
{accessibility && (
|
||||
<AccessibilityAmenity accessibility={accessibility} />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { meetingsAndConferences } from "@/constants/routes/hotelPageParams"
|
||||
import { getMeetingRooms } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Image from "@/components/Image"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
@@ -10,6 +11,8 @@ import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import { getConferenceRoomTexts } from "./util"
|
||||
|
||||
import styles from "./meetingsAndConferences.module.css"
|
||||
|
||||
import type { MeetingsAndConferencesSidePeekProps } from "@/types/components/hotelPage/sidepeek/meetingsAndConferences"
|
||||
@@ -17,10 +20,17 @@ import type { MeetingsAndConferencesSidePeekProps } from "@/types/components/hot
|
||||
export default async function MeetingsAndConferencesSidePeek({
|
||||
meetingFacilities,
|
||||
descriptions,
|
||||
hotelId,
|
||||
link,
|
||||
}: MeetingsAndConferencesSidePeekProps) {
|
||||
const lang = getLang()
|
||||
const intl = await getIntl()
|
||||
const [intl, meetingRooms] = await Promise.all([
|
||||
getIntl(),
|
||||
getMeetingRooms({ hotelId, language: lang }),
|
||||
])
|
||||
|
||||
const { seatingText, roomText } = await getConferenceRoomTexts(meetingRooms)
|
||||
|
||||
const fallbackAlt = intl.formatMessage({ id: "Creative spaces for meetings" })
|
||||
|
||||
const primaryImage = meetingFacilities?.heroImages[0]?.imageSizes.medium
|
||||
@@ -65,6 +75,14 @@ export default async function MeetingsAndConferencesSidePeek({
|
||||
{descriptions?.medium && (
|
||||
<Body color="uiTextHighContrast">{descriptions.medium}</Body>
|
||||
)}
|
||||
{roomText || seatingText ? (
|
||||
<Body color="uiTextMediumContrast">
|
||||
{roomText}
|
||||
{roomText && seatingText && <br />}
|
||||
{seatingText}
|
||||
</Body>
|
||||
) : null}
|
||||
|
||||
{link && (
|
||||
<div className={styles.buttonContainer}>
|
||||
<Button fullWidth theme="base" intent="secondary" asChild>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import type { MeetingRooms } from "@/types/components/hotelPage/meetingRooms"
|
||||
|
||||
export async function getConferenceRoomTexts(
|
||||
meetingRooms: MeetingRooms | null
|
||||
) {
|
||||
if (!meetingRooms) {
|
||||
return { seatingText: null, roomText: null }
|
||||
}
|
||||
const roomSizes = meetingRooms.map((room) => room.attributes.size)
|
||||
const roomSeating = meetingRooms
|
||||
.map((room) => {
|
||||
return room.attributes.seatings.map((seating) => seating.capacity)
|
||||
})
|
||||
.flat()
|
||||
|
||||
const seatingText = await getSeatingText(roomSeating)
|
||||
const roomText = await getRoomText(roomSizes)
|
||||
|
||||
return { seatingText, roomText }
|
||||
}
|
||||
|
||||
export async function getRoomText(roomSizes: number[]) {
|
||||
const largestRoom = Math.max(...roomSizes)
|
||||
const smallestRoom = Math.min(...roomSizes)
|
||||
const intl = await getIntl()
|
||||
|
||||
let roomText
|
||||
if (largestRoom === smallestRoom) {
|
||||
roomText = intl.formatMessage(
|
||||
{ id: "{number} square meters" },
|
||||
{ number: largestRoom }
|
||||
)
|
||||
} else if (smallestRoom != null && largestRoom) {
|
||||
{
|
||||
roomText = intl.formatMessage(
|
||||
{
|
||||
id: "{number} to {number} square meters",
|
||||
},
|
||||
{ largest: largestRoom, smallest: smallestRoom }
|
||||
)
|
||||
}
|
||||
}
|
||||
return roomText
|
||||
}
|
||||
|
||||
export async function getSeatingText(roomSeating: number[]) {
|
||||
const biggestSeating = Math.max(...roomSeating)
|
||||
const smallestSeating = Math.min(...roomSeating)
|
||||
const intl = await getIntl()
|
||||
|
||||
let seatingText
|
||||
if (biggestSeating === smallestSeating) {
|
||||
seatingText = intl.formatMessage(
|
||||
{ id: "{number} persons" },
|
||||
{ number: biggestSeating }
|
||||
)
|
||||
} else if (smallestSeating != null && biggestSeating) {
|
||||
{
|
||||
seatingText = intl.formatMessage(
|
||||
{
|
||||
id: "{number} to {number} persons",
|
||||
},
|
||||
{ highest: biggestSeating, lowest: smallestSeating }
|
||||
)
|
||||
}
|
||||
}
|
||||
return seatingText
|
||||
}
|
||||
@@ -4,16 +4,16 @@ import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./openingHours.module.css"
|
||||
|
||||
import {
|
||||
DaysEnum,
|
||||
type RestaurantBarOpeningHoursProps,
|
||||
} from "@/types/components/hotelPage/sidepeek/restaurantBar"
|
||||
import type { OpeningHoursProps } from "@/types/components/hotelPage/sidepeek/openingHours"
|
||||
import { DaysEnum } from "@/types/components/hotelPage/sidepeek/restaurantBar"
|
||||
import type { RestaurantOpeningHoursDay } from "@/types/hotel"
|
||||
|
||||
export default async function OpeningHours({
|
||||
openingHours,
|
||||
alternateOpeningHours,
|
||||
}: RestaurantBarOpeningHoursProps) {
|
||||
heading,
|
||||
type = "default",
|
||||
}: OpeningHoursProps) {
|
||||
const intl = await getIntl()
|
||||
|
||||
const closed = intl.formatMessage({ id: "Closed" })
|
||||
@@ -58,9 +58,9 @@ export default async function OpeningHours({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={type === "default" ? styles.wrapper : ""}>
|
||||
<Body textTransform="bold" asChild>
|
||||
<h5>{openingHours.name}</h5>
|
||||
<h5>{heading ?? openingHours.name}</h5>
|
||||
</Body>
|
||||
{Object.entries(groupedOpeningHours).map(([time, days]) => {
|
||||
return (
|
||||
@@ -7,7 +7,7 @@ import Link from "@/components/TempDesignSystem/Link"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import OpeningHours from "../OpeningHours"
|
||||
import OpeningHours from "../../OpeningHours"
|
||||
|
||||
import styles from "./restaurantBarItem.module.css"
|
||||
|
||||
@@ -49,7 +49,7 @@ export default async function RestaurantBarItem({
|
||||
{openingDetails.length ? (
|
||||
<div className={styles.content}>
|
||||
<Subtitle type="two" asChild>
|
||||
<h4>{intl.formatMessage({ id: "Opening Hours" })}</h4>
|
||||
<h4>{intl.formatMessage({ id: "Opening hours" })}</h4>
|
||||
</Subtitle>
|
||||
{openingDetails.map((details) => (
|
||||
<OpeningHours
|
||||
|
||||
@@ -31,7 +31,7 @@ export default async function Facility({ data }: FacilityProps) {
|
||||
</Subtitle>
|
||||
<div>
|
||||
<Subtitle type="two" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Opening Hours" })}
|
||||
{intl.formatMessage({ id: "Opening hours" })}
|
||||
</Subtitle>
|
||||
<div className={styles.openingHours}>
|
||||
<Body color="uiTextHighContrast">
|
||||
|
||||
@@ -9,7 +9,6 @@ import Breadcrumbs from "@/components/Breadcrumbs"
|
||||
import SidePeekProvider from "@/components/SidePeeks/SidePeekProvider"
|
||||
import Alert from "@/components/TempDesignSystem/Alert"
|
||||
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import { getRestaurantHeading } from "@/utils/facilityCards"
|
||||
import { generateHotelSchema } from "@/utils/jsonSchemas"
|
||||
@@ -49,8 +48,7 @@ import type {
|
||||
|
||||
export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
const lang = getLang()
|
||||
const [intl, hotelPageData, hotelData] = await Promise.all([
|
||||
getIntl(),
|
||||
const [hotelPageData, hotelData] = await Promise.all([
|
||||
getHotelPage(),
|
||||
getHotelData({ hotelId, language: lang }),
|
||||
])
|
||||
@@ -84,6 +82,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
} = hotelData.data.attributes
|
||||
const roomCategories = hotelData.included?.rooms || []
|
||||
const restaurants = hotelData.included?.restaurants || []
|
||||
|
||||
const images = gallery?.smallerImages
|
||||
const description = hotelContent.texts.descriptions.medium
|
||||
|
||||
@@ -200,6 +199,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
parking={parking}
|
||||
checkInInformation={hotelFacts.checkin}
|
||||
accessibility={hotelFacts.hotelInformation.accessibility}
|
||||
restaurants={restaurants}
|
||||
/>
|
||||
<AboutTheHotelSidePeek
|
||||
hotelAddress={address}
|
||||
@@ -222,6 +222,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
<MeetingsAndConferencesSidePeek
|
||||
meetingFacilities={conferencesAndMeetings}
|
||||
descriptions={hotelContent.texts.meetingDescription}
|
||||
hotelId={hotelId}
|
||||
/>
|
||||
{roomCategories.map((room) => (
|
||||
<RoomSidePeek key={room.name} room={room} />
|
||||
|
||||
Reference in New Issue
Block a user