diff --git a/ai/tasks/done/TASK-206-public-frontend-visual-consistency-and-annual-ranking-layout.md b/ai/tasks/done/TASK-206-public-frontend-visual-consistency-and-annual-ranking-layout.md
new file mode 100644
index 0000000..6ddefc6
--- /dev/null
+++ b/ai/tasks/done/TASK-206-public-frontend-visual-consistency-and-annual-ranking-layout.md
@@ -0,0 +1,158 @@
+---
+id: TASK-206
+title: Public frontend visual consistency and annual ranking layout
+status: done
+type: frontend
+team: Frontend Senior
+supporting_teams:
+ - Experto en interfaz
+roadmap_item: foundation
+priority: medium
+---
+
+# TASK-206 - Public frontend visual consistency and annual ranking layout
+
+## Goal
+
+Corregir la segunda pasada visual del frontend publico para unificar definitivamente las cabeceras de `index`, `historico`, `stats` y `ranking`, mejorar la legibilidad del ranking anual y ajustar la presencia visual del logo `BxB` en la seccion de clanes.
+
+## Context
+
+Tras `TASK-203` y la task local no commiteada `TASK-204`, el frontend publico sigue mostrando diferencias visibles entre heroes, etiquetas redundantes dentro de cabecera y un bloque anual demasiado compacto en `stats`. Esta tarea se limita a `frontend/` y a su documentacion de task: no toca backend, endpoints, logica de datos, scripts backend, snapshots, Elo/MMR pausado, Comunidad Hispana #03 ni assets/SVGs de armas.
+
+Preserve the current product identity: Spanish-speaking HLL Vietnam community, military/Vietnam/tactical/sober visual direction and controlled repository evolution.
+
+## Steps
+
+1. Confirmar el estado actual del frontend publico y de la task previa local `TASK-204`.
+2. Unificar el patron visual del hero tomando `index` como referencia principal y `historico` como referencia secundaria.
+3. Eliminar chips o labels de pagina redundantes dentro del hero manteniendo la navegacion superior comun.
+4. Rehacer solo la presentacion frontend del Top 20 anual en `stats` para separar claramente posicion, jugador y metricas.
+5. Ajustar la presentacion del logo `BxB` en la seccion de clanes sin reemplazar el asset.
+6. Validar por inspeccion y documentar outcome, alcance y exclusiones.
+
+## Files to Read First
+
+- `AGENTS.md`
+- `ai/repo-context.md`
+- `ai/architecture-index.md`
+- `frontend/index.html`
+- `frontend/historico.html`
+- `frontend/stats.html`
+- `frontend/ranking.html`
+- `frontend/assets/css/styles.css`
+- `frontend/assets/js/stats.js`
+- `frontend/assets/js/ranking.js`
+- `ai/tasks/done/TASK-203-public-pages-navigation-and-copy-polish.md`
+
+## Expected Files to Modify
+
+- `ai/tasks/in-progress/TASK-206-public-frontend-visual-consistency-and-annual-ranking-layout.md`
+- `frontend/index.html`
+- `frontend/historico.html`
+- `frontend/stats.html`
+- `frontend/ranking.html`
+- `frontend/assets/css/styles.css`
+- `frontend/assets/css/hero-header-compact.css`
+- `frontend/assets/css/historico.css`
+- `frontend/assets/js/stats.js`
+- `frontend/assets/js/main.js`
+
+## Constraints
+
+- Keep the change minimal.
+- Preserve HLL Vietnam project identity.
+- Do not introduce unnecessary frameworks or dependencies.
+- Do not implement backend functionality unless the task explicitly requires it.
+- Do not expand Elo/MMR, historical workers or RCON server #03 handling unless the task explicitly requires it.
+- Do not overwrite repository-specific context with generic platform template text.
+- No ejecutar `ai-platform run`.
+- No modificar backend, endpoints, logica de datos, generacion de snapshots ni scripts backend.
+- No tocar `ai/system-metrics.md`.
+- No tocar `frontend/assets/img/weapons/`, assets/SVGs de armas ni copias relacionadas.
+- No reemplazar ni editar fisicamente imagenes de clanes.
+- No reactivar Elo/MMR ni reintroducir Comunidad Hispana #03.
+
+## Validation
+
+Before completing the task ensure:
+
+- `index.html`, `historico.html`, `stats.html` y `ranking.html` mantienen navegacion superior comun con `Inicio`, `Historico`, `Estadisticas` y `Ranking`
+- los cuatro hero muestran composicion coherente con logo a la izquierda y bloque textual a la derecha
+- desaparecen del hero `Historico propio`, `Seccion Stats`, `Seccion Ranking` y cualquier chip equivalente de pagina
+- el logo de comunidad no cambia de tamano de forma notable entre `index`, `historico`, `stats` y `ranking`
+- el copy visible mantiene `Busca un jugador por nombre o ID y revisa sus resultados semanales, mensuales y ranking anual.`
+- el copy visible mantiene `Consulta los lideres publicos de la comunidad, cambia de periodo y servidor sin salir de la pagina y revisa quien destaca en cada ventana.`
+- el Top 20 anual se muestra en columnas o grid legible y el `player_id` no queda pegado al nombre como dato principal
+- el logo `BxB` gana presencia visual sin romper responsive
+- `git diff --name-only` matches the expected scope
+- no unrelated files were modified
+- integration tests are run when relevant and configured
+
+## Outcome
+
+Archivos modificados:
+
+- `frontend/index.html`
+- `frontend/historico.html`
+- `frontend/stats.html`
+- `frontend/ranking.html`
+- `frontend/assets/css/hero-header-compact.css`
+- `frontend/assets/css/historico.css`
+- `frontend/assets/css/styles.css`
+- `frontend/assets/js/main.js`
+- `frontend/assets/js/stats.js`
+- `ai/tasks/in-progress/TASK-206-public-frontend-visual-consistency-and-annual-ranking-layout.md`
+
+Hero unificado:
+
+- Se marco `index`, `historico`, `stats` y `ranking` como `hero--public-page` para dejar explicita la variante publica comun.
+- `hero-header-compact.css` quedo restringido a ese patron comun y `historico.css` se alineo con la misma escala de padding, gap, logo y copy.
+- `historico` dejo de usar una separacion mas amplia y un logo base mas pequeno que el resto; ahora mantiene una proporcion equivalente a `index`, `stats` y `ranking`.
+- `stats` y `ranking` conservan logo a la izquierda y bloque textual a la derecha con la misma composicion visual base del hero publico.
+
+Chips eliminados del hero:
+
+- `Historico propio`
+- `Seccion Stats`
+- `Seccion Ranking`
+
+Ranking anual:
+
+- El Top 20 anual de `stats` paso de lista compacta apilada a una tabla legible con columnas `Posicion`, `Jugador`, `Valor / Kills`, `Partidas`, `Muertes`, `Teamkills` y `K/D`.
+- El `player_id` se mantiene como dato secundario visual en una segunda linea bajo el nombre, en vez de aparecer pegado al jugador.
+- El bloque superior del ranking anual se suavizo con copy menos tecnico y metadatos presentados en tarjetas (`Servidor`, `Año`, `Lectura`, `Partidas base`, `Actualizado`).
+- No se tocaron endpoint ni contrato API; solo se cambio la presentacion generada por `stats.js` y su CSS asociado.
+
+Clanes / BxB:
+
+- `BxB` recibio una variante visual especifica de tarjeta y logo (`clan-card--bxb`, `clan-card__logo--bxb`) para ganar presencia sin reemplazar la imagen ni alterar el asset original.
+- El ajuste se hizo en layout y CSS, manteniendo la coherencia general de la grilla de clanes y sin romper responsive.
+
+Textos y navegacion:
+
+- La navegacion superior comun queda en `Inicio`, `Historico`, `Estadisticas` y `Ranking`.
+- Se mantuvo el copy principal pedido en `stats` y `ranking`.
+- Se corrigieron textos visibles de apoyo en `main.js` como `Historico`, `Region`, `Informacion`, `Ultimo snapshot` y `Proximamente`.
+
+Validaciones ejecutadas:
+
+- `git status --short --untracked-files=all` ejecutado antes de modificar.
+- Confirmacion de que `TASK-204-align-public-page-heroes-and-navigation-labels.md` existe en disco pero sigue `untracked`; no estaba commiteada.
+- Confirmacion previa de cambios no relacionados en `ai/system-metrics.md` y en `frontend/assets/img/weapons/` / SVGs de armas; no se tocaron.
+- Lectura previa de `AGENTS.md`, `ai/repo-context.md`, `ai/architecture-index.md`, task previa relevante, HTML/CSS/JS del frontend publico y seccion de clanes.
+- Revision de `git diff --name-only` para comprobar alcance restringido a frontend publico y task.
+- Inspeccion visual con capturas headless de Edge sobre `frontend/index.html`, `frontend/historico.html`, `frontend/stats.html` y `frontend/ranking.html` para verificar navegacion y coherencia del hero.
+- Validacion estructural de la nueva tabla anual por inspeccion del HTML/JS/CSS renderizado. La vista con datos reales no pudo comprobarse visualmente porque el backend no estaba disponible en la validacion local.
+
+Confirmaciones de alcance:
+
+- No se tocaron `backend/`, endpoints, logica de datos, snapshots ni scripts backend.
+- No se tocaron `ai/system-metrics.md`, `frontend/assets/img/weapons/` ni los SVGs de armas con cambios previos.
+- No se reactivo Elo/MMR ni se reintrodujo Comunidad Hispana #03.
+
+## Change Budget
+
+- Prefer fewer than 5 modified files.
+- Prefer changes under 200 lines when feasible.
+- Split the work into follow-up tasks if limits are exceeded.
diff --git a/frontend/assets/css/hero-header-compact.css b/frontend/assets/css/hero-header-compact.css
index a4febdd..6fd1abc 100644
--- a/frontend/assets/css/hero-header-compact.css
+++ b/frontend/assets/css/hero-header-compact.css
@@ -1,10 +1,10 @@
@media (min-width: 1121px) {
- .hero:not(.historical-hero) .hero__content {
+ .hero--public-page:not(.historical-hero) .hero__content {
padding-block: 54px 60px;
padding-inline: clamp(64px, 5.5vw, 104px);
}
- .hero:not(.historical-hero) .hero__brand {
+ .hero--public-page:not(.historical-hero) .hero__brand {
width: 100%;
max-width: 1420px;
margin: 0 auto;
@@ -14,7 +14,7 @@
align-items: center;
}
- .hero:not(.historical-hero) .logo-frame {
+ .hero--public-page:not(.historical-hero) .logo-frame {
grid-column: 1;
justify-self: start;
width: min(470px, 100%);
@@ -22,11 +22,11 @@
padding: 30px 34px;
}
- .hero:not(.historical-hero) .logo-frame__image {
+ .hero--public-page:not(.historical-hero) .logo-frame__image {
max-height: 340px;
}
- .hero:not(.historical-hero) .hero__copy {
+ .hero--public-page:not(.historical-hero) .hero__copy {
grid-column: 3;
justify-self: end;
width: 100%;
@@ -37,41 +37,41 @@
transform: none;
}
- .hero:not(.historical-hero) .hero__title {
+ .hero--public-page:not(.historical-hero) .hero__title {
max-width: none;
font-size: clamp(4.2rem, 5.2vw, 6.35rem);
line-height: 1.02;
}
- .hero:not(.historical-hero) .hero__title-accent {
+ .hero--public-page:not(.historical-hero) .hero__title-accent {
display: inline;
}
- .hero:not(.historical-hero) .hero__text {
+ .hero--public-page:not(.historical-hero) .hero__text {
max-width: 68ch;
}
}
@media (min-width: 1121px) and (max-width: 1380px) {
- .hero:not(.historical-hero) .hero__content {
+ .hero--public-page:not(.historical-hero) .hero__content {
padding-inline: clamp(44px, 4vw, 64px);
}
- .hero:not(.historical-hero) .hero__brand {
+ .hero--public-page:not(.historical-hero) .hero__brand {
grid-template-columns: minmax(320px, 390px) minmax(28px, 1fr) minmax(560px, 720px);
column-gap: clamp(20px, 2.5vw, 40px);
}
- .hero:not(.historical-hero) .logo-frame {
+ .hero--public-page:not(.historical-hero) .logo-frame {
width: min(390px, 100%);
min-height: 316px;
}
- .hero:not(.historical-hero) .logo-frame__image {
+ .hero--public-page:not(.historical-hero) .logo-frame__image {
max-height: 298px;
}
- .hero:not(.historical-hero) .hero__copy {
+ .hero--public-page:not(.historical-hero) .hero__copy {
grid-column: 3;
justify-self: end;
width: 100%;
@@ -79,14 +79,14 @@
transform: none;
}
- .hero:not(.historical-hero) .hero__title {
+ .hero--public-page:not(.historical-hero) .hero__title {
font-size: clamp(3.7rem, 5.35vw, 5.15rem);
}
}
@media (max-width: 1120px) {
- .hero:not(.historical-hero) .hero__copy {
+ .hero--public-page:not(.historical-hero) .hero__copy {
grid-column: auto;
transform: none;
}
-}
\ No newline at end of file
+}
diff --git a/frontend/assets/css/historico.css b/frontend/assets/css/historico.css
index 680ea74..8d960fe 100644
--- a/frontend/assets/css/historico.css
+++ b/frontend/assets/css/historico.css
@@ -7,8 +7,8 @@
}
.historical-hero__content {
- padding-top: 44px;
- padding-bottom: 46px;
+ padding-top: 58px;
+ padding-bottom: 64px;
}
.historical-hero__topline {
@@ -21,7 +21,7 @@
.historical-hero__copy {
display: grid;
- gap: 24px;
+ gap: 18px;
min-width: 0;
}
@@ -34,7 +34,7 @@
.historical-hero__layout--registry {
grid-template-columns: minmax(220px, 330px) minmax(0, 1fr);
- column-gap: clamp(72px, 7vw, 140px);
+ column-gap: 36px;
}
.historical-hero__layout > * {
@@ -42,8 +42,8 @@
}
.historical-logo-frame {
- width: min(300px, 100%);
- min-height: 220px;
+ width: min(340px, 100%);
+ min-height: 248px;
}
.historical-hero__title {
@@ -65,6 +65,11 @@
/* Registry/historico hero: logo left, real flexible spacer, copy anchored right. */
@media (min-width: 1121px) {
+ .hero--public-page.historical-hero .historical-hero__content {
+ padding-block: 54px 60px;
+ padding-inline: clamp(64px, 5.5vw, 104px);
+ }
+
.historical-hero__layout--registry {
width: 100%;
max-width: 1420px;
@@ -113,6 +118,10 @@
}
@media (min-width: 1121px) and (max-width: 1380px) {
+ .hero--public-page.historical-hero .historical-hero__content {
+ padding-inline: clamp(44px, 4vw, 64px);
+ }
+
.historical-hero__layout--registry {
grid-template-columns: minmax(320px, 390px) minmax(28px, 1fr) minmax(560px, 720px);
column-gap: clamp(20px, 2.5vw, 40px);
diff --git a/frontend/assets/css/styles.css b/frontend/assets/css/styles.css
index 7ca37da..bc127f4 100644
--- a/frontend/assets/css/styles.css
+++ b/frontend/assets/css/styles.css
@@ -112,7 +112,9 @@ a {
color: var(--text-soft);
font-size: 0.82rem;
font-weight: 800;
- letter-spacing: 0.12em;
+ letter-spacing: 0.1em;
+ line-height: 1.2;
+ text-align: center;
text-transform: uppercase;
transition:
transform 160ms ease,
@@ -904,6 +906,15 @@ h2 {
padding-inline: 18px;
}
+.clan-card--bxb .clan-card__brand {
+ grid-template-columns: minmax(112px, 138px) minmax(0, 1fr);
+}
+
+.clan-card__logo--bxb {
+ min-height: 108px;
+ padding: 10px;
+}
+
.clan-card__logo img {
width: auto;
height: auto;
@@ -912,6 +923,10 @@ h2 {
object-fit: contain;
}
+.clan-card__logo--bxb img {
+ max-height: 100px;
+}
+
.clan-card__logo-placeholder {
width: 100%;
min-height: 84px;
@@ -972,11 +987,6 @@ h2 {
margin-top: 8px;
}
-.stats-hero .hero__brand,
-.ranking-hero .hero__brand {
- align-items: center;
-}
-
.stats-search-form {
margin: 0 0 14px;
display: grid;
@@ -1098,26 +1108,104 @@ h2 {
.stats-annual-table-wrap {
display: grid;
- gap: 10px;
+ gap: 16px;
}
.stats-annual-meta {
display: grid;
- gap: 4px;
- margin: 0 0 8px;
+ gap: 12px;
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
+ margin: 0;
}
-.stats-annual-item {
- padding: 10px 12px;
- border: 1px solid rgba(159, 168, 141, 0.22);
- border-radius: 14px;
- margin-bottom: 8px;
+.stats-annual-card {
+ border: 1px solid rgba(159, 168, 141, 0.2);
+ border-radius: 18px;
+ padding: 18px;
background:
- linear-gradient(180deg, rgba(28, 34, 25, 0.82), rgba(12, 15, 11, 0.92));
+ linear-gradient(180deg, rgba(28, 34, 25, 0.88), rgba(12, 15, 11, 0.96));
}
-.stats-annual-item p {
- margin: 0 0 6px;
+.stats-annual-meta-item {
+ margin: 0;
+ padding: 14px 16px;
+ border: 1px solid rgba(159, 168, 141, 0.18);
+ border-radius: 16px;
+ background: rgba(15, 18, 13, 0.62);
+}
+
+.stats-annual-meta-item p,
+.stats-annual-meta-item strong {
+ margin: 0;
+}
+
+.stats-annual-meta-item p {
+ margin-bottom: 8px;
+ color: var(--muted);
+ font-size: 0.74rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.stats-annual-table-shell {
+ overflow-x: auto;
+ border: 1px solid rgba(159, 168, 141, 0.18);
+ border-radius: 18px;
+ background: linear-gradient(180deg, rgba(19, 24, 16, 0.88), rgba(10, 13, 9, 0.96));
+}
+
+.stats-annual-table {
+ width: 100%;
+ min-width: 860px;
+ border-collapse: collapse;
+}
+
+.stats-annual-table th,
+.stats-annual-table td {
+ padding: 14px 16px;
+ text-align: left;
+ border-bottom: 1px solid rgba(159, 168, 141, 0.12);
+}
+
+.stats-annual-table thead th {
+ color: var(--accent-strong);
+ font-size: 0.76rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ background: rgba(14, 18, 12, 0.96);
+}
+
+.stats-annual-table tbody tr:hover {
+ background: rgba(183, 201, 125, 0.05);
+}
+
+.stats-annual-table tbody tr:last-child td {
+ border-bottom: 0;
+}
+
+.stats-annual-rank {
+ color: var(--accent-warm);
+ font-weight: 700;
+}
+
+.stats-annual-player {
+ display: grid;
+ gap: 4px;
+}
+
+.stats-annual-player strong {
+ font-size: 1rem;
+ line-height: 1.35;
+}
+
+.stats-annual-player__id {
+ color: var(--muted);
+ font-size: 0.8rem;
+}
+
+.stats-annual-metric {
+ color: var(--accent-warm);
+ font-weight: 700;
}
.stats-annual-sub {
@@ -1499,8 +1587,8 @@ h2 {
.public-nav__link {
min-height: 46px;
padding-inline: 12px;
- font-size: 0.74rem;
- letter-spacing: 0.08em;
+ font-size: 0.72rem;
+ letter-spacing: 0.06em;
}
.stats-search-form__fields {
@@ -1525,6 +1613,11 @@ h2 {
padding: 12px 10px;
}
+ .stats-annual-table th,
+ .stats-annual-table td {
+ padding: 12px 10px;
+ }
+
.logo-frame {
min-height: 180px;
padding: 16px 18px;
diff --git a/frontend/assets/js/main.js b/frontend/assets/js/main.js
index 48598c2..05a67c0 100644
--- a/frontend/assets/js/main.js
+++ b/frontend/assets/js/main.js
@@ -33,7 +33,7 @@ const COMMUNITY_CLANS = Object.freeze([
logoAlt: "Logo de La 129",
logoClassName: "clan-card__logo--wide",
discordUrl: "",
- discordLabel: "Proximamente",
+ discordLabel: "Pr\u00f3ximamente",
},
{
name: "250 Hispania",
@@ -65,9 +65,10 @@ const COMMUNITY_CLANS = Object.freeze([
"Clan activo de la comunidad.",
logoSrc: "./assets/img/clans/bxb.png",
logoAlt: "Logo de BxB",
- logoClassName: "",
+ logoClassName: "clan-card__logo--bxb",
+ cardClassName: "clan-card--bxb",
discordUrl: "",
- discordLabel: "Proximamente",
+ discordLabel: "Pr\u00f3ximamente",
},
{
name: "7dv",
@@ -209,7 +210,7 @@ async function hydrateServers(
if (serversData.items.length === 0) {
serversList.innerHTML =
- '
Informacion de servidores disponible mas adelante.
';
+ 'Informaci\u00f3n de servidores disponible m\u00e1s adelante.
';
return;
}
@@ -283,7 +284,7 @@ function renderServerStatsCard(server) {
{ label: "Mapa", value: currentMap, valueClassName: "server-card__quickfact-value--map" },
];
if (region) {
- quickFactItems.push({ label: "Region", value: region });
+ quickFactItems.push({ label: "Regi\u00f3n", value: region });
}
const quickFacts = renderQuickFacts(quickFactItems);
@@ -345,7 +346,7 @@ function renderServerAction(server) {
return `
- Historico
+ Hist\u00f3rico
Partida actual
@@ -367,9 +368,10 @@ function hydrateCommunityClans(listNode) {
function renderCommunityClanCard(clan) {
const logoMarkup = renderClanLogo(clan);
const discordMarkup = renderClanDiscordLink(clan);
+ const cardClassName = clan.cardClassName ? ` ${escapeHtml(clan.cardClassName)}` : "";
return `
-
+
${logoMarkup}
@@ -541,7 +543,7 @@ function deriveSnapshotState(serversData) {
return {
label: isFresh
? `Actualizado ${timestampLabel}`
- : `Ultimo snapshot ${timestampLabel}`,
+ : `\u00daltimo snapshot ${timestampLabel}`,
isFresh,
};
}
diff --git a/frontend/assets/js/ranking.js b/frontend/assets/js/ranking.js
index 0d3ebd2..51c6833 100644
--- a/frontend/assets/js/ranking.js
+++ b/frontend/assets/js/ranking.js
@@ -7,7 +7,6 @@
const limitSelect = document.getElementById("ranking-limit");
const yearWrap = document.getElementById("ranking-year-wrap");
const yearInput = document.getElementById("ranking-year");
- const backendStateNode = document.getElementById("ranking-backend-state");
const stateNode = document.getElementById("ranking-state");
const titleNode = document.getElementById("ranking-title");
const metaNode = document.getElementById("ranking-meta");
@@ -48,7 +47,6 @@
applyInitialUrlState();
toggleYearField();
syncMetricState();
- setBackendState("Comprobando disponibilidad del backend", false);
setRankingState("neutral", "Esperando filtros para cargar el ranking global.");
clearRankingSurface();
refreshBackendHealth();
@@ -90,16 +88,6 @@
});
}
- function setBackendState(label, online) {
- isBackendOnline = online;
- if (!backendStateNode) {
- return;
- }
- backendStateNode.textContent = label;
- backendStateNode.classList.toggle("status-chip--ok", online);
- backendStateNode.classList.toggle("status-chip--fallback", !online);
- }
-
function setRankingState(state, message) {
if (!stateNode) {
return;
@@ -192,7 +180,7 @@
}
setFilterNote(
- "Ranking compara top globales. Para buscar un jugador concreto usa Stats.",
+ "Ranking compara top globales. Para buscar un jugador concreto usa Estadísticas.",
"neutral",
);
}
@@ -245,7 +233,7 @@
if (!payload || payload.status !== "ok") {
throw new Error("Unexpected health payload");
}
- setBackendState("Backend operativo", true);
+ isBackendOnline = true;
if (!String(stateNode?.textContent || "").includes("limite del URL")) {
setRankingState(
"neutral",
@@ -255,7 +243,7 @@
void loadRanking();
} catch (error) {
console.warn("Ranking health check failed", error);
- setBackendState("Backend no disponible", false);
+ isBackendOnline = false;
setRankingState("error", "Backend no disponible. El ranking queda en estado offline.");
renderEmptyState(
"No fue posible contactar el backend. Cuando vuelva a estar disponible podras consultar semanal, mensual o anual.",
@@ -319,7 +307,7 @@
renderRanking(payload.data || {});
} catch (error) {
console.warn("Ranking request failed", error);
- setBackendState("Backend no disponible", false);
+ isBackendOnline = false;
setRankingState("error", "Error controlado al cargar el ranking.");
renderEmptyState(
"La lectura del ranking fall\u00f3 en este intento. Revisa el backend o actualiza la p\u00e1gina.",
diff --git a/frontend/assets/js/stats.js b/frontend/assets/js/stats.js
index 4fdb3ce..f840aae 100644
--- a/frontend/assets/js/stats.js
+++ b/frontend/assets/js/stats.js
@@ -5,7 +5,6 @@
const searchStateNode = document.getElementById("stats-search-state");
const searchHelpNode = document.getElementById("stats-search-help");
const resultListNode = document.getElementById("stats-result-list");
- const backendStateNode = document.getElementById("stats-backend-state");
const profilePanel = document.getElementById("stats-profile-panel");
const profileTitle = document.getElementById("stats-profile-title");
const profileStateNode = document.getElementById("stats-profile-state");
@@ -25,9 +24,6 @@
const annualServerId = "all";
const messages = {
- backendChecking: "Comprobando disponibilidad del backend",
- backendOnline: "Backend operativo",
- backendUnavailable: "Backend no disponible. Reintenta en unos segundos.",
backendUnavailableForSearch:
"Backend no disponible. No se pueden buscar jugadores ni cargar su perfil.",
backendUnavailableForAnnual:
@@ -48,14 +44,14 @@
profileError: "No fue posible cargar las estad\u00edsticas del jugador.",
annualLoading: "Cargando ranking anual...",
annualMissing:
- "No hay snapshot generado para el a\u00f1o solicitado. Mostrar\u00e1 estado pending hasta que exista.",
+ "Todavia no hay ranking anual publicado para el a\u00f1o solicitado.",
annualReadyEmpty:
- "Ranking anual listo, pero sin datos para el a\u00f1o y servidor seleccionados.",
+ "El ranking anual ya esta disponible, pero no muestra filas para ese filtro.",
annualUnsupportedMetric:
"La m\u00e9trica anual solicitada no est\u00e1 soportada en V1.",
annualMetricInvalid:
"Par\u00e1metro de ranking anual inv\u00e1lido. Usa metric=kills en V1.",
- annualReadyPrefix: "Ranking anual listo para",
+ annualReadyPrefix: "Ranking anual disponible para",
weeklyPlaceholder:
"Sin datos semanales. El ranking semanal se actualiza al cargar un jugador.",
monthlyPlaceholder:
@@ -82,7 +78,6 @@
"El ranking anual se consulta por separado con el a\u00f1o indicado.";
}
- setBackendState(messages.backendChecking, false);
if (annualYearInput) {
annualYearInput.value = String(annualDefaultYear);
}
@@ -108,18 +103,8 @@
});
}
- function setBackendState(label, isOnline) {
- isBackendOnline = isOnline;
- if (!backendStateNode) {
- return;
- }
- backendStateNode.textContent = label;
- backendStateNode.classList.toggle("status-chip--ok", isOnline);
- backendStateNode.classList.toggle("status-chip--fallback", !isOnline);
- }
-
function markAsBackendUnavailable() {
- setBackendState(messages.backendUnavailable, false);
+ isBackendOnline = false;
if (searchStateNode) {
searchStateNode.textContent = messages.backendUnavailableForSearch;
searchStateNode.className = "stats-state stats-state--error";
@@ -173,10 +158,10 @@
throw new Error("Unexpected health payload");
}
- setBackendState(messages.backendOnline, true);
+ isBackendOnline = true;
setAnnualState(
"neutral",
- "Backend disponible. Selecciona un a\u00f1o para cargar el ranking anual (kills).",
+ "Backend disponible. Selecciona un a\u00f1o para cargar el ranking anual.",
);
void loadAnnualRanking();
} catch (error) {
@@ -345,17 +330,33 @@
setAnnualState(
"neutral",
- `${messages.annualReadyPrefix} ${serverId}, a\u00f1o ${year}, m\u00e9trica ${metric}`,
+ `${messages.annualReadyPrefix} ${serverId}, a\u00f1o ${year}.`,
);
annualContentNode.innerHTML = `
-
+
Top ${limit} anual
${renderAnnualRows(items)}
@@ -363,27 +364,54 @@
}
function renderAnnualRows(items) {
- return items
+ const rowsMarkup = items
.map((item) => {
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 metricValue = safeInt(item.metric_value, 0);
const matches = safeInt(item.matches_considered, 0);
- const kills = safeInt(item.kills, 0);
const deaths = safeInt(item.deaths, 0);
const teamkills = safeInt(item.teamkills, 0);
const kd = safeDecimal(item.kd_ratio, 2, "0.00");
return `
-
- #${rank} ${playerName} (ID: ${playerId})
- Valor: ${metricValue} - Partidas: ${matches}
- Kills: ${kills} - Deaths: ${deaths} - Teamkills: ${teamkills} - K/D: ${kd}
-
+
+ #${rank}
+
+
+ ${playerName}
+ ID ${playerId}
+
+
+ ${metricValue}
+ ${matches}
+ ${deaths}
+ ${teamkills}
+ ${kd}
+
`;
})
.join("");
+
+ return `
+
+
+
+
+ Posici\u00f3n
+ Jugador
+ Valor / Kills
+ Partidas
+ Muertes
+ Teamkills
+ K/D
+
+
+ ${rowsMarkup}
+
+
+ `;
}
function renderResultItem(item) {
@@ -508,7 +536,6 @@
);
}
- setBackendState(messages.backendOnline, true);
isBackendOnline = true;
} catch (error) {
console.warn("Player profile failed", error);
diff --git a/frontend/historico.html b/frontend/historico.html
index a5e2aca..079051b 100644
--- a/frontend/historico.html
+++ b/frontend/historico.html
@@ -21,13 +21,13 @@
Histórico
- Stats
+ Estadísticas
Ranking
-
+
@@ -43,7 +43,6 @@
-
Histórico propio
Registro de la
Comunidad Hispana
diff --git a/frontend/index.html b/frontend/index.html
index 52bf07d..eac0736 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -24,13 +24,13 @@
Histórico
- Stats
+ Estadísticas
Ranking
-
+
diff --git a/frontend/ranking.html b/frontend/ranking.html
index 26a65a5..ff9658d 100644
--- a/frontend/ranking.html
+++ b/frontend/ranking.html
@@ -9,6 +9,7 @@
/>
Ranking - HLL Vietnam
+
@@ -20,13 +21,13 @@
Histórico
- Stats
+ Estadísticas
Ranking
-
+
@@ -41,7 +42,6 @@
/>
-
Sección Ranking
Ranking
Global
@@ -62,7 +62,7 @@
Top público por ventana
- El ranking expone la lectura pública de líderes. Para búsqueda individual usa la sección Stats.
+ El ranking expone la lectura pública de líderes. Para búsqueda individual usa Estadísticas.