Polish public home stats ranking copy and player id visibility

This commit is contained in:
devRaGonSa
2026-06-11 10:58:00 +02:00
parent d225e243ad
commit 9e52be98de
8 changed files with 144 additions and 51 deletions

View File

@@ -0,0 +1,83 @@
---
id: TASK-246
title: Polish public home stats ranking copy and player id visibility
status: done
type: frontend
team: Frontend Senior
supporting_teams:
- Experto en interfaz
roadmap_item: foundation
priority: medium
---
# TASK-246 - Polish public home stats ranking copy and player id visibility
## Goal
Refine public frontend copy in home, stats and ranking, and stop showing player IDs in the public stats and ranking tables.
## Context
The scheduler and backend public reads were already validated. This task only adjusts visible frontend UI and copy:
- make the home countdown capsule more noticeable without widening it
- remove explanatory helper copy that adds noise in public panels
- remove the initial empty search state box in stats
- keep `all-servers` as an internal slug while presenting `Todos los servidores`
- stop rendering player IDs in public table rows
## Steps
1. Reviewed the real HTML and JS used by home, stats, ranking and the visible recent matches section.
2. Removed noisy helper copy from the relevant public sections.
3. Hid the initial stats search state box while preserving validation and error states.
4. Normalized annual stats server labels for visible output.
5. Removed player ID rendering from the public annual stats table and the ranking table.
6. Polished the countdown capsule styling with a stronger but still compact warm accent.
## Files to Read First
- `AGENTS.md`
- `ai/architecture-index.md`
- `ai/repo-context.md`
- `frontend/index.html`
- `frontend/stats.html`
- `frontend/ranking.html`
- `frontend/assets/js/stats.js`
- `frontend/assets/js/ranking.js`
- `frontend/assets/css/styles.css`
## Expected Files to Modify
- `frontend/index.html`
- `frontend/stats.html`
- `frontend/historico.html`
- `frontend/assets/js/historico-recent-live.js`
- `frontend/assets/js/stats.js`
- `frontend/assets/js/ranking.js`
- `frontend/assets/css/styles.css`
## Constraints
- No backend changes.
- No docker-compose or scheduler changes.
- No asset or image changes.
- Keep search by name or ID working internally.
- Keep `all` and `all-servers` as internal values only.
## Validation
- `node --check frontend/assets/js/stats.js`
- `node --check frontend/assets/js/ranking.js`
- Search frontend for the removed visible copy strings.
- Search frontend for `Objetivo:` regression.
- Check that stats and ranking table rows no longer render player IDs.
## Outcome
- Home countdown label is visually stronger without stretching full width.
- The extra helper copy under public servers is gone.
- The redundant recent matches note was removed from the visible panel where it was actually rendered.
- Stats no longer shows the initial search status box before any search.
- Annual stats visible server output now uses `Todos los servidores`.
- Stats annual rows and ranking rows no longer render player IDs in the public tables.

View File

@@ -640,6 +640,14 @@ h2 {
.release-countdown__copy .eyebrow--section { .release-countdown__copy .eyebrow--section {
margin-bottom: 0; margin-bottom: 0;
border-color: rgba(210, 182, 118, 0.42);
background:
linear-gradient(180deg, rgba(82, 68, 33, 0.28), rgba(41, 49, 24, 0.62));
color: #e0c487;
font-weight: 800;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.03),
0 10px 22px rgba(0, 0, 0, 0.18);
} }
.release-countdown__units { .release-countdown__units {
@@ -1298,11 +1306,6 @@ h2 {
line-height: 1.35; line-height: 1.35;
} }
.stats-annual-player__id {
color: var(--muted);
font-size: 0.8rem;
}
.stats-annual-metric { .stats-annual-metric {
color: var(--accent-warm); color: var(--accent-warm);
font-weight: 700; font-weight: 700;
@@ -1613,12 +1616,7 @@ h2 {
.ranking-player { .ranking-player {
display: grid; display: grid;
gap: 4px; gap: 0;
}
.ranking-player span {
color: var(--muted);
font-size: 0.82rem;
} }
.ranking-empty { .ranking-empty {

View File

@@ -83,7 +83,7 @@
} }
stateNode.hidden = true; stateNode.hidden = true;
if (noteNode) noteNode.textContent = "Lista dinámica de partidas registradas."; if (noteNode) noteNode.textContent = "";
metaNode.textContent = buildDynamicRecentMeta(items); metaNode.textContent = buildDynamicRecentMeta(items);
renderDynamicRecentMatchesPage(); renderDynamicRecentMatchesPage();
} catch (error) { } catch (error) {
@@ -92,8 +92,8 @@
} }
recentMatchesState.items = []; recentMatchesState.items = [];
recentMatchesState.page = 1; recentMatchesState.page = 1;
setDynamicState(stateNode, "No se pudieron cargar las partidas recientes dinámicas.", true); setDynamicState(stateNode, "No se pudieron cargar las partidas recientes dinámicas.", true);
metaNode.textContent = "Error al leer las partidas recientes dinámicas."; metaNode.textContent = "Error al leer las partidas recientes dinámicas.";
renderDynamicPagination(); renderDynamicPagination();
} }
} }

