# Zustand State Management

**Last Verified:** January 3, 2026  
**Version:** 1.3.2  
**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:**
- `authStore.ts` - Authentication state
- `siteStore.ts` - Site selection/management
- `sectorStore.ts` - Sector management
- `plannerStore.ts` - Planner module state
- `billingStore.ts` - Billing/credits
- `notificationStore.ts` - Notifications
- `settingsStore.ts` - User preferences
- `moduleStore.ts` - Module navigation
- `columnVisibilityStore.ts` - Table column prefs
- `pageSizeStore.ts` - Table pagination prefs
- **`onboardingStore.ts`** - Onboarding wizard state (v1.3.2)

---

## 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<void>;
  logout(): void;
  register(data: RegisterData): Promise<void>;
  refreshAccessToken(): Promise<void>;
  fetchUser(): Promise<void>;
  updateUser(data: UserUpdate): Promise<void>;
}
```

**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<void>;
  createSite(data: SiteCreate): Promise<Site>;
  updateSite(id: string, data: SiteUpdate): Promise<Site>;
  deleteSite(id: string): Promise<void>;
  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<void>;
  createSector(data: SectorCreate): Promise<Sector>;
  updateSector(id: string, data: SectorUpdate): Promise<Sector>;
  deleteSector(id: string): Promise<void>;
  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<void>;
  updateModules(settings: Partial<ModuleSettings>): Promise<void>;
  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<void>;
  fetchUsage(period?: string): Promise<void>;
  fetchLimits(): Promise<void>;
}
```

**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<void>;
  createKeyword(data: KeywordCreate): Promise<Keyword>;
  bulkDeleteKeywords(ids: string[]): Promise<void>;
  autoCluster(keywordIds: string[]): Promise<void>;
  generateIdeas(clusterId: string): Promise<void>;
  setFilters(filters: Partial<KeywordFilters>): 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<void>;
  createTask(data: TaskCreate): Promise<Task>;
  generateContent(taskId: string): Promise<Content>;
  fetchContent(contentId: string): Promise<Content>;
  updateContent(id: string, data: ContentUpdate): Promise<Content>;
  generateImages(contentId: string): Promise<void>;
  publishToWordPress(contentId: string): Promise<void>;
}
```

**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<void>;
  updateConfig(data: ConfigUpdate): Promise<void>;
  startRun(siteId: string): Promise<void>;
  pauseRun(runId: string): Promise<void>;
  resumeRun(runId: string): Promise<void>;
  cancelRun(runId: string): Promise<void>;
  fetchPipeline(siteId: string): Promise<void>;
  fetchLogs(runId: string): Promise<void>;
}
```

**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<void>;
  createIntegration(data: IntegrationCreate): Promise<SiteIntegration>;
  testConnection(id: string): Promise<TestResult>;
  triggerSync(id: string): Promise<void>;
  fetchSyncStatus(id: string): Promise<SyncStatus>;
}
```

---

## 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<Notification, 'id' | 'timestamp' | 'read'>): void;
  markAsRead(id: string): void;
  markAllAsRead(): void;
  removeNotification(id: string): void;
  clearAll(): void;
  fetchFromAPI(): Promise<void>;
  syncWithAPI(): Promise<void>;
}
```

**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<void>;
  showGuide(): void;
  toggleGuide(): void;
  loadFromBackend(): Promise<void>;
  syncToBackend(dismissed: boolean): Promise<void>;
}
```

**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 <OnboardingWizard onDismiss={dismissGuide} />;
}

// Re-show guide button
<Button onClick={showGuide}>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 |
