feat: WM2026 Tippspiel - Initial Backend + Frontend

This commit is contained in:
Ronny Müller
2026-04-03 21:41:19 +02:00
commit 1c685b90a0
2507 changed files with 997210 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
import { useState, useEffect } from 'react';
import { api, LeaderboardEntry } from '../api/client';
import styles from './LeaderboardPage.module.css';
const MEDALS = ['🥇', '🥈', '🥉'];
export default function LeaderboardPage() {
const [entries, setEntries] = useState<LeaderboardEntry[]>([]);
const [myRank, setMyRank] = useState<number | null>(null);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
useEffect(() => {
api.getLeaderboard().then(res => {
setEntries(res.entries);
setMyRank(res.currentUserRank);
setTotal(res.totalParticipants);
}).finally(() => setLoading(false));
}, []);
if (loading) return (
<div className={styles.loading}><div className={styles.spinner} /></div>
);
return (
<div className={styles.page}>
<div className={styles.pageHeader}>
<h1 className={`font-display ${styles.title}`}>🏆 Rangliste</h1>
<div className={styles.meta}>
{total} Teilnehmer{myRank ? ` · Du: Platz ${myRank}` : ''}
</div>
</div>
{entries.length === 0 ? (
<div className={styles.empty}>
Noch keine Punkte vergeben. Spiele müssen erst abgeschlossen sein.
</div>
) : (
<div className={styles.list}>
{entries.map((entry, i) => (
<div key={entry.user_id} className={`card ${styles.row} ${i < 3 ? styles.topThree : ''}`}>
<div className={styles.rank}>
{i < 3 ? MEDALS[i] : <span className={styles.rankNum}>{entry.rank}</span>}
</div>
<div className={styles.name}>{entry.full_name}</div>
<div className={styles.stats}>
<span className={styles.stat}>
<span className={styles.statVal}>{entry.exact_count}</span>
<span className={styles.statLbl}>🎯</span>
</span>
<span className={styles.stat}>
<span className={styles.statVal}>{entry.tendency_count}</span>
<span className={styles.statLbl}></span>
</span>
<span className={styles.stat}>
<span className={styles.statVal}>{entry.tips_count}</span>
<span className={styles.statLbl}>Tipps</span>
</span>
</div>
<div className={`${styles.points} ${i === 0 ? styles.gold : i === 1 ? styles.silver : i === 2 ? styles.bronze : ''}`}>
{entry.total_points} <span className={styles.ptLabel}>Pkt</span>
</div>
</div>
))}
</div>
)}
</div>
);
}