99 lines
3.6 KiB
JavaScript
99 lines
3.6 KiB
JavaScript
import React from "react";
|
|
import { NavLink, Navigate, Route, Routes, useLocation } from "react-router-dom";
|
|
import { useAuth } from "./state";
|
|
import { LoginPage } from "./pages/LoginPage";
|
|
import { DashboardPage } from "./pages/DashboardPage";
|
|
import { TargetsPage } from "./pages/TargetsPage";
|
|
import { TargetDetailPage } from "./pages/TargetDetailPage";
|
|
import { QueryInsightsPage } from "./pages/QueryInsightsPage";
|
|
import { AdminUsersPage } from "./pages/AdminUsersPage";
|
|
|
|
function Protected({ children }) {
|
|
const { tokens } = useAuth();
|
|
const location = useLocation();
|
|
if (!tokens?.accessToken) return <Navigate to="/login" state={{ from: location.pathname }} replace />;
|
|
return children;
|
|
}
|
|
|
|
function Layout({ children }) {
|
|
const { me, logout } = useAuth();
|
|
const navClass = ({ isActive }) => `nav-btn${isActive ? " active" : ""}`;
|
|
|
|
return (
|
|
<div className="shell">
|
|
<aside className="sidebar">
|
|
<div className="brand">
|
|
<img src="/nexapg-logo.svg" alt="NexaPG" className="brand-logo" />
|
|
<h1>NexaPG</h1>
|
|
</div>
|
|
<nav className="sidebar-nav">
|
|
<NavLink to="/" end className={navClass}>
|
|
<span className="nav-icon" aria-hidden="true">
|
|
<svg viewBox="0 0 24 24">
|
|
<path d="M4 6c0-1.7 3.6-3 8-3s8 1.3 8 3-3.6 3-8 3-8-1.3-8-3zm0 6c0 1.7 3.6 3 8 3s8-1.3 8-3M4 18c0 1.7 3.6 3 8 3s8-1.3 8-3" />
|
|
</svg>
|
|
</span>
|
|
<span className="nav-label">Dashboard</span>
|
|
</NavLink>
|
|
<NavLink to="/targets" className={navClass}>
|
|
<span className="nav-icon" aria-hidden="true">
|
|
<svg viewBox="0 0 24 24">
|
|
<path d="M12 3l8 4.5v9L12 21l-8-4.5v-9L12 3zM12 12l8-4.5M12 12L4 7.5M12 12v9" />
|
|
</svg>
|
|
</span>
|
|
<span className="nav-label">Targets</span>
|
|
</NavLink>
|
|
<NavLink to="/query-insights" className={navClass}>
|
|
<span className="nav-icon" aria-hidden="true">
|
|
<svg viewBox="0 0 24 24">
|
|
<path d="M4 19h16M7 15l3-3 3 2 4-5M18 8h.01" />
|
|
</svg>
|
|
</span>
|
|
<span className="nav-label">Query Insights</span>
|
|
</NavLink>
|
|
{me?.role === "admin" && (
|
|
<NavLink to="/admin/users" className={navClass}>
|
|
<span className="nav-icon" aria-hidden="true">
|
|
<svg viewBox="0 0 24 24">
|
|
<path d="M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm-7 8a7 7 0 0 1 14 0" />
|
|
</svg>
|
|
</span>
|
|
<span className="nav-label">Admin</span>
|
|
</NavLink>
|
|
)}
|
|
</nav>
|
|
<div className="profile">
|
|
<div>{me?.email}</div>
|
|
<div className="role">{me?.role}</div>
|
|
<button className="logout-btn" onClick={logout}>Logout</button>
|
|
</div>
|
|
</aside>
|
|
<main className="main">{children}</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function App() {
|
|
return (
|
|
<Routes>
|
|
<Route path="/login" element={<LoginPage />} />
|
|
<Route
|
|
path="*"
|
|
element={
|
|
<Protected>
|
|
<Layout>
|
|
<Routes>
|
|
<Route path="/" element={<DashboardPage />} />
|
|
<Route path="/targets" element={<TargetsPage />} />
|
|
<Route path="/targets/:id" element={<TargetDetailPage />} />
|
|
<Route path="/query-insights" element={<QueryInsightsPage />} />
|
|
<Route path="/admin/users" element={<AdminUsersPage />} />
|
|
</Routes>
|
|
</Layout>
|
|
</Protected>
|
|
}
|
|
/>
|
|
</Routes>
|
|
);
|
|
}
|