diff --git a/ai/tasks/done/TASK-143-player-table-hover-stats-panel.md b/ai/tasks/done/TASK-143-player-table-hover-stats-panel.md new file mode 100644 index 0000000..c2aea64 --- /dev/null +++ b/ai/tasks/done/TASK-143-player-table-hover-stats-panel.md @@ -0,0 +1,80 @@ +--- +id: TASK-143 +title: Player table hover stats panel +status: done +type: frontend +team: Frontend Senior +supporting_teams: [Experto en interfaz] +roadmap_item: foundation +priority: high +--- + +# TASK-143 - Player table hover stats panel + +## Goal + +Make the internal historical match detail player table more compact by moving expanded per-player statistics into an accessible hover/focus/click details panel. + +## Context + +The detail page player table currently includes detailed columns for weapons, most killed and death by. The desired scoreboard-like UX keeps the table focused on player/team/core combat metrics and reveals expanded statistics per row. + +## Steps + +1. Inspect the current match detail renderer and scoreboard detail CSS. +2. Reduce the visible table columns to Jugador, Equipo, K, D, TK, K/D and KPM. +3. Add accessible row hover/focus/click expanded panels with weapons, most killed, death by and direct matchups. +4. Preserve team styling, scoreboard link behavior and forbidden hidden sections. +5. Validate syntax, frontend build/container and rendered behavior. + +## Files to Read First + +- `ai/architecture-index.md` +- `ai/repo-context.md` +- `ai/orchestrator/frontend-senior.md` +- `frontend/historico-partida.html` +- `frontend/assets/js/historico-partida.js` +- `frontend/assets/css/historico-scoreboard-detail.css` +- `frontend/assets/css/historico.css` + +## Expected Files to Modify + +- `frontend/historico-partida.html` +- `frontend/assets/js/historico-partida.js` +- `frontend/assets/css/historico-scoreboard-detail.css` +- `ai/tasks/done/TASK-143-player-table-hover-stats-panel.md` + +## Constraints + +- Do not modify backend unless absolutely necessary. +- Do not modify recent match card layout. +- Do not reintroduce timeline/events/confidence/source/base or Elo/MVP blocks. +- Preserve Aliados/Eje/No disponible visual distinction. + +## Validation + +- `node --check frontend/assets/js/historico-partida.js` +- `node --check frontend/assets/js/historico.js` +- `node --check frontend/assets/js/historico-recent-live.js` +- `docker compose build --no-cache frontend` +- `docker compose up -d frontend` +- Browser verification on `historico-partida.html` +- Browser smoke verification on `historico.html` + +## Outcome + +Implemented. The match detail player table now shows only Jugador, Equipo, K, D, TK, K/D and KPM. Expanded player statistics are rendered in per-player detail panels that open on row hover, keyboard focus/Enter and touch/click via an accessible info control. The panel includes the player/team summary, weapons, most killed, death by and direct matchup balance derived from most_killed and death_by. + +Validation passed: + +- `node --check frontend/assets/js/historico-partida.js` +- `node --check frontend/assets/js/historico.js` +- `node --check frontend/assets/js/historico-recent-live.js` +- `docker compose build --no-cache frontend` +- `docker compose up -d frontend` +- Browser verification on `historico-partida.html` for `comunidad-hispana-02:1779178461:1779183861:carentanwarfare` +- Browser smoke verification on `historico.html?nocache=player-table-hover` + +Notes: + +- Browser plugin runtime tools were not exposed by tool discovery in this session, so rendered validation used local Chrome/Selenium fallback. diff --git a/frontend/assets/css/historico-scoreboard-detail.css b/frontend/assets/css/historico-scoreboard-detail.css index 273edb2..f1148ce 100644 --- a/frontend/assets/css/historico-scoreboard-detail.css +++ b/frontend/assets/css/historico-scoreboard-detail.css @@ -34,6 +34,244 @@ background: linear-gradient(90deg, rgba(159, 168, 141, 0.08), transparent 42%); } +.historical-table--players { + min-width: 760px; +} + +.historical-player-row { + cursor: pointer; + outline: 0; +} + +.historical-player-row:hover td, +.historical-player-row:focus td, +.historical-player-row:focus-within td, +.historical-player-row.is-expanded td { + background: rgba(210, 182, 118, 0.06); +} + +.historical-player-row:focus-visible td, +.historical-player-row__details-button:focus-visible { + outline: 2px solid rgba(210, 182, 118, 0.78); + outline-offset: -2px; +} + +.historical-player-row__details-button { + display: flex; + width: 100%; + min-width: 220px; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 0; + border: 0; + background: transparent; + color: inherit; + font: inherit; + text-align: left; + cursor: pointer; +} + +.historical-player-row__details-button span:first-child { + overflow: hidden; + min-width: 0; + text-overflow: ellipsis; + white-space: nowrap; +} + +.historical-player-row__details-button span:last-child { + flex: 0 0 auto; + display: inline-flex; + width: 22px; + height: 22px; + align-items: center; + justify-content: center; + border: 1px solid rgba(210, 182, 118, 0.28); + border-radius: 999px; + color: var(--accent-strong); + font-size: 0.72rem; + font-weight: 900; + line-height: 1; + text-transform: uppercase; +} + +.historical-player-detail-row { + display: none; +} + +.historical-player-row:hover + .historical-player-detail-row, +.historical-player-row:focus + .historical-player-detail-row, +.historical-player-row:focus-within + .historical-player-detail-row, +.historical-player-detail-row:hover, +.historical-player-detail-row:focus-within, +.historical-player-detail-row.is-open { + display: table-row; +} + +.historical-player-detail-row td { + padding: 0 12px 16px; + background: rgba(7, 9, 7, 0.76); + border-bottom-color: rgba(210, 182, 118, 0.16); +} + +.historical-player-stats-panel { + display: grid; + gap: 16px; + padding: 16px; + border: 1px solid rgba(210, 182, 118, 0.18); + border-radius: 16px; + background: + linear-gradient(180deg, rgba(19, 24, 17, 0.96), rgba(10, 13, 10, 0.98)), + rgba(10, 13, 10, 0.96); + box-shadow: 0 18px 34px rgba(0, 0, 0, 0.28); +} + +.historical-player-stats-panel__header { + display: grid; + grid-template-columns: minmax(180px, 1fr) minmax(0, 2fr); + gap: 16px; + align-items: start; +} + +.historical-player-stats-panel__header p, +.historical-player-stats-panel__section p, +.historical-player-stats-panel__empty { + margin: 0; + color: var(--muted); +} + +.historical-player-stats-panel__header p { + margin-bottom: 5px; + font-size: 0.72rem; + font-weight: 900; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.historical-player-stats-panel__header h4, +.historical-player-stats-panel__section h5 { + margin: 0; + color: var(--text); +} + +.historical-player-stats-panel__header h4 { + font-size: 1.08rem; +} + +.historical-player-stats-panel__summary { + display: grid; + grid-template-columns: repeat(5, minmax(70px, 1fr)); + gap: 8px; +} + +.historical-player-stats-panel__summary article { + padding: 9px 10px; + border: 1px solid rgba(159, 168, 141, 0.14); + border-radius: 10px; + background: rgba(13, 17, 12, 0.68); +} + +.historical-player-stats-panel__summary span { + display: block; + margin-bottom: 4px; + color: var(--muted); + font-size: 0.64rem; + font-weight: 900; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.historical-player-stats-panel__summary strong { + color: var(--text); +} + +.historical-player-stats-panel__grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; +} + +.historical-player-stats-panel__section { + display: grid; + gap: 10px; + align-content: start; + padding: 13px; + border: 1px solid rgba(159, 168, 141, 0.14); + border-radius: 12px; + background: rgba(13, 17, 12, 0.52); +} + +.historical-player-stats-panel__section--wide { + grid-column: 1 / -1; +} + +.historical-player-stats-panel__section h5 { + font-size: 0.78rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.historical-player-stats-panel__section ol { + display: grid; + gap: 8px; + margin: 0; + padding: 0; + list-style: none; +} + +.historical-player-stats-panel__section li { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; + align-items: baseline; +} + +.historical-player-stats-panel__section li span { + overflow: hidden; + min-width: 0; + color: var(--text-soft); + text-overflow: ellipsis; + white-space: nowrap; +} + +.historical-player-stats-panel__section li strong { + color: var(--accent-strong); +} + +.historical-player-matchups { + display: grid; + gap: 0; + overflow-x: auto; +} + +.historical-player-matchups [role="row"] { + display: grid; + grid-template-columns: minmax(180px, 1fr) repeat(3, minmax(72px, auto)); + gap: 10px; + padding: 8px 0; + border-bottom: 1px solid rgba(159, 168, 141, 0.1); +} + +.historical-player-matchups [role="row"]:last-child { + border-bottom: 0; +} + +.historical-player-matchups [role="columnheader"] { + color: var(--muted); + font-size: 0.66rem; + font-weight: 900; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.historical-player-matchups [role="cell"] { + color: var(--text-soft); +} + +.historical-player-matchups strong[role="cell"] { + color: var(--text); +} + .historical-player-team-cell { white-space: nowrap; } @@ -71,6 +309,21 @@ color: var(--muted); } +@media (max-width: 760px) { + .historical-player-stats-panel__header, + .historical-player-stats-panel__grid { + grid-template-columns: 1fr; + } + + .historical-player-stats-panel__summary { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .historical-player-matchups [role="row"] { + min-width: 520px; + } +} + .historical-scoreboard-layout { display: grid; gap: 18px; diff --git a/frontend/assets/js/historico-partida.js b/frontend/assets/js/historico-partida.js index fc479e2..5f67703 100644 --- a/frontend/assets/js/historico-partida.js +++ b/frontend/assets/js/historico-partida.js @@ -213,15 +213,39 @@ function renderPlayerSection(item, nodes) { nodes.playersNote.textContent = `${formatNumber(players.length)} jugadores con estadisticas locales.`; nodes.playersState.hidden = true; - nodes.playersBody.innerHTML = players.map((player) => renderPlayerRow(player)).join(""); + nodes.playersBody.innerHTML = players + .map((player, index) => renderPlayerRows(player, item, index)) + .join(""); + bindPlayerDetailRows(nodes.playersBody); nodes.playersTableShell.hidden = false; } -function renderPlayerRow(player) { +function renderPlayerRows(player, item, index) { const team = getTeamSideDisplay(player.team || player.team_side); + const rowId = `match-player-row-${index}`; + const panelId = `match-player-panel-${index}`; + const playerName = player.player_name || player.name || "Jugador no identificado"; + const kpm = formatKpm(player.kills, item.duration_seconds); return ` -
${escapeHtml(context.team.label)}
+Sin estadisticas ampliadas disponibles.
` + } +No disponible
+No disponible
+