aboutsummaryrefslogtreecommitdiff
path: root/client/src/App.js
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/App.js')
-rw-r--r--client/src/App.js122
1 files changed, 122 insertions, 0 deletions
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 (
+ <>
+ <div style={ghostBgStyle}>
+ <PageHeader title={
+ <Typography.Title heading={3}>PolyEnergy</Typography.Title>
+ } subTitle={
+ <Typography.Title type="secondary" heading={4}>Virtual Energy Trading</Typography.Title>
+ } />
+ </div>
+ <div style={{ padding: 20, backgroundColor: 'var(--color-fill-2)' }}>
+ <Typography.Title heading={4}>Market Data Visualization</Typography.Title>
+ {loading ? (
+ <Spin />
+ ) : (
+ <>
+ <div
+ style={{
+ width: '100%',
+ backgroundColor: 'var(--color-fill-2)',
+ }}
+ >
+ <Row gutter={24}>
+ <Col span={12}>
+ <Card style={{ height: 600}} title='Real-Time Market Data'>
+ <VChart spec={getChartSpec(fiveMinData, 'fiveMin')} />
+ </Card>
+ </Col>
+ <Col span={12}>
+ <Card style={{ height: 600}} title='Day-Ahead Market Data'>
+ <VChart spec={getChartSpec(dayAheadData, 'dayAhead')} />
+ </Card>
+ </Col>
+ </Row>
+ </div>
+ </>
+ )}
+ </div>
+ </>
+ );
+}
+
+export default App;