'use client'; import { useState, useEffect, useRef } from 'react'; import { useRouter, usePathname, useSearchParams } from 'next/navigation'; import SiteSelector from '@/components/dashboards/SiteSelector'; import SiteStatus from '@/components/dashboards/SiteStatus'; import KPI_Table from '@/components/dashboards/KPIStatus'; import DashboardLayout from './dashlayout'; import html2canvas from 'html2canvas'; import jsPDF from 'jspdf'; import dynamic from 'next/dynamic'; import { fetchPowerTimeseries } from '@/app/utils/api'; import KpiTop from '@/components/dashboards/kpitop'; import KpiBottom from '@/components/dashboards/kpibottom'; const EnergyLineChart = dynamic(() => import('@/components/dashboards/EnergyLineChart'), { ssr: false, }); const MonthlyBarChart = dynamic(() => import('@/components/dashboards/MonthlyBarChart'), { ssr: false, }); type 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; error?: string; }; const API = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:8000'; import { SiteName, SiteDetails, mockSiteData } from '@/types/SiteData'; const AdminDashboard = () => { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const siteIdMap: Record = { 'Site A': 'site_01', 'Site B': 'site_02', 'Site C': 'site_03', }; const siteParam = searchParams?.get('site'); const validSiteNames: SiteName[] = ['Site A', 'Site B', 'Site C']; const [kpi, setKpi] = useState(null); const [selectedSite, setSelectedSite] = useState(() => { if (siteParam && validSiteNames.includes(siteParam as SiteName)) { return siteParam as SiteName; } return 'Site A'; }); // Keep siteParam and selectedSite in sync useEffect(() => { if ( siteParam && validSiteNames.includes(siteParam as SiteName) && siteParam !== selectedSite ) { setSelectedSite(siteParam as SiteName); } }, [siteParam, selectedSite]); const [timeSeriesData, setTimeSeriesData] = useState<{ consumption: { time: string; value: number }[]; generation: { time: string; value: number }[]; }>({ consumption: [], generation: [] }); useEffect(() => { const fetchData = async () => { const siteId = siteIdMap[selectedSite]; const today = new Date(); // Format to YYYY-MM-DD const yyyyMMdd = today.toISOString().split('T')[0]; // Append Malaysia's +08:00 time zone manually const start = `${yyyyMMdd}T00:00:00+08:00`; const end = `${yyyyMMdd}T23:59:59+08:00`; try { const raw = await fetchPowerTimeseries(siteId, start, end); const consumption = raw.consumption.map(d => ({ time: d.time, value: d.value, })); const generation = raw.generation.map(d => ({ time: d.time, value: d.value, })); setTimeSeriesData({ consumption, generation }); } catch (error) { console.error('Failed to fetch power time series:', error); } }; fetchData(); }, [selectedSite]); // fetch KPI monthly (uses your FastAPI) useEffect(() => { const siteId = siteIdMap[selectedSite]; const url = `${API}/kpi/monthly?site=${encodeURIComponent(siteId)}&month=${currentMonth}`; fetch(url).then(r => r.json()).then(setKpi).catch(console.error); }, [selectedSite]); // derived values with safe fallbacks const yieldKwh = kpi?.yield_kwh ?? 0; const consumptionKwh = kpi?.consumption_kwh ?? 0; const gridDrawKwh = kpi?.grid_draw_kwh ?? Math.max(0, consumptionKwh - yieldKwh); const efficiencyPct = (kpi?.efficiency ?? 0) * 100; const powerFactor = kpi?.avg_power_factor ?? 0; const loadFactor = (kpi?.load_factor ?? 0); // ...your existing code above return() // Update query string when site is changed manually const handleSiteChange = (newSite: SiteName) => { setSelectedSite(newSite); const newUrl = `${pathname}?site=${encodeURIComponent(newSite)}`; router.push(newUrl); }; const currentSiteDetails: SiteDetails = mockSiteData[selectedSite] || { location: 'N/A', inverterProvider: 'N/A', emergencyContact: 'N/A', lastSyncTimestamp: 'N/A', consumptionData: [], generationData: [], systemStatus: 'N/A', temperature: 'N/A', solarPower: 0, realTimePower: 0, installedPower: 0, }; const handleCSVExport = () => { alert('Exported raw data to CSV (mock)'); }; const energyChartRef = useRef(null); const monthlyChartRef = useRef(null); const handlePDFExport = async () => { const doc = new jsPDF('p', 'mm', 'a4'); // portrait, millimeters, A4 const chartRefs = [ { ref: energyChartRef, title: 'Energy Line Chart' }, { ref: monthlyChartRef, title: 'Monthly Energy Yield' } ]; let yOffset = 10; for (const chart of chartRefs) { if (!chart.ref.current) continue; // Capture chart as image const canvas = await html2canvas(chart.ref.current, { scale: 2, // Higher scale for better resolution }); const imgData = canvas.toDataURL('image/png'); const imgProps = doc.getImageProperties(imgData); const pdfWidth = doc.internal.pageSize.getWidth() - 20; // 10 margin each side const imgHeight = (imgProps.height * pdfWidth) / imgProps.width; // Add title and image doc.setFontSize(14); doc.text(chart.title, 10, yOffset); yOffset += 6; // Space between title and chart // If content will overflow page, add a new page if (yOffset + imgHeight > doc.internal.pageSize.getHeight()) { doc.addPage(); yOffset = 10; } doc.addImage(imgData, 'PNG', 10, yOffset, pdfWidth, imgHeight); yOffset += imgHeight + 10; // Update offset for next chart } doc.save('dashboard_charts.pdf'); }; const currentMonth = new Date().toISOString().slice(0, 7); // "YYYY-MM" return (

Admin Dashboard

{/* TOP 3 CARDS */}
{/* BOTTOM 3 PANELS */}
} right={
{(kpi?.peak_demand_kw ?? 0).toFixed(2)} kW
} />
); }; export default AdminDashboard;