TKK_E32232338/catatan.php

346 lines
9.2 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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>