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

@@ -1,10 +1,13 @@
import {
type HotelFilter,
type HotelListingHotelData,
type HotelSortItem,
HotelSortOption,
} from "@scandic-hotels/trpc/types/hotel"
import type {
HotelFilter,
HotelFilters,
} from "@scandic-hotels/trpc/routers/hotels/filters/output"
import type { DestinationCityListItem } from "@scandic-hotels/trpc/types/destinationCityPage"
import type { DestinationFilter } from "@scandic-hotels/trpc/types/destinationsData"
@@ -32,7 +35,9 @@ export function getFilteredHotels(
if (filters.length) {
return hotels.filter(({ hotel }) =>
filters.every((filter) =>
hotel.detailedFacilities.some((facility) => facility.id === filter.id)
hotel.detailedFacilities.some(
(facility) => facility.id === Number(filter.id)
)
)
)
}
@@ -96,3 +101,81 @@ export function getActiveDestinationFilter(
}
return allSeoFilters.find((f) => f.filter.id === filterFromUrl.id) || null
}
export function getFiltersWithHotelAndCityCount(
hotelFilters: HotelFilters,
hotels: HotelListingHotelData[],
cities: DestinationCityListItem[]
): HotelFilters {
const flattenedFilters = Object.values(hotelFilters).flat()
const filterHotelCounts: Record<string, number> = {}
const filterCityIdentifiers: Record<string, Set<string>> = {}
hotels.forEach(({ hotel }) => {
hotel.detailedFacilities.forEach((facility) => {
filterHotelCounts[facility.id] = (filterHotelCounts[facility.id] || 0) + 1
if (!filterCityIdentifiers[facility.id]) {
filterCityIdentifiers[facility.id] = new Set()
}
if (hotel.cityIdentifier) {
filterCityIdentifiers[facility.id].add(hotel.cityIdentifier)
}
})
if (hotel.countryCode) {
filterHotelCounts[hotel.countryCode] =
(filterHotelCounts[hotel.countryCode] || 0) + 1
if (!filterCityIdentifiers[hotel.countryCode]) {
filterCityIdentifiers[hotel.countryCode] = new Set()
}
if (hotel.cityIdentifier) {
filterCityIdentifiers[hotel.countryCode].add(hotel.cityIdentifier)
}
}
})
// Count cities that match the cityIdentifiers for each filter
const filterCityCounts: Record<string, number> = {}
Object.entries(filterCityIdentifiers).forEach(
([filterId, cityIdentifiers]) => {
filterCityCounts[filterId] = cities.filter(
(city) =>
city.destination_settings.city &&
cityIdentifiers.has(city.destination_settings.city)
).length
}
)
return flattenedFilters.reduce(
(acc, filter) => {
if (filter.filterType === "facility") {
acc.facilityFilters.push({
...filter,
hotelCount: filterHotelCounts[filter.id] ?? 0,
cityCount: filterCityCounts[filter.id] ?? 0,
})
} else if (filter.filterType === "surroundings") {
acc.surroundingsFilters.push({
...filter,
hotelCount: filterHotelCounts[filter.id] ?? 0,
cityCount: filterCityCounts[filter.id] ?? 0,
})
} else if (filter.filterType === "country") {
acc.countryFilters.push({
...filter,
filterType: "country",
hotelCount: filterHotelCounts[filter.id] ?? 0,
cityCount: filterCityCounts[filter.id] ?? 0,
})
}
return acc
},
{
facilityFilters: [],
surroundingsFilters: [],
countryFilters: [],
} as HotelFilters
)
}

View File

@@ -2,8 +2,8 @@ import { produce } from "immer"
import { useContext } from "react"
import { create, useStore } from "zustand"
import { mergeHotelFiltersAndSeoFilters } from "@scandic-hotels/trpc/utils/getFiltersFromHotels"
import { getSortedCities } from "@scandic-hotels/trpc/utils/getSortedCities"
import { mergeHotelFiltersAndSeoFilters } from "@scandic-hotels/trpc/utils/mergeHotelFiltersAndSeoFilters"
import { DestinationDataContext } from "@/contexts/DestinationData"
import {
@@ -16,12 +16,13 @@ import {
getBasePathNameWithoutFilters,
getFilteredCities,
getFilteredHotels,
getFiltersWithHotelAndCityCount,
getSortedHotels,
isValidSortOption,
} from "./helper"
import type { HotelFilter } from "@scandic-hotels/trpc/routers/hotels/filters/output"
import type { DestinationFilter } from "@scandic-hotels/trpc/types/destinationsData"
import type { HotelFilter } from "@scandic-hotels/trpc/types/hotel"
import type {
DestinationDataState,
@@ -39,9 +40,10 @@ export function createDestinationDataStore({
}: InitialState) {
const defaultSort =
sortItems.find((s) => s.isDefault)?.value ?? sortItems[0].value
const allFilters = mergeHotelFiltersAndSeoFilters(hotelFilters, seoFilters)
const allSeoFilters = Object.values(seoFilters).flat()
const allFlattenedFilters = Object.values(allFilters).flat<HotelFilter[]>()
const allFlattenedFilters = Object.values(allFilters).flat()
const allFilterSlugs = allFlattenedFilters.map((filter) => filter.slug)
const activeFilters: HotelFilter[] = []
let activeSeoFilter: DestinationFilter | null = null
@@ -75,6 +77,12 @@ export function createDestinationDataStore({
const filteredCities = getFilteredCities(filteredHotels, allCities)
const activeCities = getSortedCities(filteredCities, activeSort)
const filtersWithCount = getFiltersWithHotelAndCityCount(
allFilters,
filteredHotels,
filteredCities
)
return create<DestinationDataState>((set) => ({
actions: {
updateActiveFiltersAndSort(filterSlugs, sort, filterSlugFromUrl) {
@@ -175,9 +183,16 @@ export function createDestinationDataStore({
? getFilteredCities(pendingHotels, state.allCities)
: []
const pendingFiltersWithCount = getFiltersWithHotelAndCityCount(
state.allFilters,
pendingHotels,
pendingCities
)
state.pendingFilters = filters
state.pendingHotelCount = pendingHotels.length
state.pendingCityCount = pendingCities.length
state.filtersWithCount = pendingFiltersWithCount
})
)
},
@@ -187,6 +202,11 @@ export function createDestinationDataStore({
state.pendingFilters = []
state.pendingHotelCount = state.allHotels.length
state.pendingCityCount = state.allCities.length
state.filtersWithCount = getFiltersWithHotelAndCityCount(
state.allFilters,
state.allHotels,
state.allCities
)
})
)
},
@@ -197,6 +217,19 @@ export function createDestinationDataStore({
state.pendingSort = state.activeSort
state.pendingHotelCount = state.activeHotels.length
state.pendingCityCount = state.activeCities.length
const filteredHotels = getFilteredHotels(
state.allHotels,
state.activeFilters
)
const filteredCities = getFilteredCities(
filteredHotels,
state.allCities
)
state.filtersWithCount = getFiltersWithHotelAndCityCount(
state.allFilters,
filteredHotels,
filteredCities
)
})
)
},
@@ -213,6 +246,7 @@ export function createDestinationDataStore({
activeFilters,
pendingFilters: activeFilters,
allFilters,
filtersWithCount,
activeSeoFilter,
filterFromUrl,
basePathnameWithoutFilters,