Skip to content

feat: Add mini-leaderboard and user rank badge components#63

Merged
0xdevcollins merged 3 commits intoboundlessfi:mainfrom
Dprof-in-tech:feat-implement-reusable-min-leaderboard-component
Jan 30, 2026
Merged

feat: Add mini-leaderboard and user rank badge components#63
0xdevcollins merged 3 commits intoboundlessfi:mainfrom
Dprof-in-tech:feat-implement-reusable-min-leaderboard-component

Conversation

@Dprof-in-tech
Copy link
Contributor

@Dprof-in-tech Dprof-in-tech commented Jan 30, 2026

This pull request introduces leaderboard feature to the application, including a mini leaderboard component for sidebars, a navigation badge showing the user's rank, and a main navigation link to the leaderboard page. It also includes supporting API and hook improvements for better error handling and efficiency.

Leaderboard UI Components:

  • Added MiniLeaderboard component for displaying top contributors in sidebars, with loading and error states. (components/leaderboard/mini-leaderboard.tsx, app/bounty/page.tsx) [1] [2] [3]
  • Added NavRankBadge component to show the current user's leaderboard rank in the global navbar. (components/leaderboard/nav-rank-badge.tsx, components/global-navbar.tsx) [1] [2] [3]

Navigation and Accessibility:

  • Added a "Leaderboard" link to the main navigation bar. (components/global-navbar.tsx)

API and Hook Improvements:

  • Improved leaderboard API and hooks to handle cases where the user ID is missing, returning null instead of throwing errors. (lib/api/leaderboard.ts, hooks/use-leaderboard.ts) [1] [2]
  • Optimized pagination logic in leaderboard data fetching for better performance. (hooks/use-leaderboard.ts)

Minor Fixes:

  • Improved tag toggle logic in leaderboard filters to handle undefined values more robustly. (components/leaderboard/leaderboard-filters.tsx)

closes #59

Screenshot 2026-01-30 at 02 43 44

Summary by CodeRabbit

  • New Features

    • Leaderboard link added to the main navigation with an inline rank badge.
    • New "Top Contributors" mini leaderboard widget for sidebars on large screens (includes loading, error states, and a View All link).
  • Improvements

    • Tag filtering behavior refined for more accurate results.
    • Rank fetching and related handling made safer when user identity is absent to avoid errors.

✏️ Tip: You can customize this high-level summary in your review settings.

…them into the bounty page and global navbar, and update leaderboard data fetching.
@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

Adds a MiniLeaderboard widget and NavRankBadge, integrates a Leaderboard link into the global navbar and bounty sidebar, and updates leaderboard hooks and API to accept an optional userId and adjust pagination and tag fallback behavior.

Changes

