From b7b4235419fdce593e5b08f20fa00a8c19e8420f Mon Sep 17 00:00:00 2001 From: AbuJulaybeeb Date: Wed, 25 Mar 2026 08:56:33 +0100 Subject: [PATCH 1/9] feat: implement open competitions countdown cards with radix progress --- .../app/(authenticated)/competitions/page.tsx | 59 ++++++++- .../competitions/CompetitionCard.tsx | 120 ++++++++++++++++++ 2 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 frontend/src/component/competitions/CompetitionCard.tsx diff --git a/frontend/src/app/(authenticated)/competitions/page.tsx b/frontend/src/app/(authenticated)/competitions/page.tsx index b438e761..6c6ada10 100644 --- a/frontend/src/app/(authenticated)/competitions/page.tsx +++ b/frontend/src/app/(authenticated)/competitions/page.tsx @@ -1,5 +1,58 @@ -import { notFound } from "next/navigation"; +import { CompetitionCard, ICompetition } from "@/component/competitions/CompetitionCard"; +import { ChevronDown } from "lucide-react"; + +// Mock Data based on Acceptance Criteria +const MOCK_COMPETITIONS: ICompetition[] = [ + { + id: "comp_1", + tag: "Crypto", + prizePool: 100000, + title: "Quarterly Trading Championship", + features: ["Max leverage 10x", "Live updates", "Portfolio > $1,000"], + currentParticipants: 125, + maxParticipants: 500, + // Set exactly 12 days, 8 hours, 45 mins from now for the demo + endTime: new Date(Date.now() + 1075500000).toISOString(), + }, + { + id: "comp_2", + tag: "Web3", + prizePool: 25000, + title: "DeFi Yield Farming Sprint", + features: ["DEX integration only", "No minimum portfolio", "Weekly snapshot"], + currentParticipants: 450, + maxParticipants: 1000, + endTime: new Date(Date.now() + 432000000).toISOString(), + }, +]; export default function CompetitionsPage() { - notFound(); -} + return ( +
+ {/* Featured Block Placeholder (Assumed to exist above this per issue) */} +
+ Featured Block Placeholder +
+ + {/* Header Section */} +
+

+ Open Competitions +

+ + {/* Sort Dropdown Simulator */} +
+ Sort by: Time left + +
+
+ + {/* 2-Column Grid / Masonry Layout */} +
+ {MOCK_COMPETITIONS.map((competition) => ( + + ))} +
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/component/competitions/CompetitionCard.tsx b/frontend/src/component/competitions/CompetitionCard.tsx new file mode 100644 index 00000000..0d536af7 --- /dev/null +++ b/frontend/src/component/competitions/CompetitionCard.tsx @@ -0,0 +1,120 @@ +"use client"; + +import { useEffect, useState } from "react"; +import * as Progress from "@radix-ui/react-progress"; +import { CheckCircle2 } from "lucide-react"; + +export interface ICompetition { + id: string; + tag: string; + prizePool: number; + title: string; + features: string[]; + currentParticipants: number; + maxParticipants: number; + endTime: string; // ISO Date string +} + +export function CompetitionCard({ competition }: { competition: ICompetition }) { + const [timeLeft, setTimeLeft] = useState({ days: 0, hours: 0, mins: 0 }); + const [progress, setProgress] = useState(0); + + // Calculate Progress + useEffect(() => { + const ratio = (competition.currentParticipants / competition.maxParticipants) * 100; + // Slight delay for animation effect on mount + const timer = setTimeout(() => setProgress(ratio), 100); + return () => clearTimeout(timer); + }, [competition]); + + // Countdown Logic + useEffect(() => { + const calculateTimeLeft = () => { + const difference = new Date(competition.endTime).getTime() - new Date().getTime(); + if (difference > 0) { + setTimeLeft({ + days: Math.floor(difference / (1000 * 60 * 60 * 24)), + hours: Math.floor((difference / (1000 * 60 * 60)) % 24), + mins: Math.floor((difference / 1000 / 60) % 60), + }); + } + }; + + calculateTimeLeft(); + const timer = setInterval(calculateTimeLeft, 60000); // Update every minute + return () => clearInterval(timer); + }, [competition.endTime]); + + return ( +
+ {/* Top Tag */} +
+ + {competition.tag} + +
+ + {/* Prize Pool & Title */} +

+ ${competition.prizePool.toLocaleString()} +

+

{competition.title}

+ + {/* Feature Bullets */} + + +
+ {/* Progress Bar */} +
+
+ Participants + + {competition.currentParticipants} / {competition.maxParticipants} + +
+ + + +
+ + {/* Mega Countdown */} +
+
+
+ {timeLeft.days} + Days +
+ : +
+ {timeLeft.hours} + Hours +
+ : +
+ {timeLeft.mins} + Mins +
+
+
+ + {/* Action Button */} + +
+
+ ); +} \ No newline at end of file From 1ab34c4ddb4560daa050dfba9ff8cfaab95d8dc2 Mon Sep 17 00:00:00 2001 From: Antigravity Date: Thu, 26 Mar 2026 11:56:01 +0100 Subject: [PATCH 2/9] feat: move competition cards to new Event page and update navigation --- .../app/(authenticated)/competitions/page.tsx | 64 ++---- frontend/src/app/events/page.tsx | 195 ++++++++++++++++++ frontend/src/component/Header.tsx | 53 +++-- 3 files changed, 235 insertions(+), 77 deletions(-) create mode 100644 frontend/src/app/events/page.tsx diff --git a/frontend/src/app/(authenticated)/competitions/page.tsx b/frontend/src/app/(authenticated)/competitions/page.tsx index 6c6ada10..232c1805 100644 --- a/frontend/src/app/(authenticated)/competitions/page.tsx +++ b/frontend/src/app/(authenticated)/competitions/page.tsx @@ -1,57 +1,21 @@ -import { CompetitionCard, ICompetition } from "@/component/competitions/CompetitionCard"; -import { ChevronDown } from "lucide-react"; - -// Mock Data based on Acceptance Criteria -const MOCK_COMPETITIONS: ICompetition[] = [ - { - id: "comp_1", - tag: "Crypto", - prizePool: 100000, - title: "Quarterly Trading Championship", - features: ["Max leverage 10x", "Live updates", "Portfolio > $1,000"], - currentParticipants: 125, - maxParticipants: 500, - // Set exactly 12 days, 8 hours, 45 mins from now for the demo - endTime: new Date(Date.now() + 1075500000).toISOString(), - }, - { - id: "comp_2", - tag: "Web3", - prizePool: 25000, - title: "DeFi Yield Farming Sprint", - features: ["DEX integration only", "No minimum portfolio", "Weekly snapshot"], - currentParticipants: 450, - maxParticipants: 1000, - endTime: new Date(Date.now() + 432000000).toISOString(), - }, -]; +import { useRouter } from "next/navigation"; +import { useEffect } from "react"; export default function CompetitionsPage() { - return ( -
- {/* Featured Block Placeholder (Assumed to exist above this per issue) */} -
- Featured Block Placeholder -
+ const router = useRouter(); - {/* Header Section */} -
-

