Files
ALwrity/frontend/src/stores/contentPlanningStore.ts
2025-08-22 14:08:54 +05:30

751 lines
24 KiB
TypeScript

import { create } from 'zustand';
import { contentPlanningApi } from '../services/contentPlanningApi';
// Types
export interface ContentStrategy {
id: string;
name: string;
description: string;
industry: string;
target_audience: string;
content_pillars: string[];
created_at: string;
updated_at: string;
user_id?: number;
}
export interface CalendarEvent {
id: string;
title: string;
description: string;
date: string;
scheduled_date?: string;
platform: string;
content_type: string;
status: 'draft' | 'scheduled' | 'published';
strategy_id?: string;
user_id?: number;
created_at: string;
updated_at: string;
}
export interface ContentGapAnalysis {
id: string;
website_url: string;
competitors: string[];
keywords: string[];
gaps: string[];
recommendations: AIRecommendation[];
created_at: string;
user_id?: number;
}
export interface AIRecommendation {
id: string;
type: 'strategy' | 'topic' | 'timing' | 'platform' | 'optimization';
title: string;
description: string;
confidence: number;
reasoning: string;
action_items: string[];
status: 'pending' | 'accepted' | 'rejected' | 'modified';
}
export interface AIInsight {
id: string;
type: 'performance' | 'opportunity' | 'warning' | 'trend';
title: string;
description: string;
priority: 'low' | 'medium' | 'high';
created_at: string;
}
export interface PerformanceMetrics {
engagement: number;
reach: number;
conversion: number;
roi: number;
time_range: string;
}
// New Calendar Generation Types
export interface GeneratedCalendar {
user_id: number;
strategy_id?: number;
calendar_type: string;
industry: string;
business_size: string;
generated_at: string;
content_pillars: string[];
platform_strategies: any;
content_mix: Record<string, number>;
daily_schedule: any[];
weekly_themes: any[];
content_recommendations: any[];
optimal_timing: any;
performance_predictions: any;
trending_topics: any[];
repurposing_opportunities: any[];
ai_insights: any[];
competitor_analysis: any;
gap_analysis_insights: any;
strategy_insights: any;
onboarding_insights: any;
processing_time: number;
ai_confidence: number;
// Enhanced strategy data for 12-step prompt chaining
strategy_data?: any;
strategy_analysis?: any;
quality_indicators?: any;
data_completeness?: any;
strategic_alignment?: any;
quality_gate_data?: any;
prompt_chain_data?: any;
// Metadata for tracking
metadata?: {
generated_at: string;
user_id: number;
strategy_id?: number;
calendar_type: string;
industry: string;
business_size: string;
version: string;
};
}
export interface ContentOptimization {
user_id: number;
event_id?: number;
original_content: any;
optimized_content: any;
platform_adaptations: string[];
visual_recommendations: string[];
hashtag_suggestions: string[];
keyword_optimization: any;
tone_adjustments: any;
length_optimization: any;
performance_prediction: any;
optimization_score: number;
recommendations?: any[];
created_at: string;
}
export interface PerformancePrediction {
user_id: number;
strategy_id?: number;
content_type: string;
platform: string;
predicted_engagement_rate: number;
predicted_reach: number;
predicted_conversions: number;
predicted_roi: number;
confidence_score: number;
recommendations: string[];
created_at: string;
}
export interface ContentRepurposing {
user_id: number;
strategy_id?: number;
original_content: any;
platform_adaptations: any[];
transformations: any[];
implementation_tips: string[];
gap_addresses: string[];
created_at: string;
}
export interface TrendingTopics {
user_id: number;
industry: string;
trending_topics: any[];
gap_relevance_scores: Record<string, number>;
audience_alignment_scores: Record<string, number>;
created_at: string;
}
// Store interface
interface ContentPlanningStore {
// State
strategies: ContentStrategy[];
currentStrategy: ContentStrategy | null;
latestGeneratedStrategy: any | null; // Cache for the latest generated strategy
calendarEvents: CalendarEvent[];
gapAnalyses: ContentGapAnalysis[];
aiRecommendations: AIRecommendation[];
aiInsights: AIInsight[];
performanceMetrics: PerformanceMetrics | null;
// New Calendar Generation State
generatedCalendar: GeneratedCalendar | null;
contentOptimization: ContentOptimization | null;
performancePrediction: PerformancePrediction | null;
contentRepurposing: ContentRepurposing | null;
trendingTopics: TrendingTopics | null;
calendarGenerationLoading: boolean;
calendarGenerationError: string | null;
// UI state
loading: boolean;
error: string | null;
activeTab: 'strategy' | 'calendar' | 'analytics' | 'gaps';
dataLoading: boolean;
// Actions
setLoading: (loading: boolean) => void;
setError: (error: string | null) => void;
setActiveTab: (tab: 'strategy' | 'calendar' | 'analytics' | 'gaps') => void;
// Strategy actions
createStrategy: (strategy: Omit<ContentStrategy, 'id' | 'created_at' | 'updated_at'>) => Promise<void>;
updateStrategy: (id: string, updates: Partial<ContentStrategy>) => Promise<void>;
deleteStrategy: (id: string) => Promise<void>;
setCurrentStrategy: (strategy: ContentStrategy | null) => void;
setLatestGeneratedStrategy: (strategy: any | null) => void;
// Calendar actions
createEvent: (event: Omit<CalendarEvent, 'id' | 'created_at' | 'updated_at'>) => Promise<void>;
updateEvent: (id: string, updates: Partial<CalendarEvent>) => Promise<void>;
deleteEvent: (id: string) => Promise<void>;
// Gap analysis actions
createGapAnalysis: (analysis: Omit<ContentGapAnalysis, 'id' | 'created_at'>) => Promise<void>;
updateGapAnalysis: (id: string, updates: Partial<ContentGapAnalysis>) => Promise<void>;
analyzeContentGaps: (params: { website_url: string; competitors: string[]; keywords: string[] }) => Promise<void>;
// AI actions
addAIRecommendation: (recommendation: AIRecommendation) => void;
updateAIRecommendation: (id: string, status: AIRecommendation['status']) => void;
addAIInsight: (insight: AIInsight) => void;
// Analytics actions
setPerformanceMetrics: (metrics: PerformanceMetrics) => void;
// Load data
loadStrategies: () => Promise<void>;
loadCalendarEvents: () => Promise<void>;
loadGapAnalyses: () => Promise<void>;
loadAIInsights: () => Promise<void>;
loadAIRecommendations: () => Promise<void>;
// Update data (for orchestrator)
updateStrategies: (strategies: ContentStrategy[]) => void;
updateCalendarEvents: (events: CalendarEvent[]) => void;
updateGapAnalyses: (analyses: ContentGapAnalysis[]) => void;
updateAIInsights: (data: { insights: AIInsight[]; recommendations: AIRecommendation[] }) => void;
// Health checks
checkHealth: () => Promise<boolean>;
checkDatabaseHealth: () => Promise<boolean>;
// New Calendar Generation Actions
generateCalendar: (request: {
user_id: number;
strategy_id?: number;
calendar_type: string;
industry?: string;
business_size: string;
force_refresh?: boolean;
}) => Promise<void>;
optimizeContent: (request: {
user_id: number;
event_id?: number;
title: string;
description: string;
content_type: string;
target_platform: string;
original_content?: any;
}) => Promise<void>;
predictPerformance: (request: {
user_id: number;
strategy_id?: number;
content_type: string;
platform: string;
content_data: any;
}) => Promise<void>;
repurposeContent: (request: {
user_id: number;
strategy_id?: number;
original_content: any;
target_platforms: string[];
}) => Promise<void>;
getTrendingTopics: (request: {
user_id: number;
industry: string;
limit?: number;
}) => Promise<void>;
setCalendarGenerationLoading: (loading: boolean) => void;
setCalendarGenerationError: (error: string | null) => void;
clearCalendarGenerationData: () => void;
}
// Store implementation
export const useContentPlanningStore = create<ContentPlanningStore>((set, get) => ({
// Initial state
strategies: [],
currentStrategy: null,
latestGeneratedStrategy: null,
calendarEvents: [],
gapAnalyses: [],
aiRecommendations: [],
aiInsights: [],
performanceMetrics: null,
// New Calendar Generation State
generatedCalendar: null,
contentOptimization: null,
performancePrediction: null,
contentRepurposing: null,
trendingTopics: null,
calendarGenerationLoading: false,
calendarGenerationError: null,
loading: false,
error: null,
activeTab: 'strategy',
dataLoading: false,
// UI actions
setLoading: (loading) => set({ loading }),
setError: (error) => set({ error }),
setActiveTab: (activeTab) => set({ activeTab }),
// Strategy actions
createStrategy: async (strategy) => {
set({ loading: true, error: null });
try {
const newStrategy = await contentPlanningApi.createStrategySafe({
name: strategy.name,
description: strategy.description,
industry: strategy.industry,
target_audience: strategy.target_audience,
content_pillars: strategy.content_pillars,
user_id: strategy.user_id
});
set((state) => ({
strategies: [...state.strategies, newStrategy],
loading: false,
}));
} catch (error: any) {
set({ error: error.message || 'Failed to create strategy', loading: false });
}
},
updateStrategy: async (id, updates) => {
set({ loading: true, error: null });
try {
const updatedStrategy = await contentPlanningApi.updateStrategy(id, updates);
set((state) => ({
strategies: state.strategies.map((strategy) =>
strategy.id === id ? updatedStrategy : strategy
),
loading: false,
}));
} catch (error: any) {
set({ error: error.message || 'Failed to update strategy', loading: false });
}
},
deleteStrategy: async (id) => {
set({ loading: true, error: null });
try {
await contentPlanningApi.deleteStrategy(id);
set((state) => ({
strategies: state.strategies.filter((strategy) => strategy.id !== id),
loading: false,
}));
} catch (error: any) {
set({ error: error.message || 'Failed to delete strategy', loading: false });
}
},
setCurrentStrategy: (strategy) => set({ currentStrategy: strategy }),
setLatestGeneratedStrategy: (strategy) => {
console.log('🔧 Store: Setting latest generated strategy:', {
strategyId: strategy?.id || strategy?.strategy_id,
strategyName: strategy?.name || strategy?.strategy_name,
hasStrategicInsights: !!strategy?.strategic_insights,
hasCompetitiveAnalysis: !!strategy?.competitive_analysis,
hasPerformancePredictions: !!strategy?.performance_predictions,
hasImplementationRoadmap: !!strategy?.implementation_roadmap,
hasRiskAssessment: !!strategy?.risk_assessment
});
set({ latestGeneratedStrategy: strategy });
},
// Calendar actions
createEvent: async (event) => {
set({ loading: true, error: null });
try {
const newEvent = await contentPlanningApi.createEventSafe({
title: event.title,
description: event.description,
date: event.date,
platform: event.platform,
content_type: event.content_type,
status: event.status,
strategy_id: event.strategy_id,
user_id: event.user_id
});
set((state) => ({
calendarEvents: [...state.calendarEvents, newEvent],
loading: false,
}));
} catch (error: any) {
set({ error: error.message || 'Failed to create event', loading: false });
}
},
updateEvent: async (id, updates) => {
set({ loading: true, error: null });
try {
const updatedEvent = await contentPlanningApi.updateEvent(id, updates);
set((state) => ({
calendarEvents: state.calendarEvents.map((event) =>
event.id === id ? updatedEvent : event
),
loading: false,
}));
} catch (error: any) {
set({ error: error.message || 'Failed to update event', loading: false });
}
},
deleteEvent: async (id) => {
set({ loading: true, error: null });
try {
await contentPlanningApi.deleteEvent(id);
set((state) => ({
calendarEvents: state.calendarEvents.filter((event) => event.id !== id),
loading: false,
}));
} catch (error: any) {
set({ error: error.message || 'Failed to delete event', loading: false });
}
},
// Gap analysis actions
createGapAnalysis: async (analysis) => {
set({ loading: true, error: null });
try {
const newAnalysis = await contentPlanningApi.createGapAnalysisSafe({
website_url: analysis.website_url,
competitors: analysis.competitors,
keywords: analysis.keywords,
user_id: analysis.user_id
});
set((state) => ({
gapAnalyses: [...state.gapAnalyses, newAnalysis],
loading: false,
}));
} catch (error: any) {
set({ error: error.message || 'Failed to create gap analysis', loading: false });
}
},
updateGapAnalysis: async (id, updates) => {
set({ loading: true, error: null });
try {
const updatedAnalysis = await contentPlanningApi.updateGapAnalysis(id, updates);
set((state) => ({
gapAnalyses: state.gapAnalyses.map((analysis) =>
analysis.id === id ? updatedAnalysis : analysis
),
loading: false,
}));
} catch (error: any) {
set({ error: error.message || 'Failed to update gap analysis', loading: false });
}
},
analyzeContentGaps: async (params) => {
set({ loading: true, error: null });
try {
const analysisResult = await contentPlanningApi.analyzeContentGapsSafe(params);
// Add the analysis result to the store
set((state) => ({
gapAnalyses: [...state.gapAnalyses, analysisResult],
loading: false,
}));
} catch (error: any) {
set({ error: error.message || 'Failed to analyze content gaps', loading: false });
}
},
// AI actions
addAIRecommendation: (recommendation) => {
set((state) => ({
aiRecommendations: [...state.aiRecommendations, recommendation],
}));
},
updateAIRecommendation: (id, status) => {
set((state) => ({
aiRecommendations: state.aiRecommendations.map((rec) =>
rec.id === id ? { ...rec, status } : rec
),
}));
},
addAIInsight: (insight) => {
set((state) => ({
aiInsights: [...state.aiInsights, insight],
}));
},
// Analytics actions
setPerformanceMetrics: (metrics) => set({ performanceMetrics: metrics }),
// Load data actions
loadStrategies: async () => {
set({ loading: true, error: null });
try {
const strategies = await contentPlanningApi.getStrategiesSafe();
if (Array.isArray(strategies)) {
set({ strategies, loading: false });
} else if (strategies && strategies.strategies && Array.isArray(strategies.strategies)) {
set({ strategies: strategies.strategies, loading: false });
} else {
set({ strategies: [], loading: false });
}
} catch (error: any) {
console.error('Error loading strategies:', error);
set({ error: error.message || 'Failed to load strategies', loading: false });
}
},
loadCalendarEvents: async () => {
set({ loading: true, error: null });
try {
const events = await contentPlanningApi.getEventsSafe();
set({ calendarEvents: events, loading: false });
} catch (error: any) {
set({ error: error.message || 'Failed to load calendar events', loading: false });
}
},
loadGapAnalyses: async () => {
set({ loading: true, error: null });
try {
const analyses = await contentPlanningApi.getGapAnalyses();
set({ gapAnalyses: analyses, loading: false });
} catch (error: any) {
set({ error: error.message || 'Failed to load gap analyses', loading: false });
}
},
loadAIInsights: async () => {
set({ loading: true, error: null });
try {
const response = await contentPlanningApi.getAIAnalyticsWithRetry();
// Validate response structure
if (!response || typeof response !== 'object') {
console.warn('Invalid AI analytics response:', response);
set({ aiInsights: [], loading: false });
return;
}
// Handle the response structure - it returns an object with insights array
const insights = Array.isArray(response.insights) ? response.insights : [];
// If no insights from backend, create some default insights from recommendations
let transformedInsights = insights;
if (insights.length === 0 && response.recommendations && Array.isArray(response.recommendations)) {
transformedInsights = response.recommendations.slice(0, 3).map((rec: any, index: number) => ({
id: `insight_${Date.now()}_${index}`,
type: 'opportunity',
title: rec.title || 'AI Insight',
description: rec.description || 'AI-generated insight',
priority: rec.priority === 'High' ? 'high' : rec.priority === 'Medium' ? 'medium' : 'low',
created_at: new Date().toISOString()
}));
} else {
// Transform insights data to the expected format
transformedInsights = insights.map((insight: any) => ({
id: insight.id || `insight_${Date.now()}`,
type: insight.type || 'performance',
title: insight.title || 'AI Insight',
description: insight.description || 'AI-generated insight',
priority: insight.priority || 'medium',
created_at: insight.created_at || new Date().toISOString()
}));
}
set({ aiInsights: transformedInsights, loading: false });
} catch (error: any) {
console.error('Error loading AI insights:', error);
// Check if it's a rate limit error
if (error.message?.includes('rate limit') || error.message?.includes('429')) {
console.warn('⚠️ Rate limit hit for AI insights, using empty data');
set({ aiInsights: [], loading: false });
return;
}
// Check if it's a network error
if (error.message?.includes('network') || error.message?.includes('connection')) {
console.warn('⚠️ Network error for AI insights, using empty data');
set({ aiInsights: [], loading: false });
return;
}
set({ error: error.message || 'Failed to load AI insights', loading: false, aiInsights: [] });
}
},
loadAIRecommendations: async () => {
set({ loading: true, error: null });
try {
const response = await contentPlanningApi.getAIAnalyticsWithRetry();
// Validate response structure
if (!response || typeof response !== 'object') {
console.warn('Invalid AI analytics response:', response);
set({ aiRecommendations: [], loading: false });
return;
}
// Handle the response structure - it returns an object with recommendations array
const recommendations = Array.isArray(response.recommendations) ? response.recommendations : [];
// Transform recommendations data to the expected format
const transformedRecommendations = recommendations.map((rec: any, index: number) => ({
id: rec.id || `rec_${Date.now()}_${index}`,
type: rec.type?.toLowerCase() || 'strategy',
title: rec.title || 'AI Recommendation',
description: rec.description || 'AI-generated recommendation',
confidence: rec.ai_confidence || rec.confidence || 0.8,
reasoning: rec.reasoning || rec.description || 'Generated by AI analysis',
action_items: Array.isArray(rec.content_suggestions) ? rec.content_suggestions : [],
status: rec.status || 'pending'
}));
set({ aiRecommendations: transformedRecommendations, loading: false });
} catch (error: any) {
console.error('Error loading AI recommendations:', error);
// Check if it's a rate limit error
if (error.message?.includes('rate limit') || error.message?.includes('429')) {
console.warn('⚠️ Rate limit hit for AI recommendations, using empty data');
set({ aiRecommendations: [], loading: false });
return;
}
// Check if it's a network error
if (error.message?.includes('network') || error.message?.includes('connection')) {
console.warn('⚠️ Network error for AI recommendations, using empty data');
set({ aiRecommendations: [], loading: false });
return;
}
set({ error: error.message || 'Failed to load AI recommendations', loading: false, aiRecommendations: [] });
}
},
// Update data (for orchestrator)
updateStrategies: (strategies: ContentStrategy[]) => {
set({ strategies });
},
updateCalendarEvents: (events: CalendarEvent[]) => {
set({ calendarEvents: events });
},
updateGapAnalyses: (analyses: ContentGapAnalysis[]) => {
set({ gapAnalyses: analyses });
},
updateAIInsights: (data: { insights: AIInsight[]; recommendations: AIRecommendation[] }) => {
set({
aiInsights: data.insights,
aiRecommendations: data.recommendations
});
},
// Health checks
checkHealth: async () => {
try {
const health = await contentPlanningApi.checkHealth();
return health.status === 'healthy';
} catch (error) {
console.error('Health check failed:', error);
return false;
}
},
checkDatabaseHealth: async () => {
try {
const dbHealth = await contentPlanningApi.checkDatabaseHealth();
return dbHealth.status === 'healthy';
} catch (error) {
console.error('Database health check failed:', error);
return false;
}
},
// New Calendar Generation Actions
generateCalendar: async (request) => {
set({ calendarGenerationLoading: true, calendarGenerationError: null });
try {
const generatedCalendar = await contentPlanningApi.generateComprehensiveCalendar(request);
set({ generatedCalendar, calendarGenerationLoading: false });
} catch (error: any) {
set({ calendarGenerationError: error.message || 'Failed to generate calendar', calendarGenerationLoading: false });
}
},
optimizeContent: async (request) => {
set({ loading: true, error: null });
try {
const optimizedContent = await contentPlanningApi.optimizeContent(request);
set({ contentOptimization: optimizedContent, loading: false });
} catch (error: any) {
set({ error: error.message || 'Failed to optimize content', loading: false });
}
},
predictPerformance: async (request) => {
set({ loading: true, error: null });
try {
const performancePrediction = await contentPlanningApi.predictPerformance(request);
set({ performancePrediction, loading: false });
} catch (error: any) {
set({ error: error.message || 'Failed to predict performance', loading: false });
}
},
repurposeContent: async (request) => {
set({ loading: true, error: null });
try {
const contentRepurposing = await contentPlanningApi.repurposeContent(request);
set({ contentRepurposing, loading: false });
} catch (error: any) {
set({ error: error.message || 'Failed to repurpose content', loading: false });
}
},
getTrendingTopics: async (request) => {
set({ loading: true, error: null });
try {
const trendingTopics = await contentPlanningApi.getTrendingTopics(request);
set({ trendingTopics, loading: false });
} catch (error: any) {
set({ error: error.message || 'Failed to get trending topics', loading: false });
}
},
setCalendarGenerationLoading: (loading) => set({ calendarGenerationLoading: loading }),
setCalendarGenerationError: (error) => set({ calendarGenerationError: error }),
clearCalendarGenerationData: () => set({ generatedCalendar: null, contentOptimization: null, performancePrediction: null, contentRepurposing: null, trendingTopics: null }),
}));