From 1e3a587d6487fd1a99d8462926cca748eb5609d3 Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 2 Mar 2026 15:35:40 -0500 Subject: [PATCH] feat: polish bounty card UI and fix avatar rendering --- next.config.mjs | 7 ++++ package-lock.json | 10 +++++ package.json | 1 + src/components/bounty-card.tsx | 67 ++++++++++++++++++++++++++-------- src/components/leaderboard.tsx | 23 ++++++++---- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index d5456a1..358d004 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,6 +1,13 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + images: { + remotePatterns: [ + { protocol: "https", hostname: "github.com" }, + { protocol: "https", hostname: "avatars.githubusercontent.com" }, + { protocol: "https", hostname: "ui-avatars.com" }, + ], + }, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 321594d..7e7e61f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "mergefund-hackathon-kit", "version": "0.1.0", "dependencies": { + "clsx": "^2.1.1", "next": "15.1.0", "react": "19.0.0", "react-dom": "19.0.0" @@ -2014,6 +2015,15 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", diff --git a/package.json b/package.json index 70a945f..25de7c9 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "clsx": "^2.1.1", "next": "15.1.0", "react": "19.0.0", "react-dom": "19.0.0" diff --git a/src/components/bounty-card.tsx b/src/components/bounty-card.tsx index ba1c467..5d904b2 100644 --- a/src/components/bounty-card.tsx +++ b/src/components/bounty-card.tsx @@ -1,4 +1,6 @@ -type BountyCardProps = { +import clsx from "clsx"; + +export type BountyCardProps = { title: string; reward: number; tags: string[]; @@ -6,45 +8,78 @@ type BountyCardProps = { progress: number; }; -const difficultyStyles = { +const difficultyStyles: Record = { 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", }; +const formatter = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + maximumFractionDigits: 0, +}); + +const clampProgress = (value: number) => Math.min(100, Math.max(0, value)); + export function BountyCard({ title, reward, tags, difficulty, progress }: BountyCardProps) { + const safeProgress = clampProgress(progress); + return ( -
-
+
+
+
-

{title}

-
+

Featured

+

+ {title} +

+
{tags.map((tag) => ( - + {tag} ))}
-
-
${reward}
- +
+

Reward

+

+ {formatter.format(reward)} +

+ {difficulty}
-
+ +
Progress - {progress}% + {safeProgress}%
-
+
-
+
); } diff --git a/src/components/leaderboard.tsx b/src/components/leaderboard.tsx index 684fe7a..f7926a3 100644 --- a/src/components/leaderboard.tsx +++ b/src/components/leaderboard.tsx @@ -1,5 +1,7 @@ "use client"; +import Image from "next/image"; + // BUG: Sorting algorithm doesn't handle ties correctly // When two users have the same earnings, their relative order is inconsistent // FIX: Add secondary sort key (e.g., by name or join date) @@ -43,14 +45,19 @@ export function Leaderboard() { {index + 1} - {entry.name} { - (e.target as HTMLImageElement).src = `https://ui-avatars.com/api/?name=${entry.name}`; - }} - /> +
+ {entry.name} { + const target = event.currentTarget; + target.src = `https://ui-avatars.com/api/?name=${encodeURIComponent(entry.name)}`; + }} + /> +

{entry.name}