Cohort / File(s) Summary
New leaderboard UI
components/leaderboard/mini-leaderboard.tsx, components/leaderboard/nav-rank-badge.tsx
Adds MiniLeaderboard (client component, fetches top contributors, handles loading/error, links to profiles) and NavRankBadge (shows current user's rank in nav; skeleton while loading; hides when no rank).
Navigation & page integration
components/global-navbar.tsx, app/bounty/page.tsx
Adds Leaderboard link to global navbar, injects NavRankBadge into right-side controls (placeholder userId present), and renders MiniLeaderboard in bounty page sidebar on large screens.
Hooks & API
hooks/use-leaderboard.ts, lib/api/leaderboard.ts
fetchUserRank now accepts optional userId and returns null when missing; useLeaderboard pagination uses a count-based math check for next page; useUserRank passes userId without non-null assertion.
Minor logic fix
components/leaderboard/leaderboard-filters.tsx
Replaces `filters.tags
Manifest / package
package.json
Small dependency/manifest adjustments (lines changed: +16/-11).

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Browser as Browser (UI)
participant Mini as MiniLeaderboard
participant NavBadge as NavRankBadge
participant Hook as useLeaderboard / useUserRank
participant API as lib/api/leaderboard
participant DB as Backend/DB

Browser->>Mini: mount
Mini->>Hook: useTopContributors(limit)
Hook->>API: fetchTopContributors(limit)
API->>DB: query top contributors
DB-->>API: return contributors
API-->>Hook: contributors data
Hook-->>Mini: data / error
Mini-->>Browser: render list / skeleton / error

Browser->>NavBadge: mount (with userId?)
NavBadge->>Hook: useUserRank(userId)
Hook->>API: fetchUserRank(userId?)
API-->>Hook: rank | null
Hook-->>NavBadge: rank
NavBadge-->>Browser: render badge / hide if null

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🥕 I hopped into code with a gleam in my eye,
A tiny leaderboard to help champions fly,
Trophies in nav and top names to see,
Profiles linked close — hop, click, and be free!

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The PR implements most core requirements from #59: MiniLeaderboard component with loading/error states, NavRankBadge in navbar, leaderboard navigation link, and integration into bounty pages. However, several performance optimizations (prefetch on hover, lazy loading, virtual scrolling, stale-while-revalidate) and edge case handling are not visibly implemented. Clarify whether performance optimizations and edge case handling (not-logged-in states, not-ranked users) are in this PR or deferred. Verify that tag-contextual leaderboards on bounty pages are explicitly out-of-scope as noted in the issue.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding MiniLeaderboard and NavRankBadge components, which are the primary focus of the PR.
Out of Scope Changes check ✅ Passed The PR primarily focuses on leaderboard UI and API changes. Minor refactoring in leaderboard-filters.ts (tags fallback logic) and global-navbar.tsx appear related to leaderboard integration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@components/leaderboard/mini-leaderboard.tsx`:
- Line 73: The AvatarFallback currently uses contributor.displayName[0] which
can be undefined for empty strings; update the AvatarFallback rendering to
safely extract the first character from contributor.displayName and provide a
default fallback character when displayName is empty or undefined (refer to
AvatarFallback and contributor.displayName in the mini-leaderboard component),
e.g., check length or use a nullish/default fallback so the UI always shows a
single safe character instead of undefined.
🧹 Nitpick comments (2)
components/global-navbar.tsx (1)

36-36: Replace hardcoded userId before production deployment.

The hardcoded userId="user-1" is a placeholder. Ensure this is replaced with the actual authenticated user ID from your auth context/session before merging to production. The TODO comment acknowledges this, but consider creating a tracked issue if auth integration is not part of this PR.

Do you want me to open an issue to track replacing this placeholder with actual auth integration?

hooks/use-leaderboard.ts (1)

54-60: Inconsistent pagination logic with useLeaderboard.

usePrefetchLeaderboardPage still uses the older flatMap approach for getNextPageParam, while useLeaderboard (lines 19-23) was optimized to use the math-based check. Consider applying the same optimization for consistency.

♻️ Suggested refactor
         getNextPageParam: (lastPage, allPages) => {
-            const loadedCount = allPages.flatMap(p => p.entries).length;
-            if (loadedCount < lastPage.totalCount) {
+            if (allPages.length * limit < lastPage.totalCount) {
                 return allPages.length + 1;
             }
             return undefined;
         },

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@components/leaderboard/mini-leaderboard.tsx`:
- Line 73: The AvatarFallback currently uses a full name string ("John Doe")
which is too long for the avatar; update the fallback to a single character
(e.g. "?" or "U") or derive a 1-2 character initial from
contributor.displayName; modify the AvatarFallback usage (symbol:
AvatarFallback) so it renders either contributor.displayName?.[0] ?? "?" or a
small initials helper (use contributor.displayName to compute initials) instead
of the full "John Doe" string.
🧹 Nitpick comments (2)
components/leaderboard/mini-leaderboard.tsx (2)

82-82: Consider adding a fallback for the displayed name.

If contributor.displayName is empty or undefined, nothing will be rendered for the contributor's name, which could confuse users. Consider adding a fallback display name.

♻️ Proposed fix
                                        <span className="font-medium text-white text-sm truncate group-hover:text-primary transition-colors">
-                                            {contributor.displayName}
+                                            {contributor.displayName || "Anonymous"}
                                        </span>

88-88: Consider defensive coding for totalScore.

If totalScore is ever undefined or null, calling .toLocaleString() will throw a runtime error. Consider adding a fallback.

🛡️ Proposed fix
                                        <span className="text-[10px] text-white/70 font-mono">
-                                            {contributor.totalScore.toLocaleString()} pts
+                                            {(contributor.totalScore ?? 0).toLocaleString()} pts
                                        </span>

@0xdevcollins 0xdevcollins merged commit 1626dc1 into boundlessfi:main Jan 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create Mini Leaderboard Widget and Integrate Leaderboard into App Navigation

2 participants