Skip to main content
Glama

Agile Backlog MCP

by ehartye
App.tsx•7.58 kB
import { Routes, Route, Link, useLocation, useNavigate } from 'react-router-dom'; import { Network, TreePine, List, AlertTriangle, Menu, X } from 'lucide-react'; import { useState, useEffect } from 'react'; import DependencyGraphView from './components/DependencyGraphView'; import HierarchyTreeView from './components/HierarchyTreeView'; import BacklogListView from './components/BacklogListView'; import StoryDetailView from './components/StoryDetailView'; import ProjectSelector from './components/ProjectSelector'; import SprintBoard from './components/SprintBoard'; import BurndownChart from './components/BurndownChart'; function App() { const location = useLocation(); const navigate = useNavigate(); // Initialize selectedProjectId from URL immediately const getProjectIdFromPath = (pathname: string): number | null => { const match = pathname.match(/^\/project\/(\d+)/); return match ? parseInt(match[1]) : null; }; const [selectedProjectId, setSelectedProjectId] = useState<number | null>(() => getProjectIdFromPath(location.pathname) ); const [apiConnected, setApiConnected] = useState<boolean>(true); const [checkingApi, setCheckingApi] = useState<boolean>(true); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState<boolean>(false); // Update selectedProjectId when URL changes useEffect(() => { const projectId = getProjectIdFromPath(location.pathname); setSelectedProjectId(projectId); }, [location.pathname]); useEffect(() => { const checkApiHealth = async () => { try { const response = await fetch('/api/health'); setApiConnected(response.ok); } catch (error) { setApiConnected(false); } finally { setCheckingApi(false); } }; checkApiHealth(); // Check API health every 30 seconds const interval = setInterval(checkApiHealth, 30000); return () => clearInterval(interval); }, []); // Navigate to project when selection changes const handleProjectChange = (projectId: number | null) => { if (projectId) { navigate(`/project/${projectId}`); } else { navigate('/'); } }; const getNavItems = () => { const basePrefix = selectedProjectId ? `/project/${selectedProjectId}` : ''; return [ { path: basePrefix || '/', icon: List, label: 'Backlog List' }, { path: `${basePrefix}/dag`, icon: Network, label: 'Dependency Graph' }, { path: `${basePrefix}/tree`, icon: TreePine, label: 'Hierarchy Tree' }, ]; }; return ( <div className="flex h-screen bg-gray-50 flex-col"> {/* API Error Banner */} {!checkingApi && !apiConnected && ( <div className="bg-red-600 text-white px-6 py-3 flex items-center justify-between"> <div className="flex items-center gap-3"> <AlertTriangle size={20} /> <div> <p className="font-semibold">API Server Not Running</p> <p className="text-sm text-red-100"> Please start the API server: <code className="bg-red-700 px-2 py-0.5 rounded">npm run dev:server</code> in the web-ui directory </p> </div> </div> </div> )} <div className="flex flex-1 overflow-hidden w-full"> {/* Mobile backdrop */} {isMobileMenuOpen && ( <div className="fixed inset-0 bg-black bg-opacity-50 z-40 md:hidden" onClick={() => setIsMobileMenuOpen(false)} /> )} {/* Sidebar */} <nav className={` w-64 bg-white shadow-lg flex flex-col fixed md:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out ${isMobileMenuOpen ? 'translate-x-0' : '-translate-x-full md:translate-x-0'} `}> <div className="p-6 relative"> {/* Close button for mobile */} <button onClick={() => setIsMobileMenuOpen(false)} className="absolute top-4 right-4 p-2 rounded-lg hover:bg-gray-100 md:hidden" aria-label="Close menu" > <X size={24} className="text-gray-600" /> </button> <h1 className="text-2xl font-bold text-gray-800">Agile Backlog</h1> <p className="text-sm text-gray-500 mt-1">Dependency Visualization</p> </div> <ProjectSelector selectedProjectId={selectedProjectId} onProjectChange={handleProjectChange} /> <ul className="space-y-1 px-3 mt-4"> {getNavItems().map(({ path, icon: Icon, label }) => ( <li key={path}> <Link to={path} onClick={() => setIsMobileMenuOpen(false)} className={`flex items-center space-x-3 px-4 py-3 rounded-lg transition-colors ${ location.pathname === path ? 'bg-blue-50 text-blue-600 font-medium' : 'text-gray-700 hover:bg-gray-100' }`} > <Icon size={20} /> <span>{label}</span> </Link> </li> ))} </ul> <div className="absolute bottom-0 left-0 right-0 p-4 border-t"> <div className="text-xs text-gray-500"> <p className="font-medium">Legend</p> <div className="mt-2 space-y-1"> <div className="flex items-center space-x-2"> <div className="w-3 h-3 rounded-full bg-gray-300"></div> <span>Todo</span> </div> <div className="flex items-center space-x-2"> <div className="w-3 h-3 rounded-full bg-blue-500"></div> <span>In Progress</span> </div> <div className="flex items-center space-x-2"> <div className="w-3 h-3 rounded-full bg-yellow-500"></div> <span>Review</span> </div> <div className="flex items-center space-x-2"> <div className="w-3 h-3 rounded-full bg-green-500"></div> <span>Done</span> </div> <div className="flex items-center space-x-2"> <div className="w-3 h-3 rounded-full bg-red-500"></div> <span>Blocked</span> </div> </div> </div> </div> </nav> {/* Floating menu button for mobile */} <button onClick={() => setIsMobileMenuOpen(true)} className="fixed bottom-6 right-6 p-4 bg-blue-600 text-white rounded-full shadow-lg hover:bg-blue-700 transition-colors z-30 md:hidden" aria-label="Open menu" > <Menu size={24} /> </button> {/* Main content */} <main className="flex-1 overflow-hidden w-full min-w-0"> <Routes> <Route path="/" element={<BacklogListView projectId={null} />} /> <Route path="/project/:projectId" element={<BacklogListView />} /> <Route path="/project/:projectId/story/:storyId" element={<StoryDetailView />} /> <Route path="/project/:projectId/sprint/:sprintId" element={<SprintBoard />} /> <Route path="/project/:projectId/sprint/:sprintId/burndown" element={<BurndownChart />} /> <Route path="/project/:projectId/dag" element={<DependencyGraphView />} /> <Route path="/project/:projectId/tree" element={<HierarchyTreeView />} /> </Routes> </main> </div> </div> ); } export default App;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ehartye/agile-backlog-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server