diff --git a/ai/tasks/pending/TASK-136-add-rcon-match-detail-map-hero-image.md b/ai/tasks/done/TASK-136-add-rcon-match-detail-map-hero-image.md similarity index 70% rename from ai/tasks/pending/TASK-136-add-rcon-match-detail-map-hero-image.md rename to ai/tasks/done/TASK-136-add-rcon-match-detail-map-hero-image.md index 7d30ee9..ae6ce2e 100644 --- a/ai/tasks/pending/TASK-136-add-rcon-match-detail-map-hero-image.md +++ b/ai/tasks/done/TASK-136-add-rcon-match-detail-map-hero-image.md @@ -1,7 +1,7 @@ --- id: TASK-136 title: Add RCON match detail map hero image -status: pending +status: done type: frontend team: Frontend Senior supporting_teams: @@ -138,11 +138,28 @@ Then hard refresh with `Ctrl+F5` and verify: ## Outcome -To be completed by AI Platform Run / Codex CLI. +- Added a hidden map-media slot to `frontend/historico-partida.html` and reveal it only when the current match resolves to a known local map asset. +- Added `resolveMapImagePath` in `frontend/assets/js/historico-partida.js`, mapping the available local map names including the known Carentan match to `./assets/img/maps/carentan-day.webp`. +- Added the map assets under `frontend/assets/img/maps/` because the task explicitly depends on local map image files. +- Updated the historical hero CSS so the logo, match copy and map image form a responsive desktop header and collapse cleanly on mobile. +- Shortened the RCON hero subtitle to keep the narrow mobile header from clipping while preserving the player table, hidden timeline, hidden confidence/source/base details and existing scoreboard data. ## Validation Result -To be completed by AI Platform Run / Codex CLI. +- PASS: `node --check frontend/assets/js/historico-partida.js` +- PASS: `node --check frontend/assets/js/historico.js` +- PASS: `node --check frontend/assets/js/historico-recent-live.js` +- PASS: `python -m compileall backend/app` +- PASS: `powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1` +- PASS: `powershell -ExecutionPolicy Bypass -File scripts/run-rcon-data-pipeline-tests.ps1` + - Note: the script completed successfully but emitted existing `ResourceWarning` messages from backend unittest sqlite connections. +- PASS: `docker compose up -d --build backend frontend` +- PASS: `Invoke-WebRequest "http://localhost:8000/health" | Select-Object -ExpandProperty Content` +- PASS: `Invoke-WebRequest "http://localhost:8000/api/historical/recent-matches?server=all-servers&limit=10" | Select-Object -ExpandProperty Content` +- Manual/rendered verification: Browser plugin was listed, but the required Node runtime tool was not exposed in this session; used local Chrome headless fallback. + - Verified rendered DOM contains `./assets/img/maps/carentan-day.webp`, `3 : 2`, `Ganador: Aliados`, `Carentan`, `1 h 30 min`, `AntonioPruna` and `M1 GARAND`. + - Verified timeline section remains hidden and no `snapshot`, Elo/MVP block or Comunidad Hispana #03 appeared in the rendered checks. + - Captured screenshots outside the repository at `C:\Temp\task-136-match-detail-final.png` and `C:\Temp\task-136-match-detail-mobile-final-3.png`. ## Change Budget diff --git a/frontend/assets/css/historico.css b/frontend/assets/css/historico.css index 4c2c3e7..c4b24f7 100644 --- a/frontend/assets/css/historico.css +++ b/frontend/assets/css/historico.css @@ -22,15 +22,20 @@ .historical-hero__copy { display: grid; gap: 24px; + min-width: 0; } .historical-hero__layout { display: grid; - grid-template-columns: minmax(220px, 300px) minmax(0, 1fr); + grid-template-columns: minmax(190px, 260px) minmax(0, 0.82fr) minmax(320px, 0.9fr); align-items: center; gap: 32px; } +.historical-hero__layout > * { + min-width: 0; +} + .historical-logo-frame { width: min(300px, 100%); min-height: 220px; @@ -42,6 +47,45 @@ .historical-hero__text { max-width: 60ch; + overflow-wrap: anywhere; +} + +.historical-map-hero { + position: relative; + overflow: hidden; + min-height: 220px; + margin: 0; + border: 1px solid rgba(210, 182, 118, 0.2); + border-radius: 18px; + background: + linear-gradient(180deg, rgba(19, 24, 16, 0.9), rgba(10, 13, 9, 0.72)); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.04), + 0 18px 40px rgba(0, 0, 0, 0.24); +} + +.historical-map-hero[hidden] { + display: none; +} + +.historical-map-hero::before { + content: ""; + position: absolute; + inset: 0; + z-index: 1; + border: 1px solid rgba(210, 182, 118, 0.12); + border-radius: inherit; + background: + linear-gradient(90deg, rgba(7, 9, 7, 0.24), transparent 34%), + linear-gradient(180deg, transparent 54%, rgba(7, 9, 7, 0.44)); + pointer-events: none; +} + +.historical-map-hero__image { + width: 100%; + height: 100%; + min-height: 220px; + object-fit: cover; } .historical-content { @@ -657,7 +701,24 @@ } .historical-hero__layout { - display: flex; + display: grid; + grid-template-columns: 1fr; + } + + .historical-hero__copy, + .historical-hero__copy > div, + .historical-hero__text { + width: 100%; + max-width: 100%; + } + + .historical-map-hero { + width: 100%; + min-height: 190px; + } + + .historical-map-hero__image { + min-height: 190px; } .historical-selector { diff --git a/frontend/assets/img/maps/carentan-day.webp b/frontend/assets/img/maps/carentan-day.webp new file mode 100644 index 0000000..4e4d5b1 Binary files /dev/null and b/frontend/assets/img/maps/carentan-day.webp differ diff --git a/frontend/assets/img/maps/driel-day.webp b/frontend/assets/img/maps/driel-day.webp new file mode 100644 index 0000000..9d6ab2e Binary files /dev/null and b/frontend/assets/img/maps/driel-day.webp differ diff --git a/frontend/assets/img/maps/elalamein-day.webp b/frontend/assets/img/maps/elalamein-day.webp new file mode 100644 index 0000000..dd00ef0 Binary files /dev/null and b/frontend/assets/img/maps/elalamein-day.webp differ diff --git a/frontend/assets/img/maps/elsenbornridge-day.webp b/frontend/assets/img/maps/elsenbornridge-day.webp new file mode 100644 index 0000000..334a48f Binary files /dev/null and b/frontend/assets/img/maps/elsenbornridge-day.webp differ diff --git a/frontend/assets/img/maps/foy-day.webp b/frontend/assets/img/maps/foy-day.webp new file mode 100644 index 0000000..7eb7dfe Binary files /dev/null and b/frontend/assets/img/maps/foy-day.webp differ diff --git a/frontend/assets/img/maps/hill400-day.webp b/frontend/assets/img/maps/hill400-day.webp new file mode 100644 index 0000000..24ac56b Binary files /dev/null and b/frontend/assets/img/maps/hill400-day.webp differ diff --git a/frontend/assets/img/maps/hurtgenforest-day.webp b/frontend/assets/img/maps/hurtgenforest-day.webp new file mode 100644 index 0000000..11bccf7 Binary files /dev/null and b/frontend/assets/img/maps/hurtgenforest-day.webp differ diff --git a/frontend/assets/img/maps/kharkov-day.webp b/frontend/assets/img/maps/kharkov-day.webp new file mode 100644 index 0000000..c3a0c49 Binary files /dev/null and b/frontend/assets/img/maps/kharkov-day.webp differ diff --git a/frontend/assets/img/maps/kursk-day.webp b/frontend/assets/img/maps/kursk-day.webp new file mode 100644 index 0000000..5e00bac Binary files /dev/null and b/frontend/assets/img/maps/kursk-day.webp differ diff --git a/frontend/assets/img/maps/mortain-day.webp b/frontend/assets/img/maps/mortain-day.webp new file mode 100644 index 0000000..354a601 Binary files /dev/null and b/frontend/assets/img/maps/mortain-day.webp differ diff --git a/frontend/assets/img/maps/omahabeach-day.webp b/frontend/assets/img/maps/omahabeach-day.webp new file mode 100644 index 0000000..ece99ec Binary files /dev/null and b/frontend/assets/img/maps/omahabeach-day.webp differ diff --git a/frontend/assets/img/maps/purpleheartlane-rain.webp b/frontend/assets/img/maps/purpleheartlane-rain.webp new file mode 100644 index 0000000..b4e0585 Binary files /dev/null and b/frontend/assets/img/maps/purpleheartlane-rain.webp differ diff --git a/frontend/assets/img/maps/smolensk-day.webp b/frontend/assets/img/maps/smolensk-day.webp new file mode 100644 index 0000000..f8e815a Binary files /dev/null and b/frontend/assets/img/maps/smolensk-day.webp differ diff --git a/frontend/assets/img/maps/stmariedumont-day.webp b/frontend/assets/img/maps/stmariedumont-day.webp new file mode 100644 index 0000000..2daa86d Binary files /dev/null and b/frontend/assets/img/maps/stmariedumont-day.webp differ diff --git a/frontend/assets/img/maps/stmereeglise-day.webp b/frontend/assets/img/maps/stmereeglise-day.webp new file mode 100644 index 0000000..d2ca95b Binary files /dev/null and b/frontend/assets/img/maps/stmereeglise-day.webp differ diff --git a/frontend/assets/img/maps/tobruk-dawn.webp b/frontend/assets/img/maps/tobruk-dawn.webp new file mode 100644 index 0000000..f15404a Binary files /dev/null and b/frontend/assets/img/maps/tobruk-dawn.webp differ diff --git a/frontend/assets/img/maps/tobruk-day.webp b/frontend/assets/img/maps/tobruk-day.webp new file mode 100644 index 0000000..4ed1b16 Binary files /dev/null and b/frontend/assets/img/maps/tobruk-day.webp differ diff --git a/frontend/assets/img/maps/utahbeach-day.webp b/frontend/assets/img/maps/utahbeach-day.webp new file mode 100644 index 0000000..fcd4abd Binary files /dev/null and b/frontend/assets/img/maps/utahbeach-day.webp differ diff --git a/frontend/assets/js/historico-partida.js b/frontend/assets/js/historico-partida.js index 1553a46..5022c5f 100644 --- a/frontend/assets/js/historico-partida.js +++ b/frontend/assets/js/historico-partida.js @@ -20,6 +20,8 @@ document.addEventListener("DOMContentLoaded", () => { timelineNote: document.getElementById("match-detail-timeline-note"), timelineState: document.getElementById("match-detail-timeline-state"), timelineGrid: document.getElementById("match-detail-timeline-grid"), + mapHero: document.getElementById("match-detail-map-hero"), + mapImage: document.getElementById("match-detail-map-image"), }; if (!serverSlug || !matchId) { @@ -66,6 +68,7 @@ function renderMatchDetail(item, nodes) { nodes.title.textContent = mapName; nodes.summary.textContent = `${serverName} - ${formatDetailSubtitle(item)}`; nodes.note.textContent = ""; + renderMapHero(item, mapName, nodes); nodes.grid.innerHTML = renderScoreboardDetail(item, { mapName, serverName }); renderPlayerSection(item, nodes); hideTimelineSection(nodes); @@ -74,6 +77,28 @@ function renderMatchDetail(item, nodes) { nodes.grid.hidden = false; } +function renderMapHero(item, mapName, nodes) { + if (!nodes.mapHero || !nodes.mapImage) { + return; + } + + const mapImagePath = resolveMapImagePath(item, mapName); + if (!mapImagePath) { + nodes.mapImage.removeAttribute("src"); + nodes.mapImage.alt = ""; + nodes.mapHero.hidden = true; + return; + } + + nodes.mapImage.src = mapImagePath; + nodes.mapImage.alt = mapName; + nodes.mapImage.onerror = () => { + nodes.mapImage.removeAttribute("src"); + nodes.mapHero.hidden = true; + }; + nodes.mapHero.hidden = false; +} + function renderScoreboardDetail(item, { mapName, serverName }) { const result = item.result || {}; const alliedScore = Number.isFinite(Number(result.allied_score)) @@ -273,6 +298,36 @@ function resolveMatchFactions(item, mapName) { }; } +function resolveMapImagePath(item, mapName) { + const normalizedMap = normalizeLookupText( + `${item.map?.name || ""} ${item.map?.pretty_name || ""} ${mapName || ""}`, + ).replaceAll(" ", ""); + const mapAssetByKey = { + carentan: "carentan-day.webp", + driel: "driel-day.webp", + elalamein: "elalamein-day.webp", + elsenbornridge: "elsenbornridge-day.webp", + foy: "foy-day.webp", + hill400: "hill400-day.webp", + hurtgenforest: "hurtgenforest-day.webp", + kharkov: "kharkov-day.webp", + kursk: "kursk-day.webp", + mortain: "mortain-day.webp", + omahabeach: "omahabeach-day.webp", + purpleheartlane: "purpleheartlane-rain.webp", + smolensk: "smolensk-day.webp", + stmariedumont: "stmariedumont-day.webp", + stmereeglise: "stmereeglise-day.webp", + tobrukdawn: "tobruk-dawn.webp", + tobruk: "tobruk-day.webp", + utahbeach: "utahbeach-day.webp", + }; + const matchedKey = Object.keys(mapAssetByKey).find((key) => + normalizedMap.includes(key), + ); + return matchedKey ? `./assets/img/maps/${mapAssetByKey[matchedKey]}` : ""; +} + function normalizeLookupText(value) { return String(value || "") .normalize("NFD") @@ -284,10 +339,10 @@ function normalizeLookupText(value) { function formatDetailSubtitle(item) { if (item.capture_basis === "rcon-materialized-admin-log") { - return "Partida RCON materializada"; + return "RCON"; } if (item.capture_basis === "rcon-competitive-window") { - return "Partida RCON registrada"; + return "RCON"; } if (item.result_source === "public-scoreboard-match") { return "Partida del scoreboard"; diff --git a/frontend/historico-partida.html b/frontend/historico-partida.html index 6852163..f031bb6 100644 --- a/frontend/historico-partida.html +++ b/frontend/historico-partida.html @@ -44,6 +44,18 @@

+