feat(SW-1409): Synced tabnavigation headings and section headings on hotel pages
* feat(SW-2409): Added same headings to relevant sidepeeks Approved-by: Christian Andolf Approved-by: Matilda Landström
This commit is contained in:
@@ -12,7 +12,7 @@ import ShowMoreButton from "@/components/TempDesignSystem/ShowMoreButton"
|
||||
import styles from "./accordion.module.css"
|
||||
|
||||
import type { AccordionProps } from "@/types/components/blocks/Accordion"
|
||||
import { HotelHashValues } from "@/types/components/hotelPage/tabNavigation"
|
||||
import { HotelHashValues } from "@/types/enums/hotelPage"
|
||||
|
||||
export default function AccordionSection({ accordion, title }: AccordionProps) {
|
||||
const showToggleButton = accordion.length > 5
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
type FacilityCardType,
|
||||
type FacilityGrid,
|
||||
} from "@/types/components/hotelPage/facilities"
|
||||
import { HotelHashValues } from "@/types/components/hotelPage/tabNavigation"
|
||||
import { HotelHashValues } from "@/types/enums/hotelPage"
|
||||
|
||||
export default async function Facilities({
|
||||
facilities,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { cx } from "class-variance-authority"
|
||||
import { useMemo, useRef, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
@@ -14,10 +13,9 @@ import { RoomCard } from "./RoomCard"
|
||||
import styles from "./rooms.module.css"
|
||||
|
||||
import type { RoomsProps } from "@/types/components/hotelPage/room"
|
||||
import { HotelHashValues } from "@/types/components/hotelPage/tabNavigation"
|
||||
import { HotelHashValues } from "@/types/enums/hotelPage"
|
||||
|
||||
export function Rooms({ rooms, preamble }: RoomsProps) {
|
||||
const intl = useIntl()
|
||||
export function Rooms({ heading, rooms, preamble }: RoomsProps) {
|
||||
const showToggleButton = rooms.length > 3
|
||||
const [allRoomsVisible, setAllRoomsVisible] = useState(!showToggleButton)
|
||||
const sortedRooms = useMemo(() => {
|
||||
@@ -40,12 +38,8 @@ export function Rooms({ rooms, preamble }: RoomsProps) {
|
||||
>
|
||||
<div ref={scrollRef} className={styles.scrollRef}></div>
|
||||
<header className={styles.sectionHeader}>
|
||||
<Typography variant="Title/md" className={styles.heading}>
|
||||
<h2>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Rooms",
|
||||
})}
|
||||
</h2>
|
||||
<Typography variant="Title/md">
|
||||
<h2 className={styles.heading}>{heading}</h2>
|
||||
</Typography>
|
||||
{preamble && (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
|
||||
@@ -19,6 +19,7 @@ export default async function MeetingsAndConferencesSidePeek({
|
||||
descriptions,
|
||||
meetingRooms,
|
||||
meetingPageUrl,
|
||||
heading,
|
||||
}: MeetingsAndConferencesSidePeekProps) {
|
||||
const intl = await getIntl()
|
||||
const { seatingText, roomText } = await getConferenceRoomTexts(meetingRooms)
|
||||
@@ -26,12 +27,7 @@ export default async function MeetingsAndConferencesSidePeek({
|
||||
const meetingPageHref = await appendSlugToPathname(meetingPageUrl)
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
contentKey={SidepeekSlugs.meetings}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Meetings & Conferences",
|
||||
})}
|
||||
>
|
||||
<SidePeek contentKey={SidepeekSlugs.meetings} title={heading}>
|
||||
<div className={styles.wrapper}>
|
||||
<Subtitle color="burgundy" asChild>
|
||||
<Title level="h3">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import RestaurantBarItem from "./RestaurantBarItem"
|
||||
|
||||
@@ -8,18 +7,12 @@ import styles from "./restaurantBar.module.css"
|
||||
import { SidepeekSlugs } from "@/types/components/hotelPage/hotelPage"
|
||||
import type { RestaurantBarSidePeekProps } from "@/types/components/hotelPage/sidepeek/restaurantBar"
|
||||
|
||||
export default async function RestaurantBarSidePeek({
|
||||
export default function RestaurantBarSidePeek({
|
||||
restaurants,
|
||||
heading,
|
||||
}: RestaurantBarSidePeekProps) {
|
||||
const intl = await getIntl()
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
contentKey={SidepeekSlugs.restaurant}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Restaurant & Bar",
|
||||
})}
|
||||
>
|
||||
<SidePeek contentKey={SidepeekSlugs.restaurant} title={heading}>
|
||||
<div className={styles.content}>
|
||||
{restaurants.map((restaurant) => (
|
||||
<div key={restaurant.id} className={styles.item}>
|
||||
|
||||
@@ -14,6 +14,7 @@ export default async function WellnessAndExerciseSidePeek({
|
||||
healthFacilities,
|
||||
wellnessExercisePageUrl,
|
||||
spaPage,
|
||||
heading,
|
||||
}: WellnessAndExerciseSidePeekProps) {
|
||||
const intl = await getIntl()
|
||||
const wellnessExercisePageHref = await appendSlugToPathname(
|
||||
@@ -21,12 +22,7 @@ export default async function WellnessAndExerciseSidePeek({
|
||||
)
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
contentKey={SidepeekSlugs.wellness}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Gym & Wellness",
|
||||
})}
|
||||
>
|
||||
<SidePeek contentKey={SidepeekSlugs.wellness} title={heading}>
|
||||
<div className={styles.wrapper}>
|
||||
{healthFacilities.map((facility) => (
|
||||
<Facility key={facility.type} data={facility} />
|
||||
|
||||
@@ -4,7 +4,6 @@ import { cx } from "class-variance-authority"
|
||||
import NextLink from "next/link"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
@@ -18,24 +17,19 @@ import { trackHotelTabClick } from "@/utils/tracking"
|
||||
|
||||
import styles from "./tabNavigation.module.css"
|
||||
|
||||
import {
|
||||
HotelHashValues,
|
||||
type TabNavigationProps,
|
||||
} from "@/types/components/hotelPage/tabNavigation"
|
||||
import type { HotelPageSections } from "@/types/components/hotelPage/sections"
|
||||
import { HotelHashValues } from "@/types/enums/hotelPage"
|
||||
|
||||
export default function TabNavigation({
|
||||
hasActivities,
|
||||
hasFAQ,
|
||||
hasMeetingRooms,
|
||||
hasRestaurants,
|
||||
hasWellness,
|
||||
tabValues,
|
||||
}: TabNavigationProps) {
|
||||
const hash = useHash()
|
||||
const intl = useIntl()
|
||||
interface TabNavigationProps {
|
||||
pageSections: HotelPageSections
|
||||
}
|
||||
|
||||
export default function TabNavigation({ pageSections }: TabNavigationProps) {
|
||||
const activeHash = useHash()
|
||||
const router = useRouter()
|
||||
const tabNavigationRef = useRef<HTMLDivElement>(null)
|
||||
const tabRefs = useMemo(() => new Map<string, HTMLAnchorElement>(), [])
|
||||
const tabLinks = Object.values(pageSections).map(({ hash }) => hash)
|
||||
|
||||
useStickyPosition({
|
||||
ref: tabNavigationRef,
|
||||
@@ -46,88 +40,7 @@ export default function TabNavigation({
|
||||
const { containerRef, showLeftShadow, showRightShadow } =
|
||||
useScrollShadows<HTMLDivElement>()
|
||||
|
||||
const tabLinks: { hash: HotelHashValues; text: string }[] = [
|
||||
{
|
||||
hash: HotelHashValues.overview,
|
||||
text:
|
||||
tabValues?.overview ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Overview",
|
||||
}),
|
||||
},
|
||||
{
|
||||
hash: HotelHashValues.rooms,
|
||||
text:
|
||||
tabValues?.rooms ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Rooms",
|
||||
}),
|
||||
},
|
||||
...(hasRestaurants
|
||||
? [
|
||||
{
|
||||
hash: HotelHashValues.restaurant,
|
||||
text:
|
||||
tabValues?.restaurant_bar ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Restaurant & Bar",
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(hasMeetingRooms
|
||||
? [
|
||||
{
|
||||
hash: HotelHashValues.meetings,
|
||||
text:
|
||||
tabValues?.conferences_meetings ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Meetings & Conferences",
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(hasWellness
|
||||
? [
|
||||
{
|
||||
hash: HotelHashValues.wellness,
|
||||
text:
|
||||
tabValues?.health_wellness ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Gym & Wellness",
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(hasActivities
|
||||
? [
|
||||
{
|
||||
hash: HotelHashValues.activities,
|
||||
text:
|
||||
tabValues?.activities ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Activities",
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(hasFAQ
|
||||
? [
|
||||
{
|
||||
hash: HotelHashValues.faq,
|
||||
text:
|
||||
tabValues?.faq ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "FAQ",
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
|
||||
const { activeSectionId, pauseScrollSpy } = useScrollSpy(
|
||||
tabLinks.map(({ hash }) => hash)
|
||||
)
|
||||
const { activeSectionId, pauseScrollSpy } = useScrollSpy(tabLinks)
|
||||
|
||||
const scrollLinkToCenter = useCallback(
|
||||
(hash: string) => {
|
||||
@@ -160,9 +73,9 @@ export default function TabNavigation({
|
||||
)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const activeHash = hash || HotelHashValues.overview
|
||||
scrollLinkToCenter(activeHash)
|
||||
}, [hash, scrollLinkToCenter])
|
||||
const hash = activeHash || HotelHashValues.overview
|
||||
scrollLinkToCenter(hash)
|
||||
}, [activeHash, scrollLinkToCenter])
|
||||
|
||||
useEffect(() => {
|
||||
if (activeSectionId) {
|
||||
@@ -179,19 +92,19 @@ export default function TabNavigation({
|
||||
})}
|
||||
>
|
||||
<nav className={styles.tabsContainer} ref={containerRef}>
|
||||
{tabLinks.map((link) => {
|
||||
{Object.values(pageSections).map(({ hash, heading }) => {
|
||||
const isActive =
|
||||
hash === link.hash ||
|
||||
(!hash && link.hash === HotelHashValues.overview)
|
||||
activeHash === hash ||
|
||||
(!activeHash && hash === HotelHashValues.overview)
|
||||
return (
|
||||
<Typography
|
||||
key={link.hash}
|
||||
key={hash}
|
||||
variant={
|
||||
isActive ? "Body/Paragraph/mdBold" : "Body/Paragraph/mdRegular"
|
||||
}
|
||||
>
|
||||
<NextLink
|
||||
href={`#${link.hash}`}
|
||||
href={`#${hash}`}
|
||||
className={cx(styles.link, {
|
||||
[styles.active]: isActive,
|
||||
})}
|
||||
@@ -199,15 +112,15 @@ export default function TabNavigation({
|
||||
scroll={true}
|
||||
ref={(element) => {
|
||||
if (element) {
|
||||
tabRefs.set(link.hash, element)
|
||||
tabRefs.set(hash, element)
|
||||
}
|
||||
}}
|
||||
onClick={() => {
|
||||
pauseScrollSpy()
|
||||
trackHotelTabClick(link.text)
|
||||
trackHotelTabClick(heading)
|
||||
}}
|
||||
>
|
||||
{link.text}
|
||||
{heading}
|
||||
</NextLink>
|
||||
</Typography>
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ import Breadcrumbs from "@/components/Breadcrumbs"
|
||||
import Alert from "@/components/TempDesignSystem/Alert"
|
||||
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import { setFacilityCards } from "@/utils/facilityCards"
|
||||
import { generateHotelSchema } from "@/utils/jsonSchemas"
|
||||
@@ -35,15 +36,20 @@ import PreviewImages from "./PreviewImages"
|
||||
import { Rooms } from "./Rooms"
|
||||
import SidePeeks from "./SidePeeks"
|
||||
import TabNavigation from "./TabNavigation"
|
||||
import { getTrackingHotelData, getTrackingPageData } from "./utils"
|
||||
import {
|
||||
getPageSectionsData,
|
||||
getTrackingHotelData,
|
||||
getTrackingPageData,
|
||||
} from "./utils"
|
||||
|
||||
import styles from "./hotelPage.module.css"
|
||||
|
||||
import type { HotelPageProps } from "@/types/components/hotelPage/hotelPage"
|
||||
import { HotelHashValues } from "@/types/components/hotelPage/tabNavigation"
|
||||
import { HotelHashValues } from "@/types/enums/hotelPage"
|
||||
|
||||
export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
const lang = await getLang()
|
||||
const intl = await getIntl()
|
||||
|
||||
void getHotelPage()
|
||||
void getHotel({
|
||||
@@ -68,7 +74,12 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
}
|
||||
|
||||
const jsonSchema = generateHotelSchema(hotelData)
|
||||
const { faq, content, tabValues } = hotelPageData
|
||||
const {
|
||||
faq,
|
||||
content: { spaPage, activitiesCards },
|
||||
sectionHeadings,
|
||||
} = hotelPageData
|
||||
const { hotel, restaurants, roomCategories, additionalData } = hotelData
|
||||
const {
|
||||
name,
|
||||
address,
|
||||
@@ -84,9 +95,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
ratings,
|
||||
parking,
|
||||
hotelType,
|
||||
} = hotelData.hotel
|
||||
const restaurants = hotelData.restaurants
|
||||
const roomCategories = hotelData.roomCategories
|
||||
} = hotel
|
||||
const {
|
||||
healthAndWellness,
|
||||
healthAndFitness,
|
||||
@@ -98,28 +107,29 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
meetingRooms,
|
||||
displayWebPage,
|
||||
hotelSpecialNeeds,
|
||||
} = hotelData.additionalData
|
||||
} = additionalData
|
||||
|
||||
const images = gallery?.smallerImages
|
||||
const description = hotelContent.texts.descriptions?.medium
|
||||
|
||||
const { spaPage, activitiesCards } = content
|
||||
|
||||
const hasRestaurants = restaurants.length > 0
|
||||
const hasMeetingRooms = !!meetingRoomsData?.length
|
||||
const hasWellness = healthFacilities.length > 0
|
||||
const pageSections = getPageSectionsData(
|
||||
intl,
|
||||
{
|
||||
hasWellness: healthFacilities.length > 0,
|
||||
hasRestaurants: restaurants.length > 0,
|
||||
hasMeetingRooms: !!(meetingRoomsData && meetingRoomsData.length > 0),
|
||||
hasActivities: activitiesCards.length > 0,
|
||||
hasFAQ: !!(faq && faq.accordions.length > 0),
|
||||
},
|
||||
sectionHeadings
|
||||
)
|
||||
|
||||
const facilities = setFacilityCards(
|
||||
restaurantImages ?? undefined,
|
||||
conferencesAndMeetings ?? undefined,
|
||||
healthAndWellness ?? undefined,
|
||||
hasRestaurants,
|
||||
hasMeetingRooms,
|
||||
hasWellness
|
||||
pageSections
|
||||
)
|
||||
|
||||
const topThreePois = pointsOfInterest.slice(0, 3)
|
||||
|
||||
const coordinates = {
|
||||
lat: location.latitude,
|
||||
lng: location.longitude,
|
||||
@@ -148,14 +158,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
<PreviewImages images={images} hotelName={name} />
|
||||
) : null}
|
||||
</header>
|
||||
<TabNavigation
|
||||
hasActivities={activitiesCards.length > 0}
|
||||
hasFAQ={!!faq?.accordions.length}
|
||||
hasWellness={hasWellness}
|
||||
hasRestaurants={hasRestaurants}
|
||||
hasMeetingRooms={hasMeetingRooms}
|
||||
tabValues={tabValues}
|
||||
/>
|
||||
<TabNavigation pageSections={pageSections} />
|
||||
|
||||
<main className={styles.mainSection}>
|
||||
<div id={HotelHashValues.overview} className={styles.overview}>
|
||||
@@ -185,7 +188,11 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<Rooms rooms={roomCategories} preamble={hotelRoomElevatorPitchText} />
|
||||
<Rooms
|
||||
heading={pageSections.rooms.heading}
|
||||
rooms={roomCategories}
|
||||
preamble={hotelRoomElevatorPitchText}
|
||||
/>
|
||||
{facilities && (
|
||||
<Facilities
|
||||
facilities={facilities}
|
||||
@@ -205,7 +212,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
hotelName={name}
|
||||
markerInfo={{ hotelType, hotelId }}
|
||||
/>
|
||||
<MapCard hotelName={name} pois={topThreePois} />
|
||||
<MapCard hotelName={name} pois={pointsOfInterest.slice(0, 3)} />
|
||||
</MapWithCardWrapper>
|
||||
</aside>
|
||||
<MobileMapToggle />
|
||||
@@ -237,8 +244,9 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
ecoLabels={hotelFacts.ecoLabels}
|
||||
descriptions={hotelContent.texts}
|
||||
/>
|
||||
{hasWellness ? (
|
||||
{pageSections.wellness ? (
|
||||
<WellnessAndExerciseSidePeek
|
||||
heading={pageSections.wellness.heading}
|
||||
healthFacilities={healthFacilities}
|
||||
spaPage={spaPage?.spa_page}
|
||||
wellnessExercisePageUrl={
|
||||
@@ -246,8 +254,11 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
{hasRestaurants ? (
|
||||
<RestaurantBarSidePeek restaurants={restaurants} />
|
||||
{pageSections.restaurant ? (
|
||||
<RestaurantBarSidePeek
|
||||
heading={pageSections.restaurant.heading}
|
||||
restaurants={restaurants}
|
||||
/>
|
||||
) : null}
|
||||
{activitiesCards.map((card) => (
|
||||
<ActivitiesSidePeek
|
||||
@@ -257,8 +268,9 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
sidepeekSlug={card.upcoming_activities_card.sidepeekSlug}
|
||||
/>
|
||||
))}
|
||||
{hasMeetingRooms && (
|
||||
{pageSections.meetings ? (
|
||||
<MeetingsAndConferencesSidePeek
|
||||
heading={pageSections.meetings.heading}
|
||||
meetingFacilities={conferencesAndMeetings}
|
||||
descriptions={hotelContent.texts.meetingDescription}
|
||||
meetingRooms={meetingRoomsData ?? []}
|
||||
@@ -266,7 +278,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
|
||||
displayWebPage.meetingRoom ? meetingRooms.nameInUrl : undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
) : null}
|
||||
{roomCategories.map((room) => (
|
||||
<RoomSidePeek key={room.name} hotelId={hotelId} room={room} />
|
||||
))}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import type { IntlShape } from "react-intl"
|
||||
|
||||
import { HealthFacilitiesEnum } from "@/types/components/hotelPage/facilities"
|
||||
import type {
|
||||
HotelPageSectionHeadings,
|
||||
HotelPageSections,
|
||||
} from "@/types/components/hotelPage/sections"
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKHotelInfo,
|
||||
type TrackingSDKPageData,
|
||||
} from "@/types/components/tracking"
|
||||
import { HotelHashValues } from "@/types/enums/hotelPage"
|
||||
import type { Hotel, HotelData } from "@/types/hotel"
|
||||
import type { HotelPage } from "@/types/trpc/routers/contentstack/hotelPage"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
@@ -84,3 +89,94 @@ export function translateWellnessType(type: string, intl: IntlShape) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function getPageSectionsData(
|
||||
intl: IntlShape,
|
||||
dynamicSections: {
|
||||
hasWellness: boolean
|
||||
hasRestaurants: boolean
|
||||
hasMeetingRooms: boolean
|
||||
hasActivities: boolean
|
||||
hasFAQ: boolean
|
||||
},
|
||||
sectionHeadings?: HotelPageSectionHeadings | null
|
||||
) {
|
||||
const {
|
||||
hasWellness,
|
||||
hasRestaurants,
|
||||
hasMeetingRooms,
|
||||
hasActivities,
|
||||
hasFAQ,
|
||||
} = dynamicSections
|
||||
const sections: HotelPageSections = {
|
||||
overview: {
|
||||
hash: HotelHashValues.overview,
|
||||
heading:
|
||||
sectionHeadings?.overview ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Overview",
|
||||
}),
|
||||
},
|
||||
rooms: {
|
||||
hash: HotelHashValues.rooms,
|
||||
heading:
|
||||
sectionHeadings?.rooms ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Rooms",
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
if (hasRestaurants) {
|
||||
sections.restaurant = {
|
||||
hash: HotelHashValues.restaurant,
|
||||
heading:
|
||||
sectionHeadings?.restaurant_bar ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Restaurant & Bar",
|
||||
}),
|
||||
}
|
||||
}
|
||||
if (hasMeetingRooms) {
|
||||
sections.meetings = {
|
||||
hash: HotelHashValues.meetings,
|
||||
heading:
|
||||
sectionHeadings?.conferences_meetings ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Meetings & Conferences",
|
||||
}),
|
||||
}
|
||||
}
|
||||
if (hasWellness) {
|
||||
sections.wellness = {
|
||||
hash: HotelHashValues.wellness,
|
||||
heading:
|
||||
sectionHeadings?.health_wellness ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Gym & Wellness",
|
||||
}),
|
||||
}
|
||||
}
|
||||
if (hasActivities) {
|
||||
sections.activities = {
|
||||
hash: HotelHashValues.activities,
|
||||
heading:
|
||||
sectionHeadings?.activities ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Activities",
|
||||
}),
|
||||
}
|
||||
}
|
||||
if (hasFAQ) {
|
||||
sections.faq = {
|
||||
hash: HotelHashValues.faq,
|
||||
heading:
|
||||
sectionHeadings?.faq ||
|
||||
intl.formatMessage({
|
||||
defaultMessage: "FAQ",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
return sections
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ export const hotelPageSchema = z.object({
|
||||
),
|
||||
})
|
||||
.transform(({ hotel_navigation, ...rest }) => ({
|
||||
tabValues: hotel_navigation,
|
||||
sectionHeadings: hotel_navigation,
|
||||
...rest,
|
||||
})),
|
||||
})
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { HotelHashValues } from "@/types/enums/hotelPage"
|
||||
|
||||
export interface HotelPageProps {
|
||||
hotelId: string
|
||||
}
|
||||
@@ -10,3 +12,6 @@ export enum SidepeekSlugs {
|
||||
meetings = "meetings",
|
||||
wellness = "wellness",
|
||||
}
|
||||
|
||||
export type HotelHashValue =
|
||||
(typeof HotelHashValues)[keyof typeof HotelHashValues]
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface RoomCardProps {
|
||||
}
|
||||
|
||||
export type RoomsProps = {
|
||||
heading: string
|
||||
preamble?: string
|
||||
rooms: Room[]
|
||||
}
|
||||
|
||||
27
apps/scandic-web/types/components/hotelPage/sections.ts
Normal file
27
apps/scandic-web/types/components/hotelPage/sections.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { HotelHashValue } from "./hotelPage"
|
||||
|
||||
export interface HotelPageSectionHeadings {
|
||||
overview?: string | null
|
||||
rooms?: string | null
|
||||
restaurant_bar?: string | null
|
||||
conferences_meetings?: string | null
|
||||
health_wellness?: string | null
|
||||
activities?: string | null
|
||||
offers?: string | null
|
||||
faq?: string | null
|
||||
}
|
||||
|
||||
interface HotelPageSection {
|
||||
hash: HotelHashValue
|
||||
heading: string
|
||||
}
|
||||
|
||||
export interface HotelPageSections {
|
||||
overview: HotelPageSection
|
||||
rooms: HotelPageSection
|
||||
restaurant?: HotelPageSection
|
||||
meetings?: HotelPageSection
|
||||
wellness?: HotelPageSection
|
||||
activities?: HotelPageSection
|
||||
faq?: HotelPageSection
|
||||
}
|
||||
@@ -6,4 +6,5 @@ export type MeetingsAndConferencesSidePeekProps = {
|
||||
descriptions: Hotel["hotelContent"]["texts"]["meetingDescription"]
|
||||
meetingRooms: MeetingRooms
|
||||
meetingPageUrl: string | undefined
|
||||
heading: string
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Restaurant } from "@/types/hotel"
|
||||
|
||||
export interface RestaurantBarSidePeekProps {
|
||||
restaurants: Restaurant[]
|
||||
heading: string
|
||||
}
|
||||
|
||||
export interface RestaurantBarItemProps {
|
||||
|
||||
@@ -7,4 +7,5 @@ export type WellnessAndExerciseSidePeekProps = {
|
||||
buttonCTA: string
|
||||
url: string
|
||||
}
|
||||
heading: string
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
export enum HotelHashValues {
|
||||
overview = "overview",
|
||||
rooms = "rooms",
|
||||
restaurant = "restaurants",
|
||||
meetings = "meetings",
|
||||
wellness = "wellness",
|
||||
activities = "activities",
|
||||
faq = "faq",
|
||||
}
|
||||
|
||||
type Tabs = {
|
||||
overview?: string | null
|
||||
rooms?: string | null
|
||||
restaurant_bar?: string | null
|
||||
conferences_meetings?: string | null
|
||||
health_wellness?: string | null
|
||||
activities?: string | null
|
||||
offers?: string | null
|
||||
faq?: string | null
|
||||
}
|
||||
|
||||
export type TabNavigationProps = {
|
||||
hasActivities: boolean
|
||||
hasFAQ: boolean
|
||||
hasWellness: boolean
|
||||
hasRestaurants: boolean
|
||||
hasMeetingRooms: boolean
|
||||
tabValues?: Tabs | null
|
||||
}
|
||||
@@ -7,3 +7,13 @@ export namespace HotelPageEnum {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const HotelHashValues = {
|
||||
overview: "overview",
|
||||
rooms: "rooms",
|
||||
restaurant: "restaurants",
|
||||
meetings: "meetings",
|
||||
wellness: "wellness",
|
||||
activities: "activities",
|
||||
faq: "faq",
|
||||
} as const
|
||||
|
||||
@@ -11,9 +11,13 @@ import {
|
||||
RestaurantHeadings,
|
||||
WellnessHeadings,
|
||||
} from "@/types/components/hotelPage/facilities"
|
||||
import { SidepeekSlugs } from "@/types/components/hotelPage/hotelPage"
|
||||
import { HotelHashValues } from "@/types/components/hotelPage/tabNavigation"
|
||||
import {
|
||||
type HotelHashValue,
|
||||
SidepeekSlugs,
|
||||
} from "@/types/components/hotelPage/hotelPage";
|
||||
import type { HotelPageSections } from "@/types/components/hotelPage/sections"
|
||||
import { FacilityEnum } from "@/types/enums/facilities"
|
||||
import { HotelHashValues } from "@/types/enums/hotelPage"
|
||||
import type {
|
||||
Amenities,
|
||||
Facility,
|
||||
@@ -26,24 +30,34 @@ export function setFacilityCards(
|
||||
restaurantImages: FacilityData | undefined,
|
||||
conferencesAndMeetings: FacilityData | undefined,
|
||||
healthAndWellness: FacilityData | undefined,
|
||||
hasRestaurants: boolean,
|
||||
hasMeetingRooms: boolean,
|
||||
hasWellness: boolean
|
||||
pageSections: HotelPageSections
|
||||
): Facility[] {
|
||||
const facilities = []
|
||||
if (hasRestaurants) {
|
||||
if (pageSections.restaurant) {
|
||||
facilities.push(
|
||||
setFacilityCard(restaurantImages, FacilityCardTypeEnum.restaurant)
|
||||
setFacilityCard(
|
||||
restaurantImages,
|
||||
FacilityCardTypeEnum.restaurant,
|
||||
pageSections.restaurant.heading
|
||||
)
|
||||
)
|
||||
}
|
||||
if (hasMeetingRooms) {
|
||||
if (pageSections.meetings) {
|
||||
facilities.push(
|
||||
setFacilityCard(conferencesAndMeetings, FacilityCardTypeEnum.conference)
|
||||
setFacilityCard(
|
||||
conferencesAndMeetings,
|
||||
FacilityCardTypeEnum.conference,
|
||||
pageSections.meetings.heading
|
||||
)
|
||||
)
|
||||
}
|
||||
if (hasWellness) {
|
||||
if (pageSections.wellness) {
|
||||
facilities.push(
|
||||
setFacilityCard(healthAndWellness, FacilityCardTypeEnum.wellness)
|
||||
setFacilityCard(
|
||||
healthAndWellness,
|
||||
FacilityCardTypeEnum.wellness,
|
||||
pageSections.wellness.heading
|
||||
)
|
||||
)
|
||||
}
|
||||
return facilities
|
||||
@@ -51,12 +65,13 @@ export function setFacilityCards(
|
||||
|
||||
function setFacilityCard(
|
||||
facility: FacilityData | undefined,
|
||||
type: FacilityCardTypeEnum
|
||||
type: FacilityCardTypeEnum,
|
||||
heading: string
|
||||
): Facility {
|
||||
return {
|
||||
...facility,
|
||||
id: type,
|
||||
headingText: facility?.headingText ?? "",
|
||||
headingText: heading,
|
||||
heroImages: facility?.heroImages ?? [],
|
||||
}
|
||||
}
|
||||
@@ -72,7 +87,7 @@ export function isFacilityImage(card: FacilityCardType): card is FacilityImage {
|
||||
function setCardProps(
|
||||
theme: CardProps["theme"],
|
||||
buttonText: (typeof FacilityCardButtonText)[keyof typeof FacilityCardButtonText],
|
||||
href: HotelHashValues,
|
||||
href: HotelHashValue,
|
||||
heading: string,
|
||||
slug: SidepeekSlugs,
|
||||
scriptedTopTitle?: string
|
||||
|
||||
Reference in New Issue
Block a user