feature/syasya/testlayout #7
@ -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>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,82 +1,99 @@
 | 
				
			|||||||
// 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 }) => {
 | 
				
			||||||
        return (
 | 
					  const [kpiData, setKpiData] = useState<MonthlyKPI | null>(null);
 | 
				
			||||||
            <div>
 | 
					  const [loading, setLoading] = useState(false);
 | 
				
			||||||
                <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">
 | 
					 | 
				
			||||||
                    <p>No data available</p>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // --- KPI Calculations ---
 | 
					  useEffect(() => {
 | 
				
			||||||
    const monthlyYield = siteData.generationData.reduce((sum, daily) => sum + daily, 0);
 | 
					    const fetchKPI = async () => {
 | 
				
			||||||
    const monthlyConsumption = siteData.consumptionData.reduce((sum, daily) => sum + daily, 0);
 | 
					      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);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Calculate Grid Draw/Export
 | 
					    if (siteId && month) fetchKPI();
 | 
				
			||||||
    let monthlyGridDraw = 0;
 | 
					  }, [siteId, month]);
 | 
				
			||||||
    let monthlySolarExport = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 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);
 | 
					 | 
				
			||||||
    const savingsFromSelfConsumption = selfConsumption * siteData.gridImportPrice_RM_per_kWh;
 | 
					 | 
				
			||||||
    const revenueFromExport = monthlySolarExport * siteData.solarExportTariff_RM_per_kWh;
 | 
					 | 
				
			||||||
    const monthlySaved = savingsFromSelfConsumption + revenueFromExport;
 | 
					 | 
				
			||||||
    const efficiency = siteData.theoreticalMaxGeneration_kWh && siteData.theoreticalMaxGeneration_kWh > 0
 | 
					 | 
				
			||||||
        ? (monthlyYield / siteData.theoreticalMaxGeneration_kWh) * 100
 | 
					 | 
				
			||||||
        : 0; // Default to 0 or 'N/A' if theoretical max is not set or zero
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const data = [
 | 
					 | 
				
			||||||
        { kpi: 'Monthly Yield', value: `${monthlyYield.toFixed(0)} kWh` },
 | 
					 | 
				
			||||||
        { kpi: 'Monthly Consumption', value: `${monthlyConsumption.toFixed(0)} kWh` },
 | 
					 | 
				
			||||||
        { kpi: 'Monthly Grid Draw', value: `${monthlyGridDraw.toFixed(0)} kWh` },
 | 
					 | 
				
			||||||
        // { kpi: 'Monthly Solar Export', value: `${monthlySolarExport.toFixed(0)} kWh` }, // Can add this if desired
 | 
					 | 
				
			||||||
        { kpi: 'Efficiency', value: `${efficiency.toFixed(1)}%` },
 | 
					 | 
				
			||||||
        { kpi: 'Monthly Saved', value: `RM ${monthlySaved.toFixed(2)}` },
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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>
 | 
				
			||||||
            <table className="min-h-[275px] w-full border-collapse border border-gray-700 text-black bg-white">
 | 
					        <div className="min-h-[275px] w-full flex items-center justify-center border">
 | 
				
			||||||
                <thead>
 | 
					          <p>No site selected</p>
 | 
				
			||||||
                    <tr className="bg-gray-800">
 | 
					 | 
				
			||||||
                        <th className="border border-gray-700 p-3 text-left">KPI</th>
 | 
					 | 
				
			||||||
                        <th className="border border-gray-700 p-3 text-left">Value</th>
 | 
					 | 
				
			||||||
                    </tr>
 | 
					 | 
				
			||||||
                </thead>
 | 
					 | 
				
			||||||
                <tbody>
 | 
					 | 
				
			||||||
                    {data.map((row) => (
 | 
					 | 
				
			||||||
                        <tr key={row.kpi} className="">
 | 
					 | 
				
			||||||
                            <td className="border border-gray-700 p-3">{row.kpi}</td>
 | 
					 | 
				
			||||||
                            <td className="border border-gray-700 p-3">{row.value}</td>
 | 
					 | 
				
			||||||
                        </tr>
 | 
					 | 
				
			||||||
                    ))}
 | 
					 | 
				
			||||||
                </tbody>
 | 
					 | 
				
			||||||
            </table>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (loading) {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div>
 | 
				
			||||||
 | 
					        <h2 className="text-lg font-bold mb-2">Monthly KPI</h2>
 | 
				
			||||||
 | 
					        <div className="min-h-[275px] w-full flex items-center justify-center border">
 | 
				
			||||||
 | 
					          <p>Loading...</p>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Use optional chaining and nullish coalescing to safely default values to 0
 | 
				
			||||||
 | 
					  const yield_kwh = kpiData?.yield_kwh ?? 0;
 | 
				
			||||||
 | 
					  const consumption_kwh = kpiData?.consumption_kwh ?? 0;
 | 
				
			||||||
 | 
					  const grid_draw_kwh = kpiData?.grid_draw_kwh ?? 0;
 | 
				
			||||||
 | 
					  const efficiency = kpiData?.efficiency ?? 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const data = [
 | 
				
			||||||
 | 
					    { kpi: 'Monthly Yield', value: `${yield_kwh.toFixed(0)} kWh` },
 | 
				
			||||||
 | 
					    { kpi: 'Monthly Consumption', value: `${consumption_kwh.toFixed(0)} kWh` },
 | 
				
			||||||
 | 
					    { kpi: 'Monthly Grid Draw', value: `${grid_draw_kwh.toFixed(0)} kWh` },
 | 
				
			||||||
 | 
					    { kpi: 'Efficiency', value: `${efficiency.toFixed(1)}%` },
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <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">
 | 
				
			||||||
 | 
					        <thead>
 | 
				
			||||||
 | 
					          <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">Value</th>
 | 
				
			||||||
 | 
					          </tr>
 | 
				
			||||||
 | 
					        </thead>
 | 
				
			||||||
 | 
					        <tbody>
 | 
				
			||||||
 | 
					          {data.map((row) => (
 | 
				
			||||||
 | 
					            <tr key={row.kpi}>
 | 
				
			||||||
 | 
					              <td className="border border-gray-700 p-3">{row.kpi}</td>
 | 
				
			||||||
 | 
					              <td className="border border-gray-700 p-3">{row.value}</td>
 | 
				
			||||||
 | 
					            </tr>
 | 
				
			||||||
 | 
					          ))}
 | 
				
			||||||
 | 
					        </tbody>
 | 
				
			||||||
 | 
					      </table>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default KPI_Table;
 | 
					export default KPI_Table;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user