Skip to content

Chore/remove dead rest api surface - #171

Merged
Benjtalkshow merged 3 commits into
boundlessfi:mainfrom
Abidoyesimze:chore/remove-dead-rest-api-surface
Apr 23, 2026
Merged

Chore/remove dead rest api surface#171
Benjtalkshow merged 3 commits into
boundlessfi:mainfrom
Abidoyesimze:chore/remove-dead-rest-api-surface

Conversation

@Abidoyesimze

@Abidoyesimze Abidoyesimze commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

closes #106

Summary

This PR removes unused HTTP helpers from the shared Axios client and narrows the lib/api barrel exports. Bounty REST proxy routes under app/api/bounties/ (and related application/submission proxies) were already removed on main; this change finishes the cleanup on the client side and refreshes the lockfile after a full install.

Changes

lib/api/client.ts — Drop put, patch, and del (no callers). Keep apiClient as an internal implementation detail instead of exporting it.
lib/api/index.ts — Re-export only get and post from ./client.
package-lock.json — Updated via npm install so CI/local installs match resolved dependencies.

Summary by CodeRabbit

  • Bug Fixes

    • Improved error state handling in leaderboard display for better user feedback
  • Refactor

    • Removed application review and submission selection endpoints
    • Code formatting and style improvements across the codebase

Accept upstream removal of lib/api/bounties.ts (GraphQL/types replace REST
schemas). Align lib/api/index.ts exports with ./client (no token helpers).

Made-with: Cursor
Remove put/patch/del and stop exporting apiClient; barrel exports get/post
only. Bounty proxy routes were already absent. Regenerate lockfile after
npm install; build verified with npm run build.

Made-with: Cursor
@vercel

vercel Bot commented Apr 23, 2026

Copy link
Copy Markdown

@Abidoyesimze is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Apr 23, 2026

Copy link
Copy Markdown

@Abidoyesimze Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR removes Next.js API route proxies for application reviews and submission selection endpoints, eliminates unused HTTP helper functions from the API client library, standardizes code formatting across leaderboard components and hooks, and refactors the mini-leaderboard component's rendering logic to handle error states distinctly.

Changes

Cohort / File(s) Summary
Removed API Endpoints
app/api/applications/[id]/review/route.ts, app/api/submissions/[id]/select/route.ts
Entire POST handlers deleted, eliminating status validation, feedback/review writing, and associated error responses.
API Client Refactoring
lib/api/client.ts, lib/api/index.ts
Removed apiClient export and HTTP helper functions (put, patch, del); retained only get and post exports.
Leaderboard Component Logic
components/leaderboard/mini-leaderboard.tsx
Changed rendering flow to return early on error state with minimal card; non-error path renders skeletons on load or maps contributors with profile links, rank/tier badges, and footer button.
Formatting & Import Standardization
app/leaderboard/page.tsx, components/leaderboard/leaderboard-filters.tsx, hooks/use-bounty-subscription.ts, hooks/use-leaderboard.ts, lib/graphql/ws-client.ts, lib/mock-leaderboard.ts, types/leaderboard.ts
Multi-line import refactoring, quote normalization (single to double), indentation adjustments, and module specifier updates without logic changes.
Documentation
docs/REALTIME_SYNC.md
Markdown formatting normalization (newlines, table alignment, whitespace) with no substantive content changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • 0xdevcollins
  • Benjtalkshow

Poem

