import { useState, useEffect } from 'react'
import {
LineChart, Line, BarChart, Bar, PieChart, Pie, Cell,
XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer,
AreaChart, Area, RadarChart, Radar, PolarGrid, PolarAngleAxis, PolarRadiusAxis,
ScatterChart, Scatter, ZAxis
} from 'recharts'
// API Base URL
const API_BASE = import.meta.env.DEV ? 'http://localhost:8000' : '';
function Analytics() {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
fetchAnalytics()
}, [])
const fetchAnalytics = async () => {
try {
const response = await fetch(`${API_BASE}/api/analytics`)
const result = await response.json()
setData(result)
} catch (err) {
console.error('Failed to fetch analytics:', err)
} finally {
setLoading(false)
}
}
if (loading) {
return (
)
}
// Chart colors
const COLORS = ['#4ade80', '#facc15', '#f87171', '#60a5fa', '#a78bfa']
const GRADIENT_COLORS = {
primary: '#4ade80',
secondary: '#60a5fa',
accent: '#a78bfa'
}
// Data with fallbacks
const accuracyTrend = data?.accuracy_trend || []
const teamAccuracy = data?.team_accuracy || []
const confidenceDistribution = data?.confidence_distribution || []
const calibrationData = data?.calibration || []
const overallStats = data?.overall || { total_predictions: 0, correct: 0, accuracy: 0, avg_confidence: 0 }
// New advanced data
const eloScatter = data?.elo_scatter || []
const radarData = data?.radar_data || []
const homeAwaySplit = data?.home_away_split || { home: { accuracy: 0 }, away: { accuracy: 0 } }
const streakData = data?.streak_data || []
const currentStreak = data?.current_streak || { count: 0, type: 'W' }
const topMatchups = data?.top_matchups || []
// Custom tooltip styles
const tooltipStyle = {
background: 'rgba(0,0,0,0.9)',
border: '1px solid rgba(255,255,255,0.2)',
borderRadius: '8px',
padding: '8px 12px'
}
return (
Analytics Dashboard
Advanced model performance and NBA statistics
{/* Stats Overview */}
{overallStats.total_predictions}
Total Predictions
{overallStats.correct}
Correct Predictions
{overallStats.accuracy}%
Overall Accuracy
{currentStreak.count}{currentStreak.type}
Current Streak
{/* Prediction Streak Timeline */}
Prediction Streak Timeline
Last {streakData.length} predictions
{streakData.map((item, idx) => (
e.target.style.transform = 'scale(1.2)'}
onMouseLeave={(e) => e.target.style.transform = 'scale(1)'}
>
{item.result}
))}
{/* Charts Grid - Row 1 */}
{/* Radar Chart - Model Dimensions */}
{/* ELO vs Win% Scatter */}
ELO Rating vs Win %
Bubble size = Games played
[value, name]}
labelFormatter={(label) => `Team: ${eloScatter.find(t => t.elo === label)?.team || ''}`}
/>
{eloScatter.map((entry, index) => (
|
))}
● East
● West
{/* Charts Grid - Row 2 */}
{/* Accuracy Trend */}
{/* Home vs Away Performance */}
Home vs Away Accuracy
{/* Home Bar */}
{homeAwaySplit.home?.accuracy}%
🏠 Home
{homeAwaySplit.home?.correct}/{homeAwaySplit.home?.total}
{/* Away Bar */}
{homeAwaySplit.away?.accuracy}%
✈️ Away
{homeAwaySplit.away?.correct}/{homeAwaySplit.away?.total}
{/* Mini Heatmap - Top Matchups */}
{topMatchups.length > 0 && (
Top Team Matchup Predictions
Home team win probability
{topMatchups.map((matchup, idx) => {
const prob = matchup.homeWinProb
const hue = prob > 50 ? 120 : 0 // Green for >50%, Red for <50%
const saturation = Math.abs(prob - 50) * 2 // More intense as further from 50
const lightness = 35
return (
e.currentTarget.style.transform = 'scale(1.05)'}
onMouseLeave={(e) => e.currentTarget.style.transform = 'scale(1)'}
>
{matchup.away} @ {matchup.home}
{prob}%
)
})}
)}
{/* Charts Grid - Row 3 */}
{/* Confidence Distribution */}
Confidence Distribution
`${(percent * 100).toFixed(0)}%`}
labelLine={false}
>
{confidenceDistribution.map((entry, index) => (
|
))}
{/* Calibration Chart */}
Prediction Calibration
Predicted vs Actual Win %
{/* Team Accuracy Bar */}
Accuracy by Team
[`${value}%`, 'Accuracy']}
/>
{/* Recent Predictions Table */}
Recent Predictions
| Date |
Matchup |
Prediction |
Confidence |
Result |
{(data?.recent_predictions || []).map((pred, idx) => (
| {pred.date} |
{pred.matchup} |
{pred.prediction} |
70 ? '#4ade80' : pred.confidence > 60 ? '#facc15' : '#f87171'
}}>
{pred.confidence}%
|
{pred.correct ? '✓' : '✗'}
|
))}
)
}
const tableHeaderStyle = {
textAlign: 'left',
padding: 'var(--space-3) var(--space-4)',
fontSize: '0.75rem',
fontWeight: '600',
textTransform: 'uppercase',
letterSpacing: '0.05em',
color: 'var(--text-muted)'
}
const tableCellStyle = {
padding: 'var(--space-3) var(--space-4)',
fontSize: '0.875rem'
}
export default Analytics