diff --git a/src/app/_components/leaderboard.tsx b/src/app/_components/leaderboard.tsx index 0edfdfe..8b1cbc6 100644 --- a/src/app/_components/leaderboard.tsx +++ b/src/app/_components/leaderboard.tsx @@ -1,11 +1,16 @@ 'use client' import type React from 'react' -import { type ComponentPropsWithoutRef, Fragment, useRef } from 'react' +import { + type ComponentPropsWithoutRef, + Fragment, + useRef, + useState, +} from 'react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' -import { Card, CardContent, CardHeader } from '@/components/ui/card' +import { CardContent } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Table, @@ -34,7 +39,6 @@ import { } from 'lucide-react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' -import { useState } from 'react' export function LeaderboardPage() { const router = useRouter() @@ -118,41 +122,44 @@ export function LeaderboardPage() { } return ( -
+
- - +
+
-

- +

+ Leaderboards

-

+

View player rankings and statistics

- - {currentLeaderboard.length} Players + + + {currentLeaderboard.length} Players + -
- +
- + Ranked Leaderboard Vanilla Leaderboard
- + setSearchQuery(e.target.value)} /> @@ -201,7 +208,7 @@ export function LeaderboardPage() { - +
) @@ -228,7 +235,6 @@ function LeaderboardTable({ // Set a fixed row height for virtualization const ROW_HEIGHT = 39 // Adjust based on your actual row height - console.log(leaderboard.length) // Create virtualizer instance const rowVirtualizer = useVirtualizer({ count: leaderboard.length, @@ -239,8 +245,6 @@ function LeaderboardTable({ // Get the virtualized rows const virtualRows = rowVirtualizer.getVirtualItems() - console.log({ virtualRows }) - console.log(rowVirtualizer.getTotalSize()) const paddingTop = virtualRows.length > 0 ? (virtualRows?.[0]?.start ?? 0) : 0 const paddingBottom = virtualRows.length > 0 @@ -255,8 +259,8 @@ function LeaderboardTable({ style={{ maxHeight: 'calc(100vh - 300px)' }} > - - + + @@ -393,7 +397,7 @@ function LeaderboardTable({
{Math.round(entry.peak_mmr)} - +
@@ -405,7 +409,7 @@ function LeaderboardTable({ ? 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-800 dark:bg-emerald-950 dark:text-emerald-300' : winrate < 40 ? 'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-800 dark:bg-rose-950 dark:text-rose-300' - : 'border-slate-200 bg-slate-50 text-slate-700 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-300' + : 'border-gray-200 bg-gray-50 text-gray-700 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300' )} > {Math.round(winrate)}% @@ -444,7 +448,9 @@ function LeaderboardTable({ ) : ( - No players found +

+ No players found +

)} @@ -483,7 +489,7 @@ function SortableHeader({
- + Game Type Opponent @@ -367,7 +371,7 @@ export function UserInfo() { {filteredGames.map((game) => ( {game.mmrChange > 0 ? ( - {numberFormatter.format( Math.trunc(game.mmrChange) )} + ) : ( - {numberFormatter.format( Math.trunc(game.mmrChange) )} + )} @@ -412,7 +416,7 @@ export function UserInfo() { ? 'border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-800 dark:bg-violet-950 dark:text-violet-300' : game.gameType.toLowerCase() === 'vanilla' ? 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-300' - : 'border-slate-200 bg-slate-50 text-slate-700 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-300' + : 'border-zinc-200 bg-zinc-50 text-zinc-700 dark:border-zinc-800 dark:bg-zinc-700 dark:text-zinc-300' )} > {game.gameType === 'ranked' @@ -483,8 +487,8 @@ export function UserInfo() { !vanillaLeaderboard && !lastGameLeaderboard1 && !lastGameLeaderboard2 && ( -
-

+

+

No leaderboard data available

@@ -493,8 +497,8 @@ export function UserInfo() { -
-

+

+

Achievements coming soon

@@ -525,11 +529,11 @@ function StatsCard({ return (
{icon}
-

+

{title}

{value}

-

+

{description}

@@ -552,11 +556,11 @@ function LeaderboardStatsCard({ accentColor = 'text-violet-500', }: LeaderboardStatsCardProps) { return ( -
+
@@ -567,8 +571,8 @@ function LeaderboardStatsCard({
{rank !== undefined && ( -
-

Rank

+
+

Rank

#{rank}

@@ -576,15 +580,15 @@ function LeaderboardStatsCard({ )} {mmr !== undefined && ( -
-

MMR

+
+

MMR

{mmr}

)} {rank === undefined && mmr === undefined && ( -
-

+

+

No data available

diff --git a/src/components/header.tsx b/src/components/header.tsx new file mode 100644 index 0000000..4cb1dc0 --- /dev/null +++ b/src/components/header.tsx @@ -0,0 +1,206 @@ +'use client' + +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' +import { Button } from '@/components/ui/button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' +import { LogIn, LogOut, Menu, Moon, Settings, Sun, User, X } from 'lucide-react' +import { useTheme } from 'next-themes' +import Link from 'next/link' +import { useState } from 'react' + +export function MainHeader() { + const { setTheme, theme } = useTheme() + const [mobileMenuOpen, setMobileMenuOpen] = useState(false) + + // Mock authentication state - replace with your actual auth logic + const [isAuthenticated, setIsAuthenticated] = useState(false) + + // Mock user data - replace with your actual user data + const userData = { + name: 'Player123', + avatar: '/placeholder.svg?height=40&width=40', + } + + // Toggle authentication for demo purposes + const toggleAuth = () => { + setIsAuthenticated(!isAuthenticated) + } + + return ( +
+
+
+ {/* Logo and Brand */} +
+ + + Balatro Multiplayer + + +
+ + {/* Desktop Navigation */} + + + {/* Actions: Theme Toggle, Sign In/User Menu */} +
+ {/* Theme Toggle */} + + + + + + setTheme('light')}> + Light + + setTheme('dark')}> + Dark + + setTheme('system')}> + System + + + + + {/* Sign In Button or User Menu */} + {isAuthenticated ? ( + + + + + +
+
+

{userData.name}

+
+
+ + + + Profile + + + + + + Settings + + + + + Log out + +
+
+ ) : ( + + )} + + {/* Mobile Menu Toggle */} + +
+
+
+ + {/* Mobile Navigation */} + {mobileMenuOpen && ( +
+ +
+ )} +
+ ) +} diff --git a/src/components/mode-toggle.tsx b/src/components/mode-toggle.tsx new file mode 100644 index 0000000..34ae2fa --- /dev/null +++ b/src/components/mode-toggle.tsx @@ -0,0 +1,40 @@ +'use client' + +import { Moon, Sun } from 'lucide-react' +import { useTheme } from 'next-themes' +import * as React from 'react' + +import { Button } from '@/components/ui/button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' + +export function ModeToggle() { + const { setTheme } = useTheme() + + return ( + + + + + + setTheme('light')}> + Light + + setTheme('dark')}> + Dark + + setTheme('system')}> + System + + + + ) +} diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx new file mode 100644 index 0000000..0253d2a --- /dev/null +++ b/src/components/theme-provider.tsx @@ -0,0 +1,11 @@ +'use client' + +import { ThemeProvider as NextThemesProvider } from 'next-themes' +import type * as React from 'react' + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children} +}