Skip to main content
Glama
App.tsx10.5 kB
import React, { useState, useEffect } from 'react' import { Routes, Route, Link, useLocation } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' import { Server, Activity, Terminal, Globe, Users, Database, Settings, Monitor, Minimize2, Square, X, Download, Maximize2 } from 'lucide-react' import Overview from './components/Overview' import Sessions from './components/Sessions' import ToolCalls from './components/ToolCalls' import BrowserSessions from './components/BrowserSessions' import ServerHealth from './components/ServerHealth' // API functions const fetchServerHealth = async () => { const response = await fetch('/health') if (!response.ok) throw new Error('Failed to fetch server health') return response.json() } function App() { const location = useLocation() const [isStandalone, setIsStandalone] = useState(false) const [showInstallButton, setShowInstallButton] = useState(false) const [canInstall, setCanInstall] = useState(false) const [isMaximized, setIsMaximized] = useState(false) const { data: health, isError } = useQuery({ queryKey: ['health'], queryFn: fetchServerHealth, refetchInterval: 5000 }) useEffect(() => { // Check if running as PWA const checkStandalone = () => { const standalone = window.matchMedia('(display-mode: standalone)').matches || (window.navigator as any).standalone || document.referrer.includes('android-app://'); setIsStandalone(standalone); }; checkStandalone(); window.matchMedia('(display-mode: standalone)').addListener(checkStandalone); // Check for PWA install capability const handleBeforeInstallPrompt = (e: Event) => { e.preventDefault(); setCanInstall(true); setShowInstallButton(true); }; window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt); window.addEventListener('appinstalled', () => { setCanInstall(false); setShowInstallButton(false); }); return () => { window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt); }; }, []); const handleInstallPWA = () => { const deferredPrompt = (window as any).deferredPrompt; if (deferredPrompt) { deferredPrompt.prompt(); deferredPrompt.userChoice.then((choiceResult: any) => { if (choiceResult.outcome === 'accepted') { console.log('PWA installation accepted'); } setShowInstallButton(false); (window as any).deferredPrompt = null; }); } }; const handleWindowControl = (action: 'minimize' | 'maximize' | 'close') => { if ('windowControlsOverlay' in navigator) { switch (action) { case 'minimize': // On desktop PWAs, this would minimize the window console.log('Minimize window'); break; case 'maximize': setIsMaximized(!isMaximized); if (document.fullscreenElement) { document.exitFullscreen(); } else { document.documentElement.requestFullscreen(); } break; case 'close': window.close(); break; } } }; const navigation = [ { name: 'Overview', href: '/', icon: Monitor, current: location.pathname === '/' }, { name: 'Sessions', href: '/sessions', icon: Users, current: location.pathname === '/sessions' }, { name: 'Browser', href: '/browser', icon: Globe, current: location.pathname === '/browser' }, { name: 'Tool Calls', href: '/tools', icon: Terminal, current: location.pathname === '/tools' }, { name: 'Health', href: '/health', icon: Activity, current: location.pathname === '/health' }, ] return ( <div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex flex-col"> {/* Custom Title Bar (PWA Mode) */} {isStandalone && ( <div id="titlebar" className="drag-region h-12 bg-gray-900 dark:bg-gray-950 flex items-center justify-between px-4 select-none relative" style={{ background: 'linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%)' }} > {/* Left side - App info */} <div className="flex items-center space-x-3"> <Server className="h-5 w-5 text-white" /> <span className="text-white font-medium text-sm">MCP Fullstack Dashboard</span> </div> {/* Center - Status */} <div className="no-drag flex items-center"> <div className={`flex items-center px-2 py-1 rounded-full text-xs font-medium ${ isError ? 'bg-red-500/20 text-red-200 border border-red-500/30' : health?.status === 'ok' ? 'bg-green-500/20 text-green-200 border border-green-500/30' : 'bg-yellow-500/20 text-yellow-200 border border-yellow-500/30' }`}> <div className={`w-1.5 h-1.5 rounded-full mr-1.5 ${ isError ? 'bg-red-400' : health?.status === 'ok' ? 'bg-green-400' : 'bg-yellow-400' }`} /> {isError ? 'Offline' : health?.status || 'Unknown'} </div> </div> {/* Right side - Window controls and install */} <div className="no-drag flex items-center space-x-2"> {showInstallButton && ( <button onClick={handleInstallPWA} className="p-1.5 rounded hover:bg-white/10 transition-colors" title="Install App" > <Download className="h-4 w-4 text-white" /> </button> )} <button onClick={() => handleWindowControl('minimize')} className="p-1.5 rounded hover:bg-white/10 transition-colors" title="Minimize" > <Minimize2 className="h-4 w-4 text-white" /> </button> <button onClick={() => handleWindowControl('maximize')} className="p-1.5 rounded hover:bg-white/10 transition-colors" title={isMaximized ? 'Restore' : 'Maximize'} > {isMaximized ? ( <Square className="h-4 w-4 text-white" /> ) : ( <Maximize2 className="h-4 w-4 text-white" /> )} </button> <button onClick={() => handleWindowControl('close')} className="p-1.5 rounded hover:bg-red-500/20 transition-colors" title="Close" > <X className="h-4 w-4 text-white" /> </button> </div> </div> )} {/* Navigation */} <nav className="bg-white dark:bg-gray-800 shadow-sm border-b dark:border-gray-700 flex-shrink-0"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="flex justify-between h-16"> <div className="flex"> {!isStandalone && ( <div className="flex-shrink-0 flex items-center"> <Server className="h-8 w-8 text-indigo-600" /> <span className="ml-2 text-xl font-semibold text-gray-900 dark:text-white"> MCP Fullstack </span> </div> )} <div className={`${!isStandalone ? 'ml-6' : ''} flex space-x-8`}> {navigation.map((item) => ( <Link key={item.name} to={item.href} className={`inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium transition-colors ${ item.current ? 'border-indigo-500 text-gray-900 dark:text-white' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-300 dark:hover:text-white' }`} > <item.icon className="w-4 h-4 mr-2" /> {item.name} </Link> ))} </div> </div> <div className="flex items-center space-x-4"> {!isStandalone && showInstallButton && ( <button id="install-pwa" onClick={handleInstallPWA} className="inline-flex items-center px-3 py-1.5 border border-indigo-300 text-sm font-medium rounded-md text-indigo-700 bg-indigo-50 hover:bg-indigo-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-indigo-900/20 dark:text-indigo-300 dark:border-indigo-700 dark:hover:bg-indigo-800/30 transition-colors" > <Download className="w-4 h-4 mr-2" /> Install App </button> )} {!isStandalone && ( <div className={`flex items-center px-3 py-1 rounded-full text-xs font-medium ${ isError ? 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200' : health?.status === 'ok' ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200' }`}> <div className={`w-2 h-2 rounded-full mr-2 ${ isError ? 'bg-red-500' : health?.status === 'ok' ? 'bg-green-500' : 'bg-yellow-500' }`} /> {isError ? 'Offline' : health?.status || 'Unknown'} </div> )} </div> </div> </div> </nav> {/* Main content */} <main className="flex-1 max-w-7xl mx-auto w-full py-6 sm:px-6 lg:px-8"> <Routes> <Route path="/" element={<Overview />} /> <Route path="/sessions" element={<Sessions />} /> <Route path="/browser" element={<BrowserSessions />} /> <Route path="/tools" element={<ToolCalls />} /> <Route path="/health" element={<ServerHealth />} /> </Routes> </main> </div> ) } export default App

Latest Blog Posts

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/JacobFV/mcp-fullstack'

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