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) => (
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
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)"}