🐰 Hopping through the code with glee,
Routes removed, now GraphQL-free!
Endpoints gone, the proxy's done,
Leaderboard shines—formatting's fun!
Clean imports, tidy and tight,
This refactor's looking bright! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR contains multiple formatting and import style changes unrelated to the core objective of removing dead REST API surface: reformatting in leaderboard components, hooks, docs, and type files with quote/indentation changes. Separate formatting-only changes (leaderboard pages, hooks, docs, types reformatting) into a dedicated formatting PR, keeping this PR focused solely on API surface removal and necessary supporting changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% 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 Most objectives from #106 are addressed: unused REST helpers (put/patch/del) are removed [#106], barrel exports narrowed to get/post [#106], and build verification confirmed. However, app/api/applications and app/api/submissions routes were deleted but not verified as complete cleanup; app/api/bounties deletion status unclear from summary. Clarify whether all app/api/ proxy routes (bounties, applications, submissions) have been fully deleted as part of #106 completion, or if additional cleanup is required beyond the client-side changes.
✅ Passed checks (2 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 change: removal of unused REST API surface (helpers and exports), which is the core focus of this PR.

✏️ 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: 5

🧹 Nitpick comments (1)
lib/mock-leaderboard.ts (1)

11-11: Replace deprecated .substr() with .slice().

The .substr() method is deprecated in modern JavaScript (ES2020+). While this function will continue to work for mock data, migrating to .slice() improves compatibility with current standards.

♻️ Suggested change
-  walletAddress: `0x${Math.random().toString(16).substr(2, 40)}`,
+  walletAddress: `0x${Math.random().toString(16).slice(2)}`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/mock-leaderboard.ts` at line 11, Replace the deprecated use of
String.prototype.substr in the walletAddress generation with
String.prototype.slice: locate the walletAddress template string that uses
Math.random().toString(16).substr(2, 40) and change it to use slice by computing
the same substring range (start 2, end 42) so the expression becomes
Math.random().toString(16).slice(2, 42); update the walletAddress assignment in
lib/mock-leaderboard.ts accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/leaderboard/page.tsx`:
- Around line 26-46: The filters state is initialized from URL params
(initialTimeframe, initialTier, initialTags) but never updated when searchParams
change; replace the one-time useState seed with logic that syncs state to URL
changes by adding a useEffect that watches searchParams (or
rawTimeframe/rawTier/rawTags) and calls setFilters(...) with the validated
values (using the same TIMEFRAMES/TIERS checks and split for tags) so that
filters remains in sync when the user navigates/back-forward or lands on a new
/leaderboard?... URL; keep existing validation logic and only update filters
when derived values actually differ to avoid needless renders.
- Around line 48-50: The hard-coded currentUserId ("user-1") in
app/leaderboard/page.tsx causes wrong highlighting/rank for real users; replace
it by deriving the id from the app's auth/session layer (e.g., obtain user via
your auth hook or session getter and set currentUserId = user?.id) and ensure
any UI that expects a user (sidebar highlighting, rank logic) safely handles
undefined by hiding or disabling user-specific UI until auth is available;
update any references to currentUserId in this file so they tolerate undefined
and do not assume a string.
- Around line 115-118: The AlertDescription currently renders raw error details
((error as Error)?.message) to users; change the UI to show a generic,
user-facing message like "Failed to load leaderboard data. Please try again
later." and remove the direct interpolation of error.message in the JSX, while
recording the full error to safe logging/telemetry (e.g., call your existing
logger/reportError or console.error) near the error handling in page.tsx; locate
the JSX using the AlertDescription element and the error variable to make this
change.

In `@components/leaderboard/mini-leaderboard.tsx`:
- Around line 73-110: The list currently maps contributors and if the array is
empty only the footer CTA is shown; add an explicit empty-state render when
contributors is an empty array by checking contributors?.length === 0 and
returning a small fallback block (e.g., centered message/icon and optional CTA)
inside the component between the contributors map and the footer CTA div so
users see a clear "No contributors yet" state instead of an apparent blank card;
update the JSX around the contributors?.map(...) and the subsequent <div
className="p-2"> footer to conditionally render the empty-state when
contributors exists but has length 0.
- Around line 75-78: The leaderboard page uses a wrong profile route for
contributor links: change the Link href that currently builds
`/user/${entry.contributor.userId}` to `/profile/${entry.contributor.userId}` so
it matches the rest of the app; locate the Link rendering that uses
entry.contributor.userId in the leaderboard page component and update the href
string accordingly (keep the dynamic interpolation and the existing key/props
intact).

---

Nitpick comments:
In `@lib/mock-leaderboard.ts`:
- Line 11: Replace the deprecated use of String.prototype.substr in the
walletAddress generation with String.prototype.slice: locate the walletAddress
template string that uses Math.random().toString(16).substr(2, 40) and change it
to use slice by computing the same substring range (start 2, end 42) so the
expression becomes Math.random().toString(16).slice(2, 42); update the
walletAddress assignment in lib/mock-leaderboard.ts accordingly.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8e738bb4-7559-439f-a8fa-5c73397420a9

