Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 41 additions & 8 deletions src/app/leaderboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
import { mockLeaderboard } from "@/data/mock-leaderboard";

export default function LeaderboardPage() {
const sorted = [...mockLeaderboard].sort((a, b) => b.earned - a.earned);
const sorted = [...mockLeaderboard].sort((a, b) => {
if (b.earned !== a.earned) return b.earned - a.earned;
if (b.bounties !== a.bounties) return b.bounties - a.bounties;
return a.name.localeCompare(b.name);
});

const getRank = (index: number) => {
if (index === 0) return 1;
const current = sorted[index];
const prev = sorted[index - 1];

if (
current.earned === prev.earned &&
current.bounties === prev.bounties
) {
return getRank(index - 1);
}

return index + 1;
};

return (
<div className="space-y-6">
Expand All @@ -13,24 +32,38 @@ export default function LeaderboardPage() {
</div>

<div className="card overflow-hidden">
<div className="grid grid-cols-5 gap-3 border-b border-slate-200 bg-slate-50 px-5 py-3 text-xs font-semibold text-slate-600">
<div className="hidden md:grid grid-cols-5 gap-3 border-b border-slate-200 bg-slate-50 px-5 py-3 text-xs font-semibold text-slate-600">
<div>Rank</div>
<div className="col-span-2">Developer</div>
<div>Bounties</div>
<div>Total Earned</div>
</div>

<div className="md:hidden border-b border-slate-200 bg-slate-50 px-4 py-2 text-xs font-semibold text-slate-600">
Top Developers
</div>

{sorted.map((dev, index) => (
<div
key={dev.id}
className="grid grid-cols-5 gap-3 px-5 py-4 text-sm border-b border-slate-100 last:border-b-0"
className="grid grid-cols-1 md:grid-cols-5 gap-2 md:gap-3 px-4 md:px-5 py-4 text-sm border-b border-slate-100 last:border-b-0"
>
<div className="font-semibold">#{index + 1}</div>
<div className="col-span-2">
<div className="font-semibold">{dev.name}</div>
<div className="font-semibold md:self-center">#{getRank(index)}</div>

<div className="md:col-span-2">
<div className="font-semibold break-words">{dev.name}</div>
<div className="text-xs text-slate-500">Reputation {dev.reputation}</div>
</div>
<div>{dev.bounties}</div>
<div className="font-semibold">${dev.earned.toLocaleString()}</div>

<div className="text-slate-700">
<span className="md:hidden text-xs text-slate-500 mr-2">Bounties:</span>
{dev.bounties}
</div>

<div className="font-semibold">
<span className="md:hidden text-xs text-slate-500 mr-2">Earned:</span>
${dev.earned.toLocaleString()}
</div>
</div>
))}
</div>
Expand Down
32 changes: 18 additions & 14 deletions src/components/bounty-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ type BountyCardProps = {
};

const difficultyStyles = {
Easy: "bg-emerald-50 text-emerald-700 border-emerald-200",
Medium: "bg-amber-50 text-amber-700 border-amber-200",
Hard: "bg-rose-50 text-rose-700 border-rose-200",
Easy: "bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-900/20 dark:text-emerald-300 dark:border-emerald-800",
Medium:
"bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-900/20 dark:text-amber-300 dark:border-amber-800",
Hard: "bg-rose-50 text-rose-700 border-rose-200 dark:bg-rose-900/20 dark:text-rose-300 dark:border-rose-800",
};

export function BountyCard({ title, reward, tags, difficulty, progress }: BountyCardProps) {
const clampedProgress = Math.max(0, Math.min(100, progress));

return (
<div className="card p-4 sm:p-5 hover:shadow-md transition">
<article className="card p-4 sm:p-5 hover:shadow-md hover:-translate-y-0.5 transition">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0 flex-1">
<h3 className="text-base sm:text-lg font-semibold leading-snug break-words">{title}</h3>
Expand All @@ -27,24 +30,25 @@ export function BountyCard({ title, reward, tags, difficulty, progress }: Bounty
</div>
</div>
<div className="text-right shrink-0">
<div className="text-xl sm:text-xl font-bold">${reward}</div>
<span className={`mt-1 inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] sm:text-xs font-semibold whitespace-nowrap ${difficultyStyles[difficulty]}`}>
<p className="text-[11px] uppercase tracking-wide text-slate-500 dark:text-slate-400">Reward</p>
<div className="text-xl sm:text-2xl font-bold">${reward}</div>
<span
className={`mt-1 inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] sm:text-xs font-semibold whitespace-nowrap ${difficultyStyles[difficulty]}`}
>
{difficulty}
</span>
</div>
</div>

<div className="mt-3">
<div className="flex items-center justify-between text-xs text-slate-500">
<div className="flex items-center justify-between text-xs text-slate-500 dark:text-slate-400">
<span>Progress</span>
<span>{progress}%</span>
<span>{clampedProgress}%</span>
</div>
<div className="mt-1.5 h-2 w-full rounded-full bg-slate-100">
<div
className="h-2 rounded-full bg-brand-600"
style={{ width: `${progress}%` }}
/>
<div className="mt-1.5 h-2.5 w-full rounded-full bg-slate-100 dark:bg-slate-800">
<div className="h-2.5 rounded-full bg-brand-600 transition-all" style={{ width: `${clampedProgress}%` }} />
</div>
</div>
</div>
</article>
);
}