From 40854c8e5e107a5bb60d76c92fc59dac344562d5 Mon Sep 17 00:00:00 2001 From: boluwacodes Date: Tue, 25 Aug 2026 02:26:28 +0100 Subject: [PATCH 1/3] fix(contributor-dashboard): rename "Recommended for you" to "Open bounties" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The section was labeled "Recommended for you" but available is simply bounties.filter((b) => b.status === "open"), sliced to the first 4 in whatever order the backend/mock data returns them — no relevance scoring against the signed-in contributor's history, languages, or past claims. "Recommended for you" implies a personalization that doesn't exist anywhere in this code path; this page doesn't even fetch the languages/organizations data (already available on ReputationProfile) that a real recommendation would need. Renamed to the honest "Open bounties" rather than building relevance scoring this page has no data plumbing for yet — matches the suggested fix's first option (closes #239). --- src/app/dashboard/contributor/page.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/dashboard/contributor/page.tsx b/src/app/dashboard/contributor/page.tsx index bfc89e9..cdcecd6 100644 --- a/src/app/dashboard/contributor/page.tsx +++ b/src/app/dashboard/contributor/page.tsx @@ -217,8 +217,12 @@ export default function ContributorDashboardPage() { /> )} + {/* "available" is every open bounty in whatever order the backend/mock + data returns, sliced to the first 4 — no relevance scoring against + this contributor's history/languages/orgs. "Open bounties" is the + honest label until real personalization exists (#239). */}

- Recommended for you + Open bounties

{available.slice(0, 4).map((bounty) => ( From 5ff49070f28fe08926ca8881a63a9533c84e9ab5 Mon Sep 17 00:00:00 2001 From: boluwacodes Date: Tue, 25 Aug 2026 02:26:38 +0100 Subject: [PATCH 2/3] fix(homepage): derive hero network badge text from STELLAR_NETWORK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hero badge hardcoded "Built for Stellar Mainnet" as a literal, entirely independent of STELLAR_NETWORK — the same value NetworkBadge.tsx already exists specifically to surface accurately, with its own doc comment explaining why showing the wrong network is dangerous. A deployment configured with NEXT_PUBLIC_STELLAR_NETWORK=TESTNET (exactly the case where NetworkBadge's navbar indicator *does* show, to warn visitors) rendered a homepage hero flatly contradicting that warning: "Built for Stellar Mainnet" directly above marketing copy about low transaction costs and fast settlement, while the navbar a few pixels above correctly flagged that this build isn't on the real network at all — a meaningfully misleading claim for a fundraising/escrow product, not just a copy nit. Now reads "Built for Stellar Mainnet" only when STELLAR_NETWORK === "PUBLIC", otherwise the honest "Built for Stellar (Testnet)" (closes #238). --- src/app/page.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index ec2a3dd..c836ad5 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -25,6 +25,7 @@ import { faqs, } from "@/lib/mock-data"; import { formatCurrency } from "@/lib/utils"; +import { STELLAR_NETWORK } from "@/lib/config"; const integrations = [ { icon: Code2, label: "GitHub" }, @@ -90,7 +91,7 @@ export default function HomePage() {
- Built for Stellar Mainnet + {STELLAR_NETWORK === "PUBLIC" ? "Built for Stellar Mainnet" : "Built for Stellar (Testnet)"}

Merge code. From 6bbbe45b45f21d25a7df3df71d2ab9bf74b9ece5 Mon Sep 17 00:00:00 2001 From: boluwacodes Date: Tue, 25 Aug 2026 02:26:50 +0100 Subject: [PATCH 3/3] fix(maintainer-dashboard): fix dead error-status detection, wire up Create bounty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fetchBounties (lib/api.ts) already catches every failure internally and resolves to the fallback argument instead of rejecting — it can never throw, so the try/catch here that set statStatus = "error" on catch was unreachable dead code. statStatus was "loaded" in literally every case, including when the live fetch silently failed and fell back to mockBounties, so the maintainer dashboard had no way to distinguish live data from "backend down, showing 5 hardcoded mock bounties" — contrary to what the surrounding comment claimed. Detected the fallback case by reference instead: fetchBounties returns the exact fallback array on failure, but always a freshly-mapped array (a new reference) on a real live fetch, so this reliably distinguishes the two cases without changing fetchBounties' shared contract, which other pages depend on for its transparent-fallback behavior (closes #240). Also, the "Create bounty" action rendered as a bare with button-styled classes but no onClick or href — visually identical to a real call-to-action, but clicking it did nothing. The contributor and sponsor dashboards' equivalent action buttons are both wrapped in ; this was the only one of the three left unwired. Wrapped it the same way — bounties are created from GitHub issues per this page's own subtitle, so /issues is the natural destination (closes #241). --- src/app/dashboard/maintainer/page.tsx | 36 ++++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/app/dashboard/maintainer/page.tsx b/src/app/dashboard/maintainer/page.tsx index 3e6ca6b..52e05d8 100644 --- a/src/app/dashboard/maintainer/page.tsx +++ b/src/app/dashboard/maintainer/page.tsx @@ -1,3 +1,4 @@ +import Link from "next/link"; import { Layers, Clock, Coins, FolderGit2, CheckCircle2 } from "lucide-react"; import { fetchBounties } from "@/lib/api"; import { mockBounties, recentActivity, bountiesCompletedSparkline } from "@/lib/mock-data"; @@ -9,7 +10,6 @@ import { EmptyState } from "@/components/ui/EmptyState"; import { Avatar } from "@/components/ui/Avatar"; import { formatCurrency } from "@/lib/utils"; import { PipelineBoard, ESCROW_LOCKED_EXCLUDED_STATUSES } from "./PipelineBoard"; -import type { Bounty } from "@/types"; export const metadata = { title: "Maintainer Dashboard | MergeFi", @@ -17,17 +17,17 @@ export const metadata = { export default async function MaintainerDashboardPage() { // Maintainer dashboard is a Server Component (no client-side loading state). - // We wrap fetchBounties in a try/catch so that if the fetch fails, the cards - // explicitly show "Error loading data" rather than rendering stale zeros or - // crashing the page render. - let bounties: Bounty[] = []; - let statStatus: "loaded" | "error" = "loaded"; - - try { - bounties = await fetchBounties(mockBounties); - } catch { - statStatus = "error"; - } + // fetchBounties (lib/api.ts) already catches every failure internally and + // resolves to the `fallback` argument instead of rejecting — it can never + // throw, so a try/catch around it here would be dead code. Detect the + // fallback case instead by reference: fetchBounties returns the exact + // fallback array on failure, but always a freshly-mapped array (a new + // reference, even if its contents happened to match) on a real live + // fetch, so this reliably distinguishes "live data" from "backend down, + // showing mockBounties" without changing fetchBounties' shared contract + // (#240). + const bounties = await fetchBounties(mockBounties); + const statStatus: "loaded" | "error" = bounties === mockBounties ? "error" : "loaded"; const needsReview = bounties.filter((b) => b.status === "in_review"); const open = bounties.filter((b) => b.status === "open" || b.status === "funded"); @@ -42,9 +42,15 @@ export default async function MaintainerDashboardPage() { title="Bounty pipeline" subtitle="Create bounties from your GitHub issues and approve completed work. Payout release happens automatically once a linked pull request is merged." action={ - - Create bounty - + // Bounties are created from your GitHub issues (per the subtitle + // above) — /issues is the same destination the contributor and + // sponsor dashboards' equivalent action buttons already link to + // for picking a bounty to work with (#241). + + + Create bounty + + } > {/* Pass statStatus so that a fetchBounties failure renders error states