247 lines
6.0 KiB
HTML
247 lines
6.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Sensor Kualitas Udara</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
background: #0f172a;
|
|
color: #e5e7eb;
|
|
padding: 20px;
|
|
}
|
|
|
|
.card {
|
|
background: #1e293b;
|
|
padding: 15px;
|
|
margin: 10px 0;
|
|
border-radius: 8px;
|
|
font-size: 20px;
|
|
}
|
|
|
|
#container {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 10px;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1>🌱 Sensor Kualitas Udara</h1>
|
|
|
|
<div id="container"></div>
|
|
<hr>
|
|
<button onclick="openDialog()">Add Sensor</button>
|
|
<div id="dialog"
|
|
style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.7);">
|
|
<div style="background:#1e293b; padding:20px; width:300px; margin:100px auto; border-radius:10px;">
|
|
|
|
<h3>Tambah Sensor</h3>
|
|
|
|
<label>Kecamatan:</label><br>
|
|
<select id="kecamatan" onchange="loadKelurahan()">
|
|
<option value="">-- Pilih Kecamatan --</option>
|
|
</select>
|
|
<br><br>
|
|
|
|
<label>Kelurahan:</label><br>
|
|
<select id="kelurahan">
|
|
<option value="">-- Pilih Kelurahan --</option>
|
|
</select>
|
|
<br><br>
|
|
|
|
<label>Alamat:</label><br>
|
|
<textarea id="alamat" rows="3" style="width:100%"></textarea>
|
|
<br><br>
|
|
|
|
<button onclick="addSensor()">Simpan</button>
|
|
<button onclick="closeDialog()">Batal</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function getStatus(aq) {
|
|
if (aq >= 90) return "Sangat Baik";
|
|
if (aq >= 70) return "Baik";
|
|
if (aq >= 50) return "Sedang";
|
|
if (aq > 30) return "Buruk";
|
|
return "Sangat Buruk";
|
|
}
|
|
|
|
async function fetchAllSensors() {
|
|
try {
|
|
const res = await fetch("/api/sensors");
|
|
return await res.json();
|
|
} catch (err) {
|
|
console.error("Failed to fetch sensors", err);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function fetchSensorData(id) {
|
|
try {
|
|
const res = await fetch(`/api/sensordata/${id}`);
|
|
return await res.json();
|
|
} catch (err) {
|
|
console.error(`Failed sensor ${id}`, err);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function fetchData() {
|
|
const container = document.getElementById("container");
|
|
container.innerHTML = "";
|
|
|
|
const sensors = await fetchAllSensors();
|
|
|
|
for (const sensor of sensors) {
|
|
const data = await fetchSensorData(sensor.id);
|
|
|
|
const card = document.createElement("div");
|
|
card.className = "card";
|
|
|
|
if (!data || data.error) {
|
|
card.innerHTML = `
|
|
<h3>Sensor ID: ${sensor.id}</h3>
|
|
<p style="color: orange;">Belum Ada Data</p>
|
|
`;
|
|
} else {
|
|
card.innerHTML = `
|
|
<h3>Sensor ID: ${sensor.id}</h3>
|
|
<small>${data.kecamatan} - ${data.kelurahan}</small><br>
|
|
<small>${data.alamat}</small><br><br>
|
|
|
|
Karbon Oksida: ${data.co2} ppm<br>
|
|
Karbon Monoksida: ${data.co} ppm<br>
|
|
Benzena: ${data.bz} ppm<br>
|
|
Jumlah: ${data.aq} %<br>
|
|
Kualitas: ${getStatus(data.aq)}
|
|
`;
|
|
}
|
|
|
|
container.appendChild(card);
|
|
}
|
|
}
|
|
|
|
// ============================
|
|
// ADD SENSOR
|
|
// ============================
|
|
async function addSensor() {
|
|
const kecamatan = document.getElementById("kecamatan").value;
|
|
const kelurahan = document.getElementById("kelurahan").value;
|
|
const alamat = document.getElementById("alamat").value;
|
|
|
|
if (!kecamatan || !kelurahan || !alamat) {
|
|
alert("Semua field wajib diisi!");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const res = await fetch("/api/addSensor", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: JSON.stringify({
|
|
kecamatan,
|
|
kelurahan,
|
|
alamat
|
|
})
|
|
});
|
|
|
|
const result = await res.json();
|
|
|
|
if (!res.ok) {
|
|
alert(result.error || "Gagal");
|
|
return;
|
|
}
|
|
|
|
alert("Sensor berhasil ditambahkan!");
|
|
|
|
closeDialog(); // 👈 close popup
|
|
|
|
// reset form
|
|
document.getElementById("kecamatan").value = "";
|
|
document.getElementById("kelurahan").innerHTML = `<option value="">-- Pilih Kelurahan --</option>`;
|
|
document.getElementById("alamat").value = "";
|
|
|
|
fetchData();
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert("Error");
|
|
}
|
|
}
|
|
|
|
|
|
fetchData();
|
|
setInterval(fetchData, 5000);
|
|
|
|
function openDialog() {
|
|
document.getElementById("dialog").style.display = "block";
|
|
loadKecamatan(); // load when opened
|
|
}
|
|
|
|
function closeDialog() {
|
|
document.getElementById("dialog").style.display = "none";
|
|
}
|
|
|
|
// load data kecamatan
|
|
async function loadKecamatan() {
|
|
try {
|
|
const res = await fetch("/api/kecamatan");
|
|
const data = await res.json();
|
|
|
|
const select = document.getElementById("kecamatan");
|
|
|
|
// reset
|
|
select.innerHTML = `<option value="">-- Pilih Kecamatan --</option>`;
|
|
|
|
data.forEach(k => {
|
|
const option = document.createElement("option");
|
|
option.value = k.id;
|
|
option.textContent = k.nama;
|
|
select.appendChild(option);
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error("Failed load kecamatan", err);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// load data kelurahan
|
|
async function loadKelurahan() {
|
|
const kecamatanId = document.getElementById("kecamatan").value;
|
|
|
|
const select = document.getElementById("kelurahan");
|
|
|
|
// reset first
|
|
select.innerHTML = `<option value="">-- Pilih Kelurahan --</option>`;
|
|
|
|
if (!kecamatanId) return;
|
|
|
|
try {
|
|
const res = await fetch(`/api/kelurahan?kecamatan_id=${kecamatanId}`);
|
|
const data = await res.json();
|
|
|
|
data.forEach(k => {
|
|
const option = document.createElement("option");
|
|
option.value = k.id;
|
|
option.textContent = k.nama;
|
|
select.appendChild(option);
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error("Failed load kelurahan", err);
|
|
}
|
|
}
|
|
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html> |