|
@@ -40,6 +40,14 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="col-md-2">
|
|
<div class="col-md-2">
|
|
|
|
|
+ <div class="card card-kpi text-bg-warning">
|
|
|
|
|
+ <div class="card-body text-center">
|
|
|
|
|
+ <h6>Actives</h6>
|
|
|
|
|
+ <h2 id="kpiActiveShards">–</h2>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-2">
|
|
|
<div class="card card-kpi text-bg-success">
|
|
<div class="card card-kpi text-bg-success">
|
|
|
<div class="card-body text-center">
|
|
<div class="card-body text-center">
|
|
|
<h6>Players</h6>
|
|
<h6>Players</h6>
|
|
@@ -55,14 +63,6 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-2">
|
|
|
|
|
- <div class="card card-kpi text-bg-warning">
|
|
|
|
|
- <div class="card-body text-center">
|
|
|
|
|
- <h6>Active</h6>
|
|
|
|
|
- <h2 id="kpiActiveShards">–</h2>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
@@ -156,22 +156,26 @@
|
|
|
<!-- TAB: PLAYERS -->
|
|
<!-- TAB: PLAYERS -->
|
|
|
<!-- ===================================================== -->
|
|
<!-- ===================================================== -->
|
|
|
<div class="tab-pane fade" id="tabPlayers">
|
|
<div class="tab-pane fade" id="tabPlayers">
|
|
|
|
|
+ <div class="d-flex justify-content-between align-items-center mb-2">
|
|
|
|
|
+ <h2 class="section-title mb-0">Players (Global)</h2>
|
|
|
|
|
+ <button
|
|
|
|
|
+ class="btn btn-sm btn-outline-secondary"
|
|
|
|
|
+ onclick="loadPlayersGlobal()">
|
|
|
|
|
+ 🔄 Refresh
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
-
|
|
|
|
|
- <h2 class="section-title">Players (Global)</h2>
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- <table class="table table-striped">
|
|
|
|
|
|
|
+ <table class="table table-striped">
|
|
|
<thead class="table-dark">
|
|
<thead class="table-dark">
|
|
|
- <tr>
|
|
|
|
|
- <th>Player</th>
|
|
|
|
|
- <th>Global Score</th>
|
|
|
|
|
- <th>Shards</th>
|
|
|
|
|
- <th>Actions</th>
|
|
|
|
|
- </tr>
|
|
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th>Player</th>
|
|
|
|
|
+ <th>Global Score</th>
|
|
|
|
|
+ <th>Shards</th>
|
|
|
|
|
+ <th>Actions</th>
|
|
|
|
|
+ </tr>
|
|
|
</thead>
|
|
</thead>
|
|
|
<tbody id="tablePlayers"></tbody>
|
|
<tbody id="tablePlayers"></tbody>
|
|
|
- </table>
|
|
|
|
|
|
|
+ </table>
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
@@ -182,6 +186,10 @@
|
|
|
<!-- ===================================================== -->
|
|
<!-- ===================================================== -->
|
|
|
<div class="tab-pane fade" id="tabRankings">
|
|
<div class="tab-pane fade" id="tabRankings">
|
|
|
|
|
|
|
|
|
|
+ <div class="d-flex justify-content-between align-items-center mb-2">
|
|
|
|
|
+ <h2 class="section-title mb-0">Rankings by</h2>
|
|
|
|
|
+ <button class="btn btn-sm btn-outline-secondary" onclick="refreshRankings()">🔄 Refresh</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
<ul class="nav nav-pills mb-3">
|
|
<ul class="nav nav-pills mb-3">
|
|
|
<li class="nav-item">
|
|
<li class="nav-item">
|
|
@@ -192,13 +200,36 @@
|
|
|
</li>
|
|
</li>
|
|
|
</ul>
|
|
</ul>
|
|
|
|
|
|
|
|
-
|
|
|
|
|
<div class="tab-content">
|
|
<div class="tab-content">
|
|
|
<div class="tab-pane fade show active" id="rankGlobal">
|
|
<div class="tab-pane fade show active" id="rankGlobal">
|
|
|
Ranking global de jugadores (agregación multi‑shard)
|
|
Ranking global de jugadores (agregación multi‑shard)
|
|
|
|
|
+
|
|
|
|
|
+ <table class="table table-sm table-striped">
|
|
|
|
|
+ <thead class="table-dark">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th>#</th>
|
|
|
|
|
+ <th>Player</th>
|
|
|
|
|
+ <th>Score</th>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </thead>
|
|
|
|
|
+ <tbody id="tableRankingGlobal"></tbody>
|
|
|
|
|
+ </table>
|
|
|
|
|
+
|
|
|
</div>
|
|
</div>
|
|
|
<div class="tab-pane fade" id="rankShard">
|
|
<div class="tab-pane fade" id="rankShard">
|
|
|
Ranking por shard usando <code>shard_player_stats</code>
|
|
Ranking por shard usando <code>shard_player_stats</code>
|
|
|
|
|
+
|
|
|
|
|
+ <table class="table table-sm table-striped">
|
|
|
|
|
+ <thead class="table-dark">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th>#</th>
|
|
|
|
|
+ <th>Player</th>
|
|
|
|
|
+ <th>Score</th>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </thead>
|
|
|
|
|
+ <tbody id="tableRankingShard"></tbody>
|
|
|
|
|
+ </table>
|
|
|
|
|
+
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -220,27 +251,53 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
- <table class="table table-sm table-hover">
|
|
|
|
|
- <thead>
|
|
|
|
|
- <tr>
|
|
|
|
|
- <th>Time</th>
|
|
|
|
|
- <th>Shard</th>
|
|
|
|
|
- <th>Player</th>
|
|
|
|
|
- <th>Action</th>
|
|
|
|
|
- <th>Δ</th>
|
|
|
|
|
- <th>Winner</th>
|
|
|
|
|
- </tr>
|
|
|
|
|
|
|
+ <div class="d-flex gap-2 mb-2">
|
|
|
|
|
+ <input type="text" id="filterShard" class="form-control form-control-sm" placeholder="Shard ID">
|
|
|
|
|
+
|
|
|
|
|
+ <input type="text" id="filterPlayer" class="form-control form-control-sm" placeholder="Player ID">
|
|
|
|
|
+
|
|
|
|
|
+ <select id="filterAction" class="form-select form-select-sm">
|
|
|
|
|
+ <option value="">All actions</option>
|
|
|
|
|
+ <option value="EXPLORE">EXPLORE</option>
|
|
|
|
|
+ <option value="GATHER">GATHER</option>
|
|
|
|
|
+ <option value="FIGHT">FIGHT</option>
|
|
|
|
|
+ <option value="REST">REST</option>
|
|
|
|
|
+ </select>
|
|
|
|
|
+
|
|
|
|
|
+ <button class="btn btn-sm btn-outline-primary" onclick="loadEvents(0)">
|
|
|
|
|
+ Apply
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <table class="table table-sm table-striped table-hover">
|
|
|
|
|
+ <thead class="table-dark">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th>Time</th>
|
|
|
|
|
+ <th>Shard</th>
|
|
|
|
|
+ <th>Player</th>
|
|
|
|
|
+ <th>Action</th>
|
|
|
|
|
+ <th>Δ Score</th>
|
|
|
|
|
+ </tr>
|
|
|
</thead>
|
|
</thead>
|
|
|
<tbody id="tableEvents"></tbody>
|
|
<tbody id="tableEvents"></tbody>
|
|
|
- </table>
|
|
|
|
|
|
|
+ </table>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="d-flex justify-content-between align-items-center">
|
|
|
|
|
+ <button class="btn btn-sm btn-outline-secondary" onclick="prevEventsPage()">
|
|
|
|
|
+ ◀ Prev
|
|
|
|
|
+ </button>
|
|
|
|
|
|
|
|
|
|
+ <span id="eventsPageInfo"></span>
|
|
|
|
|
|
|
|
|
|
+ <button class="btn btn-sm btn-outline-secondary" onclick="nextEventsPage()">
|
|
|
|
|
+ Next ▶
|
|
|
|
|
+ </button>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
-
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
@@ -260,6 +317,8 @@
|
|
|
let selectedShardId = null;
|
|
let selectedShardId = null;
|
|
|
let snapshotChart = null;
|
|
let snapshotChart = null;
|
|
|
let eventsChart = null;
|
|
let eventsChart = null;
|
|
|
|
|
+ let eventsPage = 0;
|
|
|
|
|
+ let eventsTotalPages = 0;
|
|
|
|
|
|
|
|
async function refreshShardsTab() {
|
|
async function refreshShardsTab() {
|
|
|
try {
|
|
try {
|
|
@@ -441,6 +500,106 @@
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* FUNCIONES PARA ACTUALIZAR PESTAÑA RANKINGS */
|
|
|
|
|
+ async function loadRankingGlobal() {
|
|
|
|
|
+ const res = await fetch(`${API_BASE}/master/rankings/global`);
|
|
|
|
|
+ const ranking = await res.json();
|
|
|
|
|
+
|
|
|
|
|
+ const tbody = document.getElementById("tableRankingGlobal");
|
|
|
|
|
+ tbody.innerHTML = "";
|
|
|
|
|
+
|
|
|
|
|
+ ranking.forEach((p, index) => {
|
|
|
|
|
+ const tr = document.createElement("tr");
|
|
|
|
|
+ tr.innerHTML = `
|
|
|
|
|
+ <td>${index + 1}</td>
|
|
|
|
|
+ <td>${p.playerId}</td>
|
|
|
|
|
+ <td>${p.score}</td>
|
|
|
|
|
+ `;
|
|
|
|
|
+ tbody.appendChild(tr);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async function loadRankingShard() {
|
|
|
|
|
+ if (!selectedShardId) return;
|
|
|
|
|
+
|
|
|
|
|
+ const res = await fetch(
|
|
|
|
|
+ `${API_BASE}/master/rankings/shards/${selectedShardId}`
|
|
|
|
|
+ );
|
|
|
|
|
+ const ranking = await res.json();
|
|
|
|
|
+
|
|
|
|
|
+ const tbody = document.getElementById("tableRankingShard");
|
|
|
|
|
+ tbody.innerHTML = "";
|
|
|
|
|
+
|
|
|
|
|
+ ranking.forEach((p, index) => {
|
|
|
|
|
+ const tr = document.createElement("tr");
|
|
|
|
|
+ tr.innerHTML = `
|
|
|
|
|
+ <td>${index + 1}</td>
|
|
|
|
|
+ <td>${p.playerId}</td>
|
|
|
|
|
+ <td>${p.score}</td>
|
|
|
|
|
+ `;
|
|
|
|
|
+ tbody.appendChild(tr);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function refreshRankings(){
|
|
|
|
|
+ loadRankingGlobal();
|
|
|
|
|
+ loadRankingShard();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* FUNCIONES PARA ACTUALIZAR LA PESTAÑA EVENTS */
|
|
|
|
|
+ async function loadEvents(page) {
|
|
|
|
|
+
|
|
|
|
|
+ const shard = document.getElementById("filterShard").value;
|
|
|
|
|
+ const player = document.getElementById("filterPlayer").value;
|
|
|
|
|
+ const action = document.getElementById("filterAction").value;
|
|
|
|
|
+
|
|
|
|
|
+ const params = new URLSearchParams({
|
|
|
|
|
+ page: page,
|
|
|
|
|
+ size: 15,
|
|
|
|
|
+ shard: shard,
|
|
|
|
|
+ player: player,
|
|
|
|
|
+ action: action
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const res = await fetch(
|
|
|
|
|
+ `${API_BASE}/master/events?${params}`
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ const data = await res.json();
|
|
|
|
|
+
|
|
|
|
|
+ eventsPage = data.number;
|
|
|
|
|
+ eventsTotalPages = data.totalPages;
|
|
|
|
|
+
|
|
|
|
|
+ const tbody = document.getElementById("tableEvents");
|
|
|
|
|
+ tbody.innerHTML = "";
|
|
|
|
|
+
|
|
|
|
|
+ data.content.forEach(e => {
|
|
|
|
|
+ const tr = document.createElement("tr");
|
|
|
|
|
+ tr.innerHTML = `
|
|
|
|
|
+ <td>${new Date(e.timestamp).toLocaleTimeString()}</td>
|
|
|
|
|
+ <td>${e.shardId}</td>
|
|
|
|
|
+ <td>${e.playerId}</td>
|
|
|
|
|
+ <td>${e.action}</td>
|
|
|
|
|
+ <td>${e.pointsDelta}</td>
|
|
|
|
|
+ `;
|
|
|
|
|
+ tbody.appendChild(tr);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById("eventsPageInfo").textContent =
|
|
|
|
|
+ `Page ${eventsPage + 1} / ${eventsTotalPages}`;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function nextEventsPage() {
|
|
|
|
|
+ if (eventsPage + 1 < eventsTotalPages) {
|
|
|
|
|
+ loadEvents(eventsPage + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function prevEventsPage() {
|
|
|
|
|
+ if (eventsPage > 0) {
|
|
|
|
|
+ loadEvents(eventsPage - 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/* FUNCIONES AUXILIARES */
|
|
/* FUNCIONES AUXILIARES */
|
|
|
function heartbeatColor(lastHeartbeat) {
|
|
function heartbeatColor(lastHeartbeat) {
|
|
@@ -479,6 +638,17 @@
|
|
|
loadPlayersGlobal();
|
|
loadPlayersGlobal();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ document.querySelector('button[data-bs-target="#tabRankings"]')
|
|
|
|
|
+ .addEventListener("shown.bs.tab", () => {
|
|
|
|
|
+ refreshRankings();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.querySelector('button[data-bs-target="#tabEvents"]')
|
|
|
|
|
+ .addEventListener("shown.bs.tab", () => {
|
|
|
|
|
+ loadEvents(0);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|