Datavision / frontend /src /components /simulator /ComparisonTab.tsx
DataVision CI/CD Bot
release: clean production build for HuggingFace Space
09801ca
Raw
History Blame Contribute Delete
5.97 kB
import React, { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { useUserStore } from '@/store/userStore';
import apiService from '@/services/api';
import SimulatorChart from './SimulatorChart';
import { GitCompare, Download, ArrowUpRight, ArrowDownRight } from 'lucide-react';
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts';
const ComparisonTab: React.FC = () => {
const { isDark } = useUserStore();
const [scenarios, setScenarios] = useState<any[]>([]);
const [comparison, setComparison] = useState<any>(null);
const [loading, setLoading] = useState(true);
const cardBg = isDark ? 'rgba(255,255,255,0.03)' : '#ffffff';
const cardBorder = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)';
const textPrimary = isDark ? '#f8fafc' : '#0f172a';
const textMuted = isDark ? '#94a3b8' : '#64748b';
useEffect(() => {
const fetchData = async () => {
try {
const savedRes = await apiService.listScenarios();
const saved = savedRes.data || [];
setScenarios(saved);
const compRes = await apiService.compareScenarios(
saved.length > 0 ? saved.map((s: any) => s.id) : []
);
setComparison(compRes.data);
} catch (err) {
console.error(err);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <div className="flex items-center justify-center h-96"><div className="w-8 h-8 border-3 border-indigo-500 border-t-transparent rounded-full animate-spin" /></div>;
const comps = comparison?.comparisons || [];
const chartData = comps.map((c: any) => ({
name: c.name,
prediction: c.prediction || 0,
impact: c.impact || c.impact_percentage || 0,
confidence: c.confidence || 90,
}));
return (
<div className="space-y-5">
{/* Header */}
<div className="flex items-center justify-between">
<h3 className="text-sm font-bold flex items-center gap-2" style={{ color: textPrimary }}>
<GitCompare className="w-4 h-4 text-indigo-400" /> Scenario Comparison & Trade-off Analysis
</h3>
<button className="flex items-center gap-1.5 px-3 py-1.5 rounded-xl border text-xs hover:bg-white/5" style={{ borderColor: cardBorder, color: textMuted }}>
<Download className="w-3.5 h-3.5" /> Export Comparison
</button>
</div>
{/* Comparison Table */}
<div className="rounded-2xl border p-5 overflow-x-auto" style={{ background: cardBg, borderColor: cardBorder }}>
<table className="w-full text-left">
<thead>
<tr className="border-b" style={{ borderColor: cardBorder }}>
<th className="py-2.5 px-3 text-xs font-semibold" style={{ color: textMuted }}>Metric / Scenario</th>
{comps.map((c: any, i: number) => (
<th key={i} className="py-2.5 px-3 text-xs font-bold" style={{ color: textPrimary }}>
{c.name}
</th>
))}
</tr>
</thead>
<tbody>
<tr className="border-b" style={{ borderColor: cardBorder }}>
<td className="py-3 px-3 text-xs font-semibold" style={{ color: textMuted }}>Target Prediction</td>
{comps.map((c: any, i: number) => (
<td key={i} className="py-3 px-3 text-sm font-bold" style={{ color: '#6366f1' }}>
{c.formatted_prediction || c.prediction?.toLocaleString()}
</td>
))}
</tr>
<tr className="border-b" style={{ borderColor: cardBorder }}>
<td className="py-3 px-3 text-xs font-semibold" style={{ color: textMuted }}>Impact vs Baseline</td>
{comps.map((c: any, i: number) => {
const imp = c.impact || c.impact_percentage || 0;
return (
<td key={i} className="py-3 px-3">
<span className={`text-xs font-bold flex items-center gap-1 ${imp >= 0 ? 'text-emerald-500' : 'text-red-500'}`}>
{imp >= 0 ? <ArrowUpRight className="w-3.5 h-3.5" /> : <ArrowDownRight className="w-3.5 h-3.5" />}
{imp >= 0 ? '+' : ''}{imp.toFixed(1)}%
</span>
</td>
);
})}
</tr>
<tr className="border-b" style={{ borderColor: cardBorder }}>
<td className="py-3 px-3 text-xs font-semibold" style={{ color: textMuted }}>AI Confidence Score</td>
{comps.map((c: any, i: number) => (
<td key={i} className="py-3 px-3 text-xs font-bold text-emerald-500">
{(c.confidence || 90).toFixed(1)}%
</td>
))}
</tr>
</tbody>
</table>
</div>
{/* Grouped Comparison Chart */}
<div className="rounded-2xl border p-5" style={{ background: cardBg, borderColor: cardBorder }}>
<h3 className="text-sm font-bold mb-4" style={{ color: textPrimary }}>Target Metric Comparison</h3>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={chartData} margin={{ top: 10, right: 30, left: 0, bottom: 0 }}>
<CartesianGrid strokeDasharray="3 3" stroke={isDark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.04)'} vertical={false} />
<XAxis dataKey="name" tick={{ fill: textMuted, fontSize: 11 }} axisLine={false} tickLine={false} />
<YAxis tick={{ fill: textMuted, fontSize: 11 }} axisLine={false} tickLine={false} />
<Tooltip contentStyle={{ background: isDark ? '#1e1e2e' : '#fff', border: `1px solid ${cardBorder}`, borderRadius: 12 }} />
<Bar dataKey="prediction" name="Target Prediction" fill="#6366f1" radius={[6, 6, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
</div>
);
};
export default ComparisonTab;