feat: rich profile page with stats ring, tip history, fun stats

Donut chart showing exact/tendency/wrong distribution.
Scrollable tip history with point badges.
Fun stats: favorite tip, home win percentage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ronny
2026-04-11 19:14:41 +02:00
parent a304ceeff5
commit 2dc55f29db
4 changed files with 368 additions and 74 deletions
@@ -0,0 +1,35 @@
.ring {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px 0;
}
.svg {
width: 160px;
height: 160px;
}
.legend {
display: flex;
gap: 16px;
margin-top: 12px;
font-size: 0.8rem;
color: var(--text-secondary);
flex-wrap: wrap;
justify-content: center;
}
.legendItem {
display: flex;
align-items: center;
gap: 4px;
}
.dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
flex-shrink: 0;
}
+65
View File
@@ -0,0 +1,65 @@
import styles from './StatsRing.module.css';
interface Props {
exact: number;
tendency: number;
wrong: number;
total: number;
}
export default function StatsRing({ exact, tendency, wrong, total }: Props) {
const radius = 55;
const circumference = 2 * Math.PI * radius;
const all = exact + tendency + wrong || 1;
const segments = [
{ value: exact / all, color: 'var(--gold)', label: 'Exakt' },
{ value: tendency / all, color: 'var(--success)', label: 'Tendenz' },
{ value: wrong / all, color: 'var(--error)', label: 'Falsch' },
];
let offset = 0;
return (
<div className={styles.ring}>
<svg viewBox="0 0 140 140" className={styles.svg}>
{/* Background circle */}
<circle cx="70" cy="70" r={radius} fill="none" stroke="var(--surface-high)" strokeWidth="12" />
{segments.map((seg, i) => {
if (seg.value === 0) return null;
const dashArray = `${seg.value * circumference} ${circumference}`;
const rotation = offset * 360 - 90;
offset += seg.value;
return (
<circle
key={i}
cx="70" cy="70" r={radius}
fill="none"
stroke={seg.color}
strokeWidth="12"
strokeDasharray={dashArray}
transform={`rotate(${rotation} 70 70)`}
strokeLinecap="round"
/>
);
})}
<text x="70" y="65" textAnchor="middle" dominantBaseline="central"
fill="var(--text-primary)" fontSize="28" fontWeight="700">
{total}
</text>
<text x="70" y="85" textAnchor="middle"
fill="var(--text-secondary)" fontSize="11">
Punkte
</text>
</svg>
<div className={styles.legend}>
{segments.map((seg, i) => (
<span key={i} className={styles.legendItem}>
<span className={styles.dot} style={{ background: seg.color }} />
{seg.label}: {Math.round(seg.value * all)}
</span>
))}
</div>
</div>
);
}