import { useEffect, useState } from 'react'; import { apiGet } from '../api.js'; import { SkeletonRows } from '../components/Skeleton.jsx'; import { IconTrophy, IconLevel } from '../icons.jsx'; import EmptyState from '../components/EmptyState.jsx'; import { useT, useFmt } from '../i18n.jsx'; /** Muss der Formel in src/bot/levels.js entsprechen */ function xpForLevel(level) { let total = 0; for (let n = 0; n < level; n++) total += 5 * n * n + 50 * n + 100; return total; } // Platz 1–3 bekommen einen Pokal in Gold/Silber/Bronze, danach schlicht die Nummer const MEDAL_COLORS = ['#f5c518', '#c0c4cc', '#c07a3e']; /** Nachrichten/Tag der letzten 30 Tage als Balken */ function ActivityChart({ stats }) { const { t } = useT(); const { zahl } = useFmt(); const byDay = new Map(stats.days.map((d) => [d.day, d])); const days = []; for (let i = 29; i >= 0; i--) { const key = new Date(Date.now() - i * 86400000).toISOString().slice(0, 10); days.push({ key, ...(byDay.get(key) ?? { messages: 0, joins: 0, leaves: 0 }) }); } const max = Math.max(1, ...days.map((d) => d.messages)); const total = days.reduce((s, d) => s + d.messages, 0); return (

{t('level.activity', zahl(total), stats.members)}

{days.map((d) => ( ))}
); } export default function Level() { const { t } = useT(); const { zahl } = useFmt(); const [rows, setRows] = useState(null); const [stats, setStats] = useState(null); const [error, setError] = useState(false); useEffect(() => { apiGet('/api/levels').then((d) => setRows(d.levels)).catch(() => setError(true)); apiGet('/api/serverstats').then(setStats).catch(() => {}); window.scrollTo(0, 0); }, []); return ( <>
LEVEL
{error &&

{t('level.error')}

} {!error && !rows && } {rows?.length === 0 && ( )} {rows?.length > 0 && (
{rows.map((r, i) => { const base = xpForLevel(r.level); const next = xpForLevel(r.level + 1); const pct = Math.floor(((r.xp - base) / (next - base)) * 100); const name = r.display_name || r.username || r.user_id; return (
{i < 3 && } {i + 1} {/* Ohne Bild bleibt der Anfangsbuchstabe — kein leeres Loch */} {r.avatar ? : {name.slice(0, 1).toUpperCase()}} {name}
{t('level.lv', r.level)} {zahl(r.xp)} XP
); })}
)} {stats && }
); }