import { useState, useEffect } from 'react'; import { api, UserStats, MyTip } from '../api/client'; import StatsRing from '../components/StatsRing'; import styles from './ProfilePage.module.css'; function initials(name: string) { return name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2); } function mostCommonTip(tips: MyTip[]): string { const counts: Record = {}; for (const t of tips) { const key = `${t.tip_home}:${t.tip_away}`; counts[key] = (counts[key] ?? 0) + 1; } let best = ''; let max = 0; for (const [key, count] of Object.entries(counts)) { if (count > max) { max = count; best = key; } } return best ? `${best} (${max}x getippt)` : '—'; } function homeWinPct(tips: MyTip[]): number { if (!tips.length) return 0; const homeWins = tips.filter(t => t.tip_home > t.tip_away).length; return Math.round((homeWins / tips.length) * 100); } export default function ProfilePage() { const [stats, setStats] = useState(null); const [tips, setTips] = useState([]); const [loading, setLoading] = useState(true); const [teamEdit, setTeamEdit] = useState(false); const [teamValue, setTeamValue] = useState(''); const [teamSaving, setTeamSaving] = useState(false); const [teamMsg, setTeamMsg] = useState<{ ok: boolean; text: string } | null>(null); useEffect(() => { Promise.all([ api.getMyStats(), api.getMyTips(), ]).then(([s, t]) => { setStats(s); setTeamValue(s.team ?? ''); setTips(t.tips); }).finally(() => setLoading(false)); }, []); const saveTeam = async () => { if (!teamValue.trim()) return; setTeamSaving(true); setTeamMsg(null); try { const res = await api.updateTeam(teamValue); setStats(prev => prev ? { ...prev, team: res.team } : prev); setTeamValue(res.team); setTeamEdit(false); setTeamMsg({ ok: true, text: 'Team gespeichert' }); } catch (e) { setTeamMsg({ ok: false, text: (e as Error).message }); } finally { setTeamSaving(false); } }; if (loading) return
; if (!stats) return
Profil nicht verfügbar.
; const evaluatedTips = tips.filter(t => t.points !== null); const recentTips = evaluatedTips.slice(0, 10); const favTip = mostCommonTip(tips); const homePct = homeWinPct(tips); function pointBadgeClass(points: number | null) { if (points === null) return ''; if (points >= 3) return styles.badgeExact; if (points >= 1) return styles.badgeTendency; return styles.badgeWrong; } function pointLabel(points: number | null) { if (points === null) return ''; if (points >= 3) return `${points} Pkt ✓✓`; if (points >= 1) return `${points} Pkt ✓`; return `${points} Pkt ✗`; } return (
{/* Header card */}
{initials(stats.fullName)}

{stats.fullName}

{stats.rank &&
🏆 Platz {stats.rank}
} {/* Team field */}
{teamEdit ? (
setTeamValue(e.target.value)} placeholder="z. B. Vertrieb Süd" maxLength={80} autoFocus onKeyDown={e => { if (e.key === 'Enter') saveTeam(); if (e.key === 'Escape') setTeamEdit(false); }} />
) : ( )}
{teamMsg && (
{teamMsg.text}
)}
{/* Stats donut ring */}

Tipp-Statistik

{stats.accuracy > 0 && (
Trefferquote {stats.accuracy}%
)}
{/* Tip history */} {recentTips.length > 0 && (

Letzte Tipps

    {recentTips.map((tip, i) => (
  • {tip.home_team_short} vs {tip.away_team_short} Tipp: {tip.tip_home}:{tip.tip_away} {pointLabel(tip.points)}
  • ))}
)} {/* Fun stats */} {tips.length > 0 && (

Fun Facts

🎯 Lieblings-Tipp {favTip}
🏠 Heimsiege getippt {homePct}%
📊 Tipps abgegeben {stats.tipsCount}
)}
); }