From 097fa72e51fa0290a0ac0cd1c302372c8a90a696 Mon Sep 17 00:00:00 2001 From: "[Valentino Heman Budiarto]" <[hemanvalentino@gmail.com]> Date: Fri, 5 Jun 2026 20:16:32 +0700 Subject: [PATCH] . --- frontend/app/admin/monitoring/page.tsx | 142 +++++++++++++++++-------- 1 file changed, 99 insertions(+), 43 deletions(-) diff --git a/frontend/app/admin/monitoring/page.tsx b/frontend/app/admin/monitoring/page.tsx index 7d28d09..d01d499 100644 --- a/frontend/app/admin/monitoring/page.tsx +++ b/frontend/app/admin/monitoring/page.tsx @@ -1,50 +1,74 @@ "use client"; import { useState, useEffect } from "react"; -// Import ikon yang diperlukan import { Activity, Power, ZapOff, AlertTriangle, Lightbulb, Wind, Projector } from "lucide-react"; export default function PowerMonitoringPage() { - - const fetchPowerStatus = async () => { - try { - const response = await fetch("http://172.17.172.17:8080/api/hardware/power-status"); - const data = await response.json(); - - // Asumsikan data dari HA berupa string angka, kita update ke room D101 - setRooms(prev => prev.map(room => - room.name === "Kelas D101" ? { ...room, power: parseFloat(data.power) || 0 } : room - )); - } catch (err) { - console.error("Gagal mengambil data daya:", err); - } -}; - -useEffect(() => { - // Ambil data awal - fetchPowerStatus(); - - // Set interval untuk update setiap 5 detik agar real-time sesuai HA - const interval = setInterval(fetchPowerStatus, 5000); - - return () => clearInterval(interval); -}, []); + // ========================================================================= + // 1. DEKLARASI STATE + // ========================================================================= const [rooms, setRooms] = useState([]); - - // 1. STATE BARU: Dipisahkan berdasarkan ID Ruangan agar tidak bentrok const [roomDeviceStatus, setRoomDeviceStatus] = useState<{ [roomId: string]: { [deviceName: string]: boolean } }>({}); + // ========================================================================= + // 2. FUNGSI FETCH DATA DARI BACKEND + // ========================================================================= + + // A. Tarik Data Daya (Power / Watt) + const fetchPowerStatus = async () => { + try { + const response = await fetch("http://172.17.172.17:8080/api/hardware/power-status"); + const data = await response.json(); + + setRooms(prev => prev.map(room => + room.name === "Kelas D101" ? { ...room, power: parseFloat(data.power) || 0 } : room + )); + } catch (err) { + console.error("Gagal mengambil data daya:", err); + } + }; + + // B. Tarik Data Status Perangkat (Sinkronisasi Real-time Antar Admin) + const fetchDeviceStatus = async () => { + try { + const response = await fetch("http://172.17.172.17:8080/api/hardware/status"); + const result = await response.json(); + + if (result.status === "success") { + const backendData = result.data; + + // Kita terjemahkan data Golang ("on"/"off") menjadi boolean (true/false) + // dan masukkan khusus untuk Kelas D101 (id: 1) + setRoomDeviceStatus(prev => ({ + ...prev, + "room_1": { + ...prev["room_1"], + "Lampu 1": backendData.lampu1 === "on", + "Lampu 2": backendData.lampu2 === "on", + "AC 1": backendData.ac === "on", + "Proyektor": backendData.projector === "on", + } + })); + } + } catch (err) { + console.error("Gagal sinkronisasi status perangkat:", err); + } + }; + + // ========================================================================= + // 3. INISIALISASI & POLLING (AUTO-REFRESH) + // ========================================================================= useEffect(() => { - // Simulasi Data Ruangan dari IoT + // A. Simulasi Data Ruangan dari DB/IoT const dummyRooms = [ - { id: 1, name: "Kelas D101", power: 1250, isRelayOn: true, lastUpdate: "Baru saja" }, + { id: 1, name: "Kelas D101", power: 0, isRelayOn: true, lastUpdate: "Real-time" }, { id: 2, name: "Kelas D102", power: 0, isRelayOn: false, lastUpdate: "2 mnt lalu" }, { id: 3, name: "Kelas D103", power: 45, isRelayOn: true, lastUpdate: "Baru saja" }, { id: 4, name: "Kelas D104", power: 0, isRelayOn: false, lastUpdate: "10 mnt lalu" }, ]; setRooms(dummyRooms); - // Inisialisasi status default (semua OFF) untuk setiap ruangan + // B. Inisialisasi status default semua device OFF const initialStatus: { [roomId: string]: { [deviceName: string]: boolean } } = {}; dummyRooms.forEach(room => { initialStatus[`room_${room.id}`] = { @@ -55,9 +79,24 @@ useEffect(() => { }; }); setRoomDeviceStatus(initialStatus); + + // C. Tarik data pertama kali saat halaman dibuka + fetchPowerStatus(); + fetchDeviceStatus(); + + // D. Jalankan Polling setiap 2 detik (2000 ms) + const interval = setInterval(() => { + fetchPowerStatus(); + fetchDeviceStatus(); + }, 2000); + + // E. Bersihkan interval jika admin pindah halaman + return () => clearInterval(interval); }, []); - // 2. FUNGSI HANDLE TOGGLE YANG DIPERBAIKI (Menerima roomId) + // ========================================================================= + // 4. FUNGSI KONTROL DEVICE (TOGGLE ON/OFF) + // ========================================================================= const handleDeviceToggle = async (roomId: number, roomName: string, deviceName: string) => { const roomIdKey = `room_${roomId}`; const currentStatus = roomDeviceStatus[roomIdKey]?.[deviceName] || false; @@ -72,29 +111,44 @@ useEffect(() => { else if (deviceName === 'Lampu 1') backendDevice = "lampu1"; else if (deviceName === 'Lampu 2') backendDevice = "lampu2"; + // Update UI seketika agar terasa responsif bagi admin yang menekan tombol + setRoomDeviceStatus(prev => ({ + ...prev, + [roomIdKey]: { + ...prev[roomIdKey], + [deviceName]: !currentStatus, + }, + })); + try { - // Tembak API Golang (Ganti dengan endpoint aslimu jika berbeda) const response = await fetch("http://172.17.172.17:8080/api/hardware/control", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ device: backendDevice, action: actionType }), }); - if (response.ok) { - // 3. UPDATE STATE HANYA UNTUK RUANGAN YANG DIKLIK + if (!response.ok) { + // Jika gagal, kembalikan posisi tombol ke keadaan semula setRoomDeviceStatus(prev => ({ ...prev, [roomIdKey]: { ...prev[roomIdKey], - [deviceName]: !currentStatus, + [deviceName]: currentStatus, }, })); - } else { const errorData = await response.json(); alert(`GAGAL: ${errorData.error || response.statusText}`); } } catch (error) { console.error("Error API:", error); + // Jika server mati, kembalikan posisi tombol + setRoomDeviceStatus(prev => ({ + ...prev, + [roomIdKey]: { + ...prev[roomIdKey], + [deviceName]: currentStatus, + }, + })); alert("GAGAL: Tidak dapat terhubung ke Server Golang."); } }; @@ -105,23 +159,25 @@ useEffect(() => { } }; + // ========================================================================= + // 5. TAMPILAN UI (RENDER) + // ========================================================================= return (
- {/* Header section... */} + {/* Header section */}

Power Monitoring & Control

-

Pantau konsumsi daya kWh meter dan kendalikan *relay* sirkuit ruangan.

+

Pantau konsumsi daya kWh meter dan kendalikan relay sirkuit ruangan.

{[...rooms].sort((a, b) => a.name.localeCompare(b.name)).map((room) => { - // Ambil status spesifik untuk ruangan ini const roomIdKey = `room_${room.id}`; const currentRoomStatus = roomDeviceStatus[roomIdKey] || {}; @@ -136,7 +192,7 @@ useEffect(() => {

{room.name}

- {/* Power display... */} + {/* Power display */}
1000 ? 'text-orange-500' : 'text-gray-800'}`}> {room.power} @@ -150,7 +206,7 @@ useEffect(() => {
)} - {/* 3. PANEL KONTROL IoT BARU DENGAN LOGIKA WARNA DIPERBAIKI */} + {/* PANEL KONTROL IoT */}

IoT Device Control

@@ -200,13 +256,13 @@ useEffect(() => { ? 'bg-purple-100 text-purple-600 border border-purple-200' : 'bg-gray-50 text-gray-400 border border-transparent'}`} > - Proyektor + Proyektor
- {/* Footer... */} + {/* Footer */}
Update: {room.lastUpdate}