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:
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { Link, Outlet, useLocation } from 'react-router-dom'
|
||||
import { useTheme } from '../hooks/useTheme'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
|
||||
const navLinks = [
|
||||
{ to: '/runs/new', label: 'New Run' },
|
||||
@@ -71,6 +72,67 @@ function ThemeToggle() {
|
||||
)
|
||||
}
|
||||
|
||||
function UserMenu({ onAction }: { onAction?: () => void }) {
|
||||
const { user, loading, signOut } = useAuth()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
if (loading) {
|
||||
return <div className="w-8 h-8 rounded-full bg-surface-3 animate-pulse" />
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<Link
|
||||
to="/login"
|
||||
onClick={onAction}
|
||||
className="px-3 py-2 rounded-md text-sm font-medium text-text-secondary hover:text-text-primary hover:bg-surface-3 transition-colors"
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
const email = user.email ?? ''
|
||||
const initials = email.charAt(0).toUpperCase()
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(!open)}
|
||||
className="flex items-center gap-2 p-1 rounded-full hover:bg-surface-3 transition-colors"
|
||||
>
|
||||
<div className="w-8 h-8 rounded-full bg-accent-600 flex items-center justify-center text-white text-sm font-medium">
|
||||
{initials}
|
||||
</div>
|
||||
</button>
|
||||
{open && (
|
||||
<>
|
||||
<div className="fixed inset-0 z-40" onClick={() => setOpen(false)} />
|
||||
<div className="absolute right-0 mt-2 w-48 bg-surface-2 border border-border-default rounded-lg shadow-lg z-50">
|
||||
<div className="px-4 py-3 border-b border-border-default">
|
||||
<p className="text-sm text-text-primary truncate">{email}</p>
|
||||
</div>
|
||||
<div className="py-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
setOpen(false)
|
||||
onAction?.()
|
||||
await signOut()
|
||||
}}
|
||||
className="w-full text-left px-4 py-2 text-sm text-text-secondary hover:text-text-primary hover:bg-surface-3 transition-colors"
|
||||
>
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Layout() {
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const location = useLocation()
|
||||
@@ -103,6 +165,7 @@ export function Layout() {
|
||||
</NavLink>
|
||||
))}
|
||||
<ThemeToggle />
|
||||
<UserMenu />
|
||||
</div>
|
||||
{/* Mobile hamburger */}
|
||||
<div className="flex items-center gap-1 sm:hidden">
|
||||
@@ -149,6 +212,9 @@ export function Layout() {
|
||||
{link.label}
|
||||
</NavLink>
|
||||
))}
|
||||
<div className="pt-2 border-t border-border-default mt-2">
|
||||
<UserMenu onAction={() => setMenuOpen(false)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user