Merged in feat/SW-1469-facility-cards (pull request #1393)

Feat(SW-1469): Add check if we should create facility cards

* fix(SW-1469): only create facility cards if supposed to


Approved-by: Erik Tiekstra
Approved-by: Fredrik Thorsson
This commit is contained in:
Matilda Landström
2025-02-24 08:19:26 +00:00
parent ec00a598d2
commit dda236aa82
7 changed files with 141 additions and 74 deletions

View File

@@ -1,5 +1,4 @@
import { meetingsAndConferences } from "@/constants/routes/hotelPageParams" import { meetingsAndConferences } from "@/constants/routes/hotelPageParams"
import { getMeetingRooms } from "@/lib/trpc/memoizedRequests"
import Image from "@/components/Image" import Image from "@/components/Image"
import Button from "@/components/TempDesignSystem/Button" import Button from "@/components/TempDesignSystem/Button"
@@ -20,15 +19,11 @@ import type { MeetingsAndConferencesSidePeekProps } from "@/types/components/hot
export default async function MeetingsAndConferencesSidePeek({ export default async function MeetingsAndConferencesSidePeek({
meetingFacilities, meetingFacilities,
descriptions, descriptions,
hotelId, meetingRooms,
meetingPageUrl, meetingPageUrl,
}: MeetingsAndConferencesSidePeekProps) { }: MeetingsAndConferencesSidePeekProps) {
const lang = getLang() const lang = getLang()
const [intl, meetingRooms] = await Promise.all([ const intl = await getIntl()
getIntl(),
getMeetingRooms({ hotelId, language: lang }),
])
const { seatingText, roomText } = await getConferenceRoomTexts(meetingRooms) const { seatingText, roomText } = await getConferenceRoomTexts(meetingRooms)
const fallbackAlt = intl.formatMessage({ id: "Creative spaces for meetings" }) const fallbackAlt = intl.formatMessage({ id: "Creative spaces for meetings" })

View File

@@ -22,6 +22,9 @@ import {
export default function TabNavigation({ export default function TabNavigation({
hasActivities, hasActivities,
hasFAQ, hasFAQ,
hasMeetingRooms,
hasRestaurants,
hasWellness,
tabValues, tabValues,
}: TabNavigationProps) { }: TabNavigationProps) {
const hash = useHash() const hash = useHash()
@@ -43,24 +46,36 @@ export default function TabNavigation({
hash: HotelHashValues.rooms, hash: HotelHashValues.rooms,
text: tabValues?.rooms || intl.formatMessage({ id: "Rooms" }), text: tabValues?.rooms || intl.formatMessage({ id: "Rooms" }),
}, },
{ ...(hasRestaurants
hash: HotelHashValues.restaurant, ? [
text: {
tabValues?.restaurant_bar || hash: HotelHashValues.restaurant,
intl.formatMessage({ id: "Restaurant & Bar" }), text:
}, tabValues?.restaurant_bar ||
{ intl.formatMessage({ id: "Restaurant & Bar" }),
hash: HotelHashValues.meetings, },
text: ]
tabValues?.conferences_meetings || : []),
intl.formatMessage({ id: "Meetings & Conferences" }), ...(hasMeetingRooms
}, ? [
{ {
hash: HotelHashValues.wellness, hash: HotelHashValues.meetings,
text: text:
tabValues?.health_wellness || tabValues?.conferences_meetings ||
intl.formatMessage({ id: "Wellness & Exercise" }), intl.formatMessage({ id: "Meetings & Conferences" }),
}, },
]
: []),
...(hasWellness
? [
{
hash: HotelHashValues.wellness,
text:
tabValues?.health_wellness ||
intl.formatMessage({ id: "Wellness & Exercise" }),
},
]
: []),
...(hasActivities ...(hasActivities
? [ ? [
{ {

View File

@@ -2,7 +2,11 @@ import { notFound } from "next/navigation"
import { Suspense } from "react" import { Suspense } from "react"
import { env } from "@/env/server" import { env } from "@/env/server"
import { getHotel, getHotelPage } from "@/lib/trpc/memoizedRequests" import {
getHotel,
getHotelPage,
getMeetingRooms,
} from "@/lib/trpc/memoizedRequests"
import AccordionSection from "@/components/Blocks/Accordion" import AccordionSection from "@/components/Blocks/Accordion"
import Breadcrumbs from "@/components/Breadcrumbs" import Breadcrumbs from "@/components/Breadcrumbs"
@@ -11,6 +15,7 @@ import Alert from "@/components/TempDesignSystem/Alert"
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton" import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
import TrackingSDK from "@/components/TrackingSDK" import TrackingSDK from "@/components/TrackingSDK"
import { getLang } from "@/i18n/serverContext" import { getLang } from "@/i18n/serverContext"
import { setFacilityCards } from "@/utils/facilityCards"
import { generateHotelSchema } from "@/utils/jsonSchemas" import { generateHotelSchema } from "@/utils/jsonSchemas"
import DynamicMap from "./Map/DynamicMap" import DynamicMap from "./Map/DynamicMap"
@@ -37,22 +42,22 @@ import { getTrackingHotelData, getTrackingPageData } from "./utils"
import styles from "./hotelPage.module.css" import styles from "./hotelPage.module.css"
import { FacilityCardTypeEnum } from "@/types/components/hotelPage/facilities"
import type { HotelPageProps } from "@/types/components/hotelPage/hotelPage" import type { HotelPageProps } from "@/types/components/hotelPage/hotelPage"
import { HotelHashValues } from "@/types/components/hotelPage/tabNavigation" import { HotelHashValues } from "@/types/components/hotelPage/tabNavigation"
import type { Facility } from "@/types/hotel"
import { PageContentTypeEnum } from "@/types/requests/contentType" import { PageContentTypeEnum } from "@/types/requests/contentType"
export default async function HotelPage({ hotelId }: HotelPageProps) { export default async function HotelPage({ hotelId }: HotelPageProps) {
const lang = getLang() const lang = getLang()
const [hotelPageData, hotelData] = await Promise.all([ const [hotelPageData, hotelData, meetingRoomsData] = await Promise.all([
getHotelPage(), getHotelPage(),
getHotel({ getHotel({
hotelId, hotelId,
isCardOnlyPayment: false, isCardOnlyPayment: false,
language: lang, language: lang,
}), }),
getMeetingRooms({ hotelId, language: lang }),
]) ])
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
@@ -97,26 +102,18 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
const { spaPage, activitiesCards } = content const { spaPage, activitiesCards } = content
const facilities: Facility[] = [ const hasRestaurants = restaurants.length > 0
{ const hasMeetingRooms = meetingRoomsData.length > 0
...restaurantImages, const hasWellness = healthFacilities.length > 0
id: FacilityCardTypeEnum.restaurant,
headingText: restaurantImages?.headingText ?? "", const facilities = setFacilityCards(
heroImages: restaurantImages?.heroImages ?? [], restaurantImages,
}, conferencesAndMeetings,
{ healthAndWellness,
...conferencesAndMeetings, hasRestaurants,
id: FacilityCardTypeEnum.conference, hasMeetingRooms,
headingText: conferencesAndMeetings?.headingText ?? "", hasWellness
heroImages: conferencesAndMeetings?.heroImages ?? [], )
},
{
...healthAndWellness,
id: FacilityCardTypeEnum.wellness,
headingText: healthAndWellness?.headingText ?? "",
heroImages: healthAndWellness?.heroImages ?? [],
},
]
const topThreePois = pointsOfInterest.slice(0, 3) const topThreePois = pointsOfInterest.slice(0, 3)
@@ -151,6 +148,9 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
<TabNavigation <TabNavigation
hasActivities={activitiesCards.length > 0} hasActivities={activitiesCards.length > 0}
hasFAQ={!!faq?.accordions.length} hasFAQ={!!faq?.accordions.length}
hasWellness={hasWellness}
hasRestaurants={hasRestaurants}
hasMeetingRooms={hasMeetingRooms}
tabValues={tabValues} tabValues={tabValues}
/> />
@@ -183,12 +183,14 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
) : null} ) : null}
</div> </div>
<Rooms rooms={roomCategories} preamble={hotelRoomElevatorPitchText} /> <Rooms rooms={roomCategories} preamble={hotelRoomElevatorPitchText} />
<Facilities {facilities && (
facilities={facilities} <Facilities
activitiesCards={activitiesCards} facilities={facilities}
amenities={detailedFacilities} activitiesCards={activitiesCards}
healthFacilities={healthFacilities} amenities={detailedFacilities}
/> healthFacilities={healthFacilities}
/>
)}
{faq && faq.accordions.length > 0 && ( {faq && faq.accordions.length > 0 && (
<AccordionSection accordion={faq.accordions} title={faq.title} /> <AccordionSection accordion={faq.accordions} title={faq.title} />
)} )}
@@ -238,14 +240,18 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
ecoLabels={hotelFacts.ecoLabels} ecoLabels={hotelFacts.ecoLabels}
descriptions={hotelContent.texts} descriptions={hotelContent.texts}
/> />
<WellnessAndExerciseSidePeek {hasWellness ? (
healthFacilities={healthFacilities} <WellnessAndExerciseSidePeek
spaPage={spaPage?.spa_page} healthFacilities={healthFacilities}
wellnessExercisePageUrl={ spaPage={spaPage?.spa_page}
displayWebPage.healthGym ? healthAndFitness.nameInUrl : undefined wellnessExercisePageUrl={
} displayWebPage.healthGym ? healthAndFitness.nameInUrl : undefined
/> }
<RestaurantBarSidePeek restaurants={restaurants} /> />
) : null}
{hasRestaurants ? (
<RestaurantBarSidePeek restaurants={restaurants} />
) : null}
{activitiesCards.map((card) => ( {activitiesCards.map((card) => (
<ActivitiesSidePeek <ActivitiesSidePeek
key={card.upcoming_activities_card.heading} key={card.upcoming_activities_card.heading}
@@ -254,14 +260,16 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
sidepeekSlug={card.upcoming_activities_card.sidepeekSlug} sidepeekSlug={card.upcoming_activities_card.sidepeekSlug}
/> />
))} ))}
<MeetingsAndConferencesSidePeek {hasMeetingRooms && (
meetingFacilities={conferencesAndMeetings} <MeetingsAndConferencesSidePeek
descriptions={hotelContent.texts.meetingDescription} meetingFacilities={conferencesAndMeetings}
hotelId={hotelId} descriptions={hotelContent.texts.meetingDescription}
meetingPageUrl={ meetingRooms={meetingRoomsData}
displayWebPage.meetingRoom ? meetingRooms.nameInUrl : undefined meetingPageUrl={
} displayWebPage.meetingRoom ? meetingRooms.nameInUrl : undefined
/> }
/>
)}
{roomCategories.map((room) => ( {roomCategories.map((room) => (
<RoomSidePeek key={room.name} room={room} /> <RoomSidePeek key={room.name} room={room} />
))} ))}

View File

@@ -1,8 +1,9 @@
import type { Hotel } from "@/types/hotel" import type { Hotel } from "@/types/hotel"
import type { MeetingRooms } from "../meetingRooms"
export type MeetingsAndConferencesSidePeekProps = { export type MeetingsAndConferencesSidePeekProps = {
meetingFacilities: Hotel["conferencesAndMeetings"] meetingFacilities: Hotel["conferencesAndMeetings"]
descriptions: Hotel["hotelContent"]["texts"]["meetingDescription"] descriptions: Hotel["hotelContent"]["texts"]["meetingDescription"]
hotelId: string meetingRooms: MeetingRooms
meetingPageUrl: string | undefined meetingPageUrl: string | undefined
} }

View File

@@ -22,5 +22,8 @@ type Tabs = {
export type TabNavigationProps = { export type TabNavigationProps = {
hasActivities: boolean hasActivities: boolean
hasFAQ: boolean hasFAQ: boolean
hasWellness: boolean
hasRestaurants: boolean
hasMeetingRooms: boolean
tabValues?: Tabs | null tabValues?: Tabs | null
} }

View File

@@ -32,7 +32,8 @@ export type Amenities = z.output<typeof detailedFacilitiesSchema>
export type CheckInData = z.output<typeof checkinSchema> export type CheckInData = z.output<typeof checkinSchema>
type CitySchema = z.output<typeof citySchema> type CitySchema = z.output<typeof citySchema>
export type City = Pick<CitySchema, "id" | "type"> & CitySchema["attributes"] export type City = Pick<CitySchema, "id" | "type"> & CitySchema["attributes"]
export type Facility = z.output<typeof facilitySchema> & { id: string } export type FacilityData = z.output<typeof facilitySchema>
export type Facility = FacilityData & { id: string }
export type ApiImage = z.output<typeof imageSchema> export type ApiImage = z.output<typeof imageSchema>
export type HealthFacility = z.output<typeof healthFacilitySchema> export type HealthFacility = z.output<typeof healthFacilitySchema>
export type HealthFacilities = HealthFacility[] export type HealthFacilities = HealthFacility[]

View File

@@ -19,9 +19,53 @@ import {
WellnessHeadings, WellnessHeadings,
} from "@/types/components/hotelPage/facilities" } from "@/types/components/hotelPage/facilities"
import { FacilityEnum } from "@/types/enums/facilities" import { FacilityEnum } from "@/types/enums/facilities"
import type { Amenities, Facility, HealthFacilities } from "@/types/hotel" import type {
Amenities,
Facility,
FacilityData,
HealthFacilities,
} from "@/types/hotel"
import type { CardProps } from "@/components/TempDesignSystem/Card/card" import type { CardProps } from "@/components/TempDesignSystem/Card/card"
export function setFacilityCards(
restaurantImages: FacilityData | undefined,
conferencesAndMeetings: FacilityData | undefined,
healthAndWellness: FacilityData | undefined,
hasRestaurants: boolean,
hasMeetingRooms: boolean,
hasWellness: boolean
): Facility[] {
const facilities = []
if (hasRestaurants) {
facilities.push(
setFacilityCard(restaurantImages, FacilityCardTypeEnum.restaurant)
)
}
if (hasMeetingRooms) {
facilities.push(
setFacilityCard(conferencesAndMeetings, FacilityCardTypeEnum.conference)
)
}
if (hasWellness) {
facilities.push(
setFacilityCard(healthAndWellness, FacilityCardTypeEnum.wellness)
)
}
return facilities
}
function setFacilityCard(
facility: FacilityData | undefined,
type: FacilityCardTypeEnum
): Facility {
return {
...facility,
id: type,
headingText: facility?.headingText ?? "",
heroImages: facility?.heroImages ?? [],
}
}
export function isFacilityCard(card: FacilityCardType): card is FacilityCard { export function isFacilityCard(card: FacilityCardType): card is FacilityCard {
return "heading" in card return "heading" in card
} }