Initial curated ITWorx_MCP_Hub source from 368fba691ea8d23403b4d56b53b5ec670d7c5020
This commit is contained in:
commit
cb5be898ea
663 files changed
+82674
No files matched your search
@@ -0,0 +1,188 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { DashboardSnapshot, ManagementApiClient, ManagementRole, SessionStatus } from "./contracts";
|
||||
import { routeTitle } from "./contracts";
|
||||
import { ApiError, createApiClient, safeApiMessage } from "./api";
|
||||
import { RbacProvider } from "./rbac-context";
|
||||
import { can } from "./rbac";
|
||||
import { basePath, resourceKind, usePath } from "./routing";
|
||||
import { Brand } from "./components/Brand";
|
||||
import { CommandMenu } from "./components/CommandMenu";
|
||||
import { ErrorPanel } from "./components/ErrorPanel";
|
||||
import { Icon } from "./components/Icon";
|
||||
import { LoadingPanel } from "./components/LoadingPanel";
|
||||
import { SessionMenu } from "./components/SessionMenu";
|
||||
import { navigation } from "./navigation";
|
||||
import { AuthGateway } from "./pages/AuthGateway";
|
||||
import { Dashboard } from "./pages/Dashboard";
|
||||
import { ForbiddenPanel } from "./pages/ForbiddenPanel";
|
||||
import { Operations } from "./pages/Operations";
|
||||
import { ProjectDetail } from "./pages/ProjectDetail";
|
||||
import { ResourcePage } from "./pages/ResourcePage";
|
||||
import { SettingsPage } from "./pages/SettingsPage";
|
||||
|
||||
export function App({ api: apiProp }: { api?: ManagementApiClient }) {
|
||||
const [api] = useState<ManagementApiClient>(() => apiProp ?? createApiClient());
|
||||
const [path, navigate] = usePath();
|
||||
const [session, setSession] = useState<SessionStatus>("checking");
|
||||
const [snapshot, setSnapshot] = useState<DashboardSnapshot | null>(null);
|
||||
const [loadState, setLoadState] = useState<"idle" | "loading" | "error">("loading");
|
||||
const [error, setError] = useState("The control API is unavailable.");
|
||||
const [roles, setRoles] = useState<ManagementRole[]>([]);
|
||||
const [subject, setSubject] = useState<string | null>(null);
|
||||
const [navOpen, setNavOpen] = useState(false);
|
||||
const [commandOpen, setCommandOpen] = useState(false);
|
||||
const navTriggerRef = useRef<HTMLButtonElement>(null);
|
||||
const title = useMemo(() => routeTitle(basePath(path)), [path]);
|
||||
|
||||
const clearIdentity = () => {
|
||||
setRoles([]);
|
||||
setSnapshot(null);
|
||||
setSubject(null);
|
||||
};
|
||||
|
||||
const closeNavigation = (restoreFocus = true) => {
|
||||
setNavOpen(false);
|
||||
if (restoreFocus) window.setTimeout(() => navTriggerRef.current?.focus(), 0);
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
setSession("checking");
|
||||
setLoadState("loading");
|
||||
let current;
|
||||
try {
|
||||
current = await api.session();
|
||||
} catch (caught) {
|
||||
if (caught instanceof ApiError && caught.status === 401) {
|
||||
clearIdentity();
|
||||
setSession("unauthenticated");
|
||||
setLoadState("idle");
|
||||
return;
|
||||
}
|
||||
clearIdentity();
|
||||
setError(safeApiMessage(caught));
|
||||
setLoadState("error");
|
||||
return;
|
||||
}
|
||||
if (!current.authenticated) {
|
||||
clearIdentity();
|
||||
setSession("unauthenticated");
|
||||
setLoadState("idle");
|
||||
return;
|
||||
}
|
||||
setSession("authenticated");
|
||||
setRoles(current.session?.roles ?? []);
|
||||
setSubject(current.session?.subject ?? null);
|
||||
try {
|
||||
setSnapshot(await api.dashboard());
|
||||
setLoadState("idle");
|
||||
} catch (caught) {
|
||||
if (caught instanceof ApiError && caught.status === 401) {
|
||||
clearIdentity();
|
||||
setSession("expired");
|
||||
setError(caught.message);
|
||||
} else {
|
||||
setError(safeApiMessage(caught));
|
||||
}
|
||||
setLoadState("error");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => { void load(); }, []);
|
||||
useEffect(() => { setNavOpen(false); }, [path]);
|
||||
useEffect(() => {
|
||||
if (!navOpen) return;
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") closeNavigation();
|
||||
};
|
||||
document.addEventListener("keydown", onKeyDown);
|
||||
return () => document.removeEventListener("keydown", onKeyDown);
|
||||
}, [navOpen]);
|
||||
useEffect(() => {
|
||||
if (session !== "authenticated") return;
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k") {
|
||||
event.preventDefault();
|
||||
setCommandOpen((current) => {
|
||||
if (!current && document.querySelector('[role="dialog"]')) return current;
|
||||
return !current;
|
||||
});
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", onKeyDown);
|
||||
return () => document.removeEventListener("keydown", onKeyDown);
|
||||
}, [session]);
|
||||
|
||||
const kind = resourceKind(path);
|
||||
const detail = basePath(path).startsWith("/projects/");
|
||||
const canReadAudit = can(roles, "audit.read");
|
||||
const active = (href: string) => basePath(path) === href || (href !== "/" && basePath(path).startsWith(`${href}/`));
|
||||
const authenticated = session === "authenticated";
|
||||
|
||||
const content = session === "checking" || loadState === "loading"
|
||||
? <LoadingPanel />
|
||||
: session === "unauthenticated" && loadState === "idle"
|
||||
? <AuthGateway />
|
||||
: loadState === "error"
|
||||
? <ErrorPanel message={error} expired={session === "expired"} retry={() => void load()} />
|
||||
: detail
|
||||
? <ProjectDetail path={path} navigate={navigate} api={api} />
|
||||
: kind
|
||||
? <ResourcePage path={path} kind={kind} api={api} navigate={navigate} />
|
||||
: snapshot && basePath(path) === "/"
|
||||
? <Dashboard snapshot={snapshot} reload={() => void load()} navigate={navigate} />
|
||||
: basePath(path) === "/operations"
|
||||
? <Operations path={path} navigate={navigate} snapshot={snapshot} />
|
||||
: basePath(path) === "/settings"
|
||||
? <SettingsPage path={path} navigate={navigate} />
|
||||
: <ForbiddenPanel />;
|
||||
|
||||
return (
|
||||
<RbacProvider roles={roles}>
|
||||
<a className="skip-link" href="#main-content">Skip to main content</a>
|
||||
<div className={authenticated ? "app-shell" : "preauth-shell"}>
|
||||
{authenticated ? <>
|
||||
{navOpen ? <button type="button" className="nav-backdrop" onClick={() => closeNavigation()} aria-label="Dismiss navigation" /> : null}
|
||||
<aside className={navOpen ? "sidebar sidebar-open" : "sidebar"} aria-label="Application sidebar">
|
||||
<div className="sidebar-head">
|
||||
<a href="/" onClick={(event) => { event.preventDefault(); navigate("/"); }}><Brand /></a>
|
||||
<button type="button" className="nav-close" aria-label="Close navigation" onClick={() => closeNavigation()}>×</button>
|
||||
</div>
|
||||
<nav aria-label="Primary navigation">
|
||||
{navigation.map((group) => {
|
||||
const items = group.items.filter((item) => !item.auditOnly || canReadAudit);
|
||||
return items.length ? <section className="nav-group" aria-label={group.label} key={group.label}>
|
||||
<p>{group.label}</p>
|
||||
{items.map((item) => <a
|
||||
aria-current={active(item.href) ? "page" : undefined}
|
||||
className={active(item.href) ? "nav-link active" : "nav-link"}
|
||||
href={item.href}
|
||||
key={item.href}
|
||||
onClick={(event) => { event.preventDefault(); navigate(item.href); }}
|
||||
><Icon name={item.icon} size={17} /><span>{item.label}</span></a>)}
|
||||
</section> : null;
|
||||
})}
|
||||
</nav>
|
||||
<div className="sidebar-foot">
|
||||
<span><span className="secure-dot" aria-hidden="true" />Policy boundary active</span>
|
||||
<small>Same-origin · server-managed</small>
|
||||
</div>
|
||||
</aside>
|
||||
</> : null}
|
||||
<main className="main-content" id="main-content" tabIndex={-1}>
|
||||
{authenticated ? <header className="topbar">
|
||||
<button ref={navTriggerRef} type="button" className="nav-trigger" aria-label="Open navigation" aria-expanded={navOpen} onClick={() => setNavOpen(true)}><span aria-hidden="true" /><span aria-hidden="true" /><span aria-hidden="true" /></button>
|
||||
<div className="topbar-title"><p className="eyebrow">Governance control plane</p><h1>{title}</h1></div>
|
||||
<div className="topbar-actions">
|
||||
<button className="command-trigger" type="button" onClick={() => setCommandOpen(true)} aria-label="Open quick jump">
|
||||
<Icon name="search" size={16} /><span>Quick jump</span><kbd>Ctrl K</kbd>
|
||||
</button>
|
||||
<SessionMenu subject={subject} roles={roles} />
|
||||
</div>
|
||||
</header> : null}
|
||||
{content}
|
||||
</main>
|
||||
</div>
|
||||
{authenticated ? <CommandMenu open={commandOpen} canReadAudit={canReadAudit} navigate={navigate} onClose={() => setCommandOpen(false)} /> : null}
|
||||
</RbacProvider>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user