2025-08-07 16:46:11 +08:00

218 lines
7.5 KiB
TypeScript

import axios from "axios";
import React, { useState, useEffect } from "react";
export type SiteName = 'Site A' | 'Site B' | 'Site C';
interface SiteStatusProps {
selectedSite: SiteName;
location: string;
inverterProvider: string;
emergencyContact: string;
lastSyncTimestamp: string;
}
const SiteStatus = ({
selectedSite,
location,
inverterProvider,
emergencyContact,
lastSyncTimestamp,
}: SiteStatusProps) => {
useEffect(() => {
const ws = new WebSocket("ws://localhost:8000/ws");
ws.onmessage = (event) => {
const data = event.data;
alert(`MQTT: ${data}`);
};
ws.onopen = () => console.log("WebSocket connected");
ws.onclose = () => console.log("WebSocket disconnected");
return () => ws.close();
}, []);
const [showModal, setShowModal] = useState(false);
const [deviceId, setDeviceId] = useState("");
const [functionType, setFunctionType] = useState("Grid");
// Map site names to site IDs
const siteIdMap: Record<SiteName, string> = {
"Site A": "site_01",
"Site B": "site_02",
"Site C": "site_03",
};
// Track devices connected per site
const [loggedDevices, setLoggedDevices] = useState<Record<string, string[]>>({
site_01: [],
site_02: [],
site_03: [],
});
const siteId = siteIdMap[selectedSite];
const devicesAtSite = loggedDevices[siteId] || [];
const handleStartLogging = () => {
setShowModal(true);
};
const handleConfirm = async () => {
const siteId = siteIdMap[selectedSite];
const topic = `ADW300/${siteId}/${deviceId}/${functionType.toLowerCase()}`;
try {
const response = await axios.post("http://localhost:8000/start-logging", {
topics: [topic],
});
console.log("Started logging:", response.data);
// Add device to list
setLoggedDevices((prev) => ({
...prev,
[siteId]: [...(prev[siteId] || []), deviceId],
}));
setShowModal(false);
} catch (error) {
console.error("Failed to start logging:", error);
}
};
const handleStopLogging = async () => {
try {
await axios.post("http://localhost:8000/stop-logging");
// Clear all devices for the site (or modify to remove only specific one)
setLoggedDevices((prev) => ({
...prev,
[siteId]: [],
}));
console.log("Stopped logging for", siteId);
} catch (error) {
console.error("Failed to stop logging:", error);
}
};
const statusMap: Record<SiteName, string> = {
'Site A': 'Active',
'Site B': 'Inactive',
'Site C': 'Faulty',
};
return (
<div className="bg-white p-4 rounded-lg shadow-md space-y-2 dark:bg-rtgray-800 dark:text-white-light">
<h2 className="text-xl font-semibold mb-3">Site Details</h2>
{/* Status */}
<div className="flex justify-between items-center text-base">
<p className="text-gray-600 dark:text-white/85 font-medium">Status:</p>
<p className={`font-semibold ${
statusMap[selectedSite] === 'Active' ? 'text-green-500' :
statusMap[selectedSite] === 'Inactive' ? 'text-orange-500' :
'text-red-500'
}`}>
{statusMap[selectedSite]}
</p>
</div>
{/* Site ID */}
<div className="flex justify-between items-center text-base">
<p className="text-gray-600 dark:text-white/85 font-medium">Site ID:</p>
<p className="font-medium">{siteId}</p>
</div>
{/* Location */}
<div className="flex justify-between items-center text-base">
<p className="text-gray-600 dark:text-white/85 font-medium">Location:</p>
<p className="font-medium">{location}</p>
</div>
{/* Inverter Provider */}
<div className="flex justify-between items-center text-base">
<p className="text-gray-600 dark:text-white/85 font-medium">Inverter Provider:</p>
<p className="font-medium">{inverterProvider}</p>
</div>
{/* Emergency Contact */}
<div className="flex justify-between items-center text-base">
<p className="text-gray-600 dark:text-white/85 font-medium">Emergency Contact:</p>
<p className="font-medium">{emergencyContact}</p>
</div>
{/* Last Sync */}
<div className="flex justify-between items-center text-base">
<p className="text-gray-600 dark:text-white/85 font-medium">Last Sync:</p>
<p className="font-medium">{lastSyncTimestamp}</p>
</div>
{/* Start Logging Button */}
<div className="flex justify-between items-center text-base space-x-2">
{devicesAtSite.length > 0 ? (
<button
onClick={handleStopLogging}
className="text-sm lg:text-md bg-red-500 hover:bg-red-600 text-white font-medium px-3 py-2 rounded"
>
Stop Logging
</button>
) : (
<button
onClick={handleStartLogging}
className="text-sm lg:text-md btn-primary px-3 py-2"
>
Start Logging
</button>
)}
</div>
{/* Modal */}
{showModal && (
<div className="fixed inset-0 z-50 bg-black bg-opacity-50 flex items-center justify-center">
<div className="bg-white rounded-lg p-6 w-[90%] max-w-md shadow-lg">
<h2 className="text-lg font-semibold mb-4">Enter Device Info</h2>
<input
type="text"
placeholder="Device ID (e.g. device_01)"
className="w-full p-2 mb-4 border rounded"
value={deviceId}
onChange={(e) => setDeviceId(e.target.value)}
/>
<select
className="w-full p-2 mb-4 border rounded"
value={functionType}
onChange={(e) => setFunctionType(e.target.value)}
>
<option value="Grid">Grid</option>
<option value="Solar">Solar</option>
</select>
<div className="flex justify-end space-x-2">
<button
onClick={() => setShowModal(false)}
className="btn-primary bg-white border-2 border-black hover:bg-rtgray-200 px-4 py-2"
>
Cancel
</button>
<button
onClick={handleConfirm}
className="btn-primary px-4 py-2"
>
Confirm
</button>
</div>
</div>
</div>
)}
</div>
);
};
export default SiteStatus;