= ({
+ open,
+ onClose,
+ onConfirmStrategy,
+ onEditStrategy,
+ onCreateNewStrategy,
+ currentStrategy,
+ strategyStatus
+}) => {
+ const [activeStep, setActiveStep] = useState(0);
+ const [loading, setLoading] = useState(false);
+
+ const steps = [
+ {
+ label: 'Welcome to ALwrity',
+ icon: ,
+ content: (
+
+
+ 🚀 Your AI-Powered Content Strategy Copilot
+
+
+ ALwrity democratizes professional content strategy and calendar creation, making it accessible to solopreneurs and small businesses.
+
+
+
+
+
+
+
+ AI-Enhanced Strategy
+
+
+ Our AI analyzes your business, competitors, and market trends to create a comprehensive content strategy.
+
+
+
+
+
+
+
+
+
+ Smart Calendar Creation
+
+
+ Automatically generate content calendars with optimal posting times and content mix.
+
+
+
+
+
+
+ )
+ },
+ {
+ label: 'What ALwrity Has Done',
+ icon: ,
+ content: (
+
+
+ 📊 Comprehensive Research & Analysis
+
+
+
+
+
+
+
+ Market Research
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Strategic Insights
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ },
+ {
+ label: 'Your Journey with ALwrity',
+ icon: ,
+ content: (
+
+
+ 🎯 Your 4-Step Success Path
+
+
+
+
+
+
+ 1
+ Review & Confirm Strategy
+
+
+ Review the AI-generated content strategy tailored to your business. Make any adjustments and confirm to activate.
+
+
+
+
+
+
+
+
+
+ 2
+ Create Content Calendar
+
+
+ ALwrity generates a comprehensive content calendar with optimal posting times and content themes.
+
+
+
+
+
+
+
+
+ 3
+ Measure Strategy KPIs
+
+
+ Track performance metrics and analyze content effectiveness to understand what works best.
+
+
+
+
+
+
+
+
+ 4
+ Optimize with AI
+
+
+ Continuously improve your strategy based on performance data and AI recommendations.
+
+
+
+
+
+
+ )
+ },
+ {
+ label: 'ALwrity as Your Copilot',
+ icon: ,
+ content: (
+
+
+ 🤖 Your AI Marketing Assistant
+
+
+ Once your strategy is active, ALwrity becomes your 24/7 content marketing copilot, handling the heavy lifting while you focus on your business.
+
+
+
+
+
+
+
+
+ Automated Execution
+
+
+ ALwrity schedules, generates, reviews, and posts content according to your strategy, saving you hours every week.
+
+
+
+
+
+
+
+
+
+ Performance Tracking
+
+
+ Monitor your content performance in real-time with detailed analytics and actionable insights.
+
+
+
+
+
+
+
+
+
+ Quality Assurance
+
+
+ Every piece of content is reviewed for quality, brand consistency, and strategic alignment.
+
+
+
+
+
+
+
+
+ Pro Tip: ALwrity learns from your content performance and continuously optimizes your strategy for better results.
+
+
+
+ )
+ },
+ {
+ label: 'Take Action',
+ icon: ,
+ content: (
+
+
+ 🎯 Ready to Activate Your Strategy?
+
+
+ {strategyStatus === 'inactive' && currentStrategy ? (
+
+
+
+ Strategy Found: We found an existing strategy that needs to be activated.
+
+
+
+
+
+
+ Current Strategy: {currentStrategy.name}
+
+
+ {currentStrategy.description}
+
+
+
+
+
+
+
+
+
+
+ }
+ sx={{
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ '&:hover': { transform: 'translateY(-2px)' },
+ transition: 'all 0.3s ease'
+ }}
+ >
+ Activate Strategy
+
+
+
+ }
+ >
+ Edit Strategy
+
+
+
+ }
+ >
+ Create New
+
+
+
+
+ ) : strategyStatus === 'none' ? (
+
+
+
+ No Strategy Found: Let's create your first content strategy!
+
+
+
+
+
+ }
+ sx={{
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ '&:hover': { transform: 'translateY(-2px)' },
+ transition: 'all 0.3s ease'
+ }}
+ >
+ Create Strategy with AI
+
+
+
+ }
+ >
+ Maybe Later
+
+
+
+
+ ) : (
+
+
+
+ Strategy Active: Your content strategy is already active and running!
+
+
+
+ }
+ sx={{
+ background: 'linear-gradient(135deg, #4caf50 0%, #45a049 100%)',
+ '&:hover': { transform: 'translateY(-2px)' },
+ transition: 'all 0.3s ease'
+ }}
+ >
+ Continue to Dashboard
+
+
+ )}
+
+ )
+ }
+ ];
+
+ const handleNext = () => {
+ setActiveStep((prevActiveStep) => prevActiveStep + 1);
+ };
+
+ const handleBack = () => {
+ setActiveStep((prevActiveStep) => prevActiveStep - 1);
+ };
+
+ const handleReset = () => {
+ setActiveStep(0);
+ };
+
+ return (
+
+ );
+};
+
+export default StrategyOnboardingDialog;
\ No newline at end of file
diff --git a/frontend/src/components/ContentPlanningDashboard/tabs/CalendarTab.tsx b/frontend/src/components/ContentPlanningDashboard/tabs/CalendarTab.tsx
index 24889843..e79128c0 100644
--- a/frontend/src/components/ContentPlanningDashboard/tabs/CalendarTab.tsx
+++ b/frontend/src/components/ContentPlanningDashboard/tabs/CalendarTab.tsx
@@ -8,7 +8,6 @@ import {
TextField,
Card,
CardContent,
- CardActions,
Chip,
IconButton,
Dialog,
@@ -42,7 +41,6 @@ import {
CalendarToday as CalendarIcon,
Event as EventIcon,
Refresh as RefreshIcon,
- AutoAwesome as AIIcon,
TrendingUp as TrendingIcon,
ContentCopy as RepurposeIcon,
Analytics as AnalyticsIcon,
@@ -64,7 +62,6 @@ import {
} from '@mui/icons-material';
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
import { contentPlanningApi } from '../../../services/contentPlanningApi';
-import CalendarGenerationWizard from '../components/CalendarGenerationWizard';
interface TabPanelProps {
children?: React.ReactNode;
@@ -100,10 +97,8 @@ const CalendarTab: React.FC = () => {
updateCalendarEvents,
// New calendar generation state
generatedCalendar,
- contentOptimization,
performancePrediction,
contentRepurposing,
- trendingTopics,
aiInsights,
calendarGenerationError,
dataLoading
@@ -131,7 +126,7 @@ const CalendarTab: React.FC = () => {
aiAnalysisResults: []
});
- const [calendarGenerationMode, setCalendarGenerationMode] = useState<'transparency' | 'wizard'>('transparency');
+ const safeCalendarEvents = Array.isArray(calendarEvents) ? calendarEvents : [];
useEffect(() => {
loadCalendarData();
@@ -214,22 +209,6 @@ const CalendarTab: React.FC = () => {
await loadCalendarData();
};
- const handleGenerateAICalendar = async () => {
- try {
- // This will now use the comprehensive data from the transparency dashboard
- const calendarConfig = {
- userData,
- calendarType: 'monthly',
- industry: userData.onboardingData?.industry || 'technology',
- businessSize: 'sme'
- };
-
- await contentPlanningApi.generateComprehensiveCalendar(calendarConfig);
- } catch (error) {
- console.error('Error generating AI calendar:', error);
- }
- };
-
const handleDataUpdate = (updatedData: any) => {
setUserData((prev: any) => ({ ...prev, ...updatedData }));
};
@@ -278,9 +257,6 @@ const CalendarTab: React.FC = () => {
}
};
- // Ensure calendarEvents is always an array
- const safeCalendarEvents = Array.isArray(calendarEvents) ? calendarEvents : [];
-
return (
@@ -321,9 +297,6 @@ const CalendarTab: React.FC = () => {
setTabValue(newValue)}>
} iconPosition="start" />
- } iconPosition="start" />
- } iconPosition="start" />
- } iconPosition="start" />
@@ -416,102 +389,6 @@ const CalendarTab: React.FC = () => {
)}
-
- {/* Calendar Generation Wizard with Data Transparency */}
-
-
-
-
- {/* Content Optimizer Tab */}
-
-
-
-
-
- Content Optimization
-
-
- {contentOptimization ? (
-
-
- Optimization Recommendations
-
-
- {contentOptimization.recommendations?.map((rec: any, index: number) => (
-
-
-
-
-
-
- ))}
-
-
- ) : (
-
-
-
- No optimization data
-
-
- Generate content optimization recommendations
-
-
- )}
-
-
-
-
-
-
- {/* Trending Topics Tab */}
-
-
-
-
-
- Trending Topics
-
-
- {trendingTopics ? (
-
-
- Current Trending Topics
-
-
- {trendingTopics.trending_topics?.map((topic: any, index: number) => (
-
- ))}
-
-
- ) : (
-
-
-
- No trending topics
-
-
- Get trending topics for your industry
-
-
- )}
-
-
-
-
-
{/* Event Dialog */}
);
};
diff --git a/frontend/src/components/ContentPlanningDashboard/tabs/CreateTab.tsx b/frontend/src/components/ContentPlanningDashboard/tabs/CreateTab.tsx
new file mode 100644
index 00000000..0c31e0c5
--- /dev/null
+++ b/frontend/src/components/ContentPlanningDashboard/tabs/CreateTab.tsx
@@ -0,0 +1,113 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Box,
+ Tabs,
+ Tab,
+ Typography
+} from '@mui/material';
+import {
+ AutoAwesome as AutoAwesomeIcon,
+ CalendarToday as CalendarIcon
+} from '@mui/icons-material';
+import ContentStrategyBuilder from '../components/ContentStrategyBuilder';
+import CalendarGenerationWizard from '../components/CalendarGenerationWizard';
+import { contentPlanningApi } from '../../../services/contentPlanningApi';
+
+interface TabPanelProps {
+ children?: React.ReactNode;
+ index: number;
+ value: number;
+}
+
+function TabPanel(props: TabPanelProps) {
+ const { children, value, index, ...other } = props;
+
+ return (
+
+ {value === index && {children}}
+
+ );
+}
+
+const CreateTab: React.FC = () => {
+ const [tabValue, setTabValue] = useState(0);
+ const [userData, setUserData] = useState({});
+
+ useEffect(() => {
+ loadUserData();
+ }, []);
+
+ const loadUserData = async () => {
+ try {
+ // Load comprehensive user data for calendar generation
+ const comprehensiveData = await contentPlanningApi.getComprehensiveUserData(1); // Pass user ID
+ setUserData(comprehensiveData.data); // Extract the data from the response
+ } catch (error) {
+ console.error('Error loading user data:', error);
+ }
+ };
+
+ const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
+ setTabValue(newValue);
+ };
+
+ const handleGenerateCalendar = async (calendarConfig: any) => {
+ try {
+ await contentPlanningApi.generateComprehensiveCalendar({
+ ...calendarConfig,
+ userData
+ });
+ } catch (error) {
+ console.error('Error generating calendar:', error);
+ }
+ };
+
+ return (
+
+
+ Create
+
+
+
+
+
+
+ Enhanced Strategy Builder
+
+ }
+ />
+
+
+ Calendar Wizard
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default CreateTab;
\ No newline at end of file
diff --git a/frontend/src/components/ContentPlanningDashboard/tabs/GapAnalysisTab.tsx b/frontend/src/components/ContentPlanningDashboard/tabs/GapAnalysisTab.tsx
index c01079f4..d0abec68 100644
--- a/frontend/src/components/ContentPlanningDashboard/tabs/GapAnalysisTab.tsx
+++ b/frontend/src/components/ContentPlanningDashboard/tabs/GapAnalysisTab.tsx
@@ -1,404 +1,95 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState } from 'react';
import {
Box,
- Grid,
- Paper,
+ Tabs,
+ Tab,
Typography,
- Button,
- TextField,
- Card,
- CardContent,
- Chip,
- Divider,
- Alert,
- CircularProgress,
- List,
- ListItem,
- ListItemText,
- ListItemIcon
+ Alert
} from '@mui/material';
import {
+ Analytics as AnalyticsIcon,
+ TrendingUp as TrendingIcon,
Search as SearchIcon,
- Add as AddIcon,
- Warning as WarningIcon,
- CheckCircle as CheckCircleIcon,
- TrendingUp as TrendingUpIcon,
- Assessment as AssessmentIcon
+ Assessment as AssessmentIcon,
+ BarChart as BarChartIcon,
+ PieChart as PieChartIcon
} from '@mui/icons-material';
-import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
-import { contentPlanningApi } from '../../../services/contentPlanningApi';
+import RefineAnalysisTab from './RefineAnalysisTab';
+import ContentOptimizerTab from './ContentOptimizerTab';
+import TrendingTopicsTab from './TrendingTopicsTab';
+import KeywordResearchTab from './KeywordResearchTab';
+import PerformanceAnalyticsTab from './PerformanceAnalyticsTab';
+import ContentPillarsTab from './ContentPillarsTab';
+
+interface TabPanelProps {
+ children?: React.ReactNode;
+ index: number;
+ value: number;
+}
+
+function TabPanel(props: TabPanelProps) {
+ const { children, value, index, ...other } = props;
+
+ return (
+
+ {value === index && {children}}
+
+ );
+}
const GapAnalysisTab: React.FC = () => {
- const {
- gapAnalyses,
- loading,
- error,
- loadGapAnalyses,
- analyzeContentGaps,
- updateGapAnalyses
- } = useContentPlanningStore();
-
- const [analysisForm, setAnalysisForm] = useState({
- website_url: '',
- competitors: [] as string[],
- keywords: [] as string[]
- });
- const [newCompetitor, setNewCompetitor] = useState('');
- const [newKeyword, setNewKeyword] = useState('');
- const [dataLoading, setDataLoading] = useState(false);
+ const [tabValue, setTabValue] = useState(0);
- useEffect(() => {
- loadGapAnalysisData();
- }, []);
-
- const loadGapAnalysisData = async () => {
- try {
- setDataLoading(true);
- const response = await contentPlanningApi.getGapAnalysesSafe();
-
- console.log('Gap Analysis Response:', response);
-
- // Transform the backend response to match frontend expectations
- if (response && response.gap_analyses) {
- const transformedAnalyses = response.gap_analyses.map((analysis: any, index: number) => ({
- id: analysis.id || `analysis_${index}`,
- website_url: analysis.website_url || 'example.com',
- competitors: analysis.competitors || [],
- keywords: analysis.keywords || [],
- gaps: analysis.gaps || [],
- recommendations: analysis.recommendations || [],
- created_at: analysis.created_at || new Date().toISOString()
- }));
-
- console.log('Transformed Analyses:', transformedAnalyses);
-
- // Update the store with transformed data
- updateGapAnalyses(transformedAnalyses);
- } else {
- console.log('No gap analyses found in response');
- updateGapAnalyses([]);
- }
- } catch (error) {
- console.error('Error loading gap analysis data:', error);
- updateGapAnalyses([]);
- } finally {
- setDataLoading(false);
- }
+ const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
+ setTabValue(newValue);
};
- const handleAddCompetitor = () => {
- if (newCompetitor.trim() && !analysisForm.competitors.includes(newCompetitor.trim())) {
- setAnalysisForm(prev => ({
- ...prev,
- competitors: [...prev.competitors, newCompetitor.trim()]
- }));
- setNewCompetitor('');
- }
- };
-
- const handleRemoveCompetitor = (competitorToRemove: string) => {
- setAnalysisForm(prev => ({
- ...prev,
- competitors: prev.competitors.filter(comp => comp !== competitorToRemove)
- }));
- };
-
- const handleAddKeyword = () => {
- if (newKeyword.trim() && !analysisForm.keywords.includes(newKeyword.trim())) {
- setAnalysisForm(prev => ({
- ...prev,
- keywords: [...prev.keywords, newKeyword.trim()]
- }));
- setNewKeyword('');
- }
- };
-
- const handleRemoveKeyword = (keywordToRemove: string) => {
- setAnalysisForm(prev => ({
- ...prev,
- keywords: prev.keywords.filter(keyword => keyword !== keywordToRemove)
- }));
- };
-
- const handleRunAnalysis = async () => {
- if (!analysisForm.website_url) {
- return;
- }
-
- try {
- setDataLoading(true);
-
- await analyzeContentGaps({
- website_url: analysisForm.website_url,
- competitors: analysisForm.competitors,
- keywords: analysisForm.keywords
- });
-
- // Reload data after analysis
- await loadGapAnalyses();
-
- // Reset form
- setAnalysisForm({
- website_url: '',
- competitors: [],
- keywords: []
- });
- } catch (error) {
- console.error('Error running gap analysis:', error);
- } finally {
- setDataLoading(false);
- }
- };
-
- // Ensure gapAnalyses is always an array and transform the data structure
- const safeGapAnalyses = Array.isArray(gapAnalyses) ? gapAnalyses : [];
-
- // Transform backend data structure to frontend expected structure
- const transformedGapAnalyses = safeGapAnalyses.map((analysis, index) => {
- // Handle the actual backend structure: { recommendations: [...] }
- const recommendations = analysis.recommendations || [];
-
- return {
- id: analysis.id || `analysis-${index}`,
- website_url: analysis.website_url || 'Unknown Website',
- competitors: analysis.competitors || [],
- keywords: analysis.keywords || [],
- recommendations: recommendations,
- created_at: analysis.created_at || new Date().toISOString(),
- // Extract gaps from recommendations if available
- gaps: recommendations.length > 0 ?
- recommendations.filter((rec: any) => rec.type === 'gap').map((rec: any) => rec.title || rec.description || 'Content gap identified') :
- []
- };
- });
-
return (
- Content Gap Analysis
+ Gap Analysis & Optimization
- {error && (
-
- {error}
-
- )}
+
+
+ } iconPosition="start" />
+ } iconPosition="start" />
+ } iconPosition="start" />
+ } iconPosition="start" />
+ } iconPosition="start" />
+ } iconPosition="start" />
+
+
-
- {/* Analysis Setup */}
-
-
-
-
- Analysis Setup
-
-
-
- setAnalysisForm(prev => ({ ...prev, website_url: e.target.value }))}
- placeholder="https://example.com"
- sx={{ mb: 2 }}
- />
-
-
- Competitors
-
-
- setNewCompetitor(e.target.value)}
- placeholder="competitor.com"
- onKeyPress={(e) => e.key === 'Enter' && handleAddCompetitor()}
- />
-
-
-
-
- {analysisForm.competitors.map((competitor, index) => (
- handleRemoveCompetitor(competitor)}
- color="primary"
- variant="outlined"
- />
- ))}
-
-
-
- Keywords
-
-
- setNewKeyword(e.target.value)}
- placeholder="target keyword"
- onKeyPress={(e) => e.key === 'Enter' && handleAddKeyword()}
- />
-
-
-
-
- {analysisForm.keywords.map((keyword, index) => (
- handleRemoveKeyword(keyword)}
- color="secondary"
- variant="outlined"
- />
- ))}
-
-
- }
- >
- {loading || dataLoading ? 'Running Analysis...' : 'Run Gap Analysis'}
-
-
-
+
+
+
- {/* Content Gaps */}
-
-
-
-
- Content Gaps
-
-
-
- {dataLoading ? (
-
-
-
- ) : transformedGapAnalyses.length === 0 ? (
-
- No previous analyses found. Run your first analysis to see results here.
-
- ) : (
-
- {transformedGapAnalyses.map((analysis) => (
-
-
-
-
- {analysis.website_url}
-
-
- {new Date(analysis.created_at).toLocaleDateString()}
-
-
-
-
-
-
-
-
-
-
- ))}
-
- )}
-
+
+
+
- {/* Detailed Analysis Results */}
- {transformedGapAnalyses.length > 0 && (
-
-
-
- Detailed Analysis Results
-
-
-
- {transformedGapAnalyses.map((analysis, index) => (
-
-
- Analysis for {analysis.website_url}
-
-
- {analysis.gaps && analysis.gaps.length > 0 && (
-
-
- Identified Content Gaps:
-
-
- {analysis.gaps.map((gap, gapIndex) => (
-
-
-
-
-
-
- ))}
-
-
- )}
-
- {analysis.recommendations && analysis.recommendations.length > 0 && (
-
-
- Recommendations:
-
-
- {analysis.recommendations.map((rec, recIndex) => (
-
-
-
-
-
-
- ))}
-
-
- )}
-
- ))}
-
- )}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/frontend/src/components/ContentPlanningDashboard/tabs/KeywordResearchTab.tsx b/frontend/src/components/ContentPlanningDashboard/tabs/KeywordResearchTab.tsx
new file mode 100644
index 00000000..6a4b1265
--- /dev/null
+++ b/frontend/src/components/ContentPlanningDashboard/tabs/KeywordResearchTab.tsx
@@ -0,0 +1,189 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Box,
+ Grid,
+ Paper,
+ Typography,
+ Card,
+ CardContent,
+ Chip,
+ CircularProgress,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Button
+} from '@mui/material';
+import {
+ Search as SearchIcon
+} from '@mui/icons-material';
+import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
+import { contentPlanningApi } from '../../../services/contentPlanningApi';
+
+const KeywordResearchTab: React.FC = () => {
+ const [keywordResearch, setKeywordResearch] = useState(null);
+ const [dataLoading, setDataLoading] = useState(false);
+
+ useEffect(() => {
+ loadKeywordResearch();
+ }, []);
+
+ const loadKeywordResearch = async () => {
+ try {
+ setDataLoading(true);
+ const eventSource = await contentPlanningApi.streamKeywordResearch();
+
+ contentPlanningApi.handleSSEData(
+ eventSource,
+ (data) => {
+ console.log('Keyword Research SSE Data:', data);
+ if (data.type === 'result' && data.data) {
+ setKeywordResearch(data.data);
+ }
+ },
+ (error) => {
+ console.error('Keyword Research SSE Error:', error);
+ },
+ () => {
+ setDataLoading(false);
+ }
+ );
+ } catch (error) {
+ console.error('Error loading keyword research:', error);
+ setDataLoading(false);
+ }
+ };
+
+ return (
+
+
+ Keyword Research
+
+
+ {dataLoading ? (
+
+
+
+ ) : keywordResearch && keywordResearch.trend_analysis ? (
+
+
+
+
+
+ High Volume Keywords
+
+
+
+
+
+ Keyword
+ Volume
+ Difficulty
+
+
+
+ {(keywordResearch.trend_analysis.high_volume_keywords || []).map((keyword: any, index: number) => (
+
+ {keyword.keyword}
+ {keyword.volume}
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ Trending Keywords
+
+ {(keywordResearch.trend_analysis.trending_keywords || []).map((keyword: any, index: number) => (
+
+
+ {keyword.keyword}
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+ Keyword Opportunities
+
+
+
+
+
+ Keyword
+ Search Volume
+ Competition
+ CPC
+ Action
+
+
+
+ {(keywordResearch.opportunities || []).map((opportunity: any, index: number) => (
+
+ {opportunity.keyword}
+ {opportunity.search_volume}
+
+
+
+ ${opportunity.cpc}
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+ ) : (
+
+ No keyword research data available
+
+ )}
+
+ );
+};
+
+export default KeywordResearchTab;
\ No newline at end of file
diff --git a/frontend/src/components/ContentPlanningDashboard/tabs/PerformanceAnalyticsTab.tsx b/frontend/src/components/ContentPlanningDashboard/tabs/PerformanceAnalyticsTab.tsx
new file mode 100644
index 00000000..f77b11f4
--- /dev/null
+++ b/frontend/src/components/ContentPlanningDashboard/tabs/PerformanceAnalyticsTab.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import {
+ Box,
+ Grid,
+ Card,
+ CardContent,
+ Typography
+} from '@mui/material';
+import {
+ BarChart as BarChartIcon
+} from '@mui/icons-material';
+import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
+
+const PerformanceAnalyticsTab: React.FC = () => {
+ const { performanceMetrics } = useContentPlanningStore();
+
+ return (
+
+
+ Performance Analytics
+
+
+ {performanceMetrics ? (
+
+
+
+
+
+ Content Performance by Type
+
+
+ No content performance data available
+
+
+
+
+
+
+
+
+
+ Growth Trends
+
+
+ No trend data available
+
+
+
+
+
+ ) : (
+
+ No performance analytics data available
+
+ )}
+
+ );
+};
+
+export default PerformanceAnalyticsTab;
\ No newline at end of file
diff --git a/frontend/src/components/ContentPlanningDashboard/tabs/RefineAnalysisTab.tsx b/frontend/src/components/ContentPlanningDashboard/tabs/RefineAnalysisTab.tsx
new file mode 100644
index 00000000..a796a987
--- /dev/null
+++ b/frontend/src/components/ContentPlanningDashboard/tabs/RefineAnalysisTab.tsx
@@ -0,0 +1,406 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Box,
+ Grid,
+ Paper,
+ Typography,
+ Button,
+ TextField,
+ Card,
+ CardContent,
+ Chip,
+ Divider,
+ Alert,
+ CircularProgress,
+ List,
+ ListItem,
+ ListItemText,
+ ListItemIcon
+} from '@mui/material';
+import {
+ Search as SearchIcon,
+ Add as AddIcon,
+ Warning as WarningIcon,
+ CheckCircle as CheckCircleIcon,
+ TrendingUp as TrendingUpIcon,
+ Assessment as AssessmentIcon
+} from '@mui/icons-material';
+import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
+import { contentPlanningApi } from '../../../services/contentPlanningApi';
+
+const RefineAnalysisTab: React.FC = () => {
+ const {
+ gapAnalyses,
+ loading,
+ error,
+ loadGapAnalyses,
+ analyzeContentGaps,
+ updateGapAnalyses
+ } = useContentPlanningStore();
+
+ const [analysisForm, setAnalysisForm] = useState({
+ website_url: '',
+ competitors: [] as string[],
+ keywords: [] as string[]
+ });
+ const [newCompetitor, setNewCompetitor] = useState('');
+ const [newKeyword, setNewKeyword] = useState('');
+ const [dataLoading, setDataLoading] = useState(false);
+
+ useEffect(() => {
+ loadGapAnalysisData();
+ }, []);
+
+ const loadGapAnalysisData = async () => {
+ try {
+ setDataLoading(true);
+ const response = await contentPlanningApi.getGapAnalysesSafe();
+
+ console.log('Gap Analysis Response:', response);
+
+ // Transform the backend response to match frontend expectations
+ if (response && response.gap_analyses) {
+ const transformedAnalyses = response.gap_analyses.map((analysis: any, index: number) => ({
+ id: analysis.id || `analysis_${index}`,
+ website_url: analysis.website_url || 'example.com',
+ competitors: analysis.competitors || [],
+ keywords: analysis.keywords || [],
+ gaps: analysis.gaps || [],
+ recommendations: analysis.recommendations || [],
+ created_at: analysis.created_at || new Date().toISOString()
+ }));
+
+ console.log('Transformed Analyses:', transformedAnalyses);
+
+ // Update the store with transformed data
+ updateGapAnalyses(transformedAnalyses);
+ } else {
+ console.log('No gap analyses found in response');
+ updateGapAnalyses([]);
+ }
+ } catch (error) {
+ console.error('Error loading gap analysis data:', error);
+ updateGapAnalyses([]);
+ } finally {
+ setDataLoading(false);
+ }
+ };
+
+ const handleAddCompetitor = () => {
+ if (newCompetitor.trim() && !analysisForm.competitors.includes(newCompetitor.trim())) {
+ setAnalysisForm(prev => ({
+ ...prev,
+ competitors: [...prev.competitors, newCompetitor.trim()]
+ }));
+ setNewCompetitor('');
+ }
+ };
+
+ const handleRemoveCompetitor = (competitorToRemove: string) => {
+ setAnalysisForm(prev => ({
+ ...prev,
+ competitors: prev.competitors.filter(comp => comp !== competitorToRemove)
+ }));
+ };
+
+ const handleAddKeyword = () => {
+ if (newKeyword.trim() && !analysisForm.keywords.includes(newKeyword.trim())) {
+ setAnalysisForm(prev => ({
+ ...prev,
+ keywords: [...prev.keywords, newKeyword.trim()]
+ }));
+ setNewKeyword('');
+ }
+ };
+
+ const handleRemoveKeyword = (keywordToRemove: string) => {
+ setAnalysisForm(prev => ({
+ ...prev,
+ keywords: prev.keywords.filter(keyword => keyword !== keywordToRemove)
+ }));
+ };
+
+ const handleRunAnalysis = async () => {
+ if (!analysisForm.website_url) {
+ return;
+ }
+
+ try {
+ setDataLoading(true);
+
+ await analyzeContentGaps({
+ website_url: analysisForm.website_url,
+ competitors: analysisForm.competitors,
+ keywords: analysisForm.keywords
+ });
+
+ // Reload data after analysis
+ await loadGapAnalyses();
+
+ // Reset form
+ setAnalysisForm({
+ website_url: '',
+ competitors: [],
+ keywords: []
+ });
+ } catch (error) {
+ console.error('Error running gap analysis:', error);
+ } finally {
+ setDataLoading(false);
+ }
+ };
+
+ // Ensure gapAnalyses is always an array and transform the data structure
+ const safeGapAnalyses = Array.isArray(gapAnalyses) ? gapAnalyses : [];
+
+ // Transform backend data structure to frontend expected structure
+ const transformedGapAnalyses = safeGapAnalyses.map((analysis, index) => {
+ // Handle the actual backend structure: { recommendations: [...] }
+ const recommendations = analysis.recommendations || [];
+
+ return {
+ id: analysis.id || `analysis-${index}`,
+ website_url: analysis.website_url || 'Unknown Website',
+ competitors: analysis.competitors || [],
+ keywords: analysis.keywords || [],
+ recommendations: recommendations,
+ created_at: analysis.created_at || new Date().toISOString(),
+ // Extract gaps from recommendations if available
+ gaps: recommendations.length > 0 ?
+ recommendations.filter((rec: any) => rec.type === 'gap').map((rec: any) => rec.title || rec.description || 'Content gap identified') :
+ []
+ };
+ });
+
+ return (
+
+
+ Refine Analysis
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+ {/* Analysis Setup */}
+
+
+
+
+ Analysis Setup
+
+
+
+ setAnalysisForm(prev => ({ ...prev, website_url: e.target.value }))}
+ placeholder="https://example.com"
+ sx={{ mb: 2 }}
+ />
+
+
+ Competitors
+
+
+ setNewCompetitor(e.target.value)}
+ placeholder="competitor.com"
+ onKeyPress={(e) => e.key === 'Enter' && handleAddCompetitor()}
+ />
+
+
+
+
+ {analysisForm.competitors.map((competitor, index) => (
+ handleRemoveCompetitor(competitor)}
+ color="primary"
+ variant="outlined"
+ />
+ ))}
+
+
+
+ Keywords
+
+
+ setNewKeyword(e.target.value)}
+ placeholder="target keyword"
+ onKeyPress={(e) => e.key === 'Enter' && handleAddKeyword()}
+ />
+
+
+
+
+ {analysisForm.keywords.map((keyword, index) => (
+ handleRemoveKeyword(keyword)}
+ color="secondary"
+ variant="outlined"
+ />
+ ))}
+
+
+ }
+ >
+ {loading || dataLoading ? 'Running Analysis...' : 'Run Gap Analysis'}
+
+
+
+
+ {/* Content Gaps */}
+
+
+
+
+ Content Gaps
+
+
+
+ {dataLoading ? (
+
+
+
+ ) : transformedGapAnalyses.length === 0 ? (
+
+ No previous analyses found. Run your first analysis to see results here.
+
+ ) : (
+
+ {transformedGapAnalyses.map((analysis) => (
+
+
+
+
+ {analysis.website_url}
+
+
+ {new Date(analysis.created_at).toLocaleDateString()}
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ {/* Detailed Analysis Results */}
+ {transformedGapAnalyses.length > 0 && (
+
+
+
+ Detailed Analysis Results
+
+
+
+ {transformedGapAnalyses.map((analysis, index) => (
+
+
+ Analysis for {analysis.website_url}
+
+
+ {analysis.gaps && analysis.gaps.length > 0 && (
+
+
+ Identified Content Gaps:
+
+
+ {analysis.gaps.map((gap, gapIndex) => (
+
+
+
+
+
+
+ ))}
+
+
+ )}
+
+ {analysis.recommendations && analysis.recommendations.length > 0 && (
+
+
+ Recommendations:
+
+
+ {analysis.recommendations.map((rec, recIndex) => (
+
+
+
+
+
+
+ ))}
+
+
+ )}
+
+ ))}
+
+ )}
+
+
+
+ );
+};
+
+export default RefineAnalysisTab;
\ No newline at end of file
diff --git a/frontend/src/components/ContentPlanningDashboard/tabs/TrendingTopicsTab.tsx b/frontend/src/components/ContentPlanningDashboard/tabs/TrendingTopicsTab.tsx
new file mode 100644
index 00000000..7131261f
--- /dev/null
+++ b/frontend/src/components/ContentPlanningDashboard/tabs/TrendingTopicsTab.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import {
+ Box,
+ Grid,
+ Paper,
+ Typography,
+ Chip
+} from '@mui/material';
+import {
+ TrendingUp as TrendingIcon
+} from '@mui/icons-material';
+import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
+
+const TrendingTopicsTab: React.FC = () => {
+ const { trendingTopics } = useContentPlanningStore();
+
+ return (
+
+
+ Trending Topics
+
+
+
+
+
+
+
+ Trending Topics
+
+
+ {trendingTopics ? (
+
+
+ Current Trending Topics
+
+
+ {trendingTopics.trending_topics?.map((topic: any, index: number) => (
+
+ ))}
+
+
+ ) : (
+
+
+
+ No trending topics
+
+
+ Get trending topics for your industry
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default TrendingTopicsTab;
\ No newline at end of file