update monthlybarchart to extract from db

This commit is contained in:
Syasya 2025-07-31 13:59:09 +08:00
parent 5a17af8486
commit 606c1047f6
2 changed files with 117 additions and 71 deletions

View File

@ -194,7 +194,7 @@ const AdminDashboard = () => {
</div>
<div ref={monthlyChartRef} className="pb-5">
<MonthlyBarChart siteData={currentSiteDetails} />
<MonthlyBarChart siteId={siteIdMap[selectedSite]} />
</div>
</div>

View File

@ -1,93 +1,139 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import {
BarChart,
Bar,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
Legend,
BarChart,
Bar,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
Legend,
} from 'recharts';
import { SiteDetails } from '@/types/SiteData';
import { format } from 'date-fns';
import { fetchPowerTimeseries } from '@/app/utils/api';
interface MonthlyBarChartProps {
siteData: SiteDetails | null;
siteId: string;
}
interface TimeSeriesEntry {
time: string;
value: number;
}
const groupTimeSeries = (
data: TimeSeriesEntry[],
mode: 'monthly'
): TimeSeriesEntry[] => {
const groupMap = new Map<string, number[]>();
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 consumptionColor = '#003049';
const generationColor = '#669bbc';
// Month names for X-axis labels
const MONTHS = [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
];
const MonthlyBarChart: React.FC<MonthlyBarChartProps> = ({ siteId }) => {
const [chartData, setChartData] = useState<
{ month: string; consumption: number; generation: number }[]
>([]);
const [loading, setLoading] = useState(true);
const MonthlyBarChart: React.FC<MonthlyBarChartProps> = ({ siteData }) => {
const chartData = React.useMemo(() => {
if (
!siteData ||
siteData.consumptionData.length === 0 ||
siteData.generationData.length === 0
) {
return [];
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<string, { consumption: number; generation: number }>();
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;
}
// Initialize totals
const monthlyData = Array.from({ length: 12 }, (_, month) => ({
month: MONTHS[month],
consumption: 0,
generation: 0,
}));
// Group daily data into months (assume data is in order from Jan 1 to Dec 31)
// Group daily data into months (assume data is in order from Jan 1 to Dec 31)
for (let i = 0; i < siteData.consumptionData.length; i++) {
const monthIndex = Math.floor(i / 30.42); // Rough approximation
if (monthIndex < 12) {
monthlyData[monthIndex].consumption += siteData.consumptionData[i];
monthlyData[monthIndex].generation += siteData.generationData[i];
}
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;
}
// ✅ Only return the last 6 months
return monthlyData.slice(-6);
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,
}));
}, [siteData]);
setChartData(formatted.slice(-6)); // last 6 months
} catch (error) {
console.error('Failed to fetch monthly power data:', error);
setChartData([]);
} finally {
setLoading(false);
}
};
if (!siteData || chartData.length === 0) {
return (
<div className="bg-white p-4 rounded-lg shadow-md dark:bg-gray-800 dark:text-white-light">
<div className="flex justify-between items-center mb-2">
<h2 className="text-lg font-bold pb-3">Monthly Energy Yield</h2>
</div>
<div className="h-96 w-full flex items-center justify-center">
<p className="text-white/70">No data available for chart. Please select a site.</p>
</div>
</div>
);
}
fetchMonthlyData();
}, [siteId]);
if (loading || !siteId || chartData.length === 0) {
return (
<div className="bg-white p-4 rounded-lg shadow-md dark:bg-gray-800 dark:text-white-light">
<div className="flex justify-between items-center mb-2">
<h2 className="text-lg font-bold pb-3">Monthly Energy Yield</h2>
</div>
<div className="lg:h-[22.6vw] h-[290px] w-full pt-10">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={chartData}>
<XAxis dataKey="month" tick={{ fontSize: 10 }}/>
<YAxis tick={{ fontSize: 10 }} />
<Tooltip />
<Legend />
<Bar dataKey="consumption" fill={consumptionColor} name="Consumption (kWh)" />
<Bar dataKey="generation" fill={generationColor} name="Generation (kWh)" />
</BarChart>
</ResponsiveContainer>
</div>
<div className="bg-white p-4 rounded-lg shadow-md dark:bg-gray-800 dark:text-white-light">
<div className="flex justify-between items-center mb-2">
<h2 className="text-lg font-bold pb-3">Monthly Energy Yield</h2>
</div>
<div className="h-96 w-full flex items-center justify-center">
<p className="text-white/70">
{loading ? 'Loading data...' : 'No data available for chart.'}
</p>
</div>
</div>
);
}
return (
<div className="bg-white p-4 rounded-lg shadow-md dark:bg-gray-800 dark:text-white-light">
<div className="flex justify-between items-center mb-2">
<h2 className="text-lg font-bold pb-3">Monthly Energy Yield</h2>
</div>
<div className="lg:h-[22.6vw] h-[290px] w-full pt-10">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={chartData}>
<XAxis dataKey="month" tick={{ fontSize: 10 }} />
<YAxis tick={{ fontSize: 10 }} />
<Tooltip />
<Legend />
<Bar dataKey="consumption" fill={consumptionColor} name="Consumption (kWh)" />
<Bar dataKey="generation" fill={generationColor} name="Generation (kWh)" />
</BarChart>
</ResponsiveContainer>
</div>
</div>
);
};
export default MonthlyBarChart;