feat(ui): NEXT TURN button (bottom-right) + turn counter; empty queue is a free skip (#44)
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
// NEXT TURN — the manual tick control (5b): a fixed bottom-right button + the current turn from
|
||||
// the ship snapshot the shell holds. One click = POST /api/turn (no body); on success the shell
|
||||
// re-fetches its snapshots. Review Focus 4: the button is NEVER disabled by game state — an
|
||||
// empty queue is a FREE SKIP (the route always returns 200) — and the only transient is an
|
||||
// in-flight request, which the handler ignores. `disabled` is never set, so the cva
|
||||
// disabled:opacity-40 styling never applies either.
|
||||
|
||||
export function NextTurnButton({ turn, onTicked }: { turn: number | null; onTicked: () => void }) {
|
||||
const [inFlight, setInFlight] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const tick = () => {
|
||||
if (inFlight) return // the only guard — a transient, never a game-state disable
|
||||
setInFlight(true)
|
||||
setError(null)
|
||||
fetch('/api/turn', { method: 'POST' })
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
onTicked() // the same app-wide snapshot refresh the shell uses after every other action
|
||||
})
|
||||
.catch((err: unknown) => setError(err instanceof Error ? err.message : String(err)))
|
||||
.finally(() => setInFlight(false))
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
right: '1rem',
|
||||
bottom: '1rem',
|
||||
zIndex: 60, // above the header/footer's 50
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-end',
|
||||
gap: '0.375rem',
|
||||
}}
|
||||
>
|
||||
{error && (
|
||||
<span style={{ fontSize: '0.62rem', color: 'var(--color-red)', letterSpacing: '0.08em' }}>{error.toUpperCase()}</span>
|
||||
)}
|
||||
<span style={{ fontSize: '0.62rem', color: 'var(--text-muted)', letterSpacing: '0.12em' }}>
|
||||
TURN {turn ?? '—'}
|
||||
</span>
|
||||
<Button variant="EXEC" size="SM" onClick={tick}>
|
||||
NEXT TURN
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user