Fix historical leaderboard labels, stats ranking table uniformity and publish Steam icon
This commit is contained in:
BIN
frontend/assets/img/brands/steam.png
Normal file
BIN
frontend/assets/img/brands/steam.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 284 KiB |
@@ -45,24 +45,32 @@ const LEADERBOARD_METRICS = Object.freeze([
|
||||
key: "kills",
|
||||
title: "Top kills",
|
||||
valueHeading: "Kills",
|
||||
ratioHeading: "Kills/partida",
|
||||
ratioMode: "kills",
|
||||
emptyMessage: "Sin datos historicos suficientes para mostrar este ranking de kills.",
|
||||
},
|
||||
{
|
||||
key: "deaths",
|
||||
title: "Top muertes",
|
||||
valueHeading: "Muertes",
|
||||
ratioHeading: "Muertes/partida",
|
||||
ratioMode: "deaths",
|
||||
emptyMessage: "Sin datos historicos suficientes para mostrar este ranking de muertes.",
|
||||
},
|
||||
{
|
||||
key: "matches_over_100_kills",
|
||||
title: "Top partidas con 100+ kills",
|
||||
valueHeading: "Partidas 100+",
|
||||
ratioHeading: null,
|
||||
ratioMode: null,
|
||||
emptyMessage: "Ningun jugador ha registrado partidas de 100+ kills en esta ventana.",
|
||||
},
|
||||
{
|
||||
key: "support",
|
||||
title: "Top puntos de soporte",
|
||||
valueHeading: "Soporte",
|
||||
ratioHeading: "Soporte/partida",
|
||||
ratioMode: "support",
|
||||
emptyMessage: "Sin datos historicos suficientes para mostrar este ranking de soporte.",
|
||||
},
|
||||
]);
|
||||
@@ -94,6 +102,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const weeklyTableNode = document.getElementById("weekly-leaderboard-table");
|
||||
const weeklyBodyNode = document.getElementById("weekly-leaderboard-body");
|
||||
const weeklyValueHeadingNode = document.getElementById("weekly-leaderboard-value-heading");
|
||||
const weeklyRatioHeadingNode = document.getElementById("weekly-leaderboard-ratio-heading");
|
||||
const weeklyWindowNoteNode = document.getElementById("weekly-window-note");
|
||||
const weeklySnapshotMetaNode = document.getElementById(
|
||||
"weekly-leaderboard-snapshot-meta",
|
||||
@@ -210,6 +219,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
weeklyBodyNode,
|
||||
weeklyTitleNode,
|
||||
weeklyValueHeadingNode,
|
||||
weeklyRatioHeadingNode,
|
||||
weeklyWindowNoteNode,
|
||||
weeklySnapshotMetaNode,
|
||||
activeMetricConfig,
|
||||
@@ -547,6 +557,7 @@ function hydrateWeeklyLeaderboard(
|
||||
bodyNode,
|
||||
titleNode,
|
||||
valueHeadingNode,
|
||||
ratioHeadingNode,
|
||||
noteNode,
|
||||
snapshotMetaNode,
|
||||
metricConfig,
|
||||
@@ -555,6 +566,7 @@ function hydrateWeeklyLeaderboard(
|
||||
const targetServerSlug = result.value?.data?.server_slug || activeServerSlug;
|
||||
const resolvedTimeframeKey = result.value?.data?.timeframe || timeframeKey;
|
||||
valueHeadingNode.textContent = metricConfig.valueHeading;
|
||||
syncLeaderboardRatioColumn(tableNode, ratioHeadingNode, bodyNode, metricConfig);
|
||||
if (result.status !== "fulfilled") {
|
||||
titleNode.textContent = buildLeaderboardTitle(
|
||||
metricConfig,
|
||||
@@ -614,13 +626,11 @@ function hydrateWeeklyLeaderboard(
|
||||
bodyNode.innerHTML = items
|
||||
.map(
|
||||
(item) => {
|
||||
const kills = resolveHistoricalKills(item, metricConfig);
|
||||
const matches = Number(item.matches_considered);
|
||||
const killsPerMatch = formatHistoricalKillsPerMatch(
|
||||
item?.kills_per_match,
|
||||
kills,
|
||||
matches,
|
||||
);
|
||||
const ratioValue = formatHistoricalRatio(item, metricConfig, matches);
|
||||
const ratioCell = metricConfig.ratioMode
|
||||
? `<td>${escapeHtml(ratioValue)}</td>`
|
||||
: "";
|
||||
|
||||
return `
|
||||
<tr>
|
||||
@@ -628,13 +638,14 @@ function hydrateWeeklyLeaderboard(
|
||||
<td>${escapeHtml(item.player?.name || "Jugador no identificado")}</td>
|
||||
<td>${escapeHtml(formatNumber(item.metric_value))}</td>
|
||||
<td>${escapeHtml(formatNumber(item.matches_considered))}</td>
|
||||
<td>${escapeHtml(killsPerMatch)}</td>
|
||||
${ratioCell}
|
||||
</tr>
|
||||
`;
|
||||
},
|
||||
)
|
||||
.join("");
|
||||
stateNode.hidden = true;
|
||||
syncLeaderboardRatioColumn(tableNode, ratioHeadingNode, bodyNode, metricConfig);
|
||||
tableNode.hidden = false;
|
||||
}
|
||||
|
||||
@@ -1724,13 +1735,43 @@ function resolveHistoricalKills(item, metricConfig) {
|
||||
return Number.NaN;
|
||||
}
|
||||
|
||||
function formatHistoricalKillsPerMatch(rawKillsPerMatch, rawKills, rawMatches) {
|
||||
const directValue = Number(rawKillsPerMatch);
|
||||
function syncLeaderboardRatioColumn(tableNode, ratioHeadingNode, bodyNode, metricConfig) {
|
||||
if (!tableNode || !ratioHeadingNode || !bodyNode) {
|
||||
return;
|
||||
}
|
||||
const showRatio = Boolean(metricConfig?.ratioMode);
|
||||
ratioHeadingNode.hidden = !showRatio;
|
||||
ratioHeadingNode.textContent = metricConfig?.ratioHeading || "";
|
||||
const ratioColumnIndex = ratioHeadingNode.cellIndex;
|
||||
bodyNode.querySelectorAll("tr").forEach((row) => {
|
||||
const ratioCell = row.children[ratioColumnIndex];
|
||||
if (ratioCell) {
|
||||
ratioCell.hidden = !showRatio;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function formatHistoricalRatio(item, metricConfig, matches) {
|
||||
if (!metricConfig?.ratioMode) {
|
||||
return "";
|
||||
}
|
||||
if (metricConfig.ratioMode === "kills") {
|
||||
const kills = resolveHistoricalKills(item, metricConfig);
|
||||
return formatHistoricalPerMatch(item?.kills_per_match, kills, matches);
|
||||
}
|
||||
if (metricConfig.ratioMode === "deaths" || metricConfig.ratioMode === "support") {
|
||||
return formatHistoricalPerMatch(null, Number(item?.metric_value), matches);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function formatHistoricalPerMatch(rawDirectValue, rawTotalValue, rawMatches) {
|
||||
const directValue = Number(rawDirectValue);
|
||||
if (Number.isFinite(directValue)) {
|
||||
return formatDecimal(directValue, 2);
|
||||
}
|
||||
|
||||
const kills = Number(rawKills);
|
||||
const kills = Number(rawTotalValue);
|
||||
const matches = Number(rawMatches);
|
||||
if (!Number.isFinite(kills) || !Number.isFinite(matches) || matches <= 0) {
|
||||
return "-";
|
||||
|
||||
@@ -271,7 +271,7 @@
|
||||
if (statusCode === 400 && normalizedMessage.includes("metric")) {
|
||||
setRankingState("warning", "La m\u00e9trica solicitada no est\u00e1 soportada.");
|
||||
renderEmptyState(
|
||||
"Usa kills, deaths, teamkills, partidas jugadas, K/D o Kills/partida.",
|
||||
"Usa Kills, Muertes, TK, Partidas, KD o Kills/partida.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -397,10 +397,10 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="ranking-table__metric">${formatMetricValue(item.metric_value, metric)}</td>
|
||||
<td>${safeInt(item.matches_considered, 0)}</td>
|
||||
<td>${safeInt(item.kills, 0)}</td>
|
||||
<td>${safeInt(item.deaths, 0)}</td>
|
||||
<td>${safeInt(item.teamkills, 0)}</td>
|
||||
<td>${safeInt(item.matches_considered, 0)}</td>
|
||||
<td>${safeDecimal(item.kd_ratio, 2, "0.00")}</td>
|
||||
${hideKppColumn ? "" : `<td>${killsPerMatch}</td>`}
|
||||
</tr>
|
||||
@@ -454,10 +454,10 @@
|
||||
function labelForMetric(metric) {
|
||||
const labels = {
|
||||
kills: "Kills",
|
||||
deaths: "Deaths",
|
||||
teamkills: "Teamkills",
|
||||
matches_considered: "Partidas jugadas",
|
||||
kd_ratio: "K/D",
|
||||
deaths: "Muertes",
|
||||
teamkills: "TK",
|
||||
matches_considered: "Partidas",
|
||||
kd_ratio: "KD",
|
||||
kills_per_match: "Kills/partida",
|
||||
};
|
||||
return labels[metric] || "Kills";
|
||||
|
||||
@@ -359,8 +359,8 @@
|
||||
.map((item) => {
|
||||
const rank = safeInt(item.ranking_position, 0);
|
||||
const playerName = escapeHtml(String(item.player_name || "Jugador sin nombre"));
|
||||
const kills = safeInt(firstFiniteValue(item.kills, item.metric_value), 0);
|
||||
const matches = safeInt(item.matches_considered, 0);
|
||||
const kills = safeInt(firstFiniteValue(item.kills, item.metric_value), 0);
|
||||
const killsPerMatch = formatKillsPerMatch(
|
||||
item.kills_per_match,
|
||||
kills,
|
||||
@@ -378,12 +378,12 @@
|
||||
<strong>${playerName}</strong>
|
||||
</div>
|
||||
</td>
|
||||
<td class="stats-annual-metric">${kills}</td>
|
||||
<td>${matches}</td>
|
||||
<td>${killsPerMatch}</td>
|
||||
<td class="stats-annual-metric">${kills}</td>
|
||||
<td>${deaths}</td>
|
||||
<td>${teamkills}</td>
|
||||
<td>${kd}</td>
|
||||
<td>${killsPerMatch}</td>
|
||||
</tr>
|
||||
`;
|
||||
})
|
||||
@@ -396,12 +396,12 @@
|
||||
<tr>
|
||||
<th>Posici\u00f3n</th>
|
||||
<th>Jugador</th>
|
||||
<th>Kills</th>
|
||||
<th>Partidas</th>
|
||||
<th>Kills/partida</th>
|
||||
<th>Kills</th>
|
||||
<th>Muertes</th>
|
||||
<th>Teamkills</th>
|
||||
<th>K/D</th>
|
||||
<th>TK</th>
|
||||
<th>KD</th>
|
||||
<th>Kills/partida</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>${rowsMarkup}</tbody>
|
||||
@@ -574,8 +574,8 @@
|
||||
<article class="stats-summary-card">
|
||||
<p class="stats-summary-title">Ventana semanal</p>
|
||||
<p><strong>Kills:</strong> ${safeInt(weeklyData?.kills, 0)}</p>
|
||||
<p><strong>Deaths:</strong> ${safeInt(weeklyData?.deaths, 0)}</p>
|
||||
<p><strong>Teamkills:</strong> ${safeInt(weeklyData?.teamkills, 0)}</p>
|
||||
<p><strong>Muertes:</strong> ${safeInt(weeklyData?.deaths, 0)}</p>
|
||||
<p><strong>TK:</strong> ${safeInt(weeklyData?.teamkills, 0)}</p>
|
||||
</article>
|
||||
`;
|
||||
|
||||
@@ -590,8 +590,8 @@
|
||||
<article class="stats-summary-card">
|
||||
<p class="stats-summary-title">Ventana mensual</p>
|
||||
<p><strong>Kills:</strong> ${safeInt(monthlyData?.kills, 0)}</p>
|
||||
<p><strong>Deaths:</strong> ${safeInt(monthlyData?.deaths, 0)}</p>
|
||||
<p><strong>Teamkills:</strong> ${safeInt(monthlyData?.teamkills, 0)}</p>
|
||||
<p><strong>Muertes:</strong> ${safeInt(monthlyData?.deaths, 0)}</p>
|
||||
<p><strong>TK:</strong> ${safeInt(monthlyData?.teamkills, 0)}</p>
|
||||
</article>
|
||||
`;
|
||||
|
||||
@@ -697,16 +697,16 @@
|
||||
<span class="stats-comparison-card__metric-value">${kills}</span>
|
||||
</div>
|
||||
<div class="stats-comparison-card__metric">
|
||||
<span class="stats-comparison-card__metric-label">Deaths</span>
|
||||
<span class="stats-comparison-card__metric-label">Muertes</span>
|
||||
<span class="stats-comparison-card__metric-value">${deaths}</span>
|
||||
</div>
|
||||
<div class="stats-comparison-card__metric">
|
||||
<span class="stats-comparison-card__metric-label">K/D</span>
|
||||
<span class="stats-comparison-card__metric-label">KD</span>
|
||||
<span class="stats-comparison-card__metric-value">${kdRatio}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="stats-comparison-card__detail">
|
||||
<strong>Kills por partida:</strong> ${killsPerMatch}
|
||||
<strong>Kills/partida:</strong> ${killsPerMatch}
|
||||
</p>
|
||||
<p class="stats-comparison-card__note">${escapeHtml(rankingState.detail)}</p>
|
||||
</article>
|
||||
@@ -739,11 +739,11 @@
|
||||
<span class="stats-comparison-card__metric-value">${safeDecimal(monthlyKillsPerMatch, 2, "0.00")}</span>
|
||||
</div>
|
||||
<div class="stats-comparison-card__metric">
|
||||
<span class="stats-comparison-card__metric-label">K/D semanal</span>
|
||||
<span class="stats-comparison-card__metric-label">KD semanal</span>
|
||||
<span class="stats-comparison-card__metric-value">${safeDecimal(weeklyKd, 2, "0.00")}</span>
|
||||
</div>
|
||||
<div class="stats-comparison-card__metric">
|
||||
<span class="stats-comparison-card__metric-label">K/D mensual</span>
|
||||
<span class="stats-comparison-card__metric-label">KD mensual</span>
|
||||
<span class="stats-comparison-card__metric-value">${safeDecimal(monthlyKd, 2, "0.00")}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -775,7 +775,7 @@
|
||||
}
|
||||
|
||||
const rank = safeInt(ranking.ranking_position, 0);
|
||||
const metric = escapeHtml(String(ranking.metric || annualMetric));
|
||||
const metric = escapeHtml(labelForStatsMetric(ranking.metric || annualMetric));
|
||||
|
||||
return `
|
||||
<p><strong>Posici\u00f3n:</strong> ${rank}</p>
|
||||
@@ -863,6 +863,18 @@
|
||||
return `Ventana: ${windowStart} - ${windowEnd}`;
|
||||
}
|
||||
|
||||
function labelForStatsMetric(metric) {
|
||||
const labels = {
|
||||
kills: "Kills",
|
||||
deaths: "Muertes",
|
||||
teamkills: "TK",
|
||||
matches_considered: "Partidas",
|
||||
kd_ratio: "KD",
|
||||
kills_per_match: "Kills/partida",
|
||||
};
|
||||
return labels[String(metric || "").trim()] || "Kills";
|
||||
}
|
||||
|
||||
function safeInt(value, fallback = 0) {
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
|
||||
Reference in New Issue
Block a user