Files
nuzlocke-tracker/frontend/src/pages/admin/AdminGenlockes.tsx

108 lines
3.3 KiB
TypeScript
Raw Normal View History

import { useState, useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import { AdminTable, type Column } from '../../components/admin/AdminTable'
import { DeleteConfirmModal } from '../../components/admin/DeleteConfirmModal'
import { useGenlockes } from '../../hooks/useGenlockes'
import { useDeleteGenlocke } from '../../hooks/useAdmin'
import type { GenlockeListItem } from '../../types/game'
export function AdminGenlockes() {
const { data: genlockes = [], isLoading } = useGenlockes()
const deleteGenlocke = useDeleteGenlocke()
const navigate = useNavigate()
const [deleting, setDeleting] = useState<GenlockeListItem | null>(null)
const [statusFilter, setStatusFilter] = useState('')
const filtered = useMemo(() => {
if (!statusFilter) return genlockes
return genlockes.filter((g) => g.status === statusFilter)
}, [genlockes, statusFilter])
const columns: Column<GenlockeListItem>[] = [
{ header: 'Name', accessor: (g) => g.name, sortKey: (g) => g.name },
{
header: 'Status',
accessor: (g) => (
<span
className={
g.status === 'active'
? 'text-status-active'
: g.status === 'completed'
? 'text-text-link'
: 'text-status-failed'
}
>
{g.status}
</span>
),
sortKey: (g) => g.status,
},
{
header: 'Legs',
accessor: (g) => `${g.completedLegs} / ${g.totalLegs}`,
sortKey: (g) => g.totalLegs,
},
{
header: 'Created',
accessor: (g) => new Date(g.createdAt).toLocaleDateString(),
sortKey: (g) => g.createdAt,
},
]
return (
<div>
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold">Genlockes</h2>
</div>
<div className="mb-4 flex items-center gap-4">
<select
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
className="px-3 py-2 border rounded-md bg-surface-2 border-border-default"
>
<option value="">All statuses</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
<option value="failed">Failed</option>
</select>
{statusFilter && (
<button
onClick={() => setStatusFilter('')}
className="text-sm text-text-tertiary hover:text-text-primary"
>
Clear filters
</button>
)}
<span className="text-sm text-text-tertiary whitespace-nowrap">
{filtered.length} genlockes
</span>
</div>
<AdminTable
columns={columns}
data={filtered}
isLoading={isLoading}
emptyMessage="No genlockes found."
keyFn={(g) => g.id}
onRowClick={(g) => navigate(`/admin/genlockes/${g.id}`)}
/>
{deleting && (
<DeleteConfirmModal
title={`Delete "${deleting.name}"?`}
message="This will permanently delete the genlocke. Linked runs will be preserved but detached."
onConfirm={() =>
deleteGenlocke.mutate(deleting.id, {
onSuccess: () => setDeleting(null),
})
}
onCancel={() => setDeleting(null)}
isDeleting={deleteGenlocke.isPending}
/>
)}
</div>
)
}