Initial eth_hedge_sim: P0 market, auth UI, one-click deploy.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { NavLink, Navigate, Route, Routes } from "react-router-dom";
|
||||
import { clearSession, getToken, getUsername } from "./api/client";
|
||||
import LoginPage from "./pages/Login";
|
||||
import PlanPage from "./pages/Plan";
|
||||
import TradesPage from "./pages/Trades";
|
||||
import StatsPage from "./pages/Stats";
|
||||
import SettingsPage from "./pages/Settings";
|
||||
|
||||
function Shell({ children }: { children: ReactNode }) {
|
||||
const user = getUsername();
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<nav className="topnav">
|
||||
<div className="brand">eth_hedge_sim</div>
|
||||
<NavLink to="/plan" className={({ isActive }) => (isActive ? "active" : "")}>
|
||||
自动对冲计划
|
||||
</NavLink>
|
||||
<NavLink to="/trades" className={({ isActive }) => (isActive ? "active" : "")}>
|
||||
交易记录
|
||||
</NavLink>
|
||||
<NavLink to="/stats" className={({ isActive }) => (isActive ? "active" : "")}>
|
||||
统计
|
||||
</NavLink>
|
||||
<NavLink to="/settings" className={({ isActive }) => (isActive ? "active" : "")}>
|
||||
系统设置
|
||||
</NavLink>
|
||||
<div className="spacer" />
|
||||
<span className="meta mono">{user}</span>
|
||||
<button
|
||||
className="btn ghost"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
clearSession();
|
||||
window.location.href = "/login";
|
||||
}}
|
||||
>
|
||||
退出
|
||||
</button>
|
||||
</nav>
|
||||
<main className="page">{children}</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RequireAuth({ children }: { children: ReactNode }) {
|
||||
if (!getToken()) return <Navigate to="/login" replace />;
|
||||
return <Shell>{children}</Shell>;
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route
|
||||
path="/plan"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<PlanPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/trades"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<TradesPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/stats"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<StatsPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/settings"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<SettingsPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route path="/" element={<Navigate to="/plan" replace />} />
|
||||
<Route path="*" element={<Navigate to="/plan" replace />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user