summaryrefslogtreecommitdiff
path: root/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/App.css54
-rw-r--r--frontend/src/App.js38
-rw-r--r--frontend/src/Setup.js142
3 files changed, 231 insertions, 3 deletions
diff --git a/frontend/src/App.css b/frontend/src/App.css
index 19cf08b..c67769b 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -20,4 +20,56 @@
.hide-scrollbar::-webkit-scrollbar {
display: none;
-} \ No newline at end of file
+}
+.flex-center {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.setup-container {
+ background: var(--bg-primary, #1e1e1e);
+ padding: 3rem;
+ border-radius: 8px;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.3);
+ text-align: center;
+ max-width: 500px;
+}
+
+.setup-step {
+ margin: 2rem 0;
+ padding: 1rem;
+ border: 1px solid #444;
+ border-radius: 4px;
+}
+
+.setup-step h2 {
+ margin-top: 0;
+ font-size: 1.2rem;
+ color: #fff;
+}
+
+.setup-container button {
+ background: #3b82f6;
+ color: white;
+ border: none;
+ padding: 0.75rem 1.5rem;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: bold;
+}
+
+.setup-container button:hover {
+ background: #2563eb;
+}
+
+.setup-container button:disabled {
+ background: #555;
+ cursor: not-allowed;
+}
+
+.setup-message {
+ margin-top: 1rem;
+ font-size: 0.9rem;
+ color: #aaa;
+}
diff --git a/frontend/src/App.js b/frontend/src/App.js
index 569ff74..af95c63 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -1,18 +1,52 @@
+import React, { useState, useEffect } from 'react';
import './App.css';
import Path from './Path';
import Log from './Log';
import Options from './Options';
import Info from './Info';
+import Setup from './Setup';
function App() {
+ const [setupStatus, setSetupStatus] = useState(null);
+
+ useEffect(() => {
+ const checkStatus = async () => {
+ try {
+ const resp = await fetch('http://localhost:8000/api/setup/status');
+ const rootResp = await fetch('http://localhost:8000/');
+ if (resp.ok && rootResp.ok) {
+ const data = await resp.json();
+ const rootData = await rootResp.json();
+ data.backend_ready = rootData.ready;
+ setSetupStatus(data);
+ if (!rootData.ready) {
+ setTimeout(checkStatus, 2000);
+ }
+ } else {
+ setTimeout(checkStatus, 2000);
+ }
+ } catch (e) {
+ console.error("Could not reach backend API, retrying in 2s...");
+ setTimeout(checkStatus, 2000);
+ }
+ };
+ checkStatus();
+ }, []);
+
+ if (!setupStatus) {
+ return <div className="loading">Initializing Backend Connection...</div>;
+ }
+
+ if (!setupStatus.data_ready || !setupStatus.auth_ready || !setupStatus.backend_ready) {
+ return <Setup status={setupStatus} onComplete={() => window.location.reload()} />;
+ }
+
return (
<div className="App flex flex-wrap h-screen">
- {/* Main component for Path */}
<div className="w-2/3 h-full overflow-auto hide-scrollbar">
<Path />
</div>
- {/* 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 />
diff --git a/frontend/src/Setup.js b/frontend/src/Setup.js
new file mode 100644
index 0000000..537a36c
--- /dev/null
+++ b/frontend/src/Setup.js
@@ -0,0 +1,142 @@
+import React, { useState, useEffect } from 'react';
+import './App.css';
+
+function Setup({ status, onComplete }) {
+ const [loadingData, setLoadingData] = useState(false);
+ const [loginUrl, setLoginUrl] = useState('');
+ const [message, setMessage] = useState('');
+ const [manualCode, setManualCode] = useState('');
+ const [showManualCode, setShowManualCode] = useState(false);
+
+ useEffect(() => {
+ // Automatically handle the OAuth callback if the URL has ?code=
+ const urlParams = new URLSearchParams(window.location.search);
+ const code = urlParams.get('code');
+ if (code) {
+ handleAuthCallback(code);
+ }
+ }, []);
+
+ const handleAuthCallback = async (code) => {
+ setMessage('Completing login...');
+ try {
+ const resp = await fetch('http://localhost:8000/api/setup/auth/callback', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ code })
+ });
+ if (resp.ok) {
+ setMessage('Login successful. Please ensure both steps are complete.');
+ } else {
+ setMessage('Login failed. Please try again.');
+ }
+ } catch (e) {
+ setMessage('Error during login callback.');
+ }
+ };
+
+ const downloadData = async () => {
+ setLoadingData(true);
+ setMessage('Downloading EVE Online static data (this may take a few minutes)...');
+ try {
+ const resp = await fetch('http://localhost:8000/api/setup/data', { method: 'POST' });
+ if (resp.ok) {
+ setMessage('Data downloaded successfully!');
+ } else {
+ setMessage('Failed to download data.');
+ }
+ } catch (e) {
+ setMessage('Error downloading data.');
+ }
+ setLoadingData(false);
+ };
+
+ const fetchLoginUrl = async () => {
+ try {
+ const resp = await fetch('http://localhost:8000/api/setup/auth/url');
+ if (resp.ok) {
+ const data = await resp.json();
+ window.open(data.url, '_blank');
+ setShowManualCode(true);
+ }
+ } catch (e) {
+ setMessage('Failed to fetch login URL.');
+ }
+ };
+
+ return (
+ <div className="App dark-mode flex-center">
+ <div className="setup-container">
+ <h1>Welcome to EVE Haul</h1>
+ <p>Let's get your environment set up.</p>
+
+ <div className="setup-step">
+ <h2>1. EVE Static Data</h2>
+ {status.data_ready ? (
+ <p style={{ color: 'lightgreen' }}>Data is ready!</p>
+ ) : (
+ <button onClick={downloadData} disabled={loadingData}>
+ {loadingData ? 'Downloading...' : 'Download Game Data'}
+ </button>
+ )}
+ </div>
+
+ <div className="setup-step">
+ <h2>2. EVE Single Sign-On</h2>
+ {status.auth_ready ? (
+ <p style={{ color: 'lightgreen' }}>Authenticated!</p>
+ ) : (
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
+ <button onClick={() => {
+ setShowManualCode(true);
+ fetchLoginUrl();
+ }}>
+ Login to EVE Online
+ </button>
+ {showManualCode && (
+ <div style={{ fontSize: '0.9em', color: '#ccc', textAlign: 'left', marginTop: '10px' }}>
+ <p>After granting access, you might be redirected to a broken page (like `https://localhost/callback/...`).</p>
+ <p>Copy the `code=` part from the URL address bar and paste it below:</p>
+ <div style={{ display: 'flex', gap: '5px', marginTop: '5px' }}>
+ <input
+ type="text"
+ placeholder="Paste the code here"
+ value={manualCode}
+ onChange={(e) => setManualCode(e.target.value)}
+ style={{ flex: 1, padding: '5px', borderRadius: '3px', background: '#222', color: '#fff', border: '1px solid #444' }}
+ />
+ <button onClick={() => {
+ if (manualCode.includes('?code=')) {
+ const params = new URLSearchParams(manualCode.split('?')[1]);
+ handleAuthCallback(params.get('code') || manualCode);
+ } else if (manualCode.startsWith('http') && manualCode.includes('code=')) {
+ const url = new URL(manualCode);
+ handleAuthCallback(url.searchParams.get('code') || manualCode);
+ } else {
+ handleAuthCallback(manualCode);
+ }
+ }}>Submit
+ </button>
+ </div>
+ </div>
+ )}
+ </div>
+ )}
+ </div>
+
+ <p className="setup-message">{message}</p>
+
+ {status.data_ready && status.auth_ready && !status.backend_ready && (
+ <div style={{ marginTop: '20px', padding: '15px', background: '#333', border: '1px solid #ffaa00', borderRadius: '5px' }}>
+ <h3 style={{ color: '#ffaa00', margin: '0 0 10px 0' }}>Restart Required</h3>
+ <p>Setup is complete, but the EVE Haul backend process needs to be restarted to load the downloaded data.</p>
+ <p>Please go to your terminal, press `Ctrl+C` to stop the backend, and start it again.</p>
+ <p>This page will automatically launch EVE Haul when the backend is ready.</p>
+ </div>
+ )}
+ </div>
+ </div>
+ );
+}
+
+export default Setup;