Add pre-commit hooks for linting and formatting
Set up pre-commit framework with ruff (backend) and ESLint/Prettier/tsc (frontend) hooks to catch issues locally before CI. Auto-format all frontend files with Prettier to comply with the new check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,13 +28,18 @@ export function AdminGenlockeDetail() {
|
||||
const [addingLeg, setAddingLeg] = useState(false)
|
||||
const [selectedGameId, setSelectedGameId] = useState<number | ''>('')
|
||||
|
||||
if (isLoading) return <div className="py-8 text-center text-gray-500">Loading...</div>
|
||||
if (!genlocke) return <div className="py-8 text-center text-gray-500">Genlocke not found</div>
|
||||
if (isLoading)
|
||||
return <div className="py-8 text-center text-gray-500">Loading...</div>
|
||||
if (!genlocke)
|
||||
return (
|
||||
<div className="py-8 text-center text-gray-500">Genlocke not found</div>
|
||||
)
|
||||
|
||||
const editName = name ?? genlocke.name
|
||||
const editStatus = status ?? genlocke.status
|
||||
|
||||
const hasChanges = editName !== genlocke.name || editStatus !== genlocke.status
|
||||
const hasChanges =
|
||||
editName !== genlocke.name || editStatus !== genlocke.status
|
||||
|
||||
const handleSave = () => {
|
||||
const data: Record<string, string> = {}
|
||||
@@ -48,7 +53,7 @@ export function AdminGenlockeDetail() {
|
||||
setName(null)
|
||||
setStatus(null)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -61,7 +66,7 @@ export function AdminGenlockeDetail() {
|
||||
setAddingLeg(false)
|
||||
setSelectedGameId('')
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -72,7 +77,9 @@ export function AdminGenlockeDetail() {
|
||||
Genlockes
|
||||
</Link>
|
||||
{' / '}
|
||||
<span className="text-gray-900 dark:text-gray-100">{genlocke.name}</span>
|
||||
<span className="text-gray-900 dark:text-gray-100">
|
||||
{genlocke.name}
|
||||
</span>
|
||||
</nav>
|
||||
|
||||
{/* Header */}
|
||||
@@ -124,16 +131,22 @@ export function AdminGenlockeDetail() {
|
||||
|
||||
{/* Rules (read-only) */}
|
||||
<div className="mb-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<h3 className="text-sm font-semibold mb-2 text-gray-700 dark:text-gray-300">Rules</h3>
|
||||
<h3 className="text-sm font-semibold mb-2 text-gray-700 dark:text-gray-300">
|
||||
Rules
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400">Genlocke rules:</span>
|
||||
<span className="text-gray-500 dark:text-gray-400">
|
||||
Genlocke rules:
|
||||
</span>
|
||||
<pre className="mt-1 text-xs bg-white dark:bg-gray-900 p-2 rounded border dark:border-gray-700 overflow-x-auto">
|
||||
{JSON.stringify(genlocke.genlockeRules, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400">Nuzlocke rules:</span>
|
||||
<span className="text-gray-500 dark:text-gray-400">
|
||||
Nuzlocke rules:
|
||||
</span>
|
||||
<pre className="mt-1 text-xs bg-white dark:bg-gray-900 p-2 rounded border dark:border-gray-700 overflow-x-auto">
|
||||
{JSON.stringify(genlocke.nuzlockeRules, null, 2)}
|
||||
</pre>
|
||||
@@ -144,7 +157,9 @@ export function AdminGenlockeDetail() {
|
||||
{/* Legs */}
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-lg font-semibold">Legs ({genlocke.legs.length})</h3>
|
||||
<h3 className="text-lg font-semibold">
|
||||
Legs ({genlocke.legs.length})
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => setAddingLeg(!addingLeg)}
|
||||
className="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700"
|
||||
@@ -157,7 +172,9 @@ export function AdminGenlockeDetail() {
|
||||
<div className="mb-4 flex items-center gap-3 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<select
|
||||
value={selectedGameId}
|
||||
onChange={(e) => setSelectedGameId(e.target.value ? Number(e.target.value) : '')}
|
||||
onChange={(e) =>
|
||||
setSelectedGameId(e.target.value ? Number(e.target.value) : '')
|
||||
}
|
||||
className="flex-1 px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600"
|
||||
>
|
||||
<option value="">Select a game...</option>
|
||||
@@ -222,8 +239,12 @@ export function AdminGenlockeDetail() {
|
||||
<tbody className="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{genlocke.legs.map((leg) => (
|
||||
<tr key={leg.id}>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap">{leg.legOrder}</td>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap">{leg.game.name}</td>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap">
|
||||
{leg.legOrder}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap">
|
||||
{leg.game.name}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap">
|
||||
{leg.runId ? (
|
||||
<Link
|
||||
@@ -253,13 +274,21 @@ export function AdminGenlockeDetail() {
|
||||
<span className="text-gray-400">—</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap">{leg.encounterCount}</td>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap">{leg.deathCount}</td>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap">
|
||||
{leg.encounterCount}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap">
|
||||
{leg.deathCount}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm whitespace-nowrap text-right">
|
||||
<button
|
||||
onClick={() => deleteLeg.mutate(leg.id)}
|
||||
disabled={leg.runId !== null || deleteLeg.isPending}
|
||||
title={leg.runId !== null ? 'Cannot remove a leg with a linked run' : 'Remove leg'}
|
||||
title={
|
||||
leg.runId !== null
|
||||
? 'Cannot remove a leg with a linked run'
|
||||
: 'Remove leg'
|
||||
}
|
||||
className="text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
>
|
||||
Remove
|
||||
@@ -276,22 +305,32 @@ export function AdminGenlockeDetail() {
|
||||
|
||||
{/* Stats */}
|
||||
<div className="mb-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<h3 className="text-sm font-semibold mb-2 text-gray-700 dark:text-gray-300">Stats</h3>
|
||||
<h3 className="text-sm font-semibold mb-2 text-gray-700 dark:text-gray-300">
|
||||
Stats
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400">Legs</span>
|
||||
<p className="text-lg font-semibold">{genlocke.stats.legsCompleted} / {genlocke.stats.totalLegs}</p>
|
||||
<p className="text-lg font-semibold">
|
||||
{genlocke.stats.legsCompleted} / {genlocke.stats.totalLegs}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400">Encounters</span>
|
||||
<p className="text-lg font-semibold">{genlocke.stats.totalEncounters}</p>
|
||||
<p className="text-lg font-semibold">
|
||||
{genlocke.stats.totalEncounters}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400">Deaths</span>
|
||||
<p className="text-lg font-semibold">{genlocke.stats.totalDeaths}</p>
|
||||
<p className="text-lg font-semibold">
|
||||
{genlocke.stats.totalDeaths}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400">Survival Rate</span>
|
||||
<span className="text-gray-500 dark:text-gray-400">
|
||||
Survival Rate
|
||||
</span>
|
||||
<p className="text-lg font-semibold">
|
||||
{genlocke.stats.totalEncounters > 0
|
||||
? `${Math.round(((genlocke.stats.totalEncounters - genlocke.stats.totalDeaths) / genlocke.stats.totalEncounters) * 100)}%`
|
||||
|
||||
Reference in New Issue
Block a user