import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Box, Card, CardContent, Typography, Grid, Chip, LinearProgress, Alert, CircularProgress, IconButton, List, ListItem, ListItemText, ListItemIcon, } from '@mui/material'; import { Visibility, MouseOutlined, Search, Web, Refresh, Info, CheckCircle, Error as ErrorIcon, Warning, } from '@mui/icons-material'; import { Button } from '@mui/material'; import { PlatformAnalytics as PlatformAnalyticsType, AnalyticsSummary, PlatformConnectionStatus } from '../../api/analytics'; import { cachedAnalyticsAPI } from '../../api/cachedAnalytics'; import BingInsightsCard from './BingInsightsCard'; import BackgroundJobManager from './BackgroundJobManager'; interface PlatformAnalyticsComponentProps { platforms?: string[]; showSummary?: boolean; refreshInterval?: number; // in milliseconds, 0 = no auto-refresh onDataLoaded?: (data: any) => void; onRefreshReady?: (refreshFn: () => Promise) => void; // Expose refresh function to parent onReconnect?: (platform: string) => void; // Reconnect handler for individual platforms showBackgroundJobs?: boolean; // Only render background jobs when user triggers } const PlatformAnalytics: React.FC = ({ platforms, showSummary = true, refreshInterval = 0, onDataLoaded, onRefreshReady, onReconnect, showBackgroundJobs = false, }) => { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [analyticsData, setAnalyticsData] = useState>({}); const [summary, setSummary] = useState(null); const [, setPlatformStatus] = useState>({}); const [lastUpdated, setLastUpdated] = useState(null); const loadData = useCallback(async () => { try { setLoading(true); setError(null); // Load platform connection status const statusResponse = await cachedAnalyticsAPI.getPlatformStatus(); setPlatformStatus(statusResponse.platforms); // Load analytics data const analyticsResponse = await cachedAnalyticsAPI.getAnalyticsData(platforms); setAnalyticsData(analyticsResponse.data as Record); setSummary(analyticsResponse.summary); setLastUpdated(new Date()); if (onDataLoaded) { onDataLoaded({ analytics: analyticsResponse.data, summary: analyticsResponse.summary, status: statusResponse.platforms, }); } } catch (err: unknown) { console.error('Error loading analytics data:', err); let errorMessage = 'Failed to load analytics data'; if (err instanceof Error) { errorMessage = (err as Error).message; } else if (typeof err === 'string') { errorMessage = err; } setError(errorMessage); } finally { setLoading(false); } }, [platforms, onDataLoaded]); // Method to force refresh (bypass cache) const forceRefresh = useCallback(async () => { setLoading(true); setError(null); try { // Clear cache and force fresh data await cachedAnalyticsAPI.forceRefreshAnalyticsData(platforms); // Reload data await loadData(); } catch (err) { console.error('PlatformAnalytics: Force refresh failed:', err); setError(err instanceof Error ? err.message : 'Failed to refresh data'); } finally { setLoading(false); } }, [platforms, loadData]); // One-run guard to prevent duplicate calls in StrictMode const dataLoadedRef = useRef(false); useEffect(() => { if (dataLoadedRef.current) return; dataLoadedRef.current = true; loadData(); // Set up auto-refresh if interval is specified let interval: NodeJS.Timeout | null = null; if (refreshInterval > 0) { interval = setInterval(loadData, refreshInterval); } return () => { if (interval) { clearInterval(interval); } }; }, [platforms, refreshInterval, loadData]); // Expose refresh function to parent component useEffect(() => { if (onRefreshReady) { onRefreshReady(forceRefresh); } }, [onRefreshReady, forceRefresh]); const getPlatformIcon = (platform: string) => { switch (platform.toLowerCase()) { case 'gsc': return ; case 'wix': return ; case 'wordpress': return ; case 'bing': return ; default: return ; } }; const getStatusColor = (status: string) => { switch (status) { case 'success': return 'success'; case 'error': return 'error'; case 'partial': return 'warning'; default: return 'default'; } }; const getStatusIcon = (status: string) => { switch (status) { case 'success': return ; case 'error': return ; case 'partial': return ; default: return ; } }; 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 renderMetricsCard = (platform: string, data: PlatformAnalyticsType) => { const metrics = data.metrics; return ( {getPlatformIcon(platform)} {platform.toUpperCase()} {getStatusIcon(data.status)} {data.status === 'success' && ( <> {metrics.total_clicks !== undefined && ( {formatNumber(metrics.total_clicks)} Clicks )} {metrics.total_impressions !== undefined && ( {formatNumber(metrics.total_impressions)} Impressions )} {metrics.avg_ctr !== undefined && ( CTR {metrics.avg_ctr}% )} {metrics.avg_position !== undefined && ( Avg Position {metrics.avg_position.toFixed(1)} )} {metrics.top_queries && metrics.top_queries.length > 0 && ( Top Queries {metrics.top_queries.slice(0, 3).map((query, index) => ( {index + 1} ))} )} )} {data.status === 'error' && ( {data.error_message || 'Failed to load analytics data'} {onReconnect && ( )} )} {data.status === 'partial' && ( {data.error_message || 'Limited analytics data available'} )} Last updated: {data.last_updated ? new Date(data.last_updated).toLocaleString() : 'Never'} ); }; const renderSummaryCard = () => { if (!summary) return null; return ( Analytics Summary {summary.connected_platforms} Connected Platforms {formatNumber(summary.total_clicks)} Total Clicks {formatNumber(summary.total_impressions)} Total Impressions {summary.overall_ctr}% Overall CTR {lastUpdated && ( Last refreshed: {lastUpdated.toLocaleString()} )} ); }; if (loading) { return ( Loading analytics data... ); } if (error) { return ( {error} ); } return ( {showSummary && renderSummaryCard()} {Object.entries(analyticsData) .filter(([platform]) => platform.toLowerCase() !== 'wordpress') // Exclude WordPress analytics .map(([platform, data]) => ( {renderMetricsCard(platform, data)} ))} {/* Background Job Manager - render only when explicitly enabled */} {showBackgroundJobs && ( { console.log('🎉 Background job completed:', job); // Refresh analytics data when job completes forceRefresh(); }} /> )} {/* Debug Section - Show data structure for all platforms */} Debug: Platform Data Structures {Object.entries(analyticsData).map(([platform, data]) => ( {platform.toUpperCase()} Data: {JSON.stringify(data, null, 2)} ))} {/* Bing Insights Card - Show when Bing is connected */} {analyticsData.bing && ( Debug: Bing data structure: {JSON.stringify(analyticsData.bing, null, 2)} {analyticsData.bing.metrics?.connection_status === 'connected' && ( { console.log('Bing insights loaded:', insights); }} /> )} )} {Object.keys(analyticsData).length === 0 && ( No analytics data available. Connect your platforms to see analytics insights. )} ); }; export default PlatformAnalytics;