import { produce } from "immer" import { useContext } from "react" import { create, useStore } from "zustand" import { HotelListingDataContext } from "@/contexts/HotelListingData" import { trackFilterChangeEvent, trackSortingChangeEvent, } from "@/utils/tracking/destinationPage" import { getFilteredHotels, getSortedHotels, isValidSortOption } from "./helper" import type { HotelFilter } from "@scandic-hotels/trpc/types/hotel" import type { HotelListingDataState, InitialState, } from "@/types/stores/hotel-listing-data" export function createHotelListingDataStore({ allHotels, allFilters, sortItems, searchParams, }: InitialState) { const defaultSort = sortItems.find((s) => s.isDefault)?.value ?? sortItems[0].value const allFilterSlugs = Object.values(allFilters).flatMap( (filter: HotelFilter[]) => filter.map((f) => f.slug) ) const activeFilters: string[] = [] let activeSort = defaultSort if (searchParams) { const sortParam = searchParams.get("sort") const filterParam = searchParams.get("filter") if (sortParam && isValidSortOption(sortParam, sortItems)) { activeSort = sortParam } if (filterParam) { const filters = filterParam.split(",") filters.forEach((filter) => { if (allFilterSlugs.includes(filter)) { activeFilters.push(filter) } }) } } const filteredHotels = getFilteredHotels(allHotels, activeFilters) const activeHotels = getSortedHotels(filteredHotels, activeSort) return create((set) => ({ actions: { updateActiveFiltersAndSort(filters, sort) { return set( produce((state: HotelListingDataState) => { const newSort = sort && isValidSortOption(sort, state.sortItems) ? sort : state.defaultSort const filteredHotels = getFilteredHotels(state.allHotels, filters) const sortedHotels = getSortedHotels(filteredHotels, newSort) // Tracking if (newSort !== state.activeSort) { trackSortingChangeEvent(newSort) } if ( JSON.stringify(filters) !== JSON.stringify(state.activeFilters) ) { const facilityFiltersUsed = filters.filter((f) => state.allFilters.facilityFilters .map((ff) => ff.slug) .includes(f) ) const surroundingsFiltersUsed = filters.filter((f) => state.allFilters.surroundingsFilters .map((sf) => sf.slug) .includes(f) ) trackFilterChangeEvent( facilityFiltersUsed, surroundingsFiltersUsed ) } state.activeSort = newSort state.activeFilters = filters state.activeHotels = sortedHotels state.pendingFilters = filters state.pendingSort = newSort state.pendingHotelCount = filteredHotels.length state.isLoading = false }) ) }, setIsLoading(isLoading) { return set( produce((state: HotelListingDataState) => { state.isLoading = isLoading }) ) }, setPendingSort(sort) { return set( produce((state: HotelListingDataState) => { state.pendingSort = sort }) ) }, togglePendingFilter(filter) { return set( produce((state: HotelListingDataState) => { const isActive = state.pendingFilters.includes(filter) const filters = isActive ? state.pendingFilters.filter((f) => f !== filter) : [...state.pendingFilters, filter] const pendingHotels = getFilteredHotels(state.allHotels, filters) state.pendingFilters = filters state.pendingHotelCount = pendingHotels.length }) ) }, clearPendingFilters() { return set( produce((state: HotelListingDataState) => { state.pendingFilters = [] state.pendingHotelCount = state.allHotels.length }) ) }, resetPendingValues() { return set( produce((state: HotelListingDataState) => { state.pendingFilters = state.activeFilters state.pendingSort = state.activeSort state.pendingHotelCount = state.activeHotels.length }) ) }, }, allHotels, activeHotels: activeHotels, pendingHotelCount: activeHotels.length, activeSort, pendingSort: activeSort, defaultSort, activeFilters, pendingFilters: activeFilters, allFilters, allFilterSlugs, sortItems, isLoading: false, })) } export function useHotelListingDataStore( selector: (store: HotelListingDataState) => T ) { const store = useContext(HotelListingDataContext) if (!store) { throw new Error( "useHotelListingDataStore must be used within HotelListingDataProvider" ) } return useStore(store, selector) }