summaryrefslogtreecommitdiff
path: root/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/App.css9
-rw-r--r--frontend/src/App.js8
-rw-r--r--frontend/src/Info.js5
-rw-r--r--frontend/src/InfoDisplay.js2
-rw-r--r--frontend/src/Options.js170
-rw-r--r--frontend/src/Path.js5
6 files changed, 166 insertions, 33 deletions
diff --git a/frontend/src/App.css b/frontend/src/App.css
index cc8ccba..19cf08b 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -3,15 +3,6 @@
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;
diff --git a/frontend/src/App.js b/frontend/src/App.js
index 463566a..569ff74 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -1,4 +1,3 @@
-import logo from './logo.png';
import './App.css';
import Path from './Path';
@@ -13,7 +12,7 @@ function App() {
<div className="w-2/3 h-full overflow-auto hide-scrollbar">
<Path />
</div>
- {/* Sidebar for Options, LogComponent, and Logo */}
+ {/* Sidebar for route info, controls, and log output */}
<div className="w-1/3 h-full flex flex-col">
<div className="h-1/4 overflow-auto">
<Info />
@@ -21,12 +20,9 @@ function App() {
<div className="h-1/4 overflow-auto">
<Options />
</div>
- <div className="h-1/4 overflow-auto">
+ <div className="flex-1 overflow-auto">
<Log/>
</div>
- <div className="h-1/4 flex items-center justify-center overflow-auto">
- <img src={logo} className="App-logo" alt="logo" />
- </div>
</div>
</div>
);
diff --git a/frontend/src/Info.js b/frontend/src/Info.js
index 3fcfa3e..d46d914 100644
--- a/frontend/src/Info.js
+++ b/frontend/src/Info.js
@@ -1,9 +1,10 @@
import React from 'react';
-import usePolling from './usePolling';
+import useSSE from './useSSE';
import InfoDisplay from './InfoDisplay'; // Import the DataDisplay component
const Info = () => {
- const { data, error } = usePolling('http://localhost:8000/route_info', 1000);
+ const { data: sseData, error } = useSSE('http://localhost:8000/data_stream');
+ const data = sseData ? sseData.route_info : null;
return (
<div className="text-white">
diff --git a/frontend/src/InfoDisplay.js b/frontend/src/InfoDisplay.js
index 2253802..c8a77c1 100644
--- a/frontend/src/InfoDisplay.js
+++ b/frontend/src/InfoDisplay.js
@@ -36,7 +36,7 @@ const InfoDisplay = ({ data }) => {
<td>{riskPercent} ({riskCost} isk)</td>
</tr>
<tr>
- <td style={{ textAlign: 'left' }}>Capital:</td>
+ <td style={{ textAlign: 'left' }}>Peak Capital:</td>
<td>{capitalFormatted} isk</td>
</tr>
<tr>
diff --git a/frontend/src/Options.js b/frontend/src/Options.js
index 4c8a7e9..b490f67 100644
--- a/frontend/src/Options.js
+++ b/frontend/src/Options.js
@@ -1,29 +1,173 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import './Options.css';
+import useSSE from './useSSE';
const Options = () => {
const [message, setMessage] = useState('');
+ const [messageType, setMessageType] = useState('info');
+ const [isSettingWaypoints, setIsSettingWaypoints] = useState(false);
+ const [settings, setSettings] = useState({
+ cargo: '',
+ max_warp_speed: '',
+ max_subwarp_speed: '',
+ risk_cost: '',
+ ship_cost: '',
+ tax_rate: '',
+ profit_threshold: '',
+ max_capital: '',
+ snipe_profit_threshold: ''
+ });
+ const { data: sseData } = useSSE('http://localhost:8000/data_stream');
+ const route = Array.isArray(sseData?.route) ? sseData.route : [];
+ const hasRouteWaypoints = route.length > 1;
- const handleClick = async () => {
- setMessage('Sending request...');
+ useEffect(() => {
+ fetch('http://localhost:8000/settings')
+ .then(res => res.json())
+ .then(data => {
+ setSettings({
+ cargo: data.cargo,
+ max_warp_speed: data.max_warp_speed,
+ max_subwarp_speed: data.max_subwarp_speed,
+ risk_cost: data.risk_cost,
+ ship_cost: data.ship_cost,
+ tax_rate: data.tax_rate,
+ profit_threshold: data.profit_threshold,
+ max_capital: data.max_capital,
+ snipe_profit_threshold: data.snipe_profit_threshold
+ });
+ })
+ .catch(err => console.error("Error fetching settings", err));
+ }, []);
+
+ const handleSettingsChange = (e) => {
+ const { name, value } = e.target;
+ setSettings(prev => ({ ...prev, [name]: value }));
+ };
+
+ const handleSaveSettings = async () => {
try {
- const response = await fetch('http://localhost:8000/create_route', { method: 'POST' });
- const data = await response.json();
- setMessage(`Response received: ${JSON.stringify(data)}`);
+ const response = await fetch('http://localhost:8000/settings', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ cargo: Number(settings.cargo),
+ max_warp_speed: Number(settings.max_warp_speed),
+ max_subwarp_speed: Number(settings.max_subwarp_speed),
+ risk_cost: Number(settings.risk_cost),
+ ship_cost: Number(settings.ship_cost),
+ tax_rate: Number(settings.tax_rate),
+ profit_threshold: Number(settings.profit_threshold),
+ max_capital: Number(settings.max_capital),
+ snipe_profit_threshold: Number(settings.snipe_profit_threshold)
+ })
+ });
+ if (response.ok) {
+ setMessageType('success');
+ setMessage('Settings saved');
+ setTimeout(() => setMessage(''), 3000);
+ }
+ } catch (error) {
+ setMessageType('error');
+ setMessage(`Error: ${error.message}`);
+ }
+ };
+
+ const handleCreateRoute = async () => {
+ try {
+ await fetch('http://localhost:8000/create_route', { method: 'POST' });
} catch (error) {
setMessage(`Error: ${error}`);
}
};
+ const handleSetWaypoints = async () => {
+ if (!hasRouteWaypoints) {
+ setMessageType('error');
+ setMessage('No route exists');
+ return;
+ }
+
+ setIsSettingWaypoints(true);
+ setMessageType('info');
+ setMessage('Sending to EVE...');
+ try {
+ const response = await fetch('http://localhost:8000/set_waypoints', { method: 'POST' });
+ const resData = await response.json();
+ const nextMessage = resData.message || resData.status || 'Waypoint request completed';
+ const nextType = resData.success ? 'success' : resData.status === 'partial' ? 'warning' : 'error';
+ setMessageType(nextType);
+ setMessage(nextMessage);
+ if (resData.success) {
+ setTimeout(() => setMessage(''), 4000);
+ }
+ } catch (error) {
+ setMessageType('error');
+ setMessage(`Error: ${error}`);
+ } finally {
+ setIsSettingWaypoints(false);
+ }
+ };
+
+ const messageClassName = messageType === 'error'
+ ? 'mb-1 text-red-400'
+ : messageType === 'warning'
+ ? 'mb-1 text-yellow-300'
+ : 'mb-1 text-green-400';
+
+ const waypointButtonClassName = hasRouteWaypoints && !isSettingWaypoints
+ ? 'options-button flex-1 px-2 py-1 text-sm bg-green-700 hover:bg-green-600 text-white rounded'
+ : 'options-button flex-1 px-2 py-1 text-sm bg-green-900 text-white rounded opacity-50 cursor-not-allowed';
+
return (
- <div className="p-8">
- <button
- className="options-button"
- onClick={handleClick}
- >
- Create Route
+ <div className="p-2 options-container flex flex-col content-start h-full text-white text-xs">
+ <div className="flex gap-2 mb-2">
+ <button className="options-button flex-1 px-2 py-1 text-sm bg-blue-600 hover:bg-blue-500 text-white rounded" onClick={handleCreateRoute}>
+ Create Route
+ </button>
+ <button
+ className={waypointButtonClassName}
+ onClick={handleSetWaypoints}
+ disabled={!hasRouteWaypoints || isSettingWaypoints}
+ >
+ Set in Game
+ </button>
+ </div>
+
+ {message && <p className={messageClassName}>{message}</p>}
+
+ <div className="settings-form grid grid-cols-4 gap-x-2 gap-y-1 items-center">
+ <label>Cargo (m³)</label>
+ <input name="cargo" type="number" value={settings.cargo} onChange={handleSettingsChange} className="text-black p-1 w-full" />
+
+ <label>Risk (ISK)</label>
+ <input name="risk_cost" type="number" value={settings.risk_cost} onChange={handleSettingsChange} className="text-black p-1 w-full" />
+
+ <label>Warp (AU/s)</label>
+ <input name="max_warp_speed" type="number" step="0.1" value={settings.max_warp_speed} onChange={handleSettingsChange} className="text-black p-1 w-full" />
+
+ <label>Ship (ISK)</label>
+ <input name="ship_cost" type="number" value={settings.ship_cost} onChange={handleSettingsChange} className="text-black p-1 w-full" />
+
+ <label>Sub (m/s)</label>
+ <input name="max_subwarp_speed" type="number" step="0.1" value={settings.max_subwarp_speed} onChange={handleSettingsChange} className="text-black p-1 w-full" />
+
+ <label>Tax (%)</label>
+ <input name="tax_rate" type="number" step="0.1" value={settings.tax_rate} onChange={handleSettingsChange} className="text-black p-1 w-full" />
+
+ <label>Prof. Thr.</label>
+ <input name="profit_threshold" type="number" value={settings.profit_threshold} onChange={handleSettingsChange} className="text-black p-1 w-full" />
+
+ <label>Max Cap.</label>
+ <input name="max_capital" type="number" value={settings.max_capital} onChange={handleSettingsChange} className="text-black p-1 w-full" />
+
+ <label>Snipe Thr.</label>
+ <input name="snipe_profit_threshold" type="number" value={settings.snipe_profit_threshold} onChange={handleSettingsChange} className="text-black p-1 w-full" />
+ </div>
+
+ <button className="mt-2 bg-blue-600 hover:bg-blue-500 py-1 px-2 text-white font-bold rounded" onClick={handleSaveSettings}>
+ Save
</button>
- <p className='text-white pt-2'>{message}</p>
</div>
);
}
diff --git a/frontend/src/Path.js b/frontend/src/Path.js
index f375743..3fd50ea 100644
--- a/frontend/src/Path.js
+++ b/frontend/src/Path.js
@@ -1,9 +1,10 @@
import React from 'react';
-import usePolling from './usePolling';
+import useSSE from './useSSE';
import DataDisplay from './DataDisplay'; // Import the DataDisplay component
const Path = () => {
- const { data, error } = usePolling('http://localhost:8000/route', 1000);
+ const { data: sseData, error } = useSSE('http://localhost:8000/data_stream');
+ const data = sseData ? sseData.route : null;
return (
<div className="text-white">