projectTA/resources/views/layouts/app.blade.php

686 lines
38 KiB
PHP

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>florAura</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Tailwind CSS via CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Flowbite - Untuk komponen UI seperti dropdown -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.0/flowbite.min.css" rel="stylesheet" />
<!-- Firebase SDK -->
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js"></script>
<script>
// Konfigurasi Firebase
const firebaseConfig = {
apiKey: "AIzaSyDf2QTksasAup4pzsNs9_JDpCXmBbUbywY",
authDomain: "sensoranggrek-3d9ac.firebaseapp.com",
databaseURL: "https://sensoranggrek-3d9ac-default-rtdb.firebaseio.com",
projectId: "sensoranggrek-3d9ac",
storageBucket: "sensoranggrek-3d9ac.firebasestorage.app",
messagingSenderId: "16998798790",
appId: "1:16998798790:web:885e1155255b24ab98cec7"
};
// Inisialisasi Firebase
firebase.initializeApp(firebaseConfig);
const database = firebase.database();
</script>
</head>
<body class="font-sans antialiased bg-gray-100">
<div class="min-h-screen flex flex-col">
<!-- Navbar -->
<nav class="bg-white border-gray-200 dark:bg-gray-900">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<a href="{{ route('dashboard') }}" class="flex items-center space-x-3 rtl:space-x-reverse">
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">florAura</span>
</a>
<!-- Menu navigasi diletakkan sebelum profile menu (di sebelah kanan) -->
<div class="hidden md:flex items-center space-x-1 md:me-4">
<a href="{{ route('dashboard') }}" class="py-2 px-3 {{ Request::routeIs('dashboard') ? 'text-blue-700 border-b-2 border-blue-700' : 'text-gray-900 hover:text-blue-700' }}">Dashboard</a>
<a href="{{ route('history') }}" class="py-2 px-3 {{ Request::routeIs('history') ? 'text-blue-700 border-b-2 border-blue-700' : 'text-gray-900 hover:text-blue-700' }}">History</a>
</div>
<div class="flex items-center md:order-2">
<button type="button" class="flex text-sm bg-gray-800 rounded-full md:me-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600" id="user-menu-button" aria-expanded="false" data-dropdown-toggle="user-dropdown" data-dropdown-placement="bottom">
<span class="sr-only">Open user menu</span>
<span class="inline-flex h-8 w-8 rounded-full bg-gray-500 text-white justify-center items-center">
<span class="text-sm font-medium leading-none">{{ substr(session('user_email') ?? 'User', 0, 1) }}</span>
</span>
</button>
<!-- Dropdown menu -->
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow dark:bg-gray-700 dark:divide-gray-600" id="user-dropdown">
<div class="px-4 py-3">
<span class="block text-sm text-gray-900 dark:text-white">{{ session('user_email') ?? 'User' }}</span>
</div>
<ul class="py-2" aria-labelledby="user-menu-button">
<li>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white w-full text-left">Logout</button>
</form>
</li>
</ul>
</div>
<button data-collapse-toggle="navbar-user" type="button" class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600 ml-2" aria-controls="navbar-user" aria-expanded="false" id="navbar-toggle">
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/>
</svg>
</button>
</div>
<!-- Navbar untuk tampilan mobile -->
<div class="items-center justify-between hidden w-full md:hidden" id="navbar-user">
<ul class="flex flex-col font-medium p-4 mt-4 border border-gray-100 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700">
<li>
<a href="{{ route('dashboard') }}" class="block py-2 px-3 {{ Request::routeIs('dashboard') ? 'text-white bg-blue-700 rounded' : 'text-gray-900 rounded hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700' }}">Dashboard</a>
</li>
<li>
<a href="{{ route('history') }}" class="block py-2 px-3 {{ Request::routeIs('history') ? 'text-white bg-blue-700 rounded' : 'text-gray-900 rounded hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700' }}">History</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Main content -->
<div class="flex flex-col flex-1">
<!-- Sensor Cards Bar -->
<div class="sticky top-0 z-10 flex-shrink-0 bg-white shadow">
<!-- Toggle button untuk sensor cards di mobile -->
<div class="flex items-center justify-between bg-white p-3 border-t border-gray-200 md:hidden">
<h3 class="text-sm font-medium text-gray-700">Status Sensor</h3>
<button id="toggleSensorCards" class="flex items-center bg-gray-100 hover:bg-gray-200 rounded-md px-3 py-1 text-xs text-gray-700 transition-all">
<span id="toggleSensorText">Lihat Detail</span>
<svg id="toggleSensorIcon" class="w-4 h-4 ml-1 transform transition-transform" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
</div>
<!-- Sensor Cards Bar -->
<div id="sensorCardsContainer" class="hidden md:grid grid-cols-1 xs:grid-cols-2 md:grid-cols-4 gap-3 sm:gap-4 p-3 sm:p-4 bg-gray-50 border-t border-gray-200">
<!-- Card Suhu -->
<div class="bg-white p-3 sm:p-4 rounded-lg shadow-sm border border-gray-200 transition-all duration-300 hover:shadow-md">
<div class="flex items-start sm:items-center">
<div class="flex-shrink-0 bg-red-100 rounded-full p-2 sm:p-3 mr-3 sm:mr-4">
<svg class="h-5 w-5 sm:h-6 sm:w-6 text-red-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
</div>
<div class="flex-1">
<div class="text-xs font-medium text-gray-500 uppercase">Suhu</div>
<div class="text-xl sm:text-2xl font-semibold text-gray-800 transition-all duration-300" id="nilai-suhu">--°C</div>
<div class="flex flex-col sm:flex-row sm:justify-between mt-1 sm:mt-2">
<div class="mb-1 sm:mb-0">
<span class="text-xs font-medium text-gray-500">Status:</span>
<span class="text-xs sm:text-sm font-medium ml-1" id="status-suhu">--</span>
</div>
<div>
<span class="text-xs font-medium text-gray-500">Aksi:</span>
<span class="text-xs sm:text-sm font-medium ml-1" id="aksi-suhu">--</span>
</div>
</div>
</div>
</div>
</div>
<!-- Card Kelembaban -->
<div class="bg-white p-3 sm:p-4 rounded-lg shadow-sm border border-gray-200 transition-all duration-300 hover:shadow-md">
<div class="flex items-start sm:items-center">
<div class="flex-shrink-0 bg-blue-100 rounded-full p-2 sm:p-3 mr-3 sm:mr-4">
<svg class="h-5 w-5 sm:h-6 sm:w-6 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z" />
</svg>
</div>
<div class="flex-1">
<div class="text-xs font-medium text-gray-500 uppercase">Kelembapan Tanah</div>
<div class="text-xl sm:text-2xl font-semibold text-gray-800 transition-all duration-300" id="nilai-kelembaban">--%</div>
<div class="flex flex-col sm:flex-row sm:justify-between mt-1 sm:mt-2">
<div class="mb-1 sm:mb-0">
<span class="text-xs font-medium text-gray-500">Status:</span>
<span class="text-xs sm:text-sm font-medium ml-1" id="status-kelembaban">--</span>
</div>
<div>
<span class="text-xs font-medium text-gray-500">Aksi:</span>
<span class="text-xs sm:text-sm font-medium ml-1" id="aksi-kelembaban">--</span>
</div>
</div>
</div>
</div>
</div>
<!-- Card Cahaya -->
<div class="bg-white p-3 sm:p-4 rounded-lg shadow-sm border border-gray-200 transition-all duration-300 hover:shadow-md">
<div class="flex items-start sm:items-center">
<div class="flex-shrink-0 bg-yellow-100 rounded-full p-2 sm:p-3 mr-3 sm:mr-4">
<svg class="h-5 w-5 sm:h-6 sm:w-6 text-yellow-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</div>
<div class="flex-1">
<div class="text-xs font-medium text-gray-500 uppercase">Cahaya</div>
<div class="text-xl sm:text-2xl font-semibold text-gray-800 transition-all duration-300" id="nilai-cahaya">-- lux</div>
<div class="flex flex-col sm:flex-row sm:justify-between mt-1 sm:mt-2">
<div class="mb-1 sm:mb-0">
<span class="text-xs font-medium text-gray-500">Status:</span>
<span class="text-xs sm:text-sm font-medium ml-1" id="status-cahaya">--</span>
</div>
<div>
<span class="text-xs font-medium text-gray-500">Aksi:</span>
<span class="text-xs sm:text-sm font-medium ml-1" id="aksi-cahaya">--</span>
</div>
</div>
</div>
</div>
</div>
<!-- Card Status ESP8266 -->
<div class="bg-white p-3 sm:p-4 rounded-lg shadow-sm border border-gray-200 transition-all duration-300 hover:shadow-md">
<div class="flex items-start sm:items-center">
<div class="flex-shrink-0 bg-gray-100 rounded-full p-2 sm:p-3 mr-3 sm:mr-4">
<svg class="h-5 w-5 sm:h-6 sm:w-6 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
</svg>
</div>
<div class="flex-1">
<div class="text-xs font-medium text-gray-500 uppercase">Status ESP8266</div>
<div class="flex items-center">
<span class="h-2.5 w-2.5 sm:h-3 sm:w-3 rounded-full mr-1 sm:mr-2 bg-gray-300 transition-all duration-300" id="status-dot"></span>
<span class="text-sm sm:text-lg font-semibold text-gray-800 transition-all duration-300" id="status-text">Menghubungkan...</span>
</div>
<div class="mt-2 flex items-center">
<button id="restart-esp" class="p-1.5 sm:p-2 bg-blue-500 text-white rounded-full hover:bg-blue-600 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
</button>
<span id="restart-status" class="text-xs text-gray-500 ml-2 flex-grow truncate"></span>
</div>
</div>
</div>
</div>
</div>
</div>
<main class="flex-1">
<div class="py-6">
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
@yield('content')
</div>
</div>
</main>
</div>
</div>
<!-- Script untuk Firebase Realtime -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Referensi ke path data sensor
const sensorRef = database.ref('sensor');
// Referensi ke status ESP8266
const statusRef = database.ref('status/connected');
// Referensi untuk restart
const restartRef = database.ref('system/restart');
// Tombol restart ESP8266
const restartButton = document.getElementById('restart-esp');
const restartStatus = document.getElementById('restart-status');
// Timestamp saat terakhir update
let lastUpdateTime = Date.now();
// Token CSRF untuk request API
let csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
// Tambahkan variabel untuk menyimpan status terakhir
let lastStatus = {
suhu: null,
kelembaban: null,
cahaya: null
};
// Menyimpan nilai sensor terakhir untuk dibandingkan
let lastSensorValues = {
suhu: null,
kelembapan_tanah: null,
cahaya: null
};
// Tambahkan variabel untuk menyimpan status terakhir yang dikirim ke history
let lastSentStatus = {
suhu: null,
kelembaban: null,
cahaya: null
};
// Flag untuk tracking status pengiriman data ke server
let isDataSending = false;
// Listener untuk data sensor (menggunakan snapshot)
sensorRef.on('value', (snapshot) => {
const data = snapshot.val();
if (data) {
// Suhu
if (data.suhu !== undefined && data.suhu !== lastSensorValues.suhu) {
saveSensorHistory('suhu', lastSensorValues.suhu, data.suhu);
lastSensorValues.suhu = data.suhu;
}
updateSensorValue('nilai-suhu', data.suhu ? `${data.suhu}°C` : '--°C');
if (data.suhu !== undefined) evaluateSuhu(data.suhu);
// Kelembapan Tanah
if (data.kelembapan_tanah !== undefined && data.kelembapan_tanah !== lastSensorValues.kelembapan_tanah) {
saveSensorHistory('kelembapan_tanah', lastSensorValues.kelembapan_tanah, data.kelembapan_tanah);
lastSensorValues.kelembapan_tanah = data.kelembapan_tanah;
}
updateSensorValue('nilai-kelembaban', data.kelembapan_tanah ? `${data.kelembapan_tanah}%` : '--%');
if (data.kelembapan_tanah !== undefined) evaluateKelembaban(data.kelembapan_tanah);
// Cahaya
if (data.cahaya !== undefined && data.cahaya !== lastSensorValues.cahaya) {
saveSensorHistory('cahaya', lastSensorValues.cahaya, data.cahaya);
lastSensorValues.cahaya = data.cahaya;
}
updateSensorValue('nilai-cahaya', data.cahaya ? `${data.cahaya} lux` : '-- lux');
if (data.cahaya !== undefined) evaluateCahaya(data.cahaya);
lastUpdateTime = Date.now();
}
});
// Fungsi untuk menyimpan riwayat perubahan sensor ke server API
async function saveSensorHistory(sensorType, oldValue, newValue) {
// Hindari pengiriman berulang jika masih dalam proses pengiriman
if (isDataSending) return;
// Cek jika nilainya kosong atau null
if (newValue === null || newValue === undefined) return;
// Jika tidak ada perubahan nilai, tidak perlu mengirim data
if (oldValue === newValue) return;
try {
// Tentukan status dan aksi berdasarkan nilai baru
let currentStatus = '';
let currentAction = '';
if (sensorType === 'suhu') {
if (newValue < 24) {
currentStatus = 'Rendah';
currentAction = 'Kipas Off';
} else if (newValue >= 25 && newValue <= 27) {
currentStatus = 'Normal';
currentAction = 'Kipas Off';
} else if (newValue >= 28) {
currentStatus = 'Tinggi';
currentAction = 'Kipas On';
}
} else if (sensorType === 'kelembapan_tanah') {
if (newValue < 40) {
currentStatus = 'Kering';
currentAction = 'Pompa Air On';
} else if (newValue >= 40 && newValue < 50) {
currentStatus = 'Cukup';
currentAction = 'Pompa Air Off';
} else if (newValue >= 50 && newValue < 60) {
currentStatus = 'Optimal';
currentAction = 'Pompa Air Off';
} else if (newValue >= 60 && newValue <= 70) {
currentStatus = 'Lembap';
currentAction = 'Pompa Air Off';
} else if (newValue > 70) {
currentStatus = 'Sangat Lembap';
currentAction = 'Pompa Air Off';
}
} else if (sensorType === 'cahaya') {
if (newValue < 8000) {
currentStatus = 'Rendah';
currentAction = 'Lampu On';
} else if (newValue >= 8001 && newValue <= 11000) {
currentStatus = 'Normal';
currentAction = 'Lampu Off';
} else if (newValue > 11001) {
currentStatus = 'Tinggi';
currentAction = 'Lampu Off';
}
}
// Siapkan data untuk dikirim ke server
const sensorData = {
sensor: sensorType === 'kelembapan_tanah' ? 'kelembapan_tanah' : sensorType,
oldValue: oldValue,
newValue: newValue,
timestamp: new Date().toISOString()
};
console.log('Mengirim data sensor ke API:', sensorData);
// Set flag pengiriman data
isDataSending = true;
// Kirim data ke API
const response = await fetch('/api/history', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify(sensorData)
});
// Reset flag pengiriman data
isDataSending = false;
if (!response.ok) {
throw new Error('Gagal mengirim data ke server');
}
// Update status terakhir yang dikirim
lastSentStatus[sensorType] = currentStatus;
console.log('Data sensor berhasil dikirim ke API');
} catch (error) {
console.error('Error mengirim data sensor ke API:', error);
isDataSending = false;
}
}
// Event listener untuk tombol restart
restartButton.addEventListener('click', function() {
if (confirm('Apakah Anda yakin ingin merestart ESP8266?')) {
// Nonaktifkan tombol saat proses restart
restartButton.disabled = true;
restartButton.classList.add('bg-gray-400');
restartButton.classList.remove('bg-blue-500', 'hover:bg-blue-600');
restartStatus.textContent = 'Mengirim perintah restart...';
// Set nilai restart ke true
database.ref('system/restart').set(true)
.then(() => {
restartStatus.textContent = 'Perintah restart dikirim';
restartStatus.className = 'text-xs text-green-600 ml-2';
// Setelah 3 detik, reset ke false
setTimeout(() => {
database.ref('system/restart').set(false)
.catch(() => {
console.log('Reset restart flag gagal, tetapi ESP mungkin telah membacanya');
});
}, 3000);
// Setelah 5 detik, aktifkan kembali tombol
setTimeout(() => {
restartButton.disabled = false;
restartButton.classList.remove('bg-gray-400');
restartButton.classList.add('bg-blue-500', 'hover:bg-blue-600');
restartStatus.textContent = '';
}, 5000);
})
.catch((error) => {
restartStatus.textContent = 'Gagal mengirim restart. Perlu cek aturan Firebase.';
restartStatus.className = 'text-xs text-red-600 ml-2';
console.error('Gagal mengirim restart:', error);
// Aktifkan kembali tombol
restartButton.disabled = false;
restartButton.classList.remove('bg-gray-400');
restartButton.classList.add('bg-blue-500', 'hover:bg-blue-600');
});
}
});
// Listener untuk status koneksi ESP8266
statusRef.on('value', (snapshot) => {
const isConnected = snapshot.val();
if (isConnected === true) {
lastUpdateTime = Date.now(); // Update waktu terakhir terlihat
}
updateConnectionStatus();
});
// Juga pantau timestamp last_seen untuk deteksi koneksi yang lebih akurat
const lastSeenRef = database.ref('status/last_seen');
lastSeenRef.on('value', (snapshot) => {
const lastSeenTimestamp = snapshot.val();
if (lastSeenTimestamp) {
// Parse timestamp (format: "DD-MM-YYYY HH:MM:SS")
try {
const lastSeen = parseFirebaseTimestamp(lastSeenTimestamp);
if (!isNaN(lastSeen.getTime())) {
// Hanya update jika timestamp valid
lastUpdateTime = Date.now();
updateConnectionStatus();
}
} catch (e) {
console.error("Error parsing timestamp:", e);
}
}
});
// Fungsi untuk memperbarui status koneksi
function updateConnectionStatus() {
const statusDot = document.getElementById('status-dot');
const statusText = document.getElementById('status-text');
// Jika terakhir update kurang dari 60 detik, anggap terhubung
const isConnected = (Date.now() - lastUpdateTime) < 60000;
if (isConnected) {
statusDot.className = 'h-3 w-3 rounded-full mr-2 bg-green-500 transition-all duration-300';
statusText.textContent = 'Terhubung';
statusText.className = 'text-lg font-semibold text-green-600 transition-all duration-300';
} else {
statusDot.className = 'h-3 w-3 rounded-full mr-2 bg-red-500 transition-all duration-300';
statusText.textContent = 'Terputus';
statusText.className = 'text-lg font-semibold text-red-600 transition-all duration-300';
}
}
// Fungsi untuk mengubah string timestamp Firebase menjadi objek Date
function parseFirebaseTimestamp(timestampStr) {
// Format: "DD-MM-YYYY HH:MM:SS"
const [datePart, timePart] = timestampStr.split(' ');
const [day, month, year] = datePart.split('-');
const [hour, minute, second] = timePart.split(':');
return new Date(year, month-1, day, hour, minute, second);
}
// Cek koneksi setiap 5 detik untuk memperbarui status UI
setInterval(updateConnectionStatus, 5000);
// Fungsi untuk update nilai dengan animasi smooth
function updateSensorValue(elementId, newValue) {
const element = document.getElementById(elementId);
// Tambahkan kelas untuk animasi
element.classList.add('scale-110', 'text-indigo-600');
element.textContent = newValue;
// Hapus kelas setelah animasi selesai
setTimeout(() => {
element.classList.remove('scale-110', 'text-indigo-600');
}, 300);
}
// Fungsi untuk mengevaluasi status dan aksi sensor suhu
function evaluateSuhu(suhu) {
let status = '--';
let aksi = '--';
let statusClass = '';
if (suhu < 24) {
status = 'Rendah';
aksi = 'Kipas Off';
statusClass = 'text-blue-600';
} else if (suhu >= 25 && suhu <= 27) {
status = 'Normal';
aksi = 'Kipas Off';
statusClass = 'text-green-600';
} else if (suhu >= 28) {
status = 'Tinggi';
aksi = 'Kipas On';
statusClass = 'text-red-600';
}
const statusElement = document.getElementById('status-suhu');
const aksiElement = document.getElementById('aksi-suhu');
statusElement.textContent = status;
statusElement.className = `text-sm font-medium ml-1 ${statusClass}`;
aksiElement.textContent = aksi;
aksiElement.className = `text-sm font-medium ml-1 ${aksi.includes('On') ? 'text-green-600' : 'text-gray-600'}`;
}
// Fungsi untuk mengevaluasi status dan aksi sensor kelembapan tanah
function evaluateKelembaban(kelembapanTanah) {
let status = '--';
let aksi = '--';
let statusClass = '';
if (kelembapanTanah < 40) {
status = 'Kering';
aksi = 'Pompa Air On';
statusClass = 'text-yellow-600';
} else if (kelembapanTanah >= 40 && kelembapanTanah < 50) {
status = 'Cukup';
aksi = 'Pompa Air Off';
statusClass = 'text-green-600';
} else if (kelembapanTanah >= 50 && kelembapanTanah < 60) {
status = 'Optimal';
aksi = 'Pompa Air Off';
statusClass = 'text-green-600';
} else if (kelembapanTanah >= 60 && kelembapanTanah <= 70) {
status = 'Lembap';
aksi = 'Pompa Air Off';
statusClass = 'text-blue-600';
} else if (kelembapanTanah > 70) {
status = 'Sangat Lembap';
aksi = 'Pompa Air Off';
statusClass = 'text-blue-400';
}
const statusElement = document.getElementById('status-kelembaban');
const aksiElement = document.getElementById('aksi-kelembaban');
statusElement.textContent = status;
statusElement.className = `text-sm font-medium ml-1 ${statusClass}`;
aksiElement.textContent = aksi;
aksiElement.className = `text-sm font-medium ml-1 ${aksi === 'Pompa Air On' ? 'text-green-600' : 'text-gray-600'}`;
}
// Fungsi untuk mengevaluasi status dan aksi sensor cahaya
function evaluateCahaya(cahaya) {
let status = '--';
let aksi = '--';
let statusClass = '';
if (cahaya < 8000) {
status = 'Rendah';
aksi = 'Lampu On';
statusClass = 'text-blue-600';
} else if (cahaya >= 8001 && cahaya <= 11000) {
status = 'Normal';
aksi = 'Lampu Off';
statusClass = 'text-green-600';
} else if (cahaya > 11001) {
status = 'Tinggi';
aksi = 'Lampu Off';
statusClass = 'text-yellow-600';
}
const statusElement = document.getElementById('status-cahaya');
const aksiElement = document.getElementById('aksi-cahaya');
statusElement.textContent = status;
statusElement.className = `text-xs sm:text-sm font-medium ml-1 ${statusClass}`;
aksiElement.textContent = aksi;
aksiElement.className = `text-xs sm:text-sm font-medium ml-1 ${aksi.includes('On') ? 'text-green-600' : 'text-gray-600'}`;
}
});
</script>
<!-- Script untuk Flowbite (dropdown, dll) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.0/flowbite.min.js"></script>
<!-- Script untuk User Dropdown dan Sensor Cards Toggle -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const userMenuButton = document.getElementById('user-menu-button');
const userDropdown = document.getElementById('user-dropdown');
const navbarToggle = document.getElementById('navbar-toggle');
const navbarUser = document.getElementById('navbar-user');
// Toggle Sensor Cards pada Mobile
const toggleSensorCards = document.getElementById('toggleSensorCards');
const sensorCardsContainer = document.getElementById('sensorCardsContainer');
const toggleSensorIcon = document.getElementById('toggleSensorIcon');
const toggleSensorText = document.getElementById('toggleSensorText');
if (toggleSensorCards && sensorCardsContainer) {
toggleSensorCards.addEventListener('click', function() {
sensorCardsContainer.classList.toggle('hidden');
toggleSensorIcon.classList.toggle('rotate-180');
toggleSensorText.textContent = sensorCardsContainer.classList.contains('hidden') ? 'Lihat Detail' : 'Sembunyikan';
});
// Restore state dari localStorage jika ada
const sensorCardsState = localStorage.getItem('sensorCardsVisible');
if (sensorCardsState === 'true') {
sensorCardsContainer.classList.remove('hidden');
toggleSensorIcon.classList.add('rotate-180');
toggleSensorText.textContent = 'Sembunyikan';
}
// Save state ke localStorage saat toggle
toggleSensorCards.addEventListener('click', function() {
localStorage.setItem('sensorCardsVisible', !sensorCardsContainer.classList.contains('hidden'));
});
}
// Fallback jika flowbite tidak berfungsi
if (userMenuButton && userDropdown && typeof flowbite === 'undefined') {
// Toggle dropdown saat tombol profil diklik
userMenuButton.addEventListener('click', function() {
userDropdown.classList.toggle('hidden');
});
// Tutup dropdown saat klik di luar dropdown
document.addEventListener('click', function(e) {
if (!userMenuButton.contains(e.target) && !userDropdown.contains(e.target)) {
userDropdown.classList.add('hidden');
}
});
}
if (navbarToggle && navbarUser) {
// Toggle navbar mobile
navbarToggle.addEventListener('click', function() {
navbarUser.classList.toggle('hidden');
});
}
});
</script>
</body>
</html>