mirror of
https://github.com/dat515-2025/Group-8.git
synced 2026-03-22 23:20:56 +01:00
91 lines
3.2 KiB
TypeScript
91 lines
3.2 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import './App.css';
|
|
import LoginRegisterPage from './pages/LoginRegisterPage';
|
|
import Dashboard from './pages/Dashboard';
|
|
import { logout } from './api';
|
|
import { BACKEND_URL } from './config';
|
|
|
|
function App() {
|
|
const [hasToken, setHasToken] = useState<boolean>(!!localStorage.getItem('token'));
|
|
const [processingCallback, setProcessingCallback] = useState<boolean>(false);
|
|
|
|
useEffect(() => {
|
|
const path = window.location.pathname;
|
|
|
|
// Minimal handling for provider callbacks: /auth|/oauth/:provider/callback?code=...&state=...
|
|
const parts = path.split('/').filter(Boolean);
|
|
const isCallback = parts.length === 3 && (parts[0] === 'auth') && parts[2] === 'callback';
|
|
|
|
if (isCallback) {
|
|
// Guard against double invocation in React 18 StrictMode/dev
|
|
const w = window as any;
|
|
if (w.__oauthCallbackHandled) {
|
|
return;
|
|
}
|
|
w.__oauthCallbackHandled = true;
|
|
|
|
setProcessingCallback(true);
|
|
|
|
const provider = parts[1];
|
|
const qs = window.location.search || '';
|
|
const base = BACKEND_URL.replace(/\/$/, '');
|
|
const url = `${base}/auth/${encodeURIComponent(provider)}/callback${qs}`;
|
|
(async () => {
|
|
try {
|
|
const token = localStorage.getItem('token');
|
|
const res = await fetch(url, {
|
|
method: 'GET',
|
|
credentials: 'include',
|
|
headers: token ? { Authorization: `Bearer ${token}` } : undefined,
|
|
});
|
|
let data: any = null;
|
|
try {
|
|
data = await res.json();
|
|
} catch {}
|
|
if (provider !== 'csas' && res.ok && data?.access_token) {
|
|
localStorage.setItem('token', data?.access_token);
|
|
setHasToken(true);
|
|
}
|
|
} catch {}
|
|
// Clean URL and go home regardless of result
|
|
setProcessingCallback(false);
|
|
window.history.replaceState({}, '', '/');
|
|
})();
|
|
}
|
|
|
|
const onStorage = (e: StorageEvent) => {
|
|
if (e.key === 'token') setHasToken(!!e.newValue);
|
|
};
|
|
window.addEventListener('storage', onStorage);
|
|
return () => window.removeEventListener('storage', onStorage);
|
|
}, []);
|
|
|
|
if (processingCallback) {
|
|
return (
|
|
<div style={{ display: 'grid', placeItems: 'center', height: '100vh' }}>
|
|
<div className="card" style={{ width: 360, textAlign: 'center', padding: 24 }}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
|
|
<svg width="48" height="48" viewBox="0 0 50 50" aria-label="Loading">
|
|
<circle cx="25" cy="25" r="20" fill="none" stroke="#3b82f6" strokeWidth="5" strokeLinecap="round" strokeDasharray="31.4 31.4">
|
|
<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.9s" repeatCount="indefinite" />
|
|
</circle>
|
|
</svg>
|
|
<div>Finishing sign-in…</div>
|
|
<div className="muted">Please wait</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!hasToken) {
|
|
return <LoginRegisterPage onLoggedIn={() => setHasToken(true)} />;
|
|
}
|
|
|
|
return (
|
|
<Dashboard onLogout={() => { logout(); setHasToken(false); }} />
|
|
);
|
|
}
|
|
|
|
export default App;
|