remove elo (#1304)

This commit is contained in:
NotSoToothless
2026-06-05 12:57:20 -07:00
committed by GitHub
parent a8317575a3
commit e2d02a34b3
18 changed files with 112 additions and 297 deletions
+1 -1
View File
@@ -276,7 +276,7 @@
<p class="text-sm text-muted mt-3">
<strong class="text-accent">Weekly BR Report:</strong>
Fires at the end of each BR rotation (~10 min after the BR window closes).
With a squadron name, posts the top 15 players by ELO for that squadron.
With a squadron name, posts the top 15 players for that squadron.
With an empty squadron field (or <code>*</code>), posts the top 20 squadrons
of the week with their top 5 players each. Free for all servers.
</p>
+20 -8
View File
@@ -101,8 +101,9 @@
font-weight: 700;
margin-bottom: 1rem;
}
.sq-winner { color: #90EE90; text-shadow: 0 0 10px rgba(144, 238, 144, 0.3); font-family: 'skyquakesymbols', 'Inter', sans-serif; }
.sq-loser { color: rgba(255, 255, 255, 0.7); font-family: 'skyquakesymbols', 'Inter', sans-serif; }
.sq-winner { color: #90EE90; text-shadow: 0 0 10px rgba(144, 238, 144, 0.3); font-family: 'skyquakesymbols', 'Inter', sans-serif; text-decoration: none; }
.sq-loser { color: rgb(255, 100, 100); text-shadow: 0 0 10px rgba(255, 100, 100, 0.25); font-family: 'skyquakesymbols', 'Inter', sans-serif; text-decoration: none; }
.sq-winner:hover, .sq-loser:hover { text-decoration: underline; text-underline-offset: 0.15em; }
.sq-vs { color: rgba(255, 255, 255, 0.4); font-size: 1rem; font-weight: 400; }
.match-meta {
display: flex;
@@ -643,9 +644,9 @@
<div class="match-type-badge" id="matchTypeBadge"></div>
<div class="match-map-name" id="matchMapName"></div>
<div class="match-squadrons">
<span class="sq-winner" id="sqWinner"></span>
<a class="sq-winner" id="sqWinner"></a>
<span class="sq-vs">VS</span>
<span class="sq-loser" id="sqLoser"></span>
<a class="sq-loser" id="sqLoser"></a>
</div>
<div class="match-meta" id="matchMeta"></div>
</div>
@@ -832,6 +833,12 @@
return `/MAPS/${fileName}.jpg`;
}
function squadronHref(team, fallbackName) {
if (team?.clan_id != null && team.clan_id !== '') return `/squadrons/${encodeURIComponent(String(team.clan_id))}`;
const name = fallbackName || team?.squadron || team?.squadron_tagged || '';
return name ? `/squadrons/${encodeURIComponent(name)}` : '/squadrons';
}
function renderTeamTable(players, isWinner) {
const sorted = [...players].sort((a, b) => (b.score || 0) - (a.score || 0));
return sorted.map(player => {
@@ -942,8 +949,12 @@
const TYPE_LABELS = { sqb: __t('live.squadronBattle'), rb: __t('live.randomBattle') };
document.getElementById('matchTypeBadge').textContent = TYPE_LABELS[match.game_type] || match.game_type || 'Match';
document.getElementById('matchMapName').textContent = cleanMapName;
document.getElementById('sqWinner').textContent = match.winning_tag || match.winning_squadron || 'Unknown';
document.getElementById('sqLoser').textContent = match.losing_tag || match.losing_squadron || 'Unknown';
const winnerEl = document.getElementById('sqWinner');
const loserEl = document.getElementById('sqLoser');
winnerEl.textContent = match.winning_tag || match.winning_squadron || 'Unknown';
winnerEl.href = squadronHref(match.winning_team, match.winning_squadron);
loserEl.textContent = match.losing_tag || match.losing_squadron || 'Unknown';
loserEl.href = squadronHref(match.losing_team, match.losing_squadron);
// Meta
const endTime = match.endtime_iso ? new Date(match.endtime_iso).toISOString().replace('T', ' ').substring(0, 16) + ' UTC' : 'Unknown';
@@ -997,8 +1008,9 @@
// Add duration/mode to meta
const metaEl = document.getElementById('matchMeta');
if (replay.duration) {
const mins = Math.floor(replay.duration / 60);
const secs = replay.duration % 60;
const durationSeconds = Math.max(0, Math.round(Number(replay.duration) || 0));
const mins = Math.floor(durationSeconds / 60);
const secs = durationSeconds % 60;
metaEl.innerHTML += `<span class="match-meta-item"><i class="fas fa-hourglass-half"></i>${mins}m ${secs}s</span>`;
}
if (replay.mode) {
+3 -86
View File
@@ -161,57 +161,6 @@
margin-top: 0.2rem;
}
.stat-card-performance .stat-label {
margin-top: 0;
margin-bottom: 0.35rem;
}
.stat-performance-wrap {
width: 100%;
margin-top: 0.35rem;
}
.stat-performance-wrap .sq-performance-bar {
width: 100%;
}
.sq-performance-bar {
width: 100%;
display: flex;
align-items: center;
gap: 0.55rem;
}
.sq-performance-bar-track {
position: relative;
flex: 1;
height: 12px;
border-radius: 999px;
overflow: hidden;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(144, 238, 144, 0.16);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.35);
}
.sq-performance-bar-fill {
height: 100%;
border-radius: inherit;
background: linear-gradient(90deg, rgba(245, 245, 220, 0.96), rgba(144, 238, 144, 0.96));
box-shadow: 0 0 14px rgba(144, 238, 144, 0.25);
transition: width 0.35s ease;
}
.sq-performance-bar-value {
min-width: 2.75rem;
text-align: right;
font-variant-numeric: tabular-nums;
font-size: 0.9rem;
font-weight: 700;
color: #F5F5DC;
letter-spacing: 0.02em;
}
.vehicles-section {
background: rgba(30, 30, 30, 0.6);
border-radius: 1rem;
@@ -954,18 +903,6 @@
<div class="player-empty-state">No stats yet for this player</div>
<% } %>
<%
const performanceLabel = 'ELO';
const buildPerformanceBarHtml = (score) => `
<div class="sq-performance-bar" role="progressbar" aria-valuemin="0" aria-valuemax="5" aria-valuenow="${score}" aria-label="${performanceLabel} ${score}">
<div class="sq-performance-bar-track">
<div class="sq-performance-bar-fill" style="width:${Math.max(0, Math.min(100, (Number(score) || 0) * 20))}%;"></div>
</div>
<span class="sq-performance-bar-value">${Number(score || 0).toFixed(2)}</span>
</div>
`;
const initialPerformance = Math.max(0, Math.min(5, Number(playerData.performance || 0)));
%>
<div class="player-stats-summary">
<div class="stat-card">
<span class="stat-number" id="stat-total-battles"><%= totals.totalBattles %></span>
@@ -991,11 +928,6 @@
<span class="stat-number" id="stat-kps"><%= totals.totalBattles > 0 ? (totals.totalKills / totals.totalBattles).toFixed(2) : '0.00' %></span>
<div class="stat-label"><%= t('common.kps') %></div>
</div>
<div class="stat-card stat-card-performance">
<div class="stat-label">ELO</div>
<div id="stat-performance" class="stat-performance-wrap"><%- buildPerformanceBarHtml(initialPerformance) %></div>
</div>
<div class="stat-card">
<span class="stat-number" id="stat-air-kills"><%= totals.totalAirKills %></span>
<div class="stat-label"><%= t('common.airKills') %></div>
@@ -2215,7 +2147,6 @@
const _playerUID = document.getElementById('playerChartSection').dataset.uid;
const initialVehicles = <%- JSON.stringify((playerData.vehicles || []).filter(v => v.vehicle !== 'DISCONNECTED')) %>;
const initialPerformanceRating = <%- JSON.stringify(initialPerformance) %>;
const noStatsYet = <%- JSON.stringify(Boolean(playerData.no_stats_yet)) %>;
let cumulativeRequestId = 0;
@@ -2228,7 +2159,7 @@
// Cache hit for all-time data
if (!startDate && !endDate && initialVehicles) {
renderVehicleTable(initialVehicles.map(v => ({...v})));
updateSummaryStats(initialVehicles, initialPerformanceRating);
updateSummaryStats(initialVehicles);
return;
}
@@ -2249,7 +2180,7 @@
if (thisRequest !== cumulativeRequestId) return;
if (loadingEl) loadingEl.style.display = 'none';
renderVehicleTable(data.vehicles || []);
updateSummaryStats(data.vehicles || [], data.performance);
updateSummaryStats(data.vehicles || []);
} catch (err) {
if (thisRequest !== cumulativeRequestId) return;
console.error('[Cumulative Filter] Error fetching vehicle data:', err);
@@ -2312,19 +2243,7 @@
if (isMobileView) populateMobileCards();
}
function buildPerformanceBarHtml(score) {
const rating = Math.max(0, Math.min(5, Number(score) || 0));
return `
<div class="sq-performance-bar" role="progressbar" aria-valuemin="0" aria-valuemax="5" aria-valuenow="${rating}" aria-label="ELO ${rating.toFixed(2)}">
<div class="sq-performance-bar-track">
<div class="sq-performance-bar-fill" style="width:${rating * 20}%;"></div>
</div>
<span class="sq-performance-bar-value">${rating.toFixed(2)}</span>
</div>
`;
}
function updateSummaryStats(vehicles, performanceScore) {
function updateSummaryStats(vehicles) {
vehicles = vehicles.filter(v => v.vehicle !== 'DISCONNECTED');
let totalBattles = 0, totalWins = 0, totalGK = 0, totalAK = 0;
let totalAssists = 0, totalCaptures = 0, totalDeaths = 0;
@@ -2353,8 +2272,6 @@
document.getElementById('stat-assists').textContent = totalAssists;
document.getElementById('stat-deaths').textContent = totalDeaths;
document.getElementById('stat-captures').textContent = totalCaptures;
const performanceWrap = document.getElementById('stat-performance');
if (performanceWrap) performanceWrap.innerHTML = buildPerformanceBarHtml(performanceScore ?? initialPerformanceRating);
}
function applyCumulativeFilters() {
+23 -137
View File
@@ -166,24 +166,6 @@
margin-top: 0.2rem;
}
.stat-card-performance .stat-label {
margin-top: 0;
margin-bottom: 0.35rem;
}
.stat-card-performance {
min-height: 78px;
}
.stat-performance-wrap {
width: 100%;
margin-top: 0.35rem;
}
.stat-performance-wrap .sq-performance-bar {
width: 100%;
}
.members-section {
background: rgba(30, 30, 30, 0.6);
border-radius: 1rem;
@@ -646,51 +628,6 @@
color: rgba(255, 255, 255, 0.8);
}
/* Cumulative performance bar */
.sq-mini-graph {
width: 160px;
min-height: 40px;
margin: 0 auto;
display: flex;
align-items: center;
}
.sq-performance-bar {
width: 100%;
display: flex;
align-items: center;
gap: 0.55rem;
}
.sq-performance-bar-track {
position: relative;
flex: 1;
height: 12px;
border-radius: 999px;
overflow: hidden;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(144, 238, 144, 0.16);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.35);
}
.sq-performance-bar-fill {
height: 100%;
border-radius: inherit;
background: linear-gradient(90deg, rgba(245, 245, 220, 0.96), rgba(144, 238, 144, 0.96));
box-shadow: 0 0 14px rgba(144, 238, 144, 0.25);
transition: width 0.35s ease;
}
.sq-performance-bar-value {
min-width: 2.75rem;
text-align: right;
font-variant-numeric: tabular-nums;
font-size: 0.9rem;
font-weight: 700;
color: #F5F5DC;
letter-spacing: 0.02em;
}
/* Games section */
.sq-games-section {
background: rgba(30, 30, 30, 0.6);
@@ -940,8 +877,6 @@
const summaryKps = typeof squadronData.squadron_summary.kps === 'number'
? squadronData.squadron_summary.kps.toFixed(2)
: (Number(squadronData.squadron_summary.kps || 0)).toFixed(2);
const summaryPerformance = Math.max(0, Math.min(5, Number(squadronData.squadron_summary.performance || 0)));
const summaryPerformanceWidth = summaryPerformance * 20;
%>
<div class="squadron-stats-summary">
<% if (squadronData.squadron_summary.points && squadronData.squadron_summary.points.has_points_data) { %>
@@ -978,18 +913,6 @@
<span class="stat-number" id="sq-stat-kps"><%= summaryKps %></span>
<div class="stat-label">KPS</div>
</div>
<div class="stat-card stat-card-performance">
<div class="stat-label">ELO</div>
<div id="sq-stat-performance" class="stat-performance-wrap">
<div class="sq-performance-bar" role="progressbar" aria-valuemin="0" aria-valuemax="5" aria-valuenow="<%= summaryPerformance %>" aria-label="ELO <%= summaryPerformance.toFixed(2) %>">
<div class="sq-performance-bar-track">
<div class="sq-performance-bar-fill" style="width:<%= summaryPerformanceWidth %>%;"></div>
</div>
<span class="sq-performance-bar-value"><%= summaryPerformance.toFixed(2) %></span>
</div>
</div>
</div>
<div class="stat-card">
<span class="stat-number" id="sq-stat-ground-kills"><%= squadronData.squadron_summary.ground_kills %></span>
<div class="stat-label"><%= t('common.groundKills') %></div>
@@ -1135,12 +1058,9 @@
<th class="sortable" onclick="sortMembersTable('nick')" data-sort="nick">
<%= t('common.player') %> <i class="fas fa-sort sort-icon"></i>
</th>
<th class="sortable" onclick="sortMembersTable('performance')" data-sort="performance">
ELO <i class="fas fa-sort sort-icon"></i>
</th>
<th class="sortable" onclick="sortMembersTable('sqb_points')" data-sort="sqb_points">
<%= t('common.points') %> <i class="fas fa-sort sort-icon"></i>
</th>
<th class="sortable" onclick="sortMembersTable('sqb_points')" data-sort="sqb_points">
<%= t('common.points') %> <i class="fas fa-sort sort-icon"></i>
</th>
<th class="sortable" onclick="sortMembersTable('total_battles')" data-sort="total_battles">
<%= t('common.battles') %> <i class="fas fa-sort sort-icon"></i>
</th>
@@ -1177,24 +1097,10 @@
</tr>
</thead>
<tbody id="sq-members-table-body">
<%
const buildEfficiencyBar = (score) => {
const rating = Math.max(0, Math.min(5, Number(score) || 0));
return `
<div class="sq-performance-bar" role="progressbar" aria-valuemin="0" aria-valuemax="5" aria-valuenow="${rating}" aria-label="ELO ${rating.toFixed(2)}">
<div class="sq-performance-bar-track">
<div class="sq-performance-bar-fill" style="width:${rating * 20}%;"></div>
</div>
<span class="sq-performance-bar-value">${rating.toFixed(2)}</span>
</div>
`;
};
%>
<% squadronData.players.forEach(player => { const score = Math.max(0, Math.min(5, Number(player.performance || 0))); %>
<tr class="row-link">
<td class="player-name"><a href="/players/<%= player.uid %>" class="row-link-overlay" aria-label="View <%= player.nick %>"></a><%= player.nick %><button class="pdm-details-btn" onclick="event.stopPropagation(); openPlayerDetailsModal('<%= player.uid %>', '<%= player.nick.replace(/'/g, "\\'") %>')" title="<%= t('squadrons.quickDetails') %>"><i class="fas fa-eye"></i></button></td>
<td class="stat-cell" data-performance-cell data-sort-value="<%= score %>"><div class="sq-mini-graph"><%- buildEfficiencyBar(score) %></div></td>
<td class="stat-cell"><%= player.sqb_points || 0 %></td>
<% squadronData.players.forEach(player => { %>
<tr class="row-link">
<td class="player-name"><a href="/players/<%= player.uid %>" class="row-link-overlay" aria-label="View <%= player.nick %>"></a><%= player.nick %><button class="pdm-details-btn" onclick="event.stopPropagation(); openPlayerDetailsModal('<%= player.uid %>', '<%= player.nick.replace(/'/g, "\\'") %>')" title="<%= t('squadrons.quickDetails') %>"><i class="fas fa-eye"></i></button></td>
<td class="stat-cell"><%= player.sqb_points || 0 %></td>
<td class="stat-cell"><%= player.total_battles %></td>
<td class="stat-cell"><%= player.wins %></td>
<td class="stat-cell"><%= player.win_rate.toFixed(1) %>%</td>
@@ -2443,11 +2349,6 @@
setText('sq-stat-assists', s.assists);
setText('sq-stat-deaths', s.deaths);
setText('sq-stat-captures', s.captures);
const performanceWrap = getElement('sq-stat-performance');
if (performanceWrap) {
const score = Math.max(0, Math.min(5, Number(s.performance || 0)));
performanceWrap.innerHTML = buildEfficiencyBarHtml(score);
}
}
function escapeHtml(str) {
@@ -3107,27 +3008,14 @@
renderPerformanceView();
};
function buildEfficiencyBarHtml(score) {
const rating = Math.max(0, Math.min(5, Number(score) || 0));
return `
<div class="sq-performance-bar" role="progressbar" aria-valuemin="0" aria-valuemax="5" aria-valuenow="${rating}" aria-label="ELO ${rating.toFixed(2)}">
<div class="sq-performance-bar-track">
<div class="sq-performance-bar-fill" style="width:${rating * 20}%;"></div>
</div>
<span class="sq-performance-bar-value">${rating.toFixed(2)}</span>
</div>
`;
}
function renderMembersTable(players) {
function renderMembersTable(players) {
const tbody = getElement('sq-members-table-body');
if (!tbody) return;
if (!players.length) {
tbody.innerHTML = '';
document.querySelectorAll('.sq-mini-graph').forEach(wrap => { wrap.innerHTML = ''; });
return;
}
if (!players.length) {
tbody.innerHTML = '';
return;
}
tbody.innerHTML = players.map(p => {
const nickRaw = p.nick || '';
@@ -3136,13 +3024,11 @@
const winRate = (typeof p.win_rate === 'number' ? p.win_rate.toFixed(1) : (p.win_rate || 0)) + '%';
const kdr = typeof p.kdr === 'number' ? p.kdr.toFixed(2) : (p.kdr || 0);
const kps = typeof p.kps === 'number' ? p.kps.toFixed(2) : (Number(p.kps) || 0).toFixed(2);
const performanceValue = Math.max(0, Math.min(5, Number(p.performance || 0)));
return '<tr data-uid="' + escapeHtml(String(p.uid)) + '" class="row-link">' +
'<td class="player-name"><a href="/players/' + encodeURIComponent(p.uid) + '" class="row-link-overlay" aria-label="View ' + nickEsc + '"></a>' + nickEsc +
'<button class="pdm-details-btn" onclick="event.stopPropagation(); openPlayerDetailsModal(\'' + p.uid + '\', \'' + nickJs + '\')" title="' + __t('squadrons.quickDetails') + '">' +
'<i class="fas fa-eye"></i></button></td>' +
'<td class="stat-cell" data-performance-cell data-sort-value="' + performanceValue + '"><div class="sq-mini-graph" id="sq-cumulative-spark-' + escapeHtml(String(p.uid)) + '">' + buildEfficiencyBarHtml(performanceValue) + '</div></td>' +
'<td class="stat-cell">' + (p.sqb_points || 0) + '</td>' +
return '<tr data-uid="' + escapeHtml(String(p.uid)) + '" class="row-link">' +
'<td class="player-name"><a href="/players/' + encodeURIComponent(p.uid) + '" class="row-link-overlay" aria-label="View ' + nickEsc + '"></a>' + nickEsc +
'<button class="pdm-details-btn" onclick="event.stopPropagation(); openPlayerDetailsModal(\'' + p.uid + '\', \'' + nickJs + '\')" title="' + __t('squadrons.quickDetails') + '">' +
'<i class="fas fa-eye"></i></button></td>' +
'<td class="stat-cell">' + (p.sqb_points || 0) + '</td>' +
'<td class="stat-cell">' + (p.total_battles || 0) + '</td>' +
'<td class="stat-cell">' + (p.wins || 0) + '</td>' +
'<td class="stat-cell">' + winRate + '</td>' +
@@ -3157,12 +3043,12 @@
'</tr>';
}).join('');
// Reset sort state so the next call lands on desc instead of toggling
try { currentSort = { field: '', direction: 'desc' }; } catch (e) { /* no-op */ }
if (typeof sortMembersTable === 'function') {
sortMembersTable('performance');
}
}
// Reset sort state so the next call lands on desc instead of toggling
try { currentSort = { field: '', direction: 'desc' }; } catch (e) { /* no-op */ }
if (typeof sortMembersTable === 'function') {
sortMembersTable('sqb_points');
}
}
initSeasons();
renderMembersTable(currentSquadronPlayers);