Add filter controls to admin tables
Pokemon (type), Evolutions (trigger), Games (region/generation), and Runs (status/game) now have dropdown filters alongside search. Server-side filtering for paginated tables, client-side for small datasets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useMemo } from 'react'
|
||||
import { AdminTable, type Column } from '../../components/admin/AdminTable'
|
||||
import { GameFormModal } from '../../components/admin/GameFormModal'
|
||||
import { useGames } from '../../hooks/useGames'
|
||||
@@ -15,6 +15,24 @@ export function AdminGames() {
|
||||
|
||||
const [showCreate, setShowCreate] = useState(false)
|
||||
const [editing, setEditing] = useState<Game | null>(null)
|
||||
const [regionFilter, setRegionFilter] = useState('')
|
||||
const [genFilter, setGenFilter] = useState('')
|
||||
|
||||
const regions = useMemo(
|
||||
() => [...new Set(games.map((g) => g.region))].sort(),
|
||||
[games],
|
||||
)
|
||||
const generations = useMemo(
|
||||
() => [...new Set(games.map((g) => g.generation))].sort((a, b) => a - b),
|
||||
[games],
|
||||
)
|
||||
|
||||
const filteredGames = useMemo(() => {
|
||||
let result = games
|
||||
if (regionFilter) result = result.filter((g) => g.region === regionFilter)
|
||||
if (genFilter) result = result.filter((g) => g.generation === Number(genFilter))
|
||||
return result
|
||||
}, [games, regionFilter, genFilter])
|
||||
|
||||
const columns: Column<Game>[] = [
|
||||
{ header: 'Name', accessor: (g) => g.name },
|
||||
@@ -47,11 +65,45 @@ export function AdminGames() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-4 flex items-center gap-4">
|
||||
<select
|
||||
value={regionFilter}
|
||||
onChange={(e) => setRegionFilter(e.target.value)}
|
||||
className="px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600"
|
||||
>
|
||||
<option value="">All regions</option>
|
||||
{regions.map((r) => (
|
||||
<option key={r} value={r}>{r}</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
value={genFilter}
|
||||
onChange={(e) => setGenFilter(e.target.value)}
|
||||
className="px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600"
|
||||
>
|
||||
<option value="">All generations</option>
|
||||
{generations.map((g) => (
|
||||
<option key={g} value={g}>Gen {g}</option>
|
||||
))}
|
||||
</select>
|
||||
{(regionFilter || genFilter) && (
|
||||
<button
|
||||
onClick={() => { setRegionFilter(''); setGenFilter('') }}
|
||||
className="text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
>
|
||||
Clear filters
|
||||
</button>
|
||||
)}
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400 whitespace-nowrap">
|
||||
{filteredGames.length} games
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<AdminTable
|
||||
columns={columns}
|
||||
data={games}
|
||||
data={filteredGames}
|
||||
isLoading={isLoading}
|
||||
emptyMessage="No games yet. Add one to get started."
|
||||
emptyMessage="No games found."
|
||||
onRowClick={(g) => setEditing(g)}
|
||||
keyFn={(g) => g.id}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user