Files
igny8/frontend/src/store/siteStore.ts
2025-11-09 10:27:02 +00:00

141 lines
4.9 KiB
TypeScript

/**
* Site Store (Zustand)
* Manages the currently selected/active site for filtering data globally
* Since Site → Sector → Keywords, and all workflows depend on Keywords/Sector,
* filtering by site automatically filters all downstream data.
*/
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { Site, fetchSites } from '../services/api';
interface SiteState {
activeSite: Site | null;
loading: boolean;
error: string | null;
// Actions
setActiveSite: (site: Site | null) => void;
loadActiveSite: () => Promise<void>;
refreshActiveSite: () => Promise<void>;
}
export const useSiteStore = create<SiteState>()(
persist<SiteState>(
(set, get) => ({
activeSite: null,
loading: false,
error: null,
setActiveSite: (site) => {
set({ activeSite: site, error: null });
// Ensure site is persisted immediately
if (typeof window !== 'undefined') {
try {
const state = get();
localStorage.setItem('site-storage', JSON.stringify({
state: { activeSite: site },
version: 0
}));
} catch (e) {
console.warn('Failed to persist site to localStorage:', e);
}
}
// Dispatch event for components to refresh data
window.dispatchEvent(new CustomEvent('siteChanged', { detail: { siteId: site?.id } }));
// Load sectors for the newly selected site (if sector store is available)
if (site && typeof window !== 'undefined') {
// Import dynamically to avoid circular dependency
import('./sectorStore').then(({ useSectorStore }) => {
const sectorStore = useSectorStore.getState();
sectorStore.loadSectorsForSite(site.id);
}).catch(() => {
// Sector store might not be available yet, that's okay
});
}
},
loadActiveSite: async () => {
set({ loading: true, error: null });
try {
const response = await fetchSites();
const allSites = response.results || [];
// Find the first active site (or the one that was previously selected)
const activeSites = allSites.filter(site => site.is_active);
const previousSite = get().activeSite;
let siteToSet: Site | null = null;
if (previousSite) {
// Check if previous site still exists and is accessible
const previousSiteExists = allSites.find(s => s.id === previousSite.id);
if (previousSiteExists && previousSiteExists.is_active) {
// Keep the previously selected site if it's still active
siteToSet = previousSiteExists;
} else if (previousSiteExists && !previousSiteExists.is_active) {
// Previous site exists but is inactive - clear it and find a new active site
siteToSet = activeSites.length > 0 ? activeSites[0] : null;
} else {
// Previous site no longer exists - find a new active site
siteToSet = activeSites.length > 0 ? activeSites[0] : null;
}
} else if (activeSites.length > 0) {
// No previous site - use the first active site
siteToSet = activeSites[0];
}
set({ activeSite: siteToSet, loading: false });
// Immediately persist to localStorage for getActiveSiteId() to access
if (siteToSet && typeof window !== 'undefined') {
try {
localStorage.setItem('site-storage', JSON.stringify({
state: { activeSite: siteToSet },
version: 0
}));
} catch (e) {
console.warn('Failed to persist site to localStorage:', e);
}
}
} catch (error: any) {
set({
error: error.message || 'Failed to load active site',
loading: false
});
}
},
refreshActiveSite: async () => {
const currentSite = get().activeSite;
if (!currentSite) {
await get().loadActiveSite();
return;
}
set({ loading: true, error: null });
try {
const response = await fetchSites();
const updatedSite = (response.results || []).find(s => s.id === currentSite.id);
if (updatedSite && updatedSite.is_active) {
set({ activeSite: updatedSite, loading: false });
} else {
// Current site is no longer active, load a new one
await get().loadActiveSite();
}
} catch (error: any) {
set({
error: error.message || 'Failed to refresh active site',
loading: false
});
}
},
}),
{
name: 'site-storage',
partialize: (state) => ({
activeSite: state.activeSite,
}),
}
)
);