Spaces:
Running
Running
| import { useState, useEffect, useCallback } from 'react'; | |
| import { Scan, Calendar, HelpCircle } from 'lucide-react'; | |
| import { Header } from './components/Header'; | |
| import { Tabs } from './components/Tabs'; | |
| import { ClassificationPage } from './pages/ClassificationPage'; | |
| import { GestationalAgePage } from './pages/GestationalAgePage'; | |
| import { HelpPage } from './pages/HelpPage'; | |
| import { ImageProvider } from './lib/ImageContext'; | |
| import { checkHealth, getFeedbackStats } from './lib/api'; | |
| const tabs = [ | |
| { id: 'classification', label: 'View Classification', icon: <Scan className="w-4 h-4" /> }, | |
| { id: 'gestational-age', label: 'Gestational Age', icon: <Calendar className="w-4 h-4" /> }, | |
| { id: 'help', label: 'Help', icon: <HelpCircle className="w-4 h-4" /> }, | |
| ]; | |
| function App() { | |
| const [activeTab, setActiveTab] = useState('classification'); | |
| const [isConnected, setIsConnected] = useState(false); | |
| const [feedbackStats, setFeedbackStats] = useState<{ | |
| total: number; | |
| correct: number; | |
| incorrect: number; | |
| } | null>(null); | |
| useEffect(() => { | |
| const checkConnection = async () => { | |
| const healthy = await checkHealth(); | |
| setIsConnected(healthy); | |
| }; | |
| checkConnection(); | |
| const interval = setInterval(checkConnection, 10000); | |
| return () => clearInterval(interval); | |
| }, []); | |
| // Load feedback stats | |
| const loadFeedbackStats = useCallback(async () => { | |
| try { | |
| const stats = await getFeedbackStats(); | |
| setFeedbackStats({ | |
| total: stats.total_feedback, | |
| correct: stats.correct_count, | |
| incorrect: stats.incorrect_count | |
| }); | |
| } catch { | |
| // Ignore errors - stats are optional | |
| } | |
| }, []); | |
| useEffect(() => { | |
| loadFeedbackStats(); | |
| }, [loadFeedbackStats]); | |
| return ( | |
| <ImageProvider> | |
| <div className="h-screen flex flex-col bg-dark-bg gradient-mesh overflow-hidden"> | |
| {/* Header - fixed height */} | |
| <Header isConnected={isConnected} feedbackStats={feedbackStats} /> | |
| {/* Tabs - fixed height */} | |
| <Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} /> | |
| {/* Main content - fills remaining space */} | |
| <main className="flex-1 flex min-h-0 overflow-hidden"> | |
| {activeTab === 'classification' && <ClassificationPage onFeedbackUpdate={loadFeedbackStats} />} | |
| {activeTab === 'gestational-age' && <GestationalAgePage />} | |
| {activeTab === 'help' && <HelpPage />} | |
| </main> | |
| {/* Footer - fixed height, always visible */} | |
| <footer className="flex-shrink-0 px-6 py-3 border-t border-dark-border bg-white"> | |
| <div className="flex items-center justify-between text-xs"> | |
| <span className="text-text-secondary">FetalCLIP • Foundation Model for Fetal Ultrasound Analysis</span> | |
| <div className="flex items-center gap-4"> | |
| <a | |
| href="https://huggingface.co/numansaeed/fetalclip-model" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-accent-blue hover:text-accent-blue-hover transition-colors font-medium" | |
| > | |
| 🤗 Model Hub | |
| </a> | |
| <a | |
| href="https://arxiv.org/abs/2502.14807" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-accent-blue hover:text-accent-blue-hover transition-colors font-medium" | |
| > | |
| 📄 Paper | |
| </a> | |
| </div> | |
| </div> | |
| </footer> | |
| </div> | |
| </ImageProvider> | |
| ); | |
| } | |
| export default App; | |