From 2c5b7da8165e3f8b90748e8ced1de0e3e8d52309 Mon Sep 17 00:00:00 2001 From: lazydancer Date: Sun, 22 Dec 2024 17:33:11 -0500 Subject: Initial --- frontend/src/App.css | 32 ++++++++++++++ frontend/src/App.js | 35 +++++++++++++++ frontend/src/App.test.js | 8 ++++ frontend/src/DataDisplay.css | 25 +++++++++++ frontend/src/DataDisplay.js | 94 ++++++++++++++++++++++++++++++++++++++++ frontend/src/Info.js | 16 +++++++ frontend/src/InfoDisplay.js | 61 ++++++++++++++++++++++++++ frontend/src/Log.js | 18 ++++++++ frontend/src/LogComponent.js | 22 ++++++++++ frontend/src/Options.css | 11 +++++ frontend/src/Options.js | 31 +++++++++++++ frontend/src/Path.js | 16 +++++++ frontend/src/index.css | 17 ++++++++ frontend/src/index.js | 17 ++++++++ frontend/src/logo.png | Bin 0 -> 337650 bytes frontend/src/reportWebVitals.js | 13 ++++++ frontend/src/setupTests.js | 5 +++ frontend/src/topo.svg | 4 ++ frontend/src/usePolling.js | 32 ++++++++++++++ frontend/src/useSSE.js | 32 ++++++++++++++ 20 files changed, 489 insertions(+) create mode 100644 frontend/src/App.css create mode 100644 frontend/src/App.js create mode 100644 frontend/src/App.test.js create mode 100644 frontend/src/DataDisplay.css create mode 100644 frontend/src/DataDisplay.js create mode 100644 frontend/src/Info.js create mode 100644 frontend/src/InfoDisplay.js create mode 100644 frontend/src/Log.js create mode 100644 frontend/src/LogComponent.js create mode 100644 frontend/src/Options.css create mode 100644 frontend/src/Options.js create mode 100644 frontend/src/Path.js create mode 100644 frontend/src/index.css create mode 100644 frontend/src/index.js create mode 100644 frontend/src/logo.png create mode 100644 frontend/src/reportWebVitals.js create mode 100644 frontend/src/setupTests.js create mode 100644 frontend/src/topo.svg create mode 100644 frontend/src/usePolling.js create mode 100644 frontend/src/useSSE.js (limited to 'frontend/src') diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 0000000..cc8ccba --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1,32 @@ +.App { + text-align: center; + background: url(topo.svg), #111; +} + + +.App-logo { + height: 40vmin; + max-height: 256px; + pointer-events: none; + + mix-blend-mode: lighten; +} + +.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; +} + +.hide-scrollbar::-webkit-scrollbar { + display: none; +} \ No newline at end of file diff --git a/frontend/src/App.js b/frontend/src/App.js new file mode 100644 index 0000000..463566a --- /dev/null +++ b/frontend/src/App.js @@ -0,0 +1,35 @@ +import logo from './logo.png'; +import './App.css'; + +import Path from './Path'; +import Log from './Log'; +import Options from './Options'; +import Info from './Info'; + +function App() { + return ( +
+ {/* Main component for Path */} +
+ +
+ {/* Sidebar for Options, LogComponent, and Logo */} +
+
+ +
+
+ +
+
+ +
+
+ logo +
+
+
+ ); +} + +export default App; diff --git a/frontend/src/App.test.js b/frontend/src/App.test.js new file mode 100644 index 0000000..1f03afe --- /dev/null +++ b/frontend/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/frontend/src/DataDisplay.css b/frontend/src/DataDisplay.css new file mode 100644 index 0000000..c7e5fe6 --- /dev/null +++ b/frontend/src/DataDisplay.css @@ -0,0 +1,25 @@ +.fade-enter { + opacity: 0; +} + +.fade-enter-active { + opacity: 1; + transition: opacity 500ms ease-in; +} + +.fade-exit { + opacity: 1; + transition: opacity 500ms ease-out; +} + +.fade-exit-active { + opacity: 0; +} + +.gridCustom { + display: grid; + grid-template-columns: 1fr 5fr 1fr 2fr; + gap: 1rem; /* Adjust the gap size as needed */ + padding-top: 10px; + padding-left: 40px + } \ No newline at end of file diff --git a/frontend/src/DataDisplay.js b/frontend/src/DataDisplay.js new file mode 100644 index 0000000..294f4fb --- /dev/null +++ b/frontend/src/DataDisplay.js @@ -0,0 +1,94 @@ +import React from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import './DataDisplay.css'; // Import the CSS file for transitions + + +const DataDisplay = ({ data }) => { + console.log("DataDisplay Data: ", data); + const getIcon = (name) => { + // Replace these with actual icon components or image tags + if (name === 'system') return + + ; + if (name === 'station') return + + ; + if (name === 'buy') return + + ; + if (name === 'sell') return + + ; + + return ''; // Default icon or empty + }; + + if (!data) { + return

