Polish frontend hero consistency and annual ranking layout
This commit is contained in:
@@ -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.
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
@media (min-width: 1121px) {
|
@media (min-width: 1121px) {
|
||||||
.hero:not(.historical-hero) .hero__content {
|
.hero--public-page:not(.historical-hero) .hero__content {
|
||||||
padding-block: 54px 60px;
|
padding-block: 54px 60px;
|
||||||
padding-inline: clamp(64px, 5.5vw, 104px);
|
padding-inline: clamp(64px, 5.5vw, 104px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero:not(.historical-hero) .hero__brand {
|
.hero--public-page:not(.historical-hero) .hero__brand {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1420px;
|
max-width: 1420px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero:not(.historical-hero) .logo-frame {
|
.hero--public-page:not(.historical-hero) .logo-frame {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
justify-self: start;
|
justify-self: start;
|
||||||
width: min(470px, 100%);
|
width: min(470px, 100%);
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
padding: 30px 34px;
|
padding: 30px 34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero:not(.historical-hero) .logo-frame__image {
|
.hero--public-page:not(.historical-hero) .logo-frame__image {
|
||||||
max-height: 340px;
|
max-height: 340px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero:not(.historical-hero) .hero__copy {
|
.hero--public-page:not(.historical-hero) .hero__copy {
|
||||||
grid-column: 3;
|
grid-column: 3;
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -37,41 +37,41 @@
|
|||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero:not(.historical-hero) .hero__title {
|
.hero--public-page:not(.historical-hero) .hero__title {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
font-size: clamp(4.2rem, 5.2vw, 6.35rem);
|
font-size: clamp(4.2rem, 5.2vw, 6.35rem);
|
||||||
line-height: 1.02;
|
line-height: 1.02;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero:not(.historical-hero) .hero__title-accent {
|
.hero--public-page:not(.historical-hero) .hero__title-accent {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero:not(.historical-hero) .hero__text {
|
.hero--public-page:not(.historical-hero) .hero__text {
|
||||||
max-width: 68ch;
|
max-width: 68ch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1121px) and (max-width: 1380px) {
|
@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);
|
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);
|
grid-template-columns: minmax(320px, 390px) minmax(28px, 1fr) minmax(560px, 720px);
|
||||||
column-gap: clamp(20px, 2.5vw, 40px);
|
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%);
|
width: min(390px, 100%);
|
||||||
min-height: 316px;
|
min-height: 316px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero:not(.historical-hero) .logo-frame__image {
|
.hero--public-page:not(.historical-hero) .logo-frame__image {
|
||||||
max-height: 298px;
|
max-height: 298px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero:not(.historical-hero) .hero__copy {
|
.hero--public-page:not(.historical-hero) .hero__copy {
|
||||||
grid-column: 3;
|
grid-column: 3;
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -79,14 +79,14 @@
|
|||||||
transform: none;
|
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);
|
font-size: clamp(3.7rem, 5.35vw, 5.15rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1120px) {
|
@media (max-width: 1120px) {
|
||||||
.hero:not(.historical-hero) .hero__copy {
|
.hero--public-page:not(.historical-hero) .hero__copy {
|
||||||
grid-column: auto;
|
grid-column: auto;
|
||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.historical-hero__content {
|
.historical-hero__content {
|
||||||
padding-top: 44px;
|
padding-top: 58px;
|
||||||
padding-bottom: 46px;
|
padding-bottom: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.historical-hero__topline {
|
.historical-hero__topline {
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
.historical-hero__copy {
|
.historical-hero__copy {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 24px;
|
gap: 18px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
.historical-hero__layout--registry {
|
.historical-hero__layout--registry {
|
||||||
grid-template-columns: minmax(220px, 330px) minmax(0, 1fr);
|
grid-template-columns: minmax(220px, 330px) minmax(0, 1fr);
|
||||||
column-gap: clamp(72px, 7vw, 140px);
|
column-gap: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.historical-hero__layout > * {
|
.historical-hero__layout > * {
|
||||||
@@ -42,8 +42,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.historical-logo-frame {
|
.historical-logo-frame {
|
||||||
width: min(300px, 100%);
|
width: min(340px, 100%);
|
||||||
min-height: 220px;
|
min-height: 248px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.historical-hero__title {
|
.historical-hero__title {
|
||||||
@@ -65,6 +65,11 @@
|
|||||||
|
|
||||||
/* Registry/historico hero: logo left, real flexible spacer, copy anchored right. */
|
/* Registry/historico hero: logo left, real flexible spacer, copy anchored right. */
|
||||||
@media (min-width: 1121px) {
|
@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 {
|
.historical-hero__layout--registry {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1420px;
|
max-width: 1420px;
|
||||||
@@ -113,6 +118,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1121px) and (max-width: 1380px) {
|
@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 {
|
.historical-hero__layout--registry {
|
||||||
grid-template-columns: minmax(320px, 390px) minmax(28px, 1fr) minmax(560px, 720px);
|
grid-template-columns: minmax(320px, 390px) minmax(28px, 1fr) minmax(560px, 720px);
|
||||||
column-gap: clamp(20px, 2.5vw, 40px);
|
column-gap: clamp(20px, 2.5vw, 40px);
|
||||||
|
|||||||
@@ -112,7 +112,9 @@ a {
|
|||||||
color: var(--text-soft);
|
color: var(--text-soft);
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
letter-spacing: 0.12em;
|
letter-spacing: 0.1em;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
transition:
|
transition:
|
||||||
transform 160ms ease,
|
transform 160ms ease,
|
||||||
@@ -904,6 +906,15 @@ h2 {
|
|||||||
padding-inline: 18px;
|
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 {
|
.clan-card__logo img {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -912,6 +923,10 @@ h2 {
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clan-card__logo--bxb img {
|
||||||
|
max-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
.clan-card__logo-placeholder {
|
.clan-card__logo-placeholder {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 84px;
|
min-height: 84px;
|
||||||
@@ -972,11 +987,6 @@ h2 {
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-hero .hero__brand,
|
|
||||||
.ranking-hero .hero__brand {
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-search-form {
|
.stats-search-form {
|
||||||
margin: 0 0 14px;
|
margin: 0 0 14px;
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -1098,26 +1108,104 @@ h2 {
|
|||||||
|
|
||||||
.stats-annual-table-wrap {
|
.stats-annual-table-wrap {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-annual-meta {
|
.stats-annual-meta {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 4px;
|
gap: 12px;
|
||||||
margin: 0 0 8px;
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-annual-item {
|
.stats-annual-card {
|
||||||
padding: 10px 12px;
|
border: 1px solid rgba(159, 168, 141, 0.2);
|
||||||
border: 1px solid rgba(159, 168, 141, 0.22);
|
border-radius: 18px;
|
||||||
border-radius: 14px;
|
padding: 18px;
|
||||||
margin-bottom: 8px;
|
|
||||||
background:
|
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 {
|
.stats-annual-meta-item {
|
||||||
margin: 0 0 6px;
|
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 {
|
.stats-annual-sub {
|
||||||
@@ -1499,8 +1587,8 @@ h2 {
|
|||||||
.public-nav__link {
|
.public-nav__link {
|
||||||
min-height: 46px;
|
min-height: 46px;
|
||||||
padding-inline: 12px;
|
padding-inline: 12px;
|
||||||
font-size: 0.74rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.06em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-search-form__fields {
|
.stats-search-form__fields {
|
||||||
@@ -1525,6 +1613,11 @@ h2 {
|
|||||||
padding: 12px 10px;
|
padding: 12px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stats-annual-table th,
|
||||||
|
.stats-annual-table td {
|
||||||
|
padding: 12px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.logo-frame {
|
.logo-frame {
|
||||||
min-height: 180px;
|
min-height: 180px;
|
||||||
padding: 16px 18px;
|
padding: 16px 18px;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const COMMUNITY_CLANS = Object.freeze([
|
|||||||
logoAlt: "Logo de La 129",
|
logoAlt: "Logo de La 129",
|
||||||
logoClassName: "clan-card__logo--wide",
|
logoClassName: "clan-card__logo--wide",
|
||||||
discordUrl: "",
|
discordUrl: "",
|
||||||
discordLabel: "Proximamente",
|
discordLabel: "Pr\u00f3ximamente",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "250 Hispania",
|
name: "250 Hispania",
|
||||||
@@ -65,9 +65,10 @@ const COMMUNITY_CLANS = Object.freeze([
|
|||||||
"Clan activo de la comunidad.",
|
"Clan activo de la comunidad.",
|
||||||
logoSrc: "./assets/img/clans/bxb.png",
|
logoSrc: "./assets/img/clans/bxb.png",
|
||||||
logoAlt: "Logo de BxB",
|
logoAlt: "Logo de BxB",
|
||||||
logoClassName: "",
|
logoClassName: "clan-card__logo--bxb",
|
||||||
|
cardClassName: "clan-card--bxb",
|
||||||
discordUrl: "",
|
discordUrl: "",
|
||||||
discordLabel: "Proximamente",
|
discordLabel: "Pr\u00f3ximamente",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "7dv",
|
name: "7dv",
|
||||||
@@ -209,7 +210,7 @@ async function hydrateServers(
|
|||||||
|
|
||||||
if (serversData.items.length === 0) {
|
if (serversData.items.length === 0) {
|
||||||
serversList.innerHTML =
|
serversList.innerHTML =
|
||||||
'<p class="servers-empty">Informacion de servidores disponible mas adelante.</p>';
|
'<p class="servers-empty">Informaci\u00f3n de servidores disponible m\u00e1s adelante.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +284,7 @@ function renderServerStatsCard(server) {
|
|||||||
{ label: "Mapa", value: currentMap, valueClassName: "server-card__quickfact-value--map" },
|
{ label: "Mapa", value: currentMap, valueClassName: "server-card__quickfact-value--map" },
|
||||||
];
|
];
|
||||||
if (region) {
|
if (region) {
|
||||||
quickFactItems.push({ label: "Region", value: region });
|
quickFactItems.push({ label: "Regi\u00f3n", value: region });
|
||||||
}
|
}
|
||||||
const quickFacts = renderQuickFacts(quickFactItems);
|
const quickFacts = renderQuickFacts(quickFactItems);
|
||||||
|
|
||||||
@@ -345,7 +346,7 @@ function renderServerAction(server) {
|
|||||||
return `
|
return `
|
||||||
<div class="server-card__actions">
|
<div class="server-card__actions">
|
||||||
<a class="server-action-link" href="${escapeHtml(actions.historicalUrl)}">
|
<a class="server-action-link" href="${escapeHtml(actions.historicalUrl)}">
|
||||||
Historico
|
Hist\u00f3rico
|
||||||
</a>
|
</a>
|
||||||
<a class="server-action-link" href="${escapeHtml(actions.currentMatchUrl)}">
|
<a class="server-action-link" href="${escapeHtml(actions.currentMatchUrl)}">
|
||||||
Partida actual
|
Partida actual
|
||||||
@@ -367,9 +368,10 @@ function hydrateCommunityClans(listNode) {
|
|||||||
function renderCommunityClanCard(clan) {
|
function renderCommunityClanCard(clan) {
|
||||||
const logoMarkup = renderClanLogo(clan);
|
const logoMarkup = renderClanLogo(clan);
|
||||||
const discordMarkup = renderClanDiscordLink(clan);
|
const discordMarkup = renderClanDiscordLink(clan);
|
||||||
|
const cardClassName = clan.cardClassName ? ` ${escapeHtml(clan.cardClassName)}` : "";
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<article class="clan-card">
|
<article class="clan-card${cardClassName}">
|
||||||
<div class="clan-card__brand">
|
<div class="clan-card__brand">
|
||||||
${logoMarkup}
|
${logoMarkup}
|
||||||
<div class="clan-card__copy">
|
<div class="clan-card__copy">
|
||||||
@@ -541,7 +543,7 @@ function deriveSnapshotState(serversData) {
|
|||||||
return {
|
return {
|
||||||
label: isFresh
|
label: isFresh
|
||||||
? `Actualizado ${timestampLabel}`
|
? `Actualizado ${timestampLabel}`
|
||||||
: `Ultimo snapshot ${timestampLabel}`,
|
: `\u00daltimo snapshot ${timestampLabel}`,
|
||||||
isFresh,
|
isFresh,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
const limitSelect = document.getElementById("ranking-limit");
|
const limitSelect = document.getElementById("ranking-limit");
|
||||||
const yearWrap = document.getElementById("ranking-year-wrap");
|
const yearWrap = document.getElementById("ranking-year-wrap");
|
||||||
const yearInput = document.getElementById("ranking-year");
|
const yearInput = document.getElementById("ranking-year");
|
||||||
const backendStateNode = document.getElementById("ranking-backend-state");
|
|
||||||
const stateNode = document.getElementById("ranking-state");
|
const stateNode = document.getElementById("ranking-state");
|
||||||
const titleNode = document.getElementById("ranking-title");
|
const titleNode = document.getElementById("ranking-title");
|
||||||
const metaNode = document.getElementById("ranking-meta");
|
const metaNode = document.getElementById("ranking-meta");
|
||||||
@@ -48,7 +47,6 @@
|
|||||||
applyInitialUrlState();
|
applyInitialUrlState();
|
||||||
toggleYearField();
|
toggleYearField();
|
||||||
syncMetricState();
|
syncMetricState();
|
||||||
setBackendState("Comprobando disponibilidad del backend", false);
|
|
||||||
setRankingState("neutral", "Esperando filtros para cargar el ranking global.");
|
setRankingState("neutral", "Esperando filtros para cargar el ranking global.");
|
||||||
clearRankingSurface();
|
clearRankingSurface();
|
||||||
refreshBackendHealth();
|
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) {
|
function setRankingState(state, message) {
|
||||||
if (!stateNode) {
|
if (!stateNode) {
|
||||||
return;
|
return;
|
||||||
@@ -192,7 +180,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
setFilterNote(
|
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",
|
"neutral",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -245,7 +233,7 @@
|
|||||||
if (!payload || payload.status !== "ok") {
|
if (!payload || payload.status !== "ok") {
|
||||||
throw new Error("Unexpected health payload");
|
throw new Error("Unexpected health payload");
|
||||||
}
|
}
|
||||||
setBackendState("Backend operativo", true);
|
isBackendOnline = true;
|
||||||
if (!String(stateNode?.textContent || "").includes("limite del URL")) {
|
if (!String(stateNode?.textContent || "").includes("limite del URL")) {
|
||||||
setRankingState(
|
setRankingState(
|
||||||
"neutral",
|
"neutral",
|
||||||
@@ -255,7 +243,7 @@
|
|||||||
void loadRanking();
|
void loadRanking();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("Ranking health check failed", 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.");
|
setRankingState("error", "Backend no disponible. El ranking queda en estado offline.");
|
||||||
renderEmptyState(
|
renderEmptyState(
|
||||||
"No fue posible contactar el backend. Cuando vuelva a estar disponible podras consultar semanal, mensual o anual.",
|
"No fue posible contactar el backend. Cuando vuelva a estar disponible podras consultar semanal, mensual o anual.",
|
||||||
@@ -319,7 +307,7 @@
|
|||||||
renderRanking(payload.data || {});
|
renderRanking(payload.data || {});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("Ranking request failed", error);
|
console.warn("Ranking request failed", error);
|
||||||
setBackendState("Backend no disponible", false);
|
isBackendOnline = false;
|
||||||
setRankingState("error", "Error controlado al cargar el ranking.");
|
setRankingState("error", "Error controlado al cargar el ranking.");
|
||||||
renderEmptyState(
|
renderEmptyState(
|
||||||
"La lectura del ranking fall\u00f3 en este intento. Revisa el backend o actualiza la p\u00e1gina.",
|
"La lectura del ranking fall\u00f3 en este intento. Revisa el backend o actualiza la p\u00e1gina.",
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
const searchStateNode = document.getElementById("stats-search-state");
|
const searchStateNode = document.getElementById("stats-search-state");
|
||||||
const searchHelpNode = document.getElementById("stats-search-help");
|
const searchHelpNode = document.getElementById("stats-search-help");
|
||||||
const resultListNode = document.getElementById("stats-result-list");
|
const resultListNode = document.getElementById("stats-result-list");
|
||||||
const backendStateNode = document.getElementById("stats-backend-state");
|
|
||||||
const profilePanel = document.getElementById("stats-profile-panel");
|
const profilePanel = document.getElementById("stats-profile-panel");
|
||||||
const profileTitle = document.getElementById("stats-profile-title");
|
const profileTitle = document.getElementById("stats-profile-title");
|
||||||
const profileStateNode = document.getElementById("stats-profile-state");
|
const profileStateNode = document.getElementById("stats-profile-state");
|
||||||
@@ -25,9 +24,6 @@
|
|||||||
const annualServerId = "all";
|
const annualServerId = "all";
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
backendChecking: "Comprobando disponibilidad del backend",
|
|
||||||
backendOnline: "Backend operativo",
|
|
||||||
backendUnavailable: "Backend no disponible. Reintenta en unos segundos.",
|
|
||||||
backendUnavailableForSearch:
|
backendUnavailableForSearch:
|
||||||
"Backend no disponible. No se pueden buscar jugadores ni cargar su perfil.",
|
"Backend no disponible. No se pueden buscar jugadores ni cargar su perfil.",
|
||||||
backendUnavailableForAnnual:
|
backendUnavailableForAnnual:
|
||||||
@@ -48,14 +44,14 @@
|
|||||||
profileError: "No fue posible cargar las estad\u00edsticas del jugador.",
|
profileError: "No fue posible cargar las estad\u00edsticas del jugador.",
|
||||||
annualLoading: "Cargando ranking anual...",
|
annualLoading: "Cargando ranking anual...",
|
||||||
annualMissing:
|
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:
|
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:
|
annualUnsupportedMetric:
|
||||||
"La m\u00e9trica anual solicitada no est\u00e1 soportada en V1.",
|
"La m\u00e9trica anual solicitada no est\u00e1 soportada en V1.",
|
||||||
annualMetricInvalid:
|
annualMetricInvalid:
|
||||||
"Par\u00e1metro de ranking anual inv\u00e1lido. Usa metric=kills en V1.",
|
"Par\u00e1metro de ranking anual inv\u00e1lido. Usa metric=kills en V1.",
|
||||||
annualReadyPrefix: "Ranking anual listo para",
|
annualReadyPrefix: "Ranking anual disponible para",
|
||||||
weeklyPlaceholder:
|
weeklyPlaceholder:
|
||||||
"Sin datos semanales. El ranking semanal se actualiza al cargar un jugador.",
|
"Sin datos semanales. El ranking semanal se actualiza al cargar un jugador.",
|
||||||
monthlyPlaceholder:
|
monthlyPlaceholder:
|
||||||
@@ -82,7 +78,6 @@
|
|||||||
"El ranking anual se consulta por separado con el a\u00f1o indicado.";
|
"El ranking anual se consulta por separado con el a\u00f1o indicado.";
|
||||||
}
|
}
|
||||||
|
|
||||||
setBackendState(messages.backendChecking, false);
|
|
||||||
if (annualYearInput) {
|
if (annualYearInput) {
|
||||||
annualYearInput.value = String(annualDefaultYear);
|
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() {
|
function markAsBackendUnavailable() {
|
||||||
setBackendState(messages.backendUnavailable, false);
|
isBackendOnline = false;
|
||||||
if (searchStateNode) {
|
if (searchStateNode) {
|
||||||
searchStateNode.textContent = messages.backendUnavailableForSearch;
|
searchStateNode.textContent = messages.backendUnavailableForSearch;
|
||||||
searchStateNode.className = "stats-state stats-state--error";
|
searchStateNode.className = "stats-state stats-state--error";
|
||||||
@@ -173,10 +158,10 @@
|
|||||||
throw new Error("Unexpected health payload");
|
throw new Error("Unexpected health payload");
|
||||||
}
|
}
|
||||||
|
|
||||||
setBackendState(messages.backendOnline, true);
|
isBackendOnline = true;
|
||||||
setAnnualState(
|
setAnnualState(
|
||||||
"neutral",
|
"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();
|
void loadAnnualRanking();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -345,17 +330,33 @@
|
|||||||
|
|
||||||
setAnnualState(
|
setAnnualState(
|
||||||
"neutral",
|
"neutral",
|
||||||
`${messages.annualReadyPrefix} ${serverId}, a\u00f1o ${year}, m\u00e9trica ${metric}`,
|
`${messages.annualReadyPrefix} ${serverId}, a\u00f1o ${year}.`,
|
||||||
);
|
);
|
||||||
|
|
||||||
annualContentNode.innerHTML = `
|
annualContentNode.innerHTML = `
|
||||||
<article class="stats-summary-card">
|
<article class="stats-annual-card">
|
||||||
<p class="stats-summary-title">Top ${limit} anual</p>
|
<p class="stats-summary-title">Top ${limit} anual</p>
|
||||||
<div class="stats-annual-meta">
|
<div class="stats-annual-meta">
|
||||||
<p><strong>Servidor:</strong> ${escapeHtml(serverId)}</p>
|
<article class="stats-annual-meta-item">
|
||||||
<p><strong>M\u00e9trica:</strong> ${escapeHtml(metric)}</p>
|
<p>Servidor</p>
|
||||||
<p><strong>Partidas fuente:</strong> ${safeInt(sourceMatches, 0)}</p>
|
<strong>${escapeHtml(serverId)}</strong>
|
||||||
<p><strong>Actualizado:</strong> ${escapeHtml(generatedAt || "No disponible")}</p>
|
</article>
|
||||||
|
<article class="stats-annual-meta-item">
|
||||||
|
<p>A\u00f1o</p>
|
||||||
|
<strong>${year}</strong>
|
||||||
|
</article>
|
||||||
|
<article class="stats-annual-meta-item">
|
||||||
|
<p>Lectura</p>
|
||||||
|
<strong>${escapeHtml(metric)}</strong>
|
||||||
|
</article>
|
||||||
|
<article class="stats-annual-meta-item">
|
||||||
|
<p>Partidas base</p>
|
||||||
|
<strong>${safeInt(sourceMatches, 0)}</strong>
|
||||||
|
</article>
|
||||||
|
<article class="stats-annual-meta-item">
|
||||||
|
<p>Actualizado</p>
|
||||||
|
<strong>${escapeHtml(generatedAt || "No disponible")}</strong>
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
${renderAnnualRows(items)}
|
${renderAnnualRows(items)}
|
||||||
</article>
|
</article>
|
||||||
@@ -363,27 +364,54 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderAnnualRows(items) {
|
function renderAnnualRows(items) {
|
||||||
return 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 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 metricValue = safeInt(item.metric_value, 0);
|
const metricValue = safeInt(item.metric_value, 0);
|
||||||
const matches = safeInt(item.matches_considered, 0);
|
const matches = safeInt(item.matches_considered, 0);
|
||||||
const kills = safeInt(item.kills, 0);
|
|
||||||
const deaths = safeInt(item.deaths, 0);
|
const deaths = safeInt(item.deaths, 0);
|
||||||
const teamkills = safeInt(item.teamkills, 0);
|
const teamkills = safeInt(item.teamkills, 0);
|
||||||
const kd = safeDecimal(item.kd_ratio, 2, "0.00");
|
const kd = safeDecimal(item.kd_ratio, 2, "0.00");
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<article class="stats-annual-item">
|
<tr>
|
||||||
<p><strong>#${rank}</strong> ${playerName} <span class="stats-annual-sub">(ID: ${playerId})</span></p>
|
<td class="stats-annual-rank">#${rank}</td>
|
||||||
<p><strong>Valor:</strong> ${metricValue} - <strong>Partidas:</strong> ${matches}</p>
|
<td>
|
||||||
<p><strong>Kills:</strong> ${kills} - <strong>Deaths:</strong> ${deaths} - <strong>Teamkills:</strong> ${teamkills} - <strong>K/D:</strong> ${kd}</p>
|
<div class="stats-annual-player">
|
||||||
</article>
|
<strong>${playerName}</strong>
|
||||||
|
<span class="stats-annual-player__id">ID ${playerId}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="stats-annual-metric">${metricValue}</td>
|
||||||
|
<td>${matches}</td>
|
||||||
|
<td>${deaths}</td>
|
||||||
|
<td>${teamkills}</td>
|
||||||
|
<td>${kd}</td>
|
||||||
|
</tr>
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="stats-annual-table-shell">
|
||||||
|
<table class="stats-annual-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Posici\u00f3n</th>
|
||||||
|
<th>Jugador</th>
|
||||||
|
<th>Valor / Kills</th>
|
||||||
|
<th>Partidas</th>
|
||||||
|
<th>Muertes</th>
|
||||||
|
<th>Teamkills</th>
|
||||||
|
<th>K/D</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>${rowsMarkup}</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderResultItem(item) {
|
function renderResultItem(item) {
|
||||||
@@ -508,7 +536,6 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setBackendState(messages.backendOnline, true);
|
|
||||||
isBackendOnline = true;
|
isBackendOnline = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("Player profile failed", error);
|
console.warn("Player profile failed", error);
|
||||||
|
|||||||
@@ -21,13 +21,13 @@
|
|||||||
Histórico
|
Histórico
|
||||||
</a>
|
</a>
|
||||||
<a class="public-nav__link" href="./stats.html">
|
<a class="public-nav__link" href="./stats.html">
|
||||||
Stats
|
Estadísticas
|
||||||
</a>
|
</a>
|
||||||
<a class="public-nav__link" href="./ranking.html">
|
<a class="public-nav__link" href="./ranking.html">
|
||||||
Ranking
|
Ranking
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
<header class="hero historical-hero">
|
<header class="hero hero--public-page historical-hero">
|
||||||
<div class="hero__overlay"></div>
|
<div class="hero__overlay"></div>
|
||||||
<div class="hero__content historical-hero__content">
|
<div class="hero__content historical-hero__content">
|
||||||
<div class="historical-hero__layout historical-hero__layout--registry">
|
<div class="historical-hero__layout historical-hero__layout--registry">
|
||||||
@@ -43,7 +43,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="historical-hero__copy">
|
<div class="historical-hero__copy">
|
||||||
<div>
|
<div>
|
||||||
<p class="eyebrow eyebrow--section">Histórico propio</p>
|
|
||||||
<h1 class="historical-hero__title">
|
<h1 class="historical-hero__title">
|
||||||
Registro de la
|
Registro de la
|
||||||
<span class="hero__title-accent">Comunidad Hispana</span>
|
<span class="hero__title-accent">Comunidad Hispana</span>
|
||||||
|
|||||||
@@ -24,13 +24,13 @@
|
|||||||
Histórico
|
Histórico
|
||||||
</a>
|
</a>
|
||||||
<a class="public-nav__link" href="./stats.html">
|
<a class="public-nav__link" href="./stats.html">
|
||||||
Stats
|
Estadísticas
|
||||||
</a>
|
</a>
|
||||||
<a class="public-nav__link" href="./ranking.html">
|
<a class="public-nav__link" href="./ranking.html">
|
||||||
Ranking
|
Ranking
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
<header class="hero">
|
<header class="hero hero--public-page">
|
||||||
<div class="hero__overlay"></div>
|
<div class="hero__overlay"></div>
|
||||||
<div class="hero__content">
|
<div class="hero__content">
|
||||||
<div class="hero__brand">
|
<div class="hero__brand">
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
/>
|
/>
|
||||||
<title>Ranking - HLL Vietnam</title>
|
<title>Ranking - HLL Vietnam</title>
|
||||||
<link rel="stylesheet" href="./assets/css/styles.css" />
|
<link rel="stylesheet" href="./assets/css/styles.css" />
|
||||||
|
<link rel="stylesheet" href="./assets/css/hero-header-compact.css" />
|
||||||
</head>
|
</head>
|
||||||
<body data-backend-base-url="http://127.0.0.1:8000">
|
<body data-backend-base-url="http://127.0.0.1:8000">
|
||||||
<div class="page-shell stats-page-shell">
|
<div class="page-shell stats-page-shell">
|
||||||
@@ -20,13 +21,13 @@
|
|||||||
Histórico
|
Histórico
|
||||||
</a>
|
</a>
|
||||||
<a class="public-nav__link" href="./stats.html">
|
<a class="public-nav__link" href="./stats.html">
|
||||||
Stats
|
Estadísticas
|
||||||
</a>
|
</a>
|
||||||
<a class="public-nav__link is-active" href="./ranking.html" aria-current="page">
|
<a class="public-nav__link is-active" href="./ranking.html" aria-current="page">
|
||||||
Ranking
|
Ranking
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
<header class="hero stats-hero ranking-hero">
|
<header class="hero hero--public-page stats-hero ranking-hero">
|
||||||
<div class="hero__overlay"></div>
|
<div class="hero__overlay"></div>
|
||||||
<div class="hero__content">
|
<div class="hero__content">
|
||||||
<div class="hero__brand">
|
<div class="hero__brand">
|
||||||
@@ -41,7 +42,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="hero__copy">
|
<div class="hero__copy">
|
||||||
<p class="eyebrow eyebrow--section">Sección Ranking</p>
|
|
||||||
<h1 class="hero__title">
|
<h1 class="hero__title">
|
||||||
Ranking
|
Ranking
|
||||||
<span class="hero__title-accent">Global</span>
|
<span class="hero__title-accent">Global</span>
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
<h2>Top público por ventana</h2>
|
<h2>Top público por ventana</h2>
|
||||||
</div>
|
</div>
|
||||||
<p class="panel__intro panel__intro--tight">
|
<p class="panel__intro panel__intro--tight">
|
||||||
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.
|
||||||
</p>
|
</p>
|
||||||
<form class="ranking-form" id="ranking-form">
|
<form class="ranking-form" id="ranking-form">
|
||||||
<div class="ranking-form__grid">
|
<div class="ranking-form__grid">
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
<div class="hero__actions ranking-form__actions">
|
<div class="hero__actions ranking-form__actions">
|
||||||
<button class="discord-button" type="submit">Actualizar ranking</button>
|
<button class="discord-button" type="submit">Actualizar ranking</button>
|
||||||
<a class="secondary-button secondary-button--compact" href="./stats.html">
|
<a class="secondary-button secondary-button--compact" href="./stats.html">
|
||||||
Buscar jugador en Stats
|
Buscar jugador en Estadísticas
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="Consulta estadísticas personales de jugadores en el panel Stats de HLL Vietnam."
|
content="Consulta estadísticas personales de jugadores en la zona pública de HLL Vietnam."
|
||||||
/>
|
/>
|
||||||
<title>Stats - HLL Vietnam</title>
|
<title>Estadísticas - HLL Vietnam</title>
|
||||||
<link rel="stylesheet" href="./assets/css/styles.css" />
|
<link rel="stylesheet" href="./assets/css/styles.css" />
|
||||||
|
<link rel="stylesheet" href="./assets/css/hero-header-compact.css" />
|
||||||
</head>
|
</head>
|
||||||
<body data-backend-base-url="http://127.0.0.1:8000">
|
<body data-backend-base-url="http://127.0.0.1:8000">
|
||||||
<div class="page-shell stats-page-shell">
|
<div class="page-shell stats-page-shell">
|
||||||
@@ -20,13 +21,13 @@
|
|||||||
Histórico
|
Histórico
|
||||||
</a>
|
</a>
|
||||||
<a class="public-nav__link is-active" href="./stats.html" aria-current="page">
|
<a class="public-nav__link is-active" href="./stats.html" aria-current="page">
|
||||||
Stats
|
Estadísticas
|
||||||
</a>
|
</a>
|
||||||
<a class="public-nav__link" href="./ranking.html">
|
<a class="public-nav__link" href="./ranking.html">
|
||||||
Ranking
|
Ranking
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
<header class="hero stats-hero">
|
<header class="hero hero--public-page stats-hero">
|
||||||
<div class="hero__overlay"></div>
|
<div class="hero__overlay"></div>
|
||||||
<div class="hero__content">
|
<div class="hero__content">
|
||||||
<div class="hero__brand">
|
<div class="hero__brand">
|
||||||
@@ -41,7 +42,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="hero__copy">
|
<div class="hero__copy">
|
||||||
<p class="eyebrow eyebrow--section">Sección Stats</p>
|
|
||||||
<h1 class="hero__title">
|
<h1 class="hero__title">
|
||||||
Estadísticas
|
Estadísticas
|
||||||
<span class="hero__title-accent">Personales</span>
|
<span class="hero__title-accent">Personales</span>
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
<h2>Top 20 anual</h2>
|
<h2>Top 20 anual</h2>
|
||||||
</div>
|
</div>
|
||||||
<p class="panel__intro panel__intro--tight">
|
<p class="panel__intro panel__intro--tight">
|
||||||
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.
|
||||||
</p>
|
</p>
|
||||||
<form class="stats-annual-form" id="stats-annual-form">
|
<form class="stats-annual-form" id="stats-annual-form">
|
||||||
<label class="stats-search-form__label" for="stats-annual-year">Año</label>
|
<label class="stats-search-form__label" for="stats-annual-year">Año</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user