From 9b54bad317b69d17134ddc27a5570984fba15826 Mon Sep 17 00:00:00 2001 From: Navan Chauhan Date: Sun, 27 Apr 2025 12:03:11 -0600 Subject: initial commit - client --- client/src/App.css | 38 +++++++++++++ client/src/App.js | 122 ++++++++++++++++++++++++++++++++++++++++++ client/src/App.test.js | 8 +++ client/src/index.css | 13 +++++ client/src/index.js | 23 ++++++++ client/src/logo.svg | 1 + client/src/reportWebVitals.js | 13 +++++ client/src/setupTests.js | 5 ++ 8 files changed, 223 insertions(+) create mode 100644 client/src/App.css create mode 100644 client/src/App.js create mode 100644 client/src/App.test.js create mode 100644 client/src/index.css create mode 100644 client/src/index.js create mode 100644 client/src/logo.svg create mode 100644 client/src/reportWebVitals.js create mode 100644 client/src/setupTests.js (limited to 'client/src') diff --git a/client/src/App.css b/client/src/App.css new file mode 100644 index 0000000..74b5e05 --- /dev/null +++ b/client/src/App.css @@ -0,0 +1,38 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/client/src/App.js b/client/src/App.js new file mode 100644 index 0000000..d130e08 --- /dev/null +++ b/client/src/App.js @@ -0,0 +1,122 @@ +import React, { useEffect, useState } from 'react'; +import { Typography, Spin, Message, Card, Grid, PageHeader } from '@arco-design/web-react'; +import '@arco-design/web-react/dist/css/arco.css'; +import { VChart } from '@visactor/react-vchart'; + +const { Row, Col } = Grid; +const ghostBgStyle = { + backgroundImage: 'radial-gradient(var(--color-fill-3) 1px, rgba(0, 0, 0, 0) 1px)', + backgroundSize: '16px 16px', + padding: 20, +}; + +function App() { + const [fiveMinData, setFiveMinData] = useState([]); + const [dayAheadData, setDayAheadData] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + Promise.all([ + fetch('http://127.0.0.1:8000/market/real-time'), + fetch('http://127.0.0.1:8000/market/day-ahead') + ]) + .then(async ([res1, res2]) => { + if (!res1.ok || !res2.ok) throw new Error('Failed to fetch data'); + const json1 = await res1.json(); + const json2 = await res2.json(); + setFiveMinData(json1); + setDayAheadData(json2); + }) + .catch((err) => { + console.error(err); + Message.error('Failed to load market data.'); + }) + .finally(() => setLoading(false)); + }, []); + + const getChartSpec = (data, title) => ({ + 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'] + } + } + ] + }, + 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: title === 'fiveMin' ? 0.9 : 0.05, + end: 1 + } + ] + }); + + return ( + <> +
+ PolyEnergy + } subTitle={ + Virtual Energy Trading + } /> +
+
+ Market Data Visualization + {loading ? ( + + ) : ( + <> +
+ + + + + + + + + + + + +
+ + )} +
+ + ); +} + +export default App; diff --git a/client/src/App.test.js b/client/src/App.test.js new file mode 100644 index 0000000..1f03afe --- /dev/null +++ b/client/src/App.test.js @@ -0,0 +1,8 @@ +import { render, screen } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', () => { + render(); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/client/src/index.css b/client/src/index.css new file mode 100644 index 0000000..ec2585e --- /dev/null +++ b/client/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/client/src/index.js b/client/src/index.js new file mode 100644 index 0000000..10d3d73 --- /dev/null +++ b/client/src/index.js @@ -0,0 +1,23 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; +import { initVChartArcoTheme } from '@visactor/vchart-arco-theme'; + +initVChartArcoTheme({ + defaultMode: 'light', + isWatchingMode: true +}); + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/client/src/logo.svg b/client/src/logo.svg new file mode 100644 index 0000000..9dfc1c0 --- /dev/null +++ b/client/src/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/reportWebVitals.js b/client/src/reportWebVitals.js new file mode 100644 index 0000000..5253d3a --- /dev/null +++ b/client/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = onPerfEntry => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/client/src/setupTests.js b/client/src/setupTests.js new file mode 100644 index 0000000..8f2609b --- /dev/null +++ b/client/src/setupTests.js @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; -- cgit v1.2.3