import React from "react"; import { motion } from "framer-motion"; import { getStats, type Stats } from "../api"; const REFRESH_MS = 45_000; export interface StatsSectionProps { /** Optional refetch trigger: when this value changes, stats are refetched. */ refetchTrigger?: number; } function AnimatedNumber({ value }: { value: number }) { return ( {value.toLocaleString()} ); } export function StatsSection({ refetchTrigger }: StatsSectionProps) { const [stats, setStats] = React.useState(null); const [loading, setLoading] = React.useState(true); const [refreshing, setRefreshing] = React.useState(false); const load = React.useCallback(async (userRefresh = false) => { if (userRefresh) setRefreshing(true); try { const s = await getStats(); setStats(s); } catch { setStats(null); } finally { setLoading(false); if (userRefresh) setRefreshing(false); } }, []); React.useEffect(() => { load(); const t = setInterval(load, REFRESH_MS); return () => clearInterval(t); }, [load, refetchTrigger]); if (loading && !stats) { return ( ); } if (!stats) return Stats unavailable; const n = (v: number | undefined | null) => Number(v ?? 0); const ts = (v: number | undefined | null) => new Date(Number(v ?? 0) * 1000).toLocaleString(); const dailyBudget = Number(stats.dailyBudgetSats) || 1; const budgetUsed = n(stats.spentTodaySats); const budgetPct = Math.min(100, (budgetUsed / dailyBudget) * 100); return ( Faucet stats sats Pool balance Daily budget: / sats Total paid sats Total claims Claims (24h) {stats.recentPayouts?.length > 0 && ( <> Recent payouts {stats.recentPayouts.slice(0, 10).map((p, i) => ( {p.pubkey_prefix ?? "…"} — {n(p.payout_sats).toLocaleString()} sats — {ts(p.claimed_at)} ))} > )} load(true)} disabled={refreshing} > ↻ Refresh ); }
Stats unavailable
sats
Pool balance