203 lines
6.1 KiB
TypeScript
203 lines
6.1 KiB
TypeScript
'use client';
|
|
|
|
import Card from '../ui/Card';
|
|
|
|
interface RatingBreakdownProps {
|
|
skillRating?: number;
|
|
safetyRating?: number;
|
|
sportsmanshipRating?: number;
|
|
}
|
|
|
|
export default function RatingBreakdown({
|
|
skillRating = 1450,
|
|
safetyRating = 92,
|
|
sportsmanshipRating = 4.8
|
|
}: RatingBreakdownProps) {
|
|
return (
|
|
<div className="space-y-6">
|
|
<Card>
|
|
<h3 className="text-lg font-semibold text-white mb-6">Rating Components</h3>
|
|
|
|
<div className="space-y-6">
|
|
<RatingComponent
|
|
label="Skill Rating"
|
|
value={skillRating}
|
|
maxValue={2000}
|
|
color="primary-blue"
|
|
description="Based on race results, competition strength, and consistency"
|
|
breakdown={[
|
|
{ label: 'Race Results', percentage: 60 },
|
|
{ label: 'Competition Quality', percentage: 25 },
|
|
{ label: 'Consistency', percentage: 15 }
|
|
]}
|
|
/>
|
|
|
|
<RatingComponent
|
|
label="Safety Rating"
|
|
value={safetyRating}
|
|
maxValue={100}
|
|
color="green-400"
|
|
suffix="%"
|
|
description="Reflects incident-free racing and clean overtakes"
|
|
breakdown={[
|
|
{ label: 'Incident Rate', percentage: 70 },
|
|
{ label: 'Clean Overtakes', percentage: 20 },
|
|
{ label: 'Position Awareness', percentage: 10 }
|
|
]}
|
|
/>
|
|
|
|
<RatingComponent
|
|
label="Sportsmanship"
|
|
value={sportsmanshipRating}
|
|
maxValue={5}
|
|
color="warning-amber"
|
|
suffix="/5"
|
|
description="Community feedback on racing behavior and fair play"
|
|
breakdown={[
|
|
{ label: 'Peer Reviews', percentage: 50 },
|
|
{ label: 'Fair Racing', percentage: 30 },
|
|
{ label: 'Team Play', percentage: 20 }
|
|
]}
|
|
/>
|
|
</div>
|
|
</Card>
|
|
|
|
<Card>
|
|
<h3 className="text-lg font-semibold text-white mb-4">Rating History</h3>
|
|
|
|
<div className="space-y-3">
|
|
<HistoryItem
|
|
date="November 2024"
|
|
skillChange={+15}
|
|
safetyChange={+2}
|
|
sportsmanshipChange={0}
|
|
/>
|
|
<HistoryItem
|
|
date="October 2024"
|
|
skillChange={+28}
|
|
safetyChange={-1}
|
|
sportsmanshipChange={+0.1}
|
|
/>
|
|
<HistoryItem
|
|
date="September 2024"
|
|
skillChange={-12}
|
|
safetyChange={+3}
|
|
sportsmanshipChange={0}
|
|
/>
|
|
</div>
|
|
</Card>
|
|
|
|
<Card className="bg-charcoal-200/50 border-primary-blue/30">
|
|
<div className="flex items-center gap-3 mb-3">
|
|
<div className="text-2xl">📈</div>
|
|
<h3 className="text-lg font-semibold text-white">Rating Insights</h3>
|
|
</div>
|
|
<ul className="space-y-2 text-sm text-gray-400">
|
|
<li className="flex items-start gap-2">
|
|
<span className="text-green-400 mt-0.5">✓</span>
|
|
<span>Strong safety rating - keep up the clean racing!</span>
|
|
</li>
|
|
<li className="flex items-start gap-2">
|
|
<span className="text-warning-amber mt-0.5">→</span>
|
|
<span>Skill rating improving - competitive against higher-rated drivers</span>
|
|
</li>
|
|
<li className="flex items-start gap-2">
|
|
<span className="text-primary-blue mt-0.5">i</span>
|
|
<span>Complete more races to stabilize your ratings</span>
|
|
</li>
|
|
</ul>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function RatingComponent({
|
|
label,
|
|
value,
|
|
maxValue,
|
|
color,
|
|
suffix = '',
|
|
description,
|
|
breakdown
|
|
}: {
|
|
label: string;
|
|
value: number;
|
|
maxValue: number;
|
|
color: string;
|
|
suffix?: string;
|
|
description: string;
|
|
breakdown: { label: string; percentage: number }[];
|
|
}) {
|
|
const percentage = (value / maxValue) * 100;
|
|
|
|
return (
|
|
<div>
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-white font-medium">{label}</span>
|
|
<span className={`text-2xl font-bold text-${color}`}>
|
|
{value}{suffix}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="w-full bg-deep-graphite rounded-full h-2 mb-3">
|
|
<div
|
|
className={`bg-${color} rounded-full h-2 transition-all duration-500`}
|
|
style={{ width: `${percentage}%` }}
|
|
/>
|
|
</div>
|
|
|
|
<p className="text-xs text-gray-400 mb-3">{description}</p>
|
|
|
|
<div className="space-y-1">
|
|
{breakdown.map((item, index) => (
|
|
<div key={index} className="flex items-center justify-between text-xs">
|
|
<span className="text-gray-500">{item.label}</span>
|
|
<span className="text-gray-400">{item.percentage}%</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function HistoryItem({
|
|
date,
|
|
skillChange,
|
|
safetyChange,
|
|
sportsmanshipChange
|
|
}: {
|
|
date: string;
|
|
skillChange: number;
|
|
safetyChange: number;
|
|
sportsmanshipChange: number;
|
|
}) {
|
|
const formatChange = (value: number) => {
|
|
if (value === 0) return '—';
|
|
return value > 0 ? `+${value}` : `${value}`;
|
|
};
|
|
|
|
const getChangeColor = (value: number) => {
|
|
if (value === 0) return 'text-gray-500';
|
|
return value > 0 ? 'text-green-400' : 'text-red-400';
|
|
};
|
|
|
|
return (
|
|
<div className="flex items-center justify-between p-3 rounded bg-deep-graphite border border-charcoal-outline">
|
|
<span className="text-white text-sm">{date}</span>
|
|
<div className="flex items-center gap-4 text-xs">
|
|
<div className="text-center">
|
|
<div className="text-gray-500 mb-1">Skill</div>
|
|
<div className={getChangeColor(skillChange)}>{formatChange(skillChange)}</div>
|
|
</div>
|
|
<div className="text-center">
|
|
<div className="text-gray-500 mb-1">Safety</div>
|
|
<div className={getChangeColor(safetyChange)}>{formatChange(safetyChange)}</div>
|
|
</div>
|
|
<div className="text-center">
|
|
<div className="text-gray-500 mb-1">Sports</div>
|
|
<div className={getChangeColor(sportsmanshipChange)}>{formatChange(sportsmanshipChange)}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |