144 lines
4.0 KiB
TypeScript
144 lines
4.0 KiB
TypeScript
import React, { useRef, useEffect, useState } from 'react';
|
|
import { Line } from 'react-chartjs-2';
|
|
import ChartJS from 'chart.js/auto';
|
|
import zoomPlugin from 'chartjs-plugin-zoom';
|
|
|
|
ChartJS.register(zoomPlugin);
|
|
|
|
interface EnergyLineChartProps {
|
|
consumptionData: number[];
|
|
generationData: number[];
|
|
}
|
|
|
|
const labels = [
|
|
'08:00', '08:30', '09:00', '09:30', '10:00',
|
|
'10:30', '11:00', '11:30', '12:00', '12:30',
|
|
'13:00', '13:30', '14:00', '14:30', '15:00',
|
|
'15:30', '16:00', '16:30', '17:00',
|
|
];
|
|
|
|
const EnergyLineChart = ({ consumptionData, generationData }: EnergyLineChartProps) => {
|
|
const chartRef = useRef<any>(null);
|
|
|
|
const [startIndex, setStartIndex] = useState(0);
|
|
const [endIndex, setEndIndex] = useState(labels.length - 1);
|
|
|
|
useEffect(() => {
|
|
if (typeof window !== 'undefined') {
|
|
import('hammerjs');
|
|
}
|
|
}, []);
|
|
|
|
// Filter data arrays based on selected range
|
|
const filteredConsumption = consumptionData.slice(startIndex, endIndex + 1);
|
|
const filteredGeneration = generationData.slice(startIndex, endIndex + 1);
|
|
const filteredLabels = labels.slice(startIndex, endIndex + 1);
|
|
|
|
const allDataPoints = [...filteredConsumption, ...filteredGeneration];
|
|
const maxDataValue = allDataPoints.length > 0 ? Math.max(...allDataPoints) : 0;
|
|
const yAxisSuggestedMax = maxDataValue * 1.15;
|
|
|
|
const data = {
|
|
labels: filteredLabels,
|
|
datasets: [
|
|
{
|
|
label: 'Consumption',
|
|
data: filteredConsumption,
|
|
borderColor: '#8884d8',
|
|
tension: 0.4,
|
|
fill: false,
|
|
},
|
|
{
|
|
label: 'Generation',
|
|
data: filteredGeneration,
|
|
borderColor: '#82ca9d',
|
|
tension: 0.4,
|
|
fill: false,
|
|
},
|
|
],
|
|
};
|
|
|
|
const options = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: { position: 'top' as const },
|
|
zoom: {
|
|
zoom: {
|
|
wheel: { enabled: true },
|
|
pinch: { enabled: true },
|
|
mode: 'x' as const,
|
|
},
|
|
pan: { enabled: true, mode: 'x' as const },
|
|
},
|
|
tooltip: { enabled: true, mode: 'index' as const, intersect: false },
|
|
},
|
|
scales: {
|
|
y: { beginAtZero: true, suggestedMax: yAxisSuggestedMax },
|
|
},
|
|
};
|
|
|
|
const handleResetZoom = () => {
|
|
chartRef.current?.resetZoom();
|
|
};
|
|
|
|
return (
|
|
<div className="bg-white p-4 rounded-lg shadow-md dark:bg-gray-800 dark:text-white-light">
|
|
<div className="h-98 w-full">
|
|
<div className="flex justify-between items-center mb-2">
|
|
<h2 className="text-lg font-bold dark:text-white-light">Energy Consumption & Generation</h2>
|
|
<button onClick={handleResetZoom} className="btn-primary px-8 py-2 text-sm">
|
|
Reset
|
|
</button>
|
|
</div>
|
|
|
|
{/* Time range selectors */}
|
|
<div className="mb-4 flex gap-4 items-center">
|
|
<label className='font-medium'>
|
|
From:{' '}
|
|
<select
|
|
value={startIndex}
|
|
onChange={(e) => {
|
|
const val = Number(e.target.value);
|
|
setStartIndex(val <= endIndex ? val : endIndex);
|
|
}}
|
|
className="border rounded p-1"
|
|
>
|
|
{labels.map((label, idx) => (
|
|
<option key={idx} value={idx}>
|
|
{label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<label className='font-medium'>
|
|
To:{' '}
|
|
<select
|
|
value={endIndex}
|
|
onChange={(e) => {
|
|
const val = Number(e.target.value);
|
|
setEndIndex(val >= startIndex ? val : startIndex);
|
|
}}
|
|
className="border rounded p-1"
|
|
>
|
|
{labels.map((label, idx) => (
|
|
<option key={idx} value={idx}>
|
|
{label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
</div>
|
|
|
|
<div className="h-96 w-full">
|
|
<Line ref={chartRef} data={data} options={options} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default EnergyLineChart;
|
|
|
|
|