feat: add admin player management page
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { prisma } from "@/lib/prisma"
|
||||
import Navigation from "@/components/Navigation"
|
||||
import Link from "next/link"
|
||||
import { redirect } from "next/navigation"
|
||||
import { getSession } from "@/lib/auth-simple"
|
||||
import { hasRole } from "@/lib/permissions"
|
||||
|
||||
export default async function AdminPlayersPage() {
|
||||
const session = await getSession()
|
||||
|
||||
if (!session) {
|
||||
redirect("/auth/login")
|
||||
}
|
||||
|
||||
// Only site_admin and club_admin can manage players
|
||||
const permission = await hasRole('club_admin')
|
||||
if (!permission.allowed) {
|
||||
redirect("/auth/login")
|
||||
}
|
||||
|
||||
// Get all players with their user association
|
||||
const players = await prisma.player.findMany({
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
orderBy: { name: "asc" },
|
||||
})
|
||||
|
||||
// Get total count
|
||||
const playerCount = await prisma.player.count()
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<Navigation />
|
||||
|
||||
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
||||
<div className="px-4 py-6 sm:px-0">
|
||||
{/* Page Header */}
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Player Management</h1>
|
||||
<p className="text-gray-500 mt-1">
|
||||
Manage {playerCount} player{playerCount !== 1 ? 's' : ''} in the system
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Player Table */}
|
||||
<div className="bg-white shadow rounded-lg overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Player Name
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Current Elo
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Games
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Record
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Win Rate
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
User Account
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{players.map((player) => (
|
||||
<tr key={player.id} className="hover:bg-gray-50">
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0 h-10 w-10 bg-gray-200 rounded-full flex items-center justify-center">
|
||||
<span className="text-sm font-medium text-gray-600">
|
||||
{player.name.charAt(0).toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{player.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
{player.currentElo}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{player.gamesPlayed}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{player.wins}-{player.losses}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{player.gamesPlayed > 0
|
||||
? ((player.wins / player.gamesPlayed) * 100).toFixed(1) + '%'
|
||||
: 'N/A'}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{player.user ? (
|
||||
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">
|
||||
{player.user.email}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-gray-400 italic">No account</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<Link
|
||||
href={`/players/${player.id}/profile`}
|
||||
className="text-green-600 hover:text-green-900"
|
||||
>
|
||||
View Profile
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user