346 lines
9.2 KiB
PHP
346 lines
9.2 KiB
PHP
<?php
|
||
require_once 'functions/auth.php';
|
||
if(!isLoggedIn()) { header("Location: login.php"); exit; }
|
||
require_once 'partials/header.php';
|
||
|
||
if (!defined('NODERED_URL')) {
|
||
define('NODERED_URL', 'https://node-red.aquamonn.online');
|
||
}
|
||
$noderedUrl = rtrim(NODERED_URL, '/');
|
||
?>
|
||
|
||
<div class="dashboard-layout">
|
||
<?php require_once 'partials/sidebar.php'; ?>
|
||
|
||
<div class="main-content page-enter">
|
||
|
||
<div style="display:flex; justify-content:flex-end; margin-bottom:10px;">
|
||
<div class="topbar-icons">
|
||
<div class="notif" id="notifBtn">
|
||
<i class="fas fa-bell"></i>
|
||
<span class="notif-badge">0</span>
|
||
<div class="notif-dropdown" id="notifDropdown">
|
||
<p><b>Notifikasi</b></p>
|
||
<ul><li>Memuat data…</li></ul>
|
||
</div>
|
||
</div>
|
||
<div class="user" id="userBtn">
|
||
<i class="fas fa-user"></i>
|
||
<div class="user-dropdown" id="userDropdown">
|
||
<div class="user-info">
|
||
<p class="user-email"><?= htmlspecialchars($_SESSION['email'] ?? 'User') ?></p>
|
||
</div>
|
||
<a href="logout.php">Logout</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Filter tanggal -->
|
||
<div style="margin-bottom:14px; display:flex; gap:10px; align-items:center; flex-wrap:wrap;">
|
||
<input type="date" id="filter-date"
|
||
style="padding:6px 12px; border-radius:8px; border:1px solid rgba(255,255,255,.2);
|
||
background:rgba(255,255,255,.1); color:#fff; font-size:13px; cursor:pointer;">
|
||
<button onclick="applyFilter()"
|
||
style="padding:6px 16px; border-radius:8px; background:#22c55e; color:#fff;
|
||
border:none; font-size:13px; cursor:pointer; font-weight:600;">
|
||
Filter
|
||
</button>
|
||
<button onclick="clearFilter()"
|
||
style="padding:6px 16px; border-radius:8px; background:rgba(255,255,255,.15); color:#fff;
|
||
border:none; font-size:13px; cursor:pointer;">
|
||
Reset
|
||
</button>
|
||
<span id="status-label" style="font-size:11px; color:rgba(255,255,255,.4); margin-left:4px;"></span>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<div style="background:white; display:inline-block; padding:5px 15px;
|
||
border-radius:10px 10px 0 0; font-weight:bold; font-size:12px;">Laporan</div>
|
||
<table class="catatan-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Tanggal</th>
|
||
<th>Waktu</th>
|
||
<th>Amonia</th>
|
||
<th>Kekeruhan</th>
|
||
<th>Suhu</th>
|
||
<th>Tinggi Air</th>
|
||
<th>Jarak sensor</th>
|
||
<th>pH</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tbody-catatan">
|
||
<tr>
|
||
<td colspan="6" style="text-align:center;padding:20px;color:#888;">⏳ Memuat…</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="pagination">
|
||
<span id="pagination-info">–</span>
|
||
<div class="pagination-controls">
|
||
<button id="btn-prev" onclick="changePage(-1)">Previous</button>
|
||
<span id="page-indicator" style="padding:0 10px;font-weight:bold;">1</span>
|
||
<button id="btn-next" onclick="changePage(1)">Next</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<?php require_once 'partials/footer.php'; ?>
|
||
<script>
|
||
const NODE_RED_URL = "<?= $noderedUrl ?>";
|
||
|
||
let currentPage = 1;
|
||
const rowsPerPage = 10;
|
||
let historyData = [];
|
||
let pollingTimer = null;
|
||
|
||
// ===============================
|
||
// Stop Polling
|
||
// ===============================
|
||
function stopPolling() {
|
||
console.log("Menghentikan polling Node-RED");
|
||
|
||
if (pollingTimer) {
|
||
clearInterval(pollingTimer);
|
||
pollingTimer = null;
|
||
}
|
||
}
|
||
|
||
// ===============================
|
||
// Start Polling
|
||
// ===============================
|
||
function startPolling() {
|
||
|
||
stopPolling();
|
||
|
||
loadHistory();
|
||
|
||
pollingTimer = setInterval(() => {
|
||
loadHistory();
|
||
}, 5000); // refresh setiap 5 detik
|
||
}
|
||
|
||
// ===============================
|
||
// Ambil History dari Node-RED
|
||
// ===============================
|
||
async function fetchHistory(limit = 100, date = null) {
|
||
|
||
try {
|
||
|
||
const params = new URLSearchParams();
|
||
params.append("limit", limit);
|
||
|
||
if (date) {
|
||
params.append("date", date);
|
||
}
|
||
|
||
const response = await fetch(
|
||
`${NODE_RED_URL}/get_history?${params.toString()}`,
|
||
{
|
||
method: "GET",
|
||
headers: {
|
||
"Accept":"application/json"
|
||
}
|
||
}
|
||
);
|
||
|
||
if (!response.ok) {
|
||
throw new Error("HTTP " + response.status);
|
||
}
|
||
|
||
const decoded = await response.json();
|
||
|
||
let list = [];
|
||
|
||
if (Array.isArray(decoded)) {
|
||
list = decoded;
|
||
}
|
||
else if (decoded.items) {
|
||
list = decoded.items;
|
||
}
|
||
else if (decoded.data) {
|
||
list = decoded.data;
|
||
}
|
||
else if (decoded.history) {
|
||
list = decoded.history;
|
||
}
|
||
|
||
return list;
|
||
|
||
} catch(err) {
|
||
|
||
console.error("Fetch History Error :", err);
|
||
|
||
return [];
|
||
}
|
||
|
||
}
|
||
|
||
// ===============================
|
||
// Load Data
|
||
// ===============================
|
||
async function loadHistory() {
|
||
|
||
document.getElementById("status-label").innerHTML = "Sinkronisasi...";
|
||
|
||
const date = document.getElementById("filter-date").value;
|
||
|
||
historyData = await fetchHistory(
|
||
100,
|
||
date !== "" ? date : null
|
||
);
|
||
|
||
currentPage = 1;
|
||
|
||
renderTable();
|
||
|
||
document.getElementById("status-label").innerHTML =
|
||
"Update : " + new Date().toLocaleTimeString();
|
||
|
||
}
|
||
|
||
// ===============================
|
||
// Render Table
|
||
// ===============================
|
||
function renderTable() {
|
||
|
||
const tbody = document.getElementById("tbody-catatan");
|
||
|
||
tbody.innerHTML = "";
|
||
|
||
if(historyData.length===0){
|
||
|
||
tbody.innerHTML=`
|
||
<tr>
|
||
<td colspan="6" style="text-align:center;padding:20px;color:#999">
|
||
Tidak ada data
|
||
</td>
|
||
</tr>`;
|
||
|
||
document.getElementById("pagination-info").innerHTML="0 Data";
|
||
|
||
return;
|
||
}
|
||
|
||
const start=(currentPage-1)*rowsPerPage;
|
||
const end=start+rowsPerPage;
|
||
|
||
const pageData=historyData.slice(start,end);
|
||
|
||
pageData.forEach(item=>{
|
||
|
||
let tanggal = "-";
|
||
let waktu = "-";
|
||
|
||
if (item.time) {
|
||
|
||
const arr = item.time.split("T");
|
||
|
||
tanggal = arr[0];
|
||
|
||
waktu = arr[1].replace(".000Z", "");
|
||
}
|
||
|
||
// Data sensor
|
||
const ammonia = Number(item.ammonia ?? item.amonia ?? 0).toFixed(1);
|
||
const turbidity = Number(item.turbidity ?? item.kekeruhan ?? 0).toFixed(1);
|
||
|
||
// suhu & ph (sesuaikan jika nanti sudah dikirim dari Node-RED)
|
||
const suhu = Number(item.suhu ?? 0).toFixed(1);
|
||
const tinggi_air = Number(item.tinggiAir ?? item.tinggi_air ?? 0).toFixed(1);
|
||
const jarak_sensor = Number(item.distance ?? item.jarak_Sensor ?? 0).toFixed(1);
|
||
const ph = Number(item.ph ?? 0).toFixed(1);
|
||
|
||
tbody.innerHTML+=`
|
||
<tr>
|
||
<td>${tanggal}</td>
|
||
<td>${waktu}</td>
|
||
<td>${ammonia}</td>
|
||
<td>${turbidity}</td>
|
||
<td>${suhu}</td>
|
||
<td>${tinggi_air}</td>
|
||
<td>${jarak_sensor}</td>
|
||
<td>${ph}</td>
|
||
</tr>`;
|
||
});
|
||
|
||
document.getElementById("page-indicator").innerHTML=currentPage;
|
||
|
||
document.getElementById("pagination-info").innerHTML=
|
||
`${start+1}-${Math.min(end,historyData.length)} dari ${historyData.length}`;
|
||
|
||
document.getElementById("btn-prev").disabled=currentPage===1;
|
||
document.getElementById("btn-next").disabled=end>=historyData.length;
|
||
|
||
}
|
||
|
||
// ===============================
|
||
// Pagination
|
||
// ===============================
|
||
function changePage(dir){
|
||
|
||
const maxPage=Math.ceil(historyData.length/rowsPerPage);
|
||
|
||
currentPage+=dir;
|
||
|
||
if(currentPage<1)
|
||
currentPage=1;
|
||
|
||
if(currentPage>maxPage)
|
||
currentPage=maxPage;
|
||
|
||
renderTable();
|
||
|
||
}
|
||
|
||
// ===============================
|
||
// Filter
|
||
// ===============================
|
||
function applyFilter(){
|
||
loadHistory();
|
||
}
|
||
|
||
function clearFilter(){
|
||
|
||
document.getElementById("filter-date").value="";
|
||
|
||
loadHistory();
|
||
|
||
}
|
||
|
||
// ===============================
|
||
// Dropdown User
|
||
// ===============================
|
||
document.getElementById("userBtn").onclick=function(){
|
||
|
||
document.getElementById("userDropdown")
|
||
.classList.toggle("show");
|
||
|
||
};
|
||
|
||
document.getElementById("notifBtn").onclick=function(){
|
||
|
||
document.getElementById("notifDropdown")
|
||
.classList.toggle("show");
|
||
|
||
};
|
||
|
||
// ===============================
|
||
// Start
|
||
// ===============================
|
||
window.onload=()=>{
|
||
|
||
startPolling();
|
||
|
||
};
|
||
|
||
window.onbeforeunload=()=>{
|
||
|
||
stopPolling();
|
||
|
||
};
|
||
</script>
|