From ef8de88163896e93cbbe6abd418de84af69b2f2a Mon Sep 17 00:00:00 2001 From: devRaGonSa <97627393+devRaGonSa@users.noreply.github.com> Date: Tue, 19 May 2026 19:33:13 +0200 Subject: [PATCH 1/3] fix: actually render scoreboard match detail layout --- frontend/assets/js/historico-partida.js | 222 ++++++++---------------- 1 file changed, 76 insertions(+), 146 deletions(-) diff --git a/frontend/assets/js/historico-partida.js b/frontend/assets/js/historico-partida.js index 80dde3a..ae353dc 100644 --- a/frontend/assets/js/historico-partida.js +++ b/frontend/assets/js/historico-partida.js @@ -25,7 +25,7 @@ document.addEventListener("DOMContentLoaded", () => { if (!serverSlug || !matchId) { nodes.title.textContent = "Partida no seleccionada"; nodes.summary.textContent = "Vuelve al historico y abre una partida registrada."; - nodes.note.textContent = "Faltan parametros internos para cargar este detalle."; + nodes.note.textContent = ""; setState(nodes.state, "No hay una partida seleccionada.", true); return; } @@ -46,8 +46,7 @@ async function loadMatchDetail({ backendBaseUrl, serverSlug, matchId, nodes }) { nodes.title.textContent = "Detalle no disponible"; nodes.summary.textContent = "La partida existe como enlace interno, pero todavia no hay detalle suficiente para mostrar."; - nodes.note.textContent = - "El historico local puede tener solo una ventana RCON parcial o ningun registro ampliado."; + nodes.note.textContent = ""; setState(nodes.state, "Detalle no disponible para esta partida."); return; } @@ -56,7 +55,7 @@ async function loadMatchDetail({ backendBaseUrl, serverSlug, matchId, nodes }) { } catch (error) { nodes.title.textContent = "Detalle no disponible"; nodes.summary.textContent = "No se pudo conectar con el backend local."; - nodes.note.textContent = "Comprueba que el backend este levantado y vuelve a intentarlo."; + nodes.note.textContent = ""; setState(nodes.state, "Error al cargar el detalle de la partida.", true); } } @@ -66,31 +65,81 @@ function renderMatchDetail(item, nodes) { const serverName = item.server?.name || item.server?.slug || "Servidor no disponible"; nodes.title.textContent = mapName; nodes.summary.textContent = `${serverName} - ${formatDetailSubtitle(item)}`; - nodes.note.textContent = buildDetailNote(item); - nodes.grid.innerHTML = [ - renderDetailCard("Servidor", serverName), - renderDetailCard("Mapa", mapName), - renderDetailCard("Modo", formatGameMode(item.game_mode || item.gamestate?.game_mode)), - renderDetailCard("Marcador", formatScore(item.result)), - renderDetailCard("Ganador", formatWinner(item.winner || item.result?.winner)), - renderDetailCard("Resultado", formatMatchResult(item.result)), - renderDetailCard("Inicio", formatMatchTimestamp(item, "start")), - renderDetailCard("Fin", formatMatchTimestamp(item, "end")), - renderDetailCard("Duracion", formatDuration(item.duration_seconds)), - renderDetailCard("Confianza", formatConfidence(item.confidence)), - renderDetailCard( - "Fuente", - formatSourceBasis(item.source_basis || item.result_source) || "No disponible", - ), - renderDetailCard("Base", formatCaptureBasis(item.capture_basis)), - ].join(""); + nodes.note.textContent = ""; + nodes.grid.innerHTML = renderScoreboardDetail(item, { mapName, serverName }); renderPlayerSection(item, nodes); - renderTimelineSection(item, nodes); + hideTimelineSection(nodes); renderActions(item, nodes.actions); nodes.state.hidden = true; nodes.grid.hidden = false; } +function renderScoreboardDetail(item, { mapName, serverName }) { + const result = item.result || {}; + const alliedScore = Number.isFinite(Number(result.allied_score)) + ? formatNumber(result.allied_score) + : "-"; + const axisScore = Number.isFinite(Number(result.axis_score)) + ? formatNumber(result.axis_score) + : "-"; + const winner = String(item.winner || result.winner || "").toLowerCase(); + const isAlliedWinner = winner === "allies" || winner === "allied"; + const isAxisWinner = winner === "axis"; + const metadata = [ + ["Servidor", serverName], + ["Mapa", mapName], + ["Modo", formatGameMode(item.game_mode || item.gamestate?.game_mode)], + ["Duracion", formatDuration(item.duration_seconds)], + ["Inicio", formatMatchTimestamp(item, "start")], + ]; + if (item.ended_at) { + metadata.push(["Fin", formatMatchTimestamp(item, "end")]); + } + + return ` +
+
+ Bando aliado + Aliados + ${isAlliedWinner ? "Ganador" : ""} +
+
+ Marcador + ${escapeHtml(alliedScore)} - ${escapeHtml(axisScore)} + ${escapeHtml(formatWinner(winner))} +
+
+ Bando eje + Eje + ${isAxisWinner ? "Ganador" : ""} +
+
+
+ ${metadata.map(([label, value]) => renderCompactMeta(label, value)).join("")} +
+ `; +} + +function renderCompactMeta(label, value) { + return ` +
+ ${escapeHtml(label)} + ${escapeHtml(value || "No disponible")} +
+ `; +} + +function hideTimelineSection(nodes) { + if (!nodes.timelineSection) { + return; + } + nodes.timelineSection.hidden = true; + nodes.timelineNote.textContent = ""; + nodes.timelineState.hidden = true; + nodes.timelineGrid.hidden = true; + nodes.timelineGrid.innerHTML = ""; +} + function renderPlayerSection(item, nodes) { const players = Array.isArray(item.players) ? item.players : []; nodes.playersSection.hidden = false; @@ -128,37 +177,6 @@ function renderPlayerRow(player) { `; } -function renderTimelineSection(item, nodes) { - const eventCounts = Array.isArray(item.timeline?.event_counts) - ? item.timeline.event_counts - : []; - nodes.timelineSection.hidden = false; - if (eventCounts.length === 0) { - nodes.timelineNote.textContent = - "No hay resumen de eventos disponible para esta partida."; - setState(nodes.timelineState, "Sin eventos agregados para mostrar."); - nodes.timelineGrid.hidden = true; - nodes.timelineGrid.innerHTML = ""; - return; - } - - nodes.timelineNote.textContent = `${formatNumber(eventCounts.length)} tipos de evento registrados.`; - nodes.timelineState.hidden = true; - nodes.timelineGrid.innerHTML = eventCounts.map((event) => renderTimelineCard(event)).join(""); - nodes.timelineGrid.hidden = false; -} - -function renderTimelineCard(event) { - const label = - event.event_type || - event.type || - event.name || - event.label || - "Evento registrado"; - const count = event.count ?? event.total ?? event.event_count ?? 0; - return renderDetailCard(formatEventType(label), formatNumber(count)); -} - function renderActions(item, actionsNode) { const matchUrl = normalizeExternalMatchUrl(item.match_url); if (!matchUrl) { @@ -179,23 +197,6 @@ function renderActions(item, actionsNode) { actionsNode.hidden = false; } -function renderDetailCard(label, value) { - return ` -
-

${escapeHtml(label)}

- ${escapeHtml(value)} -
- `; -} - -function buildDetailNote(item) { - const source = formatSourceBasis(item.source_basis || item.result_source); - if (source) { - return `Detalle servido desde ${source}. Los campos sin cobertura fiable se muestran como no disponibles.`; - } - return "Detalle servido desde el historico local disponible para esta partida."; -} - function formatDetailSubtitle(item) { if (item.capture_basis === "rcon-materialized-admin-log") { return "Partida RCON materializada"; @@ -209,52 +210,13 @@ function formatDetailSubtitle(item) { return "Partida historica"; } -function formatCaptureBasis(value) { - if (value === "rcon-materialized-admin-log") { - return "Registro RCON materializado"; - } - if (value === "rcon-competitive-window") { - return "Ventana competitiva RCON"; - } - if (value === "public-scoreboard-match") { - return "Scoreboard persistido"; - } - return value ? String(value).replaceAll("-", " ") : "Historico local"; -} - -function formatSourceBasis(value) { - if (value === "admin-log-match-ended") { - return "cierre RCON confirmado"; - } - if (value === "rcon-session") { - return "sesion RCON registrada"; - } - if (value === "public-scoreboard-match") { - return "scoreboard externo"; - } - return value ? String(value).replaceAll("-", " ") : ""; -} - -function formatConfidence(value) { - if (value === "exact") { - return "Exacta"; - } - if (value === "approximate") { - return "Aproximada"; - } - if (value === "partial") { - return "Parcial"; - } - return value ? String(value).replaceAll("-", " ") : "No disponible"; -} - function formatTeamSide(value) { const normalized = String(value || "").toLowerCase(); if (normalized === "allies" || normalized === "allied") { return "Aliados"; } if (normalized === "axis") { - return "Axis"; + return "Eje"; } return value || "No disponible"; } @@ -281,39 +243,18 @@ function formatDuration(value) { return `${formatNumber(hours)} h ${formatNumber(remainingMinutes)} min`; } -function formatMatchResult(result) { - if (hasMatchScore(result)) { - return `${formatWinner(result.winner)} (${formatScore(result)})`; - } - return result?.winner ? formatWinner(result.winner) : "Resultado no disponible"; -} - function formatWinner(value) { const normalized = String(value || "").toLowerCase(); if (normalized === "allies" || normalized === "allied") { - return "Aliados"; + return "Ganador: Aliados"; } if (normalized === "axis") { - return "Axis"; + return "Ganador: Eje"; } if (normalized === "draw") { return "Empate"; } - return "No disponible"; -} - -function formatScore(result) { - if (!hasMatchScore(result)) { - return "Resultado no disponible"; - } - return `${Number(result.allied_score)} - ${Number(result.axis_score)}`; -} - -function hasMatchScore(result) { - return ( - Number.isFinite(Number(result?.allied_score)) && - Number.isFinite(Number(result?.axis_score)) - ); + return "Resultado no disponible"; } function formatOptionalNumber(value) { @@ -346,14 +287,6 @@ function formatNamedCounts(items) { .join(" / "); } -function formatEventType(value) { - const normalized = String(value || "").replaceAll("_", " ").replaceAll("-", " "); - if (!normalized) { - return "Evento"; - } - return normalized.charAt(0).toUpperCase() + normalized.slice(1); -} - function formatNumber(value) { const parsedValue = Number(value); if (!Number.isFinite(parsedValue)) { @@ -394,9 +327,6 @@ function formatMatchTimestamp(item, kind) { if (timestamp) { return formatTimestamp(timestamp); } - if (item.timestamp_confidence === "server-time-only") { - return "No disponible"; - } return "No disponible"; } From 70f5719ec04382d9651c8b8f13580137ef44fe52 Mon Sep 17 00:00:00 2001 From: devRaGonSa <97627393+devRaGonSa@users.noreply.github.com> Date: Tue, 19 May 2026 19:40:23 +0200 Subject: [PATCH 2/3] fix: add scoreboard detail stylesheet --- .../css/historico-scoreboard-detail.css | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 frontend/assets/css/historico-scoreboard-detail.css diff --git a/frontend/assets/css/historico-scoreboard-detail.css b/frontend/assets/css/historico-scoreboard-detail.css new file mode 100644 index 0000000..709992f --- /dev/null +++ b/frontend/assets/css/historico-scoreboard-detail.css @@ -0,0 +1,111 @@ +.historical-scoreboard-detail { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(180px, 280px) minmax(0, 1fr); + align-items: stretch; + gap: 14px; + margin-bottom: 14px; + padding: 18px; + border: 1px solid rgba(210, 182, 118, 0.24); + border-radius: 22px; + background: + radial-gradient(circle at top center, rgba(210, 182, 118, 0.14), transparent 44%), + linear-gradient(180deg, rgba(29, 34, 24, 0.96), rgba(11, 14, 10, 0.98)); + box-shadow: var(--shadow-soft); +} + +.historical-scoreboard-detail__side, +.historical-scoreboard-detail__score { + display: grid; + align-content: center; + justify-items: center; + min-height: 142px; + padding: 18px; + border: 1px solid rgba(159, 168, 141, 0.14); + border-radius: 18px; + background: rgba(13, 17, 12, 0.48); + text-align: center; +} + +.historical-scoreboard-detail__side span, +.historical-scoreboard-detail__score span, +.historical-match-compact-meta span { + margin-bottom: 7px; + color: var(--muted); + font-size: 0.72rem; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.historical-scoreboard-detail__side strong { + color: var(--text); + font-size: clamp(1.35rem, 2.8vw, 2.6rem); + line-height: 1; +} + +.historical-scoreboard-detail__side em, +.historical-scoreboard-detail__score em { + min-height: 1.2em; + margin-top: 10px; + color: var(--accent-strong); + font-style: normal; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.historical-scoreboard-detail__side.is-winner { + border-color: rgba(210, 182, 118, 0.42); + background: + radial-gradient(circle at top, rgba(210, 182, 118, 0.14), transparent 55%), + rgba(25, 24, 16, 0.7); +} + +.historical-scoreboard-detail__score { + border-color: rgba(210, 182, 118, 0.36); + background: + radial-gradient(circle at center, rgba(210, 182, 118, 0.18), transparent 62%), + rgba(11, 14, 10, 0.72); +} + +.historical-scoreboard-detail__score strong { + color: var(--text); + font-size: clamp(3.4rem, 7vw, 6.2rem); + line-height: 0.95; + letter-spacing: 0.02em; +} + +.historical-match-compact-meta { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); + gap: 12px; +} + +.historical-match-compact-meta article { + padding: 13px 14px; + border: 1px solid rgba(159, 168, 141, 0.14); + border-radius: 16px; + background: rgba(13, 17, 12, 0.44); +} + +.historical-match-compact-meta strong { + display: block; + color: var(--text); + font-size: 1rem; + line-height: 1.35; +} + +@media (max-width: 720px) { + .historical-scoreboard-detail { + grid-template-columns: 1fr; + padding: 14px; + } + + .historical-scoreboard-detail__side, + .historical-scoreboard-detail__score { + min-height: 112px; + } + + .historical-scoreboard-detail__score { + order: -1; + } +} From 566199ceed223263e29303bc1d6e269e71d00368 Mon Sep 17 00:00:00 2001 From: devRaGonSa <97627393+devRaGonSa@users.noreply.github.com> Date: Tue, 19 May 2026 19:41:00 +0200 Subject: [PATCH 3/3] fix: load scoreboard detail stylesheet --- frontend/historico-partida.html | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/historico-partida.html b/frontend/historico-partida.html index 8e2abc1..6852163 100644 --- a/frontend/historico-partida.html +++ b/frontend/historico-partida.html @@ -10,6 +10,7 @@ Detalle de partida - HLL Vietnam +