'use client' import { useState } from 'react' import { Button } from '@/components/ui/button' // NEXT TURN — the manual tick control (5b): a normal-flow block in the right pane (#52) carrying // 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. // #52 — the fixed bottom-right overlay became the right pane's last flex child (mission-control): // full width, size MD, pinned to the pane bottom when the content is short (marginTop:auto) and // scrolling with the pane when the cargo list is long. No position/zIndex any more — so the // active-tasks strip can no longer paint over it. export function NextTurnButton({ turn, onTicked }: { turn: number | null; onTicked: () => void }) { const [inFlight, setInFlight] = useState(false) const [error, setError] = useState(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 (
{error && ( {error.toUpperCase()} )} TURN {turn ?? '—'}
) }