import { create } from 'zustand'; import { persist } from 'zustand/middleware'; // Feed/session sort. '' = backend default order (id DESC on the pending // feed). One choice drives the whole chain: feed table, session pages, // dashboard batch numbering and the "continue auditing" start page - they // must all use the same order or batch N stops matching session page N. export type FeedSortBy = '' | 'id' | 'date' | 'chainage' | 'latlong'; export type FeedSortDir = 'asc' | 'desc'; export interface FeedTab { // '' = pending tab; 'YYYY-MM-DD' or 'YYYY-MM-DD_YYYY-MM-DD' (range) = history tab date: string; isRectificationTab: boolean; // History tabs only: show records audited BY the logged-in user auditedByMe?: boolean; totalAnomalyCount: number; anomalies: any[]; anomaliesCopy: any[]; selectedAnomalyIndex: number; filters: any; page: number; rowsPerPage: number; } // Tracks what filter params were last used to fetch dashboard stats interface DashboardCache { siteId: string; auditStatus: string; fromDate: string; tillDate: string; assetIds: string; minChainage: string; maxChainage: string; searchAnomalyId: string; filterPlaza: string; isRectificationEnabled?: boolean; date?: string; sortBy?: string; sortDir?: string; stats: { globalTotal: number; batchTotal: number; batchAudited: number; batchPending: number; batchAnomaliesFound: number; firstPendingIndex: number; } | null; } interface FeedState { tabs: FeedTab[]; selectedTabIndex: number; assets: any[]; assetTypes: any[]; filterFromDate: string; filterTillDate: string; filterMinChainage: string; filterMaxChainage: string; filterPlaza: string; selectedAssetIds: number[]; sites: any[]; auditStatus: string; selectedSite: any; isFiltersModalOpen: boolean; isInitialized: boolean; // Cache: last filter key used for the second GlobalFeed effect lastFetchedFilterKey: string; // Dashboard stats cache dashboardCache: DashboardCache; searchAnomalyId: string; isRectificationEnabled: boolean; isFeedLoading: boolean; sortBy: FeedSortBy; sortDir: FeedSortDir; // Actions setTabs: (tabs: FeedTab[]) => void; setSelectedTabIndex: (index: number) => void; setAssets: (assets: any[]) => void; setAssetTypes: (assetTypes: any[]) => void; setSelectedAssetIds: (ids: number[]) => void; setSites: (sites: any[]) => void; setSelectedSite: (site: any) => void; setAuditStatus: (status: string) => void; setIsFiltersModalOpen: (isOpen: boolean) => void; setIsInitialized: (val: boolean) => void; setLastFetchedFilterKey: (key: string) => void; setDashboardCache: (cache: DashboardCache) => void; addTab: (tab: FeedTab) => void; closeTab: (index: number) => void; updateTab: (index: number, partialTab: Partial) => void; setFilterFromDate: (date: string) => void; setFilterTillDate: (date: string) => void; setFilterMinChainage: (val: string) => void; setFilterMaxChainage: (val: string) => void; setFilterPlaza: (val: string) => void; setSearchAnomalyId: (id: string) => void; setIsRectificationEnabled: (val: boolean) => void; setIsFeedLoading: (val: boolean) => void; setSortBy: (val: FeedSortBy) => void; setSortDir: (val: FeedSortDir) => void; resetStore: () => void; } const EMPTY_DASHBOARD_CACHE: DashboardCache = { siteId: '__unset__', auditStatus: '__unset__', fromDate: '__unset__', tillDate: '__unset__', assetIds: '__unset__', minChainage: '__unset__', maxChainage: '__unset__', searchAnomalyId: '__unset__', filterPlaza: '__unset__', isRectificationEnabled: false, date: '', stats: null, }; const initialFeedState = { tabs: [], selectedTabIndex: -1, assets: [], assetTypes: [], filterFromDate: '', filterTillDate: '', filterMinChainage: '', filterMaxChainage: '', filterPlaza: '', selectedAssetIds: [], selectedSite: null, sites: [], auditStatus: 'Manual', isFiltersModalOpen: false, isInitialized: false, lastFetchedFilterKey: '', dashboardCache: EMPTY_DASHBOARD_CACHE, searchAnomalyId: '', isRectificationEnabled: false, isFeedLoading: false, sortBy: '' as FeedSortBy, sortDir: 'asc' as FeedSortDir, }; export const useFeedStore = create()( persist( (set) => ({ ...initialFeedState, resetStore: () => set(initialFeedState), setTabs: (tabs) => set({ tabs }), setSelectedTabIndex: (index) => set({ selectedTabIndex: index }), setAssets: (assets) => set({ assets }), setAssetTypes: (assetTypes) => set({ assetTypes }), setSelectedAssetIds: (selectedAssetIds) => set({ selectedAssetIds }), setSites: (sites) => set({ sites }), setSelectedSite: (selectedSite) => set({ selectedSite }), setAuditStatus: (auditStatus) => set({ auditStatus }), setIsFiltersModalOpen: (isFiltersModalOpen) => set({ isFiltersModalOpen }), setIsInitialized: (isInitialized) => set({ isInitialized }), setLastFetchedFilterKey: (lastFetchedFilterKey) => set({ lastFetchedFilterKey }), setDashboardCache: (dashboardCache) => set({ dashboardCache }), setFilterFromDate: (filterFromDate) => set({ filterFromDate }), setFilterTillDate: (filterTillDate) => set({ filterTillDate }), setFilterMinChainage: (filterMinChainage) => set({ filterMinChainage }), setFilterMaxChainage: (filterMaxChainage) => set({ filterMaxChainage }), setFilterPlaza: (filterPlaza) => set({ filterPlaza }), setSearchAnomalyId: (searchAnomalyId) => set({ searchAnomalyId }), setIsRectificationEnabled: (isRectificationEnabled) => set({ isRectificationEnabled }), setIsFeedLoading: (isFeedLoading) => set({ isFeedLoading }), setSortBy: (sortBy) => set({ sortBy }), setSortDir: (sortDir) => set({ sortDir }), addTab: (tab) => set((state) => ({ tabs: [...state.tabs, tab], selectedTabIndex: state.tabs.length })), closeTab: (index) => set((state) => { const newTabs = [...state.tabs]; newTabs.splice(index, 1); const newIndex = state.selectedTabIndex >= newTabs.length ? newTabs.length - 1 : state.selectedTabIndex; return { tabs: newTabs, selectedTabIndex: newIndex }; }), updateTab: (index, partialTab) => set((state) => { const newTabs = [...state.tabs]; newTabs[index] = { ...newTabs[index], ...partialTab }; return { tabs: newTabs }; }) }), { name: 'feed-store-storage', partialize: (state) => ({ tabs: state.tabs, selectedTabIndex: state.selectedTabIndex, filterFromDate: state.filterFromDate, filterTillDate: state.filterTillDate, filterMinChainage: state.filterMinChainage, filterMaxChainage: state.filterMaxChainage, filterPlaza: state.filterPlaza, selectedAssetIds: state.selectedAssetIds, selectedSite: state.selectedSite, auditStatus: state.auditStatus, searchAnomalyId: state.searchAnomalyId, isRectificationEnabled: state.isRectificationEnabled, sortBy: state.sortBy, sortDir: state.sortDir, }), } ) );