update monthlybarchart to extract from db
This commit is contained in:
parent
5a17af8486
commit
606c1047f6
@ -194,7 +194,7 @@ const AdminDashboard = () => {
|
||||
|
||||
</div>
|
||||
<div ref={monthlyChartRef} className="pb-5">
|
||||
<MonthlyBarChart siteData={currentSiteDetails} />
|
||||
<MonthlyBarChart siteId={siteIdMap[selectedSite]} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user