From 66ece49705b633dccd4f07ee6443df47910583f5 Mon Sep 17 00:00:00 2001 From: ajaysi Date: Tue, 12 Aug 2025 22:35:21 +0530 Subject: [PATCH] Alwrity version 0.5.4 --- ToBeMigrated/utils/alwrity_utils.py | 232 --- ToBeMigrated/utils/test_config_settings.py | 310 ---- ToBeMigrated/utils/voice_processing.py | 23 - ToBeMigrated/utils/website_analyzer/models.py | 45 - .../endpoints/ai_generation_endpoints.py | 1020 ++++------- .../endpoints/content_strategy/__init__.py | 8 + .../content_strategy/educational_content.py | 319 ++++ .../api/enhanced_strategy_routes.py | 2 +- .../ai_generation/strategy_generator.py | 121 +- .../content_strategy/core/strategy_service.py | 2 +- docs/sse_migration_strategy.md | 343 ++++ frontend/build/asset-manifest.json | 6 +- frontend/build/index.html | 2 +- .../components/ContentStrategyBuilder.tsx | 803 +-------- .../components/ActionButtons.tsx | 227 +++ .../components/CategoryDetailView.tsx | 257 +++ .../components/EducationalModal.tsx | 554 ++++++ .../components/ErrorAlert.tsx | 57 + .../components/StrategyDisplay.tsx | 79 + .../ContentStrategyBuilder/index.ts | 18 + .../types/contentStrategy.types.ts | 124 ++ .../StrategyIntelligenceTab.tsx | 103 ++ .../components/CompetitiveAnalysisCard.tsx | 435 +++++ .../components/ConfirmationDialog.tsx | 97 + .../components/ImplementationRoadmapCard.tsx | 453 +++++ .../components/PerformancePredictionsCard.tsx | 539 ++++++ .../components/ProgressiveCard.tsx | 259 +++ .../components/ProgressiveDemo.tsx | 413 +++++ .../components/RiskAssessmentCard.tsx | 404 +++++ .../components/StrategicInsightsCard.tsx | 330 ++++ .../components/StrategyActions.tsx | 162 ++ .../components/StrategyHeader.tsx | 619 +++++++ .../components/TestComponent.tsx | 33 + .../hooks/useStrategyActions.ts | 84 + .../hooks/useStrategyData.ts | 178 ++ .../components/StrategyIntelligence/index.ts | 22 + .../styles/analysisCardStyles.ts | 377 ++++ .../StrategyIntelligence/styles/index.ts | 1 + .../types/strategy.types.ts | 267 +++ .../utils/strategyTransformers.ts | 473 +++++ .../components/StrategyIntelligenceTab.tsx | 103 ++ .../StrategyIntelligenceTab.tsx.backup | 1578 +++++++++++++++++ .../tabs/ContentStrategyTab.tsx | 106 +- frontend/src/services/contentPlanningApi.ts | 101 +- 44 files changed, 9577 insertions(+), 2112 deletions(-) delete mode 100644 ToBeMigrated/utils/alwrity_utils.py delete mode 100644 ToBeMigrated/utils/test_config_settings.py delete mode 100644 ToBeMigrated/utils/voice_processing.py delete mode 100644 ToBeMigrated/utils/website_analyzer/models.py create mode 100644 backend/api/content_planning/api/content_strategy/endpoints/content_strategy/__init__.py create mode 100644 backend/api/content_planning/api/content_strategy/endpoints/content_strategy/educational_content.py create mode 100644 docs/sse_migration_strategy.md create mode 100644 frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/ActionButtons.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/CategoryDetailView.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/EducationalModal.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/ErrorAlert.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/StrategyDisplay.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/index.ts create mode 100644 frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/types/contentStrategy.types.ts create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/StrategyIntelligenceTab.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/CompetitiveAnalysisCard.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ConfirmationDialog.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ImplementationRoadmapCard.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/PerformancePredictionsCard.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ProgressiveCard.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ProgressiveDemo.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/RiskAssessmentCard.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategicInsightsCard.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategyActions.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategyHeader.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/TestComponent.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/hooks/useStrategyActions.ts create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/hooks/useStrategyData.ts create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/index.ts create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/styles/analysisCardStyles.ts create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/styles/index.ts create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/types/strategy.types.ts create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/utils/strategyTransformers.ts create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligenceTab.tsx create mode 100644 frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligenceTab.tsx.backup diff --git a/ToBeMigrated/utils/alwrity_utils.py b/ToBeMigrated/utils/alwrity_utils.py deleted file mode 100644 index d8375f85..00000000 --- a/ToBeMigrated/utils/alwrity_utils.py +++ /dev/null @@ -1,232 +0,0 @@ -import re -import os -import PyPDF2 -import openai -import streamlit as st -import tempfile -from loguru import logger - - -from lib.ai_writers.ai_news_article_writer import ai_news_generation -from lib.ai_writers.ai_finance_report_generator.ai_financial_dashboard import get_dashboard -from lib.ai_writers.ai_facebook_writer.facebook_ai_writer import facebook_main_menu -from lib.ai_writers.linkedin_writer.linkedin_ai_writer import linkedin_main_menu -from lib.ai_writers.twitter_writers.twitter_dashboard import run_dashboard -from lib.ai_writers.insta_ai_writer import insta_writer -from lib.ai_writers.youtube_writers.youtube_ai_writer import youtube_main_menu -from lib.ai_writers.ai_essay_writer import ai_essay_generator -from lib.gpt_providers.text_to_image_generation.main_generate_image_from_prompt import generate_image -#from lib.content_planning_calender.content_planning_agents_alwrity_crew import ai_agents_content_planner -from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen - - -def ai_agents_team(): - # Define options for AI Content Teams - st.title("🐲 Your AI Agents Teams") - st.markdown("""Alwrity offers AI agents team for content creators to easily modify them for their needs. - Abstracting tech & plumbing, easily define role, goal, task. Use different AI agents framework.""") - - options = [ - "AI Planning Team", - "AI Content Creation Team" - ] - - # Radio button for choosing an AI Content Team - selected_team = st.radio("**Choose AI Agents Team:**", options) - - if selected_team == "AI Planning Team": - st.title("AI Agents for Content Ideation") - plan_keywords = st.text_input( - "Enter Keywords to get 2 months content calendar:", - placeholder="Enter keywords to generate AI content calendar:", - help="Enter at least two words for better results." - ) - if st.button("Get calendar"): - if plan_keywords and len(plan_keywords.split()) >= 2: - with st.spinner("Get Content Plan..."): - try: - #plan_content = ai_agents_content_planner(plan_keywords) - st.success(f"Coming soon: Content plan for: {plan_keywords}") - #st.markdown(plan_content) - except Exception as err: - st.error(f"Failed to generate content plan: {err}") - else: - st.error("🚫 Single keywords are just too vague. Try again.") - elif selected_team == "AI Content Creation Team": - content_agents() - - - -def content_agents(): - st.markdown("AI Agents Team for Content Writing") - content_keywords = st.text_input( - "Enter Main Domain Keywords of your business:", - placeholder="Better keywords, Better content. Get keywords from Google search", - help="These keywords define your main business sector, blogging niche, Industry, domain etc" - ) - - if st.button("Start Writing"): - if content_keywords and len(content_keywords.split()) >= 2: - with st.spinner("Generating Content..."): - try: - #calendar_content = ai_agents_writers(content_keywords) - st.success(f"🚫 Not implemented yet: {content_keywords}") - #st.markdown(calendar_content) - except Exception as err: - st.error(f"🚫 Failed to generate content with AI Agents: {err}") - else: - st.error("🚫 Single keywords are just too vague. Try again.") - - - -def essay_writer(): - st.title("AI Essay Writer πŸ“") - st.write("Select your essay type, education level, and desired length, then let AI generate an essay for you. ✨") - - # Define essay types and education levels - essay_types = [ - "πŸ“– Argumentative - Forming an opinion via research. Building an evidence-based argument.", - "πŸ“š Expository - Knowledge of a topic. Communicating information clearly.", - "βœ’οΈ Narrative - Creative language use. Presenting a compelling narrative.", - "🎨 Descriptive - Creative language use. Describing sensory details." - ] - - education_levels = [ - "🏫 Primary School", - "🏫 High School", - "πŸŽ“ College", - "πŸŽ“ Graduate School" - ] - - # Define the options for number of pages - num_pages_options = [ - "πŸ“„ Short Form (1-2 pages)", - "πŸ“„πŸ“„ Medium Form (3-5 pages)", - "πŸ“„πŸ“„πŸ“„ Long Form (6+ pages)" - ] - - # Create columns for input fields - col1, col2 = st.columns(2) - - with col1: - # Ask the user for the title of the essay - essay_title = st.text_input("πŸ“ Essay Title", placeholder="Enter the title of your essay", help="Provide a clear and concise title for your essay.") - - # Ask the user for type of essay - selected_essay_type = st.selectbox("πŸ“š Type of Essay", options=essay_types, help="Choose the type of essay you want to write.") - - with col2: - # Ask the user for level of education - selected_education_level = st.selectbox("πŸŽ“ Level of Education", options=education_levels, help="Choose your level of education.") - - # Ask the user for number of pages - selected_num_pages = st.selectbox("πŸ“„ Number of Pages", options=num_pages_options, help="Select the length of your essay.") - - if st.button("πŸš€ Generate Essay"): - if essay_title: - st.success("Generating your essay... ✨") - ai_essay_generator(essay_title, selected_essay_type, selected_education_level, selected_num_pages) - else: - st.error("Please enter a valid title for your essay. 🚫") - - -def ai_news_writer(): - """ AI News Writer """ - st.markdown("

πŸ“° AI News Writer πŸ—žοΈ

", unsafe_allow_html=True) - - # Input for news keywords - news_keywords = st.text_input( - "**πŸ”‘ Enter Keywords from News Headlines:**", - placeholder="Describe the News article in 3-5 words. Enter main keywords describing the News Event:", - help="Enter at least two words for better results." - ) - - if news_keywords and len(news_keywords.split()) < 2: - st.error("🚫 News keywords should be at least two words long. Least, you can do..") - - # Selectbox for country and language - countries = [ - ("es", "Spain"), - ("vn", "Vietnam"), - ("pk", "Pakistan"), - ("in", "India"), - ("de", "Germany"), - ("cn", "China") - ] - - languages = [ - ("en", "English"), - ("es", "Spanish"), - ("vi", "Vietnamese"), - ("ar", "Arabic"), - ("hi", "Hindi"), - ("de", "German"), - ("zh-cn", "Chinese") - ] - - col1, col2 = st.columns(2) - with col1: - news_country = st.selectbox("**🌍 Select Origin Country of News Event:**", - countries, format_func=lambda x: x[1], help="Which country did the NEWS originate from ?") - with col2: - news_language = st.selectbox("**πŸ—£οΈ Select News Article Language to Search For:**", - languages, format_func=lambda x: x[1], help="Language to output News Article in ?") - - if st.button("πŸ“° Generate News Report"): - if news_keywords and len(news_keywords.split()) >= 2: - with st.spinner("Generating News Report... ⏳"): - try: - news_report = ai_news_generation(news_keywords, news_country, news_language) - st.success(f"Successfully generated news report on: {news_keywords} πŸŽ‰") - st.markdown(news_report) - except Exception as err: - st.error(f"Failed to generate news report: {err} ❌") - else: - st.error("Please enter valid keywords for the news report. 🚫") - - -def ai_finance_ta_writer(): - st.markdown("
AI Financial Technical Analysis Writer
", unsafe_allow_html=True) - - ticker_symbol = st.text_input( - "Enter Ticker Symbol for TA:", - placeholder="Enter a valid Ticker Symbol (Examples: IBM, BABA, HDFCBANK.NS, TATAMOTORS.NS etc)", - help="Be sure of the ticker symbol. Double-check it! Examples: IBM, BABA, HDFCBANK.NS, TATAMOTORS.NS" - ) - - if st.button("Generate TA Report"): - if ticker_symbol: - with st.spinner("Generating TA Report..."): - try: - # Get dashboard instance and generate technical analysis - dashboard = get_dashboard() - ta_report = dashboard.generate_technical_analysis(ticker_symbol) - st.success(f"Successfully generated TA report for: {ticker_symbol}") - st.markdown(ta_report) - except Exception as err: - st.error(f"🚫 Check ticker symbol: Failed to write Financial Technical Analysis. Error: {err}") - else: - st.error("🚫 Provide a valid Ticker Symbol. Don't waste my time.") - -def ai_social_writer(): - # Define social media platforms as radio buttons - social_media_options = [ - ("facebook", "Facebook"), - ("linkedin", "LinkedIn"), - ("twitter", "Twitter"), - ("instagram", "Instagram"), - ("youtube", "YouTube") - ] - - # Selectbox for choosing a platform - selected_platform = st.radio("Choose a Social Media Platform:", social_media_options, format_func=lambda x: x[1]) - if "facebook" in selected_platform: - facebook_main_menu() - elif "linkedin" in selected_platform: - linkedin_main_menu() - elif "twitter" in selected_platform: - run_dashboard() - elif "instagram" in selected_platform: - insta_writer() - elif "youtube" in selected_platform: - youtube_main_menu() \ No newline at end of file diff --git a/ToBeMigrated/utils/test_config_settings.py b/ToBeMigrated/utils/test_config_settings.py deleted file mode 100644 index 316c0de9..00000000 --- a/ToBeMigrated/utils/test_config_settings.py +++ /dev/null @@ -1,310 +0,0 @@ -"""Test configuration settings page for ALwrity.""" - -import streamlit as st -from loguru import logger -import asyncio -from lib.web_crawlers.async_web_crawler import AsyncWebCrawlerService -from pages.style_utils import ( - get_test_config_styles, - get_glass_container, - get_info_section, - get_example_box, - get_analysis_section, - get_style_guide_html -) -import sys -from lib.personalization.style_analyzer import StyleAnalyzer - -# Set page config - must be the first Streamlit command -st.set_page_config( - layout="wide", - initial_sidebar_state="collapsed", - menu_items={ - 'Get Help': None, - 'Report a bug': None, - 'About': None - } -) - -import yaml -from pathlib import Path -import os -from loguru import logger -from lib.utils.read_main_config_params import get_personalization_settings -from lib.web_crawlers.crawl4ai_web_crawler import analyze_style - -# Configure logger -logger.remove() # Remove default handler -logger.add( - "logs/test_config_settings.log", - rotation="500 MB", - retention="10 days", - level="DEBUG", - format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", - backtrace=True, - diagnose=True -) -logger.add( - sys.stdout, - level="INFO", - format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}" -) - -# Apply CSS styles -st.markdown(get_test_config_styles(), unsafe_allow_html=True) - -def load_website_url(): - """Load website URL from config file.""" - try: - logger.debug("Loading website URL from config file") - config_path = Path(os.environ["ALWRITY_CONFIG"]) - config = yaml.safe_load(config_path.read_text()) - url = config.get('website', {}).get('url', '') - logger.info(f"Loaded website URL: {url}") - return url - except Exception as e: - logger.error(f"Error loading website URL: {str(e)}", exc_info=True) - return '' - -def display_style_analysis(analysis_results: dict): - """Display the style analysis results in a structured format.""" - try: - # Writing Style Section - st.markdown("### 🎨 Writing Style Analysis") - writing_style = analysis_results.get("writing_style", {}) - writing_style_content = f""" - - """ - st.markdown(get_analysis_section("Writing Style", writing_style_content), unsafe_allow_html=True) - - # Content Characteristics Section - content_chars = analysis_results.get("content_characteristics", {}) - content_chars_content = f""" - - """ - st.markdown(get_analysis_section("Content Characteristics", content_chars_content), unsafe_allow_html=True) - - # Target Audience Section - target_audience = analysis_results.get("target_audience", {}) - target_audience_content = f""" - - """ - st.markdown(get_analysis_section("Target Audience", target_audience_content), unsafe_allow_html=True) - - # Content Type Section - content_type = analysis_results.get("content_type", {}) - content_type_content = f""" - - """ - st.markdown(get_analysis_section("Content Type", content_type_content), unsafe_allow_html=True) - - # Recommended Settings Section - recommended = analysis_results.get("recommended_settings", {}) - recommended_content = f""" - - """ - st.markdown(get_analysis_section("Recommended Settings", recommended_content), unsafe_allow_html=True) - - except Exception as e: - logger.error(f"Error displaying style analysis: {str(e)}") - st.error(f"Error displaying analysis results: {str(e)}") - -def render_test_config_settings(): - """Render the test configuration settings page.""" - try: - logger.info("Starting to render test configuration settings") - - # Add back button at the top - col1, col2 = st.columns([1, 3]) - with col1: - if st.button("← Back to Personalization Setup"): - logger.info("User clicked back to personalization setup") - # Set session state for navigation - st.session_state.current_step = 4 - st.session_state.next_step = "personalization_setup" - # Navigate back to the main page where personalization setup is rendered - st.switch_page("alwrity.py") - - # Title and description - st.title("🎨 Find Your Style with ALwrity") - st.markdown(get_glass_container( - "

Enter a website URL or provide content samples to analyze your writing style and get personalized recommendations.

" - ), unsafe_allow_html=True) - - # Create two columns for the layout - col1, col2 = st.columns([2, 1]) - - with col1: - # Website URL input - st.markdown("### Website URL") - url = st.text_input( - "Enter your website URL", - placeholder="https://example.com", - help="Provide your website URL to analyze your content style. Leave empty if you want to provide written samples instead." - ) - logger.debug(f"Website URL input value: {url}") - - # Alternative: Written samples - if not url: - st.markdown("### Written Samples") - st.markdown(get_info_section(""" -

No website URL? No problem! You can provide written samples of your content instead.

-

Share your best articles, blog posts, or any content that represents your writing style.

- """), unsafe_allow_html=True) - samples = st.text_area( - "Paste your content samples here", - help="Paste 2-3 samples of your best content. This helps ALwrity understand your writing style." - ) - logger.debug(f"Sample text length: {len(samples) if samples else 0}") - - st.markdown('', unsafe_allow_html=True) - - # ALwrity Style button - st.markdown("
", unsafe_allow_html=True) - if st.button("🎨 ALwrity Style", use_container_width=True): - if url: - with st.status("Starting style analysis...", expanded=True) as status: - try: - logger.info(f"Starting style analysis for URL: {url}") - - # Step 1: Initialize crawler - status.update(label="Step 1/4: Initializing web crawler...", state="running") - crawler_service = AsyncWebCrawlerService() - - # Step 2: Crawl website - status.update(label="Step 2/4: Crawling website content...", state="running") - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - result = loop.run_until_complete(crawler_service.crawl_website(url)) - loop.close() - - if result.get('success', False): - content = result.get('content', {}) - - # Step 3: Initialize style analyzer - status.update(label="Step 3/4: Analyzing content style...", state="running") - style_analyzer = StyleAnalyzer() - - # Step 4: Perform style analysis - status.update(label="Step 4/4: Generating style recommendations...", state="running") - style_analysis = style_analyzer.analyze_content_style(content) - - if style_analysis.get('error'): - status.update(label="Analysis failed", state="error") - st.error(f"Style analysis failed: {style_analysis['error']}") - else: - status.update(label="Analysis complete!", state="complete") - # Display style analysis results - display_style_analysis(style_analysis) - - # Display original content in tabs - tab1, tab2, tab3 = st.tabs(["Content", "Metadata", "Links"]) - - with tab1: - st.markdown("### Main Content") - st.markdown(content.get('main_content', 'No content found')) - - with tab2: - st.markdown("### Metadata") - st.markdown(f""" - **Title:** {content.get('title', 'No title found')} - - **Description:** {content.get('description', 'No description found')} - - **Meta Tags:** - {content.get('meta_tags', {})} - """) - - with tab3: - st.markdown("### Links") - for link in content.get('links', []): - st.markdown(f"- [{link.get('text', '')}]({link.get('href', '')})") - - else: - status.update(label="Crawling failed", state="error") - st.error(f"Failed to analyze website: {result.get('error', 'Unknown error')}") - - except Exception as e: - logger.error(f"Error during style analysis: {str(e)}") - st.error(f"Analysis failed: {str(e)}") - elif samples: - with st.spinner("Analyzing content samples..."): - try: - # TODO: Implement sample text analysis - st.info("Sample text analysis coming soon!") - except Exception as e: - logger.error(f"Error analyzing samples: {str(e)}") - st.error(f"Analysis failed: {str(e)}") - else: - st.warning("Please provide either a website URL or content samples") - - with col2: - st.markdown(""" - ### How ALwrity Discovers Your Style - - **AI-Powered Style Analysis** - - ALwrity AI analyzes your existing content to understand your unique writing style and preferences. This helps us generate content that matches your voice perfectly. - - **Step 1: Content Analysis** - - We'll analyze your website content or written samples to understand: - - - Writing tone and voice - - Vocabulary and language style - - Content structure and formatting - - Target audience and engagement style - - **Step 2: Style Recommendations** - - Based on the analysis, we'll provide: - - - Personalized writing guidelines - - Content structure templates - - Tone and voice recommendations - - Audience engagement strategies - - **Step 3: Content Generation** - - Finally, we'll use these insights to: - - - Generate content that matches your style - - Maintain consistency across all content - - Optimize for your target audience - - Ensure brand voice alignment - """) - - except Exception as e: - logger.error(f"Error in render_test_config_settings: {str(e)}") - st.error(f"An error occurred: {str(e)}") - -if __name__ == "__main__": - logger.info("Starting test config settings page") - render_test_config_settings() - logger.info("Test config settings page rendered successfully") \ No newline at end of file diff --git a/ToBeMigrated/utils/voice_processing.py b/ToBeMigrated/utils/voice_processing.py deleted file mode 100644 index 912dfced..00000000 --- a/ToBeMigrated/utils/voice_processing.py +++ /dev/null @@ -1,23 +0,0 @@ -import streamlit as st -from streamlit_mic_recorder import speech_to_text - -def record_voice(language="en"): - # https://github.com/B4PT0R/streamlit-mic-recorder?tab=readme-ov-file#example - state = st.session_state - if "text_received" not in state: - state.text_received = [] - - text = speech_to_text( - start_prompt="πŸŽ™οΈPress & SpeakπŸ”Š", - stop_prompt="πŸ”‡Stop Recording🚨", - language=language, - use_container_width=True, - just_once=False, - ) - if text: - state.text_received.append(text) - result = "" - for text in state.text_received: - result += text - state.text_received = [] - return result if result else None diff --git a/ToBeMigrated/utils/website_analyzer/models.py b/ToBeMigrated/utils/website_analyzer/models.py deleted file mode 100644 index b5b55054..00000000 --- a/ToBeMigrated/utils/website_analyzer/models.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Data models for website analysis results.""" - -from dataclasses import dataclass -from typing import List, Dict, Optional -from datetime import datetime - -@dataclass -class SEORecommendation: - """A single SEO recommendation.""" - priority: str # 'high', 'medium', 'low' - category: str # 'content', 'technical', 'meta', etc. - issue: str - recommendation: str - impact: str - -@dataclass -class MetaTagAnalysis: - """Analysis of meta tags.""" - title: Dict[str, str] # {'status': 'good', 'value': 'actual title', 'recommendation': 'suggestion'} - description: Dict[str, str] - keywords: Dict[str, str] - has_robots: bool - has_sitemap: bool - -@dataclass -class ContentAnalysis: - """Analysis of page content.""" - word_count: int - headings_structure: Dict[str, int] # {'h1': 1, 'h2': 3, etc} - keyword_density: Dict[str, float] - readability_score: float - content_quality_score: float - -@dataclass -class SEOAnalysisResult: - """Complete SEO analysis result.""" - url: str - analyzed_at: datetime - overall_score: float # 0-100 - meta_tags: MetaTagAnalysis - content: ContentAnalysis - recommendations: List[SEORecommendation] - errors: List[str] - warnings: List[str] - success: bool \ No newline at end of file diff --git a/backend/api/content_planning/api/content_strategy/endpoints/ai_generation_endpoints.py b/backend/api/content_planning/api/content_strategy/endpoints/ai_generation_endpoints.py index d75bbb3e..1df7e533 100644 --- a/backend/api/content_planning/api/content_strategy/endpoints/ai_generation_endpoints.py +++ b/backend/api/content_planning/api/content_strategy/endpoints/ai_generation_endpoints.py @@ -8,8 +8,6 @@ from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from loguru import logger from datetime import datetime -from fastapi.responses import StreamingResponse -import json # Import database from services.database import get_db_session @@ -19,6 +17,9 @@ from ....services.content_strategy.ai_generation import AIStrategyGenerator, Str from ....services.enhanced_strategy_service import EnhancedStrategyService from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService +# Import educational content manager +from .content_strategy.educational_content import EducationalContentManager + # Import utilities from ....utils.error_handlers import ContentPlanningErrorHandler from ....utils.response_builders import ResponseBuilder @@ -34,6 +35,9 @@ def get_db(): finally: db.close() +# Global storage for latest strategies (more persistent than task status) +_latest_strategies = {} + @router.post("/generate-comprehensive-strategy") async def generate_comprehensive_strategy( user_id: int, @@ -302,427 +306,194 @@ async def optimize_existing_strategy( logger.error(f"❌ Error optimizing strategy: {str(e)}") raise ContentPlanningErrorHandler.handle_general_error(e, "optimize_existing_strategy") -@router.get("/generate-comprehensive-strategy/stream") -async def generate_comprehensive_strategy_stream( - user_id: int, - strategy_name: Optional[str] = None, - config: Optional[Dict[str, Any]] = None, +@router.post("/generate-comprehensive-strategy-polling") +async def generate_comprehensive_strategy_polling( + request: Dict[str, Any], db: Session = Depends(get_db) -): - """Generate comprehensive AI strategy with Server-Sent Events for progress updates.""" +) -> Dict[str, Any]: + """Generate a comprehensive AI-powered content strategy using polling approach.""" try: - logger.info(f"πŸš€ Starting streaming AI strategy generation for user: {user_id}") + # Extract parameters from request body + user_id = request.get("user_id", 1) + strategy_name = request.get("strategy_name") + config = request.get("config", {}) - async def generate_strategy_stream(): - try: - # Step 1: Get user context with educational content - yield f"data: {json.dumps({ - 'step': 1, - 'message': 'Getting user context...', - 'progress': 10, - 'educational_content': { - 'title': 'πŸ” Analyzing Your Data', - 'description': 'We\'re gathering all your onboarding information to create a personalized strategy.', - 'details': [ - 'πŸ“Š Website analysis data', - '🎯 Research preferences', - 'πŸ”‘ API configurations', - 'πŸ“ˆ Historical performance metrics' - ], - 'insight': 'Your data helps us understand your business context, target audience, and competitive landscape.', - 'ai_prompt_preview': 'Analyzing user onboarding data to extract business context, audience insights, and competitive positioning...' - } - })}\n\n" + logger.info(f"πŸš€ Starting polling-based AI strategy generation for user: {user_id}") + + # Get user context and onboarding data + db_service = EnhancedStrategyDBService(db) + enhanced_service = EnhancedStrategyService(db_service) + + # Get onboarding data for context + onboarding_data = await enhanced_service._get_onboarding_data(user_id) - db_service = EnhancedStrategyDBService(db) - enhanced_service = EnhancedStrategyService(db_service) - onboarding_data = await enhanced_service._get_onboarding_data(user_id) - - context = { + # Build context for AI generation + context = { "onboarding_data": onboarding_data, "user_id": user_id, "generation_config": config or {} } - # Step 2: Generate base strategy fields - yield f"data: {json.dumps({ - 'step': 2, - 'message': 'Generating base strategy fields...', - 'progress': 20, - 'educational_content': { - 'title': 'πŸ—οΈ Building Foundation', - 'description': 'Creating the core strategy framework based on your business objectives.', - 'details': [ - '🎯 Business objectives mapping', - 'πŸ“Š Target metrics definition', - 'πŸ’° Budget allocation strategy', - '⏰ Timeline planning' - ], - 'insight': 'A solid foundation ensures your content strategy aligns with business goals and resources.', - 'ai_prompt_preview': 'Generating strategic foundation: business objectives, target metrics, budget allocation, and timeline planning...' - } - })}\n\n" + # Create strategy generation config + generation_config = StrategyGenerationConfig( + include_competitive_analysis=config.get("include_competitive_analysis", True) if config else True, + include_content_calendar=config.get("include_content_calendar", True) if config else True, + include_performance_predictions=config.get("include_performance_predictions", True) if config else True, + include_implementation_roadmap=config.get("include_implementation_roadmap", True) if config else True, + include_risk_assessment=config.get("include_risk_assessment", True) if config else True, + max_content_pieces=config.get("max_content_pieces", 50) if config else 50, + timeline_months=config.get("timeline_months", 12) if config else 12 + ) # Initialize AI strategy generator - from ....services.content_strategy.ai_generation import AIStrategyGenerator - strategy_generator = AIStrategyGenerator() + strategy_generator = AIStrategyGenerator(generation_config) + + # Start generation in background (non-blocking) + import asyncio + import uuid + + # Generate unique task ID + task_id = str(uuid.uuid4()) + + # Store initial status + generation_status = { + "task_id": task_id, + "user_id": user_id, + "status": "started", + "progress": 0, + "step": 0, + "message": "Initializing AI strategy generation...", + "started_at": datetime.utcnow().isoformat(), + "estimated_completion": None, + "strategy": None, + "error": None, + "educational_content": EducationalContentManager.get_initialization_content() + } + + # Store status in memory (in production, use Redis or database) + if not hasattr(generate_comprehensive_strategy_polling, '_task_status'): + generate_comprehensive_strategy_polling._task_status = {} + + generate_comprehensive_strategy_polling._task_status[task_id] = generation_status + + # Start background task + async def generate_strategy_background(): + try: + logger.info(f"πŸ”„ Starting background strategy generation for task: {task_id}") - # Step 3: Generate strategic insights with real-time educational content - yield f"data: {json.dumps({ - 'step': 3, - 'message': 'Generating strategic insights...', - 'progress': 30, - 'educational_content': { - 'title': '🧠 Strategic Intelligence Analysis', - 'description': 'AI is analyzing your market position and identifying strategic opportunities.', - 'details': [ - '🎯 Market positioning analysis', - 'πŸ’‘ Opportunity identification', - 'πŸ“ˆ Growth potential assessment', - 'πŸŽͺ Competitive advantage mapping' - ], - 'insight': 'Strategic insights help you understand where you stand in the market and how to differentiate.', - 'ai_prompt_preview': 'Analyzing market position, identifying strategic opportunities, assessing growth potential, and mapping competitive advantages...', - 'estimated_time': '15-20 seconds' - } - })}\n\n" + # Step 1: Get user context + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 1, + "progress": 10, + "message": "Getting user context...", + "educational_content": EducationalContentManager.get_step_content(1) + }) - try: - # Create a custom AI service manager that emits educational content to SSE - from services.ai_service_manager import AIServiceManager, AIServiceType - - class SSEAIServiceManager(AIServiceManager): - def __init__(self, sse_yield_func): - super().__init__() - self.sse_yield = sse_yield_func - - async def _emit_educational_content(self, service_type: AIServiceType, status: str, error_message: str = None, processing_time: float = None): - """Override to emit educational content to SSE stream.""" - try: - educational_content = self._get_educational_content(service_type, status, error_message, processing_time) - - # Emit to SSE stream - yield_data = { - 'type': 'educational_content', - 'service_type': service_type.value, - 'status': status, - 'educational_content': educational_content - } - - if processing_time: - yield_data['processing_time'] = processing_time - if error_message: - yield_data['error_message'] = error_message - - await self.sse_yield(f"data: {json.dumps(yield_data)}\n\n") - logger.info(f"πŸ“š Emitted educational content for {service_type.value}: {status}") - - except Exception as e: - logger.error(f"Error emitting educational content to SSE: {e}") - - # Use the SSE-enabled AI service manager - sse_ai_manager = SSEAIServiceManager(lambda data: generate_strategy_stream().__anext__()) - - # Generate strategic insights with educational content - strategic_insights = await strategy_generator._generate_strategic_insights({}, context, sse_ai_manager) - - yield f"data: {json.dumps({ - 'step': 3, - 'message': 'Strategic insights generated successfully', - 'progress': 35, - 'success': True, - 'educational_content': { - 'title': 'βœ… Strategic Insights Complete', - 'description': 'Successfully identified key strategic opportunities and market positioning.', - 'achievement': f'Generated {len(strategic_insights.get("insights", []))} strategic insights', - 'next_step': 'Moving to competitive analysis...' - } - })}\n\n" - except Exception as e: - yield f"data: {json.dumps({ - 'step': 3, - 'message': f'Strategic insights generation failed: {str(e)}', - 'progress': 35, - 'success': False, - 'error': str(e), - 'educational_content': { - 'title': '⚠️ Strategic Insights Issue', - 'description': 'We encountered an issue with strategic analysis, but continuing with other components.', - 'fallback': 'Will use industry best practices for strategic positioning.' - } - })}\n\n" - strategic_insights = {} + # Step 2: Generate base strategy fields + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 2, + "progress": 20, + "message": "Generating base strategy fields...", + "educational_content": EducationalContentManager.get_step_content(2) + }) - # Step 4: Generate competitive analysis with educational content - yield f"data: {json.dumps({ - 'step': 4, - 'message': 'Generating competitive analysis...', - 'progress': 40, - 'educational_content': { - 'title': 'πŸ” Competitive Intelligence Analysis', - 'description': 'AI is analyzing your competitors to identify gaps and opportunities.', - 'details': [ - '🏒 Competitor content strategies', - 'πŸ“Š Market gap analysis', - '🎯 Differentiation opportunities', - 'πŸ“ˆ Industry trend analysis' - ], - 'insight': 'Understanding your competitors helps you find unique angles and underserved market segments.', - 'ai_prompt_preview': 'Analyzing competitor content strategies, identifying market gaps, finding differentiation opportunities, and assessing industry trends...', - 'estimated_time': '20-25 seconds' - } - })}\n\n" + # Step 3: Generate strategic insights + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 3, + "progress": 30, + "message": "Generating strategic insights...", + "educational_content": EducationalContentManager.get_step_content(3) + }) - try: - competitive_analysis = await strategy_generator._generate_competitive_analysis({}, context, sse_ai_manager) - yield f"data: {json.dumps({ - 'step': 4, - 'message': 'Competitive analysis generated successfully', - 'progress': 45, - 'success': True, - 'educational_content': { - 'title': 'βœ… Competitive Analysis Complete', - 'description': 'Successfully analyzed competitive landscape and identified market opportunities.', - 'achievement': f'Analyzed {len(competitive_analysis.get("competitors", []))} competitors', - 'next_step': 'Moving to content calendar generation...' - } - })}\n\n" - except Exception as e: - yield f"data: {json.dumps({ - 'step': 4, - 'message': f'Competitive analysis generation failed: {str(e)}', - 'progress': 45, - 'success': False, - 'error': str(e), - 'educational_content': { - 'title': '⚠️ Competitive Analysis Issue', - 'description': 'We encountered an issue with competitive analysis, but continuing with other components.', - 'fallback': 'Will use industry best practices for competitive positioning.' - } - })}\n\n" - competitive_analysis = {} + strategic_insights = await strategy_generator._generate_strategic_insights({}, context) - # Step 5: Generate content calendar with educational content - yield f"data: {json.dumps({ - 'step': 5, - 'message': 'Generating content calendar...', - 'progress': 50, - 'educational_content': { - 'title': 'πŸ“… Content Calendar Creation', - 'description': 'AI is building a comprehensive content schedule optimized for your audience.', - 'details': [ - 'πŸ“ Content piece generation', - 'πŸ“… Optimal publishing schedule', - '🎯 Audience engagement timing', - 'πŸ”„ Content repurposing strategy' - ], - 'insight': 'A well-planned content calendar ensures consistent engagement and maximizes content ROI.', - 'ai_prompt_preview': 'Generating content pieces, optimizing publishing schedule, determining audience engagement timing, and planning content repurposing...', - 'estimated_time': '25-30 seconds' - } - })}\n\n" + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 3, + "progress": 35, + "message": "Strategic insights generated successfully", + "educational_content": EducationalContentManager.get_step_completion_content(3, strategic_insights) + }) - try: - content_calendar = await strategy_generator._generate_content_calendar({}, context, sse_ai_manager) - yield f"data: {json.dumps({ - 'step': 5, - 'message': 'Content calendar generated successfully', - 'progress': 55, - 'success': True, - 'educational_content': { - 'title': 'βœ… Content Calendar Complete', - 'description': 'Successfully created comprehensive content schedule.', - 'achievement': f'Generated {len(content_calendar.get("content_pieces", []))} content pieces', - 'next_step': 'Moving to performance predictions...' - } - })}\n\n" - except Exception as e: - yield f"data: {json.dumps({ - 'step': 5, - 'message': f'Content calendar generation failed: {str(e)}', - 'progress': 55, - 'success': False, - 'error': str(e), - 'educational_content': { - 'title': '⚠️ Content Calendar Issue', - 'description': 'We encountered an issue with content calendar generation, but continuing with other components.', - 'fallback': 'Will use industry best practices for content scheduling.' - } - })}\n\n" - content_calendar = {} + # Step 4: Generate competitive analysis + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 4, + "progress": 40, + "message": "Generating competitive analysis...", + "educational_content": EducationalContentManager.get_step_content(4) + }) - # Step 6: Generate performance predictions with educational content - yield f"data: {json.dumps({ - 'step': 6, - 'message': 'Generating performance predictions...', - 'progress': 60, - 'educational_content': { - 'title': 'πŸ“Š Performance Forecasting', - 'description': 'AI is predicting content performance and ROI based on industry data.', - 'details': [ - 'πŸ“ˆ Traffic growth projections', - 'πŸ’° ROI predictions', - '🎯 Conversion rate estimates', - 'πŸ“Š Engagement metrics forecasting' - ], - 'insight': 'Performance predictions help you set realistic expectations and optimize resource allocation.', - 'ai_prompt_preview': 'Analyzing industry benchmarks, predicting traffic growth, estimating ROI, forecasting conversion rates, and projecting engagement metrics...', - 'estimated_time': '15-20 seconds' - } - })}\n\n" + competitive_analysis = await strategy_generator._generate_competitive_analysis({}, context) - try: - performance_predictions = await strategy_generator._generate_performance_predictions({}, context, sse_ai_manager) - yield f"data: {json.dumps({ - 'step': 6, - 'message': 'Performance predictions generated successfully', - 'progress': 65, - 'success': True, - 'educational_content': { - 'title': 'βœ… Performance Predictions Complete', - 'description': 'Successfully predicted content performance and ROI.', - 'achievement': f'Predicted {performance_predictions.get("estimated_roi", "15-25%")} ROI', - 'next_step': 'Moving to implementation roadmap...' - } - })}\n\n" - except Exception as e: - yield f"data: {json.dumps({ - 'step': 6, - 'message': f'Performance predictions generation failed: {str(e)}', - 'progress': 65, - 'success': False, - 'error': str(e), - 'educational_content': { - 'title': '⚠️ Performance Predictions Issue', - 'description': 'We encountered an issue with performance predictions, but continuing with other components.', - 'fallback': 'Will use industry benchmarks for performance estimates.' - } - })}\n\n" - performance_predictions = {} + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 4, + "progress": 45, + "message": "Competitive analysis generated successfully", + "educational_content": EducationalContentManager.get_step_completion_content(4, competitive_analysis) + }) - # Step 7: Generate implementation roadmap with educational content - yield f"data: {json.dumps({ - 'step': 7, - 'message': 'Generating implementation roadmap...', - 'progress': 70, - 'educational_content': { - 'title': 'πŸ—ΊοΈ Implementation Roadmap', - 'description': 'AI is creating a detailed implementation plan for your content strategy.', - 'details': [ - 'πŸ“‹ Task breakdown and timeline', - 'πŸ‘₯ Resource allocation planning', - '🎯 Milestone definition', - 'πŸ“Š Success metric tracking' - ], - 'insight': 'A clear implementation roadmap ensures successful strategy execution and measurable results.', - 'ai_prompt_preview': 'Creating implementation roadmap: task breakdown, resource allocation, milestone planning, and success metric definition...', - 'estimated_time': '15-20 seconds' - } - })}\n\n" + # Step 5: Generate performance predictions + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 4, + "progress": 40, + "message": "Generating performance predictions...", + "educational_content": EducationalContentManager.get_step_content(4) + }) - try: - implementation_roadmap = await strategy_generator._generate_implementation_roadmap({}, context, sse_ai_manager) - yield f"data: {json.dumps({ - 'step': 7, - 'message': 'Implementation roadmap generated successfully', - 'progress': 75, - 'success': True, - 'educational_content': { - 'title': 'βœ… Implementation Roadmap Complete', - 'description': 'Successfully created detailed implementation plan.', - 'achievement': f'Planned {implementation_roadmap.get("total_duration", "12 months")} implementation timeline', - 'next_step': 'Moving to risk assessment...' - } - })}\n\n" - except Exception as e: - yield f"data: {json.dumps({ - 'step': 7, - 'message': f'Implementation roadmap generation failed: {str(e)}', - 'progress': 75, - 'success': False, - 'error': str(e), - 'educational_content': { - 'title': '⚠️ Implementation Roadmap Issue', - 'description': 'We encountered an issue with implementation roadmap generation, but continuing with other components.', - 'fallback': 'Will use industry best practices for implementation planning.' - } - })}\n\n" - implementation_roadmap = {} + performance_predictions = await strategy_generator._generate_performance_predictions({}, context) - # Step 8: Generate risk assessment with educational content - yield f"data: {json.dumps({ - 'step': 8, - 'message': 'Generating risk assessment...', - 'progress': 80, - 'educational_content': { - 'title': '⚠️ Risk Assessment', - 'description': 'AI is identifying potential risks and mitigation strategies for your content strategy.', - 'details': [ - 'πŸ” Risk identification and analysis', - 'πŸ“Š Risk probability assessment', - 'πŸ›‘οΈ Mitigation strategy development', - 'πŸ“ˆ Risk monitoring framework' - ], - 'insight': 'Proactive risk assessment helps you prepare for challenges and maintain strategy effectiveness.', - 'ai_prompt_preview': 'Assessing risks: identifying potential challenges, analyzing probability and impact, developing mitigation strategies, and creating monitoring framework...', - 'estimated_time': '10-15 seconds' - } - })}\n\n" + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 4, + "progress": 45, + "message": "Performance predictions generated successfully", + "educational_content": EducationalContentManager.get_step_completion_content(4, performance_predictions) + }) - try: - risk_assessment = await strategy_generator._generate_risk_assessment({}, context, sse_ai_manager) - yield f"data: {json.dumps({ - 'step': 8, - 'message': 'Risk assessment generated successfully', - 'progress': 85, - 'success': True, - 'educational_content': { - 'title': 'βœ… Risk Assessment Complete', - 'description': 'Successfully identified risks and mitigation strategies.', - 'achievement': f'Assessed {risk_assessment.get("overall_risk_level", "Medium")} risk level', - 'next_step': 'Finalizing comprehensive strategy...' - } - })}\n\n" - except Exception as e: - yield f"data: {json.dumps({ - 'step': 8, - 'message': f'Risk assessment generation failed: {str(e)}', - 'progress': 85, - 'success': False, - 'error': str(e), - 'educational_content': { - 'title': '⚠️ Risk Assessment Issue', - 'description': 'We encountered an issue with risk assessment, but continuing with strategy finalization.', - 'fallback': 'Will use industry best practices for risk management.' - } - })}\n\n" - risk_assessment = {} + # Step 5: Generate implementation roadmap + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 5, + "progress": 50, + "message": "Generating implementation roadmap...", + "educational_content": EducationalContentManager.get_step_content(5) + }) - # Step 9: Compile comprehensive strategy - yield f"data: {json.dumps({ - 'step': 9, - 'message': 'Compiling comprehensive strategy...', - 'progress': 90, - 'educational_content': { - 'title': 'πŸ“‹ Strategy Compilation', - 'description': 'AI is compiling all components into a comprehensive content strategy.', - 'details': [ - 'πŸ”— Component integration', - 'πŸ“Š Data synthesis', - 'πŸ“ Strategy documentation', - 'βœ… Quality validation' - ], - 'insight': 'A comprehensive strategy integrates all components into a cohesive, actionable plan.', - 'ai_prompt_preview': 'Compiling comprehensive strategy: integrating all components, synthesizing data, documenting strategy, and validating quality...', - 'estimated_time': '5-10 seconds' - } - })}\n\n" + implementation_roadmap = await strategy_generator._generate_implementation_roadmap({}, context) - # Compile the comprehensive strategy + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 5, + "progress": 55, + "message": "Implementation roadmap generated successfully", + "educational_content": EducationalContentManager.get_step_completion_content(5, implementation_roadmap) + }) + + # Step 6: Generate risk assessment + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 6, + "progress": 60, + "message": "Generating risk assessment...", + "educational_content": EducationalContentManager.get_step_content(6) + }) + + risk_assessment = await strategy_generator._generate_risk_assessment({}, context) + + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 6, + "progress": 65, + "message": "Risk assessment generated successfully", + "educational_content": EducationalContentManager.get_step_completion_content(6, risk_assessment) + }) + + # Step 7: Compile comprehensive strategy + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 7, + "progress": 70, + "message": "Compiling comprehensive strategy...", + "educational_content": EducationalContentManager.get_step_content(7) + }) + + # Compile the comprehensive strategy (NO CONTENT CALENDAR) comprehensive_strategy = { "strategic_insights": strategic_insights, "competitive_analysis": competitive_analysis, - "content_calendar": content_calendar, "performance_predictions": performance_predictions, "implementation_roadmap": implementation_roadmap, "risk_assessment": risk_assessment, @@ -731,265 +502,226 @@ async def generate_comprehensive_strategy_stream( "comprehensive": True, "generation_timestamp": datetime.utcnow().isoformat(), "user_id": user_id, - "strategy_name": strategy_name or "Enhanced Content Strategy" + "strategy_name": strategy_name or "Enhanced Content Strategy", + "content_calendar_ready": False # Indicates calendar needs to be generated separately } } - # Step 10: Complete with educational content - yield f"data: {json.dumps({ - 'step': 10, - 'message': 'Strategy generation completed successfully!', - 'progress': 100, - 'success': True, - 'strategy': comprehensive_strategy, - 'educational_content': { - 'title': 'πŸŽ‰ Strategy Generation Complete!', - 'description': 'Your comprehensive AI-powered content strategy is ready!', - 'summary': { - 'total_components': 6, - 'successful_components': sum([ - 1 if strategic_insights else 0, - 1 if competitive_analysis else 0, - 1 if content_calendar else 0, - 1 if performance_predictions else 0, - 1 if implementation_roadmap else 0, - 1 if risk_assessment else 0 - ]), - 'total_content_pieces': len(content_calendar.get("content_pieces", [])), - 'estimated_roi': performance_predictions.get("estimated_roi", "15-25%"), - 'implementation_timeline': implementation_roadmap.get("total_duration", "12 months"), - 'risk_level': risk_assessment.get("overall_risk_level", "Medium") - }, - 'key_achievements': [ - '🧠 Strategic insights generated', - 'πŸ” Competitive analysis completed', - 'πŸ“… Content calendar created', - 'πŸ“Š Performance predictions calculated', - 'πŸ—ΊοΈ Implementation roadmap planned', - '⚠️ Risk assessment conducted' - ], - 'next_steps': [ - 'Review your comprehensive strategy', - 'Customize specific components as needed', - 'Share with your team for feedback', - 'Begin implementation following the roadmap' - ], - 'ai_insights': 'Your strategy leverages advanced AI analysis of your business context, competitive landscape, and industry best practices to create a data-driven content approach.', - 'personalization_note': 'This strategy is uniquely tailored to your business based on your onboarding data, ensuring relevance and effectiveness.' + # Step 8: Complete + completion_content = EducationalContentManager.get_step_content(8) + completion_content = EducationalContentManager.update_completion_summary( + completion_content, + { + "performance_predictions": performance_predictions, + "implementation_roadmap": implementation_roadmap, + "risk_assessment": risk_assessment } - })}\n\n" + ) + + # Save the comprehensive strategy to database + try: + from models.enhanced_strategy_models import EnhancedContentStrategy + + # Create enhanced strategy record + enhanced_strategy = EnhancedContentStrategy( + user_id=user_id, + name=strategy_name or "Enhanced Content Strategy", + industry="technology", # Default, can be updated later + + # Store the comprehensive AI analysis in the dedicated field + comprehensive_ai_analysis=comprehensive_strategy, + + # Store metadata + ai_recommendations=comprehensive_strategy, + + # Mark as AI-generated and comprehensive + created_at=datetime.utcnow(), + updated_at=datetime.utcnow() + ) + + # Add to database + db.add(enhanced_strategy) + db.commit() + db.refresh(enhanced_strategy) + + logger.info(f"πŸ’Ύ Strategy saved to database with ID: {enhanced_strategy.id}") + + # Update the comprehensive strategy with the database ID + comprehensive_strategy["metadata"]["strategy_id"] = enhanced_strategy.id + + except Exception as db_error: + logger.error(f"❌ Error saving strategy to database: {str(db_error)}") + # Continue without database save, strategy is still available in memory + + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "step": 8, + "progress": 100, + "status": "completed", + "message": "Strategy generation completed successfully!", + "strategy": comprehensive_strategy, + "completed_at": datetime.utcnow().isoformat(), + "educational_content": completion_content + }) + + # Store in global latest strategies for persistent access + _latest_strategies[user_id] = { + "strategy": comprehensive_strategy, + "completed_at": datetime.utcnow().isoformat(), + "task_id": task_id + } + + logger.info(f"βœ… Background strategy generation completed for task: {task_id}") + logger.info(f"πŸ’Ύ Strategy stored in global storage for user: {user_id}") except Exception as e: - logger.error(f"❌ Error in streaming strategy generation: {str(e)}") - yield f"data: {json.dumps({'error': f'Strategy generation failed: {str(e)}', 'progress': 0, 'success': False})}\n\n" + logger.error(f"❌ Error in background strategy generation for task {task_id}: {str(e)}") + generate_comprehensive_strategy_polling._task_status[task_id].update({ + "status": "failed", + "error": str(e), + "message": f"Strategy generation failed: {str(e)}", + "failed_at": datetime.utcnow().isoformat() + }) - return StreamingResponse( - generate_strategy_stream(), - media_type="text/plain", - headers={ - "Cache-Control": "no-cache", - "Connection": "keep-alive", - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Cache-Control" - } - ) + # Start the background task + asyncio.create_task(generate_strategy_background()) - except Exception as e: - logger.error(f"❌ Error starting streaming strategy generation: {str(e)}") - raise HTTPException( - status_code=500, - detail=f"Failed to start streaming strategy generation: {str(e)}" - ) - -@router.get("/ai-generation-education") -async def get_ai_generation_education() -> Dict[str, Any]: - """Get educational content about the AI generation process.""" - try: - logger.info("πŸ“š Providing AI generation educational content") - - educational_content = { - "title": "πŸ€– AI-Powered Strategy Generation", - "subtitle": "Understanding How We Create Your Content Strategy", - "overview": { - "description": "Our AI system analyzes your business data and generates a comprehensive content strategy using advanced machine learning and industry best practices.", - "total_time": "2-3 minutes", - "components": 6, - "ai_model": "Google Gemini Pro", - "personalization_level": "High" - }, - "process_steps": [ - { - "step": 1, - "title": "πŸ” Data Analysis", - "description": "Analyzing your onboarding data to understand your business context", - "duration": "5-10 seconds", - "details": [ - "Website analysis data processing", - "Research preferences analysis", - "API configuration review", - "Historical performance assessment" - ], - "ai_prompt_example": "Analyze user onboarding data to extract business context, audience insights, and competitive positioning..." - }, - { - "step": 2, - "title": "πŸ—οΈ Foundation Building", - "description": "Creating the core strategy framework based on your objectives", - "duration": "5-10 seconds", - "details": [ - "Business objectives mapping", - "Target metrics definition", - "Budget allocation strategy", - "Timeline planning" - ], - "ai_prompt_example": "Generate strategic foundation: business objectives, target metrics, budget allocation, and timeline planning..." - }, - { - "step": 3, - "title": "🧠 Strategic Intelligence", - "description": "AI analyzes your market position and identifies opportunities", - "duration": "15-20 seconds", - "details": [ - "Market positioning analysis", - "Opportunity identification", - "Growth potential assessment", - "Competitive advantage mapping" - ], - "ai_prompt_example": "Analyze market position, identify strategic opportunities, assess growth potential, and map competitive advantages..." - }, - { - "step": 4, - "title": "πŸ” Competitive Intelligence", - "description": "Analyzing competitors to identify gaps and opportunities", - "duration": "20-25 seconds", - "details": [ - "Competitor content strategies", - "Market gap analysis", - "Differentiation opportunities", - "Industry trend analysis" - ], - "ai_prompt_example": "Analyze competitor content strategies, identify market gaps, find differentiation opportunities, and assess industry trends..." - }, - { - "step": 5, - "title": "πŸ“… Content Calendar Creation", - "description": "Building a comprehensive content schedule optimized for your audience", - "duration": "25-30 seconds", - "details": [ - "Content piece generation", - "Optimal publishing schedule", - "Audience engagement timing", - "Content repurposing strategy" - ], - "ai_prompt_example": "Generate content pieces, optimize publishing schedule, determine audience engagement timing, and plan content repurposing..." - }, - { - "step": 6, - "title": "πŸ“Š Performance Forecasting", - "description": "Predicting content performance and ROI based on industry data", - "duration": "15-20 seconds", - "details": [ - "Traffic growth projections", - "ROI predictions", - "Conversion rate estimates", - "Engagement metrics forecasting" - ], - "ai_prompt_example": "Analyze industry benchmarks, predict traffic growth, estimate ROI, forecast conversion rates, and project engagement metrics..." - }, - { - "step": 7, - "title": "πŸ—ΊοΈ Implementation Roadmap", - "description": "Creating a step-by-step plan to execute your strategy", - "duration": "15-20 seconds", - "details": [ - "Phase-by-phase breakdown", - "Timeline with milestones", - "Resource allocation", - "Success checkpoints" - ], - "ai_prompt_example": "Create phase-by-phase breakdown, establish timeline with milestones, allocate resources, and set success checkpoints..." - }, - { - "step": 8, - "title": "⚠️ Risk Assessment", - "description": "Identifying potential challenges and creating mitigation strategies", - "duration": "10-15 seconds", - "details": [ - "Risk identification", - "Risk probability analysis", - "Mitigation strategies", - "Contingency planning" - ], - "ai_prompt_example": "Identify potential risks, analyze risk probabilities, develop mitigation strategies, and create contingency plans..." - } - ], - "ai_technology": { - "model": "Google Gemini Pro", - "capabilities": [ - "Advanced natural language processing", - "Context-aware analysis", - "Industry knowledge integration", - "Personalized recommendations" - ], - "data_sources": [ - "Your onboarding data", - "Industry benchmarks", - "Best practices database", - "Market research insights" - ] - }, - "personalization_features": { - "data_points_used": [ - "Business objectives and goals", - "Target audience demographics", - "Industry and market context", - "Competitive landscape", - "Content preferences and style", - "Budget and resource constraints" - ], - "customization_level": "High", - "adaptation_factors": [ - "Industry-specific insights", - "Audience behavior patterns", - "Competitive positioning", - "Resource availability" - ] - }, - "quality_assurance": { - "validation_steps": [ - "Data completeness check", - "Strategy coherence validation", - "Industry alignment verification", - "Implementation feasibility assessment" - ], - "fallback_mechanisms": [ - "Industry best practices", - "Standard templates", - "Benchmark data", - "Expert recommendations" - ] - }, - "tips_for_users": [ - "πŸ’‘ The more detailed your onboarding data, the more personalized your strategy will be", - "πŸ“Š Review and customize the generated strategy to match your specific needs", - "πŸ”„ Use the strategy as a starting point and iterate based on performance", - "πŸ“ˆ Monitor results and adjust the strategy as your business evolves", - "πŸ‘₯ Share the strategy with your team for feedback and buy-in" - ], - "technical_details": { - "processing_time": "2-3 minutes total", - "ai_calls": "8 specialized AI analyses", - "data_processing": "Real-time onboarding data integration", - "output_format": "Structured JSON with comprehensive strategy components", - "scalability": "Handles multiple concurrent generations" - } - } + logger.info(f"βœ… Polling-based AI strategy generation started for user: {user_id}, task: {task_id}") return ResponseBuilder.create_success_response( - message="AI generation educational content retrieved successfully", - data=educational_content + message="AI strategy generation started successfully", + data={ + "task_id": task_id, + "status": "started", + "message": "Strategy generation is running in the background. Use the task_id to check progress.", + "polling_endpoint": f"/api/content-planning/content-strategy/ai-generation/strategy-generation-status/{task_id}", + "estimated_completion": "2-3 minutes" + } ) except Exception as e: - logger.error(f"❌ Error getting AI generation education: {str(e)}") - raise ContentPlanningErrorHandler.handle_general_error(e, "get_ai_generation_education") \ No newline at end of file + logger.error(f"❌ Error starting polling-based strategy generation: {str(e)}") + raise ContentPlanningErrorHandler.handle_general_error(e, "generate_comprehensive_strategy_polling") + +@router.get("/strategy-generation-status/{task_id}") +async def get_strategy_generation_status_by_task( + task_id: str, + db: Session = Depends(get_db) +) -> Dict[str, Any]: + """Get the status of strategy generation for a specific task.""" + try: + logger.info(f"Getting strategy generation status for task: {task_id}") + + # Check if task status exists + if not hasattr(generate_comprehensive_strategy_polling, '_task_status'): + raise HTTPException( + status_code=404, + detail="No task status found. Task may have expired or never existed." + ) + + task_status = generate_comprehensive_strategy_polling._task_status.get(task_id) + + if not task_status: + raise HTTPException( + status_code=404, + detail=f"Task {task_id} not found. It may have expired or never existed." + ) + + logger.info(f"βœ… Strategy generation status retrieved for task: {task_id}") + + return ResponseBuilder.create_success_response( + message="Strategy generation status retrieved successfully", + data=task_status + ) + + except HTTPException: + raise + except Exception as e: + logger.error(f"❌ Error getting strategy generation status: {str(e)}") + raise ContentPlanningErrorHandler.handle_general_error(e, "get_strategy_generation_status_by_task") + +@router.get("/latest-strategy") +async def get_latest_generated_strategy( + user_id: int = Query(1, description="User ID"), + db: Session = Depends(get_db) +) -> Dict[str, Any]: + """Get the latest generated strategy from the polling system or database.""" + try: + logger.info(f"πŸ” Getting latest generated strategy for user: {user_id}") + + # First, try to get from database (most reliable) + try: + from models.enhanced_strategy_models import EnhancedContentStrategy + from sqlalchemy import desc + + # Query for the most recent strategy with comprehensive AI analysis + latest_db_strategy = db.query(EnhancedContentStrategy).filter( + EnhancedContentStrategy.user_id == user_id, + EnhancedContentStrategy.comprehensive_ai_analysis.isnot(None) + ).order_by(desc(EnhancedContentStrategy.created_at)).first() + + if latest_db_strategy and latest_db_strategy.comprehensive_ai_analysis: + logger.info(f"βœ… Found latest strategy in database: {latest_db_strategy.id}") + return ResponseBuilder.create_success_response( + message="Latest generated strategy retrieved successfully from database", + data={ + "user_id": user_id, + "strategy": latest_db_strategy.comprehensive_ai_analysis, + "completed_at": latest_db_strategy.created_at.isoformat(), + "strategy_id": latest_db_strategy.id + } + ) + except Exception as db_error: + logger.warning(f"⚠️ Database query failed: {str(db_error)}") + + # Fallback: Check in-memory task status + if not hasattr(generate_comprehensive_strategy_polling, '_task_status'): + logger.warning("⚠️ No task status storage found") + return ResponseBuilder.create_not_found_response( + message="No strategy generation tasks found", + data={"user_id": user_id, "strategy": None} + ) + + # Debug: Log all task statuses + logger.info(f"πŸ“Š Total tasks in storage: {len(generate_comprehensive_strategy_polling._task_status)}") + for task_id, task_status in generate_comprehensive_strategy_polling._task_status.items(): + logger.info(f" Task {task_id}: user_id={task_status.get('user_id')}, status={task_status.get('status')}, has_strategy={bool(task_status.get('strategy'))}") + + # Find the most recent completed strategy for this user + latest_strategy = None + latest_completion_time = None + + for task_id, task_status in generate_comprehensive_strategy_polling._task_status.items(): + logger.info(f"πŸ” Checking task {task_id}: user_id={task_status.get('user_id')} vs requested {user_id}") + + if (task_status.get("user_id") == user_id and + task_status.get("status") == "completed" and + task_status.get("strategy")): + + completion_time = task_status.get("completed_at") + logger.info(f"βœ… Found completed strategy for user {user_id} at {completion_time}") + + if completion_time and (latest_completion_time is None or completion_time > latest_completion_time): + latest_strategy = task_status.get("strategy") + latest_completion_time = completion_time + logger.info(f"πŸ”„ Updated latest strategy with completion time: {completion_time}") + + if latest_strategy: + logger.info(f"βœ… Found latest generated strategy for user: {user_id}") + return ResponseBuilder.create_success_response( + message="Latest generated strategy retrieved successfully from memory", + data={ + "user_id": user_id, + "strategy": latest_strategy, + "completed_at": latest_completion_time + } + ) + else: + logger.info(f"⚠️ No completed strategies found for user: {user_id}") + return ResponseBuilder.create_not_found_response( + message="No completed strategy generation found", + data={"user_id": user_id, "strategy": None} + ) + + except Exception as e: + logger.error(f"❌ Error getting latest generated strategy: {str(e)}") + raise ContentPlanningErrorHandler.handle_general_error(e, "get_latest_generated_strategy") diff --git a/backend/api/content_planning/api/content_strategy/endpoints/content_strategy/__init__.py b/backend/api/content_planning/api/content_strategy/endpoints/content_strategy/__init__.py new file mode 100644 index 00000000..e6db6f56 --- /dev/null +++ b/backend/api/content_planning/api/content_strategy/endpoints/content_strategy/__init__.py @@ -0,0 +1,8 @@ +""" +Content Strategy Educational Content Module +Provides educational content and messages for strategy generation process. +""" + +from .educational_content import EducationalContentManager + +__all__ = ['EducationalContentManager'] \ No newline at end of file diff --git a/backend/api/content_planning/api/content_strategy/endpoints/content_strategy/educational_content.py b/backend/api/content_planning/api/content_strategy/endpoints/content_strategy/educational_content.py new file mode 100644 index 00000000..74d7ca8a --- /dev/null +++ b/backend/api/content_planning/api/content_strategy/endpoints/content_strategy/educational_content.py @@ -0,0 +1,319 @@ +""" +Educational Content Manager +Manages educational content and messages for strategy generation process. +""" + +from typing import Dict, Any, List +from datetime import datetime + + +class EducationalContentManager: + """Manages educational content for strategy generation steps.""" + + @staticmethod + def get_initialization_content() -> Dict[str, Any]: + """Get educational content for initialization step.""" + return { + "title": "πŸ€– AI-Powered Strategy Generation", + "description": "Initializing AI analysis and preparing educational content...", + "details": [ + "πŸ”§ Setting up AI services", + "πŸ“Š Loading user context", + "🎯 Preparing strategy framework", + "πŸ“š Generating educational content" + ], + "insight": "We're getting everything ready for your personalized AI strategy generation.", + "estimated_time": "2-3 minutes total" + } + + @staticmethod + def get_step_content(step: int) -> Dict[str, Any]: + """Get educational content for a specific step.""" + step_content = { + 1: EducationalContentManager._get_user_context_content(), + 2: EducationalContentManager._get_foundation_content(), + 3: EducationalContentManager._get_strategic_insights_content(), + 4: EducationalContentManager._get_competitive_analysis_content(), + 5: EducationalContentManager._get_performance_predictions_content(), + 6: EducationalContentManager._get_implementation_roadmap_content(), + 7: EducationalContentManager._get_compilation_content(), + 8: EducationalContentManager._get_completion_content() + } + + return step_content.get(step, EducationalContentManager._get_default_content()) + + @staticmethod + def get_step_completion_content(step: int, result_data: Dict[str, Any] = None) -> Dict[str, Any]: + """Get educational content for step completion.""" + completion_content = { + 3: EducationalContentManager._get_strategic_insights_completion(result_data), + 4: EducationalContentManager._get_competitive_analysis_completion(result_data), + 5: EducationalContentManager._get_performance_predictions_completion(result_data), + 6: EducationalContentManager._get_implementation_roadmap_completion(result_data) + } + + return completion_content.get(step, EducationalContentManager._get_default_completion()) + + @staticmethod + def _get_user_context_content() -> Dict[str, Any]: + """Get educational content for user context analysis.""" + return { + "title": "πŸ” Analyzing Your Data", + "description": "We're gathering all your onboarding information to create a personalized strategy.", + "details": [ + "πŸ“Š Website analysis data", + "🎯 Research preferences", + "πŸ”‘ API configurations", + "πŸ“ˆ Historical performance metrics" + ], + "insight": "Your data helps us understand your business context, target audience, and competitive landscape.", + "ai_prompt_preview": "Analyzing user onboarding data to extract business context, audience insights, and competitive positioning..." + } + + @staticmethod + def _get_foundation_content() -> Dict[str, Any]: + """Get educational content for foundation building.""" + return { + "title": "πŸ—οΈ Building Foundation", + "description": "Creating the core strategy framework based on your business objectives.", + "details": [ + "🎯 Business objectives mapping", + "πŸ“Š Target metrics definition", + "πŸ’° Budget allocation strategy", + "⏰ Timeline planning" + ], + "insight": "A solid foundation ensures your content strategy aligns with business goals and resources.", + "ai_prompt_preview": "Generating strategic foundation: business objectives, target metrics, budget allocation, and timeline planning..." + } + + @staticmethod + def _get_strategic_insights_content() -> Dict[str, Any]: + """Get educational content for strategic insights generation.""" + return { + "title": "🧠 Strategic Intelligence Analysis", + "description": "AI is analyzing your market position and identifying strategic opportunities.", + "details": [ + "🎯 Market positioning analysis", + "πŸ’‘ Opportunity identification", + "πŸ“ˆ Growth potential assessment", + "πŸŽͺ Competitive advantage mapping" + ], + "insight": "Strategic insights help you understand where you stand in the market and how to differentiate.", + "ai_prompt_preview": "Analyzing market position, identifying strategic opportunities, assessing growth potential, and mapping competitive advantages...", + "estimated_time": "15-20 seconds" + } + + @staticmethod + def _get_competitive_analysis_content() -> Dict[str, Any]: + """Get educational content for competitive analysis.""" + return { + "title": "πŸ” Competitive Intelligence Analysis", + "description": "AI is analyzing your competitors to identify gaps and opportunities.", + "details": [ + "🏒 Competitor content strategies", + "πŸ“Š Market gap analysis", + "🎯 Differentiation opportunities", + "πŸ“ˆ Industry trend analysis" + ], + "insight": "Understanding your competitors helps you find unique angles and underserved market segments.", + "ai_prompt_preview": "Analyzing competitor content strategies, identifying market gaps, finding differentiation opportunities, and assessing industry trends...", + "estimated_time": "20-25 seconds" + } + + @staticmethod + def _get_performance_predictions_content() -> Dict[str, Any]: + """Get educational content for performance predictions.""" + return { + "title": "πŸ“Š Performance Forecasting", + "description": "AI is predicting content performance and ROI based on industry data.", + "details": [ + "πŸ“ˆ Traffic growth projections", + "πŸ’° ROI predictions", + "🎯 Conversion rate estimates", + "πŸ“Š Engagement metrics forecasting" + ], + "insight": "Performance predictions help you set realistic expectations and optimize resource allocation.", + "ai_prompt_preview": "Analyzing industry benchmarks, predicting traffic growth, estimating ROI, forecasting conversion rates, and projecting engagement metrics...", + "estimated_time": "15-20 seconds" + } + + @staticmethod + def _get_implementation_roadmap_content() -> Dict[str, Any]: + """Get educational content for implementation roadmap.""" + return { + "title": "πŸ—ΊοΈ Implementation Roadmap", + "description": "AI is creating a detailed implementation plan for your content strategy.", + "details": [ + "πŸ“‹ Task breakdown and timeline", + "πŸ‘₯ Resource allocation planning", + "🎯 Milestone definition", + "πŸ“Š Success metric tracking" + ], + "insight": "A clear implementation roadmap ensures successful strategy execution and measurable results.", + "ai_prompt_preview": "Creating implementation roadmap: task breakdown, resource allocation, milestone planning, and success metric definition...", + "estimated_time": "15-20 seconds" + } + + @staticmethod + def _get_risk_assessment_content() -> Dict[str, Any]: + """Get educational content for risk assessment.""" + return { + "title": "⚠️ Risk Assessment", + "description": "AI is identifying potential risks and mitigation strategies for your content strategy.", + "details": [ + "πŸ” Risk identification and analysis", + "πŸ“Š Risk probability assessment", + "πŸ›‘οΈ Mitigation strategy development", + "πŸ“ˆ Risk monitoring framework" + ], + "insight": "Proactive risk assessment helps you prepare for challenges and maintain strategy effectiveness.", + "ai_prompt_preview": "Assessing risks: identifying potential challenges, analyzing probability and impact, developing mitigation strategies, and creating monitoring framework...", + "estimated_time": "10-15 seconds" + } + + @staticmethod + def _get_compilation_content() -> Dict[str, Any]: + """Get educational content for strategy compilation.""" + return { + "title": "πŸ“‹ Strategy Compilation", + "description": "AI is compiling all components into a comprehensive content strategy.", + "details": [ + "πŸ”— Component integration", + "πŸ“Š Data synthesis", + "πŸ“ Strategy documentation", + "βœ… Quality validation" + ], + "insight": "A comprehensive strategy integrates all components into a cohesive, actionable plan.", + "ai_prompt_preview": "Compiling comprehensive strategy: integrating all components, synthesizing data, documenting strategy, and validating quality...", + "estimated_time": "5-10 seconds" + } + + @staticmethod + def _get_completion_content() -> Dict[str, Any]: + """Get educational content for strategy completion.""" + return { + "title": "πŸŽ‰ Strategy Generation Complete!", + "description": "Your comprehensive AI-powered content strategy is ready for review!", + "summary": { + "total_components": 5, + "successful_components": 5, + "estimated_roi": "15-25%", + "implementation_timeline": "12 months", + "risk_level": "Medium" + }, + "key_achievements": [ + "🧠 Strategic insights generated", + "πŸ” Competitive analysis completed", + "πŸ“Š Performance predictions calculated", + "πŸ—ΊοΈ Implementation roadmap planned", + "⚠️ Risk assessment conducted" + ], + "next_steps": [ + "Review your comprehensive strategy in the Strategic Intelligence tab", + "Customize specific components as needed", + "Confirm the strategy to proceed", + "Generate content calendar based on confirmed strategy" + ], + "ai_insights": "Your strategy leverages advanced AI analysis of your business context, competitive landscape, and industry best practices to create a data-driven content approach.", + "personalization_note": "This strategy is uniquely tailored to your business based on your onboarding data, ensuring relevance and effectiveness.", + "content_calendar_note": "Content calendar will be generated separately after you review and confirm this strategy, ensuring it's based on your final approved strategy." + } + + @staticmethod + def _get_default_content() -> Dict[str, Any]: + """Get default educational content.""" + return { + "title": "πŸ”„ Processing", + "description": "AI is working on your strategy...", + "details": [ + "⏳ Processing in progress", + "πŸ“Š Analyzing data", + "🎯 Generating insights", + "πŸ“ Compiling results" + ], + "insight": "The AI is working hard to create your personalized strategy.", + "estimated_time": "A few moments" + } + + @staticmethod + def _get_strategic_insights_completion(result_data: Dict[str, Any] = None) -> Dict[str, Any]: + """Get completion content for strategic insights.""" + insights_count = len(result_data.get("insights", [])) if result_data else 0 + return { + "title": "βœ… Strategic Insights Complete", + "description": "Successfully identified key strategic opportunities and market positioning.", + "achievement": f"Generated {insights_count} strategic insights", + "next_step": "Moving to competitive analysis..." + } + + @staticmethod + def _get_competitive_analysis_completion(result_data: Dict[str, Any] = None) -> Dict[str, Any]: + """Get completion content for competitive analysis.""" + competitors_count = len(result_data.get("competitors", [])) if result_data else 0 + return { + "title": "βœ… Competitive Analysis Complete", + "description": "Successfully analyzed competitive landscape and identified market opportunities.", + "achievement": f"Analyzed {competitors_count} competitors", + "next_step": "Moving to performance predictions..." + } + + @staticmethod + def _get_performance_predictions_completion(result_data: Dict[str, Any] = None) -> Dict[str, Any]: + """Get completion content for performance predictions.""" + estimated_roi = result_data.get("estimated_roi", "15-25%") if result_data else "15-25%" + return { + "title": "βœ… Performance Predictions Complete", + "description": "Successfully predicted content performance and ROI.", + "achievement": f"Predicted {estimated_roi} ROI", + "next_step": "Moving to implementation roadmap..." + } + + @staticmethod + def _get_implementation_roadmap_completion(result_data: Dict[str, Any] = None) -> Dict[str, Any]: + """Get completion content for implementation roadmap.""" + timeline = result_data.get("total_duration", "12 months") if result_data else "12 months" + return { + "title": "βœ… Implementation Roadmap Complete", + "description": "Successfully created detailed implementation plan.", + "achievement": f"Planned {timeline} implementation timeline", + "next_step": "Moving to compilation..." + } + + @staticmethod + def _get_risk_assessment_completion(result_data: Dict[str, Any] = None) -> Dict[str, Any]: + """Get completion content for risk assessment.""" + risk_level = result_data.get("overall_risk_level", "Medium") if result_data else "Medium" + return { + "title": "βœ… Risk Assessment Complete", + "description": "Successfully identified risks and mitigation strategies.", + "achievement": f"Assessed {risk_level} risk level", + "next_step": "Finalizing comprehensive strategy..." + } + + @staticmethod + def _get_default_completion() -> Dict[str, Any]: + """Get default completion content.""" + return { + "title": "βœ… Step Complete", + "description": "Successfully completed this step.", + "achievement": "Step completed successfully", + "next_step": "Moving to next step..." + } + + @staticmethod + def update_completion_summary(completion_content: Dict[str, Any], strategy_data: Dict[str, Any]) -> Dict[str, Any]: + """Update completion content with actual strategy data.""" + if "summary" in completion_content: + content_calendar = strategy_data.get("content_calendar", {}) + performance_predictions = strategy_data.get("performance_predictions", {}) + implementation_roadmap = strategy_data.get("implementation_roadmap", {}) + risk_assessment = strategy_data.get("risk_assessment", {}) + + completion_content["summary"].update({ + "total_content_pieces": len(content_calendar.get("content_pieces", [])), + "estimated_roi": performance_predictions.get("estimated_roi", "15-25%"), + "implementation_timeline": implementation_roadmap.get("total_duration", "12 months"), + "risk_level": risk_assessment.get("overall_risk_level", "Medium") + }) + + return completion_content \ No newline at end of file diff --git a/backend/api/content_planning/api/enhanced_strategy_routes.py b/backend/api/content_planning/api/enhanced_strategy_routes.py index 6b4b551e..4e9fe4b9 100644 --- a/backend/api/content_planning/api/enhanced_strategy_routes.py +++ b/backend/api/content_planning/api/enhanced_strategy_routes.py @@ -1122,4 +1122,4 @@ async def refresh_autofill( ) except Exception as e: logger.error(f"❌ Error generating fresh auto-fill payload: {str(e)}") - raise ContentPlanningErrorHandler.handle_general_error(e, "refresh_autofill") \ No newline at end of file + raise ContentPlanningErrorHandler.handle_general_error(e, "refresh_autofill") \ No newline at end of file diff --git a/backend/api/content_planning/services/content_strategy/ai_generation/strategy_generator.py b/backend/api/content_planning/services/content_strategy/ai_generation/strategy_generator.py index c86cea7f..87252bf9 100644 --- a/backend/api/content_planning/services/content_strategy/ai_generation/strategy_generator.py +++ b/backend/api/content_planning/services/content_strategy/ai_generation/strategy_generator.py @@ -60,7 +60,7 @@ class AIStrategyGenerator: strategy_name: Optional custom strategy name Returns: - Comprehensive strategy with all components + Comprehensive strategy with all components (EXCLUDING content calendar) Raises: RuntimeError: If any AI component fails to generate @@ -77,19 +77,16 @@ class AIStrategyGenerator: # Step 3: Generate competitive analysis competitive_analysis = await self._generate_competitive_analysis(base_strategy, context) - # Step 4: Generate content calendar - content_calendar = await self._generate_content_calendar(base_strategy, context) - - # Step 5: Generate performance predictions + # Step 4: Generate performance predictions performance_predictions = await self._generate_performance_predictions(base_strategy, context) - # Step 6: Generate implementation roadmap + # Step 5: Generate implementation roadmap implementation_roadmap = await self._generate_implementation_roadmap(base_strategy, context) - # Step 7: Generate risk assessment + # Step 6: Generate risk assessment risk_assessment = await self._generate_risk_assessment(base_strategy, context) - # Step 8: Compile comprehensive strategy + # Step 7: Compile comprehensive strategy (NO CONTENT CALENDAR) comprehensive_strategy = { "strategy_metadata": { "generated_at": datetime.utcnow().isoformat(), @@ -99,21 +96,21 @@ class AIStrategyGenerator: "ai_model": "gemini-pro", "personalization_level": "high", "ai_generated": True, - "comprehensive": True + "comprehensive": True, + "content_calendar_ready": False # Indicates calendar needs to be generated separately }, "base_strategy": base_strategy, "strategic_insights": strategic_insights, "competitive_analysis": competitive_analysis, - "content_calendar": content_calendar, "performance_predictions": performance_predictions, "implementation_roadmap": implementation_roadmap, "risk_assessment": risk_assessment, "summary": { - "total_content_pieces": len(content_calendar.get("content_pieces", [])), "estimated_roi": performance_predictions.get("estimated_roi", "15-25%"), "implementation_timeline": implementation_roadmap.get("total_duration", "12 months"), "risk_level": risk_assessment.get("overall_risk_level", "Medium"), - "success_probability": performance_predictions.get("success_probability", "85%") + "success_probability": performance_predictions.get("success_probability", "85%"), + "next_step": "Review strategy and generate content calendar" } } @@ -334,8 +331,55 @@ class AIStrategyGenerator: } }, "themes": {"type": "array", "items": {"type": "string"}}, - "schedule": {"type": "object"}, - "distribution_strategy": {"type": "object"} + "schedule": { + "type": "object", + "properties": { + "publishing_frequency": {"type": "string"}, + "optimal_times": {"type": "array", "items": {"type": "string"}}, + "content_mix": { + "type": "object", + "properties": { + "blog_posts": {"type": "string"}, + "social_media": {"type": "string"}, + "videos": {"type": "string"}, + "infographics": {"type": "string"}, + "newsletters": {"type": "string"} + } + }, + "seasonal_adjustments": { + "type": "object", + "properties": { + "holiday_content": {"type": "array", "items": {"type": "string"}}, + "seasonal_themes": {"type": "array", "items": {"type": "string"}}, + "peak_periods": {"type": "array", "items": {"type": "string"}} + } + } + } + }, + "distribution_strategy": { + "type": "object", + "properties": { + "primary_platforms": {"type": "array", "items": {"type": "string"}}, + "cross_posting_strategy": {"type": "string"}, + "platform_specific_content": { + "type": "object", + "properties": { + "linkedin_content": {"type": "array", "items": {"type": "string"}}, + "twitter_content": {"type": "array", "items": {"type": "string"}}, + "instagram_content": {"type": "array", "items": {"type": "string"}}, + "facebook_content": {"type": "array", "items": {"type": "string"}} + } + }, + "engagement_timing": { + "type": "object", + "properties": { + "best_times": {"type": "array", "items": {"type": "string"}}, + "frequency": {"type": "string"}, + "timezone_considerations": {"type": "string"} + } + } + } + } } } @@ -483,8 +527,33 @@ class AIStrategyGenerator: } } }, - "timeline": {"type": "object"}, - "resource_allocation": {"type": "object"}, + "timeline": { + "type": "object", + "properties": { + "start_date": {"type": "string"}, + "end_date": {"type": "string"}, + "key_milestones": {"type": "array", "items": {"type": "string"}}, + "critical_path": {"type": "array", "items": {"type": "string"}} + } + }, + "resource_allocation": { + "type": "object", + "properties": { + "team_requirements": {"type": "array", "items": {"type": "string"}}, + "budget_allocation": { + "type": "object", + "properties": { + "total_budget": {"type": "string"}, + "content_creation": {"type": "string"}, + "technology_tools": {"type": "string"}, + "marketing_promotion": {"type": "string"}, + "external_resources": {"type": "string"} + } + }, + "technology_needs": {"type": "array", "items": {"type": "string"}}, + "external_resources": {"type": "array", "items": {"type": "string"}} + } + }, "success_metrics": {"type": "array", "items": {"type": "string"}}, "total_duration": {"type": "string"} } @@ -552,9 +621,25 @@ class AIStrategyGenerator: } }, "overall_risk_level": {"type": "string"}, - "risk_categories": {"type": "object"}, + "risk_categories": { + "type": "object", + "properties": { + "technical_risks": {"type": "array", "items": {"type": "string"}}, + "market_risks": {"type": "array", "items": {"type": "string"}}, + "operational_risks": {"type": "array", "items": {"type": "string"}}, + "financial_risks": {"type": "array", "items": {"type": "string"}} + } + }, "mitigation_strategies": {"type": "array", "items": {"type": "string"}}, - "monitoring_framework": {"type": "object"} + "monitoring_framework": { + "type": "object", + "properties": { + "key_indicators": {"type": "array", "items": {"type": "string"}}, + "monitoring_frequency": {"type": "string"}, + "escalation_procedures": {"type": "array", "items": {"type": "string"}}, + "review_schedule": {"type": "string"} + } + } } } diff --git a/backend/api/content_planning/services/content_strategy/core/strategy_service.py b/backend/api/content_planning/services/content_strategy/core/strategy_service.py index d00be47c..b7ce570e 100644 --- a/backend/api/content_planning/services/content_strategy/core/strategy_service.py +++ b/backend/api/content_planning/services/content_strategy/core/strategy_service.py @@ -506,7 +506,7 @@ class EnhancedStrategyService: def _merge_strategy_with_onboarding(self, strategy_data: Dict[str, Any], field_transformations: Dict[str, Any]) -> Dict[str, Any]: """Merge strategy data with onboarding data.""" - merged_data = strategy_data.copy() + merged_data = strategy_data.copy() for field, transformation in field_transformations.items(): if field not in merged_data or merged_data[field] is None: diff --git a/docs/sse_migration_strategy.md b/docs/sse_migration_strategy.md new file mode 100644 index 00000000..be44cd84 --- /dev/null +++ b/docs/sse_migration_strategy.md @@ -0,0 +1,343 @@ +# SSE Migration Strategy & Implementation Plan + +## 🚨 **Current Implementation Problems** + +### **Backend Issues** +- **Complex SSE Manager**: The `SSEAIServiceManager` with lambda functions is overly complex +- **Async Generator Problems**: The `sse_yield` function using `__anext__()` is fragile +- **Message Format Inconsistency**: Backend sends different message formats that frontend struggles to parse +- **Tight Coupling**: AI service manager is tightly coupled to SSE implementation +- **Error Propagation**: Errors in one component cascade to others +- **Debugging Difficulty**: Complex async flows make debugging hard + +### **Frontend Issues** +- **EventSource Limitations**: No built-in reconnection, poor error handling +- **Message Parsing Complexity**: Too many message types to handle +- **Timeout Handling**: Frontend timeouts don't align with backend processing +- **Connection State Management**: Poor handling of connection states +- **Progress Tracking**: Inconsistent progress calculation and display + +### **Architecture Problems** +- **Tight Coupling**: Frontend and backend are tightly coupled to specific message formats +- **No Reusability**: SSE implementation is specific to strategy generation +- **Error Handling**: Inconsistent error handling across components +- **Testing Difficulty**: Complex async flows make testing challenging + +## 🎯 **Proposed Solution: Clean SSE with sse-starlette** + +### **Phase 1: MVP Polling Solution (1-2 hours)** +**Goal**: Get strategy generation working immediately with simple polling + +**Implementation**: +- Replace complex SSE with simple polling mechanism +- Poll strategy status every 10 seconds +- Show progress modal with educational content +- Handle timeouts gracefully +- Remove all SSE-related complexity + +**Benefits**: +- βœ… Immediate working solution +- βœ… Simple to implement and debug +- βœ… Reliable and predictable +- βœ… Easy to test + +### **Phase 2: Proper SSE Implementation (1-2 days)** +**Goal**: Implement clean, reusable SSE infrastructure + +**Implementation**: +- Use `sse-starlette` for backend SSE +- Create reusable SSE client for frontend +- Standardize message format +- Add proper error handling and reconnection +- Make SSE infrastructure reusable for other features + +**Benefits**: +- βœ… Real-time updates +- βœ… Better user experience +- βœ… Reusable infrastructure +- βœ… Proper error handling + +## πŸ—οΈ **Technical Architecture** + +### **Backend: sse-starlette Implementation** + +#### **Core SSE Module** (`backend/services/sse/`) +``` +backend/services/sse/ +β”œβ”€β”€ __init__.py +β”œβ”€β”€ sse_manager.py # Core SSE management +β”œβ”€β”€ message_formatter.py # Standardized message formatting +β”œβ”€β”€ connection_manager.py # Connection lifecycle management +β”œβ”€β”€ error_handler.py # SSE error handling +└── types.py # SSE message types and schemas +``` + +#### **SSE Manager Features** +- **Connection Management**: Handle multiple SSE connections +- **Message Broadcasting**: Send messages to specific clients +- **Error Handling**: Graceful error handling and recovery +- **Message Formatting**: Consistent message format across all features +- **Connection Monitoring**: Track connection health and status + +#### **Message Format Standardization** +```python +# Standard SSE message format +{ + "event": "progress|complete|error|educational", + "data": { + "step": 1, + "progress": 10, + "message": "Processing...", + "educational_content": {...}, + "timestamp": "2024-01-01T00:00:00Z" + } +} +``` + +### **Frontend: Reusable SSE Client** + +#### **Core SSE Module** (`frontend/src/services/sse/`) +``` +frontend/src/services/sse/ +β”œβ”€β”€ index.ts +β”œβ”€β”€ SSEConnection.ts # Core SSE connection management +β”œβ”€β”€ SSEEventManager.ts # Event handling and message parsing +β”œβ”€β”€ SSEReconnection.ts # Automatic reconnection logic +β”œβ”€β”€ SSEMessageTypes.ts # TypeScript types for messages +└── SSEUtils.ts # Utility functions +``` + +#### **SSE Client Features** +- **Automatic Reconnection**: Handle connection drops gracefully +- **Message Parsing**: Parse standardized message format +- **Event Handling**: Handle different event types +- **Error Recovery**: Recover from errors automatically +- **Connection Monitoring**: Monitor connection health + +#### **React Hook** (`frontend/src/hooks/useSSE.ts`) +```typescript +const useSSE = (url: string, options?: SSEOptions) => { + // Returns: { data, error, isConnected, reconnect } +} +``` + +## πŸ“‹ **Implementation Phases** + +### **Phase 1: MVP Polling (Immediate - 1-2 hours)** + +#### **Backend Changes** +1. **Remove SSE complexity** from `ai_generation_endpoints.py` +2. **Simplify AI generation** to return immediately after starting +3. **Add status endpoint** to check generation progress +4. **Remove SSEAIServiceManager** and related complexity + +#### **Frontend Changes** +1. **Replace SSE with polling** in `ContentStrategyBuilder.tsx` +2. **Implement simple progress modal** with educational content +3. **Add polling mechanism** (every 10 seconds) +4. **Handle timeouts gracefully** (5-minute timeout) +5. **Remove all SSE-related code** + +#### **Files to Modify** +- `backend/api/content_planning/api/content_strategy/endpoints/ai_generation_endpoints.py` +- `frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder.tsx` +- `frontend/src/services/contentPlanningApi.ts` + +### **Phase 2: Clean SSE Infrastructure (1-2 days)** + +#### **Backend Implementation** +1. **Create SSE infrastructure** (`backend/services/sse/`) +2. **Implement sse-starlette endpoints** for strategy generation +3. **Standardize message format** across all SSE endpoints +4. **Add connection management** and error handling +5. **Create reusable SSE utilities** + +#### **Frontend Implementation** +1. **Create SSE client infrastructure** (`frontend/src/services/sse/`) +2. **Implement React hook** for SSE connections +3. **Add automatic reconnection** logic +4. **Standardize message parsing** and event handling +5. **Create reusable SSE components** + +#### **New Files to Create** +``` +Backend: +- backend/services/sse/__init__.py +- backend/services/sse/sse_manager.py +- backend/services/sse/message_formatter.py +- backend/services/sse/connection_manager.py +- backend/services/sse/error_handler.py +- backend/services/sse/types.py + +Frontend: +- frontend/src/services/sse/index.ts +- frontend/src/services/sse/SSEConnection.ts +- frontend/src/services/sse/SSEEventManager.ts +- frontend/src/services/sse/SSEReconnection.ts +- frontend/src/services/sse/SSEMessageTypes.ts +- frontend/src/services/sse/SSEUtils.ts +- frontend/src/hooks/useSSE.ts +``` + +### **Phase 3: Migration & Testing (1 day)** + +#### **Migration Steps** +1. **Migrate strategy generation** to new SSE infrastructure +2. **Test end-to-end functionality** with new SSE +3. **Add comprehensive error handling** and recovery +4. **Implement educational content** streaming +5. **Add monitoring and logging** for SSE connections + +#### **Testing Strategy** +1. **Unit tests** for SSE infrastructure +2. **Integration tests** for SSE endpoints +3. **End-to-end tests** for strategy generation +4. **Error scenario testing** (network drops, timeouts) +5. **Performance testing** (multiple concurrent connections) + +## πŸ”§ **Technical Specifications** + +### **Backend SSE Manager Interface** +```python +class SSEManager: + async def create_connection(self, client_id: str) -> SSEConnection + async def send_message(self, client_id: str, message: SSEMessage) + async def broadcast_message(self, message: SSEMessage, filter_func=None) + async def close_connection(self, client_id: str) + async def get_connection_status(self, client_id: str) -> ConnectionStatus +``` + +### **Frontend SSE Client Interface** +```typescript +interface SSEConnection { + connect(): Promise + disconnect(): void + send(message: SSEMessage): void + on(event: string, handler: EventHandler): void + off(event: string, handler: EventHandler): void + isConnected(): boolean + reconnect(): Promise +} +``` + +### **Message Format Specification** +```typescript +interface SSEMessage { + event: 'progress' | 'complete' | 'error' | 'educational' | 'status' + data: { + step?: number + progress?: number + message?: string + educational_content?: EducationalContent + error?: string + timestamp: string + [key: string]: any + } +} +``` + +## 🎯 **Success Criteria** + +### **Phase 1 Success Criteria** +- βœ… Strategy generation works reliably +- βœ… No more "Request timed out" errors +- βœ… Users can see progress and educational content +- βœ… Simple, debuggable implementation +- βœ… Strategy creation completes successfully + +### **Phase 2 Success Criteria** +- βœ… Real-time progress updates via SSE +- βœ… Automatic reconnection on network issues +- βœ… Standardized message format across features +- βœ… Reusable SSE infrastructure +- βœ… Proper error handling and recovery +- βœ… Educational content streaming + +### **Phase 3 Success Criteria** +- βœ… All features migrated to new SSE infrastructure +- βœ… Comprehensive testing coverage +- βœ… Performance meets requirements +- βœ… Error scenarios handled gracefully +- βœ… Monitoring and logging in place + +## πŸš€ **Migration Benefits** + +### **Immediate Benefits (Phase 1)** +- **Reliability**: No more timeout errors +- **Simplicity**: Easy to debug and maintain +- **User Experience**: Clear progress feedback +- **Stability**: Predictable behavior + +### **Long-term Benefits (Phase 2+)** +- **Reusability**: SSE infrastructure for other features +- **Real-time Updates**: Better user experience +- **Scalability**: Handle multiple concurrent connections +- **Maintainability**: Clean, modular architecture +- **Extensibility**: Easy to add new SSE features + +## πŸ“ **Implementation Notes** + +### **Dependencies** +- **Backend**: `sse-starlette` package +- **Frontend**: No additional dependencies (uses native EventSource) + +### **Configuration** +- **SSE Timeout**: 5 minutes for long-running operations +- **Reconnection**: Exponential backoff (1s, 2s, 4s, 8s, max 30s) +- **Message Format**: JSON with standardized structure +- **Error Handling**: Graceful degradation with fallback options + +### **Monitoring & Logging** +- **Connection Status**: Track active connections +- **Message Flow**: Log message types and frequencies +- **Error Tracking**: Monitor and alert on SSE errors +- **Performance Metrics**: Track response times and throughput + +### **Security Considerations** +- **Authentication**: Validate client connections +- **Rate Limiting**: Prevent abuse of SSE endpoints +- **Message Validation**: Validate all incoming messages +- **Connection Limits**: Limit concurrent connections per user + +## πŸ”„ **Rollback Plan** + +### **If Phase 1 Fails** +- Revert to current SSE implementation +- Keep polling as fallback option +- Document issues for future reference + +### **If Phase 2 Fails** +- Keep Phase 1 polling implementation +- Identify specific issues with sse-starlette +- Consider alternative SSE libraries or WebSocket implementation + +### **If Phase 3 Fails** +- Rollback to Phase 2 implementation +- Fix specific issues identified during testing +- Re-run migration with fixes + +## πŸ“š **References & Resources** + +### **Documentation** +- [sse-starlette Documentation](https://github.com/sysid/sse-starlette) +- [Server-Sent Events MDN](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) +- [EventSource API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) + +### **Best Practices** +- [SSE Best Practices](https://html.spec.whatwg.org/multipage/server-sent-events.html) +- [Real-time Web Applications](https://web.dev/real-time-web-applications/) +- [Error Handling in SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Error_handling) + +### **Examples & Templates** +- [sse-starlette Examples](https://github.com/sysid/sse-starlette/tree/main/examples) +- [React SSE Hook Examples](https://github.com/facebook/react/tree/main/packages/react-dom/src/events) +- [FastAPI SSE Examples](https://fastapi.tiangolo.com/advanced/websockets/) + +--- + +**Next Steps**: +1. Commit current code +2. Refresh session +3. Start Phase 1 implementation (MVP polling) +4. Test strategy generation works +5. Proceed to Phase 2 (clean SSE infrastructure) \ No newline at end of file diff --git a/frontend/build/asset-manifest.json b/frontend/build/asset-manifest.json index a9ce6251..fe4f6442 100644 --- a/frontend/build/asset-manifest.json +++ b/frontend/build/asset-manifest.json @@ -1,13 +1,13 @@ { "files": { "main.css": "/static/css/main.c9966057.css", - "main.js": "/static/js/main.2ee5cd94.js", + "main.js": "/static/js/main.2819e23e.js", "index.html": "/index.html", "main.c9966057.css.map": "/static/css/main.c9966057.css.map", - "main.2ee5cd94.js.map": "/static/js/main.2ee5cd94.js.map" + "main.2819e23e.js.map": "/static/js/main.2819e23e.js.map" }, "entrypoints": [ "static/css/main.c9966057.css", - "static/js/main.2ee5cd94.js" + "static/js/main.2819e23e.js" ] } \ No newline at end of file diff --git a/frontend/build/index.html b/frontend/build/index.html index 12534f39..677fc85e 100644 --- a/frontend/build/index.html +++ b/frontend/build/index.html @@ -1 +1 @@ -Alwrity - AI Content Creation Platform
\ No newline at end of file +Alwrity - AI Content Creation Platform
\ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder.tsx b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder.tsx index 465e7455..ca82eccc 100644 --- a/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder.tsx +++ b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder.tsx @@ -50,7 +50,8 @@ import { Lightbulb as LightbulbIcon, Psychology as PsychologyIcon, Timeline as TimelineIcon, - FiberManualRecord as FiberManualRecordIcon + FiberManualRecord as FiberManualRecordIcon, + Schedule as ScheduleIcon } from '@mui/icons-material'; import { motion, AnimatePresence } from 'framer-motion'; import { useEnhancedStrategyStore, STRATEGIC_INPUT_FIELDS } from '../../../stores/enhancedStrategyStore'; @@ -63,6 +64,7 @@ import DataSourceTransparency from './DataSourceTransparency'; import { useCategoryReview } from './ContentStrategyBuilder/hooks/useCategoryReview'; import { useProgressTracking } from './ContentStrategyBuilder/hooks/useProgressTracking'; import { useAutoPopulation } from './ContentStrategyBuilder/hooks/useAutoPopulation'; +import { useActionButtonsBusinessLogic } from './ContentStrategyBuilder/components/ActionButtons'; // Import extracted utilities import { getCategoryIcon, getCategoryColor, getCategoryName, getCategoryStatus } from './ContentStrategyBuilder/utils/categoryHelpers'; @@ -72,7 +74,12 @@ import { getEducationalContent } from './ContentStrategyBuilder/utils/educationa import CategoryList from './ContentStrategyBuilder/components/CategoryList'; import ProgressTracker from './ContentStrategyBuilder/components/ProgressTracker'; import HeaderSection from './ContentStrategyBuilder/components/HeaderSection'; +import EducationalModal from './ContentStrategyBuilder/components/EducationalModal'; +import ActionButtons from './ContentStrategyBuilder/components/ActionButtons'; +import StrategyDisplay from './ContentStrategyBuilder/components/StrategyDisplay'; +import ErrorAlert from './ContentStrategyBuilder/components/ErrorAlert'; import { contentPlanningApi } from '../../../services/contentPlanningApi'; +import CategoryDetailView from './ContentStrategyBuilder/components/CategoryDetailView'; const ContentStrategyBuilder: React.FC = () => { const { @@ -155,6 +162,25 @@ const ContentStrategyBuilder: React.FC = () => { completionStats }); + // Use ActionButtons business logic hook + const { handleCreateStrategy, handleSaveStrategy } = useActionButtonsBusinessLogic({ + formData, + error, + currentStrategy, + setAIGenerating, + setError, + setCurrentStrategy, + setSaving, + setGenerationProgress, + setEducationalContent, + setShowEducationalModal, + validateAllFields, + getCompletionStats, + generateAIRecommendations, + createEnhancedStrategy, + contentPlanningApi + }); + // Auto-populate from onboarding on first load useEffect(() => { if (!autoPopulateAttempted) { @@ -211,294 +237,6 @@ const ContentStrategyBuilder: React.FC = () => { }; }, []); - const handleCreateStrategy = async () => { - try { - setAIGenerating(true); - setError(null); - - console.log('Starting strategy creation...'); - console.log('Current formData:', formData); - console.log('FormData ID:', formData.id); - - // If we have a saved strategy, use its ID - if (formData.id) { - console.log('Using existing strategy ID:', formData.id); - await generateAIRecommendations(formData.id); - } else { - console.log('No strategy ID found, creating new strategy...'); - // If no strategy is saved yet, save it first, then generate AI insights - const isValid = validateAllFields(); - console.log('Form validation result:', isValid); - - if (isValid) { - const completionStats = getCompletionStats(); - const strategyData = { - ...formData, - completion_percentage: completionStats.completion_percentage, - user_id: 1, // This would come from auth context - name: formData.name || 'Enhanced Content Strategy', - industry: formData.industry || 'General' - }; - - console.log('Attempting to create strategy with data:', strategyData); - - // Use SSE streaming endpoint for strategy generation with educational content - await generateStrategyWithSSE(strategyData); - } else { - setError('Please fill in all required fields before generating AI insights.'); - console.error('Form validation failed. Cannot generate AI insights.'); - } - } - } catch (err: any) { - setError(`Error generating AI recommendations: ${err.message || 'Unknown error'}`); - console.error('Error in handleCreateStrategy:', err); - } finally { - setAIGenerating(false); - } - }; - - const generateStrategyWithSSE = async (strategyData: any) => { - try { - console.log('πŸš€ Starting SSE strategy generation...'); - - // Initialize progress and educational content - setGenerationProgress(0); - setEducationalContent({ - title: 'πŸ€– AI-Powered Strategy Generation', - description: 'Initializing AI analysis and preparing educational content...', - details: [ - 'πŸ”§ Setting up AI services', - 'πŸ“Š Loading user context', - '🎯 Preparing strategy framework', - 'πŸ“š Generating educational content' - ], - insight: 'We\'re getting everything ready for your personalized AI strategy generation.', - estimated_time: '2-3 minutes total' - }); - - // Show educational modal - setShowEducationalModal(true); - - // Create basic strategy first - const newStrategy = await createEnhancedStrategy(strategyData); - console.log('Basic strategy created:', newStrategy); - - if (newStrategy && newStrategy.id) { - console.log('Starting AI generation for strategy ID:', newStrategy.id); - - // Set a timeout for the entire process (5 minutes) - const processTimeout = setTimeout(async () => { - console.error('⏰ Strategy generation timeout after 5 minutes'); - - // Try to check if the strategy was actually created - try { - const existingStrategy = await contentPlanningApi.getEnhancedStrategy(newStrategy.id.toString()); - if (existingStrategy) { - console.log('βœ… Strategy was created successfully despite SSE timeout'); - setCurrentStrategy(existingStrategy); - setError('Strategy created successfully! The AI generation may still be running in the background. Check the Strategic Intelligence tab for detailed insights.'); - } else { - setError('Strategy generation is taking longer than expected. The process may still be running in the background. Please check the Strategic Intelligence tab for results.'); - } - } catch (checkError) { - console.error('Error checking strategy status:', checkError); - setError('Strategy generation is taking longer than expected. The process may still be running in the background. Please check the Strategic Intelligence tab for results.'); - } - - setShowEducationalModal(false); - }, 5 * 60 * 1000); // 5 minutes - - // Add heartbeat monitoring - let lastMessageTime = Date.now(); - const heartbeatInterval = setInterval(() => { - const timeSinceLastMessage = Date.now() - lastMessageTime; - if (timeSinceLastMessage > 30000) { // 30 seconds without message - console.warn('⚠️ No SSE messages received for 30 seconds'); - setEducationalContent({ - title: 'πŸ€– AI-Powered Strategy Generation', - description: 'AI analysis is still running in the background. This may take a few more minutes.', - details: [ - '⏳ Processing complex AI analysis', - 'πŸ“Š Analyzing market data', - '🎯 Generating strategic insights', - 'πŸ“ˆ Calculating performance predictions' - ], - insight: 'The AI is working on comprehensive analysis. This is normal for complex strategies.', - estimated_time: 'Additional 1-2 minutes' - }); - } - }, 10000); // Check every 10 seconds - - // Use SSE endpoint for AI generation with educational content - const eventSource = await contentPlanningApi.streamStrategyGeneration(Number(newStrategy.id)); - - console.log('πŸ”Œ SSE EventSource created:', eventSource); - console.log('πŸ”Œ SSE readyState:', eventSource.readyState); - - // Handle SSE data with proper parsing - eventSource.onmessage = (event) => { - try { - console.log('πŸ“¨ Raw SSE message:', event.data); - - // Update last message time for heartbeat - lastMessageTime = Date.now(); - - // Parse the SSE data - const data = JSON.parse(event.data); - console.log('πŸ“¨ Parsed SSE data:', data); - console.log('πŸ“¨ Message type analysis:', { - hasStep: data.step !== undefined, - hasProgress: data.progress !== undefined, - hasEducationalContent: !!data.educational_content, - hasError: !!data.error, - hasSuccess: !!data.success, - hasType: !!data.type, - step: data.step, - progress: data.progress, - message: data.message - }); - - // Handle different types of messages - if (data.error) { - console.error('❌ SSE Error:', data.error); - clearTimeout(processTimeout); - clearInterval(heartbeatInterval); - setError(`AI generation failed: ${data.error}`); - setShowEducationalModal(false); - eventSource.close(); - return; - } - - // Handle step and progress updates (backend sends these) - if (data.step !== undefined) { - console.log('πŸ”’ Updating step:', data.step); - // Calculate progress from step (each step is 10%) - const stepProgress = Math.min(data.step * 10, 100); - console.log('πŸ“Š Calculated progress from step:', stepProgress); - setGenerationProgress(stepProgress); - } - - // Handle explicit progress updates - if (data.progress !== undefined) { - console.log('πŸ“Š Updating progress:', data.progress); - setGenerationProgress(data.progress); - } - - // Handle educational content updates - if (data.educational_content) { - console.log('πŸ“š Updating educational content:', data.educational_content); - setEducationalContent(data.educational_content); - } - - // Handle completion - if (data.step === 10 && data.success) { - console.log('βœ… Strategy generation completed successfully!'); - clearTimeout(processTimeout); - clearInterval(heartbeatInterval); - setCurrentStrategy(data.strategy); - setShowEducationalModal(false); - setError('Strategy created successfully! Check the Strategic Intelligence tab for detailed insights.'); - eventSource.close(); - } - - // Handle educational content from AI service manager - if (data.type === 'educational_content' && data.educational_content) { - console.log('πŸ“š AI Service educational content:', data.educational_content); - setEducationalContent(data.educational_content); - } - - // Handle success messages for individual steps - if (data.success && data.message) { - console.log('βœ… Step completed:', data.message); - // Progress is already updated above, just log the success - } - - } catch (parseError) { - console.error('❌ Error parsing SSE message:', parseError); - console.error('Raw message:', event.data); - } - }; - - // Handle SSE errors - eventSource.onerror = (error) => { - console.error('❌ SSE connection error:', error); - console.error(' ReadyState:', eventSource.readyState); - - // Check connection state - switch (eventSource.readyState) { - case EventSource.CONNECTING: - console.log('πŸ”„ SSE connection is connecting...'); - break; - case EventSource.OPEN: - console.log('βœ… SSE connection is open'); - break; - case EventSource.CLOSED: - console.log('πŸ”Œ SSE connection is closed'); - clearTimeout(processTimeout); - clearInterval(heartbeatInterval); - setError('Connection lost during AI generation. The process may still be running in the background. Please check the Strategic Intelligence tab for results.'); - setShowEducationalModal(false); - break; - } - }; - - // Handle SSE connection open - eventSource.onopen = () => { - console.log('βœ… SSE connection opened successfully'); - console.log(' ReadyState:', eventSource.readyState); - console.log(' URL:', eventSource.url); - - // Update educational content to show connection is established - setEducationalContent({ - title: 'πŸ”Œ Connection Established', - description: 'Successfully connected to AI generation service. Starting analysis...', - details: [ - 'βœ… SSE connection active', - 'πŸ€– AI service ready', - 'πŸ“Š Data processing initialized', - '🎯 Strategy generation starting' - ], - insight: 'The connection is now established and AI analysis is beginning.', - estimated_time: '2-3 minutes total' - }); - }; - - } else { - setError('Failed to create strategy or get strategy ID for AI generation.'); - console.error('Failed to create strategy or get strategy ID for AI generation.'); - setShowEducationalModal(false); - } - } catch (error: any) { - console.error('Error in SSE strategy generation:', error); - setError(`Error in strategy generation: ${error.message || 'Unknown error'}`); - setShowEducationalModal(false); - } - }; - - const handleSaveStrategy = async () => { - try { - setSaving(true); - setError(null); - - const completionStats = getCompletionStats(); - const strategyData = { - ...formData, - completion_percentage: completionStats.completion_percentage, - user_id: 1, - name: formData.name || 'Enhanced Content Strategy', - industry: formData.industry || 'General' - }; - - const newStrategy = await createEnhancedStrategy(strategyData); - setCurrentStrategy(newStrategy); - setError('Strategy saved successfully!'); - } catch (err: any) { - setError(`Error saving strategy: ${err.message || 'Unknown error'}`); - } finally { - setSaving(false); - } - }; - const handleReviewCategory = (categoryId: string) => { setActiveCategory(activeCategory === categoryId ? null : categoryId); }; @@ -519,75 +257,19 @@ const ContentStrategyBuilder: React.FC = () => { {/* Error Alert */} - {error && ( - - - - - } - > - - Real data required - {error || 'We could not auto-populate because required onboarding/analysis data is missing. Connect sources or complete onboarding, then retry.'} - - - )} + autoPopulateFromOnboarding(true)} + onShowDataSourceTransparency={() => setShowDataSourceTransparency(true)} + /> - {/* Success Alert */} - {!error && currentStrategy && ( - - Strategy "{currentStrategy.name}" created successfully! Check the Strategic Intelligence tab for detailed insights. - - )} - - {/* Strategy Display */} - {currentStrategy && ( - - - Created Strategy: {currentStrategy.name} - - - - - Industry: {currentStrategy.industry} - - - Completion: {currentStrategy.completion_percentage}% - - - - - Created: {new Date(currentStrategy.created_at).toLocaleDateString()} - - - ID: {currentStrategy.id} - - - - - - - - )} - - {categoryCompletionMessage && ( - - {categoryCompletionMessage} - - )} + {/* Strategy Display and Success Alerts */} + window.location.href = '/content-planning?tab=strategic-intelligence'} + /> {/* Category Overview Panel */} @@ -781,212 +463,41 @@ const ContentStrategyBuilder: React.FC = () => { {/* Main Content Area */} - {activeCategory ? ( - - {/* Category Header */} - - {getCategoryIcon(activeCategory)} - - {activeCategory.split('_').map(word => - word.charAt(0).toUpperCase() + word.slice(1) - ).join(' ')} - - - - - {/* Educational Info Dialog */} - setShowEducationalInfo(null)} - maxWidth="md" - fullWidth - > - - - - {showEducationalInfo && getEducationalContent(showEducationalInfo).title} - - - - - {showEducationalInfo && getEducationalContent(showEducationalInfo).description} - - - - Key Points: - - - {showEducationalInfo && getEducationalContent(showEducationalInfo).points.map((point, index) => ( - - - - - - - ))} - - - - Pro Tips: - - - {showEducationalInfo && getEducationalContent(showEducationalInfo).tips.map((tip, index) => ( - - - - - - - ))} - - - - - - - - {/* Category Fields */} - - - {STRATEGIC_INPUT_FIELDS - .filter(field => field.category === activeCategory) - .map((field, index) => { - // Determine grid size based on field type for better layout organization - const type = field.type; - const isWideField = type === 'json'; - const isMediumField = type === 'multiselect' || type === 'select' || type === 'text'; - const isCompactField = type === 'number' || type === 'boolean'; - const forceFullWidth = field.id === 'content_budget' || field.id === 'team_size'; - - const gridMd = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4); - const gridLg = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4); - const gridSm = 12; - - return ( - - - updateFormField(field.id, value)} - onValidate={() => validateFormField(field.id)} - onShowTooltip={() => setShowTooltip(field.id)} - onViewDataSource={() => setShowDataSourceTransparency(true)} - accentColorKey={getCategoryColor(activeCategory) as any} - isCompact={isCompactField} - /> - - - ); - })} - - - - {/* Category Actions */} - - {(() => { - const isReviewed = reviewedCategories.has(activeCategory); - console.log('πŸ” Category review status:', { - activeCategory, - isReviewed, - reviewedCategories: Array.from(reviewedCategories) - }); - return !isReviewed ? ( - - ) : ( - } - sx={{ px: 2, py: 1 }} - /> - ); - })()} - - - - - ) : ( - - - - - Select a Category to Review - - - Click on any category from the left panel to review and complete the fields. - - - - )} + setShowDataSourceTransparency(true)} + onConfirmCategoryReview={handleConfirmCategoryReviewWrapper} + onSetActiveCategory={setActiveCategory} + onSetShowEducationalInfo={setShowEducationalInfo} + getCategoryIcon={getCategoryIcon} + getCategoryColor={getCategoryColor} + getEducationalContent={getEducationalContent} + /> {/* Action Buttons */} - - - - - - - - - + {/* AI Recommendations Modal */} { - {/* Educational Modal for Strategy Generation */} - setShowEducationalModal(false)} - maxWidth="lg" - fullWidth - > - - - - {educationalContent?.title || 'AI Strategy Generation'} - - - - {educationalContent ? ( - - {/* Progress Bar */} - - - - Progress: {generationProgress}% - - - Step {Math.ceil(generationProgress / 10)} of 10 - - - - - - {/* Educational Content */} - - {educationalContent.title || 'AI Strategy Generation'} - - - {educationalContent.description && ( - - {educationalContent.description} - - )} - - {educationalContent.details && ( - - - What's happening: - - - {educationalContent.details.map((detail: string, index: number) => ( - - - - - - - ))} - - - )} - - {educationalContent.insight && ( - - - πŸ’‘ Insight: - - - {educationalContent.insight} - - - )} - - {educationalContent.ai_prompt_preview && ( - - - πŸ€– AI Prompt Preview: - - - {educationalContent.ai_prompt_preview} - - - )} - - {educationalContent.estimated_time && ( - - - ⏱️ Estimated Time: - - - {educationalContent.estimated_time} - - - )} - - {educationalContent.achievement && ( - - - βœ… Achievement: - - - {educationalContent.achievement} - - - )} - - {educationalContent.next_step && ( - - - πŸ”„ Next Step: - - - {educationalContent.next_step} - - - )} - - {/* Summary for completion */} - {educationalContent.summary && ( - - - πŸŽ‰ Strategy Generation Summary - - - - - Components: {educationalContent.summary.successful_components}/{educationalContent.summary.total_components} - - - - - Content Pieces: {educationalContent.summary.total_content_pieces} - - - - - Estimated ROI: {educationalContent.summary.estimated_roi} - - - - - Timeline: {educationalContent.summary.implementation_timeline} - - - - - )} - - ) : ( - /* Loading state when educational content is not yet available */ - - - - πŸ€– AI-Powered Strategy Generation - - - Initializing AI analysis and preparing educational content... - - - This may take a few moments as we set up the AI services. - - - )} - - - - - + educationalContent={educationalContent} + generationProgress={generationProgress} + /> {/* Data Source Transparency Modal */} { + + const handleCreateStrategy = async () => { + try { + setAIGenerating(true); + setError(null); + + console.log('Starting strategy creation...'); + console.log('Current formData:', formData); + console.log('FormData ID:', formData.id); + + // If we have a saved strategy, use its ID + if (formData.id) { + console.log('Using existing strategy ID:', formData.id); + await generateAIRecommendations(formData.id); + } else { + console.log('No strategy ID found, creating new strategy...'); + // If no strategy is saved yet, save it first, then generate AI insights + const isValid = validateAllFields(); + console.log('Form validation result:', isValid); + + if (isValid) { + const completionStats = getCompletionStats(); + const strategyData = { + ...formData, + completion_percentage: completionStats.completion_percentage, + user_id: 1, // This would come from auth context + name: formData.name || 'Enhanced Content Strategy', + industry: formData.industry || 'General' + }; + + console.log('Attempting to create strategy with data:', strategyData); + + // Use SSE streaming endpoint for strategy generation with educational content + await generateStrategyWithPolling(strategyData); + } else { + setError('Please fill in all required fields before generating AI insights.'); + console.error('Form validation failed. Cannot generate AI insights.'); + } + } + } catch (err: any) { + setError(`Error generating AI recommendations: ${err.message || 'Unknown error'}`); + console.error('Error in handleCreateStrategy:', err); + } finally { + setAIGenerating(false); + } + }; + + const generateStrategyWithPolling = async (strategyData: any) => { + try { + console.log('πŸš€ Starting polling-based strategy generation...'); + + // Initialize progress and educational content + setGenerationProgress(0); + setEducationalContent({ + title: 'πŸ€– AI-Powered Strategy Generation', + description: 'Initializing AI analysis and preparing educational content...', + details: [ + 'πŸ”§ Setting up AI services', + 'πŸ“Š Loading user context', + '🎯 Preparing strategy framework', + 'πŸ“š Generating educational content' + ], + insight: 'We\'re getting everything ready for your personalized AI strategy generation.', + estimated_time: '2-3 minutes total' + }); + + // Show educational modal + setShowEducationalModal(true); + + // Start polling-based strategy generation directly (no basic strategy creation) + const generationResult = await contentPlanningApi.startStrategyGenerationPolling(1, 'Enhanced Content Strategy'); + console.log('Strategy generation started:', generationResult); + + if (generationResult && generationResult.task_id) { + const taskId = generationResult.task_id; + console.log('Task ID received:', taskId); + + // Start polling for status updates + contentPlanningApi.pollStrategyGeneration( + taskId, + // onProgress callback + (status: any) => { + console.log('πŸ“Š Progress update:', status); + + // Update progress + if (status.progress !== undefined) { + setGenerationProgress(status.progress); + } + + // Update educational content + if (status.educational_content) { + console.log('πŸ“š Updating educational content:', status.educational_content); + setEducationalContent(status.educational_content); + } + + // Update message + if (status.message) { + console.log('πŸ“ Status message:', status.message); + } + }, + // onComplete callback + (strategy: any) => { + console.log('βœ… Strategy generation completed successfully!'); + setCurrentStrategy(strategy); + setShowEducationalModal(false); + setError('Strategy created successfully! Check the Strategic Intelligence tab for detailed insights.'); + }, + // onError callback + (error: string) => { + console.error('❌ Strategy generation failed:', error); + setError(`Strategy generation failed: ${error}`); + setShowEducationalModal(false); + }, + 5000, // 5 second polling interval for faster updates + 72 // 6 minutes max (72 * 5 seconds) + ); + + } else { + setError('Failed to start strategy generation. No task ID received.'); + setShowEducationalModal(false); + } + } catch (error: any) { + console.error('Error in polling-based strategy generation:', error); + setError(`Error in strategy generation: ${error.message || 'Unknown error'}`); + setShowEducationalModal(false); + } + }; + + const handleSaveStrategy = async () => { + try { + setSaving(true); + setError(null); + + const completionStats = getCompletionStats(); + const strategyData = { + ...formData, + completion_percentage: completionStats.completion_percentage, + user_id: 1, + name: formData.name || 'Enhanced Content Strategy', + industry: formData.industry || 'General' + }; + + const newStrategy = await createEnhancedStrategy(strategyData); + setCurrentStrategy(newStrategy); + setError('Strategy saved successfully!'); + } catch (err: any) { + setError(`Error saving strategy: ${err.message || 'Unknown error'}`); + } finally { + setSaving(false); + } + }; + + return { + handleCreateStrategy, + handleSaveStrategy + }; +}; + +// UI Component +const ActionButtons: React.FC = ({ + aiGenerating, + saving, + reviewProgressPercentage, + onCreateStrategy, + onSaveStrategy +}) => { + return ( + + + + + + + + + + ); +}; + +export default ActionButtons; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/CategoryDetailView.tsx b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/CategoryDetailView.tsx new file mode 100644 index 00000000..9015a795 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/CategoryDetailView.tsx @@ -0,0 +1,257 @@ +import React from 'react'; +import { + Box, + Typography, + Chip, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + List, + ListItem, + ListItemIcon, + ListItemText, + Grid, + CircularProgress +} from '@mui/material'; +import { + School as SchoolIcon, + Lightbulb as LightbulbIcon, + Psychology as PsychologyIcon, + Timeline as TimelineIcon, + CheckCircle as CheckCircleIcon +} from '@mui/icons-material'; +import { motion } from 'framer-motion'; +import StrategicInputField from '../StrategicInputField'; +import { CategoryDetailViewProps, EducationalInfoDialogProps } from '../types/contentStrategy.types'; + +const EducationalInfoDialog: React.FC = ({ + open, + onClose, + categoryId, + getEducationalContent +}) => { + if (!categoryId) return null; + + const educationalContent = getEducationalContent(categoryId); + + return ( + + + + + {educationalContent?.title} + + + + + {educationalContent?.description} + + + + Key Points: + + + {educationalContent?.points?.map((point: string, index: number) => ( + + + + + + + ))} + + + + Pro Tips: + + + {educationalContent?.tips?.map((tip: string, index: number) => ( + + + + + + + ))} + + + + + + + ); +}; + +const CategoryDetailView: React.FC = ({ + activeCategory, + formData, + formErrors, + autoPopulatedFields, + dataSources, + personalizationData, + completionStats, + reviewedCategories, + isMarkingReviewed, + showEducationalInfo, + STRATEGIC_INPUT_FIELDS, + onUpdateFormField, + onValidateFormField, + onShowTooltip, + onViewDataSource, + onConfirmCategoryReview, + onSetActiveCategory, + onSetShowEducationalInfo, + getCategoryIcon, + getCategoryColor, + getEducationalContent +}) => { + if (!activeCategory) { + return ( + + + + + Select a Category to Review + + + Click on any category from the left panel to review and complete the fields. + + + + ); + } + + return ( + + {/* Category Header */} + + {getCategoryIcon(activeCategory)} + + {activeCategory.split('_').map(word => + word.charAt(0).toUpperCase() + word.slice(1) + ).join(' ')} + + + + + {/* Educational Info Dialog */} + onSetShowEducationalInfo(null)} + categoryId={showEducationalInfo} + getEducationalContent={getEducationalContent} + /> + + {/* Category Fields */} + + + {STRATEGIC_INPUT_FIELDS + .filter(field => field.category === activeCategory) + .map((field, index) => { + // Determine grid size based on field type for better layout organization + const type = field.type; + const isWideField = type === 'json'; + const isMediumField = type === 'multiselect' || type === 'select' || type === 'text'; + const isCompactField = type === 'number' || type === 'boolean'; + const forceFullWidth = field.id === 'content_budget' || field.id === 'team_size'; + + const gridMd = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4); + const gridLg = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4); + const gridSm = 12; + + return ( + + + onUpdateFormField(field.id, value)} + onValidate={() => onValidateFormField(field.id)} + onShowTooltip={() => onShowTooltip(field.id)} + onViewDataSource={onViewDataSource} + accentColorKey={getCategoryColor(activeCategory) as any} + isCompact={isCompactField} + /> + + + ); + })} + + + + {/* Category Actions */} + + {(() => { + const isReviewed = reviewedCategories.has(activeCategory); + console.log('πŸ” Category review status:', { + activeCategory, + isReviewed, + reviewedCategories: Array.from(reviewedCategories) + }); + return !isReviewed ? ( + + ) : ( + } + sx={{ px: 2, py: 1 }} + /> + ); + })()} + + + + + ); +}; + +export default CategoryDetailView; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/EducationalModal.tsx b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/EducationalModal.tsx new file mode 100644 index 00000000..595c1963 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/EducationalModal.tsx @@ -0,0 +1,554 @@ +import React from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Typography, + Box, + Grid, + List, + ListItem, + ListItemIcon, + ListItemText, + CircularProgress +} from '@mui/material'; +import { + School as SchoolIcon, + Lightbulb as LightbulbIcon, + Timeline as TimelineIcon, + AutoAwesome as AutoAwesomeIcon, + CheckCircle as CheckCircleIcon, + Schedule as ScheduleIcon, + TrendingUp as TrendingUpIcon, + FiberManualRecord as FiberManualRecordIcon +} from '@mui/icons-material'; +import { motion } from 'framer-motion'; +import { EducationalModalProps } from '../types/contentStrategy.types'; + +const EducationalModal: React.FC = ({ + open, + onClose, + educationalContent, + generationProgress +}) => { + return ( + + + + + + + + + {educationalContent?.title || 'AI Strategy Generation'} + + + Creating your comprehensive content strategy + + + + + + + {educationalContent ? ( + + {/* Enhanced Progress Section */} + + + + + + Progress: {generationProgress}% + + + Step {Math.ceil(generationProgress / 10)} of 8 β€’ {Math.ceil((100 - generationProgress) / 10)} steps remaining + + + + + {generationProgress}% + + + Complete + + + + + {/* Enhanced Progress Bar with Glitch Effect */} + + + + {/* Glitch Effect Overlay */} + + + {/* Shimmer Effect */} + + + + + {/* Progress Markers */} + + {[0, 25, 50, 75, 100].map((marker) => ( + = marker ? '#667eea' : '#e0e0e0', + boxShadow: generationProgress >= marker ? '0 2px 8px rgba(102, 126, 234, 0.3)' : 'none', + transition: 'all 0.3s ease' + }} /> + ))} + + + + + + {/* Enhanced Educational Content */} + + + {/* Main Content */} + + {educationalContent.description && ( + + + + What's Happening + + + {educationalContent.description} + + + )} + + {educationalContent.details && ( + + + + Current Activities + + + + {educationalContent.details.map((detail: string, index: number) => ( + + + + + + + + + + + ))} + + + + )} + + {educationalContent.ai_prompt_preview && ( + + + + AI Processing + + + + + {educationalContent.ai_prompt_preview} + + + + )} + + + {/* Sidebar with Insights and Info */} + + + + Insights & Information + + + {educationalContent.insight && ( + + + + Key Insight + + + {educationalContent.insight} + + + )} + + {educationalContent.estimated_time && ( + + + + Time Estimate + + + {educationalContent.estimated_time} + + + )} + + {educationalContent.achievement && ( + + + + Achievement + + + {educationalContent.achievement} + + + )} + + {educationalContent.next_step && ( + + + + Next Step + + + {educationalContent.next_step} + + + )} + + + + + {/* Summary for completion */} + {educationalContent.summary && ( + + + + + + Strategy Generation Summary + + + {Object.entries(educationalContent.summary).map(([key, value]) => ( + + + + {value as string} + + + {key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())} + + + + ))} + + + + )} + + + ) : ( + + + + Initializing Strategy Generation + + + Preparing AI models and analyzing your data... + + + )} + + + + + + + ); +}; + +export default EducationalModal; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/ErrorAlert.tsx b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/ErrorAlert.tsx new file mode 100644 index 00000000..2e492699 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/ErrorAlert.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { + Box, + Alert, + Button, + Typography +} from '@mui/material'; +import { + Refresh as RefreshIcon, + Info as InfoIcon +} from '@mui/icons-material'; +import { ErrorAlertProps } from '../types/contentStrategy.types'; + +const ErrorAlert: React.FC = ({ + error, + onRetry, + onShowDataSourceTransparency +}) => { + if (!error) return null; + + return ( + + + + + } + > + + Real data required + + {error || 'We could not auto-populate because required onboarding/analysis data is missing. Connect sources or complete onboarding, then retry.'} + + + + ); +}; + +export default ErrorAlert; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/StrategyDisplay.tsx b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/StrategyDisplay.tsx new file mode 100644 index 00000000..dedc444d --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/components/StrategyDisplay.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { + Box, + Paper, + Typography, + Button, + Alert, + Grid +} from '@mui/material'; +import { + Assessment as AssessmentIcon +} from '@mui/icons-material'; +import { StrategyDisplayProps } from '../types/contentStrategy.types'; + +const StrategyDisplay: React.FC = ({ + currentStrategy, + error, + categoryCompletionMessage, + onViewStrategicIntelligence +}) => { + return ( + <> + {/* Success Alert */} + {!error && currentStrategy && ( + + Strategy "{currentStrategy.name}" created successfully! Check the Strategic Intelligence tab for detailed insights. + + )} + + {/* Strategy Display */} + {currentStrategy && ( + + + Created Strategy: {currentStrategy.name} + + + + + Industry: {currentStrategy.industry} + + + Completion: {currentStrategy.completion_percentage}% + + + + + Created: {new Date(currentStrategy.created_at).toLocaleDateString()} + + + ID: {currentStrategy.id} + + + + + + + + )} + + {/* Category Completion Message */} + {categoryCompletionMessage && ( + + {categoryCompletionMessage} + + )} + + ); +}; + +export default StrategyDisplay; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/index.ts b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/index.ts new file mode 100644 index 00000000..f56f6fc9 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/index.ts @@ -0,0 +1,18 @@ +// Main Content Strategy Builder +export { default as ContentStrategyBuilder } from '../ContentStrategyBuilder'; + +// Components +export { default as HeaderSection } from './components/HeaderSection'; +export { default as CategoryList } from './components/CategoryList'; +export { default as ProgressTracker } from './components/ProgressTracker'; +export { default as EducationalModal } from './components/EducationalModal'; +export { default as CategoryDetailView } from './components/CategoryDetailView'; +export { default as ActionButtons } from './components/ActionButtons'; +export { default as StrategyDisplay } from './components/StrategyDisplay'; +export { default as ErrorAlert } from './components/ErrorAlert'; + +// Hooks +export { useActionButtonsBusinessLogic } from './components/ActionButtons'; + +// Types +export * from './types/contentStrategy.types'; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/types/contentStrategy.types.ts b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/types/contentStrategy.types.ts new file mode 100644 index 00000000..214070de --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/ContentStrategyBuilder/types/contentStrategy.types.ts @@ -0,0 +1,124 @@ +// Educational Content Types +export interface EducationalContent { + title?: string; + description?: string; + details?: string[]; + insight?: string; + estimated_time?: string; + achievement?: string; + next_step?: string; + ai_prompt_preview?: string; + summary?: Record; +} + +// Educational Modal Props +export interface EducationalModalProps { + open: boolean; + onClose: () => void; + educationalContent: EducationalContent | null; + generationProgress: number; +} + +// Category Detail View Types +export interface CategoryDetailViewProps { + activeCategory: string | null; + formData: Record; + formErrors: Record; + autoPopulatedFields: Record; + dataSources: Record; + personalizationData: Record; + completionStats: any; + reviewedCategories: Set; + isMarkingReviewed: boolean; + showEducationalInfo: string | null; + STRATEGIC_INPUT_FIELDS: any[]; + onUpdateFormField: (fieldId: string, value: any) => void; + onValidateFormField: (fieldId: string) => boolean; + onShowTooltip: (fieldId: string) => void; + onViewDataSource: () => void; + onConfirmCategoryReview: () => void; + onSetActiveCategory: (category: string | null) => void; + onSetShowEducationalInfo: (categoryId: string | null) => void; + getCategoryIcon: (category: string) => React.ReactNode; + getCategoryColor: (category: string) => string; + getEducationalContent: (categoryId: string) => any; +} + +// Educational Info Dialog Types +export interface EducationalInfoDialogProps { + open: boolean; + onClose: () => void; + categoryId: string | null; + getEducationalContent: (categoryId: string) => any; +} + +// Action Buttons Types +export interface ActionButtonsProps { + aiGenerating: boolean; + saving: boolean; + reviewProgressPercentage: number; + onCreateStrategy: () => void; + onSaveStrategy: () => void; +} + +// Action Buttons Business Logic Types +export interface ActionButtonsBusinessLogicProps { + formData: Record; + error: string | null; + currentStrategy: any; + setAIGenerating: (generating: boolean) => void; + setError: (error: string | null) => void; + setCurrentStrategy: (strategy: any) => void; + setSaving: (saving: boolean) => void; + setGenerationProgress: (progress: number) => void; + setEducationalContent: (content: any) => void; + setShowEducationalModal: (show: boolean) => void; + validateAllFields: () => boolean; + getCompletionStats: () => any; + generateAIRecommendations: (strategyId: string) => Promise; + createEnhancedStrategy: (strategyData: any) => Promise; + contentPlanningApi: any; +} + +// Strategy Generation Types +export interface StrategyGenerationStatus { + progress?: number; + message?: string; + educational_content?: EducationalContent; + error?: string; +} + +export interface StrategyGenerationCallbacks { + onProgress: (status: StrategyGenerationStatus) => void; + onComplete: (strategy: any) => void; + onError: (error: string) => void; +} + +// Category Navigation Types +export interface CategoryNavigationProps { + activeCategory: string | null; + onCategorySelect: (category: string | null) => void; + completionStats: any; + reviewedCategories: Set; +} + +// Strategy Display Types +export interface StrategyDisplayProps { + currentStrategy: any; + error: string | null; + categoryCompletionMessage: string | null; + onViewStrategicIntelligence: () => void; +} + +// Error Alert Types +export interface ErrorAlertProps { + error: string | null; + onRetry: () => void; + onShowDataSourceTransparency: () => void; +} + +// Success Alert Types +export interface SuccessAlertProps { + currentStrategy: any; + categoryCompletionMessage: string | null; +} \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/StrategyIntelligenceTab.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/StrategyIntelligenceTab.tsx new file mode 100644 index 00000000..435731bc --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/StrategyIntelligenceTab.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { Box, CircularProgress, Alert, Typography, Grid } from '@mui/material'; +import { useStrategyData } from './hooks/useStrategyData'; +import { useStrategyActions } from './hooks/useStrategyActions'; +import StrategyHeader from './components/StrategyHeader'; +import StrategicInsightsCard from './components/StrategicInsightsCard'; +import CompetitiveAnalysisCard from './components/CompetitiveAnalysisCard'; +import PerformancePredictionsCard from './components/PerformancePredictionsCard'; +import ImplementationRoadmapCard from './components/ImplementationRoadmapCard'; +import RiskAssessmentCard from './components/RiskAssessmentCard'; +import StrategyActions from './components/StrategyActions'; +import ConfirmationDialog from './components/ConfirmationDialog'; + +const StrategyIntelligenceTab: React.FC = () => { + const { strategyData, loading, error, loadStrategyData } = useStrategyData(); + const { + strategyConfirmed, + showConfirmDialog, + setShowConfirmDialog, + handleConfirmStrategy, + confirmStrategy, + handleGenerateContentCalendar + } = useStrategyActions(); + + const handleConfirmStrategyClick = () => { + handleConfirmStrategy(); + }; + + const handleConfirmStrategyAction = async () => { + await confirmStrategy(strategyData); + }; + + const handleGenerateContentCalendarAction = async () => { + try { + await handleGenerateContentCalendar(strategyData); + } catch (error) { + console.error('Error generating content calendar:', error); + } + }; + + if (loading) { + return ( + + + + ); + } + + if (error) { + return ( + + {error} + + ); + } + + if (!strategyData) { + return ( + + + No Strategy Data Available + + + Generate a comprehensive strategy first to view strategic intelligence. + + + ); + } + + return ( + + {/* Header Section */} + + + {/* Strategy Components Grid */} + + + + + + + + + {/* Action Buttons */} + + + {/* Confirmation Dialog */} + setShowConfirmDialog(false)} + onConfirm={handleConfirmStrategyAction} + /> + + ); +}; + +export default StrategyIntelligenceTab; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/CompetitiveAnalysisCard.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/CompetitiveAnalysisCard.tsx new file mode 100644 index 00000000..71c90424 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/CompetitiveAnalysisCard.tsx @@ -0,0 +1,435 @@ +import React, { useState } from 'react'; +import { + Grid, + Typography, + Box, + Chip, + List, + ListItem, + ListItemText, + ListItemIcon, + Accordion, + AccordionSummary, + AccordionDetails, + Popover +} from '@mui/material'; +import { + TrendingUp as TrendingUpIcon, + ExpandMore as ExpandMoreIcon, + Business as BusinessIcon, + Star as StarIcon, + Warning as WarningIcon, + CheckCircle as CheckCircleIcon, + Cancel as CancelIcon, + Lightbulb as LightbulbIcon +} from '@mui/icons-material'; +import { StrategyData } from '../types/strategy.types'; +import { + ANALYSIS_CARD_STYLES, + getSectionStyles, + getAccordionStyles, + getEnhancedChipStyles, + getListItemStyles +} from '../styles'; +import ProgressiveCard from './ProgressiveCard'; + +interface CompetitiveAnalysisCardProps { + strategyData: StrategyData | null; +} + +const CompetitiveAnalysisCard: React.FC = ({ strategyData }) => { + const [chipModal, setChipModal] = useState<{ + open: boolean; + anchorEl: HTMLElement | null; + content: string; + type: 'strength' | 'weakness'; + }>({ + open: false, + anchorEl: null, + content: '', + type: 'strength' + }); + + // Get style objects + const sectionStyles = getSectionStyles(); + const accordionStyles = getAccordionStyles(); + const listItemStyles = getListItemStyles(); + + // Helper function to extract company name from description + const extractCompanyName = (description: string): string => { + if (description.includes('Jasper')) return 'Jasper AI'; + if (description.includes('Copy.ai')) return 'Copy.ai'; + if (description.includes('Writesonic')) return 'Writesonic'; + if (description.includes('Grammarly')) return 'Grammarly'; + if (description.includes('Surfer')) return 'Surfer SEO'; + if (description.includes('Clearscope')) return 'Clearscope'; + if (description.includes('Ahrefs')) return 'Ahrefs'; + if (description.includes('SEMrush')) return 'SEMrush'; + return description.split(' ').slice(0, 2).join(' '); + }; + + if (!strategyData?.competitive_analysis) { + return ( + + } + summary={ + + + Competitive analysis data not available + + + } + details={ + + + Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'} + + + } + trigger="hover" + autoCollapseDelay={3000} + /> + + ); + } + + // Summary content - always visible + const summaryContent = ( + + {/* Competitive Overview */} + + + + + {strategyData.competitive_analysis.competitors?.length || 0} + + + + Key Competitors + + + {strategyData.competitive_analysis.competitors?.length || 0} competitors analyzed + + + + + + + + + + + {/* Competitors Preview */} + + + Top Competitors + + + {strategyData.competitive_analysis.competitors?.slice(0, 3).map((competitor: any, index: number) => ( + } + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip} + /> + ))} + {(strategyData.competitive_analysis.competitors?.length || 0) > 3 && ( + + )} + + + + ); + + // Detailed content - shown on expansion + const detailedContent = ( + + {/* Competitors Analysis */} + {strategyData.competitive_analysis.competitors && strategyData.competitive_analysis.competitors.length > 0 && ( + + + Competitor Analysis ({strategyData.competitive_analysis.competitors.length}) + + + {strategyData.competitive_analysis.competitors.map((competitor: any, index: number) => ( + + } + sx={accordionStyles.accordionSummary} + > + + + + + + + {extractCompanyName(competitor.name || competitor.description || 'Unknown Competitor')} + + + {competitor.description || 'Competitor analysis'} + + + + + + + {/* Content Strategy */} + {competitor.content_strategy && ( + + + Content Strategy + + + {competitor.content_strategy} + + + )} + + {/* Strengths */} + {competitor.strengths && competitor.strengths.length > 0 && ( + + + Strengths + + + {competitor.strengths.map((strength: string, strengthIndex: number) => ( + } + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.success).chip} + onClick={(e) => setChipModal({ + open: true, + anchorEl: e.currentTarget, + content: strength, + type: 'strength' + })} + /> + ))} + + + )} + + {/* Weaknesses */} + {competitor.weaknesses && competitor.weaknesses.length > 0 && ( + + + Weaknesses + + + {competitor.weaknesses.map((weakness: string, weaknessIndex: number) => ( + } + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.warning).chip} + onClick={(e) => setChipModal({ + open: true, + anchorEl: e.currentTarget, + content: weakness, + type: 'weakness' + })} + /> + ))} + + + )} + + + + ))} + + )} + + {/* Market Gaps */} + {strategyData.competitive_analysis.market_gaps && strategyData.competitive_analysis.market_gaps.length > 0 && ( + + + Market Gaps ({strategyData.competitive_analysis.market_gaps.length}) + + + + {strategyData.competitive_analysis.market_gaps.map((gap: string, index: number) => ( + + + + + + + ))} + + + + )} + + {/* Opportunities */} + {strategyData.competitive_analysis.opportunities && strategyData.competitive_analysis.opportunities.length > 0 && ( + + + Opportunities ({strategyData.competitive_analysis.opportunities.length}) + + + + {strategyData.competitive_analysis.opportunities.map((opportunity: string, index: number) => ( + + + + + + + ))} + + + + )} + + {/* Strategic Recommendations */} + {strategyData.competitive_analysis.recommendations && strategyData.competitive_analysis.recommendations.length > 0 && ( + + + Strategic Recommendations ({strategyData.competitive_analysis.recommendations.length}) + + + + {strategyData.competitive_analysis.recommendations.map((recommendation: string, index: number) => ( + + + + + + + ))} + + + + )} + + ); + + return ( + + } + summary={summaryContent} + details={detailedContent} + trigger="hover" + autoCollapseDelay={3000} + /> + + {/* Chip Modal for detailed view */} + setChipModal({ ...chipModal, open: false })} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'left', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + PaperProps={{ + sx: { + background: ANALYSIS_CARD_STYLES.colors.background.dark, + border: `1px solid ${ANALYSIS_CARD_STYLES.colors.border.secondary}`, + backdropFilter: 'blur(10px)', + p: 2, + maxWidth: 300 + } + }} + > + + {chipModal.type === 'strength' ? ( + + ) : ( + + )} + + {chipModal.type === 'strength' ? 'Strength' : 'Weakness'} + + + + {chipModal.content} + + + + ); +}; + +export default CompetitiveAnalysisCard; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ConfirmationDialog.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ConfirmationDialog.tsx new file mode 100644 index 00000000..ce83965f --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ConfirmationDialog.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Typography, + Alert, + Box +} from '@mui/material'; +import { + CheckCircle as CheckCircleIcon +} from '@mui/icons-material'; +import { ConfirmationDialogProps } from '../types/strategy.types'; + +const ConfirmationDialog: React.FC = ({ + open, + onClose, + onConfirm +}) => { + return ( + + + + + + + + Confirm Strategy + + + + + + Are you sure you want to confirm this strategy? Once confirmed, you'll be able to generate a content calendar based on this strategy. + + + + Next Steps: After confirmation, you can generate a comprehensive content calendar that follows this strategy. + + + + + + + + + ); +}; + +export default ConfirmationDialog; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ImplementationRoadmapCard.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ImplementationRoadmapCard.tsx new file mode 100644 index 00000000..00a1d72c --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ImplementationRoadmapCard.tsx @@ -0,0 +1,453 @@ +import React from 'react'; +import { + Grid, + Typography, + Box, + Chip, + List, + ListItem, + ListItemText, + ListItemIcon, + Accordion, + AccordionSummary, + AccordionDetails +} from '@mui/material'; +import { + Timeline as TimelineIcon, + ExpandMore as ExpandMoreIcon, + Group as GroupIcon, + AttachMoney as MoneyIcon, + CheckCircle as CheckCircleIcon +} from '@mui/icons-material'; +import { StrategyData } from '../types/strategy.types'; +import { + ANALYSIS_CARD_STYLES, + getSectionStyles, + getAccordionStyles, + getEnhancedChipStyles, + getListItemStyles +} from '../styles'; +import ProgressiveCard from './ProgressiveCard'; + +interface ImplementationRoadmapCardProps { + strategyData: StrategyData | null; +} + +const ImplementationRoadmapCard: React.FC = ({ strategyData }) => { + // Get style objects + const sectionStyles = getSectionStyles(); + const accordionStyles = getAccordionStyles(); + const listItemStyles = getListItemStyles(); + + // Helper function to format budget allocation + const formatBudgetAllocation = (budgetAllocation: any): string => { + if (typeof budgetAllocation === 'string') return budgetAllocation; + if (typeof budgetAllocation === 'object' && budgetAllocation !== null) { + return Object.entries(budgetAllocation) + .map(([key, value]) => `${key}: ${value}`) + .join(', '); + } + return 'Budget allocation not specified'; + }; + + if (!strategyData?.implementation_roadmap) { + return ( + + } + summary={ + + + Implementation roadmap data not available + + + } + details={ + + + Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'} + + + } + trigger="hover" + autoCollapseDelay={3000} + /> + + ); + } + + // Summary content - always visible + const summaryContent = ( + + {/* Project Overview */} + + + + + {strategyData.implementation_roadmap.phases?.length || 0} + + + + Project Phases + + + {strategyData.implementation_roadmap.total_duration || '6 months'} implementation + + + + + total + (phase.tasks?.length || 0), 0) || 0} Tasks`} + size="small" + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.info).chip} + /> + total + (phase.milestones?.length || 0), 0) || 0} Milestones`} + size="small" + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip} + /> + + + + + {/* Phases Preview */} + + + Implementation Phases + + + {strategyData.implementation_roadmap.phases?.slice(0, 4).map((phase: any, index: number) => ( + } + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip} + /> + ))} + {(strategyData.implementation_roadmap.phases?.length || 0) > 4 && ( + + )} + + + + ); + + // Detailed content - shown on expansion + const detailedContent = ( + + {/* Project Timeline */} + {strategyData.implementation_roadmap.timeline && ( + + + Project Timeline + + + + + Duration: {strategyData.implementation_roadmap.total_duration || '6 months'} + + + {strategyData.implementation_roadmap.timeline.start_date && ( + + Start Date: {strategyData.implementation_roadmap.timeline.start_date} + + )} + {strategyData.implementation_roadmap.timeline.end_date && ( + + End Date: {strategyData.implementation_roadmap.timeline.end_date} + + )} + {strategyData.implementation_roadmap.timeline.key_milestones && strategyData.implementation_roadmap.timeline.key_milestones.length > 0 && ( + + + Key Milestones: + + + {strategyData.implementation_roadmap.timeline.key_milestones.map((milestone: string, index: number) => ( + + + + + + + ))} + + + )} + + + )} + + {/* Implementation Phases */} + {strategyData.implementation_roadmap.phases && strategyData.implementation_roadmap.phases.length > 0 && ( + + + Implementation Phases ({strategyData.implementation_roadmap.phases.length}) + + + {strategyData.implementation_roadmap.phases.map((phase: any, index: number) => ( + + } + sx={accordionStyles.accordionSummary} + > + + + {index + 1} + + + + {phase.name || `Phase ${index + 1}`} + + + {phase.duration || '2 months'} β€’ {phase.tasks?.length || 0} tasks β€’ {phase.milestones?.length || 0} milestones + + + + + + + {/* Phase Description */} + {phase.description && ( + + + {phase.description} + + + )} + + {/* Tasks */} + {phase.tasks && phase.tasks.length > 0 && ( + + + Tasks ({phase.tasks.length}) + + + {phase.tasks.map((task: string, taskIndex: number) => ( + + + + + + + ))} + + + )} + + {/* Milestones */} + {phase.milestones && phase.milestones.length > 0 && ( + + + Milestones ({phase.milestones.length}) + + + {phase.milestones.map((milestone: string, milestoneIndex: number) => ( + + + + + + + ))} + + + )} + + {/* Resources */} + {phase.resources && phase.resources.length > 0 && ( + + + Resources ({phase.resources.length}) + + + {phase.resources.map((resource: string, resourceIndex: number) => ( + + + + + + + ))} + + + )} + + + + ))} + + )} + + {/* Resource Allocation */} + {strategyData.implementation_roadmap.resource_allocation && ( + + + Resource Allocation + + + {/* Team Members */} + {strategyData.implementation_roadmap.resource_allocation.team_members && strategyData.implementation_roadmap.resource_allocation.team_members.length > 0 && ( + + + Team Members ({strategyData.implementation_roadmap.resource_allocation.team_members.length}) + + + {strategyData.implementation_roadmap.resource_allocation.team_members.map((member: string, index: number) => ( + } + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip} + /> + ))} + + + )} + + {/* Budget Allocation */} + {strategyData.implementation_roadmap.resource_allocation.budget_allocation && ( + + + Budget Allocation + + + {formatBudgetAllocation(strategyData.implementation_roadmap.resource_allocation.budget_allocation)} + + + )} + + + )} + + {/* Success Metrics */} + {strategyData.implementation_roadmap.success_metrics && strategyData.implementation_roadmap.success_metrics.length > 0 && ( + + + Success Metrics ({strategyData.implementation_roadmap.success_metrics.length}) + + + + {strategyData.implementation_roadmap.success_metrics.map((metric: string, index: number) => ( + + + + + + + ))} + + + + )} + + ); + + return ( + + } + summary={summaryContent} + details={detailedContent} + trigger="hover" + autoCollapseDelay={3000} + /> + + ); +}; + +export default ImplementationRoadmapCard; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/PerformancePredictionsCard.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/PerformancePredictionsCard.tsx new file mode 100644 index 00000000..1e5e2596 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/PerformancePredictionsCard.tsx @@ -0,0 +1,539 @@ +import React from 'react'; +import { + Grid, + Typography, + Box, + Chip, + List, + ListItem, + ListItemText, + ListItemIcon, + Divider +} from '@mui/material'; +import { + ShowChart as ShowChartIcon, + TrendingUp as TrendingUpIcon, + Timeline as TimelineIcon, + Assessment as AssessmentIcon +} from '@mui/icons-material'; +import { StrategyData } from '../types/strategy.types'; +import { + ANALYSIS_CARD_STYLES, + getSectionStyles, + getEnhancedChipStyles, + getListItemStyles +} from '../styles'; +import ProgressiveCard from './ProgressiveCard'; + +interface PerformancePredictionsCardProps { + strategyData: StrategyData | null; +} + +const PerformancePredictionsCard: React.FC = ({ strategyData }) => { + // Get style objects + const sectionStyles = getSectionStyles(); + const listItemStyles = getListItemStyles(); + + if (!strategyData?.performance_predictions) { + return ( + + } + summary={ + + + Performance predictions data not available + + + } + details={ + + + Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'} + + + } + trigger="hover" + autoCollapseDelay={3000} + /> + + ); + } + + // Summary content - always visible + const summaryContent = ( + + {/* ROI Summary */} + + + + + {strategyData.performance_predictions.roi_predictions?.estimated_roi || '25%'} + + + + ROI Predictions + + + Expected return on investment + + + + + + + + + + {/* ROI Description - Full width container */} + + + ROI of {strategyData.performance_predictions.roi_predictions?.estimated_roi || '300-350%'} is achievable leveraging the strong cost-per-lead to lifetime-value ratio. + + + + + {/* Key Metrics Preview */} + + + Key Metrics Preview + + + } + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.primary).chip} + /> + } + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.secondary).chip} + /> + } + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.accent).chip} + /> + + + + ); + + // Detailed content - shown on expansion + const detailedContent = ( + + {/* ROI Summary */} + + + ROI Summary + + + + + Estimated ROI: {strategyData.performance_predictions.roi_predictions?.estimated_roi || '25%'} + + + Success Probability: {(strategyData.performance_predictions as any)?.success_probability || '85%'} + + + Implementation Timeline: {(strategyData.performance_predictions as any)?.implementation_timeline || '6 months'} + + + + + + {/* Key Metrics */} + + + Key Metrics + + + + {/* Traffic Predictions */} + {strategyData.performance_predictions.traffic_predictions && ( + + + Traffic Growth + + + {strategyData.performance_predictions.traffic_predictions.growth_rate || '150% increase'} + + + )} + + {/* Engagement Predictions */} + {strategyData.performance_predictions.engagement_predictions && ( + + + Engagement Rate + + + {strategyData.performance_predictions.engagement_predictions.engagement_rate || '45% improvement'} + + + )} + + {/* Conversion Predictions */} + {strategyData.performance_predictions.conversion_predictions && ( + + + Conversion Rate + + + {strategyData.performance_predictions.conversion_predictions.conversion_rate || '3.2% to 5.8%'} + + + )} + + + + + + + {/* Detailed Predictions */} + + + Detailed Predictions + + + + {/* Traffic Predictions */} + {strategyData.performance_predictions.traffic_predictions && ( + + + + + + + )} + + {/* Engagement Predictions */} + {strategyData.performance_predictions.engagement_predictions && ( + + + + + + + )} + + {/* Conversion Predictions */} + {strategyData.performance_predictions.conversion_predictions && ( + + + + + + + )} + + {/* ROI Predictions */} + {strategyData.performance_predictions.roi_predictions && ( + + + + + + + )} + + + + + {/* Timeline Projections */} + + + Timeline Projections + + + + + β€’ Month 1-2: Initial setup and foundation building + + + β€’ Month 3-4: Content creation and optimization + + + β€’ Month 5-6: Scaling and performance optimization + + + β€’ Ongoing: Continuous monitoring and improvement + + + + + + ); + + return ( + + } + summary={summaryContent} + details={detailedContent} + trigger="hover" + autoCollapseDelay={3000} + /> + + ); +}; + +export default PerformancePredictionsCard; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ProgressiveCard.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ProgressiveCard.tsx new file mode 100644 index 00000000..0d2f7abb --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ProgressiveCard.tsx @@ -0,0 +1,259 @@ +import React, { useState, useRef } from 'react'; +import { + Card, + CardContent, + Box, + Button, + Typography, + Fade, + useTheme +} from '@mui/material'; +import { + ExpandMore as ExpandMoreIcon, + ExpandLess as ExpandLessIcon, + KeyboardArrowDown as ArrowDownIcon +} from '@mui/icons-material'; +import { motion, AnimatePresence } from 'framer-motion'; +import { + ANALYSIS_CARD_STYLES, + getAnalysisCardStyles, + getEnhancedChipStyles +} from '../styles'; + +interface ProgressiveCardProps { + summary: React.ReactNode; + details: React.ReactNode; + trigger?: 'hover' | 'click'; + title?: string; + subtitle?: string; + icon?: React.ReactNode; + autoCollapseDelay?: number; // milliseconds + className?: string; +} + +const ProgressiveCard: React.FC = ({ + summary, + details, + trigger = 'click', + title, + subtitle, + icon, + autoCollapseDelay = 3000, // 3 seconds default + className +}) => { + const [isExpanded, setIsExpanded] = useState(false); + const hoverTimeoutRef = useRef(null); + const theme = useTheme(); + + const cardStyles = getAnalysisCardStyles(); + + // Handle hover interactions + const handleMouseEnter = () => { + if (trigger === 'hover') { + // Clear any existing timeout + if (hoverTimeoutRef.current) { + clearTimeout(hoverTimeoutRef.current); + hoverTimeoutRef.current = null; + } + setIsExpanded(true); + } + }; + + const handleMouseLeave = () => { + if (trigger === 'hover') { + // Set timeout to auto-collapse + hoverTimeoutRef.current = setTimeout(() => { + setIsExpanded(false); + hoverTimeoutRef.current = null; + }, autoCollapseDelay); + } + }; + + // Handle click interactions + const handleToggle = () => { + if (trigger === 'click') { + setIsExpanded(!isExpanded); + } + }; + + return ( + + + + {/* Header Section */} + {title && ( + + + {icon && ( + + {icon} + + )} + + + {title} + + {subtitle && ( + + {subtitle} + + )} + + + + {/* Trigger Button */} + {trigger === 'click' && ( + + )} + + )} + + {/* Summary Section - Always Visible */} + + {summary} + + + {/* Progressive Details Section */} + + {isExpanded && ( + + + + {details} + + + + )} + + + {/* Hover Indicator (for hover trigger) */} + {trigger === 'hover' && !isExpanded && ( + + + + + Hover to see more + + + + )} + + + + ); +}; + +export default ProgressiveCard; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ProgressiveDemo.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ProgressiveDemo.tsx new file mode 100644 index 00000000..3aff6441 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/ProgressiveDemo.tsx @@ -0,0 +1,413 @@ +import React from 'react'; +import { + Grid, + Typography, + Box, + Chip +} from '@mui/material'; +import { + Lightbulb as LightbulbIcon, + TrendingUp as TrendingUpIcon, + Security as SecurityIcon, + Schedule as ScheduleIcon, + Assessment as AssessmentIcon +} from '@mui/icons-material'; +import ProgressiveCard from './ProgressiveCard'; +import { ANALYSIS_CARD_STYLES, getEnhancedChipStyles } from '../styles'; + +const ProgressiveDemo: React.FC = () => { + return ( + + + Progressive Disclosure Demo + + + + Experience the new progressive disclosure system. Cards show a summary initially, + with detailed content revealed through user interaction. + + + + {/* Click Trigger Example */} + + } + summary={ + + + + + 85% + + + + Market Analysis + + + Strong positioning identified + + + + + + + + + + + + + + + + } + details={ + + + Detailed Strategic Insights + + + + β€’ Market positioning analysis reveals strong competitive advantages + + + β€’ Consumer behavior patterns indicate high engagement potential + + + β€’ Business model optimization opportunities identified + + + β€’ Revenue growth projections show 25% increase potential + + + + } + trigger="click" + /> + + + {/* Hover Trigger Example */} + + } + summary={ + + + + + 92% + + + + ROI Predictions + + + High success probability + + + + + + + + + + + + + + + + } + details={ + + + Detailed Performance Metrics + + + + β€’ Traffic growth: 150% increase in organic visitors + + + β€’ Engagement rate: 45% improvement in user interaction + + + β€’ Conversion rate: 3.2% to 5.8% improvement + + + β€’ Revenue impact: $50K additional monthly revenue + + + + } + trigger="hover" + autoCollapseDelay={5000} + /> + + + {/* Risk Assessment Example */} + + } + summary={ + + + + + Med + + + + Risk Level + + + Medium risk identified + + + + + + + + + + + + + + + + } + details={ + + + Risk Categories & Mitigation + + + + β€’ Technical Risks: Infrastructure scalability challenges + + + β€’ Market Risks: Competitive pressure and market saturation + + + β€’ Operational Risks: Resource allocation and team scaling + + + β€’ Mitigation: Comprehensive monitoring and contingency plans + + + + } + trigger="click" + /> + + + {/* Implementation Roadmap Example */} + + } + summary={ + + + + + 6M + + + + Timeline + + + 6-month implementation + + + + + + + + + + + + + + + + + } + details={ + + + Implementation Phases + + + + β€’ Phase 1 (Months 1-2): Foundation and setup + + + β€’ Phase 2 (Months 3-4): Core implementation + + + β€’ Phase 3 (Months 5-6): Optimization and scaling + + + β€’ Phase 4 (Ongoing): Monitoring and maintenance + + + + } + trigger="hover" + autoCollapseDelay={4000} + /> + + + + ); +}; + +export default ProgressiveDemo; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/RiskAssessmentCard.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/RiskAssessmentCard.tsx new file mode 100644 index 00000000..179c65d0 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/RiskAssessmentCard.tsx @@ -0,0 +1,404 @@ +import React from 'react'; +import { + Grid, + Typography, + Box, + Chip, + List, + ListItem, + ListItemText, + ListItemIcon, + Accordion, + AccordionSummary, + AccordionDetails +} from '@mui/material'; +import { + Security as SecurityIcon, + ExpandMore as ExpandMoreIcon, + Warning as WarningIcon, + CheckCircle as CheckCircleIcon, + Error as ErrorIcon, + Info as InfoIcon +} from '@mui/icons-material'; +import { StrategyData } from '../types/strategy.types'; +import { + ANALYSIS_CARD_STYLES, + getSectionStyles, + getAccordionStyles, + getEnhancedChipStyles, + getListItemStyles +} from '../styles'; +import ProgressiveCard from './ProgressiveCard'; + +interface RiskAssessmentCardProps { + strategyData: StrategyData | null; +} + +const RiskAssessmentCard: React.FC = ({ strategyData }) => { + // Get style objects + const sectionStyles = getSectionStyles(); + const accordionStyles = getAccordionStyles(); + const listItemStyles = getListItemStyles(); + + // Helper function to get risk level color + const getRiskLevelColor = (level: string) => { + const lowerLevel = level.toLowerCase(); + if (lowerLevel.includes('high')) return ANALYSIS_CARD_STYLES.colors.error; + if (lowerLevel.includes('medium')) return ANALYSIS_CARD_STYLES.colors.warning; + if (lowerLevel.includes('low')) return ANALYSIS_CARD_STYLES.colors.success; + return ANALYSIS_CARD_STYLES.colors.info; + }; + + if (!strategyData?.risk_assessment) { + return ( + + } + summary={ + + + Risk assessment data not available + + + } + details={ + + + Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'} + + + } + trigger="hover" + autoCollapseDelay={3000} + /> + + ); + } + + // Summary content - always visible + const summaryContent = ( + + {/* Risk Overview */} + + + + + {strategyData.risk_assessment.risks?.length || 0} + + + + Risk Level + + + {strategyData.risk_assessment.overall_risk_level || 'Medium'} overall risk + + + + + + + + + + + {/* Risk Categories Preview */} + + + Risk Categories + + + {strategyData.risk_assessment.risk_categories && Object.keys(strategyData.risk_assessment.risk_categories).slice(0, 4).map((category: string) => ( + } + sx={getEnhancedChipStyles(ANALYSIS_CARD_STYLES.colors.warning).chip} + /> + ))} + {strategyData.risk_assessment.risk_categories && Object.keys(strategyData.risk_assessment.risk_categories).length > 4 && ( + + )} + + + + ); + + // Detailed content - shown on expansion + const detailedContent = ( + + {/* Overall Risk Assessment */} + + + Overall Risk Assessment + + + + } + sx={getEnhancedChipStyles(getRiskLevelColor(strategyData.risk_assessment.overall_risk_level || 'Medium')).chip} + /> + + + {strategyData.risk_assessment.risks?.length || 0} identified risks with {strategyData.risk_assessment.mitigation_strategies?.length || 0} mitigation strategies + + + + + {/* Individual Risks */} + {strategyData.risk_assessment.risks && strategyData.risk_assessment.risks.length > 0 && ( + + + Individual Risks ({strategyData.risk_assessment.risks.length}) + + + {strategyData.risk_assessment.risks.map((risk: any, index: number) => ( + + } + sx={accordionStyles.accordionSummary} + > + + + + + + + {typeof risk === 'string' ? risk : risk.risk || 'Risk'} + + + {typeof risk === 'object' && risk.probability ? `${risk.probability} probability, ${risk.impact} impact` : 'Risk assessment'} + + + + + + + {typeof risk === 'object' && ( + <> + {/* Risk Details */} + + + {risk.risk} + + + + + + + + {/* Mitigation Strategy */} + {risk.mitigation && ( + + + Mitigation Strategy + + + {risk.mitigation} + + + )} + + {/* Contingency Plan */} + {risk.contingency && ( + + + Contingency Plan + + + {risk.contingency} + + + )} + + )} + + {typeof risk === 'string' && ( + + {risk} + + )} + + + + ))} + + )} + + {/* Mitigation Strategies */} + {strategyData.risk_assessment.mitigation_strategies && strategyData.risk_assessment.mitigation_strategies.length > 0 && ( + + + Mitigation Strategies ({strategyData.risk_assessment.mitigation_strategies.length}) + + + + {strategyData.risk_assessment.mitigation_strategies.map((strategy: string, index: number) => ( + + + + + + + ))} + + + + )} + + {/* Risk Categories */} + {strategyData.risk_assessment.risk_categories && Object.keys(strategyData.risk_assessment.risk_categories).length > 0 && ( + + + Risk Categories + + + {Object.entries(strategyData.risk_assessment.risk_categories).map(([category, risks]: [string, any]) => ( + + } + sx={accordionStyles.accordionSummary} + > + + + + + + + {category.replace(/_/g, ' ')} + + + {Array.isArray(risks) ? `${risks.length} risks` : 'Risk category'} + + + + + + + {Array.isArray(risks) ? ( + + {risks.map((risk: string, index: number) => ( + + + + + + + ))} + + ) : ( + + {typeof risks === 'string' ? risks : 'Risk category details'} + + )} + + + + ))} + + )} + + {/* Monitoring Framework */} + {strategyData.risk_assessment.monitoring_framework && ( + + + Monitoring Framework + + + {Object.entries(strategyData.risk_assessment.monitoring_framework).map(([key, value]: [string, any]) => ( + + + {key.replace(/_/g, ' ')} + + + {typeof value === 'string' ? value : JSON.stringify(value)} + + + ))} + + + )} + + ); + + return ( + + } + summary={summaryContent} + details={detailedContent} + trigger="hover" + autoCollapseDelay={3000} + /> + + ); +}; + +export default RiskAssessmentCard; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategicInsightsCard.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategicInsightsCard.tsx new file mode 100644 index 00000000..becec46d --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategicInsightsCard.tsx @@ -0,0 +1,330 @@ +import React from 'react'; +import { + Grid, + Typography, + Box, + Chip, + List, + ListItem, + ListItemText, + ListItemIcon, + Accordion, + AccordionSummary, + AccordionDetails +} from '@mui/material'; +import { + Lightbulb as LightbulbIcon, + ExpandMore as ExpandMoreIcon, + TrendingUp as TrendingUpIcon, + Psychology as PsychologyIcon, + Business as BusinessIcon, + Analytics as AnalyticsIcon +} from '@mui/icons-material'; +import { motion } from 'framer-motion'; +import { StrategyData } from '../types/strategy.types'; +import { + ANALYSIS_CARD_STYLES, + getSectionStyles, + getAccordionStyles, + getEnhancedChipStyles, + getListItemStyles, + getAnimationStyles +} from '../styles'; +import ProgressiveCard from './ProgressiveCard'; + +interface StrategicInsightsCardProps { + strategyData: StrategyData | null; +} + +const StrategicInsightsCard: React.FC = ({ strategyData }) => { + // Get style objects + const sectionStyles = getSectionStyles(); + const accordionStyles = getAccordionStyles(); + const listItemStyles = getListItemStyles(); + const animationStyles = getAnimationStyles(); + + console.log('πŸ” StrategicInsightsCard - strategyData:', strategyData); + console.log('πŸ” StrategicInsightsCard - strategic_insights:', strategyData?.strategic_insights); + + if (!strategyData?.strategic_insights) { + return ( + + } + summary={ + + + Strategic insights data not available + + + } + details={ + + + Available data keys: {strategyData ? Object.keys(strategyData).join(', ') : 'No data'} + + + } + trigger="click" + /> + + ); + } + + // Helper function to get insight icon + const getInsightIcon = (type: string) => { + switch (type?.toLowerCase()) { + case 'market': return ; + case 'consumer': return ; + case 'business': return ; + case 'analytics': return ; + default: return ; + } + }; + + // Summary content - always visible + const summaryContent = ( + + {/* Market Analysis Summary */} + + + + + 85% + + + + Market Analysis + + + Strong market positioning identified + + + + + + + + + + + {/* Key Insights Preview */} + + + Key Insights Preview + + + {strategyData.strategic_insights.insights?.slice(0, 3).map((insight: any, index: number) => ( + + ))} + {(strategyData.strategic_insights.insights?.length || 0) > 3 && ( + + )} + + + + ); + + // Detailed content - shown on expansion + const detailedContent = ( + + {/* Strategic Insights by Type */} + {strategyData.strategic_insights.insights && strategyData.strategic_insights.insights.length > 0 && ( + + + Detailed Strategic Insights ({strategyData.strategic_insights.insights.length}) + + + {/* Group insights by type */} + {Object.entries( + strategyData.strategic_insights.insights.reduce((acc: any, insight: any) => { + const type = insight.type || 'General'; + if (!acc[type]) acc[type] = []; + acc[type].push(insight); + return acc; + }, {}) + ).map(([type, insights]: [string, any]) => ( + + } + sx={accordionStyles.accordionSummary} + > + + + {getInsightIcon(type)} + + + + {type} Insights ({insights.length}) + + + {insights.length} strategic {insights.length === 1 ? 'insight' : 'insights'} + + + + + + + + {insights.map((insight: any, index: number) => ( + + + + + + + + + + + + {insight.reasoning && ( + + Reasoning: {insight.reasoning} + + )} + + } + /> + + ))} + + + + + ))} + + )} + + {/* Content Opportunities */} + {strategyData.strategic_insights.content_opportunities && strategyData.strategic_insights.content_opportunities.length > 0 && ( + + + Content Opportunities ({strategyData.strategic_insights.content_opportunities.length}) + + + + {strategyData.strategic_insights.content_opportunities.map((opportunity: string, index: number) => ( + + + + + + + ))} + + + + )} + + ); + + return ( + + } + summary={summaryContent} + details={detailedContent} + trigger="hover" + autoCollapseDelay={2000} + /> + + ); +}; + +export default StrategicInsightsCard; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategyActions.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategyActions.tsx new file mode 100644 index 00000000..59817a33 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategyActions.tsx @@ -0,0 +1,162 @@ +import React from 'react'; +import { + Box, + Button, + Alert +} from '@mui/material'; +import { + Check as CheckIcon, + CalendarToday as CalendarIcon, + Refresh as RefreshIcon +} from '@mui/icons-material'; +import { motion } from 'framer-motion'; +import { StrategyData } from '../types/strategy.types'; + +interface StrategyActionsProps { + strategyData: StrategyData | null; + strategyConfirmed: boolean; + onConfirmStrategy: () => void; + onGenerateContentCalendar: () => void; + onRefreshData: () => void; +} + +const StrategyActions: React.FC = ({ + strategyData, + strategyConfirmed, + onConfirmStrategy, + onGenerateContentCalendar, + onRefreshData +}) => { + return ( + + {/* Strategy Confirmation Status */} + {strategyConfirmed && ( + + } + sx={{ + fontWeight: 600, + '&:hover': { + backgroundColor: 'rgba(76, 175, 80, 0.1)', + transform: 'translateY(-1px)' + }, + transition: 'all 0.3s ease' + }} + > + Generate Content Calendar + + } + > + Strategy confirmed! You can now generate a content calendar based on this strategy. + + + )} + + {/* Action Buttons */} + + {!strategyConfirmed ? ( + + + + ) : ( + + + + )} + + + + + + + ); +}; + +export default StrategyActions; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategyHeader.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategyHeader.tsx new file mode 100644 index 00000000..f96cac19 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/StrategyHeader.tsx @@ -0,0 +1,619 @@ +import React, { useState } from 'react'; +import { + Paper, + Grid, + Typography, + Chip, + Box, + Tooltip, + LinearProgress, + Badge, + Card, + CardContent, + CircularProgress, + Button +} from '@mui/material'; +import { + Psychology as PsychologyIcon, + TrendingUp as TrendingUpIcon, + CheckCircle as CheckCircleIcon, + Warning as WarningIcon, + Schedule as ScheduleIcon, + CalendarToday as CalendarTodayIcon, + AutoAwesome as AutoAwesomeIcon, + Timeline as TimelineIcon, + Info as InfoIcon, + Analytics as AnalyticsIcon, + Assessment as AssessmentIcon, + Business as BusinessIcon, + ShowChart as ShowChartIcon, + Security as SecurityIcon, + ArrowForward as ArrowForwardIcon, + Star as StarIcon, + DataUsage as DataUsageIcon, + Input as InputIcon, + Storage as StorageIcon, + Person as PersonIcon +} from '@mui/icons-material'; +import { motion } from 'framer-motion'; +import { StrategyData } from '../types/strategy.types'; +import { getStrategyName, getStrategyGenerationDate } from '../utils/strategyTransformers'; + +interface StrategyHeaderProps { + strategyData: StrategyData | null; + strategyConfirmed: boolean; +} + +const StrategyHeader: React.FC = ({ strategyData, strategyConfirmed }) => { + const [showNextStepText, setShowNextStepText] = useState(false); + + if (!strategyData) return null; + + // Helper function to get percentage value from string + const getPercentageValue = (value: string | undefined) => { + if (!value) return 0; + const match = value.match(/(\d+)/); + return match ? parseInt(match[1]) : 0; + }; + + // Helper function to get risk color + const getRiskColor = (riskLevel: string | undefined) => { + switch (riskLevel?.toLowerCase()) { + case 'low': return '#4caf50'; + case 'medium': return '#ff9800'; + case 'high': return '#f44336'; + case 'high-medium': return '#ff5722'; + default: return '#ff9800'; + } + }; + + // Helper function to extract company name from strategy name + const getCompanyName = (strategyName: string) => { + // Extract company name from strategy name (e.g., "Enhanced Content Strategy" -> "ALwrity") + // For now, we'll use a default company name + return "ALwrity"; + }; + + // Helper function to get timeline percentage + const getTimelinePercentage = (timeline: string | undefined) => { + if (!timeline) return 0; + const match = timeline.match(/(\d+)/); + return match ? Math.min(parseInt(match[1]) * 10, 100) : 0; // Convert months to percentage + }; + + const roiValue = getPercentageValue(strategyData.summary?.estimated_roi); + const successValue = getPercentageValue(strategyData.summary?.success_probability); + const timelineValue = getTimelinePercentage(strategyData.summary?.implementation_timeline); + const riskColor = getRiskColor(strategyData.summary?.risk_level); + const companyName = getCompanyName(getStrategyName(strategyData)); + + // Analysis components data + const analysisComponents = [ + { name: 'Strategic Insights', icon: , color: '#667eea' }, + { name: 'Competitive Analysis', icon: , color: '#4caf50' }, + { name: 'Performance Predictions', icon: , color: '#2196f3' }, + { name: 'Implementation Roadmap', icon: , color: '#ff9800' }, + { name: 'Risk Assessment', icon: , color: '#f44336' } + ]; + + // Mock data sources and user inputs (replace with actual data from strategyData) + const dataSources = [ + { name: 'Industry Analysis', type: 'Market Research', icon: }, + { name: 'Competitor Data', type: 'External Sources', icon: }, + { name: 'User Preferences', type: 'Input Survey', icon: }, + { name: 'Content Performance', type: 'Analytics', icon: } + ]; + + return ( + + + {/* Animated Border Lights */} + + + + {/* Header Section - More Compact */} + + + + + + + + + {companyName} Content Strategy + + + + Generated on {getStrategyGenerationDate(strategyData)} + + + + + {/* Strategy Metadata Chips - More Compact */} + + + } color="default"> + } + label="AI Generated" + size="small" + sx={{ + background: 'rgba(102, 126, 234, 0.2)', + color: '#667eea', + border: '1px solid rgba(102, 126, 234, 0.3)', + fontWeight: 600, + fontSize: '0.65rem', + height: 24 + }} + /> + + + + } color="default"> + } + label="Comprehensive" + size="small" + sx={{ + background: 'rgba(76, 175, 80, 0.2)', + color: '#4caf50', + border: '1px solid rgba(76, 175, 80, 0.3)', + fontWeight: 600, + fontSize: '0.65rem', + height: 24 + }} + /> + + + + } color="default"> + } + label={strategyData.strategy_metadata?.content_calendar_ready ? "Calendar Ready" : "Calendar Pending"} + size="small" + color={strategyData.strategy_metadata?.content_calendar_ready ? "success" : "warning"} + sx={{ + background: strategyData.strategy_metadata?.content_calendar_ready + ? 'rgba(76, 175, 80, 0.2)' + : 'rgba(255, 152, 0, 0.2)', + border: '1px solid rgba(255, 255, 255, 0.2)', + fontWeight: 600, + fontSize: '0.65rem', + height: 24 + }} + /> + + + + + + {/* Key Metrics Section with 4 Circular Progress Charts */} + + + {/* ROI Circular Progress */} + + + + + + + {roiValue}% + + + + + ROI + + + + + {/* Success Probability Circular Progress */} + + + + + + + {successValue}% + + + + + Success + + + + + {/* Risk Level Circular Progress */} + + + + + + + {strategyData.summary?.risk_level === 'Low' ? 'L' : + strategyData.summary?.risk_level === 'Medium' ? 'M' : + strategyData.summary?.risk_level === 'High' ? 'H' : 'HM'} + + + + + Risk + + + + + {/* Timeline Circular Progress */} + + + + + + + {strategyData.summary?.implementation_timeline?.match(/\d+/)?.[0] || '6'}m + + + + + Timeline + + + + + + + + {/* Strategy Status Section - Expanded to show data sources and analysis components */} + + + + + Strategy Status & Data Sources + + + {/* Status Info */} + + + + + + + Status + + + {strategyConfirmed ? 'Confirmed' : 'Pending Review'} + + + + + + + + User ID + + + {strategyData.strategy_metadata?.user_id} + + + + + + + {/* Data Sources */} + + + + Data Sources & User Inputs + + + {dataSources.map((source, index) => ( + + ))} + + + + + + {/* Analysis Components Section - Moved inside the card */} + + + Analysis Components + + + {analysisComponents.map((component, index) => ( + + } color="default"> + + + + ))} + } + label="Ready for Review" + size="small" + color="success" + sx={{ + background: 'linear-gradient(135deg, rgba(76, 175, 80, 0.3) 0%, rgba(76, 175, 80, 0.2) 100%)', + color: '#4caf50', + border: '1px solid rgba(76, 175, 80, 0.4)', + fontWeight: 600, + fontSize: '0.65rem', + height: 24, + boxShadow: '0 2px 8px rgba(76, 175, 80, 0.2)', + '&:hover': { + background: 'linear-gradient(135deg, rgba(76, 175, 80, 0.4) 0%, rgba(76, 175, 80, 0.3) 100%)', + boxShadow: '0 4px 12px rgba(76, 175, 80, 0.3)', + transform: 'translateY(-1px)' + }, + transition: 'all 0.2s ease' + }} + /> + + + + + + + {/* Next Steps Button - Area B */} + + setShowNextStepText(false)} + > + + + + + + + ); +}; + +export default StrategyHeader; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/TestComponent.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/TestComponent.tsx new file mode 100644 index 00000000..8d8595ab --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/TestComponent.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Box, Typography, Paper } from '@mui/material'; +import { StrategyData } from '../types/strategy.types'; + +interface TestComponentProps { + strategyData: StrategyData | null; +} + +const TestComponent: React.FC = ({ strategyData }) => { + if (!strategyData) return null; + + return ( + + + βœ… Modular Structure Test + + + Strategy Name: {strategyData.strategy_metadata?.strategy_name || strategyData.metadata?.strategy_name} + + + ROI: {strategyData.summary?.estimated_roi} + + + Success Probability: {strategyData.summary?.success_probability} + + + βœ… Modular structure is working correctly! + + + ); +}; + +export default TestComponent; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/hooks/useStrategyActions.ts b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/hooks/useStrategyActions.ts new file mode 100644 index 00000000..82d6bec7 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/hooks/useStrategyActions.ts @@ -0,0 +1,84 @@ +import { useState } from 'react'; +import { contentPlanningApi } from '../../../../../services/contentPlanningApi'; +import { StrategyData } from '../types/strategy.types'; + +export const useStrategyActions = () => { + const [strategyConfirmed, setStrategyConfirmed] = useState(false); + const [showConfirmDialog, setShowConfirmDialog] = useState(false); + + const handleConfirmStrategy = () => { + setShowConfirmDialog(true); + }; + + const confirmStrategy = async (strategyData: StrategyData | null) => { + try { + setStrategyConfirmed(true); + setShowConfirmDialog(false); + + // Save confirmation status to backend + const userId = strategyData?.strategy_metadata?.user_id || strategyData?.metadata?.user_id; + if (userId) { + try { + // Update the strategy with confirmation status + await contentPlanningApi.updateEnhancedStrategy( + userId.toString(), + { confirmed: true, confirmed_at: new Date().toISOString() } + ); + console.log('Strategy confirmation saved to backend'); + } catch (updateError) { + console.warn('Could not save confirmation to backend:', updateError); + // Don't fail the confirmation if backend update fails + } + } + + console.log('Strategy confirmed! Ready to generate content calendar.'); + } catch (error) { + console.error('Error confirming strategy:', error); + setStrategyConfirmed(false); + } + }; + + const handleGenerateContentCalendar = async (strategyData: StrategyData | null) => { + try { + const userId = strategyData?.strategy_metadata?.user_id || strategyData?.metadata?.user_id; + if (!userId) { + console.error('No strategy data available for calendar generation'); + return; + } + + // Generate content calendar based on confirmed strategy + const calendarRequest = { + user_id: userId, + strategy_id: userId, // Using user_id as strategy_id for now + calendar_type: 'comprehensive', + industry: strategyData.base_strategy?.industry || 'technology', + business_size: 'medium', // TODO: Get from strategy data + force_refresh: false + }; + + console.log('Generating content calendar with request:', calendarRequest); + + // Call the calendar generation API + const calendarResponse = await contentPlanningApi.generateCalendar(calendarRequest); + + console.log('Content calendar generated successfully:', calendarResponse); + + // TODO: Navigate to calendar tab or show success message + // You could also store the calendar data in a global state + + } catch (error) { + console.error('Error generating content calendar:', error); + // Show error message to user + throw new Error('Failed to generate content calendar. Please try again.'); + } + }; + + return { + strategyConfirmed, + showConfirmDialog, + setShowConfirmDialog, + handleConfirmStrategy, + confirmStrategy, + handleGenerateContentCalendar + }; +}; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/hooks/useStrategyData.ts b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/hooks/useStrategyData.ts new file mode 100644 index 00000000..c3e78906 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/hooks/useStrategyData.ts @@ -0,0 +1,178 @@ +import { useState, useEffect } from 'react'; +import { contentPlanningApi } from '../../../../../services/contentPlanningApi'; +import { StrategyData } from '../types/strategy.types'; +import { + getUserId, + transformPollingStrategyData, + transformFullStructureData, + transformSwotToComprehensiveStructure, + hasFullStructure +} from '../utils/strategyTransformers'; + +export const useStrategyData = () => { + const [strategyData, setStrategyData] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const loadStrategyData = async (forceRefresh = false) => { + try { + setLoading(true); + setError(null); + + const userId = getUserId(); + + // First, try to get the latest generated strategy from the polling system + try { + const latestStrategyResponse = await contentPlanningApi.getLatestGeneratedStrategy(userId); + + if (latestStrategyResponse?.strategy) { + console.log('βœ… Found latest generated strategy from polling system:', latestStrategyResponse.strategy); + + const transformedStrategy = transformPollingStrategyData(latestStrategyResponse.strategy); + + console.log('πŸ”„ Transformed strategy data:', transformedStrategy); + setStrategyData(transformedStrategy); + setLoading(false); + return; + } + } catch (pollingError) { + console.log('No latest strategy found in polling system, checking database...', pollingError); + } + + // If no strategy found in polling system, try to get from database + try { + const strategiesResponse = await contentPlanningApi.getEnhancedStrategies(userId); + + // Handle the enhanced strategies response structure + const strategies = strategiesResponse?.data?.strategies || strategiesResponse?.strategies || []; + + if (strategies && strategies.length > 0) { + // Get the most recent strategy (assuming it's sorted by creation date) + const latestStrategy = strategies[0]; + + // Check if this strategy has comprehensive AI-generated data + if (latestStrategy.comprehensive_ai_analysis) { + console.log('βœ… Found comprehensive strategy in database:', latestStrategy); + console.log('πŸ“Š Comprehensive AI analysis structure:', latestStrategy.comprehensive_ai_analysis); + console.log('πŸ” Available fields:', Object.keys(latestStrategy.comprehensive_ai_analysis)); + + // Check if this is the full 5-component structure or SWOT analysis + if (hasFullStructure(latestStrategy.comprehensive_ai_analysis)) { + // Transform the data to match frontend expectations (full 5-component structure) + const transformedStrategy = transformFullStructureData(latestStrategy); + + console.log('πŸ”„ Transformed enhanced strategy data (full structure):', transformedStrategy); + console.log('🎯 Final strategy data structure:', { + hasStrategicInsights: !!transformedStrategy.strategic_insights, + hasCompetitiveAnalysis: !!transformedStrategy.competitive_analysis, + hasPerformancePredictions: !!transformedStrategy.performance_predictions, + hasImplementationRoadmap: !!transformedStrategy.implementation_roadmap, + hasRiskAssessment: !!transformedStrategy.risk_assessment, + hasSummary: !!transformedStrategy.summary + }); + setStrategyData(transformedStrategy); + setLoading(false); + return; + } else { + // This is SWOT analysis, create a comprehensive 5-component structure enhanced with SWOT data + console.log('πŸ”„ Creating comprehensive 5-component structure from SWOT analysis'); + + const transformedStrategy = transformSwotToComprehensiveStructure(latestStrategy); + + console.log('πŸ”„ Created comprehensive 5-component structure from SWOT analysis:', transformedStrategy); + console.log('🎯 Final strategy data structure:', { + hasStrategicInsights: !!transformedStrategy.strategic_insights, + hasCompetitiveAnalysis: !!transformedStrategy.competitive_analysis, + hasPerformancePredictions: !!transformedStrategy.performance_predictions, + hasImplementationRoadmap: !!transformedStrategy.implementation_roadmap, + hasRiskAssessment: !!transformedStrategy.risk_assessment, + hasSummary: !!transformedStrategy.summary, + swotEnhanced: true + }); + setStrategyData(transformedStrategy); + setLoading(false); + return; + } + } else { + console.log('⚠️ Strategy found but no comprehensive_ai_analysis field:', { + strategyId: latestStrategy.id, + strategyName: latestStrategy.name, + availableFields: Object.keys(latestStrategy) + }); + } + } + } catch (dbError) { + console.log('No comprehensive strategies found in database, checking for recent generation...', dbError); + } + + // If no comprehensive strategy found in database, check for recent generation tasks + try { + // Try to get the latest strategy generation result + const recentStrategies = await contentPlanningApi.getStrategies(userId); + + // Handle the enhanced strategies response structure + const strategies = recentStrategies?.data?.strategies || recentStrategies?.strategies || []; + + if (strategies && strategies.length > 0) { + // Find the most recent AI-generated strategy + const aiGeneratedStrategy = strategies.find( + (strategy: any) => strategy.comprehensive_ai_analysis + ); + + if (aiGeneratedStrategy && aiGeneratedStrategy.comprehensive_ai_analysis) { + console.log('βœ… Found AI-generated strategy in recent strategies:', aiGeneratedStrategy); + + // Transform the data to match frontend expectations + const transformedStrategy = transformPollingStrategyData(aiGeneratedStrategy.comprehensive_ai_analysis); + + console.log('πŸ”„ Transformed recent strategy data:', transformedStrategy); + setStrategyData(transformedStrategy); + setLoading(false); + return; + } + } + } catch (strategyError) { + console.log('No recent strategies found, checking for generation tasks...', strategyError); + } + + // If no strategy data is available, show appropriate message + console.log('❌ No comprehensive strategy data found'); + setStrategyData(null); + setError('No comprehensive strategy data available. Please generate a strategy first.'); + setLoading(false); + + } catch (err: any) { + console.error('Error loading strategy data:', err); + setError(err.message || 'Failed to load strategy data'); + setStrategyData(null); + setLoading(false); + } + }; + + // Load data on mount and when component becomes visible + useEffect(() => { + // Always refresh data when component mounts to ensure we get the latest strategy + loadStrategyData(true); + + // Also set up a listener for when the tab becomes visible (for better UX) + const handleVisibilityChange = () => { + if (!document.hidden) { + // Tab became visible, refresh data + loadStrategyData(true); + } + }; + + document.addEventListener('visibilitychange', handleVisibilityChange); + + return () => { + document.removeEventListener('visibilitychange', handleVisibilityChange); + }; + }, []); + + return { + strategyData, + loading, + error, + loadStrategyData + }; +}; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/index.ts b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/index.ts new file mode 100644 index 00000000..b494ec08 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/index.ts @@ -0,0 +1,22 @@ +// Main Strategy Intelligence Tab +export { default as StrategyIntelligenceTab } from '../StrategyIntelligenceTab'; + +// Components +export { default as StrategyHeader } from './components/StrategyHeader'; +export { default as StrategicInsightsCard } from './components/StrategicInsightsCard'; +export { default as CompetitiveAnalysisCard } from './components/CompetitiveAnalysisCard'; +export { default as PerformancePredictionsCard } from './components/PerformancePredictionsCard'; +export { default as ImplementationRoadmapCard } from './components/ImplementationRoadmapCard'; +export { default as RiskAssessmentCard } from './components/RiskAssessmentCard'; +export { default as StrategyActions } from './components/StrategyActions'; +export { default as ConfirmationDialog } from './components/ConfirmationDialog'; + +// Hooks +export { useStrategyData } from './hooks/useStrategyData'; +export { useStrategyActions } from './hooks/useStrategyActions'; + +// Types +export * from './types/strategy.types'; + +// Utils +export * from './utils/strategyTransformers'; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/styles/analysisCardStyles.ts b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/styles/analysisCardStyles.ts new file mode 100644 index 00000000..ac1900fd --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/styles/analysisCardStyles.ts @@ -0,0 +1,377 @@ +import { Theme } from '@mui/material/styles'; + +// Color palette for analysis components +export const ANALYSIS_COLORS = { + primary: '#667eea', + secondary: '#764ba2', + accent: '#f093fb', + success: '#4caf50', + warning: '#ff9800', + error: '#f44336', + info: '#2196f3', + background: { + gradient: 'linear-gradient(135deg, #0f0f23 0%, #1a1a2e 25%, #16213e 50%, #0f3460 75%, #533483 100%)', + dark: 'rgba(0, 0, 0, 0.3)', + glass: 'rgba(255, 255, 255, 0.05)' + }, + text: { + primary: 'white', + secondary: '#e0e0e0', + muted: '#b0b0b0' + }, + border: { + primary: 'rgba(102, 126, 234, 0.3)', + secondary: 'rgba(255, 255, 255, 0.1)' + } +}; + +// Priority colors for chips +export const PRIORITY_COLORS = { + high: '#f44336', + medium: '#ff9800', + low: '#4caf50', + default: '#667eea' +}; + +// Impact colors for chips +export const IMPACT_COLORS = { + high: '#f44336', + medium: '#ff9800', + low: '#4caf50', + default: '#667eea' +}; + +// Main card container styles +export const getAnalysisCardStyles = () => ({ + card: { + height: '100%', + borderRadius: 3, + background: ANALYSIS_COLORS.background.gradient, + color: ANALYSIS_COLORS.text.primary, + boxShadow: '0 20px 60px rgba(0, 0, 0, 0.5), 0 0 40px rgba(102, 126, 234, 0.3)', + border: `1px solid ${ANALYSIS_COLORS.border.primary}`, + position: 'relative' as const, + overflow: 'hidden', + '&::before': { + content: '""', + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + background: 'radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%)', + pointerEvents: 'none' + }, + '&:hover': { + boxShadow: '0 25px 70px rgba(102, 126, 234, 0.4)', + transform: 'translateY(-4px)' + }, + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)' + }, + cardContent: { + p: 2.5, + position: 'relative' as const, + zIndex: 1 + } +}); + +// Header section styles +export const getHeaderStyles = () => ({ + headerContainer: { + display: 'flex', + alignItems: 'center', + mb: 2.5 + }, + iconContainer: { + p: 1, + borderRadius: 2, + background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', + mr: 1.5, + boxShadow: '0 4px 12px rgba(102, 126, 234, 0.3)' + }, + titleContainer: { + flex: 1 + }, + title: { + fontWeight: 700, + background: 'linear-gradient(45deg, #667eea, #764ba2, #f093fb)', + backgroundSize: '200% 200%', + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent', + animation: 'gradient 3s ease infinite', + '@keyframes gradient': { + '0%': { backgroundPosition: '0% 50%' }, + '50%': { backgroundPosition: '100% 50%' }, + '100%': { backgroundPosition: '0% 50%' } + } + } +}); + +// Market analysis header styles +export const getMarketAnalysisHeaderStyles = () => ({ + marketAnalysisContainer: { + display: 'flex', + alignItems: 'center', + gap: 2, + p: 1.5, + background: ANALYSIS_COLORS.background.dark, + borderRadius: 2, + border: `1px solid ${ANALYSIS_COLORS.border.secondary}`, + backdropFilter: 'blur(10px)' + }, + circularProgress: { + color: ANALYSIS_COLORS.primary, + '& .MuiCircularProgress-circle': { + strokeLinecap: 'round', + filter: 'drop-shadow(0 2px 4px rgba(102, 126, 234, 0.3))' + } + }, + progressText: { + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + fontWeight: 700, + color: ANALYSIS_COLORS.primary, + fontSize: '0.7rem' + }, + positionText: { + fontWeight: 600, + color: ANALYSIS_COLORS.text.primary, + display: 'block', + fontSize: '0.7rem' + }, + positionLabel: { + color: ANALYSIS_COLORS.text.secondary, + fontSize: '0.6rem' + }, + marketChip: { + background: 'rgba(0, 0, 0, 0.4)', + color: ANALYSIS_COLORS.primary, + fontWeight: 600, + fontSize: '0.6rem', + height: 20, + border: `1px solid rgba(102, 126, 234, 0.4)`, + '& .MuiChip-label': { + px: 1 + } + } +}); + +// Section container styles +export const getSectionStyles = () => ({ + sectionContainer: { + mb: 2.5, + p: 2, + background: ANALYSIS_COLORS.background.dark, + borderRadius: 2, + border: `1px solid ${ANALYSIS_COLORS.border.secondary}`, + backdropFilter: 'blur(10px)' + }, + sectionTitle: { + fontWeight: 600, + color: ANALYSIS_COLORS.text.primary, + fontSize: '0.85rem' + } +}); + +// Accordion styles +export const getAccordionStyles = () => ({ + accordion: { + mb: 1, + '&:before': { display: 'none' }, + background: ANALYSIS_COLORS.background.dark, + borderRadius: 2, + border: `1px solid ${ANALYSIS_COLORS.border.secondary}`, + backdropFilter: 'blur(10px)', + '&.Mui-expanded': { + margin: '8px 0' + } + }, + accordionSummary: { + '& .MuiAccordionSummary-content': { + margin: '8px 0' + } + }, + accordionTitle: { + fontWeight: 600, + color: ANALYSIS_COLORS.text.primary, + fontSize: '0.8rem' + }, + accordionSubtitle: { + color: ANALYSIS_COLORS.text.secondary + }, + expandIcon: { + color: ANALYSIS_COLORS.primary + } +}); + +// Enhanced chip styles +export const getEnhancedChipStyles = (color: string) => ({ + chip: { + background: `linear-gradient(135deg, ${color}70 0%, ${color}60 100%)`, + color: color, + fontWeight: 700, + fontSize: '0.75rem', + height: 30, + border: `3px solid ${color}80`, + boxShadow: `0 8px 20px ${color}50, inset 0 3px 0 rgba(255, 255, 255, 0.2), inset 0 -2px 0 rgba(0, 0, 0, 0.4), 0 0 15px ${color}30`, + backdropFilter: 'blur(20px)', + transition: 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)', + textShadow: '0 2px 4px rgba(0, 0, 0, 0.5)', + position: 'relative' as const, + '&::before': { + content: '""', + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + background: `linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(0, 0, 0, 0.1) 100%)`, + borderRadius: 'inherit', + pointerEvents: 'none' + }, + '&:hover': { + transform: 'translateY(-4px) scale(1.05)', + boxShadow: `0 12px 30px ${color}60, inset 0 3px 0 rgba(255, 255, 255, 0.3), inset 0 -2px 0 rgba(0, 0, 0, 0.5), 0 0 25px ${color}40`, + background: `linear-gradient(135deg, ${color}80 0%, ${color}70 100%)` + }, + '& .MuiChip-label': { + px: 2.5, + py: 1, + fontWeight: 700, + letterSpacing: '0.6px', + position: 'relative' as const, + zIndex: 1 + }, + '& .MuiChip-icon': { + color: color, + fontSize: '1.1rem', + filter: 'drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5))', + position: 'relative' as const, + zIndex: 1 + } + } +}); + +// List item styles +export const getListItemStyles = () => ({ + listItem: { + px: 0, + py: 1 + }, + listItemIcon: { + minWidth: 32 + }, + bulletPoint: { + width: 8, + height: 8, + borderRadius: '50%', + background: ANALYSIS_COLORS.primary, + opacity: 0.7 + }, + insightText: { + fontWeight: 500, + mb: 1, + color: ANALYSIS_COLORS.text.primary, + lineHeight: 1.5 + }, + reasoningText: { + color: ANALYSIS_COLORS.text.muted, + fontStyle: 'italic', + lineHeight: 1.4 + } +}); + +// Fallback/empty state styles +export const getFallbackStyles = () => ({ + fallbackContainer: { + textAlign: 'center', + py: 4 + }, + fallbackText: { + mb: 2, + color: ANALYSIS_COLORS.text.secondary + }, + fallbackCaption: { + color: ANALYSIS_COLORS.text.muted + } +}); + +// Animation styles +export const getAnimationStyles = () => ({ + iconAnimation: { + animate: { rotate: [0, 10, -10, 0] }, + transition: { duration: 2, repeat: Infinity, ease: "easeInOut" as const } + }, + cardAnimation: { + initial: { opacity: 0, y: 20 }, + animate: { opacity: 1, y: 0 }, + transition: { duration: 0.5 }, + whileHover: { y: -4 } + } +}); + +// Utility functions +export const getPriorityColor = (priority: string): string => { + switch (priority.toLowerCase()) { + case 'high': + return PRIORITY_COLORS.high; + case 'medium': + return PRIORITY_COLORS.medium; + case 'low': + return PRIORITY_COLORS.low; + default: + return PRIORITY_COLORS.default; + } +}; + +export const getImpactColor = (impact: string): string => { + switch (impact.toLowerCase()) { + case 'high': + return IMPACT_COLORS.high; + case 'medium': + return IMPACT_COLORS.medium; + case 'low': + return IMPACT_COLORS.low; + default: + return IMPACT_COLORS.default; + } +}; + +// Icon mapping for different insight types +export const getInsightIcon = (type: string) => { + switch (type.toLowerCase()) { + case 'market positioning': + return { icon: 'Business', color: ANALYSIS_COLORS.primary }; + case 'content opportunity': + return { icon: 'Lightbulb', color: ANALYSIS_COLORS.success }; + case 'growth potential': + return { icon: 'TrendingUp', color: ANALYSIS_COLORS.info }; + case 'competitive advantage': + return { icon: 'Star', color: ANALYSIS_COLORS.warning }; + case 'strategic recommendation': + return { icon: 'Psychology', color: ANALYSIS_COLORS.error }; + default: + return { icon: 'Lightbulb', color: ANALYSIS_COLORS.primary }; + } +}; + +// Complete style object for easy import +export const ANALYSIS_CARD_STYLES = { + colors: ANALYSIS_COLORS, + priorityColors: PRIORITY_COLORS, + impactColors: IMPACT_COLORS, + getAnalysisCardStyles, + getHeaderStyles, + getMarketAnalysisHeaderStyles, + getSectionStyles, + getAccordionStyles, + getEnhancedChipStyles, + getListItemStyles, + getFallbackStyles, + getAnimationStyles, + getPriorityColor, + getImpactColor, + getInsightIcon +}; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/styles/index.ts b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/styles/index.ts new file mode 100644 index 00000000..1c896c62 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/styles/index.ts @@ -0,0 +1 @@ +export * from './analysisCardStyles'; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/types/strategy.types.ts b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/types/strategy.types.ts new file mode 100644 index 00000000..66e32502 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/types/strategy.types.ts @@ -0,0 +1,267 @@ +export interface StrategyMetadata { + generated_at?: string; + generation_timestamp?: string; + user_id: number; + strategy_name: string; + generation_version?: string; + ai_model?: string; + personalization_level?: string; + ai_generated: boolean; + comprehensive: boolean; + content_calendar_ready: boolean; +} + +export interface StrategicInsights { + market_positioning?: { + positioning_strength: number; + current_position: string; + swot_analysis?: { + strengths: string[]; + opportunities: string[]; + }; + }; + content_opportunities?: string[]; + growth_potential?: { + market_size: string; + growth_rate: string; + key_drivers?: string[]; + competitive_advantages?: string[]; + }; + swot_summary?: { + overall_score: number; + primary_strengths: string[]; + key_opportunities: string[]; + }; + // Backend insights array structure + insights?: Array<{ + type: string; + insight: string; + confidence_level: string; + estimated_impact: string; + implementation_time: string; + priority: string; + reasoning: string; + }>; +} + +export interface CompetitiveAnalysis { + competitors?: Array<{ + name: string; + market_position: string; + strengths: string[]; + weaknesses: string[]; + content_strategy?: string; // Added to match backend + competitive_response?: string; + }>; + market_gaps?: string[]; + opportunities?: string[]; // Added to match backend + recommendations?: string[]; // Added to match backend + competitive_advantages?: { + primary: string[]; + sustainable: string[]; + development_areas: string[]; + }; + swot_competitive_insights?: { + leverage_strengths: string[]; + address_weaknesses: string[]; + capitalize_opportunities: string[]; + mitigate_threats: string[]; + }; +} + +export interface PerformancePredictions { + estimated_roi?: string; + key_metrics?: { + engagement_rate?: string; + conversion_rate?: string; + reach_growth?: string; + brand_awareness?: string; + market_share?: string; + }; + timeline_projections?: { + [key: string]: string; + }; + success_factors?: { + primary: string[]; + secondary: string[]; + risk_mitigation: string[]; + }; + swot_based_predictions?: { + strength_impact: string; + opportunity_impact: string; + weakness_mitigation: string; + threat_management: string; + }; + // Nested prediction objects from backend + traffic_predictions?: { + monthly_traffic?: string; + growth_rate?: string; + traffic_growth?: string; + month_1?: string; + }; + engagement_predictions?: { + engagement_rate?: string; + brand_awareness?: string; + time_on_page?: string; + month_3?: string; + success_factors?: string[]; + }; + conversion_predictions?: { + conversion_rate?: string; + lead_generation?: string; + month_6?: string; + risk_mitigation?: string[]; + }; + roi_predictions?: { + estimated_roi?: string; + market_share?: string; + cost_benefit?: string; + success_factors?: string[]; + }; +} + +export interface ImplementationRoadmap { + total_duration?: string; + phases?: Array<{ + phase: string; + duration: string; + tasks: string[]; // Changed from activities to match backend + milestones: string[]; // Changed from deliverables to match backend + resources: string[]; // Added to match backend + swot_focus?: string; + }>; + resource_allocation?: { + team_members?: string[]; // Changed from team_requirements to match backend + team_requirements?: string[]; // Added to match backend data + budget_allocation?: { + total_budget?: string; + content_creation?: string; + technology_tools?: string; + marketing_promotion?: string; + external_resources?: string; + }; + swot_priorities?: { + high_priority: string[]; + medium_priority: string[]; + low_priority: string[]; + }; + }; + success_metrics?: string[]; // Added to match backend + timeline?: { + start_date?: string; + end_date?: string; + key_milestones?: string[]; + }; // Added to match backend + swot_integration?: { + strength_leverage: string[]; + weakness_improvement: string[]; + opportunity_capitalization: string[]; + threat_mitigation: string[]; + }; +} + +export interface RiskAssessment { + overall_risk_level?: string; + risks?: Array<{ + risk: string; + probability: string; + impact: string; + mitigation?: string; + contingency?: string; + }>; + risk_categories?: { + market_risks?: Array<{ + risk: string; + probability: string; + impact: string; + mitigation?: string; + }>; + operational_risks?: Array<{ + risk: string; + probability: string; + impact: string; + mitigation?: string; + }>; + competitive_risks?: Array<{ + risk: string; + probability: string; + impact: string; + mitigation?: string; + }>; + technical_risks?: Array<{ + risk: string; + probability: string; + impact: string; + mitigation?: string; + }>; + financial_risks?: Array<{ + risk: string; + probability: string; + impact: string; + mitigation?: string; + }>; + }; + monitoring_framework?: { + escalation_procedures?: string[]; + key_indicators?: string[]; + monitoring_frequency?: string; + review_schedule?: string; + }; + swot_risk_mapping?: { + strength_risks: string; + weakness_risks: string; + opportunity_risks: string; + threat_risks: string; + }; + risk_mitigation_strategies?: { + based_on_strengths: string; + based_on_opportunities: string; + based_on_weaknesses: string; + based_on_threats: string; + }; + mitigation_strategies?: string[]; +} + +export interface StrategySummary { + estimated_roi: string; + implementation_timeline: string; + risk_level: string; + success_probability: string; + next_step: string; + swot_highlights?: { + key_strengths: string[]; + key_opportunities: string[]; + primary_risks: string[]; + }; +} + +export interface StrategyData { + strategy_metadata?: StrategyMetadata; + metadata?: StrategyMetadata; + base_strategy?: any; + strategic_insights?: StrategicInsights; + competitive_analysis?: CompetitiveAnalysis; + performance_predictions?: PerformancePredictions; + implementation_roadmap?: ImplementationRoadmap; + risk_assessment?: RiskAssessment; + summary?: StrategySummary; +} + +export interface StrategyActionsProps { + strategyData: StrategyData | null; + strategyConfirmed: boolean; + onConfirmStrategy: () => void; + onGenerateContentCalendar: () => void; + onRefreshData: () => void; +} + +export interface StrategyCardProps { + strategyData: StrategyData | null; + loading?: boolean; +} + +export interface ConfirmationDialogProps { + open: boolean; + onClose: () => void; + onConfirm: () => void; +} \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/utils/strategyTransformers.ts b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/utils/strategyTransformers.ts new file mode 100644 index 00000000..e6b56889 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/utils/strategyTransformers.ts @@ -0,0 +1,473 @@ +import { StrategyData } from '../types/strategy.types'; + +// Helper function to get user ID from context or store +export const getUserId = (): number => { + // TODO: Replace with actual user context/store + // For now, return default user ID + return 1; +}; + +/** + * Transform polling system strategy data to frontend format + */ +export const transformPollingStrategyData = (strategyData: any): StrategyData => { + return { + ...strategyData, + strategy_metadata: strategyData.metadata || strategyData.strategy_metadata, + // Add summary if not present + summary: strategyData.summary || { + estimated_roi: strategyData.performance_predictions?.estimated_roi || "15-25%", + implementation_timeline: strategyData.implementation_roadmap?.total_duration || "12 months", + risk_level: strategyData.risk_assessment?.overall_risk_level || "Medium", + success_probability: strategyData.performance_predictions?.success_probability || "85%", + next_step: "Review strategy and generate content calendar" + } + }; +}; + +/** + * Transform full 5-component structure from database + */ +export const transformFullStructureData = (latestStrategy: any): StrategyData => { + const comprehensiveData = latestStrategy.comprehensive_ai_analysis; + + return { + // Map metadata + strategy_metadata: comprehensiveData.metadata || comprehensiveData.strategy_metadata, + metadata: comprehensiveData.metadata || comprehensiveData.strategy_metadata, + + // Transform Strategic Insights + strategic_insights: comprehensiveData.strategic_insights ? { + market_positioning: { + positioning_strength: 75, // Default value + current_position: "Emerging", + swot_analysis: { + strengths: [], + opportunities: [] + } + }, + content_opportunities: comprehensiveData.strategic_insights.insights?.filter((insight: any) => + insight.type === 'Content Opportunity' + ).map((insight: any) => insight.insight) || [], + growth_potential: { + market_size: "Growing", + growth_rate: "High", + key_drivers: comprehensiveData.strategic_insights.insights?.filter((insight: any) => + insight.type === 'Growth Potential' + ).map((insight: any) => insight.insight) || [], + competitive_advantages: [] + }, + swot_summary: { + overall_score: 75, + primary_strengths: comprehensiveData.strategic_insights.insights?.slice(0, 2) || [], + key_opportunities: comprehensiveData.strategic_insights.insights?.slice(2, 4) || [] + }, + // Map the insights array directly + insights: comprehensiveData.strategic_insights.insights || [] + } : undefined, + + // Transform Competitive Analysis + competitive_analysis: comprehensiveData.competitive_analysis ? { + competitors: comprehensiveData.competitive_analysis.competitors || [], + market_gaps: comprehensiveData.competitive_analysis.market_gaps || [], + opportunities: comprehensiveData.competitive_analysis.opportunities || [], + recommendations: comprehensiveData.competitive_analysis.recommendations || [], + competitive_advantages: { + primary: comprehensiveData.competitive_analysis.recommendations?.slice(0, 3) || [], + sustainable: comprehensiveData.competitive_analysis.recommendations?.slice(3, 5) || [], + development_areas: comprehensiveData.competitive_analysis.opportunities || [] + }, + swot_competitive_insights: { + leverage_strengths: comprehensiveData.competitive_analysis.recommendations?.slice(0, 2) || [], + address_weaknesses: comprehensiveData.competitive_analysis.recommendations?.slice(2, 4) || [], + capitalize_opportunities: comprehensiveData.competitive_analysis.opportunities?.slice(0, 2) || [], + mitigate_threats: comprehensiveData.competitive_analysis.recommendations?.slice(4, 6) || [] + } + } : undefined, + + // Transform Performance Predictions + performance_predictions: comprehensiveData.performance_predictions ? { + estimated_roi: comprehensiveData.performance_predictions.roi_predictions?.estimated_roi || "15-25%", + key_metrics: { + engagement_rate: comprehensiveData.performance_predictions.engagement_predictions?.engagement_rate || "8-12%", + conversion_rate: comprehensiveData.performance_predictions.conversion_predictions?.conversion_rate || "3-5%", + reach_growth: comprehensiveData.performance_predictions.traffic_predictions?.traffic_growth || "40-60%", + brand_awareness: comprehensiveData.performance_predictions.engagement_predictions?.brand_awareness || "25-35%", + market_share: comprehensiveData.performance_predictions.roi_predictions?.market_share || "5-8%" + }, + timeline_projections: { + "month_1": comprehensiveData.performance_predictions.traffic_predictions?.month_1 || "Initial setup and content creation", + "month_3": comprehensiveData.performance_predictions.engagement_predictions?.month_3 || "Content optimization and audience growth", + "month_6": comprehensiveData.performance_predictions.conversion_predictions?.month_6 || "Full strategy implementation and scaling" + }, + success_factors: { + primary: comprehensiveData.performance_predictions.roi_predictions?.success_factors?.slice(0, 3) || [], + secondary: comprehensiveData.performance_predictions.engagement_predictions?.success_factors?.slice(0, 2) || [], + risk_mitigation: comprehensiveData.performance_predictions.conversion_predictions?.risk_mitigation?.slice(0, 2) || [] + }, + swot_based_predictions: { + strength_impact: "High positive impact from identified strengths", + opportunity_impact: "Significant growth potential from market opportunities", + weakness_mitigation: "Addressing weaknesses through strategic content planning", + threat_management: "Proactive threat management through diversified approach" + } + } : undefined, + + // Transform Implementation Roadmap + implementation_roadmap: comprehensiveData.implementation_roadmap ? { + total_duration: comprehensiveData.implementation_roadmap.total_duration || "6 months", + phases: comprehensiveData.implementation_roadmap.phases || [], + success_metrics: comprehensiveData.implementation_roadmap.success_metrics || [], + timeline: comprehensiveData.implementation_roadmap.timeline || { + start_date: "2024-09-01", + end_date: "2025-02-28", + key_milestones: [] + }, + resource_allocation: { + team_members: comprehensiveData.implementation_roadmap.resource_allocation?.team_members || + comprehensiveData.implementation_roadmap.resource_allocation?.team_requirements || + ["Content Strategist", "SEO Specialist", "Content Writer", "Editor"], + team_requirements: comprehensiveData.implementation_roadmap.resource_allocation?.team_requirements || + comprehensiveData.implementation_roadmap.resource_allocation?.team_members || + ["Content Strategist", "SEO Specialist", "Content Writer", "Editor"], + budget_allocation: comprehensiveData.implementation_roadmap.resource_allocation?.budget_allocation || { + total_budget: "$60,000", + content_creation: "$30,000", + technology_tools: "$5,000", + marketing_promotion: "$20,000", + external_resources: "$5,000" + }, + swot_priorities: { + high_priority: comprehensiveData.implementation_roadmap.success_metrics?.slice(0, 3) || [], + medium_priority: comprehensiveData.implementation_roadmap.success_metrics?.slice(3, 6) || [], + low_priority: comprehensiveData.implementation_roadmap.success_metrics?.slice(6, 9) || [] + } + } + } : undefined, + + // Transform Risk Assessment + risk_assessment: comprehensiveData.risk_assessment ? { + overall_risk_level: comprehensiveData.risk_assessment.overall_risk_level || "Medium", + risks: comprehensiveData.risk_assessment.risks || [], + risk_categories: comprehensiveData.risk_assessment.risk_categories || {}, + monitoring_framework: comprehensiveData.risk_assessment.monitoring_framework || { + escalation_procedures: [], + key_indicators: [], + monitoring_frequency: "Weekly for key performance metrics, Monthly for strategic content review, Quarterly for comprehensive analysis", + review_schedule: "Monthly performance review meetings to discuss analytics and tactical adjustments. Quarterly strategic comprehensive review" + }, + swot_risk_mapping: { + strength_risks: "Leverage strengths to mitigate risks", + weakness_risks: "Address weaknesses through strategic planning", + opportunity_risks: "Capitalize on opportunities while managing risks", + threat_risks: "Proactive threat management and contingency planning" + }, + risk_mitigation_strategies: { + based_on_strengths: comprehensiveData.risk_assessment.mitigation_strategies?.[0] || "Leverage identified strengths", + based_on_opportunities: comprehensiveData.risk_assessment.mitigation_strategies?.[1] || "Capitalize on market opportunities", + based_on_weaknesses: comprehensiveData.risk_assessment.mitigation_strategies?.[2] || "Address identified weaknesses", + based_on_threats: comprehensiveData.risk_assessment.mitigation_strategies?.[3] || "Proactive threat management" + }, + mitigation_strategies: comprehensiveData.risk_assessment.mitigation_strategies || [] + } : undefined, + + // Add summary + summary: comprehensiveData.summary || { + estimated_roi: comprehensiveData.performance_predictions?.roi_predictions?.estimated_roi || "15-25%", + implementation_timeline: comprehensiveData.implementation_roadmap?.total_duration || "6 months", + risk_level: comprehensiveData.risk_assessment?.overall_risk_level || "Medium", + success_probability: "85%", + next_step: "Review strategy and generate content calendar" + } + }; +}; + +/** + * Transform SWOT analysis into comprehensive 5-component structure + */ +export const transformSwotToComprehensiveStructure = (latestStrategy: any): StrategyData => { + const swotData = latestStrategy.comprehensive_ai_analysis; + + return { + strategy_metadata: { + user_id: latestStrategy.user_id, + strategy_name: latestStrategy.name, + ai_generated: true, + comprehensive: true, + content_calendar_ready: false, + generation_timestamp: latestStrategy.created_at + }, + // Enhanced Strategic Insights with SWOT data + strategic_insights: { + market_positioning: { + positioning_strength: swotData.overall_score || 75, + current_position: "Emerging", + swot_analysis: { + strengths: swotData.strengths || [], + opportunities: swotData.opportunities || [] + } + }, + content_opportunities: [ + ...(swotData.opportunities || []), + "Leverage identified market gaps", + "Focus on unique value propositions", + "Build thought leadership content" + ], + growth_potential: { + market_size: "Growing", + growth_rate: "High", + key_drivers: swotData.opportunities || [], + competitive_advantages: swotData.strengths || [] + }, + swot_summary: { + overall_score: swotData.overall_score || 75, + primary_strengths: (swotData.strengths || []).slice(0, 3), + key_opportunities: (swotData.opportunities || []).slice(0, 3) + } + }, + // Enhanced Competitive Analysis with SWOT data + competitive_analysis: { + competitors: [ + { + name: "Direct Competitors", + market_position: "Established", + strengths: swotData.strengths || [], + weaknesses: swotData.weaknesses || [], + competitive_response: "Focus on differentiation" + }, + { + name: "Emerging Competitors", + market_position: "Growing", + strengths: [], + weaknesses: swotData.weaknesses || [], + competitive_response: "Establish market leadership" + } + ], + market_gaps: [ + ...(swotData.opportunities || []), + "Content personalization opportunities", + "Niche market segments", + "Innovation in content delivery" + ], + competitive_advantages: { + primary: swotData.strengths || [], + sustainable: swotData.opportunities || [], + development_areas: swotData.weaknesses || [] + }, + swot_competitive_insights: { + leverage_strengths: swotData.strengths || [], + address_weaknesses: swotData.weaknesses || [], + capitalize_opportunities: swotData.opportunities || [], + mitigate_threats: swotData.threats || [] + } + }, + // Enhanced Performance Predictions with SWOT context + performance_predictions: { + estimated_roi: "20-30%", + key_metrics: { + engagement_rate: "5-8%", + conversion_rate: "2-4%", + reach_growth: "40-60%", + brand_awareness: "25-35%", + market_share: "3-5%" + }, + timeline_projections: { + "3_months": "Initial traction and brand awareness leveraging identified strengths", + "6_months": "Established presence and engagement addressing market opportunities", + "12_months": "Market leadership and growth capitalizing on competitive advantages" + }, + success_factors: { + primary: swotData.strengths || [], + secondary: swotData.opportunities || [], + risk_mitigation: swotData.threats || [] + }, + swot_based_predictions: { + strength_impact: "High performance in areas of identified strengths", + opportunity_impact: "Growth potential through market opportunities", + weakness_mitigation: "Improved performance by addressing weaknesses", + threat_management: "Risk-adjusted projections considering market threats" + } + }, + // Enhanced Implementation Roadmap with SWOT considerations + implementation_roadmap: { + total_duration: "12 months", + phases: [ + { + phase: "Foundation (Months 1-3)", + duration: "3 months", + tasks: [ + "Brand positioning leveraging identified strengths", + "Content strategy development addressing market opportunities", + "Weakness assessment and improvement planning" + ], + milestones: ["Brand guidelines", "Content calendar", "SWOT action plan"], + resources: ["Content Strategist", "Brand Manager", "SWOT Analyst"], + swot_focus: "Strengths and Opportunities" + }, + { + phase: "Growth (Months 4-8)", + duration: "5 months", + tasks: [ + "Content execution based on competitive advantages", + "Community building addressing market gaps", + "Threat mitigation strategies implementation" + ], + milestones: ["Content library", "Engaged audience", "Risk management framework"], + resources: ["Content Writer", "Community Manager", "Risk Analyst"], + swot_focus: "Opportunities and Threats" + }, + { + phase: "Scale (Months 9-12)", + duration: "4 months", + tasks: [ + "Market expansion capitalizing on strengths", + "Performance optimization addressing weaknesses", + "Sustainable competitive advantage development" + ], + milestones: ["Market leadership", "Optimized strategy", "Long-term competitive position"], + resources: ["Growth Manager", "Performance Analyst", "Strategy Consultant"], + swot_focus: "Strengths and Weaknesses" + } + ], + resource_allocation: { + team_members: ["Content Strategist", "SEO Specialist", "Content Writer", "Editor", "Marketing Manager"], + budget_allocation: { + total_budget: "$60,000", + content_creation: "$30,000", + technology_tools: "$5,000", + marketing_promotion: "$20,000", + external_resources: "$5,000" + }, + swot_priorities: { + high_priority: swotData.opportunities || [], + medium_priority: swotData.strengths || [], + low_priority: swotData.weaknesses || [] + } + }, + swot_integration: { + strength_leverage: swotData.strengths || [], + weakness_improvement: swotData.weaknesses || [], + opportunity_capitalization: swotData.opportunities || [], + threat_mitigation: swotData.threats || [] + } + }, + // Enhanced Risk Assessment with SWOT threats + risk_assessment: { + overall_risk_level: "Medium", + risks: [ + ...(swotData.threats?.map((threat: string) => ({ + risk: threat, + probability: "Medium", + impact: "High", + mitigation: "Strategic planning and monitoring" + })) || []), + { + risk: "Market saturation", + probability: "Medium", + impact: "Medium", + mitigation: "Innovation and differentiation" + } + ], + risk_categories: { + market_risks: [ + ...(swotData.threats?.map((threat: string) => ({ + risk: threat, + probability: "Medium", + impact: "High", + mitigation: "Strategic planning and monitoring" + })) || []), + { + risk: "Market saturation", + probability: "Medium", + impact: "Medium", + mitigation: "Innovation and differentiation" + } + ], + operational_risks: [ + { + risk: "Resource constraints", + probability: "Medium", + impact: "Medium", + mitigation: "Efficient resource allocation" + }, + { + risk: "Weakness areas", + probability: "High", + impact: "Medium", + mitigation: "Targeted improvement programs" + } + ], + competitive_risks: [ + { + risk: "Market competition", + probability: "High", + impact: "Medium", + mitigation: "Leverage competitive advantages" + }, + { + risk: "Strength erosion", + probability: "Medium", + impact: "High", + mitigation: "Continuous improvement and innovation" + } + ] + }, + swot_risk_mapping: { + strength_risks: "Risk of over-reliance on current strengths", + weakness_risks: "Risk of weakness exploitation by competitors", + opportunity_risks: "Risk of missing market opportunities", + threat_risks: "Risk of threat materialization" + }, + risk_mitigation_strategies: { + based_on_strengths: "Leverage strengths to mitigate threats", + based_on_opportunities: "Use opportunities to address weaknesses", + based_on_weaknesses: "Develop improvement plans for weak areas", + based_on_threats: "Create contingency plans for identified threats" + }, + mitigation_strategies: swotData.mitigation_strategies || [] + }, + // Enhanced summary with SWOT context + summary: { + estimated_roi: "20-30%", + implementation_timeline: "12 months", + risk_level: "Medium", + success_probability: `${swotData.overall_score || 75}%`, + next_step: "Review strategy and generate content calendar", + swot_highlights: { + key_strengths: (swotData.strengths || []).slice(0, 2), + key_opportunities: (swotData.opportunities || []).slice(0, 2), + primary_risks: (swotData.threats || []).slice(0, 2) + } + } + }; +}; + +/** + * Check if strategy data has full 5-component structure + */ +export const hasFullStructure = (comprehensiveAnalysis: any): boolean => { + return !!(comprehensiveAnalysis.strategic_insights || + comprehensiveAnalysis.competitive_analysis || + comprehensiveAnalysis.performance_predictions); +}; + +/** + * Get strategy name from metadata + */ +export const getStrategyName = (strategyData: StrategyData | null): string => { + return strategyData?.strategy_metadata?.strategy_name || + strategyData?.metadata?.strategy_name || + 'AI-Generated Strategy'; +}; + +/** + * Get strategy generation date + */ +export const getStrategyGenerationDate = (strategyData: StrategyData | null): string => { + const timestamp = strategyData?.strategy_metadata?.generated_at || + strategyData?.strategy_metadata?.generation_timestamp || + strategyData?.metadata?.generated_at || + strategyData?.metadata?.generation_timestamp || ''; + + return new Date(timestamp).toLocaleDateString(); +}; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligenceTab.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligenceTab.tsx new file mode 100644 index 00000000..fefc7793 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligenceTab.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { Box, CircularProgress, Alert, Typography, Grid } from '@mui/material'; +import { useStrategyData } from './StrategyIntelligence/hooks/useStrategyData'; +import { useStrategyActions } from './StrategyIntelligence/hooks/useStrategyActions'; +import StrategyHeader from './StrategyIntelligence/components/StrategyHeader'; +import StrategicInsightsCard from './StrategyIntelligence/components/StrategicInsightsCard'; +import CompetitiveAnalysisCard from './StrategyIntelligence/components/CompetitiveAnalysisCard'; +import PerformancePredictionsCard from './StrategyIntelligence/components/PerformancePredictionsCard'; +import ImplementationRoadmapCard from './StrategyIntelligence/components/ImplementationRoadmapCard'; +import RiskAssessmentCard from './StrategyIntelligence/components/RiskAssessmentCard'; +import StrategyActions from './StrategyIntelligence/components/StrategyActions'; +import ConfirmationDialog from './StrategyIntelligence/components/ConfirmationDialog'; + +const StrategyIntelligenceTab: React.FC = () => { + const { strategyData, loading, error, loadStrategyData } = useStrategyData(); + const { + strategyConfirmed, + showConfirmDialog, + setShowConfirmDialog, + handleConfirmStrategy, + confirmStrategy, + handleGenerateContentCalendar + } = useStrategyActions(); + + const handleConfirmStrategyClick = () => { + handleConfirmStrategy(); + }; + + const handleConfirmStrategyAction = async () => { + await confirmStrategy(strategyData); + }; + + const handleGenerateContentCalendarAction = async () => { + try { + await handleGenerateContentCalendar(strategyData); + } catch (error) { + console.error('Error generating content calendar:', error); + } + }; + + if (loading) { + return ( + + + + ); + } + + if (error) { + return ( + + {error} + + ); + } + + if (!strategyData) { + return ( + + + No Strategy Data Available + + + Generate a comprehensive strategy first to view strategic intelligence. + + + ); + } + + return ( + + {/* Header Section */} + + + {/* Strategy Components Grid */} + + + + + + + + + {/* Action Buttons */} + + + {/* Confirmation Dialog */} + setShowConfirmDialog(false)} + onConfirm={handleConfirmStrategyAction} + /> + + ); +}; + +export default StrategyIntelligenceTab; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligenceTab.tsx.backup b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligenceTab.tsx.backup new file mode 100644 index 00000000..61f126c0 --- /dev/null +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligenceTab.tsx.backup @@ -0,0 +1,1578 @@ +import React, { useState, useEffect } from 'react'; +import { + Box, + Grid, + Paper, + Typography, + Button, + Card, + CardContent, + CardActions, + Chip, + Divider, + Alert, + List, + ListItem, + ListItemText, + ListItemIcon, + CircularProgress, + Accordion, + AccordionSummary, + AccordionDetails, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + IconButton, + Tooltip, + Badge, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + LinearProgress, + Stepper, + Step, + StepLabel, + StepContent +} from '@mui/material'; +import { + TrendingUp as TrendingUpIcon, + Business as BusinessIcon, + Lightbulb as LightbulbIcon, + CheckCircle as CheckCircleIcon, + Warning as WarningIcon, + Search as SearchIcon, + Analytics as AnalyticsIcon, + Timeline as TimelineIcon, + Assessment as AssessmentIcon, + ExpandMore as ExpandMoreIcon, + Refresh as RefreshIcon, + Add as AddIcon, + Edit as EditIcon, + Visibility as VisibilityIcon, + BarChart as BarChartIcon, + PieChart as PieChartIcon, + ShowChart as ShowChartIcon, + AutoAwesome as AutoAwesomeIcon, + Psychology as PsychologyIcon, + Security as SecurityIcon, + Schedule as ScheduleIcon, + ThumbUp as ThumbUpIcon, + ThumbDown as ThumbDownIcon, + Save as SaveIcon, + PlayArrow as PlayArrowIcon, + CalendarToday as CalendarIcon, + Info as InfoIcon, + Star as StarIcon, + TrendingDown as TrendingDownIcon, + Speed as SpeedIcon, + MyLocation as TargetIcon, + Flag as FlagIcon, + Check as CheckIcon, + Close as CloseIcon +} from '@mui/icons-material'; +import { motion, AnimatePresence } from 'framer-motion'; +import { contentPlanningApi } from '../../../services/contentPlanningApi'; + +// Helper function to get user ID from context or store +const getUserId = (): number => { + // TODO: Replace with actual user context/store + // For now, return default user ID + return 1; +}; + +interface StrategyData { + strategy_metadata?: { + generated_at?: string; + generation_timestamp?: string; + user_id: number; + strategy_name: string; + generation_version?: string; + ai_model?: string; + personalization_level?: string; + ai_generated: boolean; + comprehensive: boolean; + content_calendar_ready: boolean; + }; + metadata?: { + generated_at?: string; + generation_timestamp?: string; + user_id: number; + strategy_name: string; + generation_version?: string; + ai_model?: string; + personalization_level?: string; + ai_generated: boolean; + comprehensive: boolean; + content_calendar_ready: boolean; + }; + base_strategy?: any; + strategic_insights?: any; + competitive_analysis?: any; + performance_predictions?: any; + implementation_roadmap?: any; + risk_assessment?: any; + summary?: { + estimated_roi: string; + implementation_timeline: string; + risk_level: string; + success_probability: string; + next_step: string; + }; +} + +const StrategyIntelligenceTab: React.FC = () => { + const [strategyData, setStrategyData] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [activeStep, setActiveStep] = useState(0); + const [showConfirmDialog, setShowConfirmDialog] = useState(false); + const [strategyConfirmed, setStrategyConfirmed] = useState(false); + + // Load the most recent strategy data + useEffect(() => { + loadStrategyData(); + }, []); + + const loadStrategyData = async () => { + try { + setLoading(true); + setError(null); + + // Get the latest generated strategy from the backend + const userId = getUserId(); + + // First, try to get the latest generated strategy from the polling system + try { + const latestStrategyResponse = await contentPlanningApi.getLatestGeneratedStrategy(userId); + + if (latestStrategyResponse?.data?.strategy) { + console.log('βœ… Found latest generated strategy from polling system:', latestStrategyResponse.data.strategy); + + // Transform the data structure to match frontend expectations + const strategyData = latestStrategyResponse.data.strategy; + + // Map metadata to strategy_metadata for frontend compatibility + const transformedStrategy = { + ...strategyData, + strategy_metadata: strategyData.metadata || strategyData.strategy_metadata, + // Add summary if not present + summary: strategyData.summary || { + estimated_roi: strategyData.performance_predictions?.estimated_roi || "15-25%", + implementation_timeline: strategyData.implementation_roadmap?.total_duration || "12 months", + risk_level: strategyData.risk_assessment?.overall_risk_level || "Medium", + success_probability: strategyData.performance_predictions?.success_probability || "85%", + next_step: "Review strategy and generate content calendar" + } + }; + + console.log('πŸ”„ Transformed strategy data:', transformedStrategy); + setStrategyData(transformedStrategy); + setLoading(false); + return; + } + } catch (pollingError) { + console.log('No latest strategy found in polling system, checking database...', pollingError); + } + + // If no strategy found in polling system, try to get from database + try { + const strategiesResponse = await contentPlanningApi.getEnhancedStrategies(userId); + + // Handle the enhanced strategies response structure + const strategies = strategiesResponse?.data?.strategies || strategiesResponse?.strategies || []; + + if (strategies && strategies.length > 0) { + // Get the most recent strategy (assuming it's sorted by creation date) + const latestStrategy = strategies[0]; + + // Check if this strategy has comprehensive AI-generated data + if (latestStrategy.comprehensive_ai_analysis) { + console.log('βœ… Found comprehensive strategy in database:', latestStrategy); + console.log('πŸ“Š Comprehensive AI analysis structure:', latestStrategy.comprehensive_ai_analysis); + console.log('πŸ” Available fields:', Object.keys(latestStrategy.comprehensive_ai_analysis)); + + // Check if this is the full 5-component structure or SWOT analysis + const hasFullStructure = latestStrategy.comprehensive_ai_analysis.strategic_insights || + latestStrategy.comprehensive_ai_analysis.competitive_analysis || + latestStrategy.comprehensive_ai_analysis.performance_predictions; + + if (hasFullStructure) { + // Transform the data to match frontend expectations (full 5-component structure) + const transformedStrategy = { + ...latestStrategy.comprehensive_ai_analysis, + strategy_metadata: latestStrategy.comprehensive_ai_analysis.metadata || latestStrategy.comprehensive_ai_analysis.strategy_metadata, + // Add summary if not present + summary: latestStrategy.comprehensive_ai_analysis.summary || { + estimated_roi: latestStrategy.comprehensive_ai_analysis.performance_predictions?.estimated_roi || "15-25%", + implementation_timeline: latestStrategy.comprehensive_ai_analysis.implementation_roadmap?.total_duration || "12 months", + risk_level: latestStrategy.comprehensive_ai_analysis.risk_assessment?.overall_risk_level || "Medium", + success_probability: latestStrategy.comprehensive_ai_analysis.performance_predictions?.success_probability || "85%", + next_step: "Review strategy and generate content calendar" + } + }; + + console.log('πŸ”„ Transformed enhanced strategy data (full structure):', transformedStrategy); + console.log('🎯 Final strategy data structure:', { + hasStrategicInsights: !!transformedStrategy.strategic_insights, + hasCompetitiveAnalysis: !!transformedStrategy.competitive_analysis, + hasPerformancePredictions: !!transformedStrategy.performance_predictions, + hasImplementationRoadmap: !!transformedStrategy.implementation_roadmap, + hasRiskAssessment: !!transformedStrategy.risk_assessment, + hasSummary: !!transformedStrategy.summary + }); + setStrategyData(transformedStrategy); + setLoading(false); + return; + } else { + // This is SWOT analysis, create a comprehensive 5-component structure enhanced with SWOT data + console.log('πŸ”„ Creating comprehensive 5-component structure from SWOT analysis'); + const swotData = latestStrategy.comprehensive_ai_analysis; + + const transformedStrategy = { + strategy_metadata: { + user_id: latestStrategy.user_id, + strategy_name: latestStrategy.name, + ai_generated: true, + comprehensive: true, + content_calendar_ready: false, + generation_timestamp: latestStrategy.created_at + }, + // Enhanced Strategic Insights with SWOT data + strategic_insights: { + market_positioning: { + positioning_strength: swotData.overall_score || 75, + current_position: "Emerging", + swot_analysis: { + strengths: swotData.strengths || [], + opportunities: swotData.opportunities || [] + } + }, + content_opportunities: [ + ...(swotData.opportunities || []), + "Leverage identified market gaps", + "Focus on unique value propositions", + "Build thought leadership content" + ], + growth_potential: { + market_size: "Growing", + growth_rate: "High", + key_drivers: swotData.opportunities || [], + competitive_advantages: swotData.strengths || [] + }, + swot_summary: { + overall_score: swotData.overall_score || 75, + primary_strengths: (swotData.strengths || []).slice(0, 3), + key_opportunities: (swotData.opportunities || []).slice(0, 3) + } + }, + // Enhanced Competitive Analysis with SWOT data + competitive_analysis: { + competitors: [ + { + name: "Direct Competitors", + market_position: "Established", + strengths: swotData.strengths || [], + weaknesses: swotData.weaknesses || [], + competitive_response: "Focus on differentiation" + }, + { + name: "Emerging Competitors", + market_position: "Growing", + strengths: [], + weaknesses: swotData.weaknesses || [], + competitive_response: "Establish market leadership" + } + ], + market_gaps: [ + ...(swotData.opportunities || []), + "Content personalization opportunities", + "Niche market segments", + "Innovation in content delivery" + ], + competitive_advantages: { + primary: swotData.strengths || [], + sustainable: swotData.opportunities || [], + development_areas: swotData.weaknesses || [] + }, + swot_competitive_insights: { + leverage_strengths: swotData.strengths || [], + address_weaknesses: swotData.weaknesses || [], + capitalize_opportunities: swotData.opportunities || [], + mitigate_threats: swotData.threats || [] + } + }, + // Enhanced Performance Predictions with SWOT context + performance_predictions: { + estimated_roi: "20-30%", + key_metrics: { + engagement_rate: "5-8%", + conversion_rate: "2-4%", + reach_growth: "40-60%", + brand_awareness: "25-35%", + market_share: "3-5%" + }, + timeline_projections: { + "3_months": "Initial traction and brand awareness leveraging identified strengths", + "6_months": "Established presence and engagement addressing market opportunities", + "12_months": "Market leadership and growth capitalizing on competitive advantages" + }, + success_factors: { + primary: swotData.strengths || [], + secondary: swotData.opportunities || [], + risk_mitigation: swotData.threats || [] + }, + swot_based_predictions: { + strength_impact: "High performance in areas of identified strengths", + opportunity_impact: "Growth potential through market opportunities", + weakness_mitigation: "Improved performance by addressing weaknesses", + threat_management: "Risk-adjusted projections considering market threats" + } + }, + // Enhanced Implementation Roadmap with SWOT considerations + implementation_roadmap: { + total_duration: "12 months", + phases: [ + { + phase: "Foundation (Months 1-3)", + activities: [ + "Brand positioning leveraging identified strengths", + "Content strategy development addressing market opportunities", + "Weakness assessment and improvement planning" + ], + deliverables: ["Brand guidelines", "Content calendar", "SWOT action plan"], + swot_focus: "Strengths and Opportunities" + }, + { + phase: "Growth (Months 4-8)", + activities: [ + "Content execution based on competitive advantages", + "Community building addressing market gaps", + "Threat mitigation strategies implementation" + ], + deliverables: ["Content library", "Engaged audience", "Risk management framework"], + swot_focus: "Opportunities and Threats" + }, + { + phase: "Scale (Months 9-12)", + activities: [ + "Market expansion capitalizing on strengths", + "Performance optimization addressing weaknesses", + "Sustainable competitive advantage development" + ], + deliverables: ["Market leadership", "Optimized strategy", "Long-term competitive position"], + swot_focus: "Strengths and Weaknesses" + } + ], + resource_allocation: { + team_requirements: "Content team + Marketing support + SWOT analysis expertise", + budget_allocation: "Distributed across phases with focus on opportunity areas", + swot_priorities: { + high_priority: swotData.opportunities || [], + medium_priority: swotData.strengths || [], + low_priority: swotData.weaknesses || [] + } + }, + swot_integration: { + strength_leverage: swotData.strengths || [], + weakness_improvement: swotData.weaknesses || [], + opportunity_capitalization: swotData.opportunities || [], + threat_mitigation: swotData.threats || [] + } + }, + // Enhanced Risk Assessment with SWOT threats + risk_assessment: { + overall_risk_level: "Medium", + risk_categories: { + market_risks: [ + ...(swotData.threats?.map((threat: string) => ({ + risk: threat, + probability: "Medium", + impact: "High", + mitigation: "Strategic planning and monitoring" + })) || []), + { + risk: "Market saturation", + probability: "Medium", + impact: "Medium", + mitigation: "Innovation and differentiation" + } + ], + operational_risks: [ + { + risk: "Resource constraints", + probability: "Medium", + impact: "Medium", + mitigation: "Efficient resource allocation" + }, + { + risk: "Weakness areas", + probability: "High", + impact: "Medium", + mitigation: "Targeted improvement programs" + } + ], + competitive_risks: [ + { + risk: "Market competition", + probability: "High", + impact: "Medium", + mitigation: "Leverage competitive advantages" + }, + { + risk: "Strength erosion", + probability: "Medium", + impact: "High", + mitigation: "Continuous improvement and innovation" + } + ] + }, + swot_risk_mapping: { + strength_risks: "Risk of over-reliance on current strengths", + weakness_risks: "Risk of weakness exploitation by competitors", + opportunity_risks: "Risk of missing market opportunities", + threat_risks: "Risk of threat materialization" + }, + risk_mitigation_strategies: { + based_on_strengths: "Leverage strengths to mitigate threats", + based_on_opportunities: "Use opportunities to address weaknesses", + based_on_weaknesses: "Develop improvement plans for weak areas", + based_on_threats: "Create contingency plans for identified threats" + } + }, + // Enhanced summary with SWOT context + summary: { + estimated_roi: "20-30%", + implementation_timeline: "12 months", + risk_level: "Medium", + success_probability: `${swotData.overall_score || 75}%`, + next_step: "Review strategy and generate content calendar", + swot_highlights: { + key_strengths: (swotData.strengths || []).slice(0, 2), + key_opportunities: (swotData.opportunities || []).slice(0, 2), + primary_risks: (swotData.threats || []).slice(0, 2) + } + } + }; + + console.log('πŸ”„ Created comprehensive 5-component structure from SWOT analysis:', transformedStrategy); + console.log('🎯 Final strategy data structure:', { + hasStrategicInsights: !!transformedStrategy.strategic_insights, + hasCompetitiveAnalysis: !!transformedStrategy.competitive_analysis, + hasPerformancePredictions: !!transformedStrategy.performance_predictions, + hasImplementationRoadmap: !!transformedStrategy.implementation_roadmap, + hasRiskAssessment: !!transformedStrategy.risk_assessment, + hasSummary: !!transformedStrategy.summary, + swotEnhanced: true + }); + setStrategyData(transformedStrategy); + setLoading(false); + return; + } + } else { + console.log('⚠️ Strategy found but no comprehensive_ai_analysis field:', { + strategyId: latestStrategy.id, + strategyName: latestStrategy.name, + availableFields: Object.keys(latestStrategy) + }); + } + } + } catch (dbError) { + console.log('No comprehensive strategies found in database, checking for recent generation...', dbError); + } + + // If no comprehensive strategy found in database, check for recent generation tasks + // This would be the data from our polling-based generation that might not be saved yet + try { + // Try to get the latest strategy generation result + const recentStrategies = await contentPlanningApi.getStrategies(userId); + + // Handle the enhanced strategies response structure + const strategies = recentStrategies?.data?.strategies || recentStrategies?.strategies || []; + + if (strategies && strategies.length > 0) { + // Find the most recent AI-generated strategy + const aiGeneratedStrategy = strategies.find( + (strategy: any) => strategy.comprehensive_ai_analysis + ); + + if (aiGeneratedStrategy && aiGeneratedStrategy.comprehensive_ai_analysis) { + console.log('βœ… Found AI-generated strategy in recent strategies:', aiGeneratedStrategy); + + // Transform the data to match frontend expectations + const transformedStrategy = { + ...aiGeneratedStrategy.comprehensive_ai_analysis, + strategy_metadata: aiGeneratedStrategy.comprehensive_ai_analysis.metadata || aiGeneratedStrategy.comprehensive_ai_analysis.strategy_metadata, + // Add summary if not present + summary: aiGeneratedStrategy.comprehensive_ai_analysis.summary || { + estimated_roi: aiGeneratedStrategy.comprehensive_ai_analysis.performance_predictions?.estimated_roi || "15-25%", + implementation_timeline: aiGeneratedStrategy.comprehensive_ai_analysis.implementation_roadmap?.total_duration || "12 months", + risk_level: aiGeneratedStrategy.comprehensive_ai_analysis.risk_assessment?.overall_risk_level || "Medium", + success_probability: aiGeneratedStrategy.comprehensive_ai_analysis.performance_predictions?.success_probability || "85%", + next_step: "Review strategy and generate content calendar" + } + }; + + console.log('πŸ”„ Transformed recent strategy data:', transformedStrategy); + setStrategyData(transformedStrategy); + setLoading(false); + return; + } + } + } catch (strategyError) { + console.log('No recent strategies found, checking for generation tasks...', strategyError); + } + + // If no strategy data is available, show appropriate message + console.log('❌ No comprehensive strategy data found'); + setStrategyData(null); + setError('No comprehensive strategy data available. Please generate a strategy first.'); + setLoading(false); + + } catch (err: any) { + console.error('Error loading strategy data:', err); + setError(err.message || 'Failed to load strategy data'); + setStrategyData(null); + setLoading(false); + } + }; + + const handleConfirmStrategy = () => { + setShowConfirmDialog(true); + }; + + const confirmStrategy = async () => { + try { + setStrategyConfirmed(true); + setShowConfirmDialog(false); + + // Save confirmation status to backend + const userId = strategyData?.strategy_metadata?.user_id || strategyData?.metadata?.user_id; + if (userId) { + try { + // Update the strategy with confirmation status + await contentPlanningApi.updateEnhancedStrategy( + userId.toString(), + { confirmed: true, confirmed_at: new Date().toISOString() } + ); + console.log('Strategy confirmation saved to backend'); + } catch (updateError) { + console.warn('Could not save confirmation to backend:', updateError); + // Don't fail the confirmation if backend update fails + } + } + + console.log('Strategy confirmed! Ready to generate content calendar.'); + } catch (error) { + console.error('Error confirming strategy:', error); + setStrategyConfirmed(false); + } + }; + + const handleGenerateContentCalendar = async () => { + try { + const userId = strategyData?.strategy_metadata?.user_id || strategyData?.metadata?.user_id; + if (!userId) { + console.error('No strategy data available for calendar generation'); + return; + } + + // Generate content calendar based on confirmed strategy + const calendarRequest = { + user_id: userId, + strategy_id: userId, // Using user_id as strategy_id for now + calendar_type: 'comprehensive', + industry: strategyData.base_strategy?.industry || 'technology', + business_size: 'medium', // TODO: Get from strategy data + force_refresh: false + }; + + console.log('Generating content calendar with request:', calendarRequest); + + // Call the calendar generation API + const calendarResponse = await contentPlanningApi.generateCalendar(calendarRequest); + + console.log('Content calendar generated successfully:', calendarResponse); + + // TODO: Navigate to calendar tab or show success message + // You could also store the calendar data in a global state + + } catch (error) { + console.error('Error generating content calendar:', error); + // Show error message to user + setError('Failed to generate content calendar. Please try again.'); + } + }; + + if (loading) { + return ( + + + + ); + } + + if (error) { + return ( + + {error} + + ); + } + + if (!strategyData) { + return ( + + + No Strategy Data Available + + + Generate a comprehensive strategy first to view strategic intelligence. + + + ); + } + + return ( + + {/* Header Section */} + + + + + 🧠 Strategic Intelligence + + + {strategyData.strategy_metadata?.strategy_name || strategyData.metadata?.strategy_name || 'AI-Generated Strategy'} + + + Generated on {new Date(strategyData.strategy_metadata?.generated_at || strategyData.strategy_metadata?.generation_timestamp || strategyData.metadata?.generated_at || strategyData.metadata?.generation_timestamp || '').toLocaleDateString()} + + + + + + + + + + + + + {/* Strategy Confirmation Status */} + {strategyConfirmed && ( + + } + sx={{ + fontWeight: 600, + '&:hover': { + backgroundColor: 'rgba(76, 175, 80, 0.1)', + transform: 'translateY(-1px)' + }, + transition: 'all 0.3s ease' + }} + > + Generate Content Calendar + + } + > + Strategy confirmed! You can now generate a content calendar based on this strategy. + + + )} + + {/* Strategy Components - Enhanced Grid Layout */} + + {/* Strategic Insights */} + + + + + + + + + Strategic Insights + + + {strategyData.strategic_insights && ( + <> + + + Market Positioning + + + + + + {strategyData.strategic_insights.market_positioning?.positioning_strength || 0} + + + + {strategyData.strategic_insights.market_positioning?.current_position} + + + + + + + + Content Opportunities + + + {(strategyData.strategic_insights.content_opportunities || []).map((opportunity: string, index: number) => ( + + + + + + + + + ))} + + + + + + Growth Potential + + + + + + + )} + + + + + + {/* Competitive Analysis */} + + + + + + + + + Competitive Analysis + + + {strategyData.competitive_analysis && ( + <> + + Key Competitors + + {(strategyData.competitive_analysis.competitors || []).slice(0, 2).map((competitor: any, index: number) => ( + + + {competitor.name} + + + {competitor.market_position} + + + + + + + ))} + + + + + Market Gaps + + + {(strategyData.competitive_analysis.market_gaps || []).slice(0, 3).map((gap: string, index: number) => ( + + + + + + + + + ))} + + + )} + + + + + + {/* Performance Predictions */} + + + + + + + + + Performance Predictions + + + {strategyData.performance_predictions && ( + <> + + + {strategyData.performance_predictions.estimated_roi} + + + Estimated ROI + + + + + Key Metrics + + + {Object.entries(strategyData.performance_predictions.key_metrics || {}).map(([metric, value]) => ( + + + + {value as string} + + + {metric.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())} + + + + ))} + + + + + + Timeline Projections + + + {Object.entries(strategyData.performance_predictions.timeline_projections || {}).map(([period, projection]) => ( + + + + + + + l.toUpperCase())} + primaryTypographyProps={{ + variant: 'body2', + sx: { fontWeight: 500, lineHeight: 1.4 } + }} + secondaryTypographyProps={{ + variant: 'caption', + sx: { fontWeight: 600, color: '#4caf50' } + }} + /> + + ))} + + + )} + + + + + + {/* Implementation Roadmap */} + + + + + + + + + Implementation Roadmap + + + {strategyData.implementation_roadmap && ( + <> + + {strategyData.implementation_roadmap.total_duration} + + + + {(strategyData.implementation_roadmap.phases || []).map((phase: any, index: number) => ( + + + + {phase.phase} + + + + + + Activities: {phase.activities?.length || 0} + + + Deliverables: {phase.deliverables?.length || 0} + + + + + ))} + + + + + + Resource Requirements + + + + {strategyData.implementation_roadmap.resource_allocation?.team_requirements} + + + Budget: {strategyData.implementation_roadmap.resource_allocation?.budget_allocation} + + + + )} + + + + + + {/* Risk Assessment */} + + + + + + + + + Risk Assessment + + + + {strategyData.risk_assessment && ( + + {Object.entries(strategyData.risk_assessment.risk_categories || {}).map(([category, risks]) => ( + + + {category.replace(/_/g, ' ')} + + {(risks as any[]).map((risk: any, index: number) => ( + + + {risk.risk} + + + + + + + ))} + + ))} + + )} + + + + + + + {/* Action Buttons */} + + {!strategyConfirmed ? ( + + + + ) : ( + + + + )} + + + + + + + {/* Confirmation Dialog */} + setShowConfirmDialog(false)} + maxWidth="sm" + fullWidth + PaperProps={{ + sx: { + borderRadius: 3, + boxShadow: '0 16px 48px rgba(0, 0, 0, 0.2)' + } + }} + > + + + + + + + Confirm Strategy + + + + + + Are you sure you want to confirm this strategy? Once confirmed, you'll be able to generate a content calendar based on this strategy. + + + + Next Steps: After confirmation, you can generate a comprehensive content calendar that follows this strategy. + + + + + + + + + + ); +}; + +export default StrategyIntelligenceTab; \ No newline at end of file diff --git a/frontend/src/components/ContentPlanningDashboard/tabs/ContentStrategyTab.tsx b/frontend/src/components/ContentPlanningDashboard/tabs/ContentStrategyTab.tsx index f36c5f2a..2a12e703 100644 --- a/frontend/src/components/ContentPlanningDashboard/tabs/ContentStrategyTab.tsx +++ b/frontend/src/components/ContentPlanningDashboard/tabs/ContentStrategyTab.tsx @@ -56,6 +56,7 @@ import { import { useContentPlanningStore } from '../../../stores/contentPlanningStore'; import { contentPlanningApi } from '../../../services/contentPlanningApi'; import ContentStrategyBuilder from '../components/ContentStrategyBuilder'; +import StrategyIntelligenceTab from '../components/StrategyIntelligenceTab'; interface TabPanelProps { children?: React.ReactNode; @@ -461,110 +462,7 @@ const ContentStrategyTab: React.FC = () => { {/* Strategic Intelligence Tab */} - {dataLoading.strategicIntelligence ? ( - - - - ) : strategicIntelligence && strategicIntelligence.market_positioning ? ( - - - - - - Market Positioning - - - - - {strategicIntelligence.market_positioning.score || 0}/100 - - - - - Strengths: - - - {(strategicIntelligence.market_positioning.strengths || []).map((strength: string, index: number) => ( - - - - - - - ))} - - - - - - - - - - Competitive Advantages - - {(strategicIntelligence.competitive_advantages || []).map((advantage: any, index: number) => ( - - - {advantage.advantage} - - - - - - - ))} - - - - - - - - - Strategic Risks - - {(strategicIntelligence.strategic_risks || []).map((risk: any, index: number) => ( - - - {risk.risk} - - - - - - - ))} - - - - - ) : ( - - No strategic intelligence data available - - )} + {/* Keyword Research Tab */} diff --git a/frontend/src/services/contentPlanningApi.ts b/frontend/src/services/contentPlanningApi.ts index 97a3594b..d77e83bf 100644 --- a/frontend/src/services/contentPlanningApi.ts +++ b/frontend/src/services/contentPlanningApi.ts @@ -196,7 +196,7 @@ class ContentPlanningAPI { async getStrategies(userId?: number) { const params = userId ? { user_id: userId } : {}; - const response = await apiClient.get(`${this.baseURL}/strategies/`, { params }); + const response = await apiClient.get(`${this.baseURL}/enhanced-strategies`, { params }); return response.data; } @@ -582,6 +582,105 @@ class ContentPlanningAPI { return eventSource; } + // New polling-based strategy generation methods + async startStrategyGenerationPolling(userId: number = 1, strategyName?: string, config?: any): Promise { + return this.handleRequest(async () => { + const payload = { + user_id: userId, + strategy_name: strategyName || 'Enhanced Content Strategy', + config: config || {} + }; + + console.log('πŸš€ Starting polling-based strategy generation:', payload); + + const response = await apiClient.post( + `${this.baseURL}/content-strategy/ai-generation/generate-comprehensive-strategy-polling`, + payload + ); + + console.log('βœ… Strategy generation started:', response.data); + return response.data.data || response.data; + }); + } + + async getStrategyGenerationStatus(taskId: string): Promise { + return this.handleRequest(async () => { + const response = await apiClient.get(`${this.baseURL}/content-strategy/ai-generation/strategy-generation-status/${taskId}`); + return response.data.data || response.data; + }); + } + + // Get the latest generated strategy from polling system + async getLatestGeneratedStrategy(userId: number = 1): Promise { + return this.handleRequest(async () => { + const response = await apiClient.get(`${this.baseURL}/content-strategy/ai-generation/latest-strategy`, { + params: { user_id: userId } + }); + return response.data.data || response.data; + }); + } + + // Polling utility method + async pollStrategyGeneration( + taskId: string, + onProgress: (status: any) => void, + onComplete: (strategy: any) => void, + onError: (error: any) => void, + pollInterval: number = 10000, // 10 seconds + maxAttempts: number = 36 // 6 minutes max (36 * 10 seconds) + ): Promise { + let attempts = 0; + + const poll = async () => { + try { + attempts++; + console.log(`πŸ“Š Polling attempt ${attempts}/${maxAttempts} for task: ${taskId}`); + + const status = await this.getStrategyGenerationStatus(taskId); + + // Call progress callback + onProgress(status); + + // Check if completed + if (status.status === 'completed') { + console.log('βœ… Strategy generation completed:', status); + onComplete(status.strategy); + return; + } + + // Check if failed + if (status.status === 'failed') { + console.error('❌ Strategy generation failed:', status.error); + onError(status.error || 'Strategy generation failed'); + return; + } + + // Check if max attempts reached + if (attempts >= maxAttempts) { + console.warn('⏰ Max polling attempts reached, checking final status...'); + const finalStatus = await this.getStrategyGenerationStatus(taskId); + + if (finalStatus.status === 'completed') { + onComplete(finalStatus.strategy); + } else { + onError('Strategy generation timeout. The process may still be running in the background.'); + } + return; + } + + // Continue polling + setTimeout(poll, pollInterval); + + } catch (error) { + console.error('❌ Error polling strategy generation status:', error); + onError(error); + } + }; + + // Start polling + poll(); + } + async updateEnhancedStrategy(id: string, updates: any): Promise { return this.handleRequest(async () => { const response = await apiClient.put(`${this.baseURL}/enhanced-strategies/${id}`, updates);