"use client"; import { motion, useSpring, useTransform, animate } from "framer-motion"; import Link from "next/link"; import { useEffect, useRef, useState } from "react"; type Stats = { raisedUsd: number; goalUsd: number; donationCount: number; uniqueDonors: number; }; function AnimatedNumber({ value, prefix = "", suffix = "" }: { value: number; prefix?: string; suffix?: string }) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const ctrl = animate(0, value, { duration: 1.2, ease: "easeOut", onUpdate: (v) => { el.textContent = prefix + Math.round(v).toLocaleString() + suffix; }, }); return () => ctrl.stop(); }, [value, prefix, suffix]); return {prefix}0{suffix}; } export function ProgressSection() { const [stats, setStats] = useState(null); useEffect(() => { let alive = true; const load = async () => { try { const res = await fetch("/api/public/stats", { cache: "no-store" }); if (!res.ok) return; const data = await res.json(); if (alive) setStats(data); } catch { /* silent */ } }; load(); const id = setInterval(load, 30_000); return () => { alive = false; clearInterval(id); }; }, []); const goal = stats?.goalUsd ?? 250000; const pct = stats ? Math.min(100, Math.round((stats.raisedUsd / goal) * 100)) : 0; const spring = useSpring(0, { stiffness: 80, damping: 18 }); const width = useTransform(spring, (v) => `${v}%`); useEffect(() => { spring.set(pct); }, [pct, spring]); return (
{/* Ambient glow */}
Grassroots meter Momentum you can{" "} see Every dollar is a vote for whose voice counts. These numbers are the same ones on our public board — refreshed on a short timer so the homepage always feels current.
Add your gift → | Open full totals board →
{/* Stat cards */}
{[ { label: "Raised", value: stats?.raisedUsd ?? 0, display: stats ? `$${stats.raisedUsd.toLocaleString(undefined, { maximumFractionDigits: 0 })}` : "—", color: "from-sky-500/20 to-transparent", glow: "border-sky-500/20", }, { label: "Donations", value: stats?.donationCount ?? 0, display: stats?.donationCount?.toLocaleString() ?? "—", color: "from-indigo-500/20 to-transparent", glow: "border-indigo-500/20", }, { label: "Supporters", value: stats?.uniqueDonors ?? 0, display: stats?.uniqueDonors?.toLocaleString() ?? "—", color: "from-fuchsia-500/20 to-transparent", glow: "border-fuchsia-500/20", }, ].map(({ label, display, color, glow }, i) => (

{label}

{display}

))}
{/* Progress bar */}
$0 Goal: ${goal.toLocaleString()}
{/* Glow track */}
{/* Shimmer sweep on bar */}
{/* Leading dot */}

{stats ? `${pct}% to goal` : "Loading…"}

); }