const HISTORICAL_SERVERS = Object.freeze([ { slug: "comunidad-hispana-01", label: "Comunidad Hispana #01", }, { slug: "comunidad-hispana-02", label: "Comunidad Hispana #02", }, { slug: "comunidad-hispana-03", label: "Comunidad Hispana #03", }, { slug: "all-servers", label: "Todos", }, ]); const HISTORICAL_SERVER_SLUGS = Object.freeze( HISTORICAL_SERVERS.map((server) => server.slug), ); const DEFAULT_HISTORICAL_SERVER = "all-servers"; const SNAPSHOT_CACHE_TTL_MS = 120000; const STALE_SNAPSHOT_CACHE_TTL_MS = 30000; const NEGATIVE_SNAPSHOT_CACHE_TTL_MS = 15000; let activeServerSlug = DEFAULT_HISTORICAL_SERVER; let activeLeaderboardMetric; let activeLeaderboardTimeframe; let activeServerRequestId = 0; let activeLeaderboardRequestId = 0; const LEADERBOARD_TIMEFRAMES = Object.freeze([ { key: "weekly", label: "Semanal", shortLabel: "semanal", }, { key: "monthly", label: "Mensual", shortLabel: "mensual", }, ]); const LEADERBOARD_METRICS = Object.freeze([ { key: "kills", title: "Top kills", valueHeading: "Kills", emptyMessage: "Sin datos historicos suficientes para mostrar este ranking de kills.", }, { key: "deaths", title: "Top muertes", valueHeading: "Muertes", emptyMessage: "Sin datos historicos suficientes para mostrar este ranking de muertes.", }, { key: "matches_over_100_kills", title: "Top partidas con 100+ kills", valueHeading: "Partidas 100+", emptyMessage: "Ningun jugador ha registrado partidas de 100+ kills en esta ventana.", }, { key: "support", title: "Top puntos de soporte", valueHeading: "Soporte", emptyMessage: "Sin datos historicos suficientes para mostrar este ranking de soporte.", }, ]); const DEFAULT_LEADERBOARD_METRIC = LEADERBOARD_METRICS[0].key; const DEFAULT_LEADERBOARD_TIMEFRAME = LEADERBOARD_TIMEFRAMES[0].key; activeLeaderboardMetric = DEFAULT_LEADERBOARD_METRIC; activeLeaderboardTimeframe = DEFAULT_LEADERBOARD_TIMEFRAME; document.addEventListener("DOMContentLoaded", () => { const backendBaseUrl = document.body.dataset.backendBaseUrl || "http://127.0.0.1:8000"; const selectorButtons = Array.from( document.querySelectorAll("[data-server-slug]"), ); const leaderboardTimeframeButtons = Array.from( document.querySelectorAll("[data-leaderboard-timeframe]"), ); const leaderboardTabButtons = Array.from( document.querySelectorAll("[data-leaderboard-metric]"), ); const summaryNode = document.getElementById("historical-summary"); const rangeNode = document.getElementById("historical-range"); const summaryNoteNode = document.getElementById("historical-summary-note"); const summarySnapshotMetaNode = document.getElementById( "historical-summary-snapshot-meta", ); const weeklyTitleNode = document.getElementById("weekly-ranking-title"); const weeklyStateNode = document.getElementById("weekly-leaderboard-state"); const weeklyTableNode = document.getElementById("weekly-leaderboard-table"); const weeklyBodyNode = document.getElementById("weekly-leaderboard-body"); const weeklyValueHeadingNode = document.getElementById("weekly-leaderboard-value-heading"); const weeklyWindowNoteNode = document.getElementById("weekly-window-note"); const weeklySnapshotMetaNode = document.getElementById( "weekly-leaderboard-snapshot-meta", ); const recentStateNode = document.getElementById("recent-matches-state"); const recentListNode = document.getElementById("recent-matches-list"); const recentNoteNode = document.getElementById("recent-matches-note"); const recentSnapshotMetaNode = document.getElementById( "recent-matches-snapshot-meta", ); const params = new URLSearchParams(window.location.search); activeServerSlug = normalizeServerSlug(params.get("server")); activeLeaderboardMetric = normalizeLeaderboardMetric(params.get("metric")); activeLeaderboardTimeframe = normalizeLeaderboardTimeframe( params.get("timeframe"), ); const summaryCache = new Map(); const recentMatchesCache = new Map(); const leaderboardCache = new Map(); const pendingRequestCache = new Map(); const getSummarySnapshot = (serverSlug) => getCachedJson( summaryCache, pendingRequestCache, buildSummarySnapshotKey(serverSlug), `${backendBaseUrl}/api/historical/snapshots/server-summary?server=${encodeURIComponent(serverSlug)}`, ); const getRecentMatchesSnapshot = (serverSlug) => getCachedJson( recentMatchesCache, pendingRequestCache, buildRecentMatchesSnapshotKey(serverSlug), `${backendBaseUrl}/api/historical/snapshots/recent-matches?server=${encodeURIComponent(serverSlug)}&limit=6`, ); const getLeaderboardSnapshot = (serverSlug, timeframeKey, metricKey) => getCachedJson( leaderboardCache, pendingRequestCache, buildLeaderboardSnapshotKey(serverSlug, timeframeKey, metricKey), `${backendBaseUrl}/api/historical/snapshots/leaderboard?server=${encodeURIComponent(serverSlug)}&timeframe=${encodeURIComponent(timeframeKey)}&metric=${encodeURIComponent(metricKey)}&limit=10`, ); const refreshServerContent = async () => { const requestId = activeServerRequestId + 1; const leaderboardRequestId = activeLeaderboardRequestId + 1; activeServerRequestId = requestId; activeLeaderboardRequestId = leaderboardRequestId; const activeMetricConfig = getLeaderboardMetricConfig(activeLeaderboardMetric); const activeTimeframeConfig = getLeaderboardTimeframeConfig( activeLeaderboardTimeframe, ); const activeServerLabel = getHistoricalServerLabel(activeServerSlug); syncActiveButtons(selectorButtons, activeServerSlug); syncLeaderboardTimeframes( leaderboardTimeframeButtons, activeLeaderboardTimeframe, ); syncLeaderboardTabs(leaderboardTabButtons, activeLeaderboardMetric); weeklyTitleNode.textContent = buildLeaderboardTitle( activeMetricConfig, activeServerSlug, activeLeaderboardTimeframe, ); weeklyValueHeadingNode.textContent = activeMetricConfig.valueHeading; setRangeBadge(rangeNode, "Cargando rango temporal", false); summaryNoteNode.textContent = `La vista esta leyendo snapshots precalculados del historico local para ${activeServerLabel}.`; setSnapshotMeta(summarySnapshotMetaNode, "Cargando snapshot de resumen..."); renderSummaryLoading(summaryNode); weeklyWindowNoteNode.textContent = "Cargando snapshot del ranking activo..."; setSnapshotMeta( weeklySnapshotMetaNode, `Preparando snapshot ${activeTimeframeConfig.shortLabel}...`, ); recentListNode.innerHTML = ""; recentNoteNode.textContent = buildRecentMatchesNote(activeServerSlug); setState(recentStateNode, "Cargando partidas recientes..."); setSnapshotMeta(recentSnapshotMetaNode, "Cargando snapshot de partidas..."); const cachedSummaryPayload = readCachedPayload( summaryCache, buildSummarySnapshotKey(activeServerSlug), ); if (cachedSummaryPayload) { hydrateSummary( { status: "fulfilled", value: cachedSummaryPayload }, summaryNode, rangeNode, summaryNoteNode, summarySnapshotMetaNode, ); } const cachedLeaderboardPayload = readCachedPayload( leaderboardCache, buildLeaderboardSnapshotKey( activeServerSlug, activeLeaderboardTimeframe, activeLeaderboardMetric, ), ); if (cachedLeaderboardPayload) { hydrateWeeklyLeaderboard( { status: "fulfilled", value: cachedLeaderboardPayload }, weeklyStateNode, weeklyTableNode, weeklyBodyNode, weeklyTitleNode, weeklyValueHeadingNode, weeklyWindowNoteNode, weeklySnapshotMetaNode, activeMetricConfig, activeLeaderboardTimeframe, ); } else { setState( weeklyStateNode, `Cargando ranking ${activeTimeframeConfig.shortLabel}...`, ); weeklyTableNode.hidden = true; } const cachedRecentMatchesPayload = readCachedPayload( recentMatchesCache, buildRecentMatchesSnapshotKey(activeServerSlug), ); if (cachedRecentMatchesPayload) { hydrateRecentMatches( { status: "fulfilled", value: cachedRecentMatchesPayload }, recentStateNode, recentListNode, recentSnapshotMetaNode, ); } const targetServerSlug = activeServerSlug; const targetTimeframe = activeLeaderboardTimeframe; const targetMetric = activeLeaderboardMetric; void settlePromise(getSummarySnapshot(targetServerSlug)).then((summaryResult) => { if ( !isActiveServerRequest( requestId, targetServerSlug, targetTimeframe, targetMetric, ) ) { return; } hydrateSummary( summaryResult, summaryNode, rangeNode, summaryNoteNode, summarySnapshotMetaNode, ); }); void settlePromise(getRecentMatchesSnapshot(targetServerSlug)).then((recentMatchesResult) => { if ( !isActiveServerRequest( requestId, targetServerSlug, targetTimeframe, targetMetric, ) ) { return; } hydrateRecentMatches( recentMatchesResult, recentStateNode, recentListNode, recentSnapshotMetaNode, ); }); void settlePromise( getLeaderboardSnapshot(targetServerSlug, targetTimeframe, targetMetric), ).then((leaderboardResult) => { if ( !isActiveLeaderboardRequest( requestId, leaderboardRequestId, targetServerSlug, targetTimeframe, targetMetric, ) ) { return; } hydrateWeeklyLeaderboard( leaderboardResult, weeklyStateNode, weeklyTableNode, weeklyBodyNode, weeklyTitleNode, weeklyValueHeadingNode, weeklyWindowNoteNode, weeklySnapshotMetaNode, activeMetricConfig, targetTimeframe, ); }); }; const refreshLeaderboardContent = async () => { const requestId = activeLeaderboardRequestId + 1; activeLeaderboardRequestId = requestId; const metricConfig = getLeaderboardMetricConfig(activeLeaderboardMetric); const timeframeConfig = getLeaderboardTimeframeConfig( activeLeaderboardTimeframe, ); const targetServerSlug = activeServerSlug; const targetTimeframe = activeLeaderboardTimeframe; const targetMetric = activeLeaderboardMetric; syncLeaderboardTimeframes( leaderboardTimeframeButtons, activeLeaderboardTimeframe, ); syncLeaderboardTabs(leaderboardTabButtons, activeLeaderboardMetric); weeklyTitleNode.textContent = buildLeaderboardTitle( metricConfig, activeServerSlug, activeLeaderboardTimeframe, ); weeklyValueHeadingNode.textContent = metricConfig.valueHeading; const cachedPayload = readCachedPayload( leaderboardCache, buildLeaderboardSnapshotKey( targetServerSlug, targetTimeframe, targetMetric, ), ); if (cachedPayload) { hydrateWeeklyLeaderboard( { status: "fulfilled", value: cachedPayload }, weeklyStateNode, weeklyTableNode, weeklyBodyNode, weeklyTitleNode, weeklyValueHeadingNode, weeklyWindowNoteNode, weeklySnapshotMetaNode, metricConfig, targetTimeframe, ); return; } weeklyWindowNoteNode.textContent = "Cargando snapshot del ranking activo..."; setSnapshotMeta( weeklySnapshotMetaNode, `Cargando snapshot ${timeframeConfig.shortLabel}...`, ); setState( weeklyStateNode, `Cargando ranking ${timeframeConfig.shortLabel}...`, ); weeklyTableNode.hidden = true; const leaderboardResult = await settlePromise( getLeaderboardSnapshot(targetServerSlug, targetTimeframe, targetMetric), ); if ( requestId !== activeLeaderboardRequestId || targetServerSlug !== activeServerSlug || targetTimeframe !== activeLeaderboardTimeframe || targetMetric !== activeLeaderboardMetric ) { return; } hydrateWeeklyLeaderboard( leaderboardResult, weeklyStateNode, weeklyTableNode, weeklyBodyNode, weeklyTitleNode, weeklyValueHeadingNode, weeklyWindowNoteNode, weeklySnapshotMetaNode, metricConfig, targetTimeframe, ); }; selectorButtons.forEach((button) => { button.addEventListener("click", () => { const nextServerSlug = normalizeServerSlug(button.dataset.serverSlug); if (nextServerSlug === activeServerSlug) { return; } activeServerSlug = nextServerSlug; params.set("server", activeServerSlug); params.set("timeframe", activeLeaderboardTimeframe); params.set("metric", activeLeaderboardMetric); window.history.replaceState({}, "", `?${params.toString()}`); void refreshServerContent(); }); }); leaderboardTimeframeButtons.forEach((button) => { button.addEventListener("click", () => { const nextTimeframe = normalizeLeaderboardTimeframe( button.dataset.leaderboardTimeframe, ); if (nextTimeframe === activeLeaderboardTimeframe) { return; } activeLeaderboardTimeframe = nextTimeframe; params.set("server", activeServerSlug); params.set("timeframe", activeLeaderboardTimeframe); params.set("metric", activeLeaderboardMetric); window.history.replaceState({}, "", `?${params.toString()}`); void refreshLeaderboardContent(); }); }); leaderboardTabButtons.forEach((button) => { button.addEventListener("click", () => { const nextMetric = normalizeLeaderboardMetric(button.dataset.leaderboardMetric); if (nextMetric === activeLeaderboardMetric) { return; } activeLeaderboardMetric = nextMetric; params.set("server", activeServerSlug); params.set("timeframe", activeLeaderboardTimeframe); params.set("metric", activeLeaderboardMetric); window.history.replaceState({}, "", `?${params.toString()}`); void refreshLeaderboardContent(); }); }); void refreshServerContent(); }); function isActiveServerRequest(requestId, serverSlug, timeframeKey, metricKey) { return ( requestId === activeServerRequestId && serverSlug === activeServerSlug && timeframeKey === activeLeaderboardTimeframe && metricKey === activeLeaderboardMetric ); } function isActiveLeaderboardRequest( serverRequestId, leaderboardRequestId, serverSlug, timeframeKey, metricKey, ) { return ( isActiveServerRequest(serverRequestId, serverSlug, timeframeKey, metricKey) && leaderboardRequestId === activeLeaderboardRequestId ); } function hydrateSummary(result, summaryNode, rangeNode, noteNode, snapshotMetaNode) { const emptyState = getHistoricalEmptyState(activeServerSlug); if (result.status !== "fulfilled") { renderSummaryError(summaryNode); setRangeBadge(rangeNode, "Snapshot de resumen no disponible", false); noteNode.textContent = "No se pudo leer el resumen precalculado para el alcance seleccionado."; setSnapshotMeta(snapshotMetaNode, "Error al leer el snapshot de resumen."); return; } const payload = result.value?.data; const summary = payload?.item; const hasHistoricalData = Number(summary?.imported_matches_count ?? summary?.matches_count ?? 0) > 0; if (!payload?.found || !summary || !hasHistoricalData) { renderSummaryEmpty(summaryNode, emptyState.summaryMessage); setRangeBadge(rangeNode, emptyState.rangeLabel, false); noteNode.textContent = emptyState.summaryNote; setSnapshotMeta( snapshotMetaNode, payload?.generated_at ? buildSnapshotMetaText(payload, "Snapshot de resumen pendiente de generacion.") : "Snapshot de resumen pendiente de generacion.", ); return; } const coverage = summary.coverage || {}; const timeRange = summary.time_range || {}; const rangeLabel = buildCoverageBadgeLabel(coverage, { start: payload?.source_range_start || timeRange.start, end: payload?.source_range_end || timeRange.end, }, summary.server?.slug); setRangeBadge( rangeNode, rangeLabel || "Cobertura historica disponible", coverage.status === "week-plus" && !payload?.is_stale, ); noteNode.textContent = buildSummaryNote( "snapshot-precomputed", 7, coverage, summary.server?.slug, ); setSnapshotMeta( snapshotMetaNode, buildSnapshotMetaText(payload, "Snapshot de resumen sin timestamp."), ); summaryNode.innerHTML = [ renderSummaryCard("Servidor", summary.server?.name || "Servidor no disponible"), renderSummaryCard( "Partidas registradas", formatNumber(summary.imported_matches_count ?? summary.matches_count), ), renderSummaryCard("Jugadores unicos", formatNumber(summary.unique_players)), renderSummaryCard( "Cobertura historica", buildCoveragePeriodLabel(coverage, timeRange, summary.server?.slug), ), renderSummaryCard("Inicio de registro", formatTimestamp(coverage.first_match_at)), renderSummaryCard("Ultimo cierre", formatTimestamp(coverage.last_match_at)), renderSummaryCard( "Mapas frecuentes", formatTopMaps(summary.top_maps), ), ].join(""); } function hydrateWeeklyLeaderboard( result, stateNode, tableNode, bodyNode, titleNode, valueHeadingNode, noteNode, snapshotMetaNode, metricConfig, timeframeKey, ) { const targetServerSlug = result.value?.data?.server_slug || activeServerSlug; const resolvedTimeframeKey = result.value?.data?.timeframe || timeframeKey; valueHeadingNode.textContent = metricConfig.valueHeading; if (result.status !== "fulfilled") { titleNode.textContent = buildLeaderboardTitle( metricConfig, targetServerSlug, resolvedTimeframeKey, ); noteNode.textContent = "No se pudo leer el snapshot precalculado para esta metrica."; setSnapshotMeta(snapshotMetaNode, "Error al leer el snapshot del ranking."); setState( stateNode, `No se pudo cargar el ranking ${getLeaderboardTimeframeConfig(resolvedTimeframeKey).shortLabel}.`, true, ); tableNode.hidden = true; return; } const payload = result.value?.data; titleNode.textContent = buildLeaderboardTitle( metricConfig, payload?.server_slug, payload?.timeframe || resolvedTimeframeKey, ); noteNode.textContent = buildWeeklyWindowNote(payload); setSnapshotMeta( snapshotMetaNode, buildSnapshotMetaText(payload, "Snapshot del ranking pendiente de generacion."), ); if (!payload?.found) { setState( stateNode, buildLeaderboardEmptyMessage( metricConfig, targetServerSlug, payload?.timeframe || resolvedTimeframeKey, ), ); tableNode.hidden = true; return; } const items = payload?.items; if (!Array.isArray(items) || items.length === 0) { setState( stateNode, buildLeaderboardEmptyMessage( metricConfig, targetServerSlug, payload?.timeframe || resolvedTimeframeKey, ), ); tableNode.hidden = true; return; } bodyNode.innerHTML = items .map( (item) => ` #${escapeHtml(item.ranking_position)} ${escapeHtml(item.player?.name || "Jugador no identificado")} ${escapeHtml(formatNumber(item.metric_value))} ${escapeHtml(formatNumber(item.matches_considered))} `, ) .join(""); stateNode.hidden = true; tableNode.hidden = false; } function hydrateRecentMatches(result, stateNode, listNode, snapshotMetaNode) { const emptyState = getHistoricalEmptyState(activeServerSlug); if (result.status !== "fulfilled") { setState(stateNode, "No se pudieron cargar las partidas recientes.", true); setSnapshotMeta(snapshotMetaNode, "Error al leer el snapshot de partidas."); return; } const payload = result.value?.data; setSnapshotMeta( snapshotMetaNode, buildSnapshotMetaText(payload, "Snapshot de partidas pendiente de generacion."), ); if (!payload?.found) { setState(stateNode, emptyState.recentMessage); return; } const items = payload?.items; if (!Array.isArray(items) || items.length === 0) { setState(stateNode, emptyState.recentMessage); return; } listNode.innerHTML = items.map((item) => renderRecentMatchCard(item)).join(""); stateNode.hidden = true; } function renderRecentMatchCard(item) { const mapName = item.map?.pretty_name || item.map?.name || "Mapa no disponible"; return `

Partida ${escapeHtml(item.match_id || "sin id")}

${escapeHtml(mapName)}

${escapeHtml(formatMatchResult(item.result))}

Servidor

${escapeHtml(item.server?.name || "Servidor no disponible")}

Cierre

${escapeHtml(formatTimestamp(item.closed_at))}

Jugadores

${escapeHtml(formatNumber(item.player_count))}

Marcador

${escapeHtml(formatScore(item.result))}
`; } function renderSummaryLoading(summaryNode) { summaryNode.innerHTML = renderSummaryCard("Estado", "Cargando datos historicos"); } function renderSummaryError(summaryNode) { summaryNode.innerHTML = renderSummaryCard("Estado", "Error al cargar el resumen"); } function renderSummaryEmpty(summaryNode, message = "Sin datos historicos suficientes") { summaryNode.innerHTML = renderSummaryCard("Estado", message); } function renderSummaryCard(label, value) { return `

${escapeHtml(label)}

${escapeHtml(value)}
`; } function setState(node, message, isError = false) { node.textContent = message; node.hidden = false; node.classList.toggle("is-error", isError); } function setRangeBadge(node, label, isFresh) { node.textContent = label; node.classList.toggle("status-chip--ok", isFresh); node.classList.toggle("status-chip--fallback", !isFresh); } function setSnapshotMeta(node, message) { node.textContent = message; } function syncActiveButtons(buttons, activeServerSlug) { buttons.forEach((button) => { button.classList.toggle( "is-active", button.dataset.serverSlug === activeServerSlug, ); }); } function syncLeaderboardTabs(buttons, activeMetric) { buttons.forEach((button) => { const isActive = button.dataset.leaderboardMetric === activeMetric; button.classList.toggle("is-active", isActive); button.setAttribute("aria-selected", String(isActive)); }); } function syncLeaderboardTimeframes(buttons, activeTimeframe) { buttons.forEach((button) => { const isActive = button.dataset.leaderboardTimeframe === activeTimeframe; button.classList.toggle("is-active", isActive); button.setAttribute("aria-selected", String(isActive)); }); } function normalizeServerSlug(rawValue) { const normalized = typeof rawValue === "string" ? rawValue.trim() : ""; if (HISTORICAL_SERVER_SLUGS.includes(normalized)) { return normalized; } return DEFAULT_HISTORICAL_SERVER; } function getHistoricalServerLabel(serverSlug) { return ( HISTORICAL_SERVERS.find((server) => server.slug === serverSlug)?.label || HISTORICAL_SERVERS[0].label ); } function normalizeLeaderboardMetric(rawValue) { const normalized = typeof rawValue === "string" ? rawValue.trim() : ""; if (LEADERBOARD_METRICS.some((metric) => metric.key === normalized)) { return normalized; } return DEFAULT_LEADERBOARD_METRIC; } function normalizeLeaderboardTimeframe(rawValue) { const normalized = typeof rawValue === "string" ? rawValue.trim() : ""; if (LEADERBOARD_TIMEFRAMES.some((timeframe) => timeframe.key === normalized)) { return normalized; } return DEFAULT_LEADERBOARD_TIMEFRAME; } function getLeaderboardMetricConfig(metricKey) { return ( LEADERBOARD_METRICS.find((metric) => metric.key === metricKey) || LEADERBOARD_METRICS[0] ); } function getLeaderboardTimeframeConfig(timeframeKey) { return ( LEADERBOARD_TIMEFRAMES.find((timeframe) => timeframe.key === timeframeKey) || LEADERBOARD_TIMEFRAMES[0] ); } function buildSummarySnapshotKey(serverSlug) { return `summary:${serverSlug}`; } function buildRecentMatchesSnapshotKey(serverSlug) { return `recent:${serverSlug}`; } function buildLeaderboardSnapshotKey(serverSlug, timeframeKey, metricKey) { return `leaderboard:${serverSlug}:${timeframeKey}:${metricKey}`; } function buildRangeLabel(start, end) { if (!start && !end) { return ""; } return `${formatTimestamp(start)} a ${formatTimestamp(end)}`; } function buildCoverageBadgeLabel(coverage, timeRange, serverSlug) { const rangeStart = coverage?.first_match_at || timeRange?.start; const rangeEnd = coverage?.last_match_at || timeRange?.end; if (!rangeStart && !rangeEnd) { return "Sin cobertura registrada"; } if (coverage?.status === "under-week") { return "Cobertura inicial"; } if (coverage?.status === "week-plus") { return "Cobertura historica"; } return "Periodo registrado"; } function buildCoveragePeriodLabel(coverage, timeRange, serverSlug) { const start = coverage?.first_match_at || timeRange?.start; const end = coverage?.last_match_at || timeRange?.end; if (start && end) { return `Desde ${formatDateOnly(start)} hasta ${formatDateOnly(end)}`; } if (start) { return `Desde ${formatDateOnly(start)}`; } if (end) { return `Hasta ${formatDateOnly(end)}`; } return "Sin cobertura registrada"; } function buildSummaryNote(summaryBasis, weeklyWindowDays, coverage, serverSlug) { const basisLabel = summaryBasis === "snapshot-precomputed" ? "el snapshot precalculado del historico local" : "el historico persistido disponible"; const status = coverage?.status; void weeklyWindowDays; void serverSlug; if (status === "under-week") { return `Este bloque resume ${basisLabel}. La cobertura registrada todavia es inicial y puede crecer en los proximos dias.`; } if (serverSlug === "all-servers") { return `Resumen global servido desde ${basisLabel} y combinado solo con los servidores que ya tienen historico disponible.`; } return `Resumen servido desde ${basisLabel}.`; } function buildWeeklyWindowNote(payload) { if (!payload?.found) { const timeframeLabel = getLeaderboardTimeframeConfig( payload?.timeframe || activeLeaderboardTimeframe, ).shortLabel; return `No existe un snapshot ${timeframeLabel} listo para esta metrica en el alcance activo.`; } const start = formatTimestamp(payload?.window_start); const end = formatTimestamp(payload?.window_end); const windowLabel = payload?.window_label || (payload?.timeframe === "monthly" ? "Mes activo" : "Semana activa"); if (payload?.uses_fallback) { const currentPeriodMatches = payload?.current_week_closed_matches ?? payload?.current_month_closed_matches ?? payload?.sufficient_sample?.current_week_closed_matches ?? payload?.sufficient_sample?.current_month_closed_matches ?? 0; return `${windowLabel}: ${start} a ${end}. Se muestra el ultimo periodo cerrado porque el actual todavia solo suma ${formatNumber(currentPeriodMatches)} cierres.`; } return `${windowLabel}: ${start} a ${end}.`; } function buildLeaderboardTitle(metricConfig, serverSlug, timeframeKey) { const timeframeLabel = getLeaderboardTimeframeConfig(timeframeKey).label; return `${metricConfig.title} ${timeframeLabel} - ${getHistoricalServerLabel(serverSlug)}`; } function buildRecentMatchesNote(serverSlug) { if (serverSlug === "all-servers") { return "Lista de cierres ya registrados para los servidores con historico disponible."; } return `Lista de cierres ya registrados para ${getHistoricalServerLabel(serverSlug)}.`; } function buildSnapshotMetaText(payload, missingMessage) { if (!payload?.generated_at) { return missingMessage; } const parts = [ payload.is_stale ? `Snapshot posiblemente desactualizado: ${formatTimestamp(payload.generated_at)}` : `Snapshot generado: ${formatTimestamp(payload.generated_at)}`, ]; const sourceRangeLabel = buildRangeLabel( payload?.source_range_start, payload?.source_range_end, ); if (sourceRangeLabel) { parts.push(`Fuente: ${sourceRangeLabel}`); } return parts.join(" | "); } function formatTopMaps(topMaps) { if (!Array.isArray(topMaps) || topMaps.length === 0) { return "Sin mapas frecuentes"; } return topMaps .map((item) => `${item.map_name} (${formatNumber(item.matches_count)})`) .join(" / "); } function formatDateOnly(timestamp) { if (!timestamp) { return "Fecha no disponible"; } const value = new Date(timestamp); if (Number.isNaN(value.getTime())) { return "Fecha no disponible"; } return new Intl.DateTimeFormat("es-ES", { dateStyle: "medium", }).format(value); } function formatMatchResult(result) { const winner = result?.winner; if (winner === "allies") { return "Victoria Aliada"; } if (winner === "axis") { return "Victoria Axis"; } if (winner === "draw") { return "Empate"; } return "Resultado parcial"; } function formatScore(result) { const alliedScore = Number.isFinite(result?.allied_score) ? result.allied_score : "-"; const axisScore = Number.isFinite(result?.axis_score) ? result.axis_score : "-"; return `${alliedScore} - ${axisScore}`; } function formatNumber(value) { const parsedValue = Number(value); if (!Number.isFinite(parsedValue)) { return "0"; } return new Intl.NumberFormat("es-ES").format(parsedValue); } function formatTimestamp(timestamp) { if (!timestamp) { return "Fecha no disponible"; } const value = new Date(timestamp); if (Number.isNaN(value.getTime())) { return "Fecha no disponible"; } return new Intl.DateTimeFormat("es-ES", { dateStyle: "short", timeStyle: "short", }).format(value); } async function getCachedJson(cache, pendingCache, key, url) { const cachedPayload = readCachedPayload(cache, key); if (cachedPayload) { return cachedPayload; } if (pendingCache.has(key)) { return pendingCache.get(key); } const request = fetchJson(url) .then((payload) => { writeCachedPayload(cache, key, payload); pendingCache.delete(key); return payload; }) .catch((error) => { pendingCache.delete(key); throw error; }); pendingCache.set(key, request); return request; } function readCachedPayload(cache, key) { const entry = cache.get(key); if (!entry) { return null; } if (entry.expiresAt <= Date.now()) { cache.delete(key); return null; } return entry.payload; } function writeCachedPayload(cache, key, payload) { cache.set(key, { payload, expiresAt: Date.now() + resolveSnapshotCacheTtl(payload), }); } function resolveSnapshotCacheTtl(payload) { const data = payload?.data; if (!data) { return NEGATIVE_SNAPSHOT_CACHE_TTL_MS; } if (data.snapshot_status === "missing" || data.found === false) { return NEGATIVE_SNAPSHOT_CACHE_TTL_MS; } if (data.is_stale) { return STALE_SNAPSHOT_CACHE_TTL_MS; } return SNAPSHOT_CACHE_TTL_MS; } async function settlePromise(promise) { try { const value = await promise; return { status: "fulfilled", value }; } catch (reason) { return { status: "rejected", reason }; } } async function fetchJson(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`Request failed with ${response.status}`); } return response.json(); } function escapeHtml(value) { return String(value) .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function buildLeaderboardEmptyMessage(metricConfig, serverSlug, timeframeKey) { void serverSlug; const timeframeLabel = getLeaderboardTimeframeConfig(timeframeKey).shortLabel; return metricConfig.emptyMessage.replace("esta ventana", `esta ventana ${timeframeLabel}`); } function getHistoricalEmptyState(serverSlug) { void serverSlug; return { rangeLabel: "Sin cobertura registrada", summaryMessage: "Sin datos historicos suficientes", summaryNote: "Todavia no existe un snapshot de resumen listo para el alcance seleccionado.", recentMessage: "Todavia no hay partidas recientes disponibles.", }; }