From 52c99e87670d53c243649ce545f52177adcf0dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20J=C3=A4derberg?= Date: Tue, 29 Apr 2025 06:19:25 +0000 Subject: [PATCH] Merged in fix/SW-2457-destination-page-search-for-countries (pull request #1873) * fix: able to search for countries on destinationpage * fix: filter out only desired types when using autocomplete Approved-by: Linus Flood --- .../components/BookingWidget/Client.tsx | 1 + .../DestinationOverviewPage/JumpTo/index.tsx | 1 + .../FormContent/Search/SearchList/index.tsx | 34 ++++++++++++---- .../FormContent/Search/index.tsx | 3 ++ .../Forms/BookingWidget/FormContent/index.tsx | 1 + .../routers/autocomplete/destinations.ts | 39 +++++++++++++++++-- .../server/routers/autocomplete/schema.ts | 2 +- .../util/filterAndCategorizeAutoComplete.ts | 28 ++++++++++++- apps/scandic-web/types/components/search.ts | 1 + 9 files changed, 95 insertions(+), 15 deletions(-) diff --git a/apps/scandic-web/components/BookingWidget/Client.tsx b/apps/scandic-web/components/BookingWidget/Client.tsx index 8d74c561c..9c6383c61 100644 --- a/apps/scandic-web/components/BookingWidget/Client.tsx +++ b/apps/scandic-web/components/BookingWidget/Client.tsx @@ -57,6 +57,7 @@ export default function BookingWidgetClient({ { lang, query: "", + includeTypes: ["hotels", "cities"], selectedHotelId: params.hotelId, selectedCity: params.city, }, diff --git a/apps/scandic-web/components/ContentType/DestinationPage/DestinationOverviewPage/JumpTo/index.tsx b/apps/scandic-web/components/ContentType/DestinationPage/DestinationOverviewPage/JumpTo/index.tsx index 969021017..1e98210f9 100644 --- a/apps/scandic-web/components/ContentType/DestinationPage/DestinationOverviewPage/JumpTo/index.tsx +++ b/apps/scandic-web/components/ContentType/DestinationPage/DestinationOverviewPage/JumpTo/index.tsx @@ -31,6 +31,7 @@ export function JumpTo() { return ( { void 0 diff --git a/apps/scandic-web/components/Forms/BookingWidget/FormContent/Search/SearchList/index.tsx b/apps/scandic-web/components/Forms/BookingWidget/FormContent/Search/SearchList/index.tsx index 073ab2edf..e7931befb 100644 --- a/apps/scandic-web/components/Forms/BookingWidget/FormContent/Search/SearchList/index.tsx +++ b/apps/scandic-web/components/Forms/BookingWidget/FormContent/Search/SearchList/index.tsx @@ -31,6 +31,7 @@ export default function SearchList({ isOpen, search, searchHistory, + includeTypes, }: SearchListProps) { const lang = useLang() const intl = useIntl() @@ -53,10 +54,14 @@ export default function SearchList({ isPending, isError, } = trpc.autocomplete.destinations.useQuery( - { query: debouncedSearch, lang }, + { query: debouncedSearch, lang, includeTypes }, { enabled: autocompleteQueryEnabled } ) + const typeFilteredSearchHistory = searchHistory?.filter((item) => { + return includeTypes.includes(item.type) + }) + useEffect(() => { clearErrors(searchInputName) }, [search, clearErrors, searchInputName]) @@ -159,7 +164,7 @@ export default function SearchList({ "We couldn't find a matching location for your search.", })} - {searchHistory && searchHistory.length > 0 && ( + {typeFilteredSearchHistory && typeFilteredSearchHistory.length > 0 && ( <> @@ -182,7 +187,7 @@ export default function SearchList({ getItemProps={getItemProps} handleClearSearchHistory={handleClearSearchHistory} highlightedIndex={highlightedIndex} - index={searchHistory.length} + index={typeFilteredSearchHistory.length} /> )} @@ -190,7 +195,8 @@ export default function SearchList({ ) } - const displaySearchHistory = !debouncedSearch && searchHistory?.length + const displaySearchHistory = + !debouncedSearch && typeFilteredSearchHistory?.length if (displaySearchHistory) { return ( @@ -202,14 +208,14 @@ export default function SearchList({ ) @@ -224,6 +230,15 @@ export default function SearchList({ + )} diff --git a/apps/scandic-web/components/Forms/BookingWidget/FormContent/index.tsx b/apps/scandic-web/components/Forms/BookingWidget/FormContent/index.tsx index bb0f4fb53..f9578cce6 100644 --- a/apps/scandic-web/components/Forms/BookingWidget/FormContent/index.tsx +++ b/apps/scandic-web/components/Forms/BookingWidget/FormContent/index.tsx @@ -45,6 +45,7 @@ export default function FormContent({ handlePressEnter={onSubmit} selectOnBlur={true} inputName="search" + includeTypes={["cities", "hotels"]} />
diff --git a/apps/scandic-web/server/routers/autocomplete/destinations.ts b/apps/scandic-web/server/routers/autocomplete/destinations.ts index 7a116dc9f..5cc3c8117 100644 --- a/apps/scandic-web/server/routers/autocomplete/destinations.ts +++ b/apps/scandic-web/server/routers/autocomplete/destinations.ts @@ -8,6 +8,7 @@ import { getCacheClient } from "@/services/dataCache" import { safeTry } from "@/utils/safeTry" import { getCityPageUrls } from "../contentstack/destinationCityPage/utils" +import { getCountryPageUrls } from "../contentstack/destinationCountryPage/utils" import { getHotelPageUrls } from "../contentstack/hotelPage/utils" import { getCitiesByCountry, getCountries, getLocations } from "../hotels/utils" import { filterAndCategorizeAutoComplete } from "./util/filterAndCategorizeAutoComplete" @@ -20,12 +21,14 @@ const destinationsAutoCompleteInputSchema = z.object({ selectedHotelId: z.string().optional(), selectedCity: z.string().optional(), lang: z.nativeEnum(Lang), + includeTypes: z.array(z.enum(["hotels", "cities", "countries"])), }) type DestinationsAutoCompleteOutput = { hits: { hotels: AutoCompleteLocation[] cities: AutoCompleteLocation[] + countries: AutoCompleteLocation[] } currentSelection: { hotel: (AutoCompleteLocation & { type: "hotels" }) | null @@ -39,12 +42,13 @@ export const getDestinationsAutoCompleteRoute = safeProtectedServiceProcedure const cacheClient = await getCacheClient() const lang = input.lang || ctx.lang + const locations: AutoCompleteLocation[] = await cacheClient.cacheOrGet( `autocomplete:destinations:locations:${lang}`, async () => { const hotelUrlsPromise = safeTry(getHotelPageUrls(lang)) const cityUrlsPromise = safeTry(getCityPageUrls(lang)) - + const countryUrlsPromise = safeTry(getCountryPageUrls(lang)) const countries = await getCountries({ lang: lang, serviceToken: ctx.serviceToken, @@ -53,6 +57,7 @@ export const getDestinationsAutoCompleteRoute = safeProtectedServiceProcedure if (!countries) { throw new Error("Unable to fetch countries") } + const countryNames = countries.data.map((country) => country.name) const citiesByCountry = await getCitiesByCountry({ countries: countryNames, @@ -68,12 +73,20 @@ export const getDestinationsAutoCompleteRoute = safeProtectedServiceProcedure const [hotelUrls, hotelUrlsError] = await hotelUrlsPromise const [cityUrls, cityUrlsError] = await cityUrlsPromise + const [countryUrls, countryUrlsError] = await countryUrlsPromise - if (hotelUrlsError || cityUrlsError || !hotelUrls || !cityUrls) { + if ( + hotelUrlsError || + cityUrlsError || + countryUrlsError || + !hotelUrls || + !cityUrls || + !countryUrls + ) { throw new Error("Unable to fetch location URLs") } - return locations + const hotelsAndCities = locations .map((location) => { let url: string | undefined @@ -96,13 +109,31 @@ export const getDestinationsAutoCompleteRoute = safeProtectedServiceProcedure }) .map(mapLocationToAutoCompleteLocation) .filter(isDefined) + + const countryAutoCompleteLocations = countries.data.map((country) => { + const url = countryUrls.find( + (c) => c.country && c.country === country.name + )?.url + + return { + id: country.id, + name: country.name, + type: "countries", + searchTokens: [country.name], + destination: "", + url, + } satisfies AutoCompleteLocation + }) + + return [...hotelsAndCities, ...countryAutoCompleteLocations] }, "1d" ) const hits = filterAndCategorizeAutoComplete({ - locations, + locations: locations, query: input.query, + includeTypes: input.includeTypes, }) const selectedHotel = locations.find( diff --git a/apps/scandic-web/server/routers/autocomplete/schema.ts b/apps/scandic-web/server/routers/autocomplete/schema.ts index 0d5ffa6e6..a37e3f3aa 100644 --- a/apps/scandic-web/server/routers/autocomplete/schema.ts +++ b/apps/scandic-web/server/routers/autocomplete/schema.ts @@ -3,7 +3,7 @@ import { z } from "zod" export const autoCompleteLocationSchema = z.object({ id: z.string(), name: z.string(), - type: z.enum(["cities", "hotels"]), + type: z.enum(["cities", "hotels", "countries"]), searchTokens: z.array(z.string()), destination: z.string(), url: z.string().optional(), diff --git a/apps/scandic-web/server/routers/autocomplete/util/filterAndCategorizeAutoComplete.ts b/apps/scandic-web/server/routers/autocomplete/util/filterAndCategorizeAutoComplete.ts index a317ee6be..0d3bc90fc 100644 --- a/apps/scandic-web/server/routers/autocomplete/util/filterAndCategorizeAutoComplete.ts +++ b/apps/scandic-web/server/routers/autocomplete/util/filterAndCategorizeAutoComplete.ts @@ -6,6 +6,7 @@ export type DestinationsAutoCompleteOutput = { hits: { hotels: AutoCompleteLocation[] cities: AutoCompleteLocation[] + countries: AutoCompleteLocation[] } currentSelection: { hotel: (AutoCompleteLocation & { type: "hotels" }) | null @@ -16,27 +17,50 @@ export type DestinationsAutoCompleteOutput = { export function filterAndCategorizeAutoComplete({ locations, query, + includeTypes, }: { locations: AutoCompleteLocation[] query: string + includeTypes: ("cities" | "hotels" | "countries")[] }) { const rankedLocations = filterAutoCompleteLocations(locations, query) - const sortedCities = rankedLocations.filter(isCity) - const sortedHotels = rankedLocations.filter(isHotel) + const sortedCities = rankedLocations.filter( + (loc) => shouldIncludeType(includeTypes, loc) && isCity(loc) + ) + const sortedHotels = rankedLocations.filter( + (loc) => shouldIncludeType(includeTypes, loc) && isHotel(loc) + ) + const sortedCountries = rankedLocations.filter( + (loc) => shouldIncludeType(includeTypes, loc) && isCountry(loc) + ) return { cities: sortedCities, hotels: sortedHotels, + countries: sortedCountries, } } +function shouldIncludeType( + includedTypes: ("cities" | "hotels" | "countries")[], + location: AutoCompleteLocation +) { + return includedTypes.includes(location.type) +} + function isHotel( location: AutoCompleteLocation | null | undefined ): location is AutoCompleteLocation & { type: "hotels" } { return !!location && location.type === "hotels" } +function isCountry( + location: AutoCompleteLocation | null | undefined +): location is AutoCompleteLocation & { type: "countries" } { + return !!location && location.type === "countries" +} + function isCity( location: AutoCompleteLocation | null | undefined ): location is AutoCompleteLocation & { type: "cities" } { diff --git a/apps/scandic-web/types/components/search.ts b/apps/scandic-web/types/components/search.ts index 36d64b1d6..ba9ce99e0 100644 --- a/apps/scandic-web/types/components/search.ts +++ b/apps/scandic-web/types/components/search.ts @@ -24,6 +24,7 @@ export interface SearchListProps { searchInputName: string search: string searchHistory: AutoCompleteLocation[] | null + includeTypes: ("cities" | "hotels" | "countries")[] } export interface DialogProps