import React, { useEffect, useState } from 'react'; import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Legend, } from 'recharts'; import { format } from 'date-fns'; import { fetchPowerTimeseries } from '@/app/utils/api'; interface MonthlyBarChartProps { siteId: string; } interface TimeSeriesEntry { time: string; value: number; } const groupTimeSeries = ( data: TimeSeriesEntry[], mode: 'monthly' ): TimeSeriesEntry[] => { const groupMap = new Map(); for (const entry of data) { const date = new Date(entry.time); const key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!groupMap.has(key)) groupMap.set(key, []); groupMap.get(key)!.push(entry.value); } return Array.from(groupMap.entries()).map(([time, values]) => ({ time, value: values.reduce((sum, v) => sum + v, 0), })); }; const MonthlyBarChart: React.FC = ({ siteId }) => { const [chartData, setChartData] = useState< { month: string; consumption: number; generation: number }[] >([]); const [loading, setLoading] = useState(true); function useIsDarkMode() { const [isDark, setIsDark] = useState(() => typeof document !== 'undefined' ? document.body.classList.contains('dark') : false ); useEffect(() => { const check = () => setIsDark(document.body.classList.contains('dark')); check(); // Listen for class changes on const observer = new MutationObserver(check); observer.observe(document.body, { attributes: true, attributeFilter: ['class'] }); return () => observer.disconnect(); }, []); return isDark; } const isDark = useIsDarkMode(); const consumptionColor = isDark ? '#ba8e23' : '#003049'; const generationColor = isDark ? '#fcd913' : '#669bbc'; useEffect(() => { if (!siteId) return; const fetchMonthlyData = async () => { setLoading(true); const start = '2025-01-01T00:00:00+08:00'; const end = '2025-12-31T23:59:59+08:00'; try { const res = await fetchPowerTimeseries(siteId, start, end); const groupedConsumption = groupTimeSeries(res.consumption, 'monthly'); const groupedGeneration = groupTimeSeries(res.generation, 'monthly'); const monthMap = new Map(); for (const entry of groupedConsumption) { if (!monthMap.has(entry.time)) { monthMap.set(entry.time, { consumption: 0, generation: 0 }); } monthMap.get(entry.time)!.consumption = entry.value; } for (const entry of groupedGeneration) { if (!monthMap.has(entry.time)) { monthMap.set(entry.time, { consumption: 0, generation: 0 }); } monthMap.get(entry.time)!.generation = entry.value; } const formatted = Array.from(monthMap.entries()) .sort(([a], [b]) => a.localeCompare(b)) .map(([key, val]) => ({ month: format(new Date(`${key}-01`), 'MMM'), consumption: val.consumption, generation: val.generation, })); setChartData(formatted.slice(-6)); // last 6 months } catch (error) { console.error('Failed to fetch monthly power data:', error); setChartData([]); } finally { setLoading(false); } }; fetchMonthlyData(); }, [siteId]); if (loading || !siteId || chartData.length === 0) { return (

Monthly Energy Yield

{loading ? 'Loading data...' : 'No data available for chart.'}

); } return (
[`${value.toFixed(2)} kWh`]} labelFormatter={(label) => `${label}`} contentStyle={{ background: isDark ? '#232b3e' : '#fff', color: isDark ? '#fff' : '#222', border: isDark ? '1px solid #444' : '1px solid #ccc', }} labelStyle={{ color: isDark ? '#fff' : '#222', }} cursor={{ fill: isDark ? '#808080' : '#e0e7ef', // dark mode bg, light mode bg fillOpacity: isDark ? 0.6 : 0.3, // adjust opacity as you like }} />
); }; export default MonthlyBarChart;