Merged in feat/sw-2863-move-contentstack-router-to-trpc-package (pull request #2389)
feat(SW-2863): Move contentstack router to trpc package * Add exports to packages and lint rule to prevent relative imports * Add env to trpc package * Add eslint to trpc package * Apply lint rules * Use direct imports from trpc package * Add lint-staged config to trpc * Move lang enum to common * Restructure trpc package folder structure * WIP first step * update internal imports in trpc * Fix most errors in scandic-web Just 100 left... * Move Props type out of trpc * Fix CategorizedFilters types * Move more schemas in hotel router * Fix deps * fix getNonContentstackUrls * Fix import error * Fix entry error handling * Fix generateMetadata metrics * Fix alertType enum * Fix duplicated types * lint:fix * Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package * Fix broken imports * Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package Approved-by: Linus Flood
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import type {
|
||||
CategorizedFilters,
|
||||
Filter,
|
||||
} from "../../../types/destinationFilterAndSort"
|
||||
import type { DestinationPagesHotelData } from "../../../types/hotel"
|
||||
|
||||
const HOTEL_SURROUNDINGS_FILTER_TYPE_NAMES = [
|
||||
"Hotel surroundings",
|
||||
"Hotel omgivelser",
|
||||
"Hotelumgebung",
|
||||
"Hotellia lähellä",
|
||||
"Hotellomgivelser",
|
||||
"Omgivningar",
|
||||
]
|
||||
|
||||
const HOTEL_FACILITIES_FILTER_TYPE_NAMES = [
|
||||
"Hotel facilities",
|
||||
"Hotellfaciliteter",
|
||||
"Hotelfaciliteter",
|
||||
"Hotel faciliteter",
|
||||
"Hotel-Infos",
|
||||
"Hotellin palvelut",
|
||||
]
|
||||
|
||||
export function getFiltersFromHotels(
|
||||
hotels: DestinationPagesHotelData[]
|
||||
): CategorizedFilters {
|
||||
if (hotels.length === 0) {
|
||||
return { facilityFilters: [], surroundingsFilters: [] }
|
||||
}
|
||||
|
||||
const filters = hotels.flatMap(({ hotel }) => hotel.detailedFacilities)
|
||||
const uniqueFilterNames = [...new Set(filters.map((filter) => filter.name))]
|
||||
const filterList = uniqueFilterNames
|
||||
.map((filterName) => {
|
||||
const filter = filters.find((filter) => filter.name === filterName)
|
||||
return filter
|
||||
? {
|
||||
name: filter.name,
|
||||
slug: filter.slug,
|
||||
filterType: filter.filter,
|
||||
sortOrder: filter.sortOrder,
|
||||
}
|
||||
: null
|
||||
})
|
||||
.filter((filter): filter is Filter => !!filter)
|
||||
|
||||
const facilityFilters = filterList.filter((filter) =>
|
||||
HOTEL_FACILITIES_FILTER_TYPE_NAMES.includes(filter.filterType)
|
||||
)
|
||||
const surroundingsFilters = filterList.filter((filter) =>
|
||||
HOTEL_SURROUNDINGS_FILTER_TYPE_NAMES.includes(filter.filterType)
|
||||
)
|
||||
return {
|
||||
facilityFilters: sortFilters(facilityFilters),
|
||||
surroundingsFilters: sortFilters(surroundingsFilters),
|
||||
}
|
||||
}
|
||||
|
||||
function sortFilters(filters: Filter[]): Filter[] {
|
||||
return [...filters].sort((a, b) => {
|
||||
// First sort by sortOrder
|
||||
const orderDiff = a.sortOrder - b.sortOrder
|
||||
// If sortOrder is the same, sort by name as secondary criterion
|
||||
return orderDiff === 0 ? a.name.localeCompare(b.name) : orderDiff
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { mergeRouters } from "../../.."
|
||||
import { metadataQueryRouter } from "./query"
|
||||
|
||||
export const metadataRouter = mergeRouters(metadataQueryRouter)
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const getMetadataInput = z.object({
|
||||
subpage: z.string().optional(),
|
||||
filterFromUrl: z.string().optional(),
|
||||
noIndex: z.boolean().default(false),
|
||||
})
|
||||
|
||||
export interface MetadataInputSchema extends z.input<typeof getMetadataInput> {}
|
||||
@@ -0,0 +1,201 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { findMyBooking } from "@scandic-hotels/common/constants/routes/findMyBooking"
|
||||
import { myStay } from "@scandic-hotels/common/constants/routes/myStay"
|
||||
|
||||
import { attributesSchema as hotelAttributesSchema } from "../../../routers/hotels/schemas/hotel"
|
||||
import { Country } from "../../../types/country"
|
||||
import { RTETypeEnum } from "../../../types/RTEenums"
|
||||
import { additionalDataAttributesSchema } from "../../hotels/schemas/hotel/include/additionalData"
|
||||
import { imageSchema } from "../../hotels/schemas/image"
|
||||
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
|
||||
import { systemSchema } from "../schemas/system"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
import type { ImageVaultAsset } from "../../../types/imageVault"
|
||||
import type { LanguageSwitcherData } from "../../../types/languageSwitcher"
|
||||
|
||||
const metaDataJsonSchema = z.object({
|
||||
children: z.array(
|
||||
z.object({
|
||||
type: z.nativeEnum(RTETypeEnum),
|
||||
children: z.array(
|
||||
z.object({
|
||||
text: z.string().optional(),
|
||||
})
|
||||
),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
const metaDataBlocksSchema = z
|
||||
.array(
|
||||
z.object({
|
||||
content: z
|
||||
.object({
|
||||
content: z
|
||||
.object({
|
||||
json: metaDataJsonSchema,
|
||||
})
|
||||
.optional()
|
||||
.nullable(),
|
||||
})
|
||||
.optional()
|
||||
.nullable(),
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
.nullable()
|
||||
|
||||
export const rawMetadataSchema = z.object({
|
||||
web: z
|
||||
.object({
|
||||
seo_metadata: z
|
||||
.object({
|
||||
title: z.string().nullish(),
|
||||
description: z.string().nullish(),
|
||||
noindex: z.boolean().nullish(),
|
||||
seo_image: tempImageVaultAssetSchema.nullable(),
|
||||
})
|
||||
.nullish(),
|
||||
breadcrumbs: z
|
||||
.object({
|
||||
title: z.string().nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
destination_settings: z
|
||||
.object({
|
||||
city_denmark: z.string().nullish(),
|
||||
city_finland: z.string().nullish(),
|
||||
city_germany: z.string().nullish(),
|
||||
city_poland: z.string().nullish(),
|
||||
city_norway: z.string().nullish(),
|
||||
city_sweden: z.string().nullish(),
|
||||
country: z.nativeEnum(Country).nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
heading: z.string().nullish(),
|
||||
preamble: z
|
||||
.union([
|
||||
z.string(),
|
||||
z.object({
|
||||
first_column: z.string(),
|
||||
}),
|
||||
])
|
||||
.transform((preamble) => {
|
||||
if (typeof preamble === "string") {
|
||||
return preamble
|
||||
}
|
||||
|
||||
return preamble?.first_column || null
|
||||
})
|
||||
.nullish(),
|
||||
header: z
|
||||
.object({
|
||||
heading: z.string().nullish(),
|
||||
preamble: z.string().nullish(),
|
||||
hero_image: tempImageVaultAssetSchema.nullable(),
|
||||
})
|
||||
.nullish(),
|
||||
hero_image: tempImageVaultAssetSchema.nullable(),
|
||||
images: z
|
||||
.array(z.object({ image: tempImageVaultAssetSchema }).nullish())
|
||||
.transform((images) =>
|
||||
images
|
||||
.map((image) => image?.image)
|
||||
.filter((image): image is ImageVaultAsset => !!image)
|
||||
)
|
||||
.nullish(),
|
||||
blocks: metaDataBlocksSchema,
|
||||
hotel_page_id: z.string().nullish(),
|
||||
hotelData: hotelAttributesSchema
|
||||
.pick({
|
||||
name: true,
|
||||
address: true,
|
||||
detailedFacilities: true,
|
||||
hotelContent: true,
|
||||
healthFacilities: true,
|
||||
})
|
||||
.nullish(),
|
||||
additionalHotelData: additionalDataAttributesSchema
|
||||
.pick({
|
||||
gallery: true,
|
||||
hotelParking: true,
|
||||
healthAndFitness: true,
|
||||
hotelSpecialNeeds: true,
|
||||
meetingRooms: true,
|
||||
parkingImages: true,
|
||||
accessibility: true,
|
||||
conferencesAndMeetings: true,
|
||||
})
|
||||
.nullish(),
|
||||
hotelRestaurants: z
|
||||
.array(
|
||||
z.object({
|
||||
nameInUrl: z.string().nullish(),
|
||||
elevatorPitch: z.string().nullish(),
|
||||
name: z.string().nullish(),
|
||||
content: z
|
||||
.object({
|
||||
images: z.array(imageSchema).nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
})
|
||||
)
|
||||
.nullish(),
|
||||
subpageUrl: z.string().nullish(),
|
||||
destinationData: z
|
||||
.object({
|
||||
location: z.string().nullish(),
|
||||
filter: z.string().nullish(),
|
||||
filterType: z.enum(["facility", "surroundings"]).nullish(),
|
||||
hotelCount: z.number().nullish(),
|
||||
cities: z.array(z.string()).nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
system: systemSchema,
|
||||
})
|
||||
|
||||
export interface RawMetadataSchema extends z.output<typeof rawMetadataSchema> {}
|
||||
|
||||
// Several pages are not currently routed within contentstack context.
|
||||
// This function is used to generate the urls for these pages.
|
||||
export function getNonContentstackUrls(lang: Lang, pathName: string) {
|
||||
if (Object.values(findMyBooking).includes(pathName)) {
|
||||
const urls: LanguageSwitcherData = {}
|
||||
return Object.entries(findMyBooking).reduce((acc, [lang, url]) => {
|
||||
acc[lang as Lang] = { url }
|
||||
return urls
|
||||
}, urls)
|
||||
}
|
||||
|
||||
if (Object.values(myStay).includes(pathName)) {
|
||||
const urls: LanguageSwitcherData = {}
|
||||
return Object.entries(myStay).reduce((acc, [lang, url]) => {
|
||||
acc[lang as Lang] = { url }
|
||||
return urls
|
||||
}, urls)
|
||||
}
|
||||
|
||||
if (pathName.startsWith(hotelreservation(lang))) {
|
||||
return baseUrls
|
||||
}
|
||||
|
||||
return { [lang]: { url: pathName } }
|
||||
}
|
||||
|
||||
const baseUrls: LanguageSwitcherData = {
|
||||
da: { url: "/da/" },
|
||||
de: { url: "/de/" },
|
||||
en: { url: "/en/" },
|
||||
fi: { url: "/fi/" },
|
||||
no: { url: "/no/" },
|
||||
sv: { url: "/sv/" },
|
||||
}
|
||||
|
||||
function hotelreservation(lang: Lang) {
|
||||
return `/${lang}/hotelreservation`
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
import { cache } from "react"
|
||||
|
||||
import { createCounter } from "@scandic-hotels/common/telemetry"
|
||||
import { notFound } from "@scandic-hotels/trpc/errors"
|
||||
import { contentStackUidWithServiceProcedure } from "@scandic-hotels/trpc/procedures"
|
||||
|
||||
import { router } from "../../.."
|
||||
import { PageContentTypeEnum } from "../../../enums/contentType"
|
||||
import { GetAccountPageMetadata } from "../../../graphql/Query/AccountPage/Metadata.graphql"
|
||||
import { GetCampaignOverviewPageMetadata } from "../../../graphql/Query/CampaignOverviewPage/Metadata.graphql"
|
||||
import { GetCampaignPageMetadata } from "../../../graphql/Query/CampaignPage/Metadata.graphql"
|
||||
import { GetCollectionPageMetadata } from "../../../graphql/Query/CollectionPage/Metadata.graphql"
|
||||
import { GetContentPageMetadata } from "../../../graphql/Query/ContentPage/Metadata.graphql"
|
||||
import { GetDestinationCityPageMetadata } from "../../../graphql/Query/DestinationCityPage/Metadata.graphql"
|
||||
import { GetDestinationCountryPageMetadata } from "../../../graphql/Query/DestinationCountryPage/Metadata.graphql"
|
||||
import { GetDestinationOverviewPageMetadata } from "../../../graphql/Query/DestinationOverviewPage/Metadata.graphql"
|
||||
import { GetHotelPageMetadata } from "../../../graphql/Query/HotelPage/Metadata.graphql"
|
||||
import { GetLoyaltyPageMetadata } from "../../../graphql/Query/LoyaltyPage/Metadata.graphql"
|
||||
import { GetStartPageMetadata } from "../../../graphql/Query/StartPage/Metadata.graphql"
|
||||
import { request } from "../../../graphql/request"
|
||||
import { generateTag } from "../../../utils/generateTag"
|
||||
import { getHotel } from "../../hotels/utils"
|
||||
import { getUrlsOfAllLanguages } from "../languageSwitcher/utils"
|
||||
import { getMetadataInput } from "./input"
|
||||
import { getNonContentstackUrls, rawMetadataSchema } from "./output"
|
||||
import { affix, getCityData, getCountryData } from "./utils"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
import type { LanguageSwitcherData } from "../../../types/languageSwitcher"
|
||||
import type { RawMetadataSchema } from "./output"
|
||||
|
||||
const fetchMetadata = cache(async function fetchMemoizedMetadata<T>(
|
||||
query: string,
|
||||
{ uid, lang }: { uid: string; lang: Lang }
|
||||
) {
|
||||
const getMetadataCounter = createCounter("trpc.contentstack", "metadata.get")
|
||||
const metricsGetMetadata = getMetadataCounter.init({ lang, uid })
|
||||
|
||||
metricsGetMetadata.start()
|
||||
|
||||
const response = await request<T>(
|
||||
query,
|
||||
{ locale: lang, uid },
|
||||
{
|
||||
key: generateTag(lang, uid, affix),
|
||||
ttl: "max",
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
metricsGetMetadata.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
metricsGetMetadata.success()
|
||||
|
||||
return response.data
|
||||
})
|
||||
|
||||
type Alternates = {
|
||||
canonical?: string | null
|
||||
languages?: Record<string, string>
|
||||
}
|
||||
|
||||
type Robots = {
|
||||
index?: boolean
|
||||
follow?: boolean
|
||||
}
|
||||
|
||||
export const metadataQueryRouter = router({
|
||||
get: contentStackUidWithServiceProcedure
|
||||
.input(getMetadataInput)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const variables = {
|
||||
lang: ctx.lang,
|
||||
uid: ctx.uid,
|
||||
}
|
||||
|
||||
let urls: LanguageSwitcherData | null = null
|
||||
if (
|
||||
input.subpage ||
|
||||
input.filterFromUrl ||
|
||||
!ctx.uid ||
|
||||
!ctx.contentType
|
||||
) {
|
||||
urls = getNonContentstackUrls(ctx.lang, `${ctx.lang}/${ctx.pathname}`)
|
||||
} else {
|
||||
urls = await getUrlsOfAllLanguages(ctx.lang, ctx.uid, ctx.contentType)
|
||||
}
|
||||
|
||||
let data: unknown = null
|
||||
|
||||
switch (ctx.contentType) {
|
||||
case PageContentTypeEnum.accountPage:
|
||||
const accountPageResponse = await fetchMetadata<{
|
||||
account_page: RawMetadataSchema
|
||||
}>(GetAccountPageMetadata, variables)
|
||||
data = accountPageResponse.account_page
|
||||
break
|
||||
case PageContentTypeEnum.campaignOverviewPage:
|
||||
const campaignOverviewPageResponse = await fetchMetadata<{
|
||||
campaign_overview_page: RawMetadataSchema
|
||||
}>(GetCampaignOverviewPageMetadata, variables)
|
||||
data = campaignOverviewPageResponse.campaign_overview_page
|
||||
break
|
||||
case PageContentTypeEnum.campaignPage:
|
||||
const campaignPageResponse = await fetchMetadata<{
|
||||
campaign_page: RawMetadataSchema
|
||||
}>(GetCampaignPageMetadata, variables)
|
||||
data = campaignPageResponse.campaign_page
|
||||
break
|
||||
case PageContentTypeEnum.collectionPage:
|
||||
const collectionPageResponse = await fetchMetadata<{
|
||||
collection_page: RawMetadataSchema
|
||||
}>(GetCollectionPageMetadata, variables)
|
||||
data = collectionPageResponse.collection_page
|
||||
break
|
||||
case PageContentTypeEnum.contentPage:
|
||||
const contentPageResponse = await fetchMetadata<{
|
||||
content_page: RawMetadataSchema
|
||||
}>(GetContentPageMetadata, variables)
|
||||
data = contentPageResponse.content_page
|
||||
break
|
||||
case PageContentTypeEnum.destinationOverviewPage:
|
||||
const destinationOverviewPageResponse = await fetchMetadata<{
|
||||
destination_overview_page: RawMetadataSchema
|
||||
}>(GetDestinationOverviewPageMetadata, variables)
|
||||
data = destinationOverviewPageResponse.destination_overview_page
|
||||
break
|
||||
case PageContentTypeEnum.destinationCountryPage:
|
||||
const destinationCountryPageResponse = await fetchMetadata<{
|
||||
destination_country_page: RawMetadataSchema
|
||||
}>(GetDestinationCountryPageMetadata, variables)
|
||||
|
||||
const countryData = await getCountryData(
|
||||
destinationCountryPageResponse.destination_country_page,
|
||||
input,
|
||||
ctx.serviceToken,
|
||||
ctx.lang
|
||||
)
|
||||
data = {
|
||||
...destinationCountryPageResponse.destination_country_page,
|
||||
destinationData: countryData,
|
||||
}
|
||||
break
|
||||
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
|
||||
)
|
||||
data = {
|
||||
...destinationCityPageResponse.destination_city_page,
|
||||
destinationData: cityData,
|
||||
}
|
||||
break
|
||||
case PageContentTypeEnum.loyaltyPage:
|
||||
const loyaltyPageResponse = await fetchMetadata<{
|
||||
loyalty_page: RawMetadataSchema
|
||||
}>(GetLoyaltyPageMetadata, variables)
|
||||
data = loyaltyPageResponse.loyalty_page
|
||||
break
|
||||
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
|
||||
data = {
|
||||
...hotelPageData,
|
||||
...(hotelData
|
||||
? {
|
||||
hotelData: hotelData.hotel,
|
||||
additionalHotelData: hotelData.additionalData,
|
||||
hotelRestaurants: hotelData.restaurants,
|
||||
}
|
||||
: {}),
|
||||
subpageUrl: input.subpage,
|
||||
}
|
||||
break
|
||||
case PageContentTypeEnum.startPage:
|
||||
const startPageResponse = await fetchMetadata<{
|
||||
start_page: RawMetadataSchema
|
||||
}>(GetStartPageMetadata, variables)
|
||||
data = startPageResponse.start_page
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
let alternates: Alternates | null = null
|
||||
let robots: Robots | null = null
|
||||
|
||||
if (urls) {
|
||||
const languages: Record<string, string> = {}
|
||||
Object.entries(urls).forEach(([lang, { url }]) => {
|
||||
languages[lang] = url
|
||||
})
|
||||
const canonical = urls[ctx.lang]?.url
|
||||
alternates = {
|
||||
canonical,
|
||||
languages,
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
alternates = null
|
||||
}
|
||||
|
||||
if (input.noIndex) {
|
||||
robots = {
|
||||
index: false,
|
||||
follow: false,
|
||||
}
|
||||
}
|
||||
|
||||
const transformMetadataCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"metadata.transform"
|
||||
)
|
||||
const metricsTransformMetadata = transformMetadataCounter.init()
|
||||
|
||||
metricsTransformMetadata.start()
|
||||
|
||||
const rawMetadataResult = await rawMetadataSchema.safeParseAsync(data)
|
||||
|
||||
if (!rawMetadataResult.success) {
|
||||
metricsTransformMetadata.validationError(rawMetadataResult.error)
|
||||
return { rawMetadata: null, robots: null, alternates: null }
|
||||
}
|
||||
|
||||
metricsTransformMetadata.success()
|
||||
|
||||
return { rawMetadata: rawMetadataResult.data, robots, alternates }
|
||||
}),
|
||||
})
|
||||
@@ -0,0 +1,136 @@
|
||||
import { SortOption } from "../../../enums/destinationFilterAndSort"
|
||||
import { ApiCountry } from "../../../types/country"
|
||||
import { getSortedCities } from "../../../utils/getSortedCities"
|
||||
import {
|
||||
getCityByCityIdentifier,
|
||||
getHotelIdsByCityIdentifier,
|
||||
getHotelIdsByCountry,
|
||||
getHotelsByHotelIds,
|
||||
} from "../../hotels/utils"
|
||||
import { getCityPages } from "../destinationCountryPage/utils"
|
||||
import { getFiltersFromHotels } from "./helpers"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
import type { MetadataInputSchema } from "./input"
|
||||
import type { RawMetadataSchema } from "./output"
|
||||
|
||||
export const affix = "metadata"
|
||||
|
||||
export async function getCityData(
|
||||
data: RawMetadataSchema,
|
||||
input: MetadataInputSchema,
|
||||
serviceToken: string,
|
||||
lang: Lang
|
||||
) {
|
||||
const destinationSettings = data.destination_settings
|
||||
const filter = input.filterFromUrl
|
||||
|
||||
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))
|
||||
|
||||
const cityIdentifier = cities[0]
|
||||
|
||||
if (cityIdentifier) {
|
||||
const cityData = await getCityByCityIdentifier({
|
||||
cityIdentifier,
|
||||
serviceToken,
|
||||
lang,
|
||||
})
|
||||
const hotelIds = await getHotelIdsByCityIdentifier(
|
||||
cityIdentifier,
|
||||
serviceToken
|
||||
)
|
||||
|
||||
const hotels = await getHotelsByHotelIds({ hotelIds, lang, serviceToken })
|
||||
|
||||
let filterType
|
||||
if (filter) {
|
||||
const allFilters = getFiltersFromHotels(hotels)
|
||||
const facilityFilter = allFilters.facilityFilters.find(
|
||||
(f) => f.slug === filter
|
||||
)
|
||||
const surroudingsFilter = allFilters.surroundingsFilters.find(
|
||||
(f) => f.slug === filter
|
||||
)
|
||||
|
||||
if (facilityFilter) {
|
||||
filterType = "facility"
|
||||
} else if (surroudingsFilter) {
|
||||
filterType = "surroundings"
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
location: cityData?.name,
|
||||
filter,
|
||||
filterType,
|
||||
hotelCount: hotelIds.length,
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export async function getCountryData(
|
||||
data: RawMetadataSchema,
|
||||
input: MetadataInputSchema,
|
||||
serviceToken: string,
|
||||
lang: Lang
|
||||
) {
|
||||
const country = data.destination_settings?.country
|
||||
const filter = input.filterFromUrl
|
||||
|
||||
if (country) {
|
||||
const translatedCountry = ApiCountry[lang][country]
|
||||
let filterType
|
||||
|
||||
const cities = await getCityPages(lang, serviceToken, country)
|
||||
const sortedCities = getSortedCities(cities, SortOption.Recommended)
|
||||
const hotelIds = await getHotelIdsByCountry({
|
||||
country,
|
||||
serviceToken,
|
||||
})
|
||||
|
||||
const hotels = await getHotelsByHotelIds({ hotelIds, lang, serviceToken })
|
||||
|
||||
if (filter) {
|
||||
const allFilters = getFiltersFromHotels(hotels)
|
||||
const facilityFilter = allFilters.facilityFilters.find(
|
||||
(f) => f.slug === filter
|
||||
)
|
||||
const surroudingsFilter = allFilters.surroundingsFilters.find(
|
||||
(f) => f.slug === filter
|
||||
)
|
||||
|
||||
if (facilityFilter) {
|
||||
filterType = "facility"
|
||||
} else if (surroudingsFilter) {
|
||||
filterType = "surroundings"
|
||||
}
|
||||
}
|
||||
return {
|
||||
location: translatedCountry,
|
||||
filter,
|
||||
filterType,
|
||||
cities: sortedCities.slice(0, 2).map(({ cityName }) => cityName),
|
||||
hotelCount: hotelIds.length,
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user