Add AUTH_EXPIRED_EVENT constant and dispatch event on 401 responses in API client, clearing stored token. Add handleLogout function to App component and wire up event listener to trigger logout on auth expiration. Pass onLogout prop to Layout component and add Logout button to topbar-actions. Update CSS to apply flex layout to topbar-actions and make responsive. Add backend hostname and network aliases in docker-compose to ensure consistent
62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
import { NavLink, Outlet } from "react-router-dom";
|
|
|
|
const items = [
|
|
["Dashboard", "/"],
|
|
["Users", "/users"],
|
|
["Devices", "/devices"],
|
|
["Policies", "/policies"],
|
|
["Gateways", "/gateways"],
|
|
["Audit", "/audit"],
|
|
["Settings", "/settings"]
|
|
];
|
|
|
|
type LayoutProps = {
|
|
onLogout: () => void;
|
|
};
|
|
|
|
export function Layout({ onLogout }: LayoutProps) {
|
|
return (
|
|
<div className="shell">
|
|
<aside className="sidebar">
|
|
<div className="brand-block">
|
|
<img className="brand-logo brand-logo-full" src="/NexaVPN_Logo.png" alt="NexaVPN" />
|
|
<div className="brand-copy">
|
|
<p className="eyebrow">NexaVPN</p>
|
|
<h1>Control Plane</h1>
|
|
<p className="brand-tagline">Remote access orchestration for your private WireGuard edge.</p>
|
|
</div>
|
|
</div>
|
|
<nav className="nav">
|
|
{items.map(([label, path]) => (
|
|
<NavLink
|
|
key={path}
|
|
to={path}
|
|
className={({ isActive }) => (isActive ? "nav-link active" : "nav-link")}
|
|
>
|
|
{label}
|
|
</NavLink>
|
|
))}
|
|
</nav>
|
|
</aside>
|
|
<main className="content">
|
|
<header className="topbar">
|
|
<div className="topbar-brand">
|
|
<img className="brand-logo brand-logo-mark" src="/NexaVPN_Logo_Only.png" alt="NexaVPN mark" />
|
|
<div>
|
|
<p className="eyebrow">Enterprise WireGuard</p>
|
|
<h2>Self-hosted VPN management</h2>
|
|
</div>
|
|
</div>
|
|
<div className="topbar-actions">
|
|
<div className="pill">Secure by design</div>
|
|
<button className="ghost-button" onClick={onLogout} type="button">
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</header>
|
|
<Outlet />
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|