'use client'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { useEffect, useState } from 'react'; const mlflowUrl = process.env.NEXT_PUBLIC_MLFLOW_URL ?? '/mlflow'; type NavItem = { href: string; label: string; external?: boolean; svcName?: string; // key in the health services map }; type NavSection = { label?: string; items: NavItem[]; }; const NAV: NavSection[] = [ { items: [{ href: '/', label: 'Overview' }], }, { label: 'Signals', items: [ { href: '/users', label: 'Users' }, { href: '/events', label: 'Events' }, { href: '/features', label: 'Features' }, { href: '/data-quality', label: 'Data quality' }, ], }, { label: 'Recommender', items: [ { href: '/tips', label: 'Tips' }, { href: '/reward-analytics', label: 'Rewards' }, { href: '/simulate', label: 'Simulations' }, ], }, { label: 'Operations', items: [ { href: '/health', label: 'Health' }, { href: '/ops', label: 'Ops' }, { href: '/sql', label: 'SQL runner' }, { href: '/audit', label: 'Audit log' }, ], }, { label: 'Resources', items: [ { href: '/docs', label: 'Docs' }, { href: mlflowUrl, label: 'MLflow ↗', external: true, svcName: 'mlflow' }, ], }, ]; const STATUS_DOT: Record = { ok: 'bg-green-500', degraded: 'bg-yellow-400', down: 'bg-red-500', }; export function AdminShell({ children }: { children: React.ReactNode }) { const pathname = usePathname(); const [svcStatus, setSvcStatus] = useState>({}); useEffect(() => { fetch('/api/admin/health', { credentials: 'include' }) .then((r) => r.json()) .then((data: { services?: { name: string; status: string }[] }) => { const map: Record = {}; for (const s of data.services ?? []) map[s.name] = s.status; setSvcStatus(map); }) .catch(() => {}); }, []); return (
{/* Sidebar */} {/* Main content */}
{children}
); }