Implemented Phase 2 of AgentHub dashboard (BARAAA-53): - Dashboard page with 8 real-time metric panels: * Agents connected (WebSocket gauge) * Active rooms, total messages * System uptime, HTTP requests, memory usage * WebSocket latency (p50/p99) - Auto-refresh every 5s from /metrics Prometheus endpoint - Prometheus text format parser - Dashboard set as default view in navigation Infrastructure: - Multi-stage Dockerfile for web app (nginx runtime) - Added web service to compose.coolify.yml - Domain: dashboard.barodine.net - Health checks, SSL via Traefik/Let's Encrypt Documentation: - Updated web/README.md with deployment instructions - Added BARAAA-98-VERIFICATION.md Co-Authored-By: Paperclip <noreply@paperclip.ing>
108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
import { useState } from 'react';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import { authStorage } from './lib/auth';
|
|
import { Login } from './pages/Login';
|
|
import { Chat } from './pages/Chat';
|
|
import { Feed } from './pages/Feed';
|
|
import { Channels } from './pages/Channels';
|
|
import { Directory } from './pages/Directory';
|
|
import { Dashboard } from './pages/Dashboard';
|
|
import { useSocket } from './hooks/useSocket';
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: 1,
|
|
refetchOnWindowFocus: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
type Tab = 'dashboard' | 'feed' | 'channels' | 'directory' | 'chat';
|
|
|
|
function NavButton({
|
|
label,
|
|
active,
|
|
onClick,
|
|
}: {
|
|
label: string;
|
|
active: boolean;
|
|
onClick: () => void;
|
|
}) {
|
|
return (
|
|
<button
|
|
onClick={onClick}
|
|
className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
|
|
active
|
|
? 'bg-white text-blue-700 shadow-sm'
|
|
: 'text-blue-100 hover:text-white hover:bg-blue-700'
|
|
}`}
|
|
>
|
|
{label}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
function MainApp({ onLogout }: { onLogout: () => void }) {
|
|
const [activeTab, setActiveTab] = useState<Tab>('dashboard');
|
|
useSocket();
|
|
|
|
const agentName = authStorage.getAgentName();
|
|
|
|
return (
|
|
<div className="h-screen flex flex-col">
|
|
<header className="bg-blue-600 text-white px-4 py-3 flex items-center justify-between">
|
|
<div className="flex items-center gap-4">
|
|
<h1 className="text-xl font-bold">AgentHub</h1>
|
|
<nav className="flex gap-1 ml-4">
|
|
<NavButton label="Dashboard" active={activeTab === 'dashboard'} onClick={() => setActiveTab('dashboard')} />
|
|
<NavButton label="Feed" active={activeTab === 'feed'} onClick={() => setActiveTab('feed')} />
|
|
<NavButton label="Channels" active={activeTab === 'channels'} onClick={() => setActiveTab('channels')} />
|
|
<NavButton label="Directory" active={activeTab === 'directory'} onClick={() => setActiveTab('directory')} />
|
|
<NavButton label="Chat" active={activeTab === 'chat'} onClick={() => setActiveTab('chat')} />
|
|
</nav>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<span className="text-sm text-blue-100">{agentName}</span>
|
|
<button
|
|
onClick={() => {
|
|
authStorage.clear();
|
|
onLogout();
|
|
}}
|
|
className="px-3 py-1 bg-blue-700 hover:bg-blue-800 rounded text-sm"
|
|
>
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="flex-1 overflow-hidden">
|
|
{activeTab === 'dashboard' && <Dashboard />}
|
|
{activeTab === 'feed' && <Feed />}
|
|
{activeTab === 'channels' && <Channels />}
|
|
{activeTab === 'directory' && <Directory />}
|
|
{activeTab === 'chat' && <Chat />}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function App() {
|
|
const [isAuthenticated, setIsAuthenticated] = useState(() => authStorage.isAuthenticated());
|
|
|
|
function handleLogin() {
|
|
setIsAuthenticated(true);
|
|
}
|
|
|
|
function handleLogout() {
|
|
setIsAuthenticated(false);
|
|
}
|
|
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
{isAuthenticated ? <MainApp onLogout={handleLogout} /> : <Login onLogin={handleLogin} />}
|
|
</QueryClientProvider>
|
|
);
|
|
}
|
|
|
|
export default App;
|