🚧 Add broker status page

This commit is contained in:
Jonas Bengtsson
2026-09-20 07:55:18 +02:00
parent 4322446fcf
commit 1e491642f1
9 changed files with 461 additions and 6 deletions
+37 -5
View File
@@ -10,6 +10,7 @@ import { GroupsPage } from "./pages/Groups";
import { BrokerConfigPage } from "./pages/BrokerConfig";
import { CertificatesPage } from "./pages/Certificates";
import { SystemPage } from "./pages/System";
import { StatusPage } from "./pages/Status";
// Navigation structure per spec section 57. Entries with a `to` route are
// implemented; entries without one are planned pages, rendered greyed out
@@ -21,7 +22,7 @@ const NAV_SECTIONS: {
{
title: "Broker",
items: [
{ label: "Status", hint: "Dashboard / $SYS monitoring (Release 3)" },
{ label: "Status", to: "/broker/status" },
{ label: "Configuration", to: "/broker" },
{ label: "Listeners", to: "/broker#listeners" },
{ label: "Bridges", hint: "Bridge management (post-MVP)" },
@@ -82,6 +83,19 @@ function RestartIndicator() {
);
}
// Hash-aware active matching: an item is active only when BOTH its path
// AND its hash (if any) match the current location. NavLink's default
// prefix matching lights up every /broker* item on /broker/status, and
// hash items (#listeners, #revisions) all match on pathname alone.
function isItemActive(
to: string,
location: { pathname: string; hash: string },
): boolean {
const [path, hash] = to.split("#");
if (path !== location.pathname) return false;
return hash ? location.hash === `#${hash}` : location.hash === "";
}
function Layout({ children }: { children: React.ReactNode }) {
const { session, logout } = useAuth();
const location = useLocation();
@@ -92,9 +106,7 @@ function Layout({ children }: { children: React.ReactNode }) {
const activeSection = NAV_SECTIONS.find((section) =>
section.items.some(
(item) =>
item.to &&
item.to.split("#")[0] === location.pathname,
(item) => item.to && isItemActive(item.to, location),
),
);
@@ -129,7 +141,19 @@ function Layout({ children }: { children: React.ReactNode }) {
<div>
{section.items.map((item) =>
item.to ? (
<NavLink key={item.label} to={item.to}>
<NavLink
key={item.label}
to={item.to}
end
// Full control of the active class: NavLink's
// own prefix matching would mark every /broker*
// item active on /broker/status.
className={() =>
isItemActive(item.to!, location)
? "active"
: ""
}
>
{item.label}
</NavLink>
) : (
@@ -206,6 +230,14 @@ export function App() {
</Protected>
}
/>
<Route
path="/broker/status"
element={
<Protected>
<StatusPage />
</Protected>
}
/>
<Route
path="/certificates"
element={