feat(BOOK-57): Adjusted metadata for destination pages with active seo filter

Approved-by: Chuma Mcphoy (We Ahead)
This commit is contained in:
Erik Tiekstra
2025-09-25 13:26:00 +00:00
parent 7714761c77
commit 9f02870647
20 changed files with 678 additions and 458 deletions

View File

@@ -0,0 +1,61 @@
import { getIntl } from "@/i18n"
import type { RawMetadataSchema } from "@scandic-hotels/trpc/routers/contentstack/metadata/output"
export async function getDestinationPageTitle(
data: RawMetadataSchema,
pageType: "city" | "country",
suffix: string
) {
const intl = await getIntl()
const { destinationData } = data
if (!destinationData) {
return null
}
const location = destinationData.location
if (!location) {
return null
}
const destinationTitle =
pageType === "country"
? intl.formatMessage(
{
defaultMessage: "Destinations in {location}",
},
{ location }
)
: intl.formatMessage(
{
defaultMessage: "Hotels in {location}",
},
{ location }
)
return `${destinationTitle}${suffix}`
}
export function getDestinationFilterSeoMetaTitle(
data: RawMetadataSchema,
suffix: string
) {
const filter = data.destinationData?.filter
if (!filter) {
return null
}
const foundSeoFilter = data.seo_filters?.find(
(f) => f.filterConnection.edges[0]?.node?.slug === filter
)
if (foundSeoFilter) {
if (foundSeoFilter.seo_metadata?.title) {
return `${foundSeoFilter.seo_metadata.title}${suffix}`
}
return `${foundSeoFilter.heading}${suffix}`
}
return null
}

View File

@@ -0,0 +1,148 @@
import { getIntl } from "@/i18n"
import type { RawMetadataSchema } from "@scandic-hotels/trpc/routers/contentstack/metadata/output"
async function getSubpageTitle(
subpageUrl: string,
additionalHotelData: RawMetadataSchema["additionalHotelData"],
hotelRestaurants: RawMetadataSchema["hotelRestaurants"],
hotelName: string,
destination: string
) {
const intl = await getIntl()
const restaurantSubPage = hotelRestaurants?.find(
(restaurant) => restaurant.nameInUrl === subpageUrl
)
if (restaurantSubPage) {
const restaurantTitleLong = intl.formatMessage(
{
defaultMessage:
"Explore {restaurantName} at {hotelName} in {destination}",
},
{
restaurantName: restaurantSubPage.name,
hotelName,
destination,
}
)
const restaurantTitleShort = intl.formatMessage(
{
defaultMessage: "Explore {restaurantName} at {hotelName}",
},
{
restaurantName: restaurantSubPage.name,
hotelName,
}
)
if (restaurantTitleLong.length > 60) {
return restaurantTitleShort
}
return restaurantTitleLong
}
if (!additionalHotelData) {
return null
}
switch (subpageUrl) {
case additionalHotelData.hotelParking.nameInUrl:
const parkingTitleLong = intl.formatMessage(
{
defaultMessage:
"Parking information for {hotelName} in {destination}",
},
{ hotelName, destination }
)
const parkingTitleShort = intl.formatMessage(
{ defaultMessage: "Parking information for {hotelName}" },
{ hotelName }
)
if (parkingTitleLong.length > 60) {
return parkingTitleShort
}
return parkingTitleLong
case additionalHotelData.healthAndFitness.nameInUrl:
const wellnessTitleLong = intl.formatMessage(
{
defaultMessage:
"Gym & health facilities at {hotelName} in {destination}",
},
{ hotelName, destination }
)
const wellnessTitleShort = intl.formatMessage(
{ defaultMessage: "Gym & health facilities at {hotelName}" },
{ hotelName }
)
if (wellnessTitleLong.length > 60) {
return wellnessTitleShort
}
return wellnessTitleLong
case additionalHotelData.hotelSpecialNeeds.nameInUrl:
const accessibilityTitleLong = intl.formatMessage(
{
defaultMessage:
"Accessibility information for {hotelName} in {destination}",
},
{ hotelName, destination }
)
const accessibilityTitleShort = intl.formatMessage(
{ defaultMessage: "Accessibility information for {hotelName}" },
{ hotelName }
)
if (accessibilityTitleLong.length > 60) {
return accessibilityTitleShort
}
return accessibilityTitleLong
case additionalHotelData.meetingRooms.nameInUrl:
const meetingsTitleLong = intl.formatMessage(
{
defaultMessage:
"Meetings & conferences at {hotelName} in {destination}",
},
{ hotelName, destination }
)
const meetingsTitleShort = intl.formatMessage(
{ defaultMessage: "Meetings & conferences at {hotelName}" },
{ hotelName }
)
if (meetingsTitleLong.length > 60) {
return meetingsTitleShort
}
return meetingsTitleLong
default:
return null
}
}
export async function getHotelPageTitle(data: RawMetadataSchema) {
const intl = await getIntl()
const { subpageUrl, hotelData, additionalHotelData, hotelRestaurants } = data
if (!hotelData) {
return null
}
const hotelName = hotelData.name
const destination = hotelData.translatedCityName
if (subpageUrl) {
const subpageTitle = await getSubpageTitle(
subpageUrl,
additionalHotelData,
hotelRestaurants,
hotelName,
destination
)
return subpageTitle
}
return intl.formatMessage(
{ defaultMessage: "Stay at {hotelName} | Hotel in {destination}" },
{ hotelName, destination }
)
}

