2026-01-26 02:13:06 +08:00
|
|
|
{% extends "base.html" %}
|
|
|
|
|
|
|
|
|
|
{% block content %}
|
2026-01-26 17:08:43 +08:00
|
|
|
<div class="space-y-6" x-data="{ tab: 'overview' }">
|
2026-01-26 02:13:06 +08:00
|
|
|
<!-- Header -->
|
|
|
|
|
<div class="bg-white dark:bg-slate-800 shadow rounded-lg p-6">
|
|
|
|
|
<div class="flex justify-between items-center">
|
|
|
|
|
<div>
|
|
|
|
|
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">{{ match.map_name }}</h1>
|
|
|
|
|
<p class="text-sm text-gray-500 mt-1">Match ID: {{ match.match_id }} | {{ match.start_time }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<div class="text-4xl font-black text-gray-900 dark:text-white">
|
|
|
|
|
<span class="{% if match.winner_team == 1 %}text-green-600{% endif %}">{{ match.score_team1 }}</span>
|
|
|
|
|
:
|
|
|
|
|
<span class="{% if match.winner_team == 2 %}text-green-600{% endif %}">{{ match.score_team2 }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<a href="{{ url_for('matches.raw_json', match_id=match.match_id) }}" target="_blank" class="text-sm text-yrtv-600 hover:underline">Download Raw JSON</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-01-26 17:08:43 +08:00
|
|
|
|
|
|
|
|
<!-- Tab Navigation -->
|
|
|
|
|
<div class="mt-6 border-b border-gray-200 dark:border-gray-700">
|
|
|
|
|
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
|
|
|
|
|
<button @click="tab = 'overview'"
|
|
|
|
|
:class="tab === 'overview' ? 'border-yrtv-500 text-yrtv-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
|
|
|
|
|
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm">
|
|
|
|
|
Overview
|
|
|
|
|
</button>
|
|
|
|
|
<button @click="tab = 'h2h'"
|
|
|
|
|
:class="tab === 'h2h' ? 'border-yrtv-500 text-yrtv-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
|
|
|
|
|
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm">
|
|
|
|
|
Head to Head
|
|
|
|
|
</button>
|
|
|
|
|
<button @click="tab = 'rounds'"
|
|
|
|
|
:class="tab === 'rounds' ? 'border-yrtv-500 text-yrtv-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
|
|
|
|
|
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm">
|
|
|
|
|
Round History
|
|
|
|
|
</button>
|
|
|
|
|
</nav>
|
|
|
|
|
</div>
|
2026-01-26 02:13:06 +08:00
|
|
|
</div>
|
|
|
|
|
|
2026-01-26 17:08:43 +08:00
|
|
|
<!-- Tab: Overview -->
|
|
|
|
|
<div x-show="tab === 'overview'" class="space-y-6">
|
|
|
|
|
<!-- Team 1 Stats -->
|
|
|
|
|
<div class="bg-white dark:bg-slate-800 shadow rounded-lg overflow-hidden">
|
|
|
|
|
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-slate-700">
|
|
|
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Team 1</h3>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="overflow-x-auto">
|
|
|
|
|
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
|
|
|
<thead class="bg-gray-50 dark:bg-slate-700">
|
|
|
|
|
<tr>
|
|
|
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Player</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">K</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">D</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">A</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">+/-</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">ADR</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">KAST</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Rating</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody class="bg-white dark:bg-slate-800 divide-y divide-gray-200 dark:divide-gray-700">
|
|
|
|
|
{% for p in team1_players %}
|
|
|
|
|
<tr>
|
|
|
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
<div class="flex-shrink-0 h-8 w-8">
|
|
|
|
|
{% if p.avatar_url %}
|
|
|
|
|
<img class="h-8 w-8 rounded-full" src="{{ p.avatar_url }}" alt="">
|
|
|
|
|
{% else %}
|
|
|
|
|
<div class="h-8 w-8 rounded-full bg-yrtv-100 flex items-center justify-center text-yrtv-600 font-bold text-xs border border-yrtv-200">
|
|
|
|
|
{{ (p.username or p.steam_id_64)[:2] | upper }}
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="ml-4">
|
|
|
|
|
<div class="flex items-center space-x-2">
|
|
|
|
|
<a href="{{ url_for('players.detail', steam_id=p.steam_id_64) }}" class="text-sm font-medium text-gray-900 dark:text-white hover:text-yrtv-600">
|
|
|
|
|
{{ p.username or p.steam_id_64 }}
|
|
|
|
|
</a>
|
|
|
|
|
{% if p.party_size > 1 %}
|
|
|
|
|
{% set pc = p.party_size %}
|
|
|
|
|
{% set p_color = 'bg-blue-100 text-blue-800' %}
|
|
|
|
|
{% if pc == 2 %}{% set p_color = 'bg-indigo-100 text-indigo-800' %}
|
|
|
|
|
{% elif pc == 3 %}{% set p_color = 'bg-blue-100 text-blue-800' %}
|
|
|
|
|
{% elif pc == 4 %}{% set p_color = 'bg-purple-100 text-purple-800' %}
|
|
|
|
|
{% elif pc >= 5 %}{% set p_color = 'bg-orange-100 text-orange-800' %}
|
|
|
|
|
{% endif %}
|
|
|
|
|
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium {{ p_color }} dark:bg-opacity-20" title="Roster Party of {{ p.party_size }}">
|
|
|
|
|
<svg class="mr-1 h-3 w-3" fill="currentColor" viewBox="0 0 20 20">
|
|
|
|
|
<path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z" />
|
|
|
|
|
</svg>
|
|
|
|
|
{{ p.party_size }}
|
|
|
|
|
</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-900 dark:text-white">{{ p.kills }}</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-500 dark:text-gray-400">{{ p.deaths }}</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-500 dark:text-gray-400">{{ p.assists }}</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right font-medium {% if (p.kills - p.deaths) >= 0 %}text-green-600{% else %}text-red-600{% endif %}">
|
|
|
|
|
{{ p.kills - p.deaths }}
|
|
|
|
|
</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-500 dark:text-gray-400">{{ "%.1f"|format(p.adr or 0) }}</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-500 dark:text-gray-400">{{ "%.1f"|format(p.kast or 0) }}%</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right font-bold text-gray-900 dark:text-white">{{ "%.2f"|format(p.rating or 0) }}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
2026-01-26 02:13:06 +08:00
|
|
|
</div>
|
2026-01-26 17:08:43 +08:00
|
|
|
|
|
|
|
|
<!-- Team 2 Stats -->
|
|
|
|
|
<div class="bg-white dark:bg-slate-800 shadow rounded-lg overflow-hidden">
|
|
|
|
|
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-slate-700">
|
|
|
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Team 2</h3>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="overflow-x-auto">
|
|
|
|
|
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
|
|
|
<thead class="bg-gray-50 dark:bg-slate-700">
|
|
|
|
|
<tr>
|
|
|
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Player</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">K</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">D</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">A</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">+/-</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">ADR</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">KAST</th>
|
|
|
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Rating</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody class="bg-white dark:bg-slate-800 divide-y divide-gray-200 dark:divide-gray-700">
|
|
|
|
|
{% for p in team2_players %}
|
|
|
|
|
<tr>
|
|
|
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
<div class="flex-shrink-0 h-8 w-8">
|
|
|
|
|
{% if p.avatar_url %}
|
|
|
|
|
<img class="h-8 w-8 rounded-full" src="{{ p.avatar_url }}" alt="">
|
|
|
|
|
{% else %}
|
|
|
|
|
<div class="h-8 w-8 rounded-full bg-yrtv-100 flex items-center justify-center text-yrtv-600 font-bold text-xs border border-yrtv-200">
|
|
|
|
|
{{ (p.username or p.steam_id_64)[:2] | upper }}
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="ml-4">
|
|
|
|
|
<div class="flex items-center space-x-2">
|
|
|
|
|
<a href="{{ url_for('players.detail', steam_id=p.steam_id_64) }}" class="text-sm font-medium text-gray-900 dark:text-white hover:text-yrtv-600">
|
|
|
|
|
{{ p.username or p.steam_id_64 }}
|
|
|
|
|
</a>
|
|
|
|
|
{% if p.party_size > 1 %}
|
|
|
|
|
{% set pc = p.party_size %}
|
|
|
|
|
{% set p_color = 'bg-blue-100 text-blue-800' %}
|
|
|
|
|
{% if pc == 2 %}{% set p_color = 'bg-indigo-100 text-indigo-800' %}
|
|
|
|
|
{% elif pc == 3 %}{% set p_color = 'bg-blue-100 text-blue-800' %}
|
|
|
|
|
{% elif pc == 4 %}{% set p_color = 'bg-purple-100 text-purple-800' %}
|
|
|
|
|
{% elif pc >= 5 %}{% set p_color = 'bg-orange-100 text-orange-800' %}
|
|
|
|
|
{% endif %}
|
|
|
|
|
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium {{ p_color }} dark:bg-opacity-20" title="Roster Party of {{ p.party_size }}">
|
|
|
|
|
<svg class="mr-1 h-3 w-3" fill="currentColor" viewBox="0 0 20 20">
|
|
|
|
|
<path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z" />
|
|
|
|
|
</svg>
|
|
|
|
|
{{ p.party_size }}
|
|
|
|
|
</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-900 dark:text-white">{{ p.kills }}</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-500 dark:text-gray-400">{{ p.deaths }}</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-500 dark:text-gray-400">{{ p.assists }}</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right font-medium {% if (p.kills - p.deaths) >= 0 %}text-green-600{% else %}text-red-600{% endif %}">
|
|
|
|
|
{{ p.kills - p.deaths }}
|
|
|
|
|
</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-500 dark:text-gray-400">{{ "%.1f"|format(p.adr or 0) }}</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right text-gray-500 dark:text-gray-400">{{ "%.1f"|format(p.kast or 0) }}%</td>
|
|
|
|
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-right font-bold text-gray-900 dark:text-white">{{ "%.2f"|format(p.rating or 0) }}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Tab: Head to Head -->
|
|
|
|
|
<div x-show="tab === 'h2h'" class="bg-white dark:bg-slate-800 shadow rounded-lg overflow-hidden p-6" style="display: none;">
|
2026-01-27 16:51:53 +08:00
|
|
|
<div class="flex justify-between items-end mb-6">
|
|
|
|
|
<div>
|
|
|
|
|
<h3 class="text-lg font-bold text-gray-900 dark:text-white">Head-to-Head Matrix</h3>
|
|
|
|
|
<p class="text-sm text-gray-500 mt-1">Shows <span class="font-bold text-green-600 bg-green-50 px-1 rounded">Kills</span> : <span class="font-bold text-red-500 bg-red-50 px-1 rounded">Deaths</span> interaction between players</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-xs text-gray-400 font-mono">
|
|
|
|
|
Row: Team 1 Players<br>
|
|
|
|
|
Col: Team 2 Players
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="overflow-x-auto rounded-xl border border-gray-200 dark:border-gray-700">
|
2026-01-26 02:13:06 +08:00
|
|
|
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
2026-01-27 16:51:53 +08:00
|
|
|
<thead class="bg-gray-50 dark:bg-slate-700/50">
|
2026-01-26 02:13:06 +08:00
|
|
|
<tr>
|
2026-01-27 16:51:53 +08:00
|
|
|
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider bg-gray-50 dark:bg-slate-700/50 sticky left-0 z-10">
|
|
|
|
|
Team 1 \ Team 2
|
|
|
|
|
</th>
|
2026-01-26 17:08:43 +08:00
|
|
|
{% for victim in team2_players %}
|
2026-01-27 16:51:53 +08:00
|
|
|
<th class="px-2 py-3 text-center text-xs font-medium text-gray-500 dark:text-gray-300 tracking-wider min-w-[80px]" title="{{ victim.username }}">
|
|
|
|
|
<div class="flex flex-col items-center group">
|
|
|
|
|
<div class="relative">
|
|
|
|
|
{% if victim.avatar_url %}
|
|
|
|
|
<img class="h-8 w-8 rounded-full mb-1 border-2 border-transparent group-hover:border-yrtv-400 transition-all" src="{{ victim.avatar_url }}">
|
|
|
|
|
{% else %}
|
|
|
|
|
<div class="h-8 w-8 rounded-full bg-yrtv-100 flex items-center justify-center text-yrtv-600 font-bold text-xs border-2 border-yrtv-200 mb-1 group-hover:border-yrtv-400 transition-all">
|
|
|
|
|
{{ (victim.username or victim.steam_id_64)[:2] | upper }}
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
2026-01-26 17:08:43 +08:00
|
|
|
</div>
|
2026-01-27 16:51:53 +08:00
|
|
|
<span class="truncate w-20 text-center font-bold text-gray-700 dark:text-gray-300 group-hover:text-yrtv-600 transition-colors text-[10px]">{{ victim.username or 'Player' }}</span>
|
2026-01-26 17:08:43 +08:00
|
|
|
</div>
|
|
|
|
|
</th>
|
|
|
|
|
{% endfor %}
|
2026-01-26 02:13:06 +08:00
|
|
|
</tr>
|
|
|
|
|
</thead>
|
2026-01-27 16:51:53 +08:00
|
|
|
<tbody class="bg-white dark:bg-slate-800 divide-y divide-gray-100 dark:divide-gray-700">
|
2026-01-26 17:08:43 +08:00
|
|
|
{% for killer in team1_players %}
|
2026-01-27 16:51:53 +08:00
|
|
|
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30 transition-colors">
|
|
|
|
|
<td class="px-4 py-3 whitespace-nowrap font-medium text-gray-900 dark:text-white bg-white dark:bg-slate-800 sticky left-0 z-10 border-r border-gray-100 dark:border-gray-700 shadow-sm">
|
|
|
|
|
<div class="flex items-center group">
|
|
|
|
|
{% if killer.avatar_url %}
|
|
|
|
|
<img class="h-8 w-8 rounded-full mr-3 border-2 border-transparent group-hover:border-blue-400 transition-all" src="{{ killer.avatar_url }}">
|
|
|
|
|
{% else %}
|
|
|
|
|
<div class="h-8 w-8 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 font-bold text-xs border-2 border-blue-200 mr-3 group-hover:border-blue-400 transition-all">
|
|
|
|
|
{{ (killer.username or killer.steam_id_64)[:2] | upper }}
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
<span class="truncate w-28 font-bold group-hover:text-blue-600 transition-colors">{{ killer.username or 'Player' }}</span>
|
2026-01-26 02:13:06 +08:00
|
|
|
</div>
|
|
|
|
|
</td>
|
2026-01-26 17:08:43 +08:00
|
|
|
{% for victim in team2_players %}
|
2026-01-27 16:51:53 +08:00
|
|
|
<!-- Kills: Killer -> Victim -->
|
2026-01-26 17:08:43 +08:00
|
|
|
{% set kills = h2h_matrix.get(killer.steam_id_64, {}).get(victim.steam_id_64, 0) %}
|
2026-01-27 16:51:53 +08:00
|
|
|
<!-- Deaths: Victim -> Killer (which is Killer's death) -->
|
|
|
|
|
{% set deaths = h2h_matrix.get(victim.steam_id_64, {}).get(killer.steam_id_64, 0) %}
|
|
|
|
|
|
|
|
|
|
<td class="px-2 py-3 text-center border-l border-gray-50 dark:border-gray-700/50">
|
|
|
|
|
<div class="flex items-center justify-center gap-1.5 font-mono">
|
|
|
|
|
<!-- Kills -->
|
|
|
|
|
<span class="{% if kills > deaths %}font-black text-lg text-green-600{% elif kills > 0 %}font-bold text-gray-900 dark:text-white{% else %}text-gray-300 dark:text-gray-600 text-xs{% endif %}">
|
|
|
|
|
{{ kills }}
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
|
|
<span class="text-gray-300 dark:text-gray-600 text-[10px]">:</span>
|
|
|
|
|
|
|
|
|
|
<!-- Deaths -->
|
|
|
|
|
<span class="{% if deaths > kills %}font-black text-lg text-red-500{% elif deaths > 0 %}font-bold text-gray-900 dark:text-white{% else %}text-gray-300 dark:text-gray-600 text-xs{% endif %}">
|
|
|
|
|
{{ deaths }}
|
|
|
|
|
</span>
|
2026-01-26 17:08:43 +08:00
|
|
|
</div>
|
2026-01-27 16:51:53 +08:00
|
|
|
|
|
|
|
|
<!-- Interaction Bar (Optional visual) -->
|
|
|
|
|
{% if kills + deaths > 0 %}
|
|
|
|
|
<div class="w-full h-1 bg-gray-100 dark:bg-slate-700 rounded-full mt-1 overflow-hidden flex">
|
|
|
|
|
{% set total = kills + deaths %}
|
|
|
|
|
<div class="bg-green-500 h-full" style="width: {{ (kills / total * 100) }}%"></div>
|
|
|
|
|
<div class="bg-red-500 h-full" style="width: {{ (deaths / total * 100) }}%"></div>
|
2026-01-26 02:13:06 +08:00
|
|
|
</div>
|
2026-01-26 17:08:43 +08:00
|
|
|
{% endif %}
|
2026-01-26 02:13:06 +08:00
|
|
|
</td>
|
2026-01-26 17:08:43 +08:00
|
|
|
{% endfor %}
|
2026-01-26 02:13:06 +08:00
|
|
|
</tr>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-01-26 17:08:43 +08:00
|
|
|
|
|
|
|
|
<!-- Tab: Round History -->
|
|
|
|
|
<div x-show="tab === 'rounds'" class="bg-white dark:bg-slate-800 shadow rounded-lg p-6 space-y-4" style="display: none;">
|
|
|
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-4">Round by Round History</h3>
|
|
|
|
|
|
|
|
|
|
{% if not round_details %}
|
|
|
|
|
<p class="text-gray-500">No round detail data available for this match.</p>
|
|
|
|
|
{% else %}
|
|
|
|
|
<div class="space-y-2">
|
|
|
|
|
{% for r_num, data in round_details.items() %}
|
|
|
|
|
<div x-data="{ expanded: false }" class="border border-gray-200 dark:border-gray-700 rounded-md overflow-hidden">
|
|
|
|
|
<!-- Round Header -->
|
|
|
|
|
<div @click="expanded = !expanded"
|
|
|
|
|
class="flex items-center justify-between px-4 py-3 bg-gray-50 dark:bg-slate-700 cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-600 transition">
|
|
|
|
|
<div class="flex items-center space-x-4">
|
|
|
|
|
<span class="text-sm font-bold text-gray-500 dark:text-gray-400">Round {{ r_num }}</span>
|
|
|
|
|
|
|
|
|
|
<!-- Winner Icon -->
|
|
|
|
|
{% if data.info.winner_side == 'CT' %}
|
|
|
|
|
<span class="px-2 py-0.5 rounded text-xs font-bold bg-blue-100 text-blue-800 border border-blue-200">
|
|
|
|
|
CT Win
|
|
|
|
|
</span>
|
|
|
|
|
{% elif data.info.winner_side == 'T' %}
|
|
|
|
|
<span class="px-2 py-0.5 rounded text-xs font-bold bg-yellow-100 text-yellow-800 border border-yellow-200">
|
|
|
|
|
T Win
|
|
|
|
|
</span>
|
|
|
|
|
{% else %}
|
|
|
|
|
<span class="px-2 py-0.5 rounded text-xs font-bold bg-gray-100 text-gray-800">
|
|
|
|
|
{{ data.info.winner_side }}
|
|
|
|
|
</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
|
|
|
{{ data.info.win_reason_desc }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="flex items-center space-x-4">
|
|
|
|
|
<span class="text-lg font-mono font-bold text-gray-900 dark:text-white">
|
|
|
|
|
{{ data.info.ct_score }} - {{ data.info.t_score }}
|
|
|
|
|
</span>
|
|
|
|
|
<svg :class="{'rotate-180': expanded}" class="h-5 w-5 text-gray-400 transform transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
|
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Round Details (Expanded) -->
|
|
|
|
|
<div x-show="expanded" class="p-4 bg-white dark:bg-slate-800 border-t border-gray-200 dark:border-gray-700">
|
|
|
|
|
|
|
|
|
|
<!-- Economy Section (if available) -->
|
|
|
|
|
{% if data.economy %}
|
|
|
|
|
<div class="mb-4">
|
|
|
|
|
<h4 class="text-xs font-bold text-gray-500 uppercase tracking-wider mb-2">Economy Snapshot</h4>
|
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
|
|
|
<!-- Left Team (usually CT start, but let's just list keys for now) -->
|
|
|
|
|
<!-- We can map steam_id to username via existing players list if passed, or just show summary -->
|
|
|
|
|
<!-- For simplicity v1: Just show count of weapons -->
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-xs text-gray-400 italic">
|
|
|
|
|
(Detailed economy view coming soon)
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
<!-- Events Timeline -->
|
|
|
|
|
<div class="space-y-2">
|
|
|
|
|
{% for event in data.events %}
|
|
|
|
|
<div class="flex items-center text-sm">
|
|
|
|
|
<span class="w-12 text-right text-gray-400 font-mono text-xs mr-4">{{ event.event_time }}s</span>
|
|
|
|
|
|
|
|
|
|
{% if event.event_type == 'kill' %}
|
|
|
|
|
<div class="flex items-center flex-1">
|
|
|
|
|
<span class="font-medium {% if event.is_headshot %}text-red-600{% else %}text-gray-900 dark:text-white{% endif %}">
|
|
|
|
|
{{ player_name_map.get(event.attacker_steam_id, event.attacker_steam_id) }}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="mx-2 text-gray-400">
|
|
|
|
|
{% if event.is_headshot %}⌖{% else %}🔫{% endif %}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="text-gray-600 dark:text-gray-300">
|
|
|
|
|
{{ player_name_map.get(event.victim_steam_id, event.victim_steam_id) }}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="ml-2 text-xs text-gray-400 bg-gray-100 dark:bg-slate-700 px-1 rounded">{{ event.weapon }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
{% elif event.event_type == 'bomb_plant' %}
|
|
|
|
|
<div class="flex items-center text-yellow-600 font-medium">
|
|
|
|
|
<span>💣 Bomb Planted</span>
|
|
|
|
|
</div>
|
|
|
|
|
{% elif event.event_type == 'bomb_defuse' %}
|
|
|
|
|
<div class="flex items-center text-blue-600 font-medium">
|
|
|
|
|
<span>✂️ Bomb Defused</span>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
2026-01-26 02:13:06 +08:00
|
|
|
</div>
|
2026-01-26 17:08:43 +08:00
|
|
|
|
|
|
|
|
<!-- Add Player Name Map for JS/Frontend Lookup if needed -->
|
|
|
|
|
<script>
|
|
|
|
|
// Optional: Pass player mapping to JS to replace IDs with Names in Timeline
|
|
|
|
|
// But Jinja is cleaner if we had the map.
|
|
|
|
|
</script>
|
|
|
|
|
|
2026-01-26 02:13:06 +08:00
|
|
|
{% endblock %}
|