diff --git a/app/bounty/page.tsx b/app/bounty/page.tsx index 7b32e76..bb231e5 100644 --- a/app/bounty/page.tsx +++ b/app/bounty/page.tsx @@ -229,7 +229,7 @@ export default function BountiesPage() { setSearchQuery(e.target.value)} /> diff --git a/app/globals.css b/app/globals.css index e3f8d6c..c4ef028 100644 --- a/app/globals.css +++ b/app/globals.css @@ -118,6 +118,7 @@ * { @apply border-border outline-ring/50; } + body { @apply bg-background text-foreground; } diff --git a/app/leaderboard/page.tsx b/app/leaderboard/page.tsx index d8b3978..ae98942 100644 --- a/app/leaderboard/page.tsx +++ b/app/leaderboard/page.tsx @@ -82,10 +82,10 @@ export default function LeaderboardPage() { {/* Hero Header */}
-

+

Leaderboard

-

+

Recognizing the top contributors in the ecosystem.

diff --git a/app/profile/[userId]/page.tsx b/app/profile/[userId]/page.tsx new file mode 100644 index 0000000..77f8d29 --- /dev/null +++ b/app/profile/[userId]/page.tsx @@ -0,0 +1,157 @@ +"use client"; + +import { useContributorReputation } from "@/hooks/use-reputation"; +import { ReputationCard } from "@/components/reputation/reputation-card"; +import { CompletionHistory } from "@/components/reputation/completion-history"; +import { Button } from "@/components/ui/button"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { AlertCircle, ChevronLeft } from "lucide-react"; +import Link from "next/link"; +import { useParams } from "next/navigation"; +import { useMemo } from "react"; + +export default function ProfilePage() { + const params = useParams(); + const userId = params.userId as string; + const { data: reputation, isLoading, error } = useContributorReputation(userId); + + const MAX_MOCK_HISTORY = 50; + + const mockHistory = useMemo(() => { + if (!reputation) return []; + const count = Math.min(reputation.stats.totalCompleted ?? 0, MAX_MOCK_HISTORY); + return Array(count).fill(null).map((_, i) => ({ + id: `bounty-${i}`, + bountyId: `b-${i}`, + bountyTitle: `Implemented feature #${100 + i}`, + projectName: "Drips Protocol", + projectLogoUrl: null, + difficulty: ["BEGINNER", "INTERMEDIATE", "ADVANCED"][i % 3] as "BEGINNER" | "INTERMEDIATE" | "ADVANCED", + rewardAmount: 500, + rewardCurrency: "USDC", + claimedAt: "2023-01-01T00:00:00Z", + completedAt: "2024-01-15T12:00:00Z", + completionTimeHours: 48, + maintainerRating: 5, + maintainerFeedback: "Great work!", + pointsEarned: 150 + })); + }, [reputation]); + + if (isLoading) { + return ( +
+ +
+ + +
+
+ ); + } + + if (error) { + // Check if it's a 404 (Not Found) + const apiError = error as { status?: number; message?: string }; + const isNotFound = apiError?.status === 404 || apiError?.message?.includes("404"); + + if (isNotFound) { + return ( +
+ +

Profile Not Found

+

+ We could not find a reputation profile for this user. +

+ +
+ ); + } + + // Generic Error + return ( +
+ +

Something went wrong

+

+ We encountered an error while loading the profile. +

+ +
+ ); + } + + if (!reputation) { + return ( +
+ +

Profile Not Found

+

+ We could not find a reputation profile for this user. +

+ +
+ ); + } + + return ( +
+ + +
+ {/* Left Sidebar: Reputation Card */} +
+ + + {/* Additional Sidebar Info could go here */} +
+ + {/* Main Content: Activity & History */} +
+ + + + Bounty History + + + Analytics + + + + +

Activity History

+ +
+ + +
+ Detailed analytics coming soon. +
+
+
+
+
+
+ ); +} diff --git a/components/bounty/bounty-sidebar.tsx b/components/bounty/bounty-sidebar.tsx index 1808aeb..f11640d 100644 --- a/components/bounty/bounty-sidebar.tsx +++ b/components/bounty/bounty-sidebar.tsx @@ -10,6 +10,7 @@ import { formatDistanceToNow } from "date-fns" // import { useRouter } from "next/navigation" // If we need refresh import { ApplicationDialog } from "./application-dialog" import { toast } from "sonner" +import { ParticipantCard } from "./participant-card" interface BountySidebarProps { bounty: Bounty @@ -166,6 +167,15 @@ export function BountySidebar({ bounty }: BountySidebarProps) { + {bounty.claimedBy && ( + <> +
+ +
+ + + )} + + +
+ + +
+
+ ); + } + + if (!reputation) { + return ( +
+ + ? + +
+
Unknown User
+
ID: {userId}
+
+
+ ); + } + + return ( +
+ {label &&
{label}
} + + + + + {reputation.displayName?.[0] || "?"} + +
+
+ {reputation.displayName} +
+
+ {reputation.tier} + + {reputation.totalScore} pts +
+
+ +
+
+ ); +} diff --git a/components/leaderboard/leaderboard-filters.tsx b/components/leaderboard/leaderboard-filters.tsx index 56607e4..fee53c4 100644 --- a/components/leaderboard/leaderboard-filters.tsx +++ b/components/leaderboard/leaderboard-filters.tsx @@ -81,7 +81,7 @@ export function LeaderboardFilters({ filters, onFilterChange }: LeaderboardFilte const hasActiveFilters = filters.timeframe !== "ALL_TIME" || filters.tier || (filters.tags?.length || 0) > 0; return ( -
+
{/* Timeframe Select */}