snikhilesh's picture
Upload folder using huggingface_hub
2053209 verified
/**
* Medical Report Analysis Platform - Main Application
* Professional medical-grade interface for AI-powered document analysis
*/
import { useState } from 'react';
import { FileUpload } from './components/FileUpload';
import { AnalysisStatus } from './components/AnalysisStatus';
import { AnalysisResults } from './components/AnalysisResults';
import { Header } from './components/Header';
import { ModelInfo } from './components/ModelInfo';
import './App.css';
interface JobStatus {
jobId: string;
status: 'idle' | 'uploading' | 'processing' | 'completed' | 'failed';
progress: number;
message: string;
}
interface AnalysisResult {
job_id: string;
document_type: string;
confidence: number;
analysis: any;
specialized_results: any[];
summary: string;
timestamp: string;
}
function App() {
const [jobStatus, setJobStatus] = useState<JobStatus>({
jobId: '',
status: 'idle',
progress: 0,
message: ''
});
const [analysisResult, setAnalysisResult] = useState<AnalysisResult | null>(null);
const [showModelInfo, setShowModelInfo] = useState(false);
// Use relative URL in production (HuggingFace Spaces), localhost in development
const getApiUrl = () => {
// If VITE_API_URL is explicitly set, use it
if (import.meta.env.VITE_API_URL) {
return import.meta.env.VITE_API_URL;
}
// In production (HuggingFace Spaces), use relative path
if (import.meta.env.PROD) {
return ''; // Relative path - same origin
}
// In development, use localhost
return 'http://localhost:7860';
};
const [apiUrl] = useState(getApiUrl());
const handleFileUpload = async (file: File) => {
try {
setJobStatus({
jobId: '',
status: 'uploading',
progress: 0,
message: 'Uploading document...'
});
// Upload file
const formData = new FormData();
formData.append('file', file);
const uploadResponse = await fetch(`${apiUrl}/analyze`, {
method: 'POST',
body: formData
});
if (!uploadResponse.ok) {
throw new Error('Upload failed');
}
const uploadData = await uploadResponse.json();
const jobId = uploadData.job_id;
setJobStatus({
jobId,
status: 'processing',
progress: uploadData.progress || 0,
message: uploadData.message || 'Analysis started...'
});
// Poll for status
pollJobStatus(jobId);
} catch (error) {
console.error('Upload error:', error);
setJobStatus({
jobId: '',
status: 'failed',
progress: 0,
message: error instanceof Error ? error.message : 'Upload failed'
});
}
};
const pollJobStatus = async (jobId: string) => {
try {
const statusResponse = await fetch(`${apiUrl}/status/${jobId}`);
if (!statusResponse.ok) {
throw new Error('Status check failed');
}
const statusData = await statusResponse.json();
setJobStatus({
jobId,
status: statusData.status,
progress: statusData.progress || 0,
message: statusData.message || 'Processing...'
});
if (statusData.status === 'completed') {
// Fetch results
const resultsResponse = await fetch(`${apiUrl}/results/${jobId}`);
if (!resultsResponse.ok) {
throw new Error('Failed to fetch results');
}
const resultsData = await resultsResponse.json();
setAnalysisResult(resultsData);
} else if (statusData.status === 'processing') {
// Continue polling
setTimeout(() => pollJobStatus(jobId), 2000);
} else if (statusData.status === 'failed') {
setJobStatus(prev => ({
...prev,
status: 'failed',
message: 'Analysis failed. Please try again.'
}));
}
} catch (error) {
console.error('Status polling error:', error);
setJobStatus(prev => ({
...prev,
status: 'failed',
message: error instanceof Error ? error.message : 'Status check failed'
}));
}
};
const handleReset = () => {
setJobStatus({
jobId: '',
status: 'idle',
progress: 0,
message: ''
});
setAnalysisResult(null);
};
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-blue-50">
<Header
onShowModelInfo={() => setShowModelInfo(true)}
onReset={handleReset}
hasActiveAnalysis={jobStatus.status !== 'idle'}
/>
<main className="container mx-auto px-4 py-8 max-w-7xl">
{/* Hero Section */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
Medical Report Analysis Platform
</h1>
<p className="text-lg md:text-xl text-gray-600 max-w-3xl mx-auto">
Advanced AI-powered analysis using 50+ specialized medical models across 9 clinical domains
</p>
<div className="mt-4 flex items-center justify-center gap-4 text-sm text-gray-500">
<div className="flex items-center gap-2">
<svg className="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>HIPAA Compliant</span>
</div>
<div className="flex items-center gap-2">
<svg className="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>GDPR Compliant</span>
</div>
<div className="flex items-center gap-2">
<svg className="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>FDA Guidance Aligned</span>
</div>
</div>
</div>
{/* Main Content */}
<div className="space-y-8">
{jobStatus.status === 'idle' && (
<FileUpload onFileUpload={handleFileUpload} />
)}
{(jobStatus.status === 'uploading' || jobStatus.status === 'processing') && (
<AnalysisStatus
status={jobStatus.status}
progress={jobStatus.progress}
message={jobStatus.message}
/>
)}
{jobStatus.status === 'completed' && analysisResult && (
<AnalysisResults
result={analysisResult}
onReset={handleReset}
/>
)}
{jobStatus.status === 'failed' && (
<div className="bg-red-50 border border-red-200 rounded-lg p-6 text-center">
<svg className="w-12 h-12 text-red-500 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<h3 className="text-xl font-semibold text-red-900 mb-2">Analysis Failed</h3>
<p className="text-red-700 mb-4">{jobStatus.message}</p>
<button
onClick={handleReset}
className="px-6 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
>
Try Again
</button>
</div>
)}
</div>
{/* Information Cards */}
{jobStatus.status === 'idle' && (
<div className="grid md:grid-cols-3 gap-6 mt-12">
<div className="bg-white rounded-lg shadow-md p-6">
<div className="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-4">
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2">Multi-Format Support</h3>
<p className="text-gray-600">
Process all types of medical reports: radiology, pathology, lab results, clinical notes, and more
</p>
</div>
<div className="bg-white rounded-lg shadow-md p-6">
<div className="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center mb-4">
<svg className="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2">Specialized AI Models</h3>
<p className="text-gray-600">
Leverages 50+ domain-specific models including MedGemma, MONAI, and specialized clinical AI
</p>
</div>
<div className="bg-white rounded-lg shadow-md p-6">
<div className="w-12 h-12 bg-purple-100 rounded-lg flex items-center justify-center mb-4">
<svg className="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2">Secure & Compliant</h3>
<p className="text-gray-600">
Built with medical-grade security, HIPAA compliance, and regulatory alignment (FDA, GDPR)
</p>
</div>
</div>
)}
</main>
{/* Model Info Modal */}
{showModelInfo && (
<ModelInfo onClose={() => setShowModelInfo(false)} />
)}
{/* Footer */}
<footer className="mt-16 py-8 border-t border-gray-200">
<div className="container mx-auto px-4 text-center text-gray-600">
<p className="text-sm">
Medical Report Analysis Platform • AI-Powered Clinical Intelligence
</p>
<p className="text-xs mt-2 text-gray-500">
This platform provides AI-assisted analysis. All results should be reviewed by qualified healthcare professionals.
</p>
</div>
</footer>
</div>
);
}
export default App;