📥 Commits

Reviewing files that changed from the base of the PR and between ad24ca7 and 6924192.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • app/api/applications/[id]/review/route.ts
  • app/api/submissions/[id]/select/route.ts
  • app/leaderboard/page.tsx
  • components/leaderboard/leaderboard-filters.tsx
  • components/leaderboard/mini-leaderboard.tsx
  • docs/REALTIME_SYNC.md
  • hooks/use-bounty-subscription.ts
  • hooks/use-leaderboard.ts
  • lib/api/client.ts
  • lib/api/index.ts
  • lib/graphql/generated.ts
  • lib/graphql/ws-client.ts
  • lib/mock-leaderboard.ts
  • types/leaderboard.ts
💤 Files with no reviewable changes (2)
  • app/api/submissions/[id]/select/route.ts
  • app/api/applications/[id]/review/route.ts

Comment thread app/leaderboard/page.tsx
Comment on lines +26 to +46
// Validate and initialize filters from URL
const rawTimeframe = searchParams.get("timeframe");
const rawTier = searchParams.get("tier");

const initialTimeframe = TIMEFRAMES.some((t) => t.value === rawTimeframe)
? (rawTimeframe as FiltersType["timeframe"])
: LeaderboardTimeframe.AllTime;

const initialTier = TIERS.some((t) => t.value === rawTier)
? (rawTier as ReputationTier)
: undefined;

const initialTags = searchParams.get("tags")
? searchParams.get("tags")?.split(",")
: [];

