diff --git a/src/app/connect/ConnectPanel.tsx b/src/app/connect/ConnectPanel.tsx index 5fe2f771..6878dfa5 100644 --- a/src/app/connect/ConnectPanel.tsx +++ b/src/app/connect/ConnectPanel.tsx @@ -61,21 +61,46 @@ export function ConnectPanel() {

- {address ? ( -
- - Connected: {address.slice(0, 4)}...{address.slice(-4)} ({network}) -
- ) : ( - - )} + +
+ {address ? ( +
+ + Connected: {address.slice(0, 4)}...{address.slice(-4)} ({network}) +
+ ) : ( + + )} + + {user ? ( + user.stellarAddress && user.stellarAddress === address ? ( +
+ + Linked to your account +
+ ) : address ? ( +
+ This wallet is connected locally but not linked to your GitHub account. + {user.stellarAddress ? ` Your account is currently linked to ${user.stellarAddress.slice(0, 4)}...${user.stellarAddress.slice(-4)}.` : ""} +
+ ) : user.stellarAddress ? ( +
+ Your account is linked to {user.stellarAddress.slice(0, 4)}...{user.stellarAddress.slice(-4)}, but it is not currently active in Freighter. +
+ ) : null + ) : address ? ( +
+ Wallet connected locally. Sign in with GitHub above to link this wallet to your account for payouts. +
+ ) : null} +
{error &&

{error}

} diff --git a/src/app/page.tsx b/src/app/page.tsx index 2a2387e8..065cd9f0 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -156,19 +156,24 @@ export default function HomePage() {
+ {/* Duration doesn't fit currency/count/percent/raw formats; keep pre-formatted string */}
diff --git a/src/app/reputation/[handle]/page.tsx b/src/app/reputation/[handle]/page.tsx index 0a1a1977..8a06c41e 100644 --- a/src/app/reputation/[handle]/page.tsx +++ b/src/app/reputation/[handle]/page.tsx @@ -1,10 +1,10 @@ import { notFound } from "next/navigation"; -import { fetchReputationByUsername } from "@/lib/api"; +import { fetchReputationWithStatus } from "@/lib/api"; import { mockReputationProfiles } from "@/lib/mock-data"; import { StatCard } from "@/components/ui/StatCard"; import { Badge } from "@/components/ui/Badge"; import { Avatar } from "@/components/ui/Avatar"; -import { formatCurrency, formatPercent } from "@/lib/utils"; +// formatCurrency/formatPercent no longer needed — StatCard handles numeric formatting export default async function ReputationPage({ params, @@ -12,13 +12,15 @@ export default async function ReputationPage({ params: Promise<{ handle: string }>; }) { const { handle } = await params; - const profile = await fetchReputationByUsername( + const { profile, isLive } = await fetchReputationWithStatus( handle, mockReputationProfiles[handle] ?? null, ); if (!profile) notFound(); + const cardStatus = isLive ? "loaded" as const : "error" as const; + return (
@@ -43,16 +45,27 @@ export default async function ReputationPage({
+ -
diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx index b7684677..6e7f3a61 100644 --- a/src/components/layout/Navbar.tsx +++ b/src/components/layout/Navbar.tsx @@ -7,21 +7,29 @@ import { Avatar } from "@/components/ui/Avatar"; import { ThemeToggle } from "@/components/ui/ThemeToggle"; import { NetworkBadge } from "@/components/ui/NetworkBadge"; import { useAuth } from "@/context/AuthContext"; +import type { UserRole } from "@/types"; const links = [ { href: "/issues", label: "Bounties" }, { href: "/milestones", label: "Milestones" }, ]; -const dashboardLinks = [ - { href: "/dashboard/contributor", label: "Contributor" }, - { href: "/dashboard/maintainer", label: "Maintainer" }, - { href: "/dashboard/sponsor", label: "Sponsor" }, +const dashboardLinks: { href: string; label: string; role: UserRole }[] = [ + { href: "/dashboard/contributor", label: "Contributor", role: "contributor" }, + { href: "/dashboard/maintainer", label: "Maintainer", role: "maintainer" }, + { href: "/dashboard/sponsor", label: "Sponsor", role: "sponsor" }, ]; export function Navbar() { const { user, loading, logout } = useAuth(); + // Filter dashboard links to only those matching the user's assigned roles. + // Supports multi-role users: if roles contains both "maintainer" and "sponsor", + // both dashboard links are shown. If no roles yet (e.g. new user), show none. + const visibleDashboardLinks = user + ? dashboardLinks.filter((link) => user.roles.includes(link.role)) + : []; + return (
@@ -38,25 +46,28 @@ export function Navbar() { {link.label} ))} -
- -
-
- {dashboardLinks.map((link) => ( - - {link.label} - - ))} + {/* Only show Dashboards dropdown if there are visible role-based links */} + {visibleDashboardLinks.length > 0 && ( +
+ +
+
+ {visibleDashboardLinks.map((link) => ( + + {link.label} + + ))} +
-
+ )} {user && ( Reputation @@ -67,7 +78,14 @@ export function Navbar() {
- {loading ? null : user ? ( + {loading ? ( + /* Loading skeleton: stable placeholder matching the logged-in layout dimensions + to prevent flash of logged-out buttons before auth resolves. */ +
+
+
+
+ ) : user ? (
{ + try { + const users = await request<(RawUserProfile & { id: string })[]>("/users"); + const user = users.find((u) => u.username === username); + if (!user) return { profile: fallback, isLive: false }; + const snapshot = await request( + `/reputation/${user.id}`, + ); + return { profile: adaptReputation(user, snapshot), isLive: true }; + } catch { + return { profile: fallback, isLive: false }; + } +} + export { request };