aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNavan Chauhan <navanchauhan@gmail.com>2025-04-27 21:42:45 -0600
committerNavan Chauhan <navanchauhan@gmail.com>2025-04-27 21:42:45 -0600
commit5d84f15b9c6e0991e154d0f072f9c2e612fdac1e (patch)
treefd5ff0693a204c78b3f7e381e0b1bbbb17a855fd
parent3c668861df81da46af3b684ea8e97eb64ed982b0 (diff)
fix data zoom being reset
-rw-r--r--client/src/MarketDataPage.jsx143
1 files changed, 92 insertions, 51 deletions
diff --git a/client/src/MarketDataPage.jsx b/client/src/MarketDataPage.jsx
index 4da1969..f874baa 100644
--- a/client/src/MarketDataPage.jsx
+++ b/client/src/MarketDataPage.jsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState, useContext } from 'react';
+import React, { useEffect, useState, useContext, useRef, useMemo } from 'react';
import { Typography, Spin, Message, Card, Grid } from '@arco-design/web-react';
import '@arco-design/web-react/dist/css/arco.css';
import { initVChartArcoTheme } from '@visactor/vchart-arco-theme';
@@ -40,6 +40,8 @@ function TimeAgo({ timestamp, prefix }) {
function MarketDataPage() {
const { selectedMarket } = useContext(MarketContext);
+ const fiveMinChartRef = useRef(null);
+ const dayAheadChartRef = useRef(null);
const [fiveMinZoom, setFiveMinZoom] = useState({ start: 0.9, end: 1 });
const [dayAheadZoom, setDayAheadZoom] = useState({ start: 0.05, end: 1 });
const [fiveMinData, setFiveMinData] = useState([]);
@@ -47,7 +49,16 @@ function MarketDataPage() {
const [loading, setLoading] = useState(true);
const [fiveMinLastUpdated, setFiveMinLastUpdated] = useState(null);
const [dayAheadLastUpdated, setDayAheadLastUpdated] = useState(null);
- const [now, setNow] = useState(new Date());
+ const [_, setNow] = useState(new Date());
+
+ const handleZoomChange = (e, title) => {
+ if (!e?.start || !e?.end) return;
+ if (title === 'fiveMin') {
+ setFiveMinZoom({ start: e.start, end: e.end });
+ } else {
+ setDayAheadZoom({ start: e.start, end: e.end });
+ }
+ };
useEffect(() => {
const fetchFiveMinData = async () => {
@@ -100,46 +111,73 @@ function MarketDataPage() {
};
}, [selectedMarket]);
- const getChartSpec = (data, title, zoom) => ({
- type: 'line',
- data: {
- values: data.map(d => ({
- timestamp: new Date(d.timestamp).toLocaleString(),
- LMP: d.lmp,
- Energy: d.energy,
- Congestion: d.congestion,
- Loss: d.loss
- })),
- transforms: [{
- type: 'fold',
- options: { key: 'name', value: 'value', fields: ['LMP', 'Energy', 'Congestion', 'Loss'] }
+ const fiveMinSpec = useMemo(() => ({
+ type: 'line',
+ data: {
+ values: fiveMinData.map(d => ({
+ timestamp: new Date(d.timestamp).toLocaleString(),
+ LMP: d.lmp,
+ Energy: d.energy,
+ Congestion: d.congestion,
+ Loss: d.loss
+ })),
+ transforms: [{
+ type: 'fold',
+ options: { key: 'name', value: 'value', fields: ['LMP', 'Energy', 'Congestion', 'Loss'] }
+ }]
+ },
+ xField: 'timestamp',
+ yField: 'value',
+ seriesField: 'name',
+ smooth: true,
+ legend: { position: 'top' },
+ tooltip: {
+ formatter: (datum) => ({
+ name: datum.name,
+ value: datum.value.toFixed(2)
+ })
+ },
+ dataZoom: [{
+ orient: 'bottom',
+ height: 20,
+ start: fiveMinZoom.start,
+ end: fiveMinZoom.end
}]
- },
- xField: 'timestamp',
- yField: 'value',
- seriesField: 'name',
- smooth: true,
- legend: { position: 'top' },
- tooltip: {
- formatter: (datum) => ({
- name: datum.name,
- value: datum.value.toFixed(2)
- })
- },
- dataZoom: [{
- orient: 'bottom',
- height: 20,
- start: zoom?.start ?? (title === 'fiveMin' ? 0.9 : 0.05),
- end: zoom?.end ?? 1,
- onChange: (e) => {
- if (title === 'fiveMin') {
- setFiveMinZoom({ start: e.start, end: e.end });
- } else {
- setDayAheadZoom({ start: e.start, end: e.end });
- }
- }
- }]
- });
+ }), [fiveMinData, fiveMinZoom]); // <== DEPENDENCIES
+
+ const dayAheadSpec = useMemo(() => ({
+ type: 'line',
+ data: {
+ values: dayAheadData.map(d => ({
+ timestamp: new Date(d.timestamp).toLocaleString(),
+ LMP: d.lmp,
+ Energy: d.energy,
+ Congestion: d.congestion,
+ Loss: d.loss
+ })),
+ transforms: [{
+ type: 'fold',
+ options: { key: 'name', value: 'value', fields: ['LMP', 'Energy', 'Congestion', 'Loss'] }
+ }]
+ },
+ xField: 'timestamp',
+ yField: 'value',
+ seriesField: 'name',
+ smooth: true,
+ legend: { position: 'top' },
+ tooltip: {
+ formatter: (datum) => ({
+ name: datum.name,
+ value: datum.value.toFixed(2)
+ })
+ },
+ dataZoom: [{
+ orient: 'bottom',
+ height: 20,
+ start: dayAheadZoom.start,
+ end: dayAheadZoom.end
+ }]
+ }), [dayAheadData, dayAheadZoom]);
const computeKPIs = (data) => {
if (!data.length) return { avgLmp: 0, totalEnergy: 0, maxCongestion: 0, avgLoss: 0 };
@@ -163,12 +201,7 @@ function MarketDataPage() {
<Spin style={{ display: 'block', margin: '80px auto' }} />
) : (
<>
- <div style={{ textAlign: 'center', marginBottom: 24 }}>
- <TimeAgo timestamp={fiveMinLastUpdated} prefix="Real-Time Last Updated" />
- <TimeAgo timestamp={dayAheadLastUpdated} prefix="Day-Ahead Last Updated" />
- </div>
-
- <Row gutter={24} style={{ marginBottom: 30 }}>
+ <Row gutter={24} style={{ marginBottom: 30 }}>
{[
{ label: 'Avg Real-Time LMP ($/MWh)', value: fiveMinKPIs.avgLmp },
{ label: 'Total Real-Time Energy (MWh)', value: fiveMinKPIs.totalEnergy },
@@ -216,16 +249,24 @@ function MarketDataPage() {
<Row gutter={24}>
<Col xs={24} md={12}>
- <Card title="Real-Time Market Data" style={{ borderRadius: 12, marginBottom: 24 }}>
+ <Card title="Real-Time Market Data" extra={<TimeAgo timestamp={fiveMinLastUpdated} prefix="Updated" />} style={{ borderRadius: 12, marginBottom: 24 }}>
<div style={{ padding: 10 }}>
- <VChart spec={getChartSpec(fiveMinData, 'fiveMin', fiveMinZoom)} />
+ <VChart
+ spec={fiveMinSpec}
+ ref={fiveMinChartRef}
+ onDataZoom={(e) => handleZoomChange(e.detail, 'fiveMin')}
+ />
</div>
</Card>
</Col>
<Col xs={24} md={12}>
- <Card title="Day-Ahead Market Data" style={{ borderRadius: 12, marginBottom: 24 }}>
+ <Card title="Day-Ahead Market Data" extra={<TimeAgo timestamp={dayAheadLastUpdated} prefix="Updated" />} style={{ borderRadius: 12, marginBottom: 24 }}>
<div style={{ padding: 10 }}>
- <VChart spec={getChartSpec(dayAheadData, 'dayAhead', dayAheadZoom)} />
+ <VChart
+ spec={dayAheadSpec}
+ ref={dayAheadChartRef}
+ onDataZoom={(e) => handleZoomChange(e.detail, 'dayAhead')}
+ />
</div>
</Card>
</Col>