feat(BOOK-463): Fetching hotel filters from CMS and using these inside the destination pages and select hotel page

* feat(BOOK-463): Fetching hotel filters from CMS and using these inside the destination pages

* fix(BOOK-698): fetch hotel filters from CMS on select hotel page

Approved-by: Bianca Widstam
This commit is contained in:
Erik Tiekstra
2026-01-12 12:02:25 +00:00
parent b2ca2c2612
commit 0c6a4cf186
40 changed files with 732 additions and 399 deletions

View File

@@ -12,7 +12,10 @@ import FilterCheckbox from "./FilterCheckbox"
import styles from "./filterContent.module.css"
import type { CategorizedHotelFilters, HotelFilter } from "../../../../types"
import type {
CategorizedHotelFilters,
SelectHotelFilter,
} from "../../../../types"
interface FilterContentProps {
filters: CategorizedHotelFilters
@@ -75,7 +78,7 @@ export default function FilterContent({
return null
}
function filterOutput(filters: HotelFilter[]) {
function filterOutput(filters: SelectHotelFilter[]) {
return filters.map((filter) => {
const relevantIds = showOnlyBookingCodeRates
? filter.bookingCodeFilteredIds

View File

@@ -1,5 +1,6 @@
import { dt } from "@scandic-hotels/common/dt"
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
import { getHotelFilters } from "@scandic-hotels/trpc/routers/hotels/filters/utils"
import { generateChildrenString } from "@scandic-hotels/trpc/routers/hotels/helpers"
import { serverClient } from "../../trpc"
@@ -18,7 +19,7 @@ import type {
Location,
} from "@scandic-hotels/trpc/types/locations"
import type { CategorizedHotelFilters, HotelFilter } from "../../types"
import type { CategorizedHotelFilters, SelectHotelFilter } from "../../types"
type AvailabilityInput = {
cityId: string
@@ -273,50 +274,38 @@ export async function getHotels({
return hotels
}
const hotelSurroundingsFilterNames = [
"Hotel surroundings",
"Hotel omgivelser",
"Hotelumgebung",
"Hotellia lähellä",
"Hotellomgivelser",
"Omgivningar",
]
const hotelFacilitiesFilterNames = [
"Hotel facilities",
"Hotellfaciliteter",
"Hotelfaciliteter",
"Hotel faciliteter",
"Hotel-Infos",
"Hotellin palvelut",
]
export function getFiltersFromHotels(
export async function fetchHotelFiltersAndMapToCategorizedFilters(
hotels: HotelResponse[],
showBookingCodeFilter: boolean
): CategorizedHotelFilters {
showBookingCodeFilter: boolean,
lang: Lang
): Promise<CategorizedHotelFilters> {
const defaultFilters = { facilityFilters: [], surroundingsFilters: [] }
if (!hotels.length) {
return defaultFilters
}
const { countryFilters, ...hotelFilters } = await getHotelFilters(lang)
const allFlattenedFilters = Object.values(hotelFilters).flat()
const filters = hotels.flatMap(({ hotel, availability }) => {
const hotelFilterData = allFlattenedFilters.map((filter) => {
const hotelHasFilter = hotel.detailedFacilities.some(
(facility) => facility.id.toString() === filter.id
)
return {
...filter,
hotelId: hotelHasFilter ? hotel.operaId : null,
hotelIds: hotelHasFilter ? [hotel.operaId] : [],
bookingCodeFilteredIds:
(availability.bookingCode || !showBookingCodeFilter) && hotelHasFilter
? [hotel.operaId]
: [],
}
})
const filters = hotels.flatMap(({ hotel, availability }) =>
hotel.detailedFacilities.map(
(facility) =>
<HotelFilter>{
...facility,
hotelId: hotel.operaId,
hotelIds: [hotel.operaId],
bookingCodeFilteredIds:
availability.bookingCode || !showBookingCodeFilter
? [hotel.operaId]
: [],
}
)
)
return hotelFilterData
})
const uniqueFilterIds = [...new Set(filters.map((filter) => filter.id))]
const filterList: HotelFilter[] = uniqueFilterIds
const filterList: SelectHotelFilter[] = uniqueFilterIds
.map((filterId) => {
const filter = filters.find((f) => f.id === filterId)
@@ -324,7 +313,9 @@ export function getFiltersFromHotels(
if (filter) {
const matchingFilters = filters.filter((f) => f.id === filterId)
filter.hotelIds = matchingFilters.map((f) => f.hotelId)
filter.hotelIds = matchingFilters
.map((f) => f.hotelId)
.filter((id) => id !== null)
filter.bookingCodeFilteredIds = [
...new Set(
matchingFilters.flatMap((f) => f.bookingCodeFilteredIds ?? [])
@@ -333,18 +324,17 @@ export function getFiltersFromHotels(
}
return filter
})
.filter((filter): filter is HotelFilter => filter !== undefined)
.filter((filter): filter is SelectHotelFilter => filter !== undefined)
.sort((a, b) => b.sortOrder - a.sortOrder)
return filterList.reduce<CategorizedHotelFilters>((filters, filter) => {
if (filter.filter && hotelSurroundingsFilterNames.includes(filter.filter)) {
filters.surroundingsFilters.push(filter)
}
if (filter.filter && hotelFacilitiesFilterNames.includes(filter.filter)) {
filters.facilityFilters.push(filter)
}
return filters
}, defaultFilters)
const facilityFilters = filterList.filter(
(filter) => filter.filterType === "facility"
)
const surroundingsFilters = filterList.filter(
(filter) => filter.filterType === "surroundings"
)
return {
facilityFilters,
surroundingsFilters,
}
}

View File

@@ -9,7 +9,10 @@ import BookingCodeFilter from "../BookingCodeFilter"
import HotelCardListing from "../HotelCardListing"
import { StaticMap } from "../StaticMap"
import HotelFilter from "./Filters/HotelFilter"
import { getFiltersFromHotels, type HotelResponse } from "./helpers"
import {
fetchHotelFiltersAndMapToCategorizedFilters,
type HotelResponse,
} from "./helpers"
import HotelCount from "./HotelCount"
import HotelSorter from "./HotelSorter"
import { MapWithButtonWrapper } from "./MapWithButtonWrapper"
@@ -35,7 +38,7 @@ interface SelectHotelProps {
topSlot?: ReactNode
}
export function SelectHotel({
export async function SelectHotel({
bookingCode,
city,
hotels,
@@ -61,7 +64,11 @@ export function SelectHotel({
const showBookingCodeFilter = isBookingCodeRateAvailable && !isSpecialRate
const filterList = getFiltersFromHotels(hotels, showBookingCodeFilter)
const filterList = await fetchHotelFiltersAndMapToCategorizedFilters(
hotels,
showBookingCodeFilter,
lang
)
return (
<>

View File

@@ -8,7 +8,7 @@ import { env } from "../../env/server"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { MapContainer } from "../components/MapContainer"
import {
getFiltersFromHotels,
fetchHotelFiltersAndMapToCategorizedFilters,
getHotels,
} from "../components/SelectHotel/helpers"
import {
@@ -108,7 +108,11 @@ export async function AlternativeHotelsMapPage({
config,
})
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
const filterList = await fetchHotelFiltersAndMapToCategorizedFilters(
hotels,
isBookingCodeRateAvailable,
lang
)
return (
<BookingFlowConfig config={config}>

View File

@@ -9,7 +9,7 @@ import { env } from "../../env/server"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { MapContainer } from "../components/MapContainer"
import {
getFiltersFromHotels,
fetchHotelFiltersAndMapToCategorizedFilters,
getHotels,
} from "../components/SelectHotel/helpers"
import {
@@ -109,7 +109,11 @@ export async function SelectHotelMapPage({
config,
})
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
const filterList = await fetchHotelFiltersAndMapToCategorizedFilters(
hotels,
isBookingCodeRateAvailable,
lang
)
const suspenseKey = stringify(searchParams)

View File

@@ -1,14 +1,14 @@
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
import type { HotelFilter } from "@scandic-hotels/trpc/routers/hotels/filters/output"
export type NextSearchParams = { [key: string]: string | string[] | undefined }
export type HotelFilter = Hotel["detailedFacilities"][number] & {
export type SelectHotelFilter = HotelFilter & {
hotelId: string
hotelIds: string[]
bookingCodeFilteredIds: string[]
}
export type CategorizedHotelFilters = {
facilityFilters: HotelFilter[]
surroundingsFilters: HotelFilter[]
facilityFilters: SelectHotelFilter[]
surroundingsFilters: SelectHotelFilter[]
}

View File

@@ -6,3 +6,12 @@ export enum Country {
Poland = "Poland",
Sweden = "Sweden",
}
export const CountryCode: Record<Country, string> = {
[Country.Denmark]: "DK",
[Country.Finland]: "FI",
[Country.Germany]: "DE",
[Country.Norway]: "NO",
[Country.Poland]: "PL",
[Country.Sweden]: "SE",
}

View File

@@ -8,8 +8,8 @@ export const HotelFilter = gql`
facility_id
category
slug
sort_order
}
${System}
`
export const HotelFilterRef = gql`

View File

@@ -0,0 +1,14 @@
import { gql } from "graphql-tag"
import { HotelFilter } from "../Fragments/HotelFilter.graphql"
export const GetHotelFilters = gql`
query GetHotelFilters($locale: String!) {
all_hotel_filter(locale: $locale) {
items {
...HotelFilter
}
}
}
${HotelFilter}
`

View File

@@ -1,16 +1,13 @@
import { ApiCountry } from "../../../types/country"
import { HotelSortOption } from "../../../types/hotel"
import {
getFiltersFromHotels,
mergeHotelFiltersAndSeoFilters,
} from "../../../utils/getFiltersFromHotels"
import { getSortedCities } from "../../../utils/getSortedCities"
import { mergeHotelFiltersAndSeoFilters } from "../../../utils/mergeHotelFiltersAndSeoFilters"
import { getHotelFilters } from "../../hotels/filters/utils"
import {
getCityByCityIdentifier,
getHotelIdsByCityIdentifier,
} from "../../hotels/services/getCityByCityIdentifier"
import { getHotelIdsByCountry } from "../../hotels/services/getHotelIdsByCountry"
import { getHotelsByHotelIds } from "../../hotels/services/getHotelsByHotelIds"
import { getCityPages } from "../destinationCountryPage/utils"
import { transformDestinationFiltersResponse } from "../schemas/destinationFilters"
@@ -62,11 +59,9 @@ export async function getCityData(
serviceToken
)
const hotels = await getHotelsByHotelIds({ hotelIds, lang, serviceToken })
let filterType
if (filter) {
const hotelFilters = getFiltersFromHotels(hotels, lang)
const hotelFilters = await getHotelFilters(lang)
const allFilters = mergeHotelFiltersAndSeoFilters(
hotelFilters,
seoFilters
@@ -104,6 +99,7 @@ export async function getCountryData(
lang: Lang
) {
const country = data.destination_settings?.country
const seoFilters = transformDestinationFiltersResponse(data.seo_filters)
const filter = input.filterFromUrl
if (country) {
@@ -117,10 +113,13 @@ export async function getCountryData(
serviceToken,
})
const hotels = await getHotelsByHotelIds({ hotelIds, lang, serviceToken })
if (filter) {
const allFilters = getFiltersFromHotels(hotels, lang)
const hotelFilters = await getHotelFilters(lang)
const allFilters = mergeHotelFiltersAndSeoFilters(
hotelFilters,
seoFilters
)
const facilityFilter = allFilters.facilityFilters.find(
(f) => f.slug === filter
)

View File

@@ -1,10 +1,10 @@
import { z } from "zod"
import { FacilityEnum } from "@scandic-hotels/common/constants/facilities"
import { isDefined } from "@scandic-hotels/common/utils/isDefined"
import { DestinationFilterBlocksEnum } from "../../../types/destinationsData"
import { discriminatedUnionArray } from "../../../utils/discriminatedUnion"
import { hotelFilterSchema } from "../../hotels/filters/output"
import { accordionSchema } from "./blocks/accordion"
import { contentSchema } from "./blocks/content"
import { systemSchema } from "./system"
@@ -37,12 +37,7 @@ export const destinationFilterSchema = z.object({
filterConnection: z.object({
edges: z.array(
z.object({
node: z.object({
title: z.string(),
facility_id: z.nativeEnum(FacilityEnum).catch(FacilityEnum.UNKNOWN),
category: z.string(),
slug: z.string(),
}),
node: hotelFilterSchema,
})
),
}),
@@ -84,13 +79,7 @@ export function transformDestinationFiltersResponse(
heading,
preamble,
blocks,
filter: {
id: filter.facility_id,
name: filter.title,
filterType: filter.category,
slug: filter.slug,
sortOrder: 0,
},
filter,
}
})
.filter(isDefined)

View File

@@ -0,0 +1,6 @@
import { contentstackBaseProcedure } from "../../../procedures"
import { getHotelFilters } from "./utils"
export const get = contentstackBaseProcedure.query(async ({ ctx }) => {
return getHotelFilters(ctx.lang)
})

View File

@@ -0,0 +1,6 @@
import { router } from "../../.."
import { get } from "./get"
export const filtersRouter = router({
get,
})

View File

@@ -0,0 +1,61 @@
import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language"
import { getCountryFilters } from "./utils"
export const FILTER_TYPES = ["surroundings", "facility", "country"] as const
export type FilterType = (typeof FILTER_TYPES)[number]
export const hotelFilterSchema = z
.object({
category: z.enum(FILTER_TYPES),
facility_id: z.number().transform((id) => id.toString()),
slug: z.string(),
title: z.string(),
sort_order: z.number().nullish(),
})
.transform(({ facility_id, title, category, slug, sort_order }) => ({
id: facility_id,
name: title,
filterType: category,
slug,
sortOrder: sort_order ?? 0,
hotelCount: 0,
cityCount: 0,
}))
export const hotelFiltersSchema = z
.object({
all_hotel_filter: z.object({
items: z.array(hotelFilterSchema),
}),
lang: z.nativeEnum(Lang),
})
.transform((data) => {
const filters = data.all_hotel_filter.items
if (!filters?.length) {
return {
facilityFilters: [],
surroundingsFilters: [],
countryFilters: [],
}
}
const facilityFilters = filters.filter(
(filter) => filter.filterType === "facility"
)
const surroundingsFilters = filters.filter(
(filter) => filter.filterType === "surroundings"
)
return {
facilityFilters,
surroundingsFilters,
countryFilters: getCountryFilters(data.lang),
}
})
export type HotelFilters = z.output<typeof hotelFiltersSchema>
export type HotelFilter = HotelFilters[keyof HotelFilters][number]

View File

@@ -0,0 +1,98 @@
import { CountryCode } from "@scandic-hotels/common/constants/country"
import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createCounter } from "@scandic-hotels/common/telemetry"
import { GetHotelFilters } from "../../../graphql/Query/HotelFilters.graphql"
import { request } from "../../../graphql/request"
import { hotelFiltersSchema } from "./output"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { FilterType, HotelFilter } from "./output"
export async function getHotelFilters(lang: Lang) {
const cacheClient = await getCacheClient()
const cacheKey = `${lang}:getHotelFilters`
return await cacheClient.cacheOrGet(
cacheKey,
async () => {
const getHotelFiltersCounter = createCounter(
"trpc.contentstack.hotelFilters.getAll"
)
const metricsGetHotelFilters = getHotelFiltersCounter.init({ lang })
metricsGetHotelFilters.start()
const response = await request<unknown>(
GetHotelFilters,
{ locale: lang },
{ key: `${lang}:hotel_filters`, ttl: "1d" }
)
if (!response.data) {
metricsGetHotelFilters.noDataError()
return {
facilityFilters: [],
surroundingsFilters: [],
countryFilters: [],
}
}
const validatedResponse = hotelFiltersSchema.safeParse({
...response.data,
lang,
})
if (!validatedResponse.success) {
metricsGetHotelFilters.validationError(validatedResponse.error)
return {
facilityFilters: [],
surroundingsFilters: [],
countryFilters: [],
}
}
metricsGetHotelFilters.success()
const { facilityFilters, surroundingsFilters, countryFilters } =
validatedResponse.data
return {
facilityFilters: sortFilters(facilityFilters),
surroundingsFilters: sortFilters(surroundingsFilters),
countryFilters: sortFilters(countryFilters),
}
},
"1d"
)
}
export function getCountryFilters(lang: Lang) {
const countryCodes = Object.values(CountryCode)
const localizedCountries = new Intl.DisplayNames([lang], { type: "region" })
return [...countryCodes]
.map((countryCode) => {
const localizedCountryName = localizedCountries.of(countryCode)
return localizedCountryName
? {
id: countryCode,
name: localizedCountryName,
slug: countryCode.toLowerCase(),
filterType: "country" as FilterType,
sortOrder: 0,
hotelCount: 0,
cityCount: 0,
}
: null
})
.filter((filter) => !!filter)
}
// First sort by sortOrder descending. The higher the sortOrder, the earlier it appears.
// If sortOrder is the same, sort by name as secondary criterion
export function sortFilters(filters: HotelFilter[]) {
return [...filters].sort((a, b) => {
const orderDiff = b.sortOrder - a.sortOrder
return orderDiff === 0 ? a.name.localeCompare(b.name) : orderDiff
})
}

View File

@@ -47,6 +47,7 @@ import { getHotelsByHotelIds } from "./services/getHotelsByHotelIds"
import { getLocationsByCountries } from "./services/getLocationsByCountries"
import { getPackages } from "./services/getPackages"
import { availability } from "./availability"
import { filtersRouter } from "./filters"
import { locationsRouter } from "./locations"
import type { HotelListingHotelData } from "../../types/hotel"
@@ -331,6 +332,7 @@ export const hotelQueryRouter = router({
env.CACHE_TIME_HOTELS
)
}),
filters: filtersRouter,
locations: locationsRouter,
map: router({
city: serviceProcedure

View File

@@ -1,4 +1,3 @@
import type { FacilityEnum } from "@scandic-hotels/common/constants/facilities"
import type { z } from "zod"
import type {
@@ -87,20 +86,6 @@ export type RoomType = Pick<Room, "roomTypes" | "name">
export type RewardNight = z.output<typeof rewardNightSchema>
export interface HotelFilter {
id: FacilityEnum
name: string
slug: string
filterType: string
sortOrder: number
}
export interface CategorizedHotelFilters {
facilityFilters: HotelFilter[]
surroundingsFilters: HotelFilter[]
countryFilters: HotelFilter[]
}
export enum HotelSortOption {
Recommended = "recommended",
Distance = "distance",

View File

@@ -1,150 +0,0 @@
import { logger } from "@scandic-hotels/common/logger"
import type { FacilityEnum } from "@scandic-hotels/common/constants/facilities"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { DestinationFilters } from "../types/destinationsData"
import type {
CategorizedHotelFilters,
HotelFilter,
HotelListingHotelData,
} 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",
]
function sortFilters(filters: HotelFilter[]): HotelFilter[] {
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
})
}
// Merges hotel and SEO filters, removing duplicates (by id).
// In case of duplicates, the SEO filter takes precedence.
function mergeAndDeduplicate(
hotelFilters: HotelFilter[],
seoFilters:
| DestinationFilters["facilityFilters"]
| DestinationFilters["surroundingsFilters"]
): HotelFilter[] {
const map = new Map<FacilityEnum, HotelFilter>()
hotelFilters.forEach((filter) => map.set(filter.id, filter))
seoFilters.forEach(({ filter }) => map.set(filter.id, filter))
return Array.from(map.values())
}
export function mergeHotelFiltersAndSeoFilters(
hotelFilters: CategorizedHotelFilters,
seoFilters: DestinationFilters
): CategorizedHotelFilters {
if (!seoFilters) {
return hotelFilters
}
return {
...hotelFilters,
facilityFilters: mergeAndDeduplicate(
hotelFilters.facilityFilters,
seoFilters.facilityFilters
),
surroundingsFilters: mergeAndDeduplicate(
hotelFilters.surroundingsFilters,
seoFilters.surroundingsFilters
),
}
}
export function getFiltersFromHotels(
hotels: HotelListingHotelData[],
lang: Lang
): CategorizedHotelFilters {
if (hotels.length === 0) {
return { facilityFilters: [], surroundingsFilters: [], countryFilters: [] }
}
const uniqueCountries = new Set(
hotels.flatMap(({ hotel }) => hotel.countryCode)
)
const countryFilters = [...uniqueCountries]
.map((countryCode) => {
const localizedCountry = getLocalizedCountryByCountryCode(
countryCode,
lang
)
return localizedCountry
? {
name: localizedCountry,
slug: countryCode.toLowerCase(),
filterType: "Country",
sortOrder: 0,
}
: null
})
.filter((filter): filter is HotelFilter => !!filter)
const flattenedFacilityFilters = hotels.flatMap(
({ hotel }) => hotel.detailedFacilities
)
const uniqueFacilityFilterNames = [
...new Set(flattenedFacilityFilters.map((filter) => filter.name)),
]
const facilityFilterList = uniqueFacilityFilterNames
.map((filterName) => {
const filter = flattenedFacilityFilters.find(
(filter) => filter.name === filterName
)
return filter
? {
id: filter.id,
name: filter.name,
slug: filter.slug,
filterType: filter.filter,
sortOrder: filter.sortOrder,
}
: null
})
.filter((filter): filter is HotelFilter => !!filter)
const facilityFilters = facilityFilterList.filter((filter) =>
HOTEL_FACILITIES_FILTER_TYPE_NAMES.includes(filter.filterType)
)
const surroundingsFilters = facilityFilterList.filter((filter) =>
HOTEL_SURROUNDINGS_FILTER_TYPE_NAMES.includes(filter.filterType)
)
return {
facilityFilters: sortFilters(facilityFilters),
surroundingsFilters: sortFilters(surroundingsFilters),
countryFilters: sortFilters(countryFilters),
}
}
function getLocalizedCountryByCountryCode(
countryCode: string,
lang: Lang
): string | null {
const country = new Intl.DisplayNames([lang], { type: "region" })
const localizedCountry = country.of(countryCode)
if (!localizedCountry) {
logger.error(`Could not map ${countryCode} to localized country.`)
return null
}
return localizedCountry
}

View File

@@ -0,0 +1,43 @@
// Merges hotel and SEO filters, removing duplicates (by id).
import type {
HotelFilter,
HotelFilters,
} from "../routers/hotels/filters/output"
import type {
DestinationFilter,
DestinationFilters,
} from "../types/destinationsData"
// In case of duplicates, the SEO filter takes precedence.
function mergeAndDeduplicate(
hotelFilters: HotelFilter[],
seoFilters: DestinationFilter[]
): HotelFilters["facilityFilters"] | HotelFilters["surroundingsFilters"] {
const map = new Map<string, HotelFilter>()
hotelFilters.forEach((filter) => map.set(filter.id, filter))
seoFilters.forEach(({ filter }) => map.set(filter.id, filter))
return Array.from(map.values())
}
export function mergeHotelFiltersAndSeoFilters(
hotelFilters: HotelFilters,
seoFilters: DestinationFilters
): HotelFilters {
if (!seoFilters) {
return hotelFilters
}
return {
...hotelFilters,
facilityFilters: mergeAndDeduplicate(
hotelFilters.facilityFilters,
seoFilters.facilityFilters
),
surroundingsFilters: mergeAndDeduplicate(
hotelFilters.surroundingsFilters,
seoFilters.surroundingsFilters
),
countryFilters: hotelFilters.countryFilters,
}
}