"use client" import { useState, useEffect } from "react" import Navigation from "@/components/Navigation" import Link from "next/link" interface Player { id: number name: string currentElo: number gamesPlayed: number wins: number losses: number user?: { email: string } | null } export default function AdminPlayersPage() { const [players, setPlayers] = useState([]) const [error, setError] = useState("") const [editingPlayer, setEditingPlayer] = useState(null) const [newName, setNewName] = useState("") const [isSaving, setIsSaving] = useState(false) // Merge player state const [mergeMode, setMergeMode] = useState(false) const [selectedPlayers, setSelectedPlayers] = useState([]) const [isMerging, setIsMerging] = useState(false) const [deletingId, setDeletingId] = useState(null) useEffect(() => { fetchPlayers() }, []) const fetchPlayers = async () => { try { const response = await fetch("/api/players") if (!response.ok) { throw new Error("Failed to fetch players") } const data = await response.json() setPlayers(data) } catch (err: unknown) { setError(err instanceof Error ? err.message : "Unknown error") } } const handleEditClick = (player: Player) => { setEditingPlayer(player) setNewName(player.name) } const handleSaveName = async () => { if (!editingPlayer || !newName.trim()) return setIsSaving(true) try { const response = await fetch(`/api/admin/players/${editingPlayer.id}`, { method: "PATCH", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name: newName.trim() }), }) if (!response.ok) { try { const errorData = await response.json() alert(`Error: ${errorData.error || 'Failed to update player'}`) } catch { alert(`Error: ${response.status} ${response.statusText}`) } return } const data = await response.json() if (data.success) { setPlayers(players.map(p => p.id === editingPlayer.id ? { ...p, name: data.player.name } : p )) setEditingPlayer(null) setNewName("") } else { alert(`Error: ${data.error}`) } } catch (err: unknown) { alert(`Error: ${err instanceof Error ? err.message : "Unknown error"}`) } finally { setIsSaving(false) } } const togglePlayerSelection = (playerId: number) => { setSelectedPlayers(prev => prev.includes(playerId) ? prev.filter(id => id !== playerId) : [...prev, playerId] ) } const handleMerge = async () => { if (selectedPlayers.length < 2) return setIsMerging(true) try { const response = await fetch('/api/admin/players/merge', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ playerIds: selectedPlayers, }), }) if (!response.ok) { try { const errorData = await response.json() alert(`Error: ${errorData.error || 'Failed to merge players'}`) } catch { alert(`Error: ${response.status} ${response.statusText}`) } return } const data = await response.json() if (data.success) { alert(`Successfully merged ${selectedPlayers.length} players! Elo ratings have been recalculated.`) // Refresh the player list fetchPlayers() setSelectedPlayers([]) setMergeMode(false) } else { alert(`Error: ${data.error}`) } } catch (err: unknown) { alert(`Error: ${err instanceof Error ? err.message : "Unknown error occurred"}`) } finally { setIsMerging(false) } } const handleDelete = async (playerId: number) => { if (!confirm("Are you sure you want to delete this player? This action cannot be undone and the player must not have any match history.")) { return } setDeletingId(playerId) try { const response = await fetch(`/api/admin/players/${playerId}`, { method: "DELETE", }) if (!response.ok) { try { const errorData = await response.json() alert(`Error: ${errorData.error || 'Failed to delete player'}`) } catch { alert(`Error: ${response.status} ${response.statusText}`) } return } const data = await response.json() if (data.success) { setPlayers(players.filter(p => p.id !== playerId)) } else { alert(`Error: ${data.error}`) } } catch (err: unknown) { alert(`Error: ${err instanceof Error ? err.message : 'Unknown error occurred'}`) } finally { setDeletingId(null) } } const playerCount = players.length return (
{/* Page Header */}

Player Management

Manage {playerCount} player{playerCount !== 1 ? 's' : ''} in the system

{mergeMode && selectedPlayers.length > 0 && (
{selectedPlayers.length} player{selectedPlayers.length !== 1 ? 's' : ''} selected for merging
)} {error && (
{error}
)} {/* Search and Filter Controls */}
{ const search = e.target.value try { const response = await fetch(`/api/players?search=${encodeURIComponent(search)}`) if (response.ok) { const data = await response.json() setPlayers(data) } } catch (err) { console.error('Search failed:', err) } }} />
{/* Player Table */}
{players.map((player) => { const isSelected = selectedPlayers.includes(player.id) return ( mergeMode && togglePlayerSelection(player.id)} > ) })}
Player Name Current Elo Games Record Win Rate User Account Actions
{mergeMode && ( togglePlayerSelection(player.id)} className="mr-3 h-4 w-4 text-purple-600 focus:ring-purple-500 border-gray-300 rounded" /> )}
{player.name.charAt(0).toUpperCase()}
{player.name}
{player.currentElo} {player.gamesPlayed} {player.wins}-{player.losses} {player.gamesPlayed > 0 ? ((player.wins / player.gamesPlayed) * 100).toFixed(1) + '%' : 'N/A'} {player.user ? ( {player.user.email} ) : ( No account )}
View {!mergeMode && ( <> )}
{/* Edit Player Modal */} {editingPlayer && (

Edit Player Name

Current name: {editingPlayer.name}

setNewName(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" placeholder="Enter new player name" />
)}
) }