diff --git a/.gitignore b/.gitignore index f650315..5ef6a52 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,16 @@ # dependencies /node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage # next.js /.next/ @@ -10,13 +20,17 @@ # production /build +# misc +.DS_Store +*.pem + # debug npm-debug.log* yarn-debug.log* yarn-error.log* .pnpm-debug.log* -# env files +# env files (can opt-in for committing if needed) .env* # vercel @@ -24,4 +38,4 @@ yarn-error.log* # typescript *.tsbuildinfo -next-env.d.ts \ No newline at end of file +next-env.d.ts diff --git a/README.md b/README.md index 8a836dc..e215bc4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,36 @@ -Real-Time Sports Fan Staked Token Prediction Battles for the Chiliz Paris Hackathon 2025 +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -## Deployment +## Getting Started -Project is live at: +First, run the development server: -**[https://vercel.com/destefaniandrei2-5621s-projects/v0-matchmind-dapp-design](https://vercel.com/destefaniandrei2-5621s-projects/v0-matchmind-dapp-design)** +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/app/admin/page.tsx b/app/admin/page.tsx deleted file mode 100644 index a542468..0000000 --- a/app/admin/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Header } from "@/components/layout/header" -import { AdminDashboard } from "@/components/admin/admin-dashboard" -import { MatchManagement } from "@/components/admin/match-management" -import { QuestionManagement } from "@/components/admin/question-management" -import { PayoutManagement } from "@/components/admin/payout-management" - -export default function AdminPage() { - return ( -
-
-
-

Admin Panel

