import { PageContentTypeEnum } from "@scandic-hotels/trpc/enums/contentType" import { type RawMetadataSchema } from "@scandic-hotels/trpc/routers/contentstack/metadata/output" import { env } from "@/env/server" import { serverClient } from "@/lib/trpc/server" import { getDescription } from "./description" import { getImage } from "./image" import { getTitle } from "./title" import type { Metadata } from "next" import type { AlternateURLs } from "next/dist/lib/metadata/types/alternative-urls-types" export async function generateMetadata({ searchParams, }: { searchParams: Promise<{ subpage?: string; filterFromUrl?: string }> }) { const { subpage, filterFromUrl, ...otherSearchParams } = await searchParams // If there are other (real) search params, we don't want to index the page as this will // cause duplicate content issues. const noIndexOnSearchParams = !!Object.keys(otherSearchParams).length const caller = await serverClient() const { rawMetadata, alternates, robots } = await caller.contentstack.metadata.get({ subpage, filterFromUrl, noIndex: noIndexOnSearchParams, }) if (!rawMetadata) { return { robots: { index: true, follow: true, }, } } const metadata = await getTransformedMetadata(rawMetadata, alternates, robots) if (typeof metadata?.robots === "string") { return metadata } return { ...metadata, robots: { ...metadata.robots, index: isIndexable(metadata.robots?.index, alternates), follow: isIndexable(metadata.robots?.follow, alternates), }, } } function isIndexable( pageIndexableFromSettings: boolean | null | undefined, alternates: AlternateURLs | null ) { // This is a special case for whitelisting the scandic friends pages, this can be removed when all pages are live const url = getUrl(alternates) const firstNonLangSegment = (url ?? "").substring(3) if (firstNonLangSegment.startsWith("/scandic-friends")) { return true } // If we are live we want to index the page, but if the page has been marked as noindex in contentstack we don't return pageIndexableFromSettings ?? true } function getUrl(alternates: AlternateURLs | null): string | null { try { if (!alternates?.canonical) { return null } if (typeof alternates.canonical === "string") { return alternates.canonical } if ("href" in alternates.canonical) { return alternates.canonical.href } if (typeof alternates.canonical.url === "string") { return alternates.canonical.url } return alternates.canonical.url.href } catch { return null } } function isNoIndexFromMetadata(data: RawMetadataSchema) { const isDestinationPage = [ PageContentTypeEnum.destinationCityPage, PageContentTypeEnum.destinationCountryPage, ].includes(data.system.content_type_uid as PageContentTypeEnum) if (isDestinationPage) { const filter = data.destinationData?.filter if (filter) { const foundSeoFilter = data.seo_filters?.find( (f) => f.filterConnection.edges[0]?.node?.slug === filter ) if (foundSeoFilter) { return !!foundSeoFilter.seo_metadata?.noindex } } } return !!data.web?.seo_metadata?.noindex } async function getTransformedMetadata( data: RawMetadataSchema, alternates: Metadata["alternates"] | null, robots: Metadata["robots"] | null = null ) { const metadata: Metadata = { metadataBase: env.PUBLIC_URL ? new URL(env.PUBLIC_URL) : undefined, title: { absolute: await getTitle(data) }, description: await getDescription(data), openGraph: { images: getImage(data), }, alternates, robots, } if (isNoIndexFromMetadata(data)) { metadata.robots = { index: false, follow: false, } } return metadata }