Loading ...

+ } + + const handleItemClick = async (typeId) => { + try { + const response = await fetch(`http://localhost:8000/open_market_window/${typeId}`, { method: 'POST' }); + console.log('Server Response:', response.data); + // Handle response or set state as needed + } catch (error) { + console.error('Error posting to server:', error); + // Handle error + } + } + + + const containerVariants = { + hidden: { opacity: 0, y: -20 }, + visible: { opacity: 1, y: 0 }, + exit: { opacity: 0, y: -20 } + }; + + return ( +
+ + {data.map((location, index) => ( + +
+ + {getIcon(location.location_type)} + +

{location.location}

+
+
+ {location.actions.length > 0 ? ( +
+ {location.actions.map((action, actionIndex) => ( + +
{getIcon(action.action_type)}
+
handleItemClick(action.type_id)} className={`${index === 0 ? 'font-semibold' : ''}`} style={{ cursor: 'pointer' }}>{action.item}
+
{action.quantity}
+
{action.price ? `$${action.price.toLocaleString()}` : '-'}
+
+ ))} +
+ ) : ( +

+ )} +
+
+ ))} +
+ + +
+ ); + + }; + +export default DataDisplay; \ No newline at end of file diff --git a/frontend/src/Info.js b/frontend/src/Info.js new file mode 100644 index 0000000..3fcfa3e --- /dev/null +++ b/frontend/src/Info.js @@ -0,0 +1,16 @@ +import React from 'react'; +import usePolling from './usePolling'; +import InfoDisplay from './InfoDisplay'; // Import the DataDisplay component + +const Info = () => { + const { data, error } = usePolling('http://localhost:8000/route_info', 1000); + + return ( +
+ {error &&

Error: {error}

} + {data ? :

Loading data...

} +
+ ); + }; + +export default Info; \ No newline at end of file diff --git a/frontend/src/InfoDisplay.js b/frontend/src/InfoDisplay.js new file mode 100644 index 0000000..2253802 --- /dev/null +++ b/frontend/src/InfoDisplay.js @@ -0,0 +1,61 @@ +import React from 'react'; + +const InfoDisplay = ({ data }) => { + + // Convert profit rate from isk/s to isk/hr, round, and format + const profitRatePerHour = Math.round(data.profit_rate * 3600).toLocaleString('en-US'); + + // Format risk as percent and calculate risk cost, then round + const riskPercent = (Math.round(data.risk * 100 * 100) / 100).toFixed(2) + '%'; + const riskCost = Math.round(data.risk * data.capital).toLocaleString('en-US'); + + // Format capital + const capitalFormatted = Math.round(data.capital).toLocaleString('en-US'); + + // Convert transport time to minutes and seconds, then round + const minutes = Math.floor(data.transport_time / 60); + const seconds = Math.round(data.transport_time % 60); + const transportTimeFormatted = `${minutes}m ${seconds}s`; + + // Format and round gross profit + const grossProfitFormatted = Math.round(data.gross_profit).toLocaleString('en-US'); + + const netProfitFormatted = Math.round(data.net_profit).toLocaleString('en-US'); + + return ( +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Profit Rate:{profitRatePerHour} isk/hr
Risk:{riskPercent} ({riskCost} isk)
Capital:{capitalFormatted} isk
Transport Time:{transportTimeFormatted}
Gross Profit:{grossProfitFormatted} isk
Net Profit:{netProfitFormatted} isk
+
+ ); +} + +export default InfoDisplay; diff --git a/frontend/src/Log.js b/frontend/src/Log.js new file mode 100644 index 0000000..565396f --- /dev/null +++ b/frontend/src/Log.js @@ -0,0 +1,18 @@ +import React from 'react'; +import useSSE from './useSSE'; +import LogComponent from './LogComponent'; // Import the DataDisplay component + +const Log = () => { + const { data, error } = useSSE('http://localhost:8000/data_stream', 2000); + + console.log("Log Data: ", data); + + return ( +
+ {error &&

Error: {error}

} + {data ? :

Loading data...

} +
+ ); + }; + +export default Log; \ No newline at end of file diff --git a/frontend/src/LogComponent.js b/frontend/src/LogComponent.js new file mode 100644 index 0000000..1c6900f --- /dev/null +++ b/frontend/src/LogComponent.js @@ -0,0 +1,22 @@ +import React, { useEffect, useRef } from 'react'; + +const LogComponent = ({ messages }) => { + const endOfMessagesRef = useRef(null); + + useEffect(() => { + endOfMessagesRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages]); + + return ( +
+ {messages.map((message, index) => ( +
+ {message} +
+ ))} +
+
+ ); +}; + +export default LogComponent; diff --git a/frontend/src/Options.css b/frontend/src/Options.css new file mode 100644 index 0000000..b6971c3 --- /dev/null +++ b/frontend/src/Options.css @@ -0,0 +1,11 @@ +.options-button { + background-color: #8db3be; + color: #ffffff; + font-size: 20px; + padding: 10px 20px; + transition: background-color 0.3s ease; +} + +.options-button:active { + background-color: #6a8a94; +} \ No newline at end of file diff --git a/frontend/src/Options.js b/frontend/src/Options.js new file mode 100644 index 0000000..4c8a7e9 --- /dev/null +++ b/frontend/src/Options.js @@ -0,0 +1,31 @@ +import React, { useState } from 'react'; +import './Options.css'; + +const Options = () => { + const [message, setMessage] = useState(''); + + const handleClick = async () => { + setMessage('Sending request...'); + try { + const response = await fetch('http://localhost:8000/create_route', { method: 'POST' }); + const data = await response.json(); + setMessage(`Response received: ${JSON.stringify(data)}`); + } catch (error) { + setMessage(`Error: ${error}`); + } + }; + + return ( +
+ +

{message}

+
+ ); +} + +export default Options; \ No newline at end of file diff --git a/frontend/src/Path.js b/frontend/src/Path.js new file mode 100644 index 0000000..f375743 --- /dev/null +++ b/frontend/src/Path.js @@ -0,0 +1,16 @@ +import React from 'react'; +import usePolling from './usePolling'; +import DataDisplay from './DataDisplay'; // Import the DataDisplay component + +const Path = () => { + const { data, error } = usePolling('http://localhost:8000/route', 1000); + + return ( +
+ {error &&

Error: {error}

} + {data ? :

Loading data...

} +
+ ); +}; + +export default Path; \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..17df0e7 --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,17 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +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/frontend/src/index.js b/frontend/src/index.js new file mode 100644 index 0000000..d563c0f --- /dev/null +++ b/frontend/src/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; + +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/frontend/src/logo.png b/frontend/src/logo.png new file mode 100644 index 0000000..6cfc650 Binary files /dev/null and b/frontend/src/logo.png differ diff --git a/frontend/src/reportWebVitals.js b/frontend/src/reportWebVitals.js new file mode 100644 index 0000000..5253d3a --- /dev/null +++ b/frontend/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/frontend/src/setupTests.js b/frontend/src/setupTests.js new file mode 100644 index 0000000..8f2609b --- /dev/null +++ b/frontend/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'; diff --git a/frontend/src/topo.svg b/frontend/src/topo.svg new file mode 100644 index 0000000..eabe474 --- /dev/null +++ b/frontend/src/topo.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/frontend/src/usePolling.js b/frontend/src/usePolling.js new file mode 100644 index 0000000..3081312 --- /dev/null +++ b/frontend/src/usePolling.js @@ -0,0 +1,32 @@ +// usePolling.js +import { useState, useEffect } from 'react'; + +const usePolling = (url, interval) => { + const [data, setData] = useState(null); + const [error, setError] = useState(''); + + useEffect(() => { + const fetchData = async () => { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const result = await response.json(); + setData(result); + setError(null); + } catch (err) { + setError('Failed to fetch data: ' + err.message); + } + }; + + fetchData(); + const pollingInterval = setInterval(fetchData, interval); + + return () => clearInterval(pollingInterval); + }, [url, interval]); + + return { data, error }; +}; + +export default usePolling; \ No newline at end of file diff --git a/frontend/src/useSSE.js b/frontend/src/useSSE.js new file mode 100644 index 0000000..e741895 --- /dev/null +++ b/frontend/src/useSSE.js @@ -0,0 +1,32 @@ +import { useState, useEffect } from 'react'; + +const useSSE = (url) => { + const [data, setData] = useState(null); + const [error, setError] = useState(''); + + useEffect(() => { + const eventSource = new EventSource(url); + + eventSource.onmessage = e => { + try { + const result = JSON.parse(e.data); + setData(result); + } catch (err) { + setError('Failed to parse data: ' + err.message); + } + }; + + eventSource.onerror = e => { + setError('EventSource failed'); + eventSource.close(); + }; + + return () => { + eventSource.close(); + }; + }, [url]); + + return { data, error }; +}; + +export default useSSE; -- cgit v1.3