feat: add auth system, boss pokemon details, moves/abilities API, and run ownership
Add user authentication with login/signup/protected routes, boss pokemon detail fields and result team tracking, moves and abilities selector components and API, run ownership and visibility controls, and various UI improvements across encounters, run list, and journal pages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
69
frontend/src/components/admin/AbilitySelector.tsx
Normal file
69
frontend/src/components/admin/AbilitySelector.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { useSearchAbilities } from '../../hooks/useMoves'
|
||||
|
||||
interface AbilitySelectorProps {
|
||||
label: string
|
||||
selectedId: number | null
|
||||
initialName?: string
|
||||
onChange: (id: number | null, name: string) => void
|
||||
}
|
||||
|
||||
export function AbilitySelector({
|
||||
label,
|
||||
selectedId,
|
||||
initialName,
|
||||
onChange,
|
||||
}: AbilitySelectorProps) {
|
||||
const [search, setSearch] = useState(initialName ?? '')
|
||||
const [open, setOpen] = useState(false)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const { data } = useSearchAbilities(search)
|
||||
const abilities = data?.items ?? []
|
||||
|
||||
useEffect(() => {
|
||||
function handleClick(e: MouseEvent) {
|
||||
if (ref.current && !ref.current.contains(e.target as Node)) {
|
||||
setOpen(false)
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', handleClick)
|
||||
return () => document.removeEventListener('mousedown', handleClick)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div ref={ref} className="relative">
|
||||
<label className="block text-xs font-medium mb-0.5 text-text-secondary">{label}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
setSearch(e.target.value)
|
||||
setOpen(true)
|
||||
if (!e.target.value) onChange(null, '')
|
||||
}}
|
||||
onFocus={() => search && setOpen(true)}
|
||||
placeholder="Search ability..."
|
||||
className="w-full px-2 py-1.5 text-sm border rounded bg-surface-2 border-border-default"
|
||||
/>
|
||||
{open && abilities.length > 0 && (
|
||||
<ul className="absolute z-20 mt-1 w-full bg-surface-1 border border-border-default rounded shadow-lg max-h-40 overflow-y-auto">
|
||||
{abilities.map((a) => (
|
||||
<li
|
||||
key={a.id}
|
||||
onClick={() => {
|
||||
onChange(a.id, a.name)
|
||||
setSearch(a.name)
|
||||
setOpen(false)
|
||||
}}
|
||||
className={`px-2 py-1.5 cursor-pointer hover:bg-surface-2 text-sm ${
|
||||
a.id === selectedId ? 'bg-accent-900/30' : ''
|
||||
}`}
|
||||
>
|
||||
{a.name}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user