first commit
Deploy / lint (push) Failing after 7s
Deploy / test (push) Has been skipped
Deploy / deploy (push) Has been skipped

This commit is contained in:
Dennis Thiessen
2026-02-20 17:31:01 +01:00
commit 61ab24490d
160 changed files with 17034 additions and 0 deletions
+89
View File
@@ -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>
);
}