feat: Display win/loss/tie statistics on player profiles

Add comprehensive match record breakdown showing wins, losses, and ties.

## Changes

### Data Calculation (+ page.ts)
- Enhanced playerStats mapping to detect tied matches
- Calculate `isTie` by comparing team scores (score_team_a === score_team_b)
- Add `tied` boolean field alongside existing `won` field
- Ensure wins exclude tied matches for accurate statistics

### Statistics Display (+page.svelte)
- Added "Match Record" card to Career Statistics section
- Calculate wins, losses, and ties from playerStats
- Display in compact format: "XW / YL / ZT"
- Color-coded: wins (green), losses (red), ties (blue)
- Show total matches analyzed below the record

### UI Improvements
- Expanded Career Statistics grid from 4 to 5 columns
- Responsive: 2 columns on mobile, 5 on desktop
- Consistent card styling with other career stats
- Trophy icon for Match Record card

## Implementation Details

**Tie Detection Logic:**
```typescript
const isTie = match.score_team_a === match.score_team_b;
const won = !isTie &&
  ((playerData.team_id === 2 && match.score_team_a > match.score_team_b) ||
   (playerData.team_id === 3 && match.score_team_b > match.score_team_a));
```

**Record Format:**
- Wins: Green text, "W" suffix
- Losses: Red text, "L" suffix
- Ties: Blue text, "T" suffix
- Separators: Gray "/"

The statistics are calculated from the last 15 matches with full details,
providing accurate win/loss/tie breakdown for performance tracking.

This completes Phase 3 Feature 2 - match records now fully visible.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-12 20:15:54 +01:00
parent 7e101ba274
commit 469f0df756
2 changed files with 33 additions and 5 deletions

View File

@@ -66,6 +66,12 @@
const hsPercent =
totalHeadshots > 0 && totalKills > 0 ? ((totalHeadshots / totalKills) * 100).toFixed(1) : '0.0';
// Calculate win/loss/tie statistics from playerStats
const wins = playerStats.filter((stat) => stat.won).length;
const ties = playerStats.filter((stat) => stat.tied).length;
const totalMatchesWithStats = playerStats.length;
const losses = totalMatchesWithStats - wins - ties;
// Check if player is favorited
const isFavorite = $derived($preferences.favoritePlayers.includes(profile.id));
@@ -320,7 +326,7 @@
<!-- Career Statistics -->
<div>
<h2 class="mb-4 text-2xl font-bold text-base-content">Career Statistics</h2>
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-5">
<Card padding="lg">
<div class="mb-2 flex items-center gap-2">
<Target class="h-5 w-5 text-primary" />
@@ -343,6 +349,23 @@
</div>
</Card>
<Card padding="lg">
<div class="mb-2 flex items-center gap-2">
<Trophy class="h-5 w-5 text-primary" />
<span class="text-sm font-medium text-base-content/70">Match Record</span>
</div>
<div class="flex items-baseline gap-1 text-lg font-bold text-base-content">
<span class="text-success">{wins}W</span>
<span class="text-base-content/40">/</span>
<span class="text-error">{losses}L</span>
<span class="text-base-content/40">/</span>
<span class="text-info">{ties}T</span>
</div>
<div class="mt-1 text-xs text-base-content/60">
Last {totalMatchesWithStats} matches
</div>
</Card>
<Card padding="lg">
<div class="mb-2 flex items-center gap-2">
<Trophy class="h-5 w-5 text-warning" />

View File

@@ -30,15 +30,20 @@ export const load: PageLoad = async ({ params }) => {
const playerData = match.players?.find((p) => p.id === playerId);
if (!playerData) return null;
const isTie = match.score_team_a === match.score_team_b;
const won =
!isTie &&
((playerData.team_id === 2 && match.score_team_a > match.score_team_b) ||
(playerData.team_id === 3 && match.score_team_b > match.score_team_a));
return {
match_id: match.match_id,
map: match.map,
date: match.date,
...playerData,
// Add match result (did player win?)
won:
(playerData.team_id === 2 && match.score_team_a > match.score_team_b) ||
(playerData.team_id === 3 && match.score_team_b > match.score_team_a)
// Add match results
won,
tied: isTie
};
})
.filter((stat): stat is NonNullable<typeof stat> => stat !== null);