View File

@@ -0,0 +1,79 @@
import { PageContentTypeEnum } from "@scandic-hotels/trpc/enums/contentType"
import {
getDestinationFilterSeoMetaTitle,
getDestinationPageTitle,
} from "./destinationPage"
import { getHotelPageTitle } from "./hotelPage"
import type { RawMetadataSchema } from "@scandic-hotels/trpc/routers/contentstack/metadata/output"
function getTitleSuffix(contentType: string) {
switch (contentType) {
case PageContentTypeEnum.contentPage:
case PageContentTypeEnum.collectionPage:
case PageContentTypeEnum.campaignPage:
case PageContentTypeEnum.campaignOverviewPage:
case PageContentTypeEnum.destinationOverviewPage:
case PageContentTypeEnum.destinationCityPage:
case PageContentTypeEnum.destinationCountryPage:
return " | Scandic Hotels"
default:
return ""
}
}
export async function getTitle(data: RawMetadataSchema) {
const suffix = getTitleSuffix(data.system.content_type_uid)
const metadata = data.web?.seo_metadata
const isDestinationPage = [
PageContentTypeEnum.destinationCityPage,
PageContentTypeEnum.destinationCountryPage,
].includes(data.system.content_type_uid as PageContentTypeEnum)
if (isDestinationPage) {
const destinationFilterSeoMetaTitle = getDestinationFilterSeoMetaTitle(
data,
suffix
)
if (destinationFilterSeoMetaTitle) {
return destinationFilterSeoMetaTitle
}
}
if (metadata?.title) {
return `${metadata.title}${suffix}`
}
let title: string | null = null
switch (data.system.content_type_uid) {
case PageContentTypeEnum.hotelPage:
title = await getHotelPageTitle(data)
break
case PageContentTypeEnum.destinationCityPage:
title = await getDestinationPageTitle(data, "city", suffix)
break
case PageContentTypeEnum.destinationCountryPage:
title = await getDestinationPageTitle(data, "country", suffix)
break
default:
break
}
if (title) {
return title
}
// Fallback titles from contentstack content
if (data.web?.breadcrumbs?.title) {
return `${data.web.breadcrumbs.title}${suffix}`
}
if (data.heading) {
return `${data.heading}${suffix}`
}
if (data.header?.heading) {
return `${data.header.heading}${suffix}`
}
return ""
}