import { produce } from "immer" import { useContext } from "react" import { create, useStore } from "zustand" import { getSortedCities } from "@scandic-hotels/trpc/utils/getSortedCities" import { mergeHotelFiltersAndSeoFilters } from "@scandic-hotels/trpc/utils/mergeHotelFiltersAndSeoFilters" import { DestinationDataContext } from "@/contexts/DestinationData" import { trackFilterChangeEvent, trackSortingChangeEvent, } from "@/utils/tracking/destinationPage" import { getActiveDestinationFilter, 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 { DestinationDataState, InitialState, } from "@/types/stores/destination-data" export function createDestinationDataStore({ allCities, allHotels, hotelFilters, seoFilters, pathname, sortItems, searchParams, }: 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() const allFilterSlugs = allFlattenedFilters.map((filter) => filter.slug) const activeFilters: HotelFilter[] = [] let activeSeoFilter: DestinationFilter | null = null let filterFromUrl: HotelFilter | null = null const basePathnameWithoutFilters = getBasePathNameWithoutFilters( pathname, allFilterSlugs ) if (basePathnameWithoutFilters !== pathname) { const pathParts = pathname.split("/") filterFromUrl = allFlattenedFilters.find( (filter) => filter.slug === pathParts[pathParts.length - 1] ) ?? null if (filterFromUrl) { activeFilters.push(filterFromUrl) activeSeoFilter = getActiveDestinationFilter(filterFromUrl, allSeoFilters) } } let activeSort = defaultSort if (searchParams) { const sortParam = searchParams.get("sort") if (sortParam && isValidSortOption(sortParam, sortItems)) { activeSort = sortParam } } const filteredHotels = getFilteredHotels(allHotels, activeFilters) const activeHotels = getSortedHotels(filteredHotels, activeSort) const filteredCities = getFilteredCities(filteredHotels, allCities) const activeCities = getSortedCities(filteredCities, activeSort) const filtersWithCount = getFiltersWithHotelAndCityCount( allFilters, filteredHotels, filteredCities ) return create((set) => ({ actions: { updateActiveFiltersAndSort(filterSlugs, sort, filterSlugFromUrl) { return set( produce((state: DestinationDataState) => { const newSort = sort && isValidSortOption(sort, state.sortItems) ? sort : state.defaultSort const filters = allFlattenedFilters.filter((filter) => filterSlugs.includes(filter.slug) ) const filterFromUrl = allFlattenedFilters.find( (filter) => filter.slug === filterSlugFromUrl ) ?? null const filteredHotels = getFilteredHotels(state.allHotels, filters) const sortedHotels = getSortedHotels(filteredHotels, newSort) const filteredCities = state.allHotels.length ? getFilteredCities(filteredHotels, state.allCities) : [] const sortedCities = getSortedCities(filteredCities, newSort) // Tracking if (newSort !== state.activeSort) { trackSortingChangeEvent(newSort) } if ( JSON.stringify(filters) !== JSON.stringify(state.activeFilters) ) { const facilityFiltersUsed = filters .filter( (f) => !!state.allFilters.facilityFilters.find( (ff) => ff.id === f.id ) ) .map((f) => f.slug) const surroundingsFiltersUsed = filters .filter( (f) => !!state.allFilters.surroundingsFilters.find( (sf) => sf.id === f.id ) ) .map((f) => f.slug) trackFilterChangeEvent( facilityFiltersUsed, surroundingsFiltersUsed ) } state.activeSort = newSort state.activeFilters = filters state.activeHotels = sortedHotels state.activeCities = sortedCities state.filterFromUrl = filterFromUrl state.activeSeoFilter = getActiveDestinationFilter( filterFromUrl, allSeoFilters ) state.pendingFilters = filters state.pendingSort = newSort state.pendingHotelCount = filteredHotels.length state.pendingCityCount = filteredCities.length state.isLoading = false }) ) }, setIsLoading(isLoading) { return set( produce((state: DestinationDataState) => { state.isLoading = isLoading }) ) }, setPendingSort(sort) { return set( produce((state: DestinationDataState) => { state.pendingSort = sort }) ) }, togglePendingFilter(filter) { return set( produce((state: DestinationDataState) => { const filterId = filter.id const isActive = !!state.pendingFilters.find( (pf) => pf.id === filterId ) const filters = isActive ? state.pendingFilters.filter((f) => f.id !== filterId) : [...state.pendingFilters, filter] const pendingHotels = getFilteredHotels(state.allHotels, filters) const pendingCities = state.allHotels.length ? 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 }) ) }, clearPendingFilters() { return set( produce((state: DestinationDataState) => { state.pendingFilters = [] state.pendingHotelCount = state.allHotels.length state.pendingCityCount = state.allCities.length state.filtersWithCount = getFiltersWithHotelAndCityCount( state.allFilters, state.allHotels, state.allCities ) }) ) }, resetPendingValues() { return set( produce((state: DestinationDataState) => { state.pendingFilters = state.activeFilters 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 ) }) ) }, }, allHotels, activeHotels: activeHotels, pendingHotelCount: activeHotels.length, allCities, activeCities: activeCities, pendingCityCount: activeCities.length, activeSort, pendingSort: activeSort, defaultSort, activeFilters, pendingFilters: activeFilters, allFilters, filtersWithCount, activeSeoFilter, filterFromUrl, basePathnameWithoutFilters, sortItems, isLoading: false, })) } export function useDestinationDataStore( selector: (store: DestinationDataState) => T ) { const store = useContext(DestinationDataContext) if (!store) { throw new Error("useHotelDataStore must be used within HotelDataProvider") } return useStore(store, selector) }