feat: add site_admin role, casual match support, and tournament deletion

This commit is contained in:
2026-03-30 21:49:43 -07:00
parent d821dd7ce2
commit 07804b5f8f
7 changed files with 507 additions and 7 deletions
+124
View File
@@ -0,0 +1,124 @@
"use client"
import { useState } from "react"
interface DeleteTournamentButtonProps {
tournamentId: number
tournamentName: string
matchCount: number
}
export function DeleteTournamentButton({ tournamentId, tournamentName, matchCount }: DeleteTournamentButtonProps) {
const [isOpen, setIsOpen] = useState(false)
const [isDeleting, setIsDeleting] = useState(false)
const [deleteOption, setDeleteOption] = useState<'delete' | 'orphan'>('orphan')
const handleDelete = async () => {
setIsDeleting(true)
try {
const response = await fetch(`/api/admin/tournaments/${tournamentId}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
deleteMatches: deleteOption === 'delete',
}),
})
const data = await response.json()
if (data.success) {
alert(`Tournament "${tournamentName}" deleted successfully!`)
window.location.href = "/admin/tournaments"
} else {
alert(`Error: ${data.error}`)
}
} catch (err: unknown) {
alert(`Error: ${err instanceof Error ? err.message : 'Unknown error occurred'}`)
} finally {
setIsDeleting(false)
setIsOpen(false)
}
}
return (
<>
<button
type="button"
onClick={() => setIsOpen(true)}
className="px-4 py-2 border border-red-300 rounded-md shadow-sm text-sm font-medium text-red-700 bg-white hover:bg-red-50"
>
Delete Tournament
</button>
{isOpen && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 flex items-center justify-center">
<div className="bg-white rounded-lg shadow-xl p-6 m-4 max-w-md w-full">
<h3 className="text-lg font-semibold text-gray-900 mb-4">
Delete Tournament: {tournamentName}
</h3>
<p className="text-sm text-gray-600 mb-4">
This tournament has {matchCount} game{matchCount !== 1 ? 's' : ''} associated with it.
</p>
<div className="space-y-3 mb-6">
<label className="flex items-center">
<input
type="radio"
name="deleteOption"
value="orphan"
checked={deleteOption === 'orphan'}
onChange={() => setDeleteOption('orphan')}
className="mr-3"
/>
<div>
<span className="font-medium">Orphan Games</span>
<p className="text-sm text-gray-500">
Keep games in the database but remove their tournament association
</p>
</div>
</label>
<label className="flex items-center">
<input
type="radio"
name="deleteOption"
value="delete"
checked={deleteOption === 'delete'}
onChange={() => setDeleteOption('delete')}
className="mr-3"
/>
<div>
<span className="font-medium">Delete Games</span>
<p className="text-sm text-gray-500">
Permanently delete all games associated with this tournament
</p>
</div>
</label>
</div>
<div className="flex justify-end space-x-3">
<button
type="button"
onClick={() => setIsOpen(false)}
className="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50"
>
Cancel
</button>
<button
type="button"
onClick={handleDelete}
disabled={isDeleting}
className="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-red-600 hover:bg-red-700 disabled:opacity-50"
>
{isDeleting ? 'Deleting...' : 'Delete Tournament'}
</button>
</div>
</div>
</div>
)}
</>
)
}