Skip to content

[BUG] Leaderboard fabricates the visitor's contribution history, ignores real activity, and ships a point simulator to production #671

Description

@MOHITKOURAV01

Summary

The Contributor Leaderboard tells a first-time visitor they have already filed 2 reports, had 1 verified, answered 55 quizzes and earned 125 points. None of that happened. The numbers are a hardcoded seed, and the panel ranks the visitor against five fictional people using them.

1. A new user's stats are fabricated

const [userStats, setUserStats] = useState(() => {
  try {
    const saved = localStorage.getItem(USER_STORAGE_KEY);
    return saved
      ? JSON.parse(saved)
      : { name: "You (Guest)", points: 125, reports: 2, verified: 1, quizzes: 55, avatar: "🌟" };

Open the app for the first time and the card reads "1 Verified Reports • 2 Submissions • 55 Quizzes Answered" above a rank computed from those figures. It is presented as a record of the user's own activity, in the first person ("Your Rank", "(You)"), with no indication anything is a placeholder.

The repo has consistently treated invented values shown as measurements as bugs rather than acceptable placeholders — #499 (fabricated AQI 85), #544 (invented 25 µg/m³), #546 (missing readings reported as Good). This is the same thing pointed at the user's own contribution history, and it is arguably worse, because a user cannot sanity-check it against anything.

2. It ignores the activity the app actually records

The app already tracks real contribution signals, and the leaderboard reads none of them:

signal where it lives read by leaderboard
community reports pollution-hub-reports (CommunityHub) no
challenge points pollution_hub_total_points (ChallengesWidget) no
quiz completions QUIZ_COMPLETED on the event bus no
earned badges pollution-hub-achievements no

Leaderboard invented a fourth storage key, pollution-hub-user-points, that nothing else in the codebase writes to. Submit a real report in the Community Hub and your leaderboard total does not move. Earn 10 challenge points and it does not move. The one thing that does move it is item 3.

3. A point simulator ships to production

{/* Developer Action Simulator for Testing */}
<button onClick={() => addPoints(10, "report")}>+10 New Report</button>
<button onClick={() => addPoints(50, "verified")}>+50 Verified Report</button>
<button onClick={() => addPoints(1, "quiz")}>+1 Quiz Answer</button>

These render unconditionally — no dev-mode guard, no import.meta.env.DEV check. They are visible to every visitor at the bottom of the panel, and clicking "+50 Verified Report" persists a verified report that was never submitted. Between the fabricated seed and these buttons, no number on this panel corresponds to anything.

4. Smaller defects in the same component

  • key={user.name} — names are not unique keys. A stored userStats.name matching a mock entry ("Priya Singh") produces duplicate React keys and one row silently wins.
  • currentUserRank is findIndex(...) + 1, and leaderboard is [] until the effect runs, so the first paint renders "Your Rank #0".
  • addPoints calls localStorage.setItem inside the setState updater — unguarded, so a quota error throws from inside a React state update, and the updater is not pure (it runs twice under StrictMode, writing twice).
  • Mock contributors are labelled with no indication they are sample data, so the panel reads as a real community ranking.

Expected

A first-time visitor sees zeros, with the mock contributors clearly marked as sample data. Points derive from activity the app actually recorded. The simulator is not present in a production build.

Suggested fix

Extract the scoring into src/utils/leaderboardStats.js: read the existing report/quiz/challenge keys, apply the POINT_SYSTEM weights already declared at the top of the file, and return a stats object — so the score becomes a pure function of recorded activity and can be tested without rendering. Start a new user at zero, gate the simulator behind import.meta.env.DEV, key rows on a stable id, and don't render a rank before one exists.

Metadata

Metadata

Assignees

Labels

ECSoC26Contributions considered under ECSoC'26

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions