From 5d84f15b9c6e0991e154d0f072f9c2e612fdac1e Mon Sep 17 00:00:00 2001 From: Navan Chauhan Date: Sun, 27 Apr 2025 21:42:45 -0600 Subject: fix data zoom being reset --- client/src/MarketDataPage.jsx | 143 +++++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 51 deletions(-) (limited to 'client') 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() { ) : ( <> -
- - -
- - + {[ { label: 'Avg Real-Time LMP ($/MWh)', value: fiveMinKPIs.avgLmp }, { label: 'Total Real-Time Energy (MWh)', value: fiveMinKPIs.totalEnergy }, @@ -216,16 +249,24 @@ function MarketDataPage() { - + } style={{ borderRadius: 12, marginBottom: 24 }}>
- + handleZoomChange(e.detail, 'fiveMin')} + />
- + } style={{ borderRadius: 12, marginBottom: 24 }}>
- + handleZoomChange(e.detail, 'dayAhead')} + />
-- cgit v1.2.3