www/console.php
2026-05-11 23:31:48 +01:00

263 lines
7.1 KiB
PHP

<?php
declare(strict_types=1);
require __DIR__ . "/api/db.php";
// ---------- SORT ----------
$sort = $_GET['sort'] ?? 'state';
$orderSql = ($sort === 'uid')
? "ORDER BY uid ASC"
: "ORDER BY online DESC, uid ASC";
// ---------- OFFLINE AUTOMÁTICO (20s) ----------
$pdo->query("
UPDATE contadores
SET online = 0
WHERE TIMESTAMPDIFF(SECOND, last_seen, NOW()) > 20
");
// ---------- BLOQUEIO ----------
if (isset($_GET['toggle'])) {
$uid = $_GET['toggle'];
$pdo->prepare("UPDATE contadores SET blocked = 1 - blocked WHERE uid = ?")
->execute([$uid]);
header("Location: console.php");
exit;
}
// ---------- PRIMEIRA QUERY (para totals) ----------
$sql = "
SELECT uid, entradas, saidas, online, blocked,
last_seen,
TIMESTAMPDIFF(SECOND, last_seen, NOW()) AS ago
FROM contadores
$orderSql
";
$stmt = $pdo->query($sql);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Totais
$sumEntradas = 0;
$sumSaidas = 0;
$onlineCount = 0;
$offlineCount = 0;
foreach ($rows as $r) {
$sumEntradas += (int)$r['entradas'];
$sumSaidas += (int)$r['saidas'];
if ($r['online'] == 1) $onlineCount++;
else $offlineCount++;
}
?>
<!doctype html>
<html lang="pt" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<title>Painel de Contadores</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<style>
body {
background-color: #0f0f0f;
color: #e0e0e0;
}
.card {
background-color: #1a1a1a;
border: 1px solid #333;
}
.table-striped > tbody > tr:nth-of-type(odd) {
background-color: #1c1c1c;
}
.table-striped > tbody > tr:nth-of-type(even) {
background-color: #151515;
}
.table thead {
background-color: #222;
}
h1 {
color: #f0f0f0;
}
a {
color: #58a6ff;
}
a:hover {
color: #8cc5ff;
}
.btn-danger {
background-color: #a32424;
border-color: #6b0000;
}
.btn-success {
background-color: #1f6f33;
border-color: #0f4d1f;
}
</style>
</head>
<body>
<div class="container py-4">
<h1 class="mb-4">Painel de Contadores</h1>
<!-- RESUMO -->
<div class="row mb-4 text-center">
<div class="col-md-3">
<div class="p-3 card rounded">
<h4><?= $onlineCount ?></h4>
<div class="text-success fw-bold">Online</div>
</div>
</div>
<div class="col-md-3">
<div class="p-3 card rounded">
<h4><?= $offlineCount ?></h4>
<div class="text-danger fw-bold">Offline</div>
</div>
</div>
<div class="col-md-3">
<div class="p-3 card rounded">
<h4><?= $sumEntradas ?></h4>
<div class="fw-bold">Entradas</div>
</div>
</div>
<div class="col-md-3">
<div class="p-3 card rounded">
<h4><?= $sumSaidas ?></h4>
<div class="fw-bold">Saídas</div>
</div>
</div>
</div>
<!-- TABELA -->
<div class="card shadow-sm">
<div class="card-body">
<table class="table table-striped table-hover align-middle">
<thead>
<tr>
<th><a href="?sort=uid">UID</a></th>
<th>Entradas</th>
<th>Saídas</th>
<th><a href="?sort=state">Estado</a></th>
<th>Último Sinal</th>
<th>Ações</th>
</tr>
</thead>
<tbody id="tabela_corpo">
<?php foreach ($rows as $r): ?>
<?php
$online = (int)$r['online'] === 1;
$blocked = (int)$r['blocked'] === 1;
if ($blocked) {
$state = '<span class="badge bg-danger">Bloqueado</span>';
} elseif ($online) {
$state = '<span class="badge bg-success">Online</span>';
} else {
$state = '<span class="badge bg-secondary">Offline</span>';
}
$ago = (int)$r['ago'];
$lastSeen = ($ago < 60)
? "{$ago}s"
: round($ago / 60) . "m";
?>
<tr>
<td class="fw-bold"><?= htmlspecialchars($r['uid']) ?></td>
<td><?= $r['entradas'] ?></td>
<td><?= $r['saidas'] ?></td>
<td><?= $state ?></td>
<td><?= $lastSeen ?></td>
<td>
<a href="?toggle=<?= urlencode($r['uid']) ?>"
class="btn btn-sm <?= $blocked ? 'btn-success' : 'btn-danger' ?>">
<?= $blocked ? 'Desbloquear' : 'Bloquear' ?>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- ------------ AUTO-REFRESH AJAX ---------------- -->
<script>
async function refreshTable() {
try {
const res = await fetch('/api/contadores_json.php?x=' + Date.now());
const data = await res.json();
let html = "";
for (const r of data.rows) {
// Estado
// Estado baseado APENAS no valor online vindo da DB
let state = "";
if (r.blocked == 1) {
state = '<span class="badge bg-danger">Bloqueado</span>';
}
else if (r.online == 1) {
state = '<span class="badge bg-success">Online</span>';
}
else {
state = '<span class="badge bg-secondary">Offline</span>';
}
// Tempo
let lastSeen = (r.ago < 60) ? `${r.ago}s` : `${Math.round(r.ago/60)}m`;
// Animação pulse quando r.ago <= 2
let pulseClass = (r.ago <= 2 && r.online == 1) ? "pulse-cell" : "";
html += `
<tr class="${pulseClass}">
<td class="fw-bold">${r.uid}</td>
<td>${r.entradas}</td>
<td>${r.saidas}</td>
<td>${state}</td>
<td>${lastSeen}</td>
<td>
<a href="?toggle=${r.uid}"
class="btn btn-sm ${r.blocked ? 'btn-success' : 'btn-danger'}">
${r.blocked ? 'Desbloquear' : 'Bloquear'}
</a>
</td>
</tr>
`;
}
document.getElementById("tabela_corpo").innerHTML = html;
// Atualizar totais
document.getElementById("total_online").innerText = data.online;
document.getElementById("total_offline").innerText = data.offline;
document.getElementById("total_entradas").innerText = data.entradas;
document.getElementById("total_saidas").innerText = data.saidas;
} catch (err) {
console.error("Erro AJAX:", err);
}
}
setInterval(refreshTable, 2000);
refreshTable();
</script>
</body>
</html>