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" : ""}
+
+
+
+ `;
+}
+
+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
+