Skip to content

feat(leaderboard): add seller leaderboard with category filter and realtime#36

Open
davedumto wants to merge 2 commits into
Regen-Bazaar:mainfrom
davedumto:feat/seller-leaderboard
Open

feat(leaderboard): add seller leaderboard with category filter and realtime#36
davedumto wants to merge 2 commits into
Regen-Bazaar:mainfrom
davedumto:feat/seller-leaderboard

Conversation

@davedumto

@davedumto davedumto commented May 25, 2026

Copy link
Copy Markdown

Summary

  • Adds a Top Sellers section on top of the existing impact leaderboard. Sellers are aggregated from active purchases, ranked by revenue (tie-break: projects sold), and joined to their organization for display.
  • Category filter: a dropdown driven by the distinct projects.category values; `all` returns sellers across every category.
  • Realtime updates: subscribes to `postgres_changes` on the `purchases` table; any insert/update/delete refreshes the board. Includes a manual refresh button as a fallback.
  • New files:
    • `src/lib/sellerLeaderboardService.ts` — service layer (`fetchSellerLeaderboard`, `fetchSellerCategories`, `subscribeToPurchaseChanges`).
    • `src/components/SellerLeaderboard.tsx` — UI with loading / error / empty states.
  • Minimal touch to `src/app/leaderboard/page.tsx` — the existing org/contributor sections are unchanged.

DoD coverage

  • List All Sellers Activity in Leaderboard
  • Apply Filters for Categories
  • Design and implement leaderboard UI on the frontend
  • Ensure real-time updates for leaderboard data (Supabase channel on `purchases`)

Notes / follow-ups

  • Aggregation is client-side because the JS Supabase REST API doesn't expose group-by. For larger seller counts, this should be promoted to a Postgres view or RPC.
  • No automated tests added; the repo has no test runner. Happy to add Vitest in a follow-up if desired.

Closes

closes #15

Test plan

  • `tsc --noEmit` clean
  • `eslint` clean on changed files
  • Manual: visit `/leaderboard`, switch category, observe top sellers updating; insert a purchase row and confirm the board re-fetches without reload.

Summary by CodeRabbit

  • New Features
    • Added a Top Sellers leaderboard section to the leaderboard page, ranking sellers by revenue and projects sold.
    • Filter sellers by project category with a dropdown menu.
    • Real-time updates trigger when new purchases occur, with a manual refresh button available.
    • Displays seller rank, name, revenue, projects sold, buyer count, and verification badge.

Review Change Stack

