From 75c811eb329ab50b7aa165e2a19141095cd5666f Mon Sep 17 00:00:00 2001 From: Erik Tiekstra Date: Mon, 18 Nov 2024 10:31:32 +0100 Subject: [PATCH] feat(SW-201): Added hotel metadata --- i18n/dictionaries/en.json | 5 +- lib/graphql/Query/HotelPage/Metadata.graphql | 19 ++++++++ .../routers/contentstack/metadata/output.ts | 20 ++++++-- server/routers/contentstack/metadata/query.ts | 42 ++++++++++++++-- server/routers/contentstack/metadata/utils.ts | 48 ++++++++++++------- 5 files changed, 106 insertions(+), 28 deletions(-) create mode 100644 lib/graphql/Query/HotelPage/Metadata.graphql diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index bbb4e4770..9f14b7f25 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -509,6 +509,7 @@ "to": "to", "uppercase letter": "uppercase letter", "{amount} out of {total}": "{amount} out of {total}", - "{amount} {currency}": "{amount} {currency}", - "{card} ending with {cardno}": "{card} ending with {cardno}" + "{card} ending with {cardno}": "{card} ending with {cardno}", + "{difference}{amount} {currency}": "{difference}{amount} {currency}", + "Stay at HOTEL_NAME | Hotel in DESTINATION": "Stay at {hotelName} | Hotel in {destination}" } diff --git a/lib/graphql/Query/HotelPage/Metadata.graphql b/lib/graphql/Query/HotelPage/Metadata.graphql new file mode 100644 index 000000000..6daed5c86 --- /dev/null +++ b/lib/graphql/Query/HotelPage/Metadata.graphql @@ -0,0 +1,19 @@ +#import "../../Fragments/Metadata.graphql" +#import "../../Fragments/System.graphql" + +query GetHotelPageMetadata($locale: String!, $uid: String!) { + hotel_page(locale: $locale, uid: $uid) { + hotel_page_id + web { + breadcrumbs { + title + } + seo_metadata { + ...Metadata + } + } + system { + ...System + } + } +} diff --git a/server/routers/contentstack/metadata/output.ts b/server/routers/contentstack/metadata/output.ts index 9474dd3fe..d9850dc97 100644 --- a/server/routers/contentstack/metadata/output.ts +++ b/server/routers/contentstack/metadata/output.ts @@ -1,7 +1,7 @@ import { z } from "zod" import { tempImageVaultAssetSchema } from "../schemas/imageVault" -import { getDescription, getImages, getTitle } from "./utils" +import { getDescription, getImage, getTitle } from "./utils" import type { Metadata } from "next" @@ -71,16 +71,26 @@ export const rawMetadataSchema = z.object({ .nullable(), hero_image: tempImageVaultAssetSchema.nullable(), blocks: metaDataBlocksSchema, + hotel_page_id: z.string().optional().nullable(), + hotelData: z + .object({ + name: z.string(), + city: z.string(), + description: z.string(), + image: z.object({ url: z.string(), alt: z.string() }).nullable(), + }) + .optional() + .nullable(), }) -export const metadataSchema = rawMetadataSchema.transform((data) => { +export const metadataSchema = rawMetadataSchema.transform(async (data) => { const noIndex = !!data.web?.seo_metadata?.noindex const metadata: Metadata = { - title: getTitle(data), - description: getDescription(data), + title: await getTitle(data), + description: await getDescription(data), openGraph: { - images: getImages(data), + images: getImage(data), }, } diff --git a/server/routers/contentstack/metadata/query.ts b/server/routers/contentstack/metadata/query.ts index df239d44a..4e5d78728 100644 --- a/server/routers/contentstack/metadata/query.ts +++ b/server/routers/contentstack/metadata/query.ts @@ -4,13 +4,15 @@ import { cache } from "react" import { GetAccountPageMetadata } from "@/lib/graphql/Query/AccountPage/Metadata.graphql" import { GetCollectionPageMetadata } from "@/lib/graphql/Query/CollectionPage/Metadata.graphql" import { GetContentPageMetadata } from "@/lib/graphql/Query/ContentPage/Metadata.graphql" +import { GetHotelPageMetadata } from "@/lib/graphql/Query/HotelPage/Metadata.graphql" import { GetLoyaltyPageMetadata } from "@/lib/graphql/Query/LoyaltyPage/Metadata.graphql" import { request } from "@/lib/graphql/request" import { notFound } from "@/server/errors/trpc" -import { contentstackExtendedProcedureUID, router } from "@/server/trpc" +import { contentStackUidWithServiceProcedure, router } from "@/server/trpc" import { generateTag } from "@/utils/generateTag" +import { getHotelData } from "../../hotels/query" import { metadataSchema } from "./output" import { affix } from "./utils" @@ -86,10 +88,10 @@ const fetchMetadata = cache(async function fetchMemoizedMetadata( return response.data }) -function getTransformedMetadata(data: unknown) { +async function getTransformedMetadata(data: unknown) { transformMetadataCounter.add(1) console.info("contentstack.metadata transform start") - const validatedMetadata = metadataSchema.safeParse(data) + const validatedMetadata = await metadataSchema.safeParseAsync(data) if (!validatedMetadata.success) { transformMetadataFailCounter.add(1, { @@ -112,7 +114,7 @@ function getTransformedMetadata(data: unknown) { } export const metadataQueryRouter = router({ - get: contentstackExtendedProcedureUID.query(async ({ ctx }) => { + get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => { const variables = { lang: ctx.lang, uid: ctx.uid, @@ -139,6 +141,38 @@ export const metadataQueryRouter = router({ loyalty_page: RawMetadataSchema }>(GetLoyaltyPageMetadata, variables) return getTransformedMetadata(loyaltyPageResponse.loyalty_page) + case PageTypeEnum.hotelPage: + const hotelPageResponse = await fetchMetadata<{ + hotel_page: RawMetadataSchema + }>(GetHotelPageMetadata, variables) + const hotelPageData = hotelPageResponse.hotel_page + const hotelData = hotelPageData.hotel_page_id + ? await getHotelData( + { hotelId: hotelPageData.hotel_page_id, language: ctx.lang }, + ctx.serviceToken + ) + : null + + const rawHotelData = hotelPageData + + if (hotelData?.data.attributes) { + const attributes = hotelData.data.attributes + const images = attributes.gallery?.smallerImages + + rawHotelData.hotelData = { + name: attributes.name, + city: attributes.cityName, + description: attributes.hotelContent.texts.descriptions.short, + image: images?.length + ? { + url: images[0].imageSizes.small, + alt: images[0].metaData.altText, + } + : null, + } + } + + return getTransformedMetadata(rawHotelData) default: return null } diff --git a/server/routers/contentstack/metadata/utils.ts b/server/routers/contentstack/metadata/utils.ts index 3cd9abdd0..0cdb0a886 100644 --- a/server/routers/contentstack/metadata/utils.ts +++ b/server/routers/contentstack/metadata/utils.ts @@ -1,3 +1,5 @@ +import { getIntl } from "@/i18n" + import { RTETypeEnum } from "@/types/rte/enums" import type { RawMetadataSchema } from "@/types/trpc/routers/contentstack/metadata" @@ -58,11 +60,18 @@ function truncateTextAfterLastPeriod( return `${maxLengthText}...` } -export function getTitle(data: RawMetadataSchema) { +export async function getTitle(data: RawMetadataSchema) { + const intl = await getIntl() const metadata = data.web?.seo_metadata if (metadata?.title) { return metadata.title } + if (data.hotelData) { + return intl.formatMessage( + { id: "Stay at HOTEL_NAME | Hotel in DESTINATION" }, + { hotelName: data.hotelData.name, destination: data.hotelData.city } + ) + } if (data.web?.breadcrumbs?.title) { return data.web.breadcrumbs.title } @@ -75,11 +84,15 @@ export function getTitle(data: RawMetadataSchema) { return "" } -export function getDescription(data: RawMetadataSchema) { +export async function getDescription(data: RawMetadataSchema) { + const intl = await getIntl() const metadata = data.web?.seo_metadata if (metadata?.description) { return metadata.description } + if (data.hotelData) { + return data.hotelData.description + } if (data.preamble) { return truncateTextAfterLastPeriod(data.preamble) } @@ -108,22 +121,23 @@ export function getImages(data: RawMetadataSchema) { // Currently we don't have the possibility to get smaller images from ImageVault (2024-11-15) if (metadataImage) { - return [ - { - url: metadataImage.url, - width: metadataImage.dimensions.width, - height: metadataImage.dimensions.height, - }, - ] + return { + url: metadataImage.url, + alt: metadataImage.meta.alt || undefined, + width: metadataImage.dimensions.width, + height: metadataImage.dimensions.height, + } + } + if (data.hotelData?.image) { + return data.hotelData.image } if (heroImage) { - return [ - { - url: heroImage.url, - width: heroImage.dimensions.width, - height: heroImage.dimensions.height, - }, - ] + return { + url: heroImage.url, + alt: heroImage.meta.alt || undefined, + width: heroImage.dimensions.width, + height: heroImage.dimensions.height, + } } - return [] + return undefined }