// Leaderboard — wired to PrimaAPI const Leaderboard = ({ currentUser }) => { const [animated, setAnimated] = React.useState(false); const [entries, setEntries] = React.useState([]); const [loading, setLoading] = React.useState(true); React.useEffect(() => { PrimaAPI.getLeaderboard(20) .then(data => { // data = { entries: [...] } const list = (data && data.entries) || []; setEntries(list); }) .catch(() => setEntries([])) .finally(() => setLoading(false)); }, []); React.useEffect(() => { if (!loading && entries.length > 0) { const t = setTimeout(() => setAnimated(true), 120); return () => clearTimeout(t); } }, [loading, entries.length]); // Map API entries to display format const board = entries.map((e, i) => ({ rank: e.rank || i + 1, name: e.full_name || "—", role: e.designation || "—", uploads: e.total_uploads || 0, score: e.score || 0, you: currentUser && e.user_id === currentUser.id, })); const max = board.length > 0 ? board[0].score : 1; // Derive current user stats from the board const myEntry = board.find(e => e.you); const myRank = myEntry ? myEntry.rank : "—"; const myScore = myEntry ? myEntry.score : "—"; return (
Contribution scores

Leaderboard

Scores reward upload volume and file quality — manager-approved and best-in-class artifacts weight higher than raw uploads.

Your rank
#{myRank} of {board.length}
Based on current scores
Your score
{myScore}
Lifetime contribution
Total uploads
{myEntry ? myEntry.uploads : "—"}
Artifacts contributed
{loading ? (
Loading…
) : board.length === 0 ? (

No scores yet

Upload artifacts to start building your contribution score.

) : (

Contribution leaderboard

{board.map(r => { const pct = (r.score / max) * 100; return (
{r.rank.toString().padStart(2, "0")}
{r.name}{r.you && You}
{r.role}
{r.uploads} files
{r.score}
); })}
)}
How scoring works
Each upload starts at 5 points. Manager-approved doubles the score. Items flagged Best-in-Class add another 50%. Filling all metadata fields adds a final completeness bonus.
Score multipliers
Manager Approved×2.0
Best-in-Class×1.5
Reusable×1.2
Full metadata+10%
); }; Object.assign(window, { Leaderboard });