…altime (Regen-Bazaar#15)

Adds a Top Sellers section to the impact leaderboard. Aggregates active
purchases by organization, ranks by revenue then projects sold, and supports
filtering by project category. Subscribes to the `purchases` table via
Supabase realtime so the board refreshes when new sales land.

- New service `sellerLeaderboardService` (`fetchSellerLeaderboard`,
  `fetchSellerCategories`, `subscribeToPurchaseChanges`).
- New `SellerLeaderboard` component with loading / error / empty states and a
  manual refresh affordance alongside the realtime subscription.

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds a seller leaderboard feature to the marketplace. A new service aggregates purchase data by seller with metrics (projects sold, buyer count, total revenue), supports category filtering, and streams real-time updates. A React component fetches and displays this data in a ranked table with manual refresh and category selection. The component integrates into the existing leaderboard page.

Changes

Seller Leaderboard Feature

Layer / File(s) Summary
Seller leaderboard data service
src/lib/sellerLeaderboardService.ts
Defines SellerLeaderboardEntry and SellerLeaderboardFilters types. fetchSellerLeaderboard queries active purchases joined with projects and organizations, optionally filters by category, and aggregates into seller metrics (projects sold, buyer count, total revenue) sorted by revenue descending. fetchSellerCategories fetches and deduplicates distinct project categories. subscribeToPurchaseChanges registers a Supabase realtime channel for purchase table events and returns an unsubscribe function.
SellerLeaderboard React component
src/components/SellerLeaderboard.tsx
Implements a client-side component with rank icon constants and revenue formatter, state for leaderboard entries, categories, loading, and error states. useEffect hooks load categories on mount and entries when category changes. Subscribes to purchase changes and reloads on updates with cleanup. Renders "Top Sellers" header, category dropdown, refresh button, and conditional table showing loading, error, empty, or entry rows with rank icons, seller names, projects sold, buyer count, formatted revenue, and verified badges.
Leaderboard page integration
src/app/leaderboard/page.tsx
Imports SellerLeaderboard component and renders it in the page layout between timeframe selection controls and the existing organizations/contributors leaderboard grid.

Sequence Diagram

sequenceDiagram
  participant Component as SellerLeaderboard
  participant Service as sellerLeaderboardService
  participant Supabase
  Component->>Service: fetchSellerCategories()
  Service->>Supabase: query projects (non-null category)
  Supabase-->>Service: category rows
  Service-->>Component: sorted category array
  Component->>Service: fetchSellerLeaderboard(filters)
  Service->>Supabase: query purchases with project and organization details
  Supabase-->>Service: joined purchase rows
  Service->>Service: aggregate by organization, compute metrics
  Service-->>Component: sorted SellerLeaderboardEntry array
  Component->>Service: subscribeToPurchaseChanges(onChange)
  Service->>Supabase: create realtime channel (postgres_changes)
  Supabase-->>Service: channel subscription active
  Service-->>Component: unsubscribe function
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A seller's stage, now bright and grand,
Top traders ranked across the land,
With real-time hops and filtered views,
The leaderboard sings profit news! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main changes: adding a seller leaderboard with category filter and realtime updates, which directly matches the changeset.
Linked Issues check ✅ Passed The PR fulfills all four DoD objectives from issue #15: lists seller activity via aggregation service, implements category filtering with 'all' option, provides complete frontend UI with loading/error/empty states and manual refresh, and enables real-time updates via Supabase purchase change subscriptions.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the seller leaderboard feature; the service layer, component, and page integration align entirely with issue #15 requirements without introducing unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/sellerLeaderboardService.ts (1)

139-154: 💤 Low value

Consider handling subscription errors.

The realtime subscription doesn't handle connection errors or subscription failures. Supabase's .subscribe() accepts a callback for status changes that can report errors.

♻️ Optional improvement to handle subscription status
 export const subscribeToPurchaseChanges = (
-  onChange: () => void
+  onChange: () => void,
+  onError?: (error: Error) => void
 ): (() => void) => {
   const channel = supabase
     .channel('seller-leaderboard:purchases')
     .on(
       'postgres_changes',
       { event: '*', schema: 'public', table: 'purchases' },
       onChange
     )
-    .subscribe()
+    .subscribe((status, err) => {
+      if (status === 'CHANNEL_ERROR' && onError && err) {
+        onError(err)
+      }
+    })

   return () => {
     void supabase.removeChannel(channel)
   }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/sellerLeaderboardService.ts` around lines 139 - 154, The
subscribeToPurchaseChanges function currently calls .subscribe() without
handling status callbacks; update it to pass a status callback to .subscribe()
(or to .on(...) if using that overload) that checks for subscription
errors/rejections and connection status, logs or surfaces errors via your logger
(or an onError callback), and ensures onChange only runs for actual data events;
also ensure the returned teardown still removes the created channel variable if
the subscription failed. Target symbols: subscribeToPurchaseChanges, channel,
and the .subscribe() call.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/SellerLeaderboard.tsx`:
- Around line 39-52: The load function (useCallback load) can suffer a race
where earlier fetchSellerLeaderboard responses overwrite later ones; modify load
to ignore stale responses by using an AbortController or a request-version
token: create an AbortController (or increment a local requestId counter stored
in a ref) each time load starts, pass the controller.signal to
fetchSellerLeaderboard (or capture the current requestId) and on response only
call setEntries/setError if the request wasn't aborted and the requestId matches
the latestRef; also ensure any real-time event handler that calls load triggers
the same mechanism so inflight requests are cancelled or ignored (apply the same
pattern to the other load-like function at lines 68-77).

---

Nitpick comments:
In `@src/lib/sellerLeaderboardService.ts`:
- Around line 139-154: The subscribeToPurchaseChanges function currently calls
.subscribe() without handling status callbacks; update it to pass a status
callback to .subscribe() (or to .on(...) if using that overload) that checks for
subscription errors/rejections and connection status, logs or surfaces errors
via your logger (or an onError callback), and ensures onChange only runs for
actual data events; also ensure the returned teardown still removes the created
channel variable if the subscription failed. Target symbols:
subscribeToPurchaseChanges, channel, and the .subscribe() call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5567bc6d-081d-461e-ac1c-be17c90d03d9

📥 Commits

Reviewing files that changed from the base of the PR and between bd03120 and e2c541b.

📒 Files selected for processing (3)
  • src/app/leaderboard/page.tsx
  • src/components/SellerLeaderboard.tsx
  • src/lib/sellerLeaderboardService.ts

Comment thread src/components/SellerLeaderboard.tsx
Track the in-flight request via a ref counter so that responses from
older load() calls (triggered by category changes or realtime events
arriving while a previous load is pending) don't overwrite the latest
state. Addresses CodeRabbit review feedback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

Develop a seller leaderboard feature to display top sellers based on their activity.

1 participant