Merged in feat/SW-1454-hotel-listing-city-page (pull request #1250)
feat(SW-1454): added hotel listing * feat(SW-1454): added hotel listing Approved-by: Fredrik Thorsson
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
||||
} from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPage.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"
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
getDestinationCityPageRefsSuccessCounter,
|
||||
getDestinationCityPageSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import { generatePageTags } from "./utils"
|
||||
import { generatePageTags, getHotelListData } from "./utils"
|
||||
|
||||
import type {
|
||||
GetDestinationCityPageData,
|
||||
@@ -28,8 +28,8 @@ import type {
|
||||
} from "@/types/trpc/routers/contentstack/destinationCityPage"
|
||||
|
||||
export const destinationCityPageQueryRouter = router({
|
||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||
const { lang, uid } = ctx
|
||||
get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => {
|
||||
const { lang, uid, serviceToken } = ctx
|
||||
|
||||
getDestinationCityPageRefsCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
@@ -128,27 +128,31 @@ export const destinationCityPageQueryRouter = router({
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedDestinationCityPage = destinationCityPageSchema.safeParse(
|
||||
response.data
|
||||
)
|
||||
const validatedResponse = destinationCityPageSchema.safeParse(response.data)
|
||||
|
||||
if (!validatedDestinationCityPage.success) {
|
||||
if (!validatedResponse.success) {
|
||||
getDestinationCityPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedDestinationCityPage.error),
|
||||
error: JSON.stringify(validatedResponse.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCityPage validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedDestinationCityPage.error,
|
||||
error: validatedResponse.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const hotels = await getHotelListData(
|
||||
lang,
|
||||
serviceToken,
|
||||
validatedResponse.data.destinationCityPage.destination_settings.city
|
||||
)
|
||||
|
||||
getDestinationCityPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationCityPage success",
|
||||
@@ -157,6 +161,9 @@ export const destinationCityPageQueryRouter = router({
|
||||
})
|
||||
)
|
||||
|
||||
return validatedDestinationCityPage.data
|
||||
return {
|
||||
...validatedResponse.data,
|
||||
hotels,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||
|
||||
import { getHotel } from "../../hotels/query"
|
||||
import { getHotelIdsByCityIdentifier } from "../../hotels/utils"
|
||||
import { getHotelPageUrl } from "../hotelPage/utils"
|
||||
|
||||
import type { HotelData } from "@/types/hotel"
|
||||
import type { System } from "@/types/requests/system"
|
||||
import type { GetDestinationCityPageRefsSchema } from "@/types/trpc/routers/contentstack/destinationCityPage"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
@@ -29,3 +34,29 @@ export function getConnections({
|
||||
|
||||
return connections
|
||||
}
|
||||
|
||||
export async function getHotelListData(
|
||||
lang: Lang,
|
||||
serviceToken: string,
|
||||
cityIdentifier: string
|
||||
) {
|
||||
const hotelIds = await getHotelIdsByCityIdentifier(
|
||||
cityIdentifier,
|
||||
serviceToken
|
||||
)
|
||||
|
||||
const hotels = await Promise.all(
|
||||
hotelIds.map(async (hotelId) => {
|
||||
const [hotelData, url] = await Promise.all([
|
||||
getHotel({ hotelId, language: lang }, serviceToken),
|
||||
getHotelPageUrl(lang, hotelId),
|
||||
])
|
||||
|
||||
return hotelData ? { ...hotelData, url } : null
|
||||
})
|
||||
)
|
||||
|
||||
return hotels.filter(
|
||||
(hotel): hotel is HotelData & { url: string | null } => !!hotel
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { env } from "@/env/server"
|
||||
import {
|
||||
GetDestinationCountryPage,
|
||||
GetDestinationCountryPageRefs,
|
||||
@@ -10,7 +9,6 @@ import { toApiLang } from "@/server/utils"
|
||||
|
||||
import { generateTag } from "@/utils/generateTag"
|
||||
|
||||
import { getCitiesByCountry } from "../../hotels/utils"
|
||||
import {
|
||||
destinationCountryPageRefsSchema,
|
||||
destinationCountryPageSchema,
|
||||
@@ -23,10 +21,9 @@ import {
|
||||
getDestinationCountryPageRefsSuccessCounter,
|
||||
getDestinationCountryPageSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import { generatePageTags, getCityListDataByCityIdentifier } from "./utils"
|
||||
import { generatePageTags, getCityPages } from "./utils"
|
||||
|
||||
import { ApiCountry } from "@/types/enums/country"
|
||||
import type { RequestOptionsWithOutBody } from "@/types/fetch"
|
||||
import type {
|
||||
GetDestinationCountryPageData,
|
||||
GetDestinationCountryPageRefsSchema,
|
||||
@@ -155,44 +152,10 @@ export const destinationCountryPageQueryRouter = router({
|
||||
return null
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
language: apiLang,
|
||||
})
|
||||
const options: RequestOptionsWithOutBody = {
|
||||
// needs to clear default option as only
|
||||
// cache or next.revalidate is permitted
|
||||
cache: undefined,
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
next: {
|
||||
revalidate: env.CACHE_TIME_HOTELS,
|
||||
},
|
||||
}
|
||||
const selectedCountry =
|
||||
validatedResponse.data.destinationCountryPage.destination_settings.country
|
||||
const apiCountry = ApiCountry[lang][selectedCountry]
|
||||
const cities = await getCitiesByCountry(
|
||||
[apiCountry],
|
||||
options,
|
||||
params,
|
||||
const cities = await getCityPages(
|
||||
lang,
|
||||
true,
|
||||
"destinationCountryPage"
|
||||
)
|
||||
|
||||
const cityPages = await Promise.all(
|
||||
cities[apiCountry].map(async (city) => {
|
||||
if (!city.cityIdentifier) {
|
||||
return null
|
||||
}
|
||||
|
||||
const data = await getCityListDataByCityIdentifier(
|
||||
lang,
|
||||
city.cityIdentifier
|
||||
)
|
||||
return data ? { ...data, cityName: city.name } : null
|
||||
})
|
||||
serviceToken,
|
||||
validatedResponse.data.destinationCountryPage.destination_settings.country
|
||||
)
|
||||
|
||||
getDestinationCountryPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
@@ -205,10 +168,12 @@ export const destinationCountryPageQueryRouter = router({
|
||||
|
||||
return {
|
||||
...validatedResponse.data,
|
||||
translatedCountry: apiCountry,
|
||||
cities: cityPages
|
||||
.flat()
|
||||
.filter((city): city is NonNullable<typeof city> => !!city),
|
||||
translatedCountry:
|
||||
ApiCountry[lang][
|
||||
validatedResponse.data.destinationCountryPage.destination_settings
|
||||
.country
|
||||
],
|
||||
cities,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { env } from "@/env/server"
|
||||
import { GetDestinationCityListData } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityListData.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { toApiLang } from "@/server/utils"
|
||||
|
||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||
|
||||
import { getCitiesByCountry } from "../../hotels/utils"
|
||||
import { destinationCityListDataSchema } from "../destinationCityPage/output"
|
||||
import {
|
||||
getCityListDataCounter,
|
||||
@@ -10,6 +13,8 @@ import {
|
||||
getCityListDataSuccessCounter,
|
||||
} from "./telemetry"
|
||||
|
||||
import { ApiCountry, type Country } from "@/types/enums/country"
|
||||
import type { RequestOptionsWithOutBody } from "@/types/fetch"
|
||||
import type { System } from "@/types/requests/system"
|
||||
import type { GetDestinationCityListDataResponse } from "@/types/trpc/routers/contentstack/destinationCityPage"
|
||||
import type { GetDestinationCountryPageRefsSchema } from "@/types/trpc/routers/contentstack/destinationCountryPage"
|
||||
@@ -108,3 +113,47 @@ export async function getCityListDataByCityIdentifier(
|
||||
|
||||
return validatedResponse.data
|
||||
}
|
||||
|
||||
export async function getCityPages(
|
||||
lang: Lang,
|
||||
serviceToken: string,
|
||||
country: Country
|
||||
) {
|
||||
const apiLang = toApiLang(lang)
|
||||
const params = new URLSearchParams({
|
||||
language: apiLang,
|
||||
})
|
||||
const options: RequestOptionsWithOutBody = {
|
||||
// needs to clear default option as only
|
||||
// cache or next.revalidate is permitted
|
||||
cache: undefined,
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
next: {
|
||||
revalidate: env.CACHE_TIME_HOTELS,
|
||||
},
|
||||
}
|
||||
const apiCountry = ApiCountry[lang][country]
|
||||
const cities = await getCitiesByCountry([apiCountry], options, params, lang)
|
||||
|
||||
const publishedCities = cities[apiCountry].filter((city) => city.isPublished)
|
||||
|
||||
const cityPages = await Promise.all(
|
||||
publishedCities.map(async (city) => {
|
||||
if (!city.cityIdentifier) {
|
||||
return null
|
||||
}
|
||||
|
||||
const data = await getCityListDataByCityIdentifier(
|
||||
lang,
|
||||
city.cityIdentifier
|
||||
)
|
||||
return data ? { ...data, cityName: city.name } : null
|
||||
})
|
||||
)
|
||||
|
||||
return cityPages
|
||||
.flat()
|
||||
.filter((city): city is NonNullable<typeof city> => !!city)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user