import { z } from "zod" import { Lang } from "@/constants/languages" import { safeProtectedServiceProcedure } from "@/server/trpc" import { getCacheClient } from "@/services/dataCache" import { getCitiesByCountry, getCountries, getLocations } from "../hotels/utils" import { filterLocationByQuery } from "./util/filterLocationByQuery" import { mapLocationToAutoCompleteLocation } from "./util/mapLocationToAutoCompleteLocation" import { sortAutocompleteLocations } from "./util/sortAutocompleteLocations" import type { AutoCompleteLocation } from "./schema" const destinationsAutoCompleteInputSchema = z.object({ query: z.string(), selectedHotelId: z.string().optional(), selectedCity: z.string().optional(), lang: z.nativeEnum(Lang), }) type DestinationsAutoCompleteOutput = { hits: { hotels: AutoCompleteLocation[] cities: AutoCompleteLocation[] } currentSelection: { hotel: (AutoCompleteLocation & { type: "hotels" }) | null city: (AutoCompleteLocation & { type: "cities" }) | null } } export const getDestinationsAutoCompleteRoute = safeProtectedServiceProcedure .input(destinationsAutoCompleteInputSchema) .query(async ({ ctx, input }): Promise => { const cacheClient = await getCacheClient() const locations: AutoCompleteLocation[] = await cacheClient.cacheOrGet( `autocomplete:destinations:locations:${input.lang}`, async () => { const lang = input.lang || ctx.lang const countries = await getCountries({ lang: lang, serviceToken: ctx.serviceToken, }) if (!countries) { throw new Error("Unable to fetch countries") } const countryNames = countries.data.map((country) => country.name) const citiesByCountry = await getCitiesByCountry({ countries: countryNames, serviceToken: ctx.serviceToken, lang, }) const locations = await getLocations({ lang: lang, serviceToken: ctx.serviceToken, citiesByCountry: citiesByCountry, }) return locations .map(mapLocationToAutoCompleteLocation) .filter(isDefined) }, "1d" ) const filteredLocations = locations.filter((location) => filterLocationByQuery({ location, query: input.query }) ) const selectedHotel = locations.find( (location) => location.type === "hotels" && location.id === input.selectedHotelId ) const selectedCity = locations.find( (location) => location.type === "cities" && location.name === input.selectedCity ) const sortedCities = sortAutocompleteLocations( filteredLocations.filter(isCity), input.query ) const sortedHotels = sortAutocompleteLocations( filteredLocations.filter(isHotel), input.query ) return { hits: { cities: sortedCities, hotels: sortedHotels, }, currentSelection: { city: isCity(selectedCity) ? selectedCity : null, hotel: isHotel(selectedHotel) ? selectedHotel : null, }, } }) function isHotel( location: AutoCompleteLocation | null | undefined ): location is AutoCompleteLocation & { type: "hotels" } { return !!location && location.type === "hotels" } function isCity( location: AutoCompleteLocation | null | undefined ): location is AutoCompleteLocation & { type: "cities" } { return !!location && location.type === "cities" } function isDefined( value: AutoCompleteLocation | null | undefined ): value is AutoCompleteLocation { return !!value }