Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import SignIn from "@/components/login/sign-in";
import Image from "next/image";



export default async function Home() {
return (
<div className="flex justify-center items-center h-screen">
<SignIn />

</div>


Comment on lines +4 to +12
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several extra blank lines added here that don’t change behavior but add noise to the diff. Please remove the redundant whitespace to keep the file clean.

Suggested change
export default async function Home() {
return (
<div className="flex justify-center items-center h-screen">
<SignIn />
</div>
export default async function Home() {
return (
<div className="flex justify-center items-center h-screen">
<SignIn />
</div>

Copilot uses AI. Check for mistakes.
);
}
232 changes: 232 additions & 0 deletions components/bounty/bounty-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import * as React from "react";
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component attaches an onClick handler, which requires it to be a Client Component in the Next.js app router. Add a "use client"; directive at the top of the file (or remove the event handler and use a <Link>-based API instead).

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import * as React from "react" is unused in this file and will fail linting with no-unused-vars. Remove it, or use React types (e.g., for keyboard events) if needed.

Suggested change
import * as React from "react";

Copilot uses AI. Check for mistakes.
import {
Card,
CardContent,
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CardContent is imported but never used, which will trigger lint/build failures. Remove the import or refactor the layout to use CardContent.

Suggested change
CardContent,

Copilot uses AI. Check for mistakes.
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";
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DollarSign is imported from lucide-react but never used, which will trigger lint/build failures. Remove it or render it as part of the budget display.

Suggested change
import { Clock, Users, DollarSign } from "lucide-react";
import { Clock, Users } from "lucide-react";

Copilot uses AI. Check for mistakes.
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];
Comment on lines +23 to +46
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against unmapped bounty statuses.
statusConfig only covers open/claimed/closed. If the API sends another status (e.g., in‑progress/disputed per requirements), status becomes undefined and the render will throw. Add mappings for all statuses and/or a safe fallback.

🩹 Suggested hardening
-  const status = statusConfig[bounty.status];
+  const status =
+    statusConfig[bounty.status] ?? {
+      variant: "outline",
+      label: "Unknown",
+      dotColor: "bg-slate-400",
+    };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const statusConfig = {
open: {
variant: "default" as const,
label: "Open",
dotColor: "bg-emerald-500",
},
claimed: {
variant: "secondary" as const,
label: "Claimed",
dotColor: "bg-amber-500",
},
closed: {
variant: "outline" as const,
label: "Closed",
dotColor: "bg-slate-400",
},
};
const difficultyColors: Record<string, string> = {
beginner: "text-success-400",
intermediate: "text-warning-400",
advanced: "text-error-400",
}
export function BountyCard({
bounty,
onClick,
variant = "grid",
}: BountyCardProps) {
const status = statusConfig[bounty.status];
const statusConfig = {
open: {
variant: "default" as const,
label: "Open",
dotColor: "bg-emerald-500",
},
claimed: {
variant: "secondary" as const,
label: "Claimed",
dotColor: "bg-amber-500",
},
closed: {
variant: "outline" as const,
label: "Closed",
dotColor: "bg-slate-400",
},
};
export function BountyCard({
bounty,
onClick,
variant = "grid",
}: BountyCardProps) {
const status =
statusConfig[bounty.status] ?? {
variant: "outline",
label: "Unknown",
dotColor: "bg-slate-400",
};
🤖 Prompt for AI Agents
In `@components/bounty/bounty-card.tsx` around lines 23 - 46, The statusConfig
mapping and its use in BountyCard can return undefined for unexpected
bounty.status values; update statusConfig to include all possible statuses
(e.g., "in-progress", "disputed", etc.) and/or add a safe fallback before
rendering: compute a resolvedStatus = statusConfig[bounty.status] ?? /* default
mapping like closed/default */ and use resolvedStatus in the component. Ensure
the symbols referenced are statusConfig, BountyCard and the variable status (or
replace with resolvedStatus) so unknown API statuses won't cause a render error.

const timeLeft = formatDistanceToNow(bounty.deadline, { addSuffix: true });

return (
<Card
className={cn(
"overflow-hidden w-xs rounded-4xl cursor-pointer transition-all duration-300",
"hover:shadow-lg hover:border-primary/60 hover:scale-[1.02]",
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The classes w-xs and rounded-4xl look like they may be no-ops with the current Tailwind theme setup (e.g., app/globals.css only defines radius tokens up to --radius-xl, and there’s no --radius-4xl). If these utilities aren’t defined, the card won’t size/round as intended. Prefer standard utilities (e.g., w-full/max-w-*, rounded-xl/rounded-3xl) or an explicit arbitrary value (e.g., rounded-[...]).

Copilot uses AI. Check for mistakes.
"border border-slate-200 dark:border-slate-800",
variant === "list" && "flex flex-col md:flex-row",
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the list variant, md:flex-row is applied to the outer Card, which will place the footer beside the main content on medium screens (since CardFooter is a sibling). If the footer is intended to remain at the bottom, keep the Card as a column layout and only apply row layout to the internal content area.

Suggested change
variant === "list" && "flex flex-col md:flex-row",
variant === "list" && "flex flex-col",

Copilot uses AI. Check for mistakes.
)}
Comment on lines +53 to +58
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

List variant is still capped to max-w-xs.
With max-w-xs applied unconditionally, the list layout won’t expand to full width, which undermines the list variant’s responsive intent.

💡 Proposed fix
-      className={cn(
-        "overflow-hidden w-full max-w-xs  rounded-4xl cursor-pointer transition-all duration-300",
+      className={cn(
+        "overflow-hidden w-full rounded-4xl cursor-pointer transition-all duration-300",
+        variant === "grid" && "max-w-xs",
+        variant === "list" && "max-w-none",
         "hover:shadow-lg hover:border-primary/60 hover:scale-[1.02]",
         "border border-slate-200 dark:border-slate-800",
         variant === "list" && "flex flex-col",
       )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className={cn(
"overflow-hidden w-full max-w-xs rounded-4xl cursor-pointer transition-all duration-300",
"hover:shadow-lg hover:border-primary/60 hover:scale-[1.02]",
"border border-slate-200 dark:border-slate-800",
variant === "list" && "flex flex-col",
)}
className={cn(
"overflow-hidden w-full rounded-4xl cursor-pointer transition-all duration-300",
variant === "grid" && "max-w-xs",
variant === "list" && "max-w-none",
"hover:shadow-lg hover:border-primary/60 hover:scale-[1.02]",
"border border-slate-200 dark:border-slate-800",
variant === "list" && "flex flex-col",
)}
🤖 Prompt for AI Agents
In `@components/bounty/bounty-card.tsx` around lines 99 - 104, The card always
applies "max-w-xs" which prevents the list variant from expanding; update the
className construction in bounty-card.tsx (the cn(...) call where variant ===
"list" is checked) to apply "max-w-xs" only when variant !== "list" (or
alternatively apply a full-width class like "w-full" when variant === "list"),
so the list variant can use the full available width while preserving the
compact cap for other variants.

onClick={onClick}
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attaching onClick to a div-based Card makes it mouse-only by default. For accessibility, either render a semantic interactive element (e.g., <button>/<a> via asChild) or add role="button", tabIndex={0}, and keyboard handlers for Enter/Space (plus an accessible label if needed).

Copilot uses AI. Check for mistakes.
>
{/* Main Content Section */}

<div
className={cn(
"flex-1 flex flex-col",
variant === "list" && "md:flex-row md:items-center",
)}
>
<CardHeader
className={cn("pb-3 px-4 sm:px-5", variant === "list" && "md:flex-1 md:pb-0")}
>
{/* Header Row with Status and Budget */}

<div className="flex items-start justify-between gap-2 mb-3 flex-wrap">
<div className="flex items-center gap-2">
<div
className={cn(
"w-2.5 h-2.5 rounded-full animate-pulse",
status.dotColor,
)}
/>
<Badge variant={status.variant} className="text-xs">
{status.label}
</Badge>
</div>

{variant === "grid" && (
<div className="text-right">
<div className="text-lg font-bold text-slate-900 dark:text-slate-50">
{bounty.budget.amount.toLocaleString()}
</div>
<div className="text-[10px] text-slate-400 dark:text-slate-400 font-medium">
{bounty.budget.asset}
</div>
</div>
)}
</div>

{/* Title and Description */}

<CardTitle className="text-base font-semibold line-clamp-2 text-slate-900 dark:text-slate-50 mb-1">
{bounty.title}
</CardTitle>
<CardDescription className="line-clamp-2 text-xs text-slate-600 dark:text-slate-400">
{bounty.description}
</CardDescription>

{/* Category and Model Badges */}

<div className="flex flex-wrap gap-2 mt-3">
<Badge
variant="outline"
className="text-xs px-3 py-1 bg-[#f7fff0] dark:bg-slate-900 border-[#f2ffe5] dark:border-slate-700"
>
{bounty.category}
</Badge>
<Badge
variant="outline"
className="text-xs px-3 py-1 bg-[#f7fff0] dark:bg-slate-900 border-[#f2ffe5] dark:border-slate-700"
>
{modelNames[bounty.claimingModel]}
</Badge>
{bounty.milestoneCount && bounty.milestoneCount > 1 && (
<Badge
variant="outline"
className="text-xs px-3 py-1 bg-[#f7fff0] dark:bg-slate-900 border-[#f2ffe5] dark:border-slate-700"
>
{bounty.milestoneCount} milestones
</Badge>
)}
</div>
</CardHeader>

{/* List Variant Budget Display */}

{variant === "list" && (
<div className="px-4 sm:px-6 py-3 md:w-48 flex flex-col justify-center items-end border-t md:border-t-0 md:border-l border-slate-200 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-900/50">
<div className="text-2xl font-bold text-slate-900 dark:text-slate-50">
{bounty.budget.amount.toLocaleString()}
</div>
<div className="text-xs text-slate-500 dark:text-slate-400 font-medium">
{bounty.budget.asset}
</div>
</div>
)}
</div>

{/* Footer with Creator and Meta Info */}

<CardFooter className="border-t border-[#f0f0f0] dark:border-slate-700 bg-gradient-to-r from-slate-50/50 to-transparent dark:from-slate-900/50 dark:to-transparent flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-4 justify-between text-xs text-slate-600 dark:text-slate-400 py-3 px-4 sm:px-5">
{/* Creator Info */}
<div className="flex items-center gap-2 min-w-0 order-1 sm:order-none">
<Avatar className="h-6 w-6 border border-slate-200 dark:border-slate-700 flex-shrink-0">
<AvatarImage src={bounty.creator.avatar} />
<AvatarFallback className="text-xs font-medium">
{bounty.creator.displayName?.[0]?.toUpperCase() ||
bounty.creator.wallet.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
<span className="truncate text-xs font-medium text-slate-700 dark:text-slate-300">
{bounty.creator.displayName ||
`${bounty.creator.wallet.slice(0, 8)}...`}
</span>
</div>

{/* Meta Information */}

<div className="flex items-center gap-2 sm:gap-3 flex-shrink-0 order-2 sm:order-none">
{bounty.deadline && (
<div className="flex items-center gap-1 text-slate-600 dark:text-slate-400 whitespace-nowrap text-xs">
<Clock className="h-3.5 w-3.5 flex-shrink-0" />
<span className="hidden sm:inline">{timeLeft}</span>
<span className="sm:hidden">{timeLeft.replace(" ago", "").replace(" from now", "")}</span>
</div>
)}
{bounty.applicantCount != null && bounty.applicantCount > 0 && (
<div className="flex items-center gap-1 text-slate-600 dark:text-slate-400 whitespace-nowrap text-xs">
<Users className="h-3.5 w-3.5 flex-shrink-0" />
<span>{bounty.applicantCount}</span>
</div>
)}
</div>
</CardFooter>
</Card>
);
}
Loading