-
- -
- - -
- -
-
-
- ) -} diff --git a/app/globals.css b/app/globals.css deleted file mode 100644 index ac68442..0000000 --- a/app/globals.css +++ /dev/null @@ -1,94 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -body { - font-family: Arial, Helvetica, sans-serif; -} - -@layer utilities { - .text-balance { - text-wrap: balance; - } -} - -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 0 0% 3.9%; - --card: 0 0% 100%; - --card-foreground: 0 0% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - --primary: 0 0% 9%; - --primary-foreground: 0 0% 98%; - --secondary: 0 0% 96.1%; - --secondary-foreground: 0 0% 9%; - --muted: 0 0% 96.1%; - --muted-foreground: 0 0% 45.1%; - --accent: 0 0% 96.1%; - --accent-foreground: 0 0% 9%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 89.8%; - --input: 0 0% 89.8%; - --ring: 0 0% 3.9%; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - --radius: 0.5rem; - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; - --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } - .dark { - --background: 0 0% 3.9%; - --foreground: 0 0% 98%; - --card: 0 0% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 0 0% 9%; - --secondary: 0 0% 14.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 14.9%; - --muted-foreground: 0 0% 63.9%; - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 14.9%; - --input: 0 0% 14.9%; - --ring: 0 0% 83.1%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} diff --git a/app/layout.tsx b/app/layout.tsx deleted file mode 100644 index 162d3f6..0000000 --- a/app/layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type React from "react" -import type { Metadata } from "next" -import { Inter } from "next/font/google" -import "./globals.css" -import { Toaster } from "@/components/ui/toaster" -import { WalletProvider } from "@/contexts/wallet-context" -import { ThemeProvider } from "@/components/theme-provider" - -const inter = Inter({ subsets: ["latin"] }) - -export const metadata: Metadata = { - title: "MatchMind - Football Prediction dApp", - description: "Stake CHZ and predict football matches to earn dividend rewards", - generator: 'v0.dev' -} - -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { - return ( - - - - - {children} - - - - - - ) -} diff --git a/app/match/[id]/page.tsx b/app/match/[id]/page.tsx deleted file mode 100644 index 53aab61..0000000 --- a/app/match/[id]/page.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Header } from "@/components/layout/header" -import { LiveMatch } from "@/components/match/live-match" -import { MatchLeaderboard } from "@/components/match/match-leaderboard" - -interface MatchPageProps { - params: { - id: string - } -} - -export default function MatchPage({ params }: MatchPageProps) { - return ( -
-
-
-
-
- -
-
- -
-
-
-
- ) -} diff --git a/app/page.tsx b/app/page.tsx deleted file mode 100644 index 2cf176b..0000000 --- a/app/page.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Header } from "@/components/layout/header" -import { MatchList } from "@/components/matches/match-list" -import { HeroSection } from "@/components/landing/hero-section" -import { StatsSection } from "@/components/landing/stats-section" - -export default function HomePage() { - return ( -
-
-
- -
-
-

Live & Upcoming Matches

- -
- -
-
-
- ) -} diff --git a/app/results/[id]/page.tsx b/app/results/[id]/page.tsx deleted file mode 100644 index 6033026..0000000 --- a/app/results/[id]/page.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Header } from "@/components/layout/header" -import { MatchResults } from "@/components/results/match-results" -import { FinalLeaderboard } from "@/components/results/final-leaderboard" -import { RewardSummary } from "@/components/results/reward-summary" - -interface ResultsPageProps { - params: { - id: string - } -} - -export default function ResultsPage({ params }: ResultsPageProps) { - return ( -
-
-
-

Match Results

-
-
- - -
-
- -
-
-
-
- ) -} diff --git a/app/stake/page.tsx b/app/stake/page.tsx deleted file mode 100644 index 03057fe..0000000 --- a/app/stake/page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Header } from "@/components/layout/header" -import { StakeForm } from "@/components/stake/stake-form" -import { StakeInfo } from "@/components/stake/stake-info" - -export default function StakePage() { - return ( -
-
-
-
-

Stake CHZ

-
- - -
-
-
-
- ) -} diff --git a/components.json b/components.json deleted file mode 100644 index d9ef0ae..0000000 --- a/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "tailwind.config.ts", - "css": "app/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} \ No newline at end of file diff --git a/components/admin/admin-dashboard.tsx b/components/admin/admin-dashboard.tsx deleted file mode 100644 index bc0b407..0000000 --- a/components/admin/admin-dashboard.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { Users, Trophy, Coins, Activity } from "lucide-react" - -export function AdminDashboard() { - const stats = [ - { - title: "Active Matches", - value: "3", - description: "Currently running", - icon: Activity, - color: "text-blue-500", - }, - { - title: "Total Players", - value: "2,847", - description: "Registered users", - icon: Users, - color: "text-green-500", - }, - { - title: "Total Staked", - value: "125,000 CHZ", - description: "Across all matches", - icon: Coins, - color: "text-yellow-500", - }, - { - title: "Completed Matches", - value: "156", - description: "Since launch", - icon: Trophy, - color: "text-purple-500", - }, - ] - - return ( -
- {stats.map((stat, index) => ( - - - {stat.title} - - - -
{stat.value}
-

{stat.description}

-
-
- ))} -
- ) -} diff --git a/components/admin/match-management.tsx b/components/admin/match-management.tsx deleted file mode 100644 index 74a79db..0000000 --- a/components/admin/match-management.tsx +++ /dev/null @@ -1,97 +0,0 @@ -"use client" - -import { useState } from "react" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { Button } from "@/components/ui/button" -import { Badge } from "@/components/ui/badge" -import { useToast } from "@/hooks/use-toast" -import { Play, Square, Lock } from "lucide-react" -import { mockMatches } from "@/lib/mock-data" - -export function MatchManagement() { - const [matches, setMatches] = useState(mockMatches) - const { toast } = useToast() - - const handleStartMatch = (matchId: string) => { - setMatches((prev) => prev.map((match) => (match.id === matchId ? { ...match, status: "live" as const } : match))) - toast({ - title: "Match Started", - description: "Match is now live and accepting predictions", - }) - } - - const handleLockMatch = (matchId: string) => { - setMatches((prev) => - prev.map((match) => (match.id === matchId ? { ...match, status: "completed" as const } : match)), - ) - toast({ - title: "Match Locked", - description: "Match has been completed and locked", - }) - } - - const getStatusColor = (status: string) => { - switch (status) { - case "live": - return "bg-red-500" - case "upcoming": - return "bg-blue-500" - case "completed": - return "bg-green-500" - default: - return "bg-gray-500" - } - } - - return ( - - - Match Management - - -
- {matches.slice(0, 4).map((match) => ( -
-
-
- - {match.homeTeam} vs {match.awayTeam} - - {match.status.toUpperCase()} -
-
- {match.participants} players • {match.totalStake} CHZ staked -
-
-
- {match.status === "upcoming" && ( - - )} - {match.status === "live" && ( - - )} - {match.status === "completed" && ( - - )} -
-
- ))} -
-
-
- ) -} diff --git a/components/admin/payout-management.tsx b/components/admin/payout-management.tsx deleted file mode 100644 index a487fe6..0000000 --- a/components/admin/payout-management.tsx +++ /dev/null @@ -1,156 +0,0 @@ -"use client" - -import { useState } from "react" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { Button } from "@/components/ui/button" -import { Badge } from "@/components/ui/badge" -import { useToast } from "@/hooks/use-toast" -import { Coins, Send, Clock } from "lucide-react" - -interface PayoutMatch { - id: string - homeTeam: string - awayTeam: string - totalStake: number - totalRewards: number - status: "pending" | "processing" | "completed" - completedAt: string -} - -export function PayoutManagement() { - const [payoutMatches, setPayoutMatches] = useState([ - { - id: "3", - homeTeam: "Chelsea", - awayTeam: "Arsenal", - totalStake: 18750, - totalRewards: 1875, - status: "pending", - completedAt: "2024-01-14T19:30:00Z", - }, - { - id: "6", - homeTeam: "Juventus", - awayTeam: "AC Milan", - totalStake: 13670, - totalRewards: 1367, - status: "completed", - completedAt: "2024-01-13T21:45:00Z", - }, - ]) - - const { toast } = useToast() - - const handleTriggerPayout = (matchId: string) => { - setPayoutMatches((prev) => - prev.map((match) => (match.id === matchId ? { ...match, status: "processing" as const } : match)), - ) - - toast({ - title: "Payout Initiated", - description: "Dividend distribution has been started for this match", - }) - - // Simulate payout completion - setTimeout(() => { - setPayoutMatches((prev) => - prev.map((match) => (match.id === matchId ? { ...match, status: "completed" as const } : match)), - ) - - toast({ - title: "Payout Completed", - description: "All dividends have been successfully distributed", - }) - }, 3000) - } - - const getStatusColor = (status: string) => { - switch (status) { - case "pending": - return "bg-yellow-500" - case "processing": - return "bg-blue-500" - case "completed": - return "bg-green-500" - default: - return "bg-gray-500" - } - } - - const getStatusIcon = (status: string) => { - switch (status) { - case "pending": - return - case "processing": - return - case "completed": - return - default: - return null - } - } - - return ( - - - Payout Management - - -
- {payoutMatches.map((match) => ( -
-
-
- - {match.homeTeam} vs {match.awayTeam} - - -
- {getStatusIcon(match.status)} - {match.status.toUpperCase()} -
-
-
-
-
Total Stake: {match.totalStake.toLocaleString()} CHZ
-
Dividend Pool: {match.totalRewards.toLocaleString()} CHZ
-
Completed: {new Date(match.completedAt).toLocaleString()}
-
-
-
- {match.status === "pending" && ( - - )} - {match.status === "processing" && ( - - )} - {match.status === "completed" && ( - - )} -
-
- ))} -
- -
-

Payout Information

-
    -
  • • Payouts are triggered manually after match completion
  • -
  • • Dividends are distributed based on final leaderboard rankings
  • -
  • • Principal stakes are always returned to users
  • -
  • • Processing typically takes 2-5 minutes to complete
  • -
-
-
-
- ) -} diff --git a/components/admin/question-management.tsx b/components/admin/question-management.tsx deleted file mode 100644 index 142cb03..0000000 --- a/components/admin/question-management.tsx +++ /dev/null @@ -1,176 +0,0 @@ -"use client" - -import { useState } from "react" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { Textarea } from "@/components/ui/textarea" -import { useToast } from "@/hooks/use-toast" -import { Plus, Check, X } from "lucide-react" - -interface PendingQuestion { - id: string - matchId: string - text: string - options: string[] - submittedAt: string -} - -export function QuestionManagement() { - const [pendingQuestions] = useState([ - { - id: "1", - matchId: "1", - text: "Will there be a red card in the next 10 minutes?", - options: ["Yes", "No"], - submittedAt: "2024-01-15T14:30:00Z", - }, - { - id: "2", - matchId: "1", - text: "Which player will touch the ball next?", - options: ["Midfielder", "Defender", "Forward"], - submittedAt: "2024-01-15T14:25:00Z", - }, - ]) - - const [newQuestion, setNewQuestion] = useState("") - const [newOptions, setNewOptions] = useState(["", ""]) - const { toast } = useToast() - - const handleApproveQuestion = (questionId: string) => { - toast({ - title: "Question Approved", - description: "Question has been approved and will be used in the match", - }) - } - - const handleRejectQuestion = (questionId: string) => { - toast({ - title: "Question Rejected", - description: "Question has been rejected and removed from queue", - variant: "destructive", - }) - } - - const handleAddOption = () => { - setNewOptions([...newOptions, ""]) - } - - const handleOptionChange = (index: number, value: string) => { - const updated = [...newOptions] - updated[index] = value - setNewOptions(updated) - } - - const handleSubmitQuestion = () => { - if (!newQuestion.trim() || newOptions.some((opt) => !opt.trim())) { - toast({ - title: "Invalid Question", - description: "Please fill in all fields", - variant: "destructive", - }) - return - } - - toast({ - title: "Question Submitted", - description: "New question has been added to the approval queue", - }) - - setNewQuestion("") - setNewOptions(["", ""]) - } - - return ( - - - Question Management - - - {/* Pending Questions */} -
-

Pending Approval

-
- {pendingQuestions.map((question) => ( -
-

{question.text}

-
- {question.options.map((option, index) => ( - - {option} - - ))} -
-
- - -
-
- ))} -
-
- - {/* Add New Question */} -
-

Add New Question

-
-
- -