View File

@@ -388,15 +388,14 @@
); );
const hideKppColumn = metric === "kills_per_match"; const hideKppColumn = metric === "kills_per_match";
return ` return `
<tr> <tr>
<td>#${safeInt(item.ranking_position, 0)}</td> <td>#${safeInt(item.ranking_position, 0)}</td>
<td> <td>
<div class="ranking-player"> <div class="ranking-player">
<strong>${escapeHtml(String(item.player_name || "Jugador sin nombre"))}</strong> <strong>${escapeHtml(String(item.player_name || "Jugador sin nombre"))}</strong>
<span>${escapeHtml(String(item.player_id || "Sin ID"))}</span> </div>
</div> </td>
</td>
<td class="ranking-table__metric">${formatMetricValue(item.metric_value, metric)}</td> <td class="ranking-table__metric">${formatMetricValue(item.metric_value, metric)}</td>
<td>${safeInt(item.kills, 0)}</td> <td>${safeInt(item.kills, 0)}</td>
<td>${safeInt(item.deaths, 0)}</td> <td>${safeInt(item.deaths, 0)}</td>

View File

@@ -111,7 +111,14 @@
if (!searchStateNode) { if (!searchStateNode) {
return; return;
} }
searchStateNode.textContent = message; const normalizedMessage = String(message || "").trim();
searchStateNode.hidden = !normalizedMessage;
if (!normalizedMessage) {
searchStateNode.textContent = "";
searchStateNode.className = "stats-state stats-state--neutral";
return;
}
searchStateNode.textContent = normalizedMessage;
searchStateNode.className = `stats-state stats-state--${state}`; searchStateNode.className = `stats-state stats-state--${state}`;
} }
@@ -296,6 +303,7 @@
const items = normalizeArray(data.items); const items = normalizeArray(data.items);
const limit = safeInt(data.limit, 0); const limit = safeInt(data.limit, 0);
const serverId = String(data.server_id || annualServerId); const serverId = String(data.server_id || annualServerId);
const serverLabel = labelForStatsServer(serverId);
const sourceMatches = safeInt(data.source_matches_count, 0); const sourceMatches = safeInt(data.source_matches_count, 0);
const generatedAt = formatDateTime(data.generated_at); const generatedAt = formatDateTime(data.generated_at);
const year = safeInt(data.year, annualDefaultYear); const year = safeInt(data.year, annualDefaultYear);
@@ -313,7 +321,7 @@
setAnnualState( setAnnualState(
"neutral", "neutral",
`${messages.annualReadyPrefix} ${serverId}, a\u00f1o ${year}.`, `${messages.annualReadyPrefix} ${serverLabel}, a\u00f1o ${year}.`,
); );
annualContentNode.innerHTML = ` annualContentNode.innerHTML = `
@@ -322,7 +330,7 @@
<div class="stats-annual-meta"> <div class="stats-annual-meta">
<article class="stats-annual-meta-item"> <article class="stats-annual-meta-item">
<p>Servidor</p> <p>Servidor</p>
<strong>${escapeHtml(serverId)}</strong> <strong>${escapeHtml(serverLabel)}</strong>
</article> </article>
<article class="stats-annual-meta-item"> <article class="stats-annual-meta-item">
<p>A\u00f1o</p> <p>A\u00f1o</p>
@@ -350,7 +358,6 @@
const rowsMarkup = items const rowsMarkup = items
.map((item) => { .map((item) => {
const rank = safeInt(item.ranking_position, 0); const rank = safeInt(item.ranking_position, 0);
const playerId = escapeHtml(String(item.player_id || ""));
const playerName = escapeHtml(String(item.player_name || "Jugador sin nombre")); const playerName = escapeHtml(String(item.player_name || "Jugador sin nombre"));
const kills = safeInt(firstFiniteValue(item.kills, item.metric_value), 0); const kills = safeInt(firstFiniteValue(item.kills, item.metric_value), 0);
const matches = safeInt(item.matches_considered, 0); const matches = safeInt(item.matches_considered, 0);
@@ -365,7 +372,6 @@
<td> <td>
<div class="stats-annual-player"> <div class="stats-annual-player">
<strong>${playerName}</strong> <strong>${playerName}</strong>
<span class="stats-annual-player__id">ID ${playerId}</span>
</div> </div>
</td> </td>
<td class="stats-annual-metric">${kills}</td> <td class="stats-annual-metric">${kills}</td>
@@ -948,10 +954,7 @@
} }
if (!query) { if (!query) {
setSearchState( setSearchState("neutral", "");
"neutral",
"Estado inicial: introduce al menos 4 caracteres para buscar un jugador.",
);
return; return;
} }
@@ -968,5 +971,19 @@
.replaceAll('"', "&quot;") .replaceAll('"', "&quot;")
.replaceAll("'", "&#39;"); .replaceAll("'", "&#39;");
} }
function labelForStatsServer(serverId) {
const normalized = String(serverId || "").trim().toLowerCase();
if (normalized === "comunidad-hispana-01") {
return "Comunidad Hispana #01";
}
if (normalized === "comunidad-hispana-02") {
return "Comunidad Hispana #02";
}
if (normalized === "all" || normalized === "all-servers") {
return "Todos los servidores";
}
return String(serverId || "Todos los servidores");
}
}); });