- Open Competitions -

- - {/* Sort Dropdown Simulator */} -
- Sort by: Time left - -
-
+ useEffect(() => { + router.push("/events"); + }, [router]); - {/* 2-Column Grid / Masonry Layout */} -
- {MOCK_COMPETITIONS.map((competition) => ( - - ))} + return ( +
+
+

Redirecting...

+

Competitions have moved to the new Events page.

+ + Click here if you are not redirected +
); diff --git a/frontend/src/app/events/page.tsx b/frontend/src/app/events/page.tsx new file mode 100644 index 00000000..96735569 --- /dev/null +++ b/frontend/src/app/events/page.tsx @@ -0,0 +1,195 @@ +"use client"; + +import React from "react"; +import Head from "next/head"; +import Header from "@/component/Header"; +import Footer from "@/component/Footer"; +import UnifiedBackground from "@/component/Homepage/UnifiedBackground"; +import { CompetitionCard, ICompetition } from "@/component/competitions/CompetitionCard"; +import { Search, ChevronDown, Filter } from "lucide-react"; + +// Mock Data for "Featured This Week" +const FEATURED_EVENTS = [ + { + id: "feat_1", + tag: "Trading Competition", + title: "Elite Traders Championship", + description: "Compete with the best traders globally for a $50,000 prize pool.", + stats: { participants: 1247, prize: "$58,000", time: "2d 14h" }, + image: "/feat1.png", // Placeholder + }, + { + id: "feat_2", + tag: "Live Analysis", + title: "Market Insights Summit", + description: "Join top analysts discussing crypto market trends and strategies.", + stats: { participants: 3421, prize: "$10,000", time: "5h 23m" }, + image: "/feat2.png", // Placeholder + }, + { + id: "feat_3", + tag: "DeFi Workshop", + title: "Advanced DeFi Strategies", + description: "Learn cutting-edge DeFi techniques from industry experts.", + stats: { participants: 812, prize: "$5,000", time: "1d 8h" }, + image: "/feat3.png", // Placeholder + }, +]; + +// Mock Data for "Open Competitions" (Moved from competitions/page.tsx) +const OPEN_COMPETITIONS: ICompetition[] = [ + { + id: "comp_1", + tag: "Spot Trading", + prizePool: 100000, + title: "Quarterly Trading Championship", + features: ["Minimum 10 trades", "KYC verified", "Portfolio > $1,000"], + currentParticipants: 847, + maxParticipants: 1000, + endTime: new Date(Date.now() + 1075500000).toISOString(), + }, + { + id: "comp_2", + tag: "Futures", + prizePool: 75000, + title: "Leverage Masters Contest", + features: ["Futures experience", "Risk score > 70", "Active for 30 days"], + currentParticipants: 623, + maxParticipants: 1000, + endTime: new Date(Date.now() + 750000000).toISOString(), + }, +]; + +export default function EventsPage() { + return ( + <> +
+ + +
+
+ +
+ {/* Hero Section */} +
+

+ Public Events & Competitions +

+

+ Join live trading competitions, connect with top analysts, and win exclusive rewards. +

