Skip to main content

Leaderboard (Student)

Students see how they rank by total XP against their peers — first within their section, then their class (all sections), then the whole school. The top three students sit on a podium; ranks 4 and below appear as a list. If the viewing student isn't in the top 30, their own row is pinned at the bottom under a "Your Rank" label.

At a glance

Who can do thisStudents only — the leaderboard is rendered in the student app and uses the logged-in student's section/class/school as the scope
Where it livesStudent app /dashboard/leaderboard
Triggers notifications?No — purely a read view
Related featuresCoins & Credits · Homework · Chapter Tests · Attendance

How it flows

There is no client-side polling — the leaderboard refetches only when the tab changes. The student must reopen the page (or change the tab and back) to pick up the latest XP totals.

Step-by-step

1. Open the page

  • App / route: Student app → /dashboard/leaderboard.
  • The header is a standard TopBar with back-button and title "Leader Board".
  • Three tabs sit just below the header: Section (default), Class, School.

2. Fetch and render

On open / tab change, the page fetches the leaderboard for the active scope and renders:

  • Podium (only when the response has at least 3 students):
    • Visual order on screen is 2 — 1 — 3 (silver-left, gold-centre, bronze-right).
    • First place gets a "Winner" badge and a slightly larger avatar.
    • Each podium spot shows: first name, profile picture (or coloured initial fallback derived deterministically from studentId), XP with a trophy icon, position number on the block.
    • Light theme has a warm sunset gradient + decorative trees; dark theme has animated nebula glow + twinkling stars.
  • Rank rows 4 through 30:
    • Two-digit zero-padded rank, avatar, full name, XP total, #rank pill.
    • The current student's row is highlighted with a primary-coloured border + "(You)" suffix.
  • "Your Rank" section (only if the student is not in the top 30):
    • Three-dot divider, "YOUR RANK" label, then the student's row pinned at the bottom — same visual treatment as the highlighted in-list row.
  • Footer: "Showing top N of M students" (M = data.totalStudents).

3. Empty / error states

  • Loading: spinner + "Loading leaderboard..." while the request is in flight.
  • Empty: trophy icon + "No leaderboard data available" (e.g. brand-new section with no XP yet).
  • Error: trophy icon + the error message returned from the server, falling back to "Failed to load leaderboard".
  • Fewer than 3 students: skips the podium entirely and renders all available students as plain rank rows.

Display modes (tabs)

TabScopeNotes
SectionJust the student's section (e.g. Class 8 - A)Default tab
ClassAll sections in the same class (e.g. Class 8 - A, B, C…)Larger pool than Section
SchoolEvery student in the schoolLargest pool; useful for school-wide bragging rights

Switching tabs refetches the data for that scope. Active tab gets the primary background; inactive tabs are muted.

How XP is earned

XP totals are accumulated from gamified actions across the student app. The leaderboard surfaces them; it does not award them. Features that contribute XP include:

Exact award rules per feature are defined inside each feature's flow doc; the leaderboard only shows the resulting totalXp.

Refresh cadence

  • No auto-refresh / no polling. The page fetches once on mount and once per tab switch.
  • The student must navigate away and back (or pull-down-equivalent by switching tab) to see fresh totals after, say, finishing a chapter test.
  • Avatar fallback colours are deterministic by studentId, so the same student always has the same colour across reloads.

Edge cases & things to test

  • Tied XP: two students with equal XP — confirm rank order is stable across reloads (don't flip-flop).
  • Student with 0 XP: shows up at the bottom (or "Your Rank" if outside top 30) with 0 XP — must not blank out the row.
  • Profile picture missing: fallback should be a coloured circle with initials; the colour stays the same across page reloads (deterministic by studentId).
  • Profile picture broken URL: should fall back to initials, not a broken image icon.
  • Single-name student (only firstName): "Unknown" should not appear; the lastName initial just omitted.
  • Switching tabs rapidly: stale request handling — the cancellation guard must prevent an older response from overwriting a newer one.
  • Section with < 3 students: podium hidden; all students rendered as rank rows.
  • Section with exactly 3 students: podium shown, no "rest" list, no "Your Rank" section.
  • Student not in top 30: confirm "Your Rank" pinned section appears with their actual rank, and that it is not duplicated inside the top-30 list.
  • Student in top 3: their podium spot gets the primary border / ring; no duplicate "Your Rank" section at the bottom.
  • API error: shows the server's error message, not a generic "something broke". If the server returns no message, the fallback "Failed to load leaderboard" should appear.
  • No internet: the error-state branch should render — not an indefinite spinner.
  • Light vs dark theme: podium decorations swap correctly (trees in light mode, nebula in dark mode); nothing renders in both themes simultaneously.
  • Scroll inside leaderboard: long lists (30 entries) scroll within the page without breaking the fixed header / tabs.
  • Tab persistence: navigating away and back resets to Section (no persistence) — confirm this is the intended UX.
  • XP recently earned: just after submitting a test, XP may not appear until the page is reloaded (no live websocket update).