View File

@@ -210,14 +210,12 @@
<section class="panel historical-panel" aria-labelledby="recent-matches-title"> <section class="panel historical-panel" aria-labelledby="recent-matches-title">
<div class="panel__shell"> <div class="panel__shell">
<div class="panel__header historical-panel__header"> <div class="panel__header historical-panel__header">
<div> <div>
<p class="eyebrow eyebrow--section">Partidas recientes</p> <p class="eyebrow eyebrow--section">Partidas recientes</p>
<h2 id="recent-matches-title">&Uacute;ltimas partidas registradas</h2> <h2 id="recent-matches-title">&Uacute;ltimas partidas registradas</h2>
<p class="historical-panel__note" id="recent-matches-note"> <p class="historical-panel__note" id="recent-matches-note"></p>
Lista de partidas ya registradas. <p class="historical-snapshot-meta" id="recent-matches-snapshot-meta">
</p>
<p class="historical-snapshot-meta" id="recent-matches-snapshot-meta">
Cargando datos de partidas... Cargando datos de partidas...
</p> </p>
</div> </div>

View File

@@ -93,9 +93,9 @@
aria-labelledby="release-countdown-title" aria-labelledby="release-countdown-title"
> >
<div class="release-countdown__copy"> <div class="release-countdown__copy">
<h3 id="release-countdown-title"> <p class="eyebrow eyebrow--section" id="release-countdown-title">
Fecha de lanzamiento de Hell Let Loose Vietnam (13 de agosto) Fecha de lanzamiento de Hell Let Loose Vietnam (13 de agosto)
</h3> </p>
</div> </div>
<div class="release-countdown__units" aria-live="off"> <div class="release-countdown__units" aria-live="off">
<article> <article>
@@ -130,9 +130,6 @@
Actualizado no disponible Actualizado no disponible
</p> </p>
</div> </div>
<p class="panel__intro panel__intro--tight">
El panel muestra el último estado disponible consultado de los servidores.
</p>
<div class="panel__actions panel__actions--compact"> <div class="panel__actions panel__actions--compact">
<a class="secondary-button secondary-button--compact" href="./historico.html"> <a class="secondary-button secondary-button--compact" href="./historico.html">
Abrir zona histórico Abrir zona histórico

View File

@@ -90,9 +90,13 @@
</button> </button>
</div> </div>
</form> </form>
<p class="stats-state stats-state--neutral" id="stats-search-state" role="status" aria-live="polite"> <p
Estado inicial: usa el buscador para encontrar jugadores. class="stats-state stats-state--neutral"
</p> id="stats-search-state"
role="status"
aria-live="polite"
hidden
></p>
<div class="stats-result-list" id="stats-result-list" aria-live="polite"></div> <div class="stats-result-list" id="stats-result-list" aria-live="polite"></div>
</div> </div>
</section> </section>
@@ -131,9 +135,6 @@
<p class="eyebrow eyebrow--section">Ranking anual</p> <p class="eyebrow eyebrow--section">Ranking anual</p>
<h2>Top 20 temporada 2026</h2> <h2>Top 20 temporada 2026</h2>
</div> </div>
<p class="panel__intro panel__intro--tight">
El ranking anual usa un corte ya preparado de la temporada para cargar el Top 20 sin recalcular toda la campa&ntilde;a en cada consulta.
</p>
<div class="stats-annual-season" aria-label="Temporada fija del ranking anual"> <div class="stats-annual-season" aria-label="Temporada fija del ranking anual">
<span class="stats-annual-season__label">Temporada</span> <span class="stats-annual-season__label">Temporada</span>
<strong class="stats-annual-season__value">2026</strong> <strong class="stats-annual-season__value">2026</strong>