import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Box, Card, CardContent, Typography, Grid, Chip, LinearProgress, Alert, CircularProgress, IconButton, List, ListItem, ListItemText, ListItemIcon, Accordion, AccordionSummary, AccordionDetails, Divider, Tooltip, Badge, } from '@mui/material'; import { Visibility, MouseOutlined, Search, TrendingUp, TrendingDown, Insights, Lightbulb, Assessment, Refresh, ExpandMore, CheckCircle, Error as ErrorIcon, Warning, Star, Speed, Analytics, } from '@mui/icons-material'; import { apiClient } from '../../api/client'; interface BingInsightsCardProps { siteUrl?: string; days?: number; onInsightsLoaded?: (insights: any) => void; insights?: { performance?: PerformanceInsights; seo?: SEOInsights; recommendations?: Recommendations; last_analyzed?: string; }; loading?: boolean; error?: string | null; } interface PerformanceInsights { performance_summary: { total_clicks: number; total_impressions: number; avg_ctr: number; total_queries: number; }; trends: { status?: string; message?: string; ctr_trend?: { current: number; previous: number; change_percent: number; direction: string; }; clicks_trend?: { current: number; previous: number; change_percent: number; direction: string; }; trend_strength?: string; }; performance_indicators: { ctr_score?: number; volume_score?: number; consistency_score?: number; overall_score?: number; performance_level: string; traffic_quality?: string; growth_potential?: string; }; insights: string[]; error?: string; // Add error property for error handling } interface SEOInsights { query_analysis: { total_queries: number; brand_queries: { count: number; clicks: number; percentage: number; }; non_brand_queries: { count: number; clicks: number; percentage: number; }; query_length_distribution: { short_queries: number; long_queries: number; average_length: number; }; top_categories: Record; }; content_opportunities: Array<{ query: string; impressions: number; ctr: number; opportunity: string; priority: string; }>; technical_insights: { average_position: number; average_ctr: number; position_distribution: { top_3: number; top_10: number; page_2_plus: number; }; ctr_distribution: { excellent: number; good: number; poor: number; }; }; seo_recommendations: Array<{ type: string; priority: string; recommendation: string; action: string; }>; error?: string; // Add error property for error handling } interface Recommendations { immediate_actions: Array<{ action: string; priority: string; description: string; }>; content_optimization: Array<{ query: string; opportunity: string; priority: string; }>; technical_improvements: Array<{ issue: string; solution: string; priority: string; }>; long_term_strategy: Array<{ strategy: string; timeline: string; expected_impact: string; }>; priority_score: Record; error?: string; // Add error property for error handling } const BingInsightsCard: React.FC = ({ siteUrl = 'https://www.alwrity.com/', days = 30, onInsightsLoaded, insights: propInsights, loading: propLoading, error: propError, }) => { const [internalLoading, setInternalLoading] = useState(!propInsights); const [internalError, setInternalError] = useState(null); const [internalInsights, setInternalInsights] = useState<{ performance?: PerformanceInsights; seo?: SEOInsights; recommendations?: Recommendations; last_analyzed?: string; }>({}); const debounceTimeoutRef = useRef(null); // Use props if available, otherwise use internal state const loading = propLoading !== undefined ? propLoading : internalLoading; const error = propError !== undefined ? propError : internalError; const insights = propInsights || internalInsights; const loadInsights = useCallback(async () => { // Only load if we don't have insights passed as props if (propInsights) return; // Clear any existing timeout if (debounceTimeoutRef.current) { clearTimeout(debounceTimeoutRef.current); } // Debounce the API call to prevent rapid successive requests debounceTimeoutRef.current = setTimeout(async () => { try { setInternalLoading(true); setInternalError(null); const response = await apiClient.get('/api/bing-insights/comprehensive', { params: { site_url: siteUrl, days } }); console.log('Raw Bing insights response:', response.data.data); // The API response structure is directly the insights data (no metrics wrapper) const insightsData = response.data.data; console.log('Insights data structure:', insightsData); setInternalInsights(insightsData); onInsightsLoaded?.(insightsData); } catch (err: any) { setInternalError(err.response?.data?.detail || 'Failed to load Bing insights'); } finally { setInternalLoading(false); } }, 300); // 300ms debounce }, [siteUrl, days, onInsightsLoaded, propInsights]); const formatNumber = (num: number) => { if (num >= 1000000) { return (num / 1000000).toFixed(1) + 'M'; } else if (num >= 1000) { return (num / 1000).toFixed(1) + 'K'; } return num.toString(); }; const getChangeColor = (change: number) => { if (change > 0) return 'success'; if (change < 0) return 'error'; return 'default'; }; const getChangeIcon = (change: number) => { if (change > 0) return ; if (change < 0) return ; return ; }; const getPerformanceLevelColor = (level: string) => { switch (level) { case 'excellent': return 'success'; case 'good': return 'info'; case 'fair': return 'warning'; case 'needs_improvement': return 'error'; default: return 'default'; } }; const getPriorityColor = (priority: string) => { switch (priority) { case 'high': return 'error'; case 'medium': return 'warning'; case 'low': return 'info'; default: return 'default'; } }; useEffect(() => { // Only load insights if we don't have them passed as props if (!propInsights) { loadInsights(); } // Cleanup timeout on unmount return () => { if (debounceTimeoutRef.current) { clearTimeout(debounceTimeoutRef.current); } }; }, [loadInsights, propInsights]); if (loading) { return ( Loading Bing insights... ); } if (error) { return ( }> {error} ); } return ( Bing Webmaster Insights {/* Connection Status and Basic Metrics */} Connection Status {formatNumber(insights.performance?.performance_summary?.total_clicks || 0)} Total Clicks {formatNumber(insights.performance?.performance_summary?.total_impressions || 0)} Total Impressions {(insights.performance?.performance_summary?.avg_ctr || 0).toFixed(2)}% Average CTR {formatNumber(insights.performance?.performance_summary?.total_queries || 0)} Total Queries {/* Performance Insights */} {insights.performance && !insights.performance.error && insights.performance.performance_indicators && insights.performance.performance_summary && ( }> Performance Analysis {/* Performance Summary */} Performance Summary Total Clicks: {formatNumber(insights.performance.performance_summary.total_clicks || 0)} Total Impressions: {formatNumber(insights.performance.performance_summary.total_impressions || 0)} Average CTR: {(insights.performance.performance_summary.avg_ctr || 0).toFixed(2)}% Total Queries: {formatNumber(insights.performance.performance_summary.total_queries || 0)} {/* Performance Indicators */} Performance Indicators Performance Level: Traffic Quality: {insights.performance.performance_indicators.traffic_quality || 'Unknown'} Growth Potential: {insights.performance.performance_indicators.growth_potential || 'Unknown'} {/* Legacy scores if available */} {insights.performance.performance_indicators.ctr_score !== undefined && ( CTR Score: {insights.performance.performance_indicators.ctr_score || 0}/100 70 ? 'success' : 'primary'} /> )} {/* Trends */} {insights.performance.trends && ( Trends {insights.performance.trends.status === 'insufficient_data' ? ( {insights.performance.trends.message || 'Detailed analytics data not available for trend analysis'} ) : ( {insights.performance.trends.ctr_trend && ( {getChangeIcon(insights.performance.trends.ctr_trend.change_percent || 0)} CTR Trend: 0 ? '+' : ''}${insights.performance.trends.ctr_trend.change_percent || 0}%`} color={getChangeColor(insights.performance.trends.ctr_trend.change_percent || 0)} size="small" /> )} {insights.performance.trends.clicks_trend && ( {getChangeIcon(insights.performance.trends.clicks_trend.change_percent || 0)} Clicks Trend: 0 ? '+' : ''}${insights.performance.trends.clicks_trend.change_percent || 0}%`} color={getChangeColor(insights.performance.trends.clicks_trend.change_percent || 0)} size="small" /> )} )} )} {/* Performance Insights */} {insights.performance.insights && insights.performance.insights.length > 0 && ( Key Insights {insights.performance.insights.map((insight, index) => ( ))} )} )} {/* Performance Error Fallback */} {insights.performance && insights.performance.error && ( Performance insights unavailable: {insights.performance.error} )} {/* SEO Insights */} {insights.seo && !insights.seo.error && insights.seo.query_analysis && ( }> SEO Analysis {/* Query Analysis */} Query Analysis Total Queries: {insights.seo?.query_analysis?.total_queries || 0} Brand Queries: {insights.seo?.query_analysis?.brand_queries?.percentage || 0}% Non-Brand Queries: {insights.seo?.query_analysis?.non_brand_queries?.percentage || 0}% Avg Query Length: {insights.seo?.query_analysis?.query_length_distribution?.average_length || 0} chars {/* Technical Insights */} Technical Performance Avg Position: {insights.seo?.technical_insights?.average_position !== undefined ? insights.seo.technical_insights.average_position : 'N/A'} Avg CTR: {(insights.seo?.technical_insights?.average_ctr || 0).toFixed(2)}% Top 3 Positions: {insights.seo?.technical_insights?.position_distribution?.top_3 || 0} Top 10 Positions: {insights.seo?.technical_insights?.position_distribution?.top_10 || 0} {/* Content Opportunities */} {insights.seo?.content_opportunities && insights.seo.content_opportunities.length > 0 && ( Content Opportunities {insights.seo.content_opportunities.slice(0, 3).map((opportunity, index) => ( ))} )} {/* SEO Recommendations */} {insights.seo.seo_recommendations && insights.seo.seo_recommendations.length > 0 && ( SEO Recommendations {insights.seo.seo_recommendations.map((rec, index) => ( ))} )} )} {/* SEO Error Fallback */} {insights.seo && insights.seo.error && ( SEO insights unavailable: {insights.seo.error} )} {/* Recommendations */} {insights.recommendations && ( }> Actionable Recommendations {/* Immediate Actions */} {insights.recommendations.immediate_actions && insights.recommendations.immediate_actions.length > 0 && ( Immediate Actions {insights.recommendations.immediate_actions.map((action, index) => ( ))} )} {/* Long-term Strategy */} {insights.recommendations.long_term_strategy && insights.recommendations.long_term_strategy.length > 0 && ( Long-term Strategy {insights.recommendations.long_term_strategy.map((strategy, index) => ( ))} )} )} {/* Last Updated Information */} {insights.last_analyzed && ( Last analyzed: {new Date(insights.last_analyzed).toLocaleString()} )} ); }; export default BingInsightsCard;