Compare commits

..

4 Commits

Author SHA1 Message Date
TeamCity 8603222ab4 ci: bump version to v0.6.4 2026-06-28 17:49:20 +00:00
lq64 76e0e3db78 fix: show actual bot names on watch page instead of Black/White fallbacks (#15)
The gameState stream event does not carry player names, so snapshot.white/black
were always undefined and the UI fell back to the literal strings "Black" and
"White". Fix by fetching the full game object (GET /api/tournament/{id}/game/{gameId})
on page load, which includes BotRef with real names, and storing them in
dedicated whiteName/blackName fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: LQ63 <lkhermann@web.de>
Reviewed-on: #15
2026-06-28 19:43:26 +02:00
TeamCity 0ac61032bd ci: bump version to v0.6.3 2026-06-23 12:55:59 +00:00
Janis 890c3fcecc fix: route tournament calls through backend gateway
Use relative /api/tournament path instead of hardcoded
http://141.37.123.132:8086. The browser was hitting the external
tournament-server directly with the user's RS256 token, which it
rejects. Routing through our backend lets it publish to the native
server with the auto-registered director token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 14:52:31 +02:00
6 changed files with 41 additions and 9 deletions
+10
View File
@@ -108,3 +108,13 @@
### Bug Fixes ### Bug Fixes
* jwt token issue ([147d7f0](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/147d7f0d2ca7a77bb80eb4b73b8d60b00ad2f708)) * jwt token issue ([147d7f0](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/147d7f0d2ca7a77bb80eb4b73b8d60b00ad2f708))
## [0.0.0](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/compare/0.6.2...0.0.0) (2026-06-23)
### Bug Fixes
* route tournament calls through backend gateway ([890c3fc](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/890c3fcecccec89e643180725e2a601f84fa5d99))
## [0.0.0](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/compare/0.6.3...0.0.0) (2026-06-28)
### Bug Fixes
* show actual bot names on watch page instead of Black/White fallbacks ([#15](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/issues/15)) ([76e0e3d](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/76e0e3db7869609e400593ca8f08ea8f72e72f68))
@@ -2,10 +2,10 @@
<header class="watch-head"> <header class="watch-head">
<a class="back-link" routerLink="/tournaments">← Tournaments</a> <a class="back-link" routerLink="/tournaments">← Tournaments</a>
<div class="watch-title"> <div class="watch-title">
@if (snapshot?.white && snapshot?.black) { @if (whiteName && blackName) {
<span class="player">{{ snapshot!.white!.name }}</span> <span class="player">{{ whiteName }}</span>
<span class="vs">vs</span> <span class="vs">vs</span>
<span class="player">{{ snapshot!.black!.name }}</span> <span class="player">{{ blackName }}</span>
@if (snapshot?.round) { @if (snapshot?.round) {
<span class="round-tag">Round {{ snapshot!.round }}</span> <span class="round-tag">Round {{ snapshot!.round }}</span>
} }
@@ -24,12 +24,12 @@
<div class="watch-layout"> <div class="watch-layout">
<div class="board-wrap"> <div class="board-wrap">
<div class="clock clock-top" [class.active]="status === 'ongoing' && turn === 'black'"> <div class="clock clock-top" [class.active]="status === 'ongoing' && turn === 'black'">
<span class="clock-label">{{ snapshot?.black?.name ?? 'Black' }}</span> <span class="clock-label">{{ blackName ?? 'Black' }}</span>
<span class="clock-time">{{ formatTime(clock?.blackTime) }}</span> <span class="clock-time">{{ formatTime(clock?.blackTime) }}</span>
</div> </div>
<app-chess-board [fen]="fen"></app-chess-board> <app-chess-board [fen]="fen"></app-chess-board>
<div class="clock clock-bot" [class.active]="status === 'ongoing' && turn === 'white'"> <div class="clock clock-bot" [class.active]="status === 'ongoing' && turn === 'white'">
<span class="clock-label">{{ snapshot?.white?.name ?? 'White' }}</span> <span class="clock-label">{{ whiteName ?? 'White' }}</span>
<span class="clock-time">{{ formatTime(clock?.whiteTime) }}</span> <span class="clock-time">{{ formatTime(clock?.whiteTime) }}</span>
</div> </div>
</div> </div>
@@ -1,6 +1,7 @@
import { Component, DestroyRef, OnInit, inject } from '@angular/core'; import { Component, DestroyRef, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ActivatedRoute, RouterLink } from '@angular/router'; import { ActivatedRoute, RouterLink } from '@angular/router';
import { from } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ChessBoardComponent } from '../../components/chess-board/chess-board.component'; import { ChessBoardComponent } from '../../components/chess-board/chess-board.component';
import { TournamentStreamService } from '../../services/tournament-stream.service'; import { TournamentStreamService } from '../../services/tournament-stream.service';
@@ -25,6 +26,9 @@ export class TournamentWatchComponent implements OnInit {
gameId = ''; gameId = '';
serverUrl = ''; serverUrl = '';
whiteName: string | null = null;
blackName: string | null = null;
fen = INITIAL_FEN; fen = INITIAL_FEN;
turn: 'white' | 'black' = 'white'; turn: 'white' | 'black' = 'white';
status: GameStatus = 'pending'; status: GameStatus = 'pending';
@@ -51,6 +55,15 @@ export class TournamentWatchComponent implements OnInit {
return; return;
} }
from(this.stream.fetchGame(this.serverUrl, this.tournamentId, this.gameId))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: game => {
this.whiteName = game.white?.name ?? null;
this.blackName = game.black?.name ?? null;
},
});
this.stream.streamGame(this.serverUrl, this.tournamentId, this.gameId) this.stream.streamGame(this.serverUrl, this.tournamentId, this.gameId)
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({ .subscribe({
+11 -1
View File
@@ -1,6 +1,6 @@
import { Injectable, inject } from '@angular/core'; import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { GameStreamEvent, TournamentStreamEvent } from '../models/tournament.models'; import { GameStateSnapshot, GameStreamEvent, TournamentStreamEvent } from '../models/tournament.models';
import { TournamentAuthService } from './tournament-auth.service'; import { TournamentAuthService } from './tournament-auth.service';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@@ -21,6 +21,16 @@ export class TournamentStreamService {
); );
} }
async fetchGame(serverUrl: string, tournamentId: string, gameId: string): Promise<GameStateSnapshot> {
const base = serverUrl.replace(/\/+$/, '');
const token = await this.auth.getToken(serverUrl);
const res = await fetch(`${base}/api/tournament/${tournamentId}/game/${gameId}`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error(`Game fetch failed: ${res.status}`);
return res.json() as Promise<GameStateSnapshot>;
}
private fullUrl(base: string, path: string): string { private fullUrl(base: string, path: string): string {
if (!base) return path; if (!base) return path;
return `${base.replace(/\/+$/, '')}${path}`; return `${base.replace(/\/+$/, '')}${path}`;
+1 -2
View File
@@ -2,7 +2,6 @@ import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Tournament, TournamentList, RoundPairings } from '../models/tournament.models'; import { Tournament, TournamentList, RoundPairings } from '../models/tournament.models';
import { environment } from '../../environments/environment';
export interface CreateTournamentForm { export interface CreateTournamentForm {
name: string; name: string;
@@ -15,7 +14,7 @@ export interface CreateTournamentForm {
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class TournamentService { export class TournamentService {
private readonly http = inject(HttpClient); private readonly http = inject(HttpClient);
private readonly base = `${(environment.tournamentServerUrl ?? '').replace(/\/+$/, '')}/api/tournament`; private readonly base = '/api/tournament';
list(): Observable<TournamentList> { list(): Observable<TournamentList> {
return this.http.get<TournamentList>(this.base); return this.http.get<TournamentList>(this.base);
+1 -1
View File
@@ -1,3 +1,3 @@
MAJOR=0 MAJOR=0
MINOR=6 MINOR=6
PATCH=2 PATCH=4