356 lines
7.9 KiB
PHP
356 lines
7.9 KiB
PHP
<?php
|
|
require_once __DIR__ . "/../config/db.php";
|
|
include __DIR__ . "/../partials/header.php";
|
|
include __DIR__ . "/../partials/sidebar.php";
|
|
|
|
// stats
|
|
$stats = [
|
|
"total_resi" => 0,
|
|
"resi_valid" => 0,
|
|
"resi_invalid" => 0,
|
|
"log_today" => 0,
|
|
"log_ok" => 0,
|
|
"log_fail" => 0
|
|
];
|
|
|
|
$r = $conn->query("SELECT COUNT(*) c, SUM(status='valid') v, SUM(status='invalid') i FROM resi");
|
|
|
|
if ($r && $row = $r->fetch_assoc()) {
|
|
$stats["total_resi"] = (int)($row["c"] ?? 0);
|
|
$stats["resi_valid"] = (int)($row["v"] ?? 0);
|
|
$stats["resi_invalid"] = (int)($row["i"] ?? 0);
|
|
}
|
|
|
|
$today = date("Y-m-d");
|
|
|
|
$r2 = $conn->query("
|
|
SELECT
|
|
COUNT(*) c,
|
|
SUM(status='berhasil') ok,
|
|
SUM(status='gagal') fail
|
|
FROM log_akses
|
|
WHERE DATE(created_at)='{$today}'
|
|
");
|
|
|
|
if ($r2 && $row2 = $r2->fetch_assoc()) {
|
|
$stats["log_today"] = (int)($row2["c"] ?? 0);
|
|
$stats["log_ok"] = (int)($row2["ok"] ?? 0);
|
|
$stats["log_fail"] = (int)($row2["fail"] ?? 0);
|
|
}
|
|
|
|
// latest logs
|
|
$latest = $conn->query("
|
|
SELECT
|
|
id,
|
|
nomor_resi,
|
|
status,
|
|
pesan,
|
|
created_at,
|
|
foto_path
|
|
FROM log_akses
|
|
ORDER BY created_at DESC
|
|
LIMIT 8
|
|
");
|
|
?>
|
|
|
|
<div class="row g-3">
|
|
|
|
<div class="col-12">
|
|
<div class="card-glass p-4">
|
|
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
|
|
|
<div>
|
|
<h3 class="mb-1">Dashboard</h3>
|
|
<div class="text-muted">
|
|
Ringkasan sistem monitoring paket & autentikasi resi.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
|
|
<a class="btn btn-accent px-3 py-2 text-white"
|
|
href="/smart-drop-point/public/resi_add.php">
|
|
<i class="bi bi-plus-circle me-2"></i>
|
|
Tambah Resi
|
|
</a>
|
|
|
|
<a class="btn btn-danger px-3 py-2"
|
|
href="/smart-drop-point/public/manual_open.php"
|
|
onclick="return confirm('Yakin ingin membuka pintu secara manual?')">
|
|
🔓 Buka Pintu
|
|
</a>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<div class="card-glass p-3">
|
|
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
|
|
<div>
|
|
<div class="text-muted">Total Resi Terdaftar</div>
|
|
<div class="fs-3 fw-bold">
|
|
<?= $stats["total_resi"] ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="badge badge-soft p-2">
|
|
<i class="bi bi-upc-scan fs-4"></i>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="mt-2 small text-muted">
|
|
Valid: <?= $stats["resi_valid"] ?>
|
|
•
|
|
Invalid: <?= $stats["resi_invalid"] ?>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<div class="card-glass p-3">
|
|
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
|
|
<div>
|
|
<div class="text-muted">Total Scan Hari Ini</div>
|
|
<div class="fs-3 fw-bold">
|
|
<?= $stats["log_today"] ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="badge badge-soft p-2">
|
|
<i class="bi bi-clock-history fs-4"></i>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="mt-2 small text-muted">
|
|
Berhasil: <?= $stats["log_ok"] ?>
|
|
•
|
|
Gagal: <?= $stats["log_fail"] ?>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<div class="card-glass p-3">
|
|
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
|
|
<div>
|
|
<div class="text-muted">Status Server</div>
|
|
|
|
<div class="fs-4 fw-bold">
|
|
<i class="bi bi-check-circle-fill me-2" style="color:#22c55e"></i>
|
|
Online
|
|
</div>
|
|
</div>
|
|
|
|
<div class="badge badge-soft p-2">
|
|
<i class="bi bi-hdd-network fs-4"></i>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="mt-2 small text-muted">
|
|
Mode: Localhost (XAMPP)
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
|
|
<div class="card-glass p-3">
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
|
|
<div class="fw-semibold">
|
|
Log Terbaru
|
|
</div>
|
|
|
|
<a class="text-decoration-none"
|
|
href="/smart-drop-point/public/log.php">
|
|
Lihat semua
|
|
<i class="bi bi-arrow-right"></i>
|
|
</a>
|
|
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
|
|
<table class="table table-dark table-hover align-middle mb-0">
|
|
|
|
<thead>
|
|
<tr>
|
|
<th>Waktu</th>
|
|
<th>Nomor Resi</th>
|
|
<th>Status</th>
|
|
<th>Pesan</th>
|
|
<th>Foto</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
<?php if ($latest && $latest->num_rows > 0): ?>
|
|
|
|
<?php while ($x = $latest->fetch_assoc()): ?>
|
|
|
|
<tr>
|
|
|
|
<td class="text-muted">
|
|
<?= htmlspecialchars($x["created_at"]) ?>
|
|
</td>
|
|
|
|
<td class="fw-semibold">
|
|
<?= htmlspecialchars($x["nomor_resi"] ?? "-") ?>
|
|
</td>
|
|
|
|
<td>
|
|
<?php if ($x["status"] === "berhasil"): ?>
|
|
|
|
<span class="badge text-bg-success">
|
|
Berhasil
|
|
</span>
|
|
|
|
<?php else: ?>
|
|
|
|
<span class="badge text-bg-danger">
|
|
Gagal
|
|
</span>
|
|
|
|
<?php endif; ?>
|
|
</td>
|
|
|
|
<td class="text-muted">
|
|
<?= htmlspecialchars($x["pesan"] ?? "-") ?>
|
|
</td>
|
|
|
|
<td>
|
|
|
|
<?php if (!empty($x["foto_path"])): ?>
|
|
|
|
<a href="#"
|
|
data-foto-modal="1"
|
|
data-src="<?= htmlspecialchars($x["foto_path"]) ?>"
|
|
data-caption="<?= htmlspecialchars(($x["nomor_resi"] ?? "-") . " • " . $x["created_at"]) ?>">
|
|
|
|
<img
|
|
src="<?= htmlspecialchars($x["foto_path"]) ?>"
|
|
alt="foto"
|
|
style="width:52px;height:52px;object-fit:cover;border-radius:10px;border:1px solid rgba(255,255,255,.2);cursor:pointer;"
|
|
>
|
|
|
|
</a>
|
|
|
|
<?php else: ?>
|
|
|
|
<span class="text-muted">-</span>
|
|
|
|
<?php endif; ?>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<?php endwhile; ?>
|
|
|
|
<?php else: ?>
|
|
|
|
<tr>
|
|
<td colspan="5" class="text-muted">
|
|
Belum ada log.
|
|
</td>
|
|
</tr>
|
|
|
|
<?php endif; ?>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="modal fade" id="fotoModal" tabindex="-1" aria-hidden="true">
|
|
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
|
|
<div class="modal-content bg-dark text-light">
|
|
|
|
<div class="modal-header border-secondary">
|
|
|
|
<h5 class="modal-title">Bukti Foto</h5>
|
|
|
|
<button type="button"
|
|
class="btn-close btn-close-white"
|
|
data-bs-dismiss="modal">
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div class="modal-body text-center">
|
|
|
|
<img id="fotoModalImg"
|
|
src=""
|
|
alt="foto"
|
|
style="max-width:100%;height:auto;border-radius:12px;">
|
|
|
|
<div class="mt-2 text-muted small"
|
|
id="fotoModalCaption">
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener("click", function (e) {
|
|
|
|
const a = e.target.closest("[data-foto-modal='1']");
|
|
|
|
if (!a) return;
|
|
|
|
e.preventDefault();
|
|
|
|
const src = a.getAttribute("data-src");
|
|
const caption = a.getAttribute("data-caption") || "";
|
|
|
|
document.getElementById("fotoModalImg").src = src;
|
|
document.getElementById("fotoModalCaption").textContent = caption;
|
|
|
|
const modal = new bootstrap.Modal(
|
|
document.getElementById("fotoModal")
|
|
);
|
|
|
|
modal.show();
|
|
});
|
|
|
|
document
|
|
.getElementById("fotoModal")
|
|
.addEventListener("hidden.bs.modal", function () {
|
|
|
|
document.getElementById("fotoModalImg").src = "";
|
|
document.getElementById("fotoModalCaption").textContent = "";
|
|
|
|
});
|
|
</script>
|
|
|
|
<?php include __DIR__ . "/../partials/footer.php"; ?>
|