feat: live game tiles on homepage + bot spectating
- GET /v1/games/live returns all active games with both players assigned, including current FEN (computed server-side via chess.js) - LiveGames component on homepage: mini chessboard tiles (160px) that auto-refresh every 3s, clickable to join as spectator - Tournament page: 'Zuschauen' button for non-players on ongoing rounds (bot vs bot, human vs human, human vs bot all spectatable) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
51
client/src/components/home/LiveGames.tsx
Normal file
51
client/src/components/home/LiveGames.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Chessboard } from "react-chessboard";
|
||||
import Link from "next/link";
|
||||
import { fetchLiveGames, type LiveGameEntry } from "@/lib/game";
|
||||
|
||||
export default function LiveGames() {
|
||||
const [games, setGames] = useState<LiveGameEntry[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
const load = async () => {
|
||||
const data = await fetchLiveGames();
|
||||
if (mounted) setGames(data);
|
||||
};
|
||||
load();
|
||||
const interval = setInterval(load, 3000);
|
||||
return () => { mounted = false; clearInterval(interval); };
|
||||
}, []);
|
||||
|
||||
if (!games.length) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-xl font-bold mb-3">Laufende Partien</h2>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3">
|
||||
{games.map((game) => (
|
||||
<Link
|
||||
key={game.code}
|
||||
href={`/${game.code}`}
|
||||
className="card bg-base-200 hover:bg-base-300 transition-colors overflow-hidden"
|
||||
>
|
||||
<Chessboard
|
||||
position={game.fen}
|
||||
boardWidth={160}
|
||||
arePiecesDraggable={false}
|
||||
customDarkSquareStyle={{ backgroundColor: "#4b7399" }}
|
||||
customLightSquareStyle={{ backgroundColor: "#eae9d2" }}
|
||||
/>
|
||||
<div className="px-2 py-1.5 flex flex-col gap-0.5">
|
||||
<span className="text-xs font-semibold truncate">{game.black?.name}</span>
|
||||
<span className="text-xs opacity-40 text-center leading-none">vs</span>
|
||||
<span className="text-xs font-semibold truncate">{game.white?.name}</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user