diff options
author | Navan Chauhan <navanchauhan@gmail.com> | 2025-04-27 21:42:45 -0600 |
---|---|---|
committer | Navan Chauhan <navanchauhan@gmail.com> | 2025-04-27 21:42:45 -0600 |
commit | 5d84f15b9c6e0991e154d0f072f9c2e612fdac1e (patch) | |
tree | fd5ff0693a204c78b3f7e381e0b1bbbb17a855fd | |
parent | 3c668861df81da46af3b684ea8e97eb64ed982b0 (diff) |
fix data zoom being reset
-rw-r--r-- | client/src/MarketDataPage.jsx | 143 |
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> |