Spaces:
Running
Running
| import React, { useMemo } from 'react'; | |
| import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip as BarTooltip } from 'recharts'; | |
| export default function AnalyticsSection({ files }: { files: any[] }) { | |
| const stats = useMemo(() => { | |
| const total = files.length; | |
| if (!total) return { pieData: [], barData: [], summary: { total: 0, ai: 0, real: 0 } }; | |
| let aiCount = 0; | |
| let realCount = 0; | |
| let suspCount = 0; | |
| // Group by Date for Bar Chart | |
| const dateMap: Record<string, number> = {}; | |
| files.forEach(f => { | |
| const resStr = (f.result || "").toUpperCase(); | |
| if (resStr.includes('AI') || resStr.includes('FAKE')) aiCount++; | |
| else if (resStr.includes('REAL') || resStr.includes('AUTHENTIC')) realCount++; | |
| else suspCount++; | |
| // Use uploaded_at if available, otherwise just use a generic 'Today' bucket for mapping | |
| const dateObj = f.uploaded_at ? new Date(f.uploaded_at) : new Date(); | |
| const dateStr = dateObj.toLocaleDateString(); | |
| dateMap[dateStr] = (dateMap[dateStr] || 0) + 1; | |
| }); | |
| const pieData = [ | |
| { name: 'AI Detected', value: aiCount, color: '#ef4444' }, | |
| { name: 'Authentic', value: realCount, color: '#22c55e' }, | |
| { name: 'Suspicious', value: suspCount, color: '#eab308' } | |
| ].filter(d => d.value > 0); | |
| // Sort dates chronologically for Bar Chart | |
| const barData = Object.keys(dateMap).sort((a,b) => new Date(a).getTime() - new Date(b).getTime()).map(k => ({ | |
| date: k, | |
| uploads: dateMap[k] | |
| })); | |
| const aiPercent = Math.round((aiCount / total) * 100); | |
| const realPercent = Math.round((realCount / total) * 100); | |
| return { pieData, barData, summary: { total, ai: aiPercent, real: realPercent } }; | |
| }, [files]); | |
| if (!files.length) return null; | |
| return ( | |
| <div className="w-full relative z-10 mb-12"> | |
| <div className="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| {/* Quick Stats Column */} | |
| <div className="flex flex-col gap-6 lg:col-span-1"> | |
| <div className="p-6 bg-[var(--theme-text)]/5 border border-[var(--theme-border)] rounded-2xl dash-border hover:bg-[var(--theme-text)]/10 transition backdrop-blur-sm"> | |
| <h3 className="text-[var(--theme-text)]/60 uppercase tracking-widest text-[14px] mb-4">Total Analyses</h3> | |
| <p className="text-4xl font-bold font-mono tracking-tighter text-[var(--theme-text)]">{stats.summary.total}</p> | |
| </div> | |
| <div className="grid grid-cols-2 gap-6"> | |
| <div className="p-6 bg-[var(--theme-text)]/5 border border-[var(--theme-border)] rounded-2xl hover:border-red-500/50 transition backdrop-blur-sm group"> | |
| <h3 className="text-[var(--theme-text)]/40 uppercase tracking-widest text-[14px] mb-6.5">Synthetic</h3> | |
| <p className="text-2xl font-bold font-mono text-red-400 group-hover:text-red-300 transition">{stats.summary.ai}%</p> | |
| </div> | |
| <div className="p-6 bg-[var(--theme-text)]/5 border border-[var(--theme-border)] rounded-2xl hover:border-green-500/50 transition backdrop-blur-sm group"> | |
| <h3 className="text-[var(--theme-text)]/40 uppercase tracking-widest text-[14px] mb-6.5">Authentic</h3> | |
| <p className="text-2xl font-bold font-mono text-green-400 group-hover:text-green-300 transition">{stats.summary.real}%</p> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Charts Container */} | |
| <div className="lg:col-span-2 grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div className="p-6 bg-[var(--theme-text)]/5 border border-[var(--theme-border)] rounded-2xl backdrop-blur-sm flex flex-col h-[280px]"> | |
| <h3 className="text-[var(--theme-text)]/60 text-sm tracking-widest uppercase mb-4 text-center">Result Distribution</h3> | |
| <div className="flex-1 w-full min-h-0"> | |
| <ResponsiveContainer width="100%" height="100%"> | |
| <PieChart> | |
| <Pie data={stats.pieData} dataKey="value" cx="50%" cy="50%" innerRadius={60} outerRadius={80} paddingAngle={5} stroke="none"> | |
| {stats.pieData.map((e, index) => <Cell key={`cell-${index}`} fill={e.color} />)} | |
| </Pie> | |
| <Tooltip | |
| contentStyle={{ backgroundColor: 'var(--theme-bg)', backdropFilter: 'blur(12px)', borderColor: 'var(--theme-border)', borderRadius: '12px' }} | |
| itemStyle={{ color: 'var(--theme-text)' }} | |
| /> | |
| </PieChart> | |
| </ResponsiveContainer> | |
| </div> | |
| </div> | |
| <div className="p-6 bg-[var(--theme-text)]/5 border border-[var(--theme-border)] rounded-2xl backdrop-blur-sm flex flex-col h-[280px]"> | |
| <h3 className="text-[var(--theme-text)]/60 text-sm tracking-widest uppercase mb-4 text-center">Upload Volume</h3> | |
| <div className="flex-1 w-full min-h-0"> | |
| <ResponsiveContainer width="100%" height="100%"> | |
| <BarChart data={stats.barData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}> | |
| <XAxis dataKey="date" stroke="var(--theme-text)" opacity={0.5} fontSize={10} tickLine={false} axisLine={false} /> | |
| <YAxis stroke="var(--theme-text)" opacity={0.5} fontSize={10} tickLine={false} axisLine={false} allowDecimals={false} /> | |
| <BarTooltip | |
| cursor={{fill: 'var(--theme-border)'}} | |
| contentStyle={{ backgroundColor: 'var(--theme-bg)', backdropFilter: 'blur(12px)', borderColor: 'var(--theme-border)', borderRadius: '8px' }} | |
| /> | |
| <Bar dataKey="uploads" fill="var(--theme-text)" radius={[4, 4, 0, 0]} opacity={0.8} /> | |
| </BarChart> | |
| </ResponsiveContainer> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |