# Zustand State Management **Last Verified:** January 20, 2026 **Version:** 1.8.4 **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 **Available Stores (11 total):** | Store | File | Purpose | |-------|------|---------| | Auth | `authStore.ts` | Authentication, user session, account | | Site | `siteStore.ts` | Site selection/management | | Sector | `sectorStore.ts` | Sector management within sites | | Planner | `plannerStore.ts` | Planner module state | | Billing | `billingStore.ts` | Credits, billing info | | Notification | `notificationStore.ts` | App notifications | | Settings | `settingsStore.ts` | User preferences | | Module | `moduleStore.ts` | Module enable/disable state | | Column Visibility | `columnVisibilityStore.ts` | Table column preferences | | Page Size | `pageSizeStore.ts` | Table pagination preferences | | Onboarding | `onboardingStore.ts` | Onboarding wizard 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 --- ## Notification Store (`notificationStore.ts`) - v1.2.0 **Purpose:** In-app notifications for AI task completions and system events ```typescript type NotificationType = 'success' | 'error' | 'warning' | 'info'; type NotificationCategory = 'ai_task' | 'system' | 'info'; interface Notification { id: string; apiId?: number; // Server ID for synced notifications type: NotificationType; category: NotificationCategory; title: string; message: string; timestamp: Date; read: boolean; actionLabel?: string; actionHref?: string; metadata?: { taskId?: string; functionName?: string; count?: number; credits?: number; }; } interface NotificationStore { notifications: Notification[]; unreadCount: number; isLoading: boolean; lastFetched: Date | null; // Actions addNotification(notification: Omit): void; markAsRead(id: string): void; markAllAsRead(): void; removeNotification(id: string): void; clearAll(): void; fetchFromAPI(): Promise; syncWithAPI(): Promise; } ``` **Features:** - In-memory queue for optimistic UI updates - API sync for persistent notifications - Auto-dismissal with configurable timeout - Read/unread state tracking - Category-based filtering (ai_task, system, info) **API Integration:** Uses `notifications.api.ts` service: - `fetchNotifications()` - List with pagination - `fetchUnreadCount()` - Unread count - `markNotificationRead()` - Mark single as read - `markAllNotificationsRead()` - Mark all as read - `deleteNotification()` - Delete notification **Usage:** ```typescript const { notifications, unreadCount, addNotification, markAsRead } = useNotificationStore(); // Add AI task completion notification addNotification({ type: 'success', category: 'ai_task', title: 'Content Generated', message: 'Generated 5 articles successfully', metadata: { count: 5, credits: 250 } }); ``` --- ## 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) notificationStore (global, polls/syncs independently) moduleStore (global, loads once per account) billingStore (global, loads once per account) onboardingStore (global, syncs with UserSettings backend) uiStore (local only, no API) ``` --- ## Onboarding Store (`onboardingStore.ts`) (v1.3.2) **Purpose:** Manages onboarding wizard and guide screen state ```typescript interface OnboardingState { isGuideDismissed: boolean; isGuideVisible: boolean; isLoading: boolean; lastSyncedAt: Date | null; } interface OnboardingActions { dismissGuide(): Promise; showGuide(): void; toggleGuide(): void; loadFromBackend(): Promise; syncToBackend(dismissed: boolean): Promise; } ``` **Persistence:** - Local: `onboarding-storage` in localStorage - Backend: `UserSettings` with key `workflow_guide_dismissed` **Features:** - Cross-device sync via backend UserSettings - Rate-limited sync (max every 5 minutes) - Graceful fallback if backend unavailable **Usage:** ```typescript const { isGuideVisible, dismissGuide, showGuide } = useOnboardingStore(); // Show onboarding wizard if (isGuideVisible) { return ; } // Re-show guide button ``` --- ## Store Files Location ``` frontend/src/store/ ├── authStore.ts ├── siteStore.ts ├── sectorStore.ts ├── moduleStore.ts ├── billingStore.ts ├── notificationStore.ts # v1.2.0 ├── onboardingStore.ts ├── pageSizeStore.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 |