first commit
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
interface ScoreCardProps {
|
||||
compositeScore: number | null;
|
||||
dimensions: { dimension: string; score: number }[];
|
||||
}
|
||||
|
||||
function scoreColor(score: number): string {
|
||||
if (score > 70) return 'text-emerald-400';
|
||||
if (score >= 40) return 'text-amber-400';
|
||||
return 'text-red-400';
|
||||
}
|
||||
|
||||
function ringGradient(score: number): string {
|
||||
if (score > 70) return '#10b981';
|
||||
if (score >= 40) return '#f59e0b';
|
||||
return '#ef4444';
|
||||
}
|
||||
|
||||
function barGradient(score: number): string {
|
||||
if (score > 70) return 'from-emerald-500 to-emerald-400';
|
||||
if (score >= 40) return 'from-amber-500 to-amber-400';
|
||||
return 'from-red-500 to-red-400';
|
||||
}
|
||||
|
||||
function ScoreRing({ score }: { score: number }) {
|
||||
const radius = 36;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
const clamped = Math.max(0, Math.min(100, score));
|
||||
const offset = circumference - (clamped / 100) * circumference;
|
||||
const color = ringGradient(score);
|
||||
|
||||
return (
|
||||
<div className="relative inline-flex items-center justify-center">
|
||||
<svg width="88" height="88" className="-rotate-90">
|
||||
<circle cx="44" cy="44" r={radius} fill="none" strokeWidth="6" className="stroke-white/[0.06]" />
|
||||
<circle
|
||||
cx="44" cy="44" r={radius} fill="none" strokeWidth="6" strokeLinecap="round"
|
||||
strokeDasharray={circumference} strokeDashoffset={offset}
|
||||
stroke={color}
|
||||
style={{ filter: `drop-shadow(0 0 8px ${color}40)`, transition: 'all 0.6s ease' }}
|
||||
/>
|
||||
</svg>
|
||||
<span className={`absolute text-lg font-bold ${scoreColor(score)}`}>
|
||||
{Math.round(score)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScoreCard({ compositeScore, dimensions }: ScoreCardProps) {
|
||||
return (
|
||||
<div className="glass p-5">
|
||||
<div className="flex items-center gap-4">
|
||||
{compositeScore !== null ? (
|
||||
<ScoreRing score={compositeScore} />
|
||||
) : (
|
||||
<div className="flex h-[88px] w-[88px] items-center justify-center text-sm text-gray-500">N/A</div>
|
||||
)}
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 uppercase tracking-wider">Composite Score</p>
|
||||
<p className={`text-2xl font-bold ${compositeScore !== null ? scoreColor(compositeScore) : 'text-gray-500'}`}>
|
||||
{compositeScore !== null ? Math.round(compositeScore) : '—'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dimensions.length > 0 && (
|
||||
<div className="mt-5 space-y-2.5">
|
||||
<p className="text-[10px] font-medium uppercase tracking-widest text-gray-500">Dimensions</p>
|
||||
{dimensions.map((d) => (
|
||||
<div key={d.dimension} className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-300 capitalize">{d.dimension}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-1.5 w-20 rounded-full bg-white/[0.06] overflow-hidden">
|
||||
<div
|
||||
className={`h-1.5 rounded-full bg-gradient-to-r ${barGradient(d.score)} transition-all duration-500`}
|
||||
style={{ width: `${Math.max(0, Math.min(100, d.score))}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className={`w-8 text-right font-medium text-xs ${scoreColor(d.score)}`}>
|
||||
{Math.round(d.score)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user