fix: improve loading on destination overview page
- Only load data from Contentstack - Use static JSON for destination list - Some logic improvements to data handling and types
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@ export const blocksSchema = z.discriminatedUnion("__typename", [
|
||||
|
||||
export const destinationOverviewPageSchema = z.object({
|
||||
destination_overview_page: z.object({
|
||||
title: z.string(),
|
||||
heading: z.string().nullish(),
|
||||
blocks: discriminatedUnionArray(blocksSchema.options),
|
||||
location: mapLocationSchema,
|
||||
system: systemSchema.merge(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Lang } from "@/constants/languages"
|
||||
import {
|
||||
GetDestinationOverviewPage,
|
||||
GetDestinationOverviewPageRefs,
|
||||
@@ -20,6 +21,12 @@ import {
|
||||
} from "../../hotels/utils"
|
||||
import { getCityPageUrls } from "../destinationCityPage/utils"
|
||||
import { getCountryPageUrls } from "../destinationCountryPage/utils"
|
||||
import destinationsDataDa from "./destinations-da.json" with { assert: "json" }
|
||||
import destinationsDataDe from "./destinations-de.json" with { assert: "json" }
|
||||
import destinationsDataEn from "./destinations-en.json" with { assert: "json" }
|
||||
import destinationsDataFi from "./destinations-fi.json" with { assert: "json" }
|
||||
import destinationsDataNo from "./destinations-no.json" with { assert: "json" }
|
||||
import destinationsDataSv from "./destinations-sv.json" with { assert: "json" }
|
||||
import {
|
||||
destinationOverviewPageRefsSchema,
|
||||
destinationOverviewPageSchema,
|
||||
@@ -200,80 +207,123 @@ export const destinationOverviewPageQueryRouter = router({
|
||||
}
|
||||
}),
|
||||
destinations: router({
|
||||
get: serviceProcedure.query(async function ({ ctx }) {
|
||||
const countries = await getCountries({
|
||||
lang: ctx.lang,
|
||||
serviceToken: ctx.serviceToken,
|
||||
})
|
||||
get: serviceProcedure.query(async function ({
|
||||
ctx,
|
||||
}): Promise<DestinationsData> {
|
||||
// For go live we are using static data here, as it rarely changes.
|
||||
// This also improves operational reliance as we are not hammering
|
||||
// a lot of endpoints for a lot of data.
|
||||
// Re-implement once we have better API support and established caching
|
||||
// patterns and mechanisms.
|
||||
|
||||
const countryPages = await getCountryPageUrls(ctx.lang)
|
||||
// NOTE: To update the static data set `useStaticData = false`.
|
||||
// Then go to the "Hotels & Destinations" page and visit every language.
|
||||
// At the time of commit http://localhost:3000/en/destinations.
|
||||
// This will update the JSON file locally, each page load for each language,
|
||||
// if all data loads correctly.
|
||||
// Set back `useStaticData = true` again and test with the updated JSON file.
|
||||
// Add, commit and push the updated JSON files with useStaticData = true here.
|
||||
const useStaticData = true
|
||||
|
||||
if (!countries) {
|
||||
return null
|
||||
if (useStaticData) {
|
||||
switch (ctx.lang) {
|
||||
case Lang.da:
|
||||
return destinationsDataDa
|
||||
case Lang.de:
|
||||
return destinationsDataDe
|
||||
case Lang.fi:
|
||||
return destinationsDataFi
|
||||
case Lang.en:
|
||||
return destinationsDataEn
|
||||
case Lang.no:
|
||||
return destinationsDataNo
|
||||
case Lang.sv:
|
||||
return destinationsDataSv
|
||||
default:
|
||||
return []
|
||||
}
|
||||
} else {
|
||||
return await updateJSONOnDisk()
|
||||
}
|
||||
|
||||
const countryNames = countries.data.map((country) => country.name)
|
||||
async function updateJSONOnDisk() {
|
||||
const { lang } = ctx
|
||||
|
||||
const citiesByCountry = await getCitiesByCountry({
|
||||
lang: ctx.lang,
|
||||
countries: countryNames,
|
||||
serviceToken: ctx.serviceToken,
|
||||
onlyPublished: true,
|
||||
})
|
||||
const countries = await getCountries({
|
||||
lang,
|
||||
serviceToken: ctx.serviceToken,
|
||||
})
|
||||
|
||||
const cityPages = await getCityPageUrls(ctx.lang)
|
||||
if (!countries) {
|
||||
return []
|
||||
}
|
||||
|
||||
const destinations: DestinationsData = await Promise.all(
|
||||
Object.entries(citiesByCountry).map(async ([country, cities]) => {
|
||||
const citiesWithHotelCount = await Promise.all(
|
||||
cities.map(async (city) => {
|
||||
const [hotels] = await safeTry(
|
||||
getHotelIdsByCityId({
|
||||
cityId: city.id,
|
||||
serviceToken: ctx.serviceToken,
|
||||
})
|
||||
)
|
||||
const countryNames = countries.data.map((country) => country.name)
|
||||
|
||||
const cityPage = cityPages.find(
|
||||
(cityPage) => cityPage.city === city.cityIdentifier
|
||||
)
|
||||
const citiesByCountry = await getCitiesByCountry({
|
||||
lang,
|
||||
countries: countryNames,
|
||||
serviceToken: ctx.serviceToken,
|
||||
onlyPublished: true,
|
||||
})
|
||||
|
||||
if (!cityPage) {
|
||||
return null
|
||||
}
|
||||
const cityPages = await getCityPageUrls(lang)
|
||||
|
||||
return {
|
||||
id: city.id,
|
||||
name: city.name,
|
||||
hotelIds: hotels || [],
|
||||
hotelCount: hotels?.length ?? 0,
|
||||
url: cityPage.url,
|
||||
}
|
||||
})
|
||||
)
|
||||
const destinations = await Promise.all(
|
||||
Object.entries(citiesByCountry).map(async ([country, cities]) => {
|
||||
const activeCitiesWithHotelCount: Cities = await Promise.all(
|
||||
cities.map(async (city) => {
|
||||
const [hotels] = await safeTry(
|
||||
getHotelIdsByCityId({
|
||||
cityId: city.id,
|
||||
serviceToken: ctx.serviceToken,
|
||||
})
|
||||
)
|
||||
|
||||
const activeCitiesWithHotelCount: Cities =
|
||||
citiesWithHotelCount.filter(
|
||||
(city): city is Cities[number] => !!city
|
||||
const cityPage = cityPages.find(
|
||||
(cityPage) => cityPage.city === city.cityIdentifier
|
||||
)
|
||||
|
||||
return {
|
||||
id: city.id,
|
||||
name: city.name,
|
||||
hotelIds: hotels || [],
|
||||
hotelCount: hotels ? hotels.length : 0,
|
||||
url: cityPage?.url,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const countryPage = countryPages.find(
|
||||
(countryPage) => countryPage.country === country
|
||||
)
|
||||
const countryPages = await getCountryPageUrls(lang)
|
||||
const countryPage = countryPages.find(
|
||||
(countryPage) => countryPage.country === country
|
||||
)
|
||||
|
||||
return {
|
||||
country,
|
||||
countryUrl: countryPage?.url,
|
||||
numberOfHotels: activeCitiesWithHotelCount.reduce(
|
||||
(acc, city) => acc + city.hotelCount,
|
||||
0
|
||||
),
|
||||
cities: activeCitiesWithHotelCount,
|
||||
return {
|
||||
country,
|
||||
countryUrl: countryPage?.url,
|
||||
numberOfHotels: activeCitiesWithHotelCount.reduce(
|
||||
(acc, city) => acc + city.hotelCount,
|
||||
0
|
||||
),
|
||||
cities: activeCitiesWithHotelCount,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const data = destinations.sort((a, b) =>
|
||||
a.country.localeCompare(b.country)
|
||||
)
|
||||
const fs = await import("node:fs")
|
||||
fs.writeFileSync(
|
||||
`./server/routers/contentstack/destinationOverviewPage/destinations-${lang}.json`,
|
||||
JSON.stringify(data),
|
||||
{
|
||||
encoding: "utf-8",
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return destinations.sort((a, b) => a.country.localeCompare(b.country))
|
||||
)
|
||||
return data
|
||||
}
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CancellationRuleEnum } from "@/constants/booking"
|
||||
import { Lang } from "@/constants/languages"
|
||||
import { env } from "@/env/server"
|
||||
import * as api from "@/lib/api"
|
||||
import { dt } from "@/lib/dt"
|
||||
@@ -79,7 +80,6 @@ import type {
|
||||
} from "@/types/trpc/routers/hotel/availability"
|
||||
import type { HotelInput } from "@/types/trpc/routers/hotel/hotel"
|
||||
import type { CityLocation } from "@/types/trpc/routers/hotel/locations"
|
||||
import type { Lang } from "@/constants/routes/hotelReservation"
|
||||
|
||||
export const getHotel = cache(
|
||||
async (input: HotelInput, serviceToken: string) => {
|
||||
@@ -1198,7 +1198,9 @@ export const hotelQueryRouter = router({
|
||||
}) {
|
||||
const lang = input?.lang ?? ctx.lang
|
||||
const countries = await getCountries({
|
||||
lang: lang,
|
||||
// Countries need to be in English regardless of incoming lang because
|
||||
// we use the names as input for API endpoints.
|
||||
lang: Lang.en,
|
||||
serviceToken: ctx.serviceToken,
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user