From 5d84f15b9c6e0991e154d0f072f9c2e612fdac1e Mon Sep 17 00:00:00 2001
From: Navan Chauhan <navanchauhan@gmail.com>
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/src/MarketDataPage.jsx')

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>
-- 
cgit v1.2.3

