KPI extract db

This commit is contained in:
Syasya 2025-07-31 18:17:16 +08:00
parent 606c1047f6
commit ec9d840804
2 changed files with 86 additions and 72 deletions

View File

@ -184,7 +184,7 @@ const AdminDashboard = () => {
</div> </div>
<div> <div>
<KPI_Table siteData={currentSiteDetails} /> <KPI_Table siteId={siteIdMap[selectedSite]} month="2025-07" />
</div> </div>
</div> </div>
@ -199,9 +199,6 @@ const AdminDashboard = () => {
</div> </div>
<div className="flex flex-col md:flex-row gap-4 justify-center"> <div className="flex flex-col md:flex-row gap-4 justify-center">
<button onClick={handleCSVExport} className="text-sm lg:text-lg btn-primary">
Export Raw Data to CSV
</button>
<button onClick={handlePDFExport} className="text-sm lg:text-lg btn-primary"> <button onClick={handlePDFExport} className="text-sm lg:text-lg btn-primary">
Export Chart Images to PDF Export Chart Images to PDF
</button> </button>

View File

@ -1,59 +1,74 @@
// components/KPI_Table.tsx import React, { useEffect, useState } from 'react';
import React from 'react';
import { SiteDetails } from '@/types/SiteData'; // Adjust import path as necessary
interface KPI_TableProps { interface KPI_TableProps {
siteData: SiteDetails | null; // Pass the selected site's data as a prop siteId: string;
month: string; // format: "YYYY-MM"
} }
const KPI_Table: React.FC<KPI_TableProps> = ({ siteData }) => { interface MonthlyKPI {
site: string;
month: string;
yield_kwh: number | null;
consumption_kwh: number | null;
grid_draw_kwh: number | null;
efficiency: number | null;
}
if (!siteData) { const KPI_Table: React.FC<KPI_TableProps> = ({ siteId, month }) => {
const [kpiData, setKpiData] = useState<MonthlyKPI | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchKPI = async () => {
setLoading(true);
try {
const res = await fetch(`http://localhost:8000/kpi/monthly?site=${siteId}&month=${month}`);
const data = await res.json();
setKpiData(data);
} catch (err) {
console.error('Failed to fetch KPI:', err);
setKpiData(null); // fallback
} finally {
setLoading(false);
}
};
if (siteId && month) fetchKPI();
}, [siteId, month]);
if (!siteId) {
return ( return (
<div> <div>
<h2 className="text-lg font-bold mb-2">Monthly KPI</h2> <h2 className="text-lg font-bold mb-2">Monthly KPI</h2>
<p className="text-white/70">Select a site to view KPIs.</p>
<div className="min-h-[275px] w-full flex items-center justify-center border"> <div className="min-h-[275px] w-full flex items-center justify-center border">
<p>No data available</p> <p>No site selected</p>
</div> </div>
</div> </div>
); );
} }
// --- KPI Calculations --- if (loading) {
const monthlyYield = siteData.generationData.reduce((sum, daily) => sum + daily, 0); return (
const monthlyConsumption = siteData.consumptionData.reduce((sum, daily) => sum + daily, 0); <div>
<h2 className="text-lg font-bold mb-2">Monthly KPI</h2>
// Calculate Grid Draw/Export <div className="min-h-[275px] w-full flex items-center justify-center border">
let monthlyGridDraw = 0; <p>Loading...</p>
let monthlySolarExport = 0; </div>
</div>
// A simplistic model for grid draw/export: );
// If generation > consumption, excess is exported.
// If consumption > generation, deficit is drawn from grid.
// Note: This is highly simplified. Real-world calculations involve time-of-use, battery storage, etc.
const netEnergy = monthlyYield - monthlyConsumption;
if (netEnergy > 0) {
monthlySolarExport = netEnergy;
} else {
monthlyGridDraw = Math.abs(netEnergy);
} }
const selfConsumption = Math.min(monthlyYield, monthlyConsumption); // Use optional chaining and nullish coalescing to safely default values to 0
const savingsFromSelfConsumption = selfConsumption * siteData.gridImportPrice_RM_per_kWh; const yield_kwh = kpiData?.yield_kwh ?? 0;
const revenueFromExport = monthlySolarExport * siteData.solarExportTariff_RM_per_kWh; const consumption_kwh = kpiData?.consumption_kwh ?? 0;
const monthlySaved = savingsFromSelfConsumption + revenueFromExport; const grid_draw_kwh = kpiData?.grid_draw_kwh ?? 0;
const efficiency = siteData.theoreticalMaxGeneration_kWh && siteData.theoreticalMaxGeneration_kWh > 0 const efficiency = kpiData?.efficiency ?? 0;
? (monthlyYield / siteData.theoreticalMaxGeneration_kWh) * 100
: 0; // Default to 0 or 'N/A' if theoretical max is not set or zero
const data = [ const data = [
{ kpi: 'Monthly Yield', value: `${monthlyYield.toFixed(0)} kWh` }, { kpi: 'Monthly Yield', value: `${yield_kwh.toFixed(0)} kWh` },
{ kpi: 'Monthly Consumption', value: `${monthlyConsumption.toFixed(0)} kWh` }, { kpi: 'Monthly Consumption', value: `${consumption_kwh.toFixed(0)} kWh` },
{ kpi: 'Monthly Grid Draw', value: `${monthlyGridDraw.toFixed(0)} kWh` }, { kpi: 'Monthly Grid Draw', value: `${grid_draw_kwh.toFixed(0)} kWh` },
// { kpi: 'Monthly Solar Export', value: `${monthlySolarExport.toFixed(0)} kWh` }, // Can add this if desired
{ kpi: 'Efficiency', value: `${efficiency.toFixed(1)}%` }, { kpi: 'Efficiency', value: `${efficiency.toFixed(1)}%` },
{ kpi: 'Monthly Saved', value: `RM ${monthlySaved.toFixed(2)}` },
]; ];
return ( return (
@ -61,14 +76,14 @@ const KPI_Table: React.FC<KPI_TableProps> = ({ siteData }) => {
<h2 className="text-lg font-bold mb-2">Monthly KPI</h2> <h2 className="text-lg font-bold mb-2">Monthly KPI</h2>
<table className="min-h-[275px] w-full border-collapse border border-gray-700 text-black bg-white"> <table className="min-h-[275px] w-full border-collapse border border-gray-700 text-black bg-white">
<thead> <thead>
<tr className="bg-gray-800"> <tr className="bg-gray-800 text-black">
<th className="border border-gray-700 p-3 text-left">KPI</th> <th className="border border-gray-700 p-3 text-left">KPI</th>
<th className="border border-gray-700 p-3 text-left">Value</th> <th className="border border-gray-700 p-3 text-left">Value</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{data.map((row) => ( {data.map((row) => (
<tr key={row.kpi} className=""> <tr key={row.kpi}>
<td className="border border-gray-700 p-3">{row.kpi}</td> <td className="border border-gray-700 p-3">{row.kpi}</td>
<td className="border border-gray-700 p-3">{row.value}</td> <td className="border border-gray-700 p-3">{row.value}</td>
</tr> </tr>
@ -80,3 +95,5 @@ const KPI_Table: React.FC<KPI_TableProps> = ({ siteData }) => {
}; };
export default KPI_Table; export default KPI_Table;