import { useEffect, useMemo, useState } from "react"; import type { ManagementApiClient, ResourceKind, ResourcePage as ResourcePageRecord, ResourceRecord } from "../contracts"; import { routeTitle } from "../contracts"; import { safeApiMessage } from "../api"; import { Breadcrumbs } from "../components/Breadcrumbs"; import type { Density } from "../components/DataTable"; import { columnsFor, DataTable } from "../components/DataTable"; import { ErrorPanel } from "../components/ErrorPanel"; import { Icon } from "../components/Icon"; import { LoadingPanel } from "../components/LoadingPanel"; import { basePath } from "../routing"; import { useCan } from "../rbac-context"; import { CatalogWorkflow } from "./CatalogWorkflow"; import { ClientOnboarding } from "./ClientOnboarding"; import { PolicySimulator } from "./PolicySimulator"; import { ToolReadinessModal } from "./ToolReadinessModal"; const descriptions: Record = { projects: "Inspect health, environments and bounded capabilities across registered projects.", servers: "Review project-scoped virtual MCP endpoints published through the trusted gateway.", tools: "Find governed tools, verify their route and run safe readiness checks before invocation.", connectors: "Monitor purpose-built source integrations without exposing their underlying credentials.", clients: "Manage project-bound machine identities and generate secret-free client configuration.", policies: "Inspect effective policy records and explain decisions against known entities.", audit: "Trace governed requests by project, client, tool, decision and non-secret trace identifier.", }; type Feedback = { tone: "info" | "danger"; message: string } | null; export function ResourcePage({ path, kind, api, navigate }: { path: string; kind: ResourceKind; api: ManagementApiClient; navigate: (next: string) => void }) { const canDisableClients = useCan("client.disable"); const canTestTools = useCan("tool.test"); const params = new URLSearchParams(path.split("?")[1] ?? ""); const urlFilter = params.get("q") ?? ""; const [filterInput, setFilterInput] = useState(urlFilter); const [data, setData] = useState(null); const [state, setState] = useState<"loading" | "ready" | "error">("loading"); const [error, setError] = useState(""); const [feedback, setFeedback] = useState(null); const [cursorStack, setCursorStack] = useState<(string | undefined)[]>([undefined]); const [density, setDensity] = useState("comfortable"); const [testingTool, setTestingTool] = useState(null); const [refreshKey, setRefreshKey] = useState(0); const cursor = cursorStack[cursorStack.length - 1]; const hasCurrentData = data?.kind === kind; const isRefreshing = state === "loading" && hasCurrentData; const visibleColumns = useMemo(() => { const hideActions = (kind === "clients" && !canDisableClients) || (kind === "tools" && !canTestTools); return hideActions ? columnsFor(kind).filter((column) => column !== "actions") : columnsFor(kind); }, [kind, canDisableClients, canTestTools]); useEffect(() => { setFilterInput(urlFilter); }, [urlFilter]); useEffect(() => { const normalizedFilter = filterInput.trim(); if (normalizedFilter === urlFilter) return; const timer = window.setTimeout(() => { navigate(normalizedFilter ? `/${kind}?q=${encodeURIComponent(normalizedFilter)}` : `/${kind}`); }, 260); return () => window.clearTimeout(timer); }, [filterInput, kind, navigate, urlFilter]); useEffect(() => { setCursorStack([undefined]); }, [kind, urlFilter]); useEffect(() => { let active = true; setState("loading"); setError(""); void api.resource(kind, { filter: urlFilter, cursor, limit: 25 }) .then((value) => { if (!active) return; setData(value); setState("ready"); }) .catch((caught) => { if (!active) return; setError(safeApiMessage(caught)); setState("error"); }); return () => { active = false; }; }, [api, cursor, kind, refreshKey, urlFilter]); const nextPage = () => { if (data?.page.nextCursor) setCursorStack((stack) => [...stack, data.page.nextCursor]); }; const previousPage = () => setCursorStack((stack) => (stack.length > 1 ? stack.slice(0, -1) : stack)); const retry = () => setRefreshKey((value) => value + 1); const action = async (id: string, verb: "disable" | "revoke") => { setFeedback({ tone: "info", message: "Submitting server-authorized client action…" }); try { const result = await api.clientAction(id, verb); setFeedback({ tone: "info", message: result.message }); retry(); } catch (caught) { setFeedback({ tone: "danger", message: safeApiMessage(caught) }); } }; const config = async (id: string) => { try { const value = await api.generatedConfig(id); setFeedback({ tone: "info", message: `Generated configuration for ${value.clientId}: ${value.endpoint} · scopes ${value.scopes.join(", ")}. Secret material is write-only and is not displayed.` }); } catch (caught) { setFeedback({ tone: "danger", message: safeApiMessage(caught) }); } }; if (!hasCurrentData && state === "error") return ; if (!hasCurrentData || !data) return ; return

Management workspace

{routeTitle(basePath(path))}

{descriptions[kind]}

{data.page.total}total · server paginated
Density
{isRefreshing ? : null} {state === "error" ?
{error}
: null} {data.rows.length === 0 ?
No matching records{urlFilter ? "Clear or broaden the current filter." : "Nothing has been registered in this workspace yet."}
: }
Showing {data.rows.length} of {data.page.total}
{feedback ?
{feedback.message}
: null}
{kind === "tools" ? : null} {kind === "policies" ? : null} {kind === "clients" ? <>

Client safety

Write-only credentials

Generated configuration includes endpoint, identity reference and scopes only. Client secrets are never returned by the API or rendered into the DOM.

Secret-free browser surface
: null} {testingTool ? setTestingTool(null)} /> : null}
; }