From 8efab7323a49c8e9c118e9970e7fb4a60b103592 Mon Sep 17 00:00:00 2001 From: Ekene Ngwudike Date: Sat, 24 Jan 2026 11:51:01 +0100 Subject: [PATCH 1/8] feat: Build BountyCard component with shadcn composition - Implement BountyCard as composition of shadcn Card component - Add grid and list layout variants with responsive design - Include bounty-specific data display: title, description, budget, status, category, claiming model, milestones - Add status indicators with semantic color coding (emerald for open, amber for claimed, blue for in-progress, red for disputed) - Display creator info with avatar, wallet, and timestamp - Show meta information: deadline countdown and applicant count - Implement smooth hover effects with shadow elevation and scale - Add text truncation for long titles and descriptions - Support click handlers for navigation - Ensure full mobile responsiveness: - Responsive padding and spacing (mobile-first) --- app/page.tsx | 19 +++ components/bounty/bounty-card.tsx | 232 ++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 components/bounty/bounty-card.tsx diff --git a/app/page.tsx b/app/page.tsx index 2ca07ab..d3a5eca 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,10 +1,29 @@ import SignIn from "@/components/login/sign-in"; import Image from "next/image"; +import { BountyCard } from "@/components/bounty/bounty-card" + +const mockBounty = { + id: "1", + title: "Build Authentication System", + description: "Need a robust auth system with JWT support", + budget: { amount: 5000, asset: "USDC" }, + status: "open" as const, + category: "Backend", + claimingModel: 2 as const, + creator: { wallet: "0x1234...5678", displayName: "Alice" }, + deadline: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), + applicantCount: 3, + milestoneCount: 2, +} + export default async function Home() { return (
+
+ + ); } diff --git a/components/bounty/bounty-card.tsx b/components/bounty/bounty-card.tsx new file mode 100644 index 0000000..ed347f2 --- /dev/null +++ b/components/bounty/bounty-card.tsx @@ -0,0 +1,232 @@ +import * as React from "react"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Clock, Users, DollarSign } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { formatDistanceToNow } from "date-fns"; + +interface Bounty { + id: string; + title: string; + description: string; + budget: { + amount: number; + asset: string; + }; + status: + | "open" + | "claimed" + | "in_progress" + | "under_review" + | "completed" + | "disputed"; + category: string; + claimingModel: 1 | 2 | 3 | 4; + creator: { + wallet: string; + displayName?: string; + avatar?: string; + }; + deadline: Date; + applicantCount?: number; + milestoneCount?: number; +} + +interface BountyCardProps { + bounty: Bounty; + onClick?: () => void; + variant?: "grid" | "list"; +} + +const statusConfig = { + open: { + variant: "default" as const, + label: "Open", + dotColor: "bg-emerald-500", + }, + claimed: { + variant: "secondary" as const, + label: "Claimed", + dotColor: "bg-amber-500", + }, + in_progress: { + variant: "secondary" as const, + label: "In Progress", + dotColor: "bg-blue-500", + }, + under_review: { + variant: "secondary" as const, + label: "Under Review", + dotColor: "bg-amber-500", + }, + completed: { + variant: "outline" as const, + label: "Completed", + dotColor: "bg-slate-400", + }, + disputed: { + variant: "destructive" as const, + label: "Disputed", + dotColor: "bg-red-500", + }, +}; + +const modelNames = { + 1: "Single Claim", + 2: "Application", + 3: "Competition", + 4: "Multi-Winner", +}; + +export function BountyCard({ + bounty, + onClick, + variant = "grid", +}: BountyCardProps) { + const status = statusConfig[bounty.status]; + const timeLeft = formatDistanceToNow(bounty.deadline, { addSuffix: true }); + + return ( + + {/* Main Content Section */} + +
+ + {/* Header Row with Status and Budget */} + +
+
+
+ + {status.label} + +
+ + {variant === "grid" && ( +
+
+ {bounty.budget.amount.toLocaleString()} +
+
+ {bounty.budget.asset} +
+
+ )} +
+ + {/* Title and Description */} + + + {bounty.title} + + + {bounty.description} + + + {/* Category and Model Badges */} + +
+ + {bounty.category} + + + {modelNames[bounty.claimingModel]} + + {bounty.milestoneCount && bounty.milestoneCount > 1 && ( + + {bounty.milestoneCount} milestones + + )} +
+ + + {/* List Variant Budget Display */} + + {variant === "list" && ( +
+
+ {bounty.budget.amount.toLocaleString()} +
+
+ {bounty.budget.asset} +
+
+ )} +
+ + {/* Footer with Creator and Meta Info */} + + + {/* Creator Info */} +
+ + + + {bounty.creator.displayName?.[0]?.toUpperCase() || + bounty.creator.wallet.slice(0, 2).toUpperCase()} + + + + {bounty.creator.displayName || + `${bounty.creator.wallet.slice(0, 8)}...`} + +
+ + {/* Meta Information */} + +
+ {bounty.deadline && ( +
+ + {timeLeft} + {timeLeft.replace(" ago", "").replace(" from now", "")} +
+ )} + {bounty.applicantCount != null && bounty.applicantCount > 0 && ( +
+ + {bounty.applicantCount} +
+ )} +
+
+ + ); +} From b1bbbf29e5cd313f30dbaab723f18b03faa11c86 Mon Sep 17 00:00:00 2001 From: Ekene Ngwudike Date: Sat, 24 Jan 2026 11:54:01 +0100 Subject: [PATCH 2/8] feat: removed demo data from page.tsx file --- app/page.tsx | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index d3a5eca..9414e10 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,27 +1,13 @@ import SignIn from "@/components/login/sign-in"; import Image from "next/image"; -import { BountyCard } from "@/components/bounty/bounty-card" -const mockBounty = { - id: "1", - title: "Build Authentication System", - description: "Need a robust auth system with JWT support", - budget: { amount: 5000, asset: "USDC" }, - status: "open" as const, - category: "Backend", - claimingModel: 2 as const, - creator: { wallet: "0x1234...5678", displayName: "Alice" }, - deadline: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), - applicantCount: 3, - milestoneCount: 2, -} export default async function Home() { return (
- +
From 97b9c878d3e721a454c226975ebdf34599cf54b2 Mon Sep 17 00:00:00 2001 From: Ekene Ngwudike Date: Sat, 24 Jan 2026 14:11:44 +0100 Subject: [PATCH 3/8] Feat: cleaned up code based on corrections that was suggested --- components/bounty/bounty-card.tsx | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/components/bounty/bounty-card.tsx b/components/bounty/bounty-card.tsx index ed347f2..ef6a0f1 100644 --- a/components/bounty/bounty-card.tsx +++ b/components/bounty/bounty-card.tsx @@ -1,7 +1,7 @@ -import * as React from "react"; +"use client"; + import { Card, - CardContent, CardDescription, CardFooter, CardHeader, @@ -9,7 +9,7 @@ import { } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Clock, Users, DollarSign } from "lucide-react"; +import { Clock, Users } from "lucide-react"; import { cn } from "@/lib/utils"; import { formatDistanceToNow } from "date-fns"; @@ -97,12 +97,20 @@ export function BountyCard({ return ( { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + onClick?.(); + } + }} > {/* Main Content Section */} @@ -113,7 +121,10 @@ export function BountyCard({ )} > {/* Header Row with Status and Budget */} @@ -193,7 +204,7 @@ export function BountyCard({ {/* Footer with Creator and Meta Info */} - + {/* Creator Info */}
@@ -216,7 +227,9 @@ export function BountyCard({
{timeLeft} - {timeLeft.replace(" ago", "").replace(" from now", "")} + + {timeLeft.replace(" ago", "").replace(" from now", "")} +
)} {bounty.applicantCount != null && bounty.applicantCount > 0 && ( From 7f40ae5842babb2d5fcc67d3cb201f9ce7e6cbcc Mon Sep 17 00:00:00 2001 From: Ekene Ngwudike Date: Sat, 24 Jan 2026 14:13:58 +0100 Subject: [PATCH 4/8] Feat: removed extra whitespace --- app/page.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/page.tsx b/app/page.tsx index 9414e10..b4979a3 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -7,7 +7,6 @@ export default async function Home() { return (
-
From 8b74d8425d9c936f55c188e0d7249cc445462882 Mon Sep 17 00:00:00 2001 From: Ekene Ngwudike Date: Sun, 25 Jan 2026 01:06:58 +0100 Subject: [PATCH 5/8] merging conflicts with updated setup --- components/bounty/github-bounty-card.tsx | 145 +++++++++++++++++++ package-lock.json | 171 +++++++++++++++++++++-- 2 files changed, 302 insertions(+), 14 deletions(-) create mode 100644 components/bounty/github-bounty-card.tsx diff --git a/components/bounty/github-bounty-card.tsx b/components/bounty/github-bounty-card.tsx new file mode 100644 index 0000000..337f761 --- /dev/null +++ b/components/bounty/github-bounty-card.tsx @@ -0,0 +1,145 @@ +import Link from "next/link" +import Image from "next/image" +import { formatDistanceToNow } from "date-fns" +import { Github, Bug, Sparkles, FileText, RefreshCw, Circle } from "lucide-react" +import { Bounty, BountyType } from "@/types/bounty" +import { Badge } from "@/components/ui/badge" +import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card" +import { cn } from "@/lib/utils" + +interface BountyCardProps { + bounty: Bounty +} + +const typeConfig: Record = { + bug: { label: "Bug", icon: , className: "bg-error-500 text-white border-transparent" }, + feature: { label: "Feature", icon: , className: "bg-primary text-primary-foreground border-transparent" }, + documentation: { label: "Docs", icon: , className: "bg-secondary-500 text-white border-transparent" }, + refactor: { label: "Refactor", icon: , className: "bg-gray-700 text-gray-100 border-transparent" }, + other: { label: "Other", icon: , className: "bg-gray-800 text-gray-300 border-gray-600" }, +} + +const difficultyColors: Record = { + beginner: "text-success-400", + intermediate: "text-warning-400", + advanced: "text-error-400", +} + +const statusColors: Record = { + open: "bg-success-500/10 text-success-500 border-success-500/20", + claimed: "bg-warning-500/10 text-warning-500 border-warning-500/20", + closed: "bg-gray-500/10 text-gray-500 border-gray-500/20", +} + +export function BountyCard({ bounty }: BountyCardProps) { + const typeInfo = typeConfig[bounty.type] + const difficultyColor = bounty.difficulty ? difficultyColors[bounty.difficulty] : "text-gray-400" + const statusColor = statusColors[bounty.status] || statusColors.closed + + // Prevent card click when clicking interactive elements + const handleInteractiveClick = (e: React.MouseEvent) => { + e.stopPropagation() + } + + return ( + + +
+
+ {bounty.projectLogoUrl ? ( +
+ {bounty.projectName} +
+ ) : ( +
+ {(bounty.projectName || "Unknown").substring(0, 2).toUpperCase()} +
+ )} +
+

{bounty.projectName}

+ + {formatDistanceToNow(new Date(bounty.createdAt), { addSuffix: true })} + +
+
+ +
+ + {typeInfo.icon} + {typeInfo.label} + + + {bounty.status} + +
+
+ +

+ + {bounty.issueTitle} + +

+
+ + +

+ {bounty.description.replace(/[#*`_]/g, '') /* Simple stripped markdown preview */} +

+ +
+ {bounty.tags.slice(0, 3).map(tag => ( + + {tag} + + ))} + {bounty.tags.length > 3 && ( + +{bounty.tags.length - 3} + )} +
+
+ + +
+ {(bounty.rewardAmount !== null && bounty.rewardAmount !== undefined) ? ( +
+ Reward + + {bounty.rewardAmount} {bounty.rewardCurrency} + +
+ ) : ( +
+ Reward + - +
+ )} + + {bounty.difficulty && ( +
+ Difficulty + + {bounty.difficulty} + +
+ )} +
+ + + + +
+
+ ) +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 676bf26..c883e98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,9 @@ "@radix-ui/react-toggle": "^1.1.10", "@radix-ui/react-toggle-group": "^1.1.11", "@radix-ui/react-tooltip": "^1.2.8", + "@tanstack/react-query": "^5.90.20", + "@tanstack/react-query-devtools": "^5.91.2", + "axios": "^1.13.2", "better-auth": "^1.4.12", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -3323,6 +3326,59 @@ "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, + "node_modules/@tanstack/query-core": { + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz", + "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.92.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.92.0.tgz", + "integrity": "sha512-N8D27KH1vEpVacvZgJL27xC6yPFUy0Zkezn5gnB3L3gRCxlDeSuiya7fKge8Y91uMTnC8aSxBQhcK6ocY7alpQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.20.tgz", + "integrity": "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.91.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.91.2.tgz", + "integrity": "sha512-ZJ1503ay5fFeEYFUdo7LMNFzZryi6B0Cacrgr2h1JRkvikK1khgIq6Nq2EcblqEdIlgB/r7XDW8f8DQ89RuUgg==", + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.92.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.90.14", + "react": "^18 || ^19" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -4300,6 +4356,12 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -4326,6 +4388,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -4559,7 +4632,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4746,6 +4818,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -5087,6 +5171,15 @@ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "license": "MIT" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -5152,7 +5245,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -5292,7 +5384,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5302,7 +5393,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5340,7 +5430,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5353,7 +5442,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5999,6 +6087,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -6015,11 +6123,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6080,7 +6203,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -6114,7 +6236,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -6202,7 +6323,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6274,7 +6394,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6287,7 +6406,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -6303,7 +6421,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -7458,7 +7575,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8083,6 +8199,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -8624,6 +8761,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", From b40b19cb45a3f7575773654d5cb9c7e1e9351fcb Mon Sep 17 00:00:00 2001 From: Ekene Ngwudike Date: Sun, 25 Jan 2026 01:24:37 +0100 Subject: [PATCH 6/8] Made Corrections to bounty card to fix upcoming conflict issues --- components/bounty/bounty-card.tsx | 9 +++++---- components/bounty/github-bounty-card.tsx | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/components/bounty/bounty-card.tsx b/components/bounty/bounty-card.tsx index ef6a0f1..0ac378a 100644 --- a/components/bounty/bounty-card.tsx +++ b/components/bounty/bounty-card.tsx @@ -177,12 +177,13 @@ export function BountyCard({ > {modelNames[bounty.claimingModel]} - {bounty.milestoneCount && bounty.milestoneCount > 1 && ( + {bounty.milestoneCount != null && ( - {bounty.milestoneCount} milestones + {bounty.milestoneCount}{" "} + {bounty.milestoneCount === 1 ? "milestone" : "milestones"} )}
@@ -232,7 +233,7 @@ export function BountyCard({
)} - {bounty.applicantCount != null && bounty.applicantCount > 0 && ( + {bounty.applicantCount != null && (
{bounty.applicantCount} diff --git a/components/bounty/github-bounty-card.tsx b/components/bounty/github-bounty-card.tsx index 337f761..b52c7d0 100644 --- a/components/bounty/github-bounty-card.tsx +++ b/components/bounty/github-bounty-card.tsx @@ -1,3 +1,5 @@ +"use client"; + import Link from "next/link" import Image from "next/image" import { formatDistanceToNow } from "date-fns" From 328ba0badf11e08152013075d46dc0eb8be4373c Mon Sep 17 00:00:00 2001 From: Ekene Ngwudike Date: Sun, 25 Jan 2026 01:36:54 +0100 Subject: [PATCH 7/8] Removed all backticks from bounty card component --- components/bounty/bounty-card.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bounty/bounty-card.tsx b/components/bounty/bounty-card.tsx index 0ac378a..cdb4cde 100644 --- a/components/bounty/bounty-card.tsx +++ b/components/bounty/bounty-card.tsx @@ -180,7 +180,7 @@ export function BountyCard({ {bounty.milestoneCount != null && ( {bounty.milestoneCount}{" "} {bounty.milestoneCount === 1 ? "milestone" : "milestones"} From 6a8fdc9c3315cb0be58beedef6340f4ac4c7c767 Mon Sep 17 00:00:00 2001 From: Ekene Ngwudike Date: Sun, 25 Jan 2026 02:14:45 +0100 Subject: [PATCH 8/8] Resolved Build error --- app/globals.css | 1 - components/bounty/bounty-card.tsx | 145 ++++++++++-------------------- 2 files changed, 46 insertions(+), 100 deletions(-) diff --git a/app/globals.css b/app/globals.css index 86b5e85..de7a79e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,6 +1,5 @@ @import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap'); @import 'tailwindcss'; -@plugin "@tailwindcss/typography"; @custom-variant dark (&:is(.dark *)); diff --git a/components/bounty/bounty-card.tsx b/components/bounty/bounty-card.tsx index cdb4cde..ae62d2e 100644 --- a/components/bounty/bounty-card.tsx +++ b/components/bounty/bounty-card.tsx @@ -12,33 +12,7 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Clock, Users } from "lucide-react"; import { cn } from "@/lib/utils"; import { formatDistanceToNow } from "date-fns"; - -interface Bounty { - id: string; - title: string; - description: string; - budget: { - amount: number; - asset: string; - }; - status: - | "open" - | "claimed" - | "in_progress" - | "under_review" - | "completed" - | "disputed"; - category: string; - claimingModel: 1 | 2 | 3 | 4; - creator: { - wallet: string; - displayName?: string; - avatar?: string; - }; - deadline: Date; - applicantCount?: number; - milestoneCount?: number; -} +import { Bounty } from "@/types/bounty"; interface BountyCardProps { bounty: Bounty; @@ -57,33 +31,11 @@ const statusConfig = { label: "Claimed", dotColor: "bg-amber-500", }, - in_progress: { - variant: "secondary" as const, - label: "In Progress", - dotColor: "bg-blue-500", - }, - under_review: { - variant: "secondary" as const, - label: "Under Review", - dotColor: "bg-amber-500", - }, - completed: { + closed: { variant: "outline" as const, - label: "Completed", + label: "Closed", dotColor: "bg-slate-400", }, - disputed: { - variant: "destructive" as const, - label: "Disputed", - dotColor: "bg-red-500", - }, -}; - -const modelNames = { - 1: "Single Claim", - 2: "Application", - 3: "Competition", - 4: "Multi-Winner", }; export function BountyCard({ @@ -92,12 +44,14 @@ export function BountyCard({ variant = "grid", }: BountyCardProps) { const status = statusConfig[bounty.status]; - const timeLeft = formatDistanceToNow(bounty.deadline, { addSuffix: true }); + const timeLeft = bounty.updatedAt + ? formatDistanceToNow(new Date(bounty.updatedAt), { addSuffix: true }) + : "N/A"; return ( - {/* Header Row with Status and Budget */} + {/* Header Row with Status and Reward */}
@@ -141,13 +95,13 @@ export function BountyCard({
- {variant === "grid" && ( + {variant === "grid" && bounty.rewardAmount && (
- {bounty.budget.amount.toLocaleString()} + {bounty.rewardAmount.toLocaleString()}
- {bounty.budget.asset} + {bounty.rewardCurrency}
)} @@ -156,89 +110,82 @@ export function BountyCard({ {/* Title and Description */} - {bounty.title} + {bounty.issueTitle} {bounty.description} - {/* Category and Model Badges */} + {/* Type and Difficulty Badges */}
- {bounty.category} + {bounty.type} - - {modelNames[bounty.claimingModel]} - - {bounty.milestoneCount != null && ( + {bounty.difficulty && ( + + {bounty.difficulty} + + )} + {bounty.tags.length > 0 && ( - {bounty.milestoneCount}{" "} - {bounty.milestoneCount === 1 ? "milestone" : "milestones"} + {bounty.tags.slice(0, 1).join(", ")} )}
- {/* List Variant Budget Display */} + {/* List Variant Reward Display */} - {variant === "list" && ( + {variant === "list" && bounty.rewardAmount && (
- {bounty.budget.amount.toLocaleString()} + {bounty.rewardAmount.toLocaleString()}
- {bounty.budget.asset} + {bounty.rewardCurrency}
)}
- {/* Footer with Creator and Meta Info */} + {/* Footer with Project and Meta Info */} - {/* Creator Info */} + {/* Project Info */}
- - - - {bounty.creator.displayName?.[0]?.toUpperCase() || - bounty.creator.wallet.slice(0, 2).toUpperCase()} - - + {bounty.projectLogoUrl && ( + + + + {bounty.projectName?.[0]?.toUpperCase()} + + + )} - {bounty.creator.displayName || - `${bounty.creator.wallet.slice(0, 8)}...`} + {bounty.projectName}
{/* Meta Information */}
- {bounty.deadline && ( -
- - {timeLeft} - - {timeLeft.replace(" ago", "").replace(" from now", "")} - -
- )} - {bounty.applicantCount != null && ( -
- - {bounty.applicantCount} -
- )} +
+ + {timeLeft} + + {timeLeft.replace(" ago", "").replace(" from now", "")} + +