import { useState, useMemo } from "react"; import { Text, TextInput, Stack, Group, Box, ThemeIcon, Container, Title, Divider, UnstyledButton, Popover, ActionIcon, Skeleton, } from "@mantine/core"; import { MagnifyingGlassIcon, CaretUpIcon, CaretDownIcon, ChartBarIcon, InfoIcon, } from "@phosphor-icons/react"; import { PlayerStats } from "../types"; import Avatar from "@/components/avatar"; import { useNavigate } from "@tanstack/react-router"; interface PlayerStatsTableProps { playerStats: PlayerStats[]; } type SortKey = keyof PlayerStats | "mmr"; type SortDirection = "asc" | "desc"; interface SortConfig { key: SortKey; direction: SortDirection; } interface PlayerListItemProps { stat: PlayerStats; index: number; onPlayerClick: (playerId: string) => void; } const PlayerListItem = ({ stat, index, onPlayerClick }: PlayerListItemProps) => { const calculateMMR = (stat: PlayerStats): number => { if (stat.matches === 0) return 0; const winScore = stat.win_percentage; const matchConfidence = Math.min(stat.matches / 15, 1); const avgCupsScore = Math.min(stat.avg_cups_per_match * 10, 100); const marginScore = stat.margin_of_victory ? Math.min(stat.margin_of_victory * 20, 50) : 0; const volumeBonus = Math.min(stat.matches * 0.5, 10); const baseMMR = winScore * 0.5 + avgCupsScore * 0.25 + marginScore * 0.15 + volumeBonus * 0.1; const finalMMR = baseMMR * matchConfidence; return Math.round(finalMMR * 10) / 10; }; const mmr = calculateMMR(stat); return ( <> onPlayerClick(stat.id)} style={{ borderRadius: 0, transition: "background-color 0.15s ease", }} styles={{ root: { '&:hover': { backgroundColor: 'var(--mantine-color-gray-0)', }, }, }} > {stat.player_name} {stat.matches} matches {stat.tournaments} tournaments MMR {mmr.toFixed(1)} W {stat.wins} L {stat.losses} W% {stat.win_percentage.toFixed(1)}% AVG {stat.avg_cups_per_match.toFixed(1)} CF {stat.total_cups_made} CA {stat.total_cups_against} ); }; const PlayerStatsTable = ({ playerStats }: PlayerStatsTableProps) => { const navigate = useNavigate(); const [search, setSearch] = useState(""); const [sortConfig, setSortConfig] = useState({ key: "mmr" as SortKey, direction: "desc", }); const calculateMMR = (stat: PlayerStats): number => { if (stat.matches === 0) return 0; const winScore = stat.win_percentage; const matchConfidence = Math.min(stat.matches / 15, 1); const avgCupsScore = Math.min(stat.avg_cups_per_match * 10, 100); const marginScore = stat.margin_of_victory ? Math.min(stat.margin_of_victory * 20, 50) : 0; const volumeBonus = Math.min(stat.matches * 0.5, 10); const baseMMR = winScore * 0.5 + avgCupsScore * 0.25 + marginScore * 0.15 + volumeBonus * 0.1; const finalMMR = baseMMR * matchConfidence; return Math.round(finalMMR * 10) / 10; }; const filteredAndSortedStats = useMemo(() => { let filtered = playerStats.filter((stat) => stat.player_name.toLowerCase().includes(search.toLowerCase()) ); return filtered.sort((a, b) => { let aValue: number | string; let bValue: number | string; if (sortConfig.key === "mmr") { aValue = calculateMMR(a); bValue = calculateMMR(b); } else { aValue = a[sortConfig.key]; bValue = b[sortConfig.key]; } if (typeof aValue === "number" && typeof bValue === "number") { return sortConfig.direction === "desc" ? bValue - aValue : aValue - bValue; } if (typeof aValue === "string" && typeof bValue === "string") { return sortConfig.direction === "desc" ? bValue.localeCompare(aValue) : aValue.localeCompare(bValue); } return 0; }); }, [playerStats, search, sortConfig]); const handlePlayerClick = (playerId: string) => { navigate({ to: `/profile/${playerId}` }); }; const handleSort = (key: SortKey) => { setSortConfig((prev) => ({ key, direction: prev.key === key && prev.direction === "desc" ? "asc" : "desc", })); }; const getSortIcon = (key: SortKey) => { if (sortConfig.key !== key) return null; return sortConfig.direction === "desc" ? ( ) : ( ); }; if (playerStats.length === 0) { return ( No Stats Available ); } return ( setSearch(e.currentTarget.value)} leftSection={} size="md" px="md" /> {filteredAndSortedStats.length} of {playerStats.length} players Sort: handleSort("mmr")} style={{ display: "flex", alignItems: "center", gap: 4 }} > MMR {getSortIcon("mmr")} handleSort("wins")} style={{ display: "flex", alignItems: "center", gap: 4 }} > Wins {getSortIcon("wins")} handleSort("matches")} style={{ display: "flex", alignItems: "center", gap: 4 }} > Matches {getSortIcon("matches")} MMR Calculation: • Win Rate (50%) • Average Cups/Match (25%) • Average Win Margin (15%) • Match Volume Bonus (10%) * Confidence penalty applied for players with <15 matches ** Not an official rating {filteredAndSortedStats.map((stat, index) => ( {index < filteredAndSortedStats.length - 1 && } ))} {filteredAndSortedStats.length === 0 && search && ( No players found matching "{search}" )} ); }; export default PlayerStatsTable;