diff --git a/client/src/app/tournament/[code]/page.tsx b/client/src/app/tournament/[code]/page.tsx
index ac133d8..a79199f 100644
--- a/client/src/app/tournament/[code]/page.tsx
+++ b/client/src/app/tournament/[code]/page.tsx
@@ -242,6 +242,11 @@ export default function TournamentDetailPage() {
Spielen
)}
+ {!isMyGame && g.gameCode && !g.result && (
+
+ Zuschauen
+
+ )}
{g.gameCode && g.result && (
Ansehen
diff --git a/client/src/components/home/LiveGames.tsx b/client/src/components/home/LiveGames.tsx
new file mode 100644
index 0000000..cb3a7ad
--- /dev/null
+++ b/client/src/components/home/LiveGames.tsx
@@ -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
([]);
+
+ 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 (
+
+
Laufende Partien
+
+ {games.map((game) => (
+
+
+
+ {game.black?.name}
+ vs
+ {game.white?.name}
+
+
+ ))}
+
+
+ );
+}
diff --git a/client/src/lib/game.ts b/client/src/lib/game.ts
index 1c8fd7b..213751e 100644
--- a/client/src/lib/game.ts
+++ b/client/src/lib/game.ts
@@ -35,6 +35,25 @@ export const fetchActiveGame = async (code: string) => {
}
};
+export type LiveGameEntry = {
+ code: string;
+ white: { name?: string };
+ black: { name?: string };
+ fen: string;
+ timeControl?: number;
+ tournamentCode?: string;
+};
+
+export const fetchLiveGames = async (): Promise => {
+ try {
+ const res = await fetch(`${API_URL}/v1/games/live`, { cache: "no-store" });
+ if (res.ok) return res.json();
+ } catch (err) {
+ console.error(err);
+ }
+ return [];
+};
+
export const fetchPublicGames = async () => {
try {
const res = await fetch(`${API_URL}/v1/games`, { cache: "no-store" });
diff --git a/server/src/controllers/games.controller.ts b/server/src/controllers/games.controller.ts
index 33da8d7..4972feb 100644
--- a/server/src/controllers/games.controller.ts
+++ b/server/src/controllers/games.controller.ts
@@ -1,9 +1,28 @@
import type { Game, User } from "@michess/types";
import type { Request, Response } from "express";
import { nanoid } from "nanoid";
+import { Chess } from "chess.js";
import GameModel, { activeGames } from "../db/models/game.model.js";
+export const getLiveGames = (_req: Request, res: Response) => {
+ const live = activeGames
+ .filter(g => g.white?.id && g.black?.id && !g.winner)
+ .map(g => {
+ const chess = new Chess();
+ if (g.pgn) chess.loadPgn(g.pgn);
+ return {
+ code: g.code,
+ white: { name: g.white?.name },
+ black: { name: g.black?.name },
+ fen: chess.fen(),
+ timeControl: g.timeControl,
+ tournamentCode: g.tournamentCode
+ };
+ });
+ res.status(200).json(live);
+};
+
export const getGames = async (req: Request, res: Response) => {
try {
if (!req.query.id && !req.query.userid) {
diff --git a/server/src/routes/games.route.ts b/server/src/routes/games.route.ts
index 13b0be6..2fac086 100644
--- a/server/src/routes/games.route.ts
+++ b/server/src/routes/games.route.ts
@@ -4,6 +4,7 @@ import * as controller from "../controllers/games.controller.js";
const router = Router();
+router.get("/live", controller.getLiveGames);
router.route("/").get(controller.getGames).post(controller.createGame);
router.route("/:code").get(controller.getActiveGame);