From 88964d61263332bcab883ba9b194749b085d0a12 Mon Sep 17 00:00:00 2001 From: devRaGonSa Date: Tue, 9 Jun 2026 14:30:33 +0200 Subject: [PATCH] Polish frontend hero consistency and annual ranking layout --- ...l-consistency-and-annual-ranking-layout.md | 158 ++++++++++++++++++ frontend/assets/css/hero-header-compact.css | 32 ++-- frontend/assets/css/historico.css | 21 ++- frontend/assets/css/styles.css | 131 ++++++++++++--- frontend/assets/js/main.js | 18 +- frontend/assets/js/ranking.js | 20 +-- frontend/assets/js/stats.js | 97 +++++++---- frontend/historico.html | 5 +- frontend/index.html | 4 +- frontend/ranking.html | 10 +- frontend/stats.html | 12 +- 11 files changed, 392 insertions(+), 116 deletions(-) create mode 100644 ai/tasks/done/TASK-206-public-frontend-visual-consistency-and-annual-ranking-layout.md 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

-

Servidor: ${escapeHtml(serverId)}

-

M\u00e9trica: ${escapeHtml(metric)}

-

Partidas fuente: ${safeInt(sourceMatches, 0)}

-

Actualizado: ${escapeHtml(generatedAt || "No disponible")}

+
+

Servidor

+ ${escapeHtml(serverId)} +
+
+

A\u00f1o

+ ${year} +
+
+

Lectura

+ ${escapeHtml(metric)} +
+
+

Partidas base

+ ${safeInt(sourceMatches, 0)} +
+
+

Actualizado

+ ${escapeHtml(generatedAt || "No disponible")} +
${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 ` +
+ + + + + + + + + + + + + ${rowsMarkup} +
Posici\u00f3nJugadorValor / KillsPartidasMuertesTeamkillsK/D
+
+ `; } 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.

@@ -123,7 +123,7 @@ diff --git a/frontend/stats.html b/frontend/stats.html index 809dd4b..6603591 100644 --- a/frontend/stats.html +++ b/frontend/stats.html @@ -5,10 +5,11 @@ - Stats - HLL Vietnam + Estadísticas - HLL Vietnam +
@@ -20,13 +21,13 @@ Histórico - Stats + Estadísticas Ranking -
+
@@ -41,7 +42,6 @@ />
-

Sección Stats

Estadísticas Personales @@ -126,7 +126,7 @@

Top 20 anual

- El ranking anual usa snapshots pre-computados para evitar recalcular toda la temporada en cada consulta. + 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.