# Zustand State Management **Last Verified:** December 25, 2025 **Framework:** Zustand 4 with persist middleware --- ## Store Architecture All stores in `/frontend/src/store/` use Zustand with TypeScript. **Key Patterns:** - `persist` middleware for localStorage persistence - Async actions for API calls - Selectors for derived state --- ## Auth Store (`authStore.ts`) **Purpose:** User authentication and session management ```typescript interface AuthState { user: User | null; account: Account | null; isAuthenticated: boolean; accessToken: string | null; refreshToken: string | null; isLoading: boolean; error: string | null; } interface AuthActions { login(email: string, password: string): Promise; logout(): void; register(data: RegisterData): Promise; refreshAccessToken(): Promise; fetchUser(): Promise; updateUser(data: UserUpdate): Promise; } ``` **Persistence:** `accessToken`, `refreshToken` in localStorage **Usage:** ```typescript const { user, login, logout, isAuthenticated } = useAuthStore(); ``` --- ## Site Store (`siteStore.ts`) **Purpose:** Site selection and management ```typescript interface SiteState { sites: Site[]; currentSite: Site | null; isLoading: boolean; error: string | null; } interface SiteActions { fetchSites(): Promise; createSite(data: SiteCreate): Promise; updateSite(id: string, data: SiteUpdate): Promise; deleteSite(id: string): Promise; setCurrentSite(site: Site): void; } ``` **Persistence:** `currentSite.id` in localStorage **Auto-selection:** If no site selected and sites exist, auto-selects first site --- ## Sector Store (`sectorStore.ts`) **Purpose:** Sector selection and management within sites ```typescript interface SectorState { sectors: Sector[]; currentSector: Sector | null; isLoading: boolean; error: string | null; } interface SectorActions { fetchSectors(siteId: string): Promise; createSector(data: SectorCreate): Promise; updateSector(id: string, data: SectorUpdate): Promise; deleteSector(id: string): Promise; setCurrentSector(sector: Sector): void; } ``` **Persistence:** `currentSector.id` in localStorage **Dependency:** Reloads when `currentSite` changes --- ## Module Store (`moduleStore.ts`) **Purpose:** Track which modules are enabled/disabled ```typescript interface ModuleState { modules: ModuleSettings; isLoading: boolean; error: string | null; } interface ModuleSettings { planner_enabled: boolean; writer_enabled: boolean; linker_enabled: boolean; optimizer_enabled: boolean; automation_enabled: boolean; integration_enabled: boolean; publisher_enabled: boolean; } interface ModuleActions { fetchModules(): Promise; updateModules(settings: Partial): Promise; isModuleEnabled(module: ModuleName): boolean; } ``` **Usage:** ```typescript const { isModuleEnabled } = useModuleStore(); if (isModuleEnabled('planner')) { /* show planner */ } ``` **Currently used for:** Sidebar visibility only --- ## Billing Store (`billingStore.ts`) **Purpose:** Credit balance and usage tracking ```typescript interface BillingState { balance: CreditBalance | null; usage: CreditUsage[]; limits: PlanLimits | null; isLoading: boolean; error: string | null; } interface CreditBalance { ideaCredits: number; contentCredits: number; imageCredits: number; optimizationCredits: number; } interface BillingActions { fetchBalance(): Promise; fetchUsage(period?: string): Promise; fetchLimits(): Promise; } ``` **Refresh triggers:** - After content generation - After image generation - After optimization (when implemented) --- ## Planner Store (`plannerStore.ts`) **Purpose:** Keywords, clusters, and content ideas state ```typescript interface PlannerState { keywords: Keyword[]; clusters: Cluster[]; ideas: ContentIdea[]; selectedKeywords: string[]; filters: KeywordFilters; isLoading: boolean; error: string | null; } interface PlannerActions { fetchKeywords(siteId: string, sectorId?: string): Promise; createKeyword(data: KeywordCreate): Promise; bulkDeleteKeywords(ids: string[]): Promise; autoCluster(keywordIds: string[]): Promise; generateIdeas(clusterId: string): Promise; setFilters(filters: Partial): void; selectKeywords(ids: string[]): void; } ``` --- ## Writer Store (`writerStore.ts`) **Purpose:** Tasks and content management ```typescript interface WriterState { tasks: Task[]; content: Content[]; currentContent: Content | null; filters: TaskFilters; isLoading: boolean; isGenerating: boolean; error: string | null; } interface WriterActions { fetchTasks(siteId: string, sectorId?: string): Promise; createTask(data: TaskCreate): Promise; generateContent(taskId: string): Promise; fetchContent(contentId: string): Promise; updateContent(id: string, data: ContentUpdate): Promise; generateImages(contentId: string): Promise; publishToWordPress(contentId: string): Promise; } ``` **Generation state:** `isGenerating` tracks active AI operations --- ## Automation Store (`automationStore.ts`) **Purpose:** Automation pipeline state and control ```typescript interface AutomationState { config: AutomationConfig | null; currentRun: AutomationRun | null; pipeline: PipelineOverview | null; history: AutomationRun[]; logs: AutomationLog[]; isLoading: boolean; error: string | null; } interface AutomationActions { fetchConfig(siteId: string): Promise; updateConfig(data: ConfigUpdate): Promise; startRun(siteId: string): Promise; pauseRun(runId: string): Promise; resumeRun(runId: string): Promise; cancelRun(runId: string): Promise; fetchPipeline(siteId: string): Promise; fetchLogs(runId: string): Promise; } ``` **Polling:** Active runs trigger status polling every 5 seconds --- ## Integration Store (`integrationStore.ts`) **Purpose:** WordPress integration management ```typescript interface IntegrationState { integrations: SiteIntegration[]; currentIntegration: SiteIntegration | null; syncStatus: SyncStatus | null; isLoading: boolean; isSyncing: boolean; error: string | null; } interface IntegrationActions { fetchIntegrations(siteId: string): Promise; createIntegration(data: IntegrationCreate): Promise; testConnection(id: string): Promise; triggerSync(id: string): Promise; fetchSyncStatus(id: string): Promise; } ``` --- ## UI Store (`uiStore.ts`) **Purpose:** UI state (sidebar, modals, notifications) ```typescript interface UIState { sidebarOpen: boolean; sidebarCollapsed: boolean; theme: 'light' | 'dark' | 'system'; notifications: Notification[]; } interface UIActions { toggleSidebar(): void; collapseSidebar(): void; setTheme(theme: Theme): void; addNotification(notification: Notification): void; removeNotification(id: string): void; } ``` **Persistence:** `theme`, `sidebarCollapsed` in localStorage --- ## Store Dependencies ``` authStore │ └── siteStore (loads after auth) │ └── sectorStore (loads when site changes) │ ├── plannerStore (scoped to site/sector) ├── writerStore (scoped to site/sector) ├── automationStore (scoped to site) └── integrationStore (scoped to site) moduleStore (global, loads once per account) billingStore (global, loads once per account) uiStore (local only, no API) ``` --- ## Store Files Location ``` frontend/src/store/ ├── authStore.ts ├── siteStore.ts ├── sectorStore.ts ├── moduleStore.ts ├── billingStore.ts ├── plannerStore.ts ├── writerStore.ts ├── automationStore.ts ├── integrationStore.ts ├── uiStore.ts └── index.ts (re-exports) ``` --- ## Best Practices 1. **Always scope to site/sector** when fetching module data 2. **Check module enabled** before showing features 3. **Handle loading states** in UI components 4. **Clear store on logout** to prevent data leaks 5. **Use selectors** for derived/filtered data --- ## Planned Changes | Item | Description | Priority | |------|-------------|----------| | Add `linkerStore` | State for internal linking results | Medium | | Add `optimizerStore` | State for optimization results | Medium | | Better error typing | Typed error codes per store | Low | | DevTools integration | Zustand devtools middleware | Low |