feat: WM2026 Tippspiel - Initial Backend + Frontend
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user