'use client' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/mobile-tooltip' import type React from 'react' import { useState } from 'react' import { GamesTable } from '@/app/(home)/players/[id]/_components/games-table' import { MmrTrendChart } from '@/app/(home)/players/[id]/_components/mmr-trend-chart' import { WinrateTrendChart } from '@/app/(home)/players/[id]/_components/winrate-trend-chart' import { OpponentsTable } from '@/app/(home)/players/[id]/_components/opponents-table' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { cn } from '@/lib/utils' import { RANKED_CHANNEL, VANILLA_CHANNEL } from '@/shared/constants' import { api } from '@/trpc/react' import { ArrowDownCircle, ArrowUpCircle, BarChart3, ChevronDown, ChevronUp, EllipsisVertical, Filter, IceCreamCone, ShieldHalf, Star, Trophy, UserIcon, } from 'lucide-react' import { ExternalIcon } from 'next/dist/client/components/react-dev-overlay/ui/icons/external' import Link from 'next/link' import { useParams } from 'next/navigation' import { isNonNullish } from 'remeda' const numberFormatter = new Intl.NumberFormat('en-US', { signDisplay: 'exceptZero', }) const dateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'long', }) export function UserInfo() { const [filter, setFilter] = useState('all') const [leaderboardFilter, setLeaderboardFilter] = useState('all') const { id } = useParams() if (!id || typeof id !== 'string') return null // Fetch games data unconditionally const gamesQuery = api.history.user_games.useSuspenseQuery({ user_id: id }) const games = gamesQuery[0] || [] // Ensure games is always an array const [discord_user] = api.discord.get_user_by_id.useSuspenseQuery({ user_id: id, }) const [rankedLeaderboard] = api.leaderboard.get_leaderboard.useSuspenseQuery({ channel_id: RANKED_CHANNEL, }) const [vanillaLeaderboard] = api.leaderboard.get_leaderboard.useSuspenseQuery( { channel_id: VANILLA_CHANNEL, } ) const [vanillaUserRank] = api.leaderboard.get_user_rank.useSuspenseQuery({ channel_id: VANILLA_CHANNEL, user_id: id, }) const [rankedUserRank] = api.leaderboard.get_user_rank.useSuspenseQuery({ channel_id: RANKED_CHANNEL, user_id: id, }) const filteredGamesByLeaderboard = leaderboardFilter === 'all' ? games : games.filter( (game) => game.gameType.toLowerCase() === leaderboardFilter?.toLowerCase() ) // Filter by result const filteredGames = filter === 'all' ? filteredGamesByLeaderboard : filter === 'wins' ? filteredGamesByLeaderboard.filter((game) => game.result === 'win') : filter === 'losses' ? filteredGamesByLeaderboard.filter((game) => game.result === 'loss') : filter === 'wins-and-losses' ? filteredGamesByLeaderboard.filter( (game) => game.result === 'win' || game.result === 'loss' ) : filteredGamesByLeaderboard.filter((game) => game.result === 'tie') const games_played = games.length let wins = 0 let losses = 0 let ties = 0 for (const game of games) { if (game.result === 'win') { wins++ } else if (game.result === 'loss') { losses++ } else if (game.result === 'tie' || game.result === 'unknown') { ties++ } else { ties++ } } const aliases = [...new Set(games.map((g) => g.playerName))] const lastGame = games.at(0) const currentName = lastGame?.playerName ?? discord_user.username const meaningful_games = games_played - ties const profileData = { username: currentName, avatar: discord_user.avatar_url, games: games_played, meaningful_games, wins, losses, ties, winRate: meaningful_games > 0 ? Math.ceil((wins / meaningful_games) * 100) : 0, lossRate: meaningful_games > 0 ? Math.floor((losses / meaningful_games) * 100) : 0, } const firstGame = games.at(-1) // Get last games for each leaderboard const lastRankedGame = games .filter((game) => game.gameType === 'ranked') .at(0) const lastVanillaGame = games .filter((game) => game.gameType.toLowerCase() === 'vanilla') .at(0) console.log(games) const avgOpponentMmr = games .filter( (g) => g.result !== 'tie' && g.result !== 'unknown' && g.gameType === 'ranked' ) .reduce((acc, g) => acc + g.opponentMmr, 0) / meaningful_games return (
Also known as:
{firstGame ? ( <>First game: {dateFormatter.format(firstGame.gameTime)}> ) : ( <>No games played yet> )}
No leaderboard data available
Achievements coming soon
{value}
{description}
Rank
#{rank}
MMR
{mmr}
No data available