Polish public home stats ranking copy and player id visibility
This commit is contained in:
@@ -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.
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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('"', """)
|
.replaceAll('"', """)
|
||||||
.replaceAll("'", "'");
|
.replaceAll("'", "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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">Últimas partidas registradas</h2>
|
<h2 id="recent-matches-title">Ú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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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ñ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>
|
||||||
|
|||||||
Reference in New Issue
Block a user