93 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			93 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import React, { useEffect, useState } from "react";
 | |
| 
 | |
| interface KPI_TableProps {
 | |
|   siteId: string;
 | |
|   month: string; // format: "YYYY-MM"
 | |
| }
 | |
| 
 | |
| interface MonthlyKPI {
 | |
|   site: string;
 | |
|   month: string;
 | |
|   yield_kwh: number | null;
 | |
|   consumption_kwh: number | null;
 | |
|   grid_draw_kwh: number | null;
 | |
|   efficiency: number | null;
 | |
|   peak_demand_kw: number | null;
 | |
|   avg_power_factor: number | null;
 | |
|   load_factor: number | null;
 | |
| }
 | |
| 
 | |
| const KPI_Table: React.FC<KPI_TableProps> = ({ siteId, month }) => {
 | |
|   const [kpiData, setKpiData] = useState<MonthlyKPI | null>(null);
 | |
|   const [loading, setLoading] = useState(false);
 | |
| 
 | |
|   useEffect(() => {
 | |
|     if (!siteId || !month) return;
 | |
| 
 | |
|     const fetchKPI = async () => {
 | |
|       setLoading(true);
 | |
|       try {
 | |
|         const res = await fetch(
 | |
|           `http://localhost:8000/kpi/monthly?site=${siteId}&month=${month}`
 | |
|         );
 | |
|         setKpiData(await res.json());
 | |
|       } catch (err) {
 | |
|         console.error("Failed to fetch KPI:", err);
 | |
|         setKpiData(null);
 | |
|       } finally {
 | |
|         setLoading(false);
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     fetchKPI();
 | |
|   }, [siteId, month]);
 | |
| 
 | |
|   const formatValue = (value: number | null, unit = "", decimals = 2) =>
 | |
|     value != null ? `${value.toFixed(decimals)}${unit}` : "—";
 | |
| 
 | |
|   const rows = [
 | |
|     { label: "Monthly Yield", value: formatValue(kpiData?.yield_kwh ?? null, " kWh", 0) },
 | |
|     { label: "Monthly Consumption", value: formatValue(kpiData?.consumption_kwh ?? null, " kWh", 0) },
 | |
|     { label: "Monthly Grid Draw", value: formatValue(kpiData?.grid_draw_kwh ?? null, " kWh", 0) },
 | |
|     { label: "Efficiency", value: formatValue(kpiData?.efficiency ?? null, "%", 1) },
 | |
|     { label: "Peak Demand", value: formatValue(kpiData?.peak_demand_kw ?? null, " kW") },
 | |
|     { label: "Power Factor", value: formatValue(kpiData?.avg_power_factor ?? null) },
 | |
|     { label: "Load Factor", value: formatValue(kpiData?.load_factor ?? null) },
 | |
|   ];
 | |
| 
 | |
|   return (
 | |
|     <div>
 | |
|       <h2 className="text-lg font-bold mb-2 dark:text-white">Monthly KPI</h2>
 | |
|       <div className="min-h-[275px] border rounded">
 | |
|         {!siteId ? (
 | |
|           <p className="text-center py-10">No site selected</p>
 | |
|         ) : loading ? (
 | |
|           <p className="text-center py-10">Loading...</p>
 | |
|         ) : (
 | |
|           <table className="w-full border-collapse">
 | |
|             <thead>
 | |
|               <tr className="bg-gray-100 dark:bg-rtgray-800">
 | |
|                 <th className="border p-3 text-left dark:text-white">KPI</th>
 | |
|                 <th className="border p-3 text-left dark:text-white">Value</th>
 | |
|               </tr>
 | |
|             </thead>
 | |
|             <tbody>
 | |
|               {rows.map((row) => (
 | |
|                 <tr key={row.label} className="even:bg-gray-50 dark:even:bg-rtgray-800">
 | |
|                   <td className="border p-2.5 dark:text-white">{row.label}</td>
 | |
|                   <td className="border p-2.5 dark:text-white">{row.value}</td>
 | |
|                 </tr>
 | |
|               ))}
 | |
|             </tbody>
 | |
|           </table>
 | |
|         )}
 | |
|       </div>
 | |
|     </div>
 | |
|   );
 | |
| };
 | |
| 
 | |
| export default KPI_Table;
 | |
| 
 | |
| 
 | |
| 
 |