153 lines
6.3 KiB
TypeScript
153 lines
6.3 KiB
TypeScript
/**
|
|
* Prediction Stats Overview Component - STC Theme (Gold/Black)
|
|
*/
|
|
|
|
import { FC } from 'react';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Progress } from '@/components/ui/progress';
|
|
import type { PredictionStats } from '../types';
|
|
|
|
interface StatsOverviewProps {
|
|
stats: PredictionStats;
|
|
}
|
|
|
|
export const StatsOverview: FC<StatsOverviewProps> = ({ stats }) => {
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Main Stats */}
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
<Card className="bg-primary-800 border-primary-700">
|
|
<CardContent className="p-4">
|
|
<p className="text-gray-400 text-sm">Total Predictions</p>
|
|
<p className="text-3xl font-bold text-gold">{stats.totalPredictions}</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card className="bg-primary-800 border-primary-700">
|
|
<CardContent className="p-4">
|
|
<p className="text-gray-400 text-sm">Win Rate</p>
|
|
<p className={`text-3xl font-bold ${stats.winRate >= 50 ? 'text-green-400' : 'text-red-400'}`}>
|
|
{stats.winRate.toFixed(1)}%
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card className="bg-primary-800 border-primary-700">
|
|
<CardContent className="p-4">
|
|
<p className="text-gray-400 text-sm">Avg P&L</p>
|
|
<p className={`text-3xl font-bold ${stats.averagePnlPercent >= 0 ? 'text-green-400' : 'text-red-400'}`}>
|
|
{stats.averagePnlPercent >= 0 ? '+' : ''}{stats.averagePnlPercent.toFixed(2)}%
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card className="bg-primary-800 border-primary-700">
|
|
<CardContent className="p-4">
|
|
<p className="text-gray-400 text-sm">Win/Loss</p>
|
|
<p className="text-3xl font-bold">
|
|
<span className="text-green-400">{stats.winCount}</span>
|
|
<span className="text-gray-500">/</span>
|
|
<span className="text-red-400">{stats.lossCount}</span>
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Breakdown */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{/* By Type */}
|
|
<Card className="bg-primary-800 border-primary-700">
|
|
<CardHeader>
|
|
<CardTitle className="text-white">By Model Type</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
{stats.byType.map((item) => (
|
|
<div key={item.predictionType} className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<span className="text-gray-300">{item.predictionType}</span>
|
|
<span className="text-gray-500 text-sm">({item.total})</span>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Progress
|
|
value={item.winRate}
|
|
className="w-24 h-2 bg-primary-700"
|
|
indicatorClassName={item.winRate >= 50 ? 'bg-green-500' : 'bg-red-500'}
|
|
/>
|
|
<span className={`text-sm font-medium w-12 text-right ${
|
|
item.winRate >= 50 ? 'text-green-400' : 'text-red-400'
|
|
}`}>
|
|
{item.winRate.toFixed(0)}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* By Asset Class */}
|
|
<Card className="bg-primary-800 border-primary-700">
|
|
<CardHeader>
|
|
<CardTitle className="text-white">By Asset Class</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
{stats.byAssetClass.map((item) => (
|
|
<div key={item.assetClass} className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<span className="text-gray-300">{item.assetClass}</span>
|
|
<span className="text-gray-500 text-sm">({item.total})</span>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Progress
|
|
value={item.winRate}
|
|
className="w-24 h-2 bg-primary-700"
|
|
indicatorClassName={item.winRate >= 50 ? 'bg-green-500' : 'bg-red-500'}
|
|
/>
|
|
<span className={`text-sm font-medium w-12 text-right ${
|
|
item.winRate >= 50 ? 'text-green-400' : 'text-red-400'
|
|
}`}>
|
|
{item.winRate.toFixed(0)}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Outcome Distribution */}
|
|
<Card className="bg-primary-800 border-primary-700">
|
|
<CardHeader>
|
|
<CardTitle className="text-white">Outcome Distribution</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center gap-1 h-8">
|
|
<div
|
|
className="bg-green-500 h-full rounded-l transition-all"
|
|
style={{ width: `${(stats.winCount / stats.totalPredictions) * 100}%` }}
|
|
title={`Wins: ${stats.winCount}`}
|
|
/>
|
|
<div
|
|
className="bg-yellow-500 h-full transition-all"
|
|
style={{ width: `${(stats.partialCount / stats.totalPredictions) * 100}%` }}
|
|
title={`Partial: ${stats.partialCount}`}
|
|
/>
|
|
<div
|
|
className="bg-red-500 h-full transition-all"
|
|
style={{ width: `${(stats.lossCount / stats.totalPredictions) * 100}%` }}
|
|
title={`Losses: ${stats.lossCount}`}
|
|
/>
|
|
<div
|
|
className="bg-gray-600 h-full rounded-r transition-all"
|
|
style={{ width: `${(stats.expiredCount / stats.totalPredictions) * 100}%` }}
|
|
title={`Expired: ${stats.expiredCount}`}
|
|
/>
|
|
</div>
|
|
<div className="flex justify-between text-sm mt-2">
|
|
<span className="text-green-400">Wins: {stats.winCount}</span>
|
|
<span className="text-yellow-400">Partial: {stats.partialCount}</span>
|
|
<span className="text-red-400">Losses: {stats.lossCount}</span>
|
|
<span className="text-gray-400">Expired: {stats.expiredCount}</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|