Files
eth_hedge_sim/frontend/src/App.tsx
T

141 lines
3.9 KiB
TypeScript

import { type ReactNode, useEffect } from "react";
import { NavLink, Navigate, Route, Routes } from "react-router-dom";
import {
clearSession,
getToken,
getUsername,
startAuthTokenAutoRefresh,
} from "./api/client";
import FundsBar from "./components/FundsBar";
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";
const NAV = [
{ to: "/plan", label: "监控", full: "自动对冲计划" },
{ to: "/trades", label: "记录", full: "交易记录" },
{ to: "/stats", label: "统计", full: "统计" },
{ to: "/settings", label: "设置", full: "系统设置" },
] as const;
function Shell({ children }: { children: ReactNode }) {
const user = getUsername();
return (
<div className="app-shell">
<div className="app-container">
<header className="app-header-block">
<div className="brand-row">
<div className="brand">
<img
className="brand-mark"
src="/icons/brand.png"
alt=""
width={28}
height={28}
/>
<span className="brand-full"></span>
<span className="brand-short"></span>
</div>
<div className="brand-actions">
<span className="meta mono topnav-user">{user}</span>
<button
className="btn ghost topnav-logout"
type="button"
onClick={() => {
clearSession();
window.location.href = "/login";
}}
>
退
</button>
</div>
</div>
<nav className="top-nav" aria-label="主导航">
{NAV.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) => (isActive ? "active" : "")}
>
<span className="nav-label-full">{item.full}</span>
<span className="nav-label-short">{item.label}</span>
</NavLink>
))}
</nav>
</header>
<FundsBar />
<main className="page">{children}</main>
</div>
<nav className="bottom-nav" aria-label="手机导航">
{NAV.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) =>
isActive ? "bottom-nav-item active" : "bottom-nav-item"
}
>
<span className="bottom-nav-label">{item.label}</span>
</NavLink>
))}
</nav>
</div>
);
}
function RequireAuth({ children }: { children: ReactNode }) {
useEffect(() => {
if (!getToken()) return;
return startAuthTokenAutoRefresh();
}, []);
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>
);
}