2 Commits

Author SHA1 Message Date
TeamCity e374d5e791 ci: bump version to v0.4.4 2026-06-21 14:06:48 +00:00
Janis Eccarius 5b5fd6f027 fix(tournaments): load both user bots and official bots in join dialog
openJoinDialog now fetches user bots and official bots in parallel via
forkJoin. Each section shows its own empty state independently.

Official bot difficulty buttons are hidden when no official bots are
registered. User bots empty state links to /bots to create one.

Disables all join buttons while any join is in progress.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 15:40:24 +02:00
4 changed files with 61 additions and 44 deletions
+5
View File
@@ -81,3 +81,8 @@
### Bug Fixes ### Bug Fixes
* **analysis:** fix API field mismatch and enable full game analysis ([ce1fb0d](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/ce1fb0d60b695093495ee0ad824c511dd2db7fbb)) * **analysis:** fix API field mismatch and enable full game analysis ([ce1fb0d](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/ce1fb0d60b695093495ee0ad824c511dd2db7fbb))
## [0.0.0](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/compare/0.4.3...0.0.0) (2026-06-21)
### Bug Fixes
* **tournaments:** load both user bots and official bots in join dialog ([5b5fd6f](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/5b5fd6f027b4aedb951a802725fcd929d514c359))
@@ -202,17 +202,16 @@
<span class="dialog-brand">Join with a bot</span> <span class="dialog-brand">Join with a bot</span>
<button type="button" class="dialog-close" (click)="closeJoinDialog()">×</button> <button type="button" class="dialog-close" (click)="closeJoinDialog()">×</button>
</div> </div>
<p class="join-hint">Select an official (engine-backed) bot to enter this tournament. These are the bots that actually play their moves.</p>
@if (botsLoading) { @if (botsLoading) {
<div class="dialog-loading"><span class="pulse"></span>Loading bots…</div> <div class="dialog-loading"><span class="pulse"></span>Loading bots…</div>
} @else if (userBots.length === 0) { } @else {
<p class="join-empty">No official bots are available. The official-bots engine service must be running to register them.</p> @if (userBots.length === 0) {
<p class="join-empty">You have no bots. <a routerLink="/bots">Create one</a> to join with your own bot.</p>
} @else { } @else {
<div class="bot-pick-list"> <div class="bot-pick-list">
@for (bot of userBots; track bot.id) { @for (bot of userBots; track bot.id) {
<button type="button" class="bot-pick-row" <button type="button" class="bot-pick-row"
[disabled]="!!joiningBotId" [disabled]="!!joiningBotId || !!joiningOfficialDifficulty"
(click)="joinWithBot(bot)"> (click)="joinWithBot(bot)">
<span class="bot-pick-avatar">{{ bot.name.charAt(0).toUpperCase() }}</span> <span class="bot-pick-avatar">{{ bot.name.charAt(0).toUpperCase() }}</span>
<span class="bot-pick-name">{{ bot.name }}</span> <span class="bot-pick-name">{{ bot.name }}</span>
@@ -233,6 +232,9 @@
<span class="join-divider-label">or join with an official bot</span> <span class="join-divider-label">or join with an official bot</span>
</div> </div>
@if (officialBots.length === 0) {
<p class="join-empty">No official bots are available. The official-bots engine service must be running to register them.</p>
} @else {
<div class="official-bot-grid"> <div class="official-bot-grid">
@for (d of officialDifficulties; track d) { @for (d of officialDifficulties; track d) {
<button type="button" class="official-bot-btn" <button type="button" class="official-bot-btn"
@@ -246,10 +248,12 @@
</button> </button>
} }
</div> </div>
}
@if (officialJoinError) { @if (officialJoinError) {
<div class="dialog-error">{{ officialJoinError }}</div> <div class="dialog-error">{{ officialJoinError }}</div>
} }
}
</div> </div>
</div> </div>
} }
@@ -3,6 +3,7 @@ import { CommonModule, TitleCasePipe } from '@angular/common';
import { Router, RouterLink } from '@angular/router'; import { Router, RouterLink } from '@angular/router';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { forkJoin } from 'rxjs';
import { TournamentService } from '../../services/tournament.service'; import { TournamentService } from '../../services/tournament.service';
import { AuthService } from '../../services/auth.service'; import { AuthService } from '../../services/auth.service';
import { BotService } from '../../services/bot.service'; import { BotService } from '../../services/bot.service';
@@ -58,6 +59,7 @@ export class TournamentsComponent implements OnInit {
joinDialogTournamentId: string | null = null; joinDialogTournamentId: string | null = null;
userBots: Bot[] = []; userBots: Bot[] = [];
officialBots: Bot[] = [];
botsLoading = false; botsLoading = false;
joiningBotId: string | null = null; joiningBotId: string | null = null;
joinError: string | null = null; joinError: string | null = null;
@@ -175,16 +177,22 @@ export class TournamentsComponent implements OnInit {
this.joinDialogTournamentId = tournamentId; this.joinDialogTournamentId = tournamentId;
this.joinError = null; this.joinError = null;
this.botsLoading = true; this.botsLoading = true;
this.botService.listOfficial() forkJoin({ user: this.botService.list(), official: this.botService.listOfficial() })
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({ .subscribe({
next: bots => { this.userBots = bots; this.botsLoading = false; }, next: ({ user, official }) => {
this.userBots = user;
this.officialBots = official;
this.botsLoading = false;
},
error: () => { this.botsLoading = false; } error: () => { this.botsLoading = false; }
}); });
} }
closeJoinDialog(): void { closeJoinDialog(): void {
this.joinDialogTournamentId = null; this.joinDialogTournamentId = null;
this.userBots = [];
this.officialBots = [];
this.joiningBotId = null; this.joiningBotId = null;
this.joinError = null; this.joinError = null;
this.joiningOfficialDifficulty = null; this.joiningOfficialDifficulty = null;
+1 -1
View File
@@ -1,3 +1,3 @@
MAJOR=0 MAJOR=0
MINOR=4 MINOR=4
PATCH=3 PATCH=4