+
+ + +
+
+ + {/* Search and Filters */} +
+
+
+ + +
+
+ {["All", "Events", "Competitions", "Past"].map((cat) => ( + + ))} +
+
+ + +
+
+
+ + {/* Featured Section */} +
+
+

+ Featured This Week + +

+
+
+ {FEATURED_EVENTS.map((event) => ( +
+
+
+ Image Placeholder +
+
+
+ + {event.tag} + +

+ {event.title} +

+

+ {event.description} +

+
+
+ Participants + {event.stats.participants} +
+
+ Prize Pool + {event.stats.prize} +
+
+ Ends In + {event.stats.time} +
+
+ +
+
+ ))} +
+
+ + {/* Open Competitions Section */} +
+
+

Open Competitions

+ +
+
+ {OPEN_COMPETITIONS.map((competition) => ( + + ))} +
+
+
+ +
+
+
+ + ); +} diff --git a/frontend/src/component/Header.tsx b/frontend/src/component/Header.tsx index 70f3ede5..b1766f32 100644 --- a/frontend/src/component/Header.tsx +++ b/frontend/src/component/Header.tsx @@ -1,43 +1,42 @@ "use client"; import Link from "next/link"; +import { Search, Bell } from "lucide-react"; export default function Header() { return ( -
+
From fa6a6130958757fd21e3b31a256a228f2cc00e53 Mon Sep 17 00:00:00 2001 From: Antigravity Date: Thu, 26 Mar 2026 12:26:13 +0100 Subject: [PATCH 3/9] fix: resolve build errors by adding missing 'use client' and cleaning merge conflict markers --- .../app/(authenticated)/competitions/page.tsx | 2 + frontend/src/app/events/page.tsx | 403 +++++------------- 2 files changed, 115 insertions(+), 290 deletions(-) diff --git a/frontend/src/app/(authenticated)/competitions/page.tsx b/frontend/src/app/(authenticated)/competitions/page.tsx index 232c1805..b5b08f25 100644 --- a/frontend/src/app/(authenticated)/competitions/page.tsx +++ b/frontend/src/app/(authenticated)/competitions/page.tsx @@ -1,3 +1,5 @@ +"use client"; + import { useRouter } from "next/navigation"; import { useEffect } from "react"; diff --git a/frontend/src/app/events/page.tsx b/frontend/src/app/events/page.tsx index 33b14083..caa0d8de 100644 --- a/frontend/src/app/events/page.tsx +++ b/frontend/src/app/events/page.tsx @@ -1,8 +1,6 @@ "use client"; -<<<<<<< feat/93-open-competitions-cards -import React from "react"; -import Head from "next/head"; +import React, { useState } from "react"; import Header from "@/component/Header"; import Footer from "@/component/Footer"; import UnifiedBackground from "@/component/Homepage/UnifiedBackground"; @@ -61,69 +59,6 @@ const OPEN_COMPETITIONS: ICompetition[] = [ }, ]; -export default function EventsPage() { - return ( - <> -
- - -
-
- -
- {/* Hero Section */} -
-

- Public Events & Competitions -

-

- Join live trading competitions, connect with top analysts, and win exclusive rewards. -

-
- - -
-
- - {/* Search and Filters */} -
-
-
- - -
-
- {["All", "Events", "Competitions", "Past"].map((cat) => ( - - ))} -
-
- - - -
-
+
+ {/* Hero Section */} +
+

+ Public Events & Competitions +

+

+ Join live trading competitions, connect with top analysts, and win exclusive rewards. +

+
+ +
+
- {/* Search and Filter Bar */} -
-
- {/* Search Input */} -
- - -
- - {/* Tabs */} -
- {tabs.map((tab) => ( - - ))} -
- - {/* Filter and Sort */} -
- - -
+ ))} +
+
+ +
+
-<<<<<<< feat/93-open-competitions-cards - {/* Featured Section */} -
-
-

- Featured This Week - -

-
-
- {FEATURED_EVENTS.map((event) => ( -
-
-
- Image Placeholder + {/* Featured Section */} +
+
+

+ Featured This Week + +

+
+
+ {FEATURED_EVENTS.map((event) => ( +
+
+
+ Image Placeholder +
+
+
+ + {event.tag} + +

+ {event.title} +

+

+ {event.description} +

+
+
+ Participants + {event.stats.participants} +
+
+ Prize Pool + {event.stats.prize} +
+
+ Ends In + {event.stats.time}
-
- - {event.tag} - -

- {event.title} -

-

- {event.description} -

-
-
- Participants - {event.stats.participants} -
-
- Prize Pool - {event.stats.prize} -
-
- Ends In - {event.stats.time} -
-
- -
+
- ))} -
-
- - {/* Open Competitions Section */} -
-
-

Open Competitions

- -
-
- {OPEN_COMPETITIONS.map((competition) => ( - - ))} -
-
-
+
+ ))} +
+ -
-
-
- -======= - {/* Events Grid */} - - - + {/* Open Competitions Section */} +
+
+

Open Competitions

+ +
+
+ {OPEN_COMPETITIONS.map((competition) => ( + + ))} +
+
+