const [filters, setFilters] = useState<FiltersType>({
timeframe: initialTimeframe,
tier: initialTier,
tags: initialTags || [],
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep filter state in sync when the URL changes.

useState only consumes initialTimeframe, initialTier, and initialTags on the first render. If the user navigates with browser back/forward or lands on another /leaderboard?... URL while this page stays mounted, filters can remain stale and the URL-sync effect can overwrite the navigated query.

💡 Suggested direction
+  // Consider extracting URL parsing into a helper and syncing `filters`
+  // when `searchParams.toString()` changes, with an equality guard to avoid loops.
+  // Also build the replace URL without a trailing `?` when params are empty.
+
   // Sync debounced filters to URL
   useEffect(() => {
     const params = new URLSearchParams();
@@
-    router.replace(`/leaderboard?${params.toString()}`, { scroll: false });
+    const query = params.toString();
+    router.replace(query ? `/leaderboard?${query}` : "/leaderboard", {
+      scroll: false,
+    });
   }, [debouncedFilters, router]);

Also applies to: 75-86

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/leaderboard/page.tsx` around lines 26 - 46, The filters state is
initialized from URL params (initialTimeframe, initialTier, initialTags) but
never updated when searchParams change; replace the one-time useState seed with
logic that syncs state to URL changes by adding a useEffect that watches
searchParams (or rawTimeframe/rawTier/rawTags) and calls setFilters(...) with
the validated values (using the same TIMEFRAMES/TIERS checks and split for tags)
so that filters remains in sync when the user navigates/back-forward or lands on
a new /leaderboard?... URL; keep existing validation logic and only update
filters when derived values actually differ to avoid needless renders.

Comment thread app/leaderboard/page.tsx
Comment on lines +48 to +50
// Fake current user ID for demo purposes
// In a real app this would come from auth context
const currentUserId = "user-1";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid shipping a hard-coded current user.

currentUserId = "user-1" makes the sidebar rank and current-user highlighting wrong for every real user. Prefer auth-derived user identity, or pass undefined/hide user-specific UI until auth is available.

Do you want me to draft a follow-up implementation that wires this to the project’s auth source?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/leaderboard/page.tsx` around lines 48 - 50, The hard-coded currentUserId
("user-1") in app/leaderboard/page.tsx causes wrong highlighting/rank for real
users; replace it by deriving the id from the app's auth/session layer (e.g.,
obtain user via your auth hook or session getter and set currentUserId =
user?.id) and ensure any UI that expects a user (sidebar highlighting, rank
logic) safely handles undefined by hiding or disabling user-specific UI until
auth is available; update any references to currentUserId in this file so they
tolerate undefined and do not assume a string.

Comment thread app/leaderboard/page.tsx
Comment on lines +115 to +118
<AlertDescription className="flex flex-col gap-2">
<p>
Failed to load leaderboard data. {(error as Error)?.message}
</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Don’t render raw error messages directly to users.

error.message can include backend or GraphQL details. Show a generic message here and reserve detailed errors for safe logging/telemetry.

🛡️ Proposed user-facing copy change
-                  <p>
-                    Failed to load leaderboard data. {(error as Error)?.message}
-                  </p>
+                  <p>Failed to load leaderboard data. Please try again.</p>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<AlertDescription className="flex flex-col gap-2">
<p>
Failed to load leaderboard data. {(error as Error)?.message}
</p>
<AlertDescription className="flex flex-col gap-2">
<p>Failed to load leaderboard data. Please try again.</p>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/leaderboard/page.tsx` around lines 115 - 118, The AlertDescription
currently renders raw error details ((error as Error)?.message) to users; change
the UI to show a generic, user-facing message like "Failed to load leaderboard
data. Please try again later." and remove the direct interpolation of
error.message in the JSX, while recording the full error to safe
logging/telemetry (e.g., call your existing logger/reportError or console.error)
near the error handling in page.tsx; locate the JSX using the AlertDescription
element and the error variable to make this change.

Comment on lines +73 to +110
{contributors?.map(
(contributor: LeaderboardContributor, index: number) => (
<Link
key={contributor.id}
href={`/profile/${contributor.userId}`}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted/50 transition-colors border-b border-border/40 last:border-0 group"
>
<div className="flex-shrink-0 relative">
<Avatar className="h-9 w-9 border border-border/50">
<AvatarImage src={contributor.avatarUrl || undefined} />
<AvatarFallback>
{contributor.displayName?.[0] ?? "?"}
</AvatarFallback>
</Avatar>
<div className="absolute -top-1 -left-1 text-foreground flex items-center justify-center w-4 h-4 rounded-full bg-background border border-border text-[10px] font-bold">
{index + 1}
</div>
) : (
<div className="flex flex-col">
{contributors?.map((contributor: LeaderboardContributor, index: number) => (
<Link
key={contributor.id}
href={`/profile/${contributor.userId}`}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted/50 transition-colors border-b border-border/40 last:border-0 group"
>
<div className="flex-shrink-0 relative">
<Avatar className="h-9 w-9 border border-border/50">
<AvatarImage src={contributor.avatarUrl || undefined} />
<AvatarFallback>{contributor.displayName?.[0] ?? "?"}</AvatarFallback>
</Avatar>
<div className="absolute -top-1 -left-1 text-foreground flex items-center justify-center w-4 h-4 rounded-full bg-background border border-border text-[10px] font-bold">
{index + 1}
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-medium text-foreground text-sm truncate group-hover:text-muted-foreground transition-colors">
{contributor.displayName}
</span>
</div>
<div className="flex items-center gap-2 mt-0.5">
<TierBadge tier={contributor.tier} className="h-4 text-[10px] px-1.5 py-0" />
<span className="text-[10px] text-muted-foreground font-mono">
{contributor.totalScore.toLocaleString()} pts
</span>
</div>
</div>
</Link>
))}
<div className="p-2">
<Button variant="ghost" className="w-full text-xs h-8 text-muted-foreground hover:text-foreground" asChild>
<Link href="/leaderboard">
See full rankings
</Link>
</Button>
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-medium text-foreground text-sm truncate group-hover:text-muted-foreground transition-colors">
{contributor.displayName}
</span>
</div>
)}
</CardContent>
</Card>
);
<div className="flex items-center gap-2 mt-0.5">
<TierBadge
tier={contributor.tier}
className="h-4 text-[10px] px-1.5 py-0"
/>
<span className="text-[10px] text-muted-foreground font-mono">
{contributor.totalScore.toLocaleString()} pts
</span>
</div>
</div>
</Link>
),
)}
<div className="p-2">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Render an explicit empty state when there are no contributors.

If contributors resolves to an empty array, this card currently shows only the footer CTA, which looks like missing content.

💡 Proposed empty-state handling
-            {contributors?.map(
+            {contributors?.length ? contributors.map(
               (contributor: LeaderboardContributor, index: number) => (
                 <Link
                   key={contributor.id}
                   href={`/profile/${contributor.userId}`}
                   className="flex items-center gap-3 px-4 py-3 hover:bg-muted/50 transition-colors border-b border-border/40 last:border-0 group"
@@
                 </Link>
               ),
-            )}
+            ) : (
+              <div className="px-4 py-6 text-center text-sm text-muted-foreground">
+                No contributors yet
+              </div>
+            )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{contributors?.map(
(contributor: LeaderboardContributor, index: number) => (
<Link
key={contributor.id}
href={`/profile/${contributor.userId}`}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted/50 transition-colors border-b border-border/40 last:border-0 group"
>
<div className="flex-shrink-0 relative">
<Avatar className="h-9 w-9 border border-border/50">
<AvatarImage src={contributor.avatarUrl || undefined} />
<AvatarFallback>
{contributor.displayName?.[0] ?? "?"}
</AvatarFallback>
</Avatar>
<div className="absolute -top-1 -left-1 text-foreground flex items-center justify-center w-4 h-4 rounded-full bg-background border border-border text-[10px] font-bold">
{index + 1}
</div>
) : (
<div className="flex flex-col">
{contributors?.map((contributor: LeaderboardContributor, index: number) => (
<Link
key={contributor.id}
href={`/profile/${contributor.userId}`}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted/50 transition-colors border-b border-border/40 last:border-0 group"
>
<div className="flex-shrink-0 relative">
<Avatar className="h-9 w-9 border border-border/50">
<AvatarImage src={contributor.avatarUrl || undefined} />
<AvatarFallback>{contributor.displayName?.[0] ?? "?"}</AvatarFallback>
</Avatar>
<div className="absolute -top-1 -left-1 text-foreground flex items-center justify-center w-4 h-4 rounded-full bg-background border border-border text-[10px] font-bold">
{index + 1}
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-medium text-foreground text-sm truncate group-hover:text-muted-foreground transition-colors">
{contributor.displayName}
</span>
</div>
<div className="flex items-center gap-2 mt-0.5">
<TierBadge tier={contributor.tier} className="h-4 text-[10px] px-1.5 py-0" />
<span className="text-[10px] text-muted-foreground font-mono">
{contributor.totalScore.toLocaleString()} pts
</span>
</div>
</div>
</Link>
))}
<div className="p-2">
<Button variant="ghost" className="w-full text-xs h-8 text-muted-foreground hover:text-foreground" asChild>
<Link href="/leaderboard">
See full rankings
</Link>
</Button>
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-medium text-foreground text-sm truncate group-hover:text-muted-foreground transition-colors">
{contributor.displayName}
</span>
</div>
)}
</CardContent>
</Card>
);
<div className="flex items-center gap-2 mt-0.5">
<TierBadge
tier={contributor.tier}
className="h-4 text-[10px] px-1.5 py-0"
/>
<span className="text-[10px] text-muted-foreground font-mono">
{contributor.totalScore.toLocaleString()} pts
</span>
</div>
</div>
</Link>
),
)}
<div className="p-2">
{contributors?.length ? contributors.map(
(contributor: LeaderboardContributor, index: number) => (
<Link
key={contributor.id}
href={`/profile/${contributor.userId}`}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted/50 transition-colors border-b border-border/40 last:border-0 group"
>
<div className="flex-shrink-0 relative">
<Avatar className="h-9 w-9 border border-border/50">
<AvatarImage src={contributor.avatarUrl || undefined} />
<AvatarFallback>
{contributor.displayName?.[0] ?? "?"}
</AvatarFallback>
</Avatar>
<div className="absolute -top-1 -left-1 text-foreground flex items-center justify-center w-4 h-4 rounded-full bg-background border border-border text-[10px] font-bold">
{index + 1}
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-medium text-foreground text-sm truncate group-hover:text-muted-foreground transition-colors">
{contributor.displayName}
</span>
</div>
<div className="flex items-center gap-2 mt-0.5">
<TierBadge
tier={contributor.tier}
className="h-4 text-[10px] px-1.5 py-0"
/>
<span className="text-[10px] text-muted-foreground font-mono">
{contributor.totalScore.toLocaleString()} pts
</span>
</div>
</div>
</Link>
),
) : (
<div className="px-4 py-6 text-center text-sm text-muted-foreground">
No contributors yet
</div>
)}
<div className="p-2">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/leaderboard/mini-leaderboard.tsx` around lines 73 - 110, The list
currently maps contributors and if the array is empty only the footer CTA is
shown; add an explicit empty-state render when contributors is an empty array by
checking contributors?.length === 0 and returning a small fallback block (e.g.,
centered message/icon and optional CTA) inside the component between the
contributors map and the footer CTA div so users see a clear "No contributors
yet" state instead of an apparent blank card; update the JSX around the
contributors?.map(...) and the subsequent <div className="p-2"> footer to
conditionally render the empty-state when contributors exists but has length 0.

Comment on lines +75 to +78
<Link
key={contributor.id}
href={`/profile/${contributor.userId}`}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted/50 transition-colors border-b border-border/40 last:border-0 group"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect Next.js app routes and leaderboard navigation targets.
# Expect: Either both /profile/[id] and /user/[id] routes exist intentionally,
# or MiniLeaderboard and LeaderboardPage use the same canonical route.

printf '\n== App route pages ==\n'
fd -i 'page\.(tsx|ts|jsx|js)$' app

printf '\n== Leaderboard/profile/user navigation references ==\n'
rg -n -C3 'href=\{?`/(profile|user)/|router\.push\(`/((profile|user))/' --type=tsx --type=ts

Repository: boundlessfi/bounties

Length of output: 516


🏁 Script executed:

#!/bin/bash
# Read mini-leaderboard to verify href at lines 75-78
echo "=== mini-leaderboard.tsx lines 70-82 ==="
fd -i 'mini-leaderboard\.tsx' | xargs cat -n | sed -n '70,82p'

echo ""
echo "=== app/leaderboard/page.tsx lines 135-145 ==="
fd -i 'page\.tsx' app/leaderboard | xargs cat -n | sed -n '135,145p'

echo ""
echo "=== Search for /user/ and /profile/ hrefs without file type restrictions ==="
rg -n 'href.*/(profile|user)/' --max-count=20

Repository: boundlessfi/bounties

Length of output: 1771


🏁 Script executed:

#!/bin/bash
# Search for any /user/ route definition
echo "=== Checking for /user/[userId] route ==="
fd -i 'page\.tsx' app/user 2>/dev/null || echo "No user directory found"

echo ""
echo "=== All user-related pages/routes ==="
fd 'page\.(tsx|ts)$' app | grep -i user

echo ""
echo "=== Verify /profile/ route exists ==="
fd 'page\.(tsx|ts)$' app | grep -i profile

Repository: boundlessfi/bounties

Length of output: 267


Fix inconsistent profile route navigation in leaderboard.

The leaderboard page navigates to /user/${entry.contributor.userId} (line 138), but this route does not exist. The mini-leaderboard component correctly uses /profile/${contributor.userId}, which is the only defined profile route in the application. Update line 138 in app/leaderboard/page.tsx to use /profile/ to match the rest of the codebase and prevent broken navigation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/leaderboard/mini-leaderboard.tsx` around lines 75 - 78, The
leaderboard page uses a wrong profile route for contributor links: change the
Link href that currently builds `/user/${entry.contributor.userId}` to
`/profile/${entry.contributor.userId}` so it matches the rest of the app; locate
the Link rendering that uses entry.contributor.userId in the leaderboard page
component and update the href string accordingly (keep the dynamic interpolation
and the existing key/props intact).

@Benjtalkshow
Benjtalkshow merged commit ed167b6 into boundlessfi:main Apr 23, 2026
1 of 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.

Remove Next.js API route proxies

2 participants