79 lines
2.9 KiB
TypeScript
79 lines
2.9 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { NavLink, Outlet, useLocation } from 'react-router-dom';
|
|
import { LayoutDashboard, Settings, Activity, History } from 'lucide-react';
|
|
|
|
const MainLayout: React.FC = () => {
|
|
const [isServerOnline, setIsServerOnline] = useState(false);
|
|
const location = useLocation();
|
|
|
|
useEffect(() => {
|
|
// Basic ping to check if our API is running
|
|
fetch('/api/health')
|
|
.then((res) => {
|
|
if (res.ok) setIsServerOnline(true);
|
|
})
|
|
.catch(() => setIsServerOnline(false));
|
|
|
|
// Optional: add a polling interval
|
|
const interval = setInterval(() => {
|
|
fetch('/api/health')
|
|
.then((res) => setIsServerOnline(res.ok))
|
|
.catch(() => setIsServerOnline(false));
|
|
}, 10000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
const getPageTitle = (pathname: string) => {
|
|
switch (pathname) {
|
|
case '/': return 'Dashboard Overview';
|
|
case '/settings': return 'Definitions & Policies';
|
|
case '/history': return 'Run History';
|
|
default: return 'AI Ops';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div id="root">
|
|
<aside className="sidebar">
|
|
<div className="sidebar-header">
|
|
<h1>AI Ops Control</h1>
|
|
<p>Topology & Telemetry</p>
|
|
</div>
|
|
<nav className="sidebar-nav">
|
|
<NavLink to="/" className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}>
|
|
<LayoutDashboard /> Dashboard
|
|
</NavLink>
|
|
<NavLink to="/history" className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}>
|
|
<History /> History
|
|
</NavLink>
|
|
<NavLink to="/settings" className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}>
|
|
<Settings /> Policies & Limits
|
|
</NavLink>
|
|
</nav>
|
|
<div className="sidebar-footer">
|
|
<div className={`status-dot ${isServerOnline ? 'online' : 'offline'}`} />
|
|
{isServerOnline ? 'Server Online' : 'Connecting...'}
|
|
</div>
|
|
</aside>
|
|
|
|
<main className="main-content">
|
|
<header className="top-bar">
|
|
<h2 className="page-title">{getPageTitle(location.pathname)}</h2>
|
|
<div>
|
|
<div className="badge neutral flex items-center gap-2">
|
|
<Activity size={14} /> Agent Ready
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div className="content-scroll animate-fade-in" key={location.pathname}>
|
|
<Outlet />
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default MainLayout;
|