Merged in feat/SW-1065-meetings-page (pull request #1287)
Feat(SW-1065): Meetings hotel subpage Approved-by: Erik Tiekstra
This commit is contained in:
@@ -21,7 +21,7 @@ export default async function MeetingsAndConferencesSidePeek({
|
||||
meetingFacilities,
|
||||
descriptions,
|
||||
hotelId,
|
||||
link,
|
||||
meetingPageUrl,
|
||||
}: MeetingsAndConferencesSidePeekProps) {
|
||||
const lang = getLang()
|
||||
const [intl, meetingRooms] = await Promise.all([
|
||||
@@ -83,10 +83,15 @@ export default async function MeetingsAndConferencesSidePeek({
|
||||
</Body>
|
||||
) : null}
|
||||
|
||||
{link && (
|
||||
{meetingPageUrl && (
|
||||
<div className={styles.buttonContainer}>
|
||||
<Button fullWidth theme="base" intent="secondary" asChild>
|
||||
<Link href={link} weight="bold" color="burgundy">
|
||||
<Link
|
||||
href={`/${meetingPageUrl}`}
|
||||
weight="bold"
|
||||
color="burgundy"
|
||||
appendToCurrentPath
|
||||
>
|
||||
{intl.formatMessage({ id: "About meetings & conferences" })}
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
@@ -88,6 +88,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
hotelRoomElevatorPitchText,
|
||||
gallery,
|
||||
hotelParking,
|
||||
meetingRooms,
|
||||
displayWebPage,
|
||||
hotelSpecialNeeds,
|
||||
} = hotelData.additionalData
|
||||
@@ -259,6 +260,9 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
meetingFacilities={conferencesAndMeetings}
|
||||
descriptions={hotelContent.texts.meetingDescription}
|
||||
hotelId={hotelId}
|
||||
meetingPageUrl={
|
||||
displayWebPage.meetingRoom ? meetingRooms.nameInUrl : undefined
|
||||
}
|
||||
/>
|
||||
{roomCategories.map((room) => (
|
||||
<RoomSidePeek key={room.name} room={room} />
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"use client"
|
||||
import { useRef, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Grids from "@/components/TempDesignSystem/Grids"
|
||||
import MeetingRoomCard from "@/components/TempDesignSystem/MeetingRoomCard"
|
||||
import ShowMoreButton from "@/components/TempDesignSystem/ShowMoreButton"
|
||||
|
||||
import styles from "./meetingsAdditionalContent.module.css"
|
||||
|
||||
import type { MeetingRooms } from "@/types/components/hotelPage/meetingRooms"
|
||||
|
||||
interface MeetingsAdditionalContentProps {
|
||||
rooms: MeetingRooms
|
||||
}
|
||||
|
||||
export default function MeetingsAdditionalContent({
|
||||
rooms,
|
||||
}: MeetingsAdditionalContentProps) {
|
||||
const intl = useIntl()
|
||||
const showToggleButton = rooms.length > 3
|
||||
const [allRoomsVisible, setAllRoomsVisible] = useState(!showToggleButton)
|
||||
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
function handleShowMore() {
|
||||
if (scrollRef.current && allRoomsVisible) {
|
||||
scrollRef.current.scrollIntoView({ behavior: "smooth" })
|
||||
}
|
||||
setAllRoomsVisible((state) => !state)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.section} ref={scrollRef}>
|
||||
<Grids.Stackable
|
||||
className={`${styles.grid} ${allRoomsVisible ? styles.allVisible : ""}`}
|
||||
>
|
||||
{rooms.map((room) => (
|
||||
<MeetingRoomCard key={room.id} room={room.attributes} />
|
||||
))}
|
||||
</Grids.Stackable>
|
||||
{showToggleButton ? (
|
||||
<ShowMoreButton
|
||||
loadMoreData={handleShowMore}
|
||||
showLess={allRoomsVisible}
|
||||
textShowMore={intl.formatMessage({
|
||||
id: "Show more rooms",
|
||||
})}
|
||||
textShowLess={intl.formatMessage({
|
||||
id: "Show less rooms",
|
||||
})}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.grid:not(.allVisible) > :nth-child(n + 4) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x4);
|
||||
z-index: 0;
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import AccessibilityAdditionalContent from "./Accessibility"
|
||||
import ParkingAdditionalContent from "./Parking"
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
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 { Country } from "@/types/enums/country"
|
||||
|
||||
interface MeetingsSidebarProps {
|
||||
phoneNumber: string
|
||||
email?: string
|
||||
country: string
|
||||
}
|
||||
|
||||
export default async function MeetingsSidebar({
|
||||
phoneNumber,
|
||||
email,
|
||||
country,
|
||||
}: MeetingsSidebarProps) {
|
||||
const intl = await getIntl()
|
||||
|
||||
return (
|
||||
<aside className={styles.sidebar}>
|
||||
<Title level="h3" as="h4">
|
||||
{intl.formatMessage({ id: "Contact us" })}
|
||||
</Title>
|
||||
<div>
|
||||
<Link
|
||||
href={`tel:${phoneNumber}`}
|
||||
color="peach80"
|
||||
textDecoration="underline"
|
||||
>
|
||||
{phoneNumber}
|
||||
</Link>
|
||||
{country === Country.Finland ? (
|
||||
<Body>
|
||||
{intl.formatMessage({
|
||||
id: "Price 0,16 €/min + local call charges",
|
||||
})}
|
||||
</Body>
|
||||
) : null}
|
||||
<Body>{email} </Body>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import RestaurantSidebar from "./RestaurantSidebar/RestaurantSidebar"
|
||||
import MeetingsSidebar from "./MeetingsSidebar"
|
||||
import ParkingSidebar from "./ParkingSidebar"
|
||||
import WellnessSidebar from "./WellnessSidebar"
|
||||
|
||||
import type { MeetingRooms } from "@/types/components/hotelPage/meetingRooms"
|
||||
import type { AdditionalData, Hotel, Restaurant } from "@/types/hotel"
|
||||
|
||||
interface HotelSubpageSidebarProps {
|
||||
@@ -9,6 +11,7 @@ interface HotelSubpageSidebarProps {
|
||||
hotel: Hotel
|
||||
additionalData: AdditionalData
|
||||
restaurants: Restaurant[]
|
||||
meetingRooms: MeetingRooms | undefined
|
||||
}
|
||||
|
||||
export default function HotelSubpageSidebar({
|
||||
@@ -16,6 +19,7 @@ export default function HotelSubpageSidebar({
|
||||
hotel,
|
||||
additionalData,
|
||||
restaurants,
|
||||
meetingRooms,
|
||||
}: HotelSubpageSidebarProps) {
|
||||
const restaurantSubPage = restaurants.find(
|
||||
(restaurant) => restaurant.nameInUrl === subpage
|
||||
@@ -32,6 +36,17 @@ export default function HotelSubpageSidebar({
|
||||
return <WellnessSidebar hotel={hotel} />
|
||||
case additionalData.hotelSpecialNeeds.nameInUrl:
|
||||
return null
|
||||
case additionalData.meetingRooms.nameInUrl:
|
||||
if (!meetingRooms) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<MeetingsSidebar
|
||||
phoneNumber={meetingRooms[0].attributes.phoneNumber}
|
||||
email={meetingRooms[0].attributes.email}
|
||||
country={hotel.address.country}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
.sidebar {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
@media (min-width: 1367px) {
|
||||
.sidebar {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
align-items: start;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
display: grid;
|
||||
width: 100%;
|
||||
gap: var(--Spacing-x4);
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.intro {
|
||||
@@ -36,6 +37,10 @@
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.meetingsContent {
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
@media (min-width: 1367px) {
|
||||
.contentContainer {
|
||||
grid-template-columns: var(--max-width-text-block) 1fr;
|
||||
@@ -47,4 +52,8 @@
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.meetingsContent {
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { notFound } from "next/navigation"
|
||||
import { Suspense } from "react"
|
||||
|
||||
import { getHotel, getHotelPage } from "@/lib/trpc/memoizedRequests"
|
||||
import {
|
||||
getHotel,
|
||||
getHotelPage,
|
||||
getMeetingRooms,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Breadcrumbs from "@/components/Breadcrumbs"
|
||||
import Hero from "@/components/Hero"
|
||||
@@ -11,6 +15,7 @@ import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import MeetingsAdditionalContent from "./AdditionalContent/Meetings"
|
||||
import HotelSubpageAdditionalContent from "./AdditionalContent"
|
||||
import HtmlContent from "./HtmlContent"
|
||||
import HotelSubpageSidebar from "./Sidebar"
|
||||
@@ -18,10 +23,7 @@ import { getSubpageData } from "./utils"
|
||||
|
||||
import styles from "./hotelSubpage.module.css"
|
||||
|
||||
interface HotelSubpageProps {
|
||||
hotelId: string
|
||||
subpage: string
|
||||
}
|
||||
import type { HotelSubpageProps } from "@/types/components/hotelPage/subpage"
|
||||
|
||||
export default async function HotelSubpage({
|
||||
hotelId,
|
||||
@@ -45,6 +47,11 @@ export default async function HotelSubpage({
|
||||
|
||||
const { hotel, restaurants, additionalData } = hotelData
|
||||
|
||||
let meetingRooms
|
||||
if (hotelData.additionalData.meetingRooms.nameInUrl === subpage) {
|
||||
meetingRooms = await getMeetingRooms({ hotelId: hotelId, language: lang })
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className={styles.hotelSubpage}>
|
||||
@@ -80,11 +87,18 @@ export default async function HotelSubpage({
|
||||
/>
|
||||
</main>
|
||||
|
||||
{meetingRooms && (
|
||||
<div className={styles.meetingsContent}>
|
||||
<MeetingsAdditionalContent rooms={meetingRooms} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<HotelSubpageSidebar
|
||||
subpage={subpage}
|
||||
hotel={hotel}
|
||||
additionalData={additionalData}
|
||||
restaurants={restaurants}
|
||||
meetingRooms={meetingRooms}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -74,6 +74,19 @@ export function getSubpageData(
|
||||
}
|
||||
: null,
|
||||
}
|
||||
case additionalData.meetingRooms.nameInUrl:
|
||||
const meetingImage = hotel.conferencesAndMeetings?.heroImages[0]
|
||||
return {
|
||||
elevatorPitch: hotel.hotelContent.texts.meetingDescription?.medium,
|
||||
mainBody: additionalData.meetingRooms.mainBody,
|
||||
heading: intl.formatMessage({ id: "Meetings, Conferences & Events" }),
|
||||
heroImage: meetingImage
|
||||
? {
|
||||
src: meetingImage.imageSizes.medium,
|
||||
alt: meetingImage.metaData.altText || "",
|
||||
}
|
||||
: null,
|
||||
}
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user