temproary docs uplaoded
This commit is contained in:
516
v2/Live Docs on Server/igny8-app-docs/30-FRONTEND/STORES.md
Normal file
516
v2/Live Docs on Server/igny8-app-docs/30-FRONTEND/STORES.md
Normal file
@@ -0,0 +1,516 @@
|
||||
# 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<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 |
|
||||
Reference in New Issue
Block a user