Merged in feat/SW-1452-city-page-filter-2 (pull request #1392)

feat(SW-1452): Added filtering and sorting to destination city pages

* feat(SW-1452): Added filtering and sorting to destination city pages

* feat(SW-1452): Added temporary component for country pages to avoid Context issues


Approved-by: Matilda Landström
This commit is contained in:
Erik Tiekstra
2025-02-25 13:02:38 +00:00
parent 8a564274c5
commit 3867baadd6
53 changed files with 1561 additions and 255 deletions

View File

@@ -0,0 +1,5 @@
import { z } from "zod"
export const getMetadataInput = z.object({
subpage: z.string().optional(),
})

View File

@@ -2,6 +2,7 @@ import { z } from "zod"
import { attributesSchema as hotelAttributesSchema } from "../../hotels/schemas/hotel"
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
import { systemSchema } from "../schemas/system"
import { getDescription, getImage, getTitle } from "./utils"
import type { Metadata } from "next"
@@ -61,6 +62,17 @@ export const rawMetadataSchema = z.object({
})
.optional()
.nullable(),
destination_settings: z
.object({
city_denmark: z.string().optional().nullable(),
city_finland: z.string().optional().nullable(),
city_germany: z.string().optional().nullable(),
city_poland: z.string().optional().nullable(),
city_norway: z.string().optional().nullable(),
city_sweden: z.string().optional().nullable(),
})
.optional()
.nullable(),
heading: z.string().optional().nullable(),
preamble: z.string().optional().nullable(),
header: z
@@ -77,6 +89,10 @@ export const rawMetadataSchema = z.object({
.pick({ name: true, address: true, hotelContent: true, gallery: true })
.optional()
.nullable(),
cityName: z.string().optional().nullable(),
cityFilter: z.string().optional().nullable(),
cityFilterType: z.enum(["facility", "surroundings"]).optional().nullable(),
system: systemSchema,
})
export const metadataSchema = rawMetadataSchema.transform(async (data) => {

View File

@@ -16,8 +16,9 @@ import { contentStackUidWithServiceProcedure, router } from "@/server/trpc"
import { generateTag } from "@/utils/generateTag"
import { getHotel } from "../../hotels/query"
import { getMetadataInput } from "./input"
import { metadataSchema } from "./output"
import { affix } from "./utils"
import { affix, getCityData } from "./utils"
import { PageContentTypeEnum } from "@/types/requests/contentType"
import type { RawMetadataSchema } from "@/types/trpc/routers/contentstack/metadata"
@@ -117,76 +118,85 @@ async function getTransformedMetadata(data: unknown) {
}
export const metadataQueryRouter = router({
get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => {
const variables = {
lang: ctx.lang,
uid: ctx.uid,
}
get: contentStackUidWithServiceProcedure
.input(getMetadataInput)
.query(async ({ ctx, input }) => {
const variables = {
lang: ctx.lang,
uid: ctx.uid,
}
switch (ctx.contentType) {
case PageContentTypeEnum.accountPage:
const accountPageResponse = await fetchMetadata<{
account_page: RawMetadataSchema
}>(GetAccountPageMetadata, variables)
return getTransformedMetadata(accountPageResponse.account_page)
case PageContentTypeEnum.collectionPage:
const collectionPageResponse = await fetchMetadata<{
collection_page: RawMetadataSchema
}>(GetCollectionPageMetadata, variables)
return getTransformedMetadata(collectionPageResponse.collection_page)
case PageContentTypeEnum.contentPage:
const contentPageResponse = await fetchMetadata<{
content_page: RawMetadataSchema
}>(GetContentPageMetadata, variables)
return getTransformedMetadata(contentPageResponse.content_page)
case PageContentTypeEnum.destinationOverviewPage:
const destinationOverviewPageResponse = await fetchMetadata<{
destination_overview_page: RawMetadataSchema
}>(GetDestinationOverviewPageMetadata, variables)
return getTransformedMetadata(
destinationOverviewPageResponse.destination_overview_page
)
case PageContentTypeEnum.destinationCountryPage:
const destinationCountryPageResponse = await fetchMetadata<{
destination_country_page: RawMetadataSchema
}>(GetDestinationCountryPageMetadata, variables)
return getTransformedMetadata(
destinationCountryPageResponse.destination_country_page
)
case PageContentTypeEnum.destinationCityPage:
const destinationCityPageResponse = await fetchMetadata<{
destination_city_page: RawMetadataSchema
}>(GetDestinationCityPageMetadata, variables)
return getTransformedMetadata(
destinationCityPageResponse.destination_city_page
)
case PageContentTypeEnum.loyaltyPage:
const loyaltyPageResponse = await fetchMetadata<{
loyalty_page: RawMetadataSchema
}>(GetLoyaltyPageMetadata, variables)
return getTransformedMetadata(loyaltyPageResponse.loyalty_page)
case PageContentTypeEnum.hotelPage:
const hotelPageResponse = await fetchMetadata<{
hotel_page: RawMetadataSchema
}>(GetHotelPageMetadata, variables)
const hotelPageData = hotelPageResponse.hotel_page
const hotelData = hotelPageData.hotel_page_id
? await getHotel(
{
hotelId: hotelPageData.hotel_page_id,
isCardOnlyPayment: false,
language: ctx.lang,
},
ctx.serviceToken
)
: null
switch (ctx.contentType) {
case PageContentTypeEnum.accountPage:
const accountPageResponse = await fetchMetadata<{
account_page: RawMetadataSchema
}>(GetAccountPageMetadata, variables)
return getTransformedMetadata(accountPageResponse.account_page)
case PageContentTypeEnum.collectionPage:
const collectionPageResponse = await fetchMetadata<{
collection_page: RawMetadataSchema
}>(GetCollectionPageMetadata, variables)
return getTransformedMetadata(collectionPageResponse.collection_page)
case PageContentTypeEnum.contentPage:
const contentPageResponse = await fetchMetadata<{
content_page: RawMetadataSchema
}>(GetContentPageMetadata, variables)
return getTransformedMetadata(contentPageResponse.content_page)
case PageContentTypeEnum.destinationOverviewPage:
const destinationOverviewPageResponse = await fetchMetadata<{
destination_overview_page: RawMetadataSchema
}>(GetDestinationOverviewPageMetadata, variables)
return getTransformedMetadata(
destinationOverviewPageResponse.destination_overview_page
)
case PageContentTypeEnum.destinationCountryPage:
const destinationCountryPageResponse = await fetchMetadata<{
destination_country_page: RawMetadataSchema
}>(GetDestinationCountryPageMetadata, variables)
return getTransformedMetadata(
destinationCountryPageResponse.destination_country_page
)
case PageContentTypeEnum.destinationCityPage:
const destinationCityPageResponse = await fetchMetadata<{
destination_city_page: RawMetadataSchema
}>(GetDestinationCityPageMetadata, variables)
const cityData = await getCityData(
destinationCityPageResponse.destination_city_page,
input,
ctx.serviceToken,
ctx.lang
)
return getTransformedMetadata({
...destinationCityPageResponse.destination_city_page,
...cityData,
})
case PageContentTypeEnum.loyaltyPage:
const loyaltyPageResponse = await fetchMetadata<{
loyalty_page: RawMetadataSchema
}>(GetLoyaltyPageMetadata, variables)
return getTransformedMetadata(loyaltyPageResponse.loyalty_page)
case PageContentTypeEnum.hotelPage:
const hotelPageResponse = await fetchMetadata<{
hotel_page: RawMetadataSchema
}>(GetHotelPageMetadata, variables)
const hotelPageData = hotelPageResponse.hotel_page
const hotelData = hotelPageData.hotel_page_id
? await getHotel(
{
hotelId: hotelPageData.hotel_page_id,
isCardOnlyPayment: false,
language: ctx.lang,
},
ctx.serviceToken
)
: null
return getTransformedMetadata({
...hotelPageData,
hotelData: hotelData?.hotel,
})
default:
return null
}
}),
return getTransformedMetadata({
...hotelPageData,
hotelData: hotelData?.hotel,
})
default:
return null
}
}),
})

View File

@@ -1,7 +1,19 @@
import { getFiltersFromHotels } from "@/stores/hotel-data/helper"
import { getIntl } from "@/i18n"
import {
getCityByCityIdentifier,
getHotelIdsByCityIdentifier,
getHotelsByHotelIds,
} from "../../hotels/utils"
import { RTETypeEnum } from "@/types/rte/enums"
import type { RawMetadataSchema } from "@/types/trpc/routers/contentstack/metadata"
import type {
MetadataInputSchema,
RawMetadataSchema,
} from "@/types/trpc/routers/contentstack/metadata"
import type { Lang } from "@/constants/languages"
export const affix = "metadata"
@@ -75,6 +87,27 @@ export async function getTitle(data: RawMetadataSchema) {
}
)
}
if (data.system.content_type_uid === "destination_city_page") {
if (data.cityName) {
if (data.cityFilter) {
if (data.cityFilterType === "facility") {
return intl.formatMessage(
{ id: "Hotels with {filter} in {cityName}" },
{ cityName: data.cityName, filter: data.cityFilter }
)
} else if (data.cityFilterType === "surroundings") {
return intl.formatMessage(
{ id: "Hotels near {filter} in {cityName}" },
{ cityName: data.cityName, filter: data.cityFilter }
)
}
}
return intl.formatMessage(
{ id: "Hotels in {city}" },
{ city: data.cityName }
)
}
}
if (data.web?.breadcrumbs?.title) {
return data.web.breadcrumbs.title
}
@@ -149,3 +182,64 @@ export function getImage(data: RawMetadataSchema) {
}
return undefined
}
export async function getCityData(
data: RawMetadataSchema,
input: MetadataInputSchema,
serviceToken: string,
lang: Lang
) {
const destinationSettings = data.destination_settings
const cityFilter = input.subpage
let cityIdentifier
let cityData
let filterType
if (destinationSettings) {
const {
city_sweden,
city_norway,
city_denmark,
city_finland,
city_germany,
city_poland,
} = destinationSettings
const cities = [
city_denmark,
city_finland,
city_germany,
city_poland,
city_norway,
city_sweden,
].filter((city): city is string => Boolean(city))
cityIdentifier = cities[0]
if (cityIdentifier) {
cityData = await getCityByCityIdentifier(cityIdentifier, serviceToken)
const hotelIds = await getHotelIdsByCityIdentifier(
cityIdentifier,
serviceToken
)
const hotels = await getHotelsByHotelIds(hotelIds, lang, serviceToken)
if (cityFilter) {
const allFilters = getFiltersFromHotels(hotels)
const facilityFilter = allFilters.facilityFilters.find(
(f) => f.slug === cityFilter
)
const surroudingsFilter = allFilters.surroundingsFilters.find(
(f) => f.slug === cityFilter
)
if (facilityFilter) {
filterType = "facility"
} else if (surroudingsFilter) {
filterType = "surroundings"
}
}
}
return { cityName: cityData?.name, cityFilter, cityFilterType: filterType }
}
return null
}