Spaces:
Running
Running
import React, { useState, useEffect } from 'react'; | |
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts'; | |
import { Loader2, TrendingUp, Brain, Cpu, MessageSquare } from 'lucide-react'; | |
import { Link } from 'react-router-dom'; | |
import api from '../services/api'; | |
interface AlgorithmStat { | |
algorithm: string; | |
name: string; | |
count: number; | |
} | |
interface DashboardStats { | |
classical_ml: AlgorithmStat[]; | |
deep_learning: AlgorithmStat[]; | |
llms: AlgorithmStat[]; | |
} | |
const Dashboard: React.FC = () => { | |
const [stats, setStats] = useState<DashboardStats | null>(null); | |
const [loading, setLoading] = useState(true); | |
const [error, setError] = useState<string | null>(null); | |
useEffect(() => { | |
fetchDashboardStats(); | |
}, []); | |
const fetchDashboardStats = async () => { | |
try { | |
setLoading(true); | |
const response = await api.get('/search/dashboard-stats'); | |
setStats(response.data); | |
} catch (err) { | |
setError('Failed to load dashboard statistics'); | |
} finally { | |
setLoading(false); | |
} | |
}; | |
if (loading) { | |
return ( | |
<div className="flex justify-center items-center h-64"> | |
<Loader2 className="h-8 w-8 animate-spin text-blue-600" /> | |
<span className="ml-2 text-gray-600">Loading dashboard...</span> | |
</div> | |
); | |
} | |
if (error) { | |
return ( | |
<div className="text-center text-red-600 p-8"> | |
<p>{error}</p> | |
<button | |
onClick={fetchDashboardStats} | |
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700" | |
> | |
Retry | |
</button> | |
</div> | |
); | |
} | |
if (!stats) return null; | |
const combinedData = [ | |
...stats.classical_ml.map(item => ({ ...item, category: 'Classical ML' })), | |
...stats.deep_learning.map(item => ({ ...item, category: 'Deep Learning' })), | |
...stats.llms.map(item => ({ ...item, category: 'LLMs' })) | |
].sort((a, b) => b.count - a.count).slice(0, 12); | |
const categoryTotals = [ | |
{ | |
name: 'Classical ML', | |
value: stats.classical_ml.reduce((sum, item) => sum + item.count, 0), | |
color: '#3B82F6' | |
}, | |
{ | |
name: 'Deep Learning', | |
value: stats.deep_learning.reduce((sum, item) => sum + item.count, 0), | |
color: '#8B5CF6' | |
}, | |
{ | |
name: 'LLMs', | |
value: stats.llms.reduce((sum, item) => sum + item.count, 0), | |
color: '#10B981' | |
} | |
]; | |
const totalPapers = categoryTotals.reduce((sum, cat) => sum + cat.value, 0); | |
return ( | |
<div className="space-y-8"> | |
<div className="text-center"> | |
<h1 className="text-4xl font-bold text-gray-900 mb-2"> | |
AI Algorithms in Medical Research | |
</h1> | |
<p className="text-xl text-gray-600"> | |
Explore the landscape of AI algorithms used in PubMed medical papers | |
</p> | |
</div> | |
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> | |
<div className="bg-white p-6 rounded-lg shadow-lg"> | |
<div className="flex items-center"> | |
<TrendingUp className="h-8 w-8 text-blue-600" /> | |
<div className="ml-4"> | |
<p className="text-sm font-medium text-gray-600">Total Papers</p> | |
<p className="text-2xl font-bold text-gray-900">{totalPapers.toLocaleString()}</p> | |
</div> | |
</div> | |
</div> | |
<div className="bg-white p-6 rounded-lg shadow-lg"> | |
<div className="flex items-center"> | |
<Brain className="h-8 w-8 text-blue-600" /> | |
<div className="ml-4"> | |
<p className="text-sm font-medium text-gray-600">Classical ML Papers</p> | |
<p className="text-2xl font-bold text-gray-900">{categoryTotals[0].value.toLocaleString()}</p> | |
</div> | |
</div> | |
</div> | |
<div className="bg-white p-6 rounded-lg shadow-lg"> | |
<div className="flex items-center"> | |
<Cpu className="h-8 w-8 text-purple-600" /> | |
<div className="ml-4"> | |
<p className="text-sm font-medium text-gray-600">Deep Learning Papers</p> | |
<p className="text-2xl font-bold text-gray-900">{categoryTotals[1].value.toLocaleString()}</p> | |
</div> | |
</div> | |
</div> | |
<div className="bg-white p-6 rounded-lg shadow-lg"> | |
<div className="flex items-center"> | |
<MessageSquare className="h-8 w-8 text-green-600" /> | |
<div className="ml-4"> | |
<p className="text-sm font-medium text-gray-600">LLMs Papers</p> | |
<p className="text-2xl font-bold text-gray-900">{categoryTotals[2].value.toLocaleString()}</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> | |
<div className="bg-white p-6 rounded-lg shadow-lg"> | |
<h2 className="text-xl font-bold text-gray-900 mb-4">Algorithm Distribution</h2> | |
<ResponsiveContainer width="100%" height={300}> | |
<PieChart> | |
<Pie | |
data={categoryTotals} | |
cx="50%" | |
cy="50%" | |
labelLine={false} | |
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(1)}%`} | |
outerRadius={80} | |
fill="#8884d8" | |
dataKey="value" | |
> | |
{categoryTotals.map((entry, index) => ( | |
<Cell key={`cell-${index}`} fill={entry.color} /> | |
))} | |
</Pie> | |
<Tooltip /> | |
</PieChart> | |
</ResponsiveContainer> | |
</div> | |
<div className="bg-white p-6 rounded-lg shadow-lg"> | |
<h2 className="text-xl font-bold text-gray-900 mb-4">Top Algorithms by Usage</h2> | |
<ResponsiveContainer width="100%" height={300}> | |
<BarChart data={combinedData}> | |
<CartesianGrid strokeDasharray="3 3" /> | |
<XAxis | |
dataKey="name" | |
angle={-45} | |
textAnchor="end" | |
height={80} | |
fontSize={12} | |
/> | |
<YAxis /> | |
<Tooltip /> | |
<Legend /> | |
<Bar | |
dataKey="count" | |
fill="#3B82F6" | |
name="Paper Count" | |
/> | |
</BarChart> | |
</ResponsiveContainer> | |
</div> | |
</div> | |
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
<div className="bg-white p-6 rounded-lg shadow-lg"> | |
<h2 className="text-xl font-bold text-gray-900 mb-4">Classical ML Algorithms</h2> | |
<div className="space-y-3"> | |
{stats.classical_ml.slice(0, 8).map((algo, index) => ( | |
<div key={algo.algorithm} className="flex justify-between items-center p-3 bg-gray-50 rounded"> | |
<div> | |
<Link | |
to={`/algorithm/${algo.algorithm}`} | |
className="font-medium text-gray-900 hover:text-blue-600 transition-colors" | |
> | |
{algo.name} | |
</Link> | |
</div> | |
<span className="text-blue-600 font-bold">{algo.count.toLocaleString()}</span> | |
</div> | |
))} | |
</div> | |
</div> | |
<div className="bg-white p-6 rounded-lg shadow-lg"> | |
<h2 className="text-xl font-bold text-gray-900 mb-4">Deep Learning Algorithms</h2> | |
<div className="space-y-3"> | |
{stats.deep_learning.slice(0, 8).map((algo, index) => ( | |
<div key={algo.algorithm} className="flex justify-between items-center p-3 bg-gray-50 rounded"> | |
<div> | |
<Link | |
to={`/algorithm/${algo.algorithm}`} | |
className="font-medium text-gray-900 hover:text-purple-600 transition-colors" | |
> | |
{algo.name} | |
</Link> | |
</div> | |
<span className="text-purple-600 font-bold">{algo.count.toLocaleString()}</span> | |
</div> | |
))} | |
</div> | |
</div> | |
<div className="bg-white p-6 rounded-lg shadow-lg"> | |
<h2 className="text-xl font-bold text-gray-900 mb-4">Large Language Models</h2> | |
<div className="space-y-3"> | |
{stats.llms.slice(0, 8).map((algo, index) => ( | |
<div key={algo.algorithm} className="flex justify-between items-center p-3 bg-gray-50 rounded"> | |
<div> | |
<Link | |
to={`/algorithm/${algo.algorithm}`} | |
className="font-medium text-gray-900 hover:text-green-600 transition-colors" | |
> | |
{algo.name} | |
</Link> | |
</div> | |
<span className="text-green-600 font-bold">{algo.count.toLocaleString()}</span> | |
</div> | |
))} | |
</div> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
export default Dashboard; |