8e2afb93f3
- NCWF-5: scaffold /analysis route with ChessBoard viewer and navigation - NCWF-6: FEN / PGN / Game-ID input form with depth selector - NCWF-7: extend GameApiService with analyzePosition(); add AnalysisService with game-wide annotation pipeline; proxy /api/analysis -> :8087 - NCWF-8: EvalTimelineComponent — SVG win-chance chart per ply - NCWF-9: AnnotatedMoveListComponent — quality labels (!! ! ?! ? ??) derived from win-chance delta Also fix pre-existing app.spec.ts failure (missing provideHttpClient). Apply project-wide prettier formatting pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
105 lines
3.8 KiB
HTML
105 lines
3.8 KiB
HTML
<div class="challenges-container">
|
|
<div class="challenges-header">
|
|
<h1>Active Challenges</h1>
|
|
<button type="button" class="back-btn" (click)="goBack()">← Back</button>
|
|
</div>
|
|
|
|
<div *ngIf="errorMessage" class="error-banner">
|
|
{{ errorMessage }}
|
|
</div>
|
|
|
|
<div class="challenges-grid">
|
|
<!-- Incoming Challenges -->
|
|
<div class="challenges-section">
|
|
<h2>Incoming Challenges</h2>
|
|
<div *ngIf="loading" class="loading-spinner">Loading...</div>
|
|
|
|
<div *ngIf="!loading && incomingChallenges.length === 0" class="empty-state">
|
|
<p>No incoming challenges</p>
|
|
</div>
|
|
|
|
<div *ngIf="!loading && incomingChallenges.length > 0" class="challenge-list">
|
|
<div *ngFor="let challenge of incomingChallenges" class="challenge-card">
|
|
<div class="challenge-header">
|
|
<span class="challenger-name">{{ getChallengerDisplay(challenge) }}</span>
|
|
<span class="time-control">{{ getTimeControlDisplay(challenge) }}</span>
|
|
</div>
|
|
|
|
<div class="challenge-details">
|
|
<div class="detail">
|
|
<span class="label">Status:</span>
|
|
<span class="value" [class]="'status-' + challenge.status">
|
|
{{ challenge.status | uppercase }}
|
|
</span>
|
|
</div>
|
|
<div class="detail">
|
|
<span class="label">Expires in:</span>
|
|
<span class="value">{{ getExpirationInfo(challenge) }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="challenge-actions" *ngIf="challenge.status === 'created'">
|
|
<button type="button" class="btn btn-decline" (click)="declineChallenge(challenge)">
|
|
Decline
|
|
</button>
|
|
<button type="button" class="btn btn-accept" (click)="acceptChallenge(challenge)">
|
|
Accept
|
|
</button>
|
|
</div>
|
|
|
|
<div
|
|
class="challenge-actions"
|
|
*ngIf="challenge.status === 'accepted' && challenge.gameId"
|
|
>
|
|
<button type="button" class="btn btn-accept" (click)="openGame(challenge)">Play</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Outgoing Challenges -->
|
|
<div class="challenges-section">
|
|
<h2>Outgoing Challenges</h2>
|
|
|
|
<div *ngIf="!loading && outgoingChallenges.length === 0" class="empty-state">
|
|
<p>No outgoing challenges</p>
|
|
</div>
|
|
|
|
<div *ngIf="!loading && outgoingChallenges.length > 0" class="challenge-list">
|
|
<div *ngFor="let challenge of outgoingChallenges" class="challenge-card">
|
|
<div class="challenge-header">
|
|
<span class="challenger-name">→ {{ getOpponentDisplay(challenge) }}</span>
|
|
<span class="time-control">{{ getTimeControlDisplay(challenge) }}</span>
|
|
</div>
|
|
|
|
<div class="challenge-details">
|
|
<div class="detail">
|
|
<span class="label">Status:</span>
|
|
<span class="value" [class]="'status-' + challenge.status">
|
|
{{ challenge.status | uppercase }}
|
|
</span>
|
|
</div>
|
|
<div class="detail">
|
|
<span class="label">Expires in:</span>
|
|
<span class="value">{{ getExpirationInfo(challenge) }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="challenge-actions" *ngIf="challenge.status === 'created'">
|
|
<button type="button" class="btn btn-cancel" (click)="cancelChallenge(challenge)">
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
|
|
<div
|
|
class="challenge-actions"
|
|
*ngIf="challenge.status === 'accepted' && challenge.gameId"
|
|
>
|
|
<button type="button" class="btn btn-accept" (click)="openGame(challenge)">Play</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|