import { useEffect, useState } from 'react'; import { api, type SpaceStats, type StatsLeader } from '../../../api/client'; import { Avatar } from '../../ui/Avatar'; import { useT, type TranslationKey } from '../../../i18n'; interface StatsPanelProps { spaceId: string; } const RANGES: { days: number; key: TranslationKey }[] = [ { days: 7, key: 'stats.range.7' }, { days: 30, key: 'stats.range.30' }, { days: 365, key: 'stats.range.365' }, ]; function Leaderboard({ title, rows, format, }: { title: string; rows: StatsLeader[]; format: (value: number) => string; }) { // The bar is relative to the leader, not to the total: with five people the // share of a total is tiny and every bar looks the same. const max = rows.length > 0 ? Math.max(...rows.map((r) => r.value)) : 0; return (

{title}

); } export function StatsPanel({ spaceId }: StatsPanelProps) { const t = useT(); const [days, setDays] = useState(30); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { let cancelled = false; setLoading(true); api.stats.space(spaceId, days) .then((data) => { if (!cancelled) setStats(data); }) .catch(() => { if (!cancelled) setStats(null); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [spaceId, days]); const formatDuration = (ms: number) => { const minutes = Math.round(ms / 60000); const hours = Math.floor(minutes / 60); return hours > 0 ? t('stats.hours', { hours, minutes: minutes % 60 }) : t('stats.minutes', { minutes }); }; const isEmpty = !stats || (stats.voice.length === 0 && stats.messages.length === 0); return (

{t('stats.title')}

{RANGES.map((range) => ( ))}
{loading ? (
{Array.from({ length: 4 }).map((_, i) => (
))}
) : isEmpty ? (

{t('stats.empty')}

) : (
{stats.voice.length > 0 && (

{t('stats.total.voice', { value: formatDuration(stats.totals.voiceMs) })}

)} {stats.messages.length > 0 && (
String(value)} />

{t('stats.total.messages', { value: stats.totals.messages })}

)}
)}

{t('stats.note')}

); }