import React, { useMemo, useState } from 'react' import { Link, Outlet, useLocation } from 'react-router-dom' import { getLastRequestIds } from '../api/client' import { Button, Code, TextInput } from '../components/primitives' import { Icons } from '../components/ui/Icons' type NavItem = { label: string to: string icon: React.ComponentType } type NavCategory = { label: string items: NavItem[] } const navCategories: NavCategory[] = [ { label: 'Platform', items: [ { label: 'Overview', to: '/', icon: Icons.Activity }, { label: 'Tenants', to: '/tenants', icon: Icons.Tenants }, { label: 'Fleet Nodes', to: '/fleet', icon: Icons.Fleet }, { label: 'Observability', to: '/observability', icon: Icons.Observability }, ], }, { label: 'Infrastructure', items: [ { label: 'Scale & Placement', to: '/scale-placement', icon: Icons.Topology }, { label: 'Deployments', to: '/deployments', icon: Icons.Deployments }, ], }, { label: 'Resources', items: [ { label: 'Config Registry', to: '/config', icon: Icons.Config }, { label: 'Type Definitions', to: '/definitions', icon: Icons.Definitions }, { label: 'Documents', to: '/documents', icon: Icons.Storage }, ], }, { label: 'Governance', items: [ { label: 'Audit Log', to: '/audit-log', icon: Icons.Audit }, { label: 'Platform Drift', to: '/drift', icon: Icons.Drift }, ], }, { label: 'Management', items: [ { label: 'Users', to: '/users', icon: Icons.Users }, { label: 'Sessions', to: '/sessions', icon: Icons.Sessions }, { label: 'Roles & Permissions', to: '/roles-permissions', icon: Icons.ShieldAlert }, { label: 'Settings', to: '/settings', icon: Icons.Settings }, ], }, ] function normalizePath(pathname: string) { if (pathname === '') return '/' if (pathname === '/') return '/' return pathname.endsWith('/') ? pathname.slice(0, -1) : pathname } export function Layout() { const location = useLocation() const activePath = normalizePath(location.pathname) const [query, setQuery] = useState('') const lastIds = getLastRequestIds() const grafana = useMemo(() => { const base = (import.meta.env.VITE_GRAFANA_URL as string | undefined) ?? '' const loki = (import.meta.env.VITE_GRAFANA_LOKI_DATASOURCE as string | undefined) ?? 'Loki' const tempo = (import.meta.env.VITE_GRAFANA_TEMPO_DATASOURCE as string | undefined) ?? 'Tempo' return { base, loki, tempo } }, []) function openGrafanaLogs(id: string) { if (!grafana.base) return const left = encodeURIComponent( JSON.stringify({ datasource: grafana.loki, queries: [{ refId: 'A', expr: `{correlation_id="${id}"}` }], }), ) window.open(`${grafana.base.replace(/\/$/, '')}/explore?left=${left}`, '_blank', 'noreferrer') } function openGrafanaTrace(id: string) { if (!grafana.base) return const left = encodeURIComponent( JSON.stringify({ datasource: grafana.tempo, queries: [{ refId: 'A', queryType: 'traceId', traceId: id }], }), ) window.open(`${grafana.base.replace(/\/$/, '')}/explore?left=${left}`, '_blank', 'noreferrer') } async function copy(text: string) { try { await navigator.clipboard.writeText(text) } catch { return } } const NavLink = ({ item }: { item: NavItem }) => { const active = activePath === normalizePath(item.to) const Icon = item.icon return ( { if (!active) e.currentTarget.style.background = 'rgba(255,255,255,0.05)' }} onMouseOut={(e) => { if (!active) e.currentTarget.style.background = 'transparent' }} > {item.label} ) } return (
{/* Sidebar */} {/* Main Content */}
{ if (e.key === 'Enter') { const id = query.trim() if (!id) return openGrafanaLogs(id) } }} />
{lastIds && (
request_id {lastIds.requestId}
{lastIds.correlationId && (
correlation_id {lastIds.correlationId}
)}
)}
) }