Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Medical Report Analyzer</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); | |
body { font-family: 'Inter', sans-serif; } | |
.fade-in { animation: fadeIn 0.5s ease-in-out; } | |
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } | |
.spinner { border: 4px solid rgba(0,0,0,0.1); width: 24px; height: 24px; border-radius: 50%; border-left-color: #fff; animation: spin 1s ease infinite; } | |
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } | |
/* Styles for the rendered markdown */ | |
.prose h3 { font-size: 1.25rem; font-weight: 600; margin-top: 1.5rem; margin-bottom: 0.5rem; } | |
.prose ul { list-style-type: disc; margin-left: 1.5rem; } | |
.prose li { margin-bottom: 0.5rem; } | |
.prose p { margin-bottom: 1rem; } | |
.prose strong { font-weight: 600; } | |
</style> | |
</head> | |
<body class="bg-gray-100 text-gray-800"> | |
<!-- Header --> | |
<header class="bg-white shadow-sm"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
<div class="flex justify-between items-center py-4"> | |
<h1 class="text-xl font-bold text-gray-800">Medical Report Analyzer</h1> | |
<!-- Public app: no auth controls --> | |
</div> | |
</div> | |
</header> | |
<!-- Main Content Grid --> | |
<main class="max-w-7xl mx-auto p-4 sm:p-6 lg:p-8"> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-8"> | |
<!-- Input Section --> | |
<div class="bg-white rounded-2xl shadow-lg p-6 md:p-8"> | |
<h2 class="text-2xl font-bold text-gray-900 mb-4">Enter Your Medical Report</h2> | |
<form id="report-form"> | |
<!-- Input Type Selector --> | |
<div class="mb-4"> | |
<label class="block text-sm font-medium text-gray-700 mb-2">Select Input Type:</label> | |
<div class="flex items-center space-x-4"> | |
<label class="flex items-center"> | |
<input type="radio" name="input_type" value="text" class="form-radio" checked> | |
<span class="ml-2">Text</span> | |
</label> | |
<label class="flex items-center"> | |
<input type="radio" name="input_type" value="file" class="form-radio"> | |
<span class="ml-2">PDF / Image</span> | |
</label> | |
</div> | |
</div> | |
<!-- Text Input --> | |
<div id="text-input-container"> | |
<textarea id="report-text" name="report_text" rows="18" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500" placeholder="Paste the text from your medical report here..."></textarea> | |
</div> | |
<!-- File Input --> | |
<div id="file-input-container" class="hidden"> | |
<input type="file" id="report-file" name="report_file" accept=".pdf, image/png, image/jpeg" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:bg-gray-50 file:text-gray-700 hover:file:bg-gray-100"> | |
</div> | |
<button type="submit" id="analyze-report-btn" class="mt-4 w-full flex items-center justify-center bg-blue-600 text-white font-bold py-3 px-8 rounded-lg hover:bg-blue-700"> | |
<span id="btn-text">Simplify Report</span> | |
<div id="btn-spinner" class="spinner hidden ml-3"></div> | |
</button> | |
</form> | |
</div> | |
<!-- Output Section --> | |
<div class="bg-white rounded-2xl shadow-lg p-6 md:p-8"> | |
<h2 class="text-2xl font-bold text-gray-900 mb-4">Simplified Explanation</h2> | |
<div id="result-container" class="prose max-w-none bg-gray-50 p-4 rounded-lg min-h-[400px]"> | |
<p class="text-gray-500">Your easy-to-understand summary will appear here after you submit a report.</p> | |
</div> | |
</div> | |
</div> | |
</main> | |
<script> | |
const reportForm = document.getElementById('report-form'); | |
const reportText = document.getElementById('report-text'); | |
const reportFile = document.getElementById('report-file'); | |
const resultContainer = document.getElementById('result-container'); | |
const analyzeBtn = document.getElementById('analyze-report-btn'); | |
const btnText = document.getElementById('btn-text'); | |
const btnSpinner = document.getElementById('btn-spinner'); | |
const textInputContainer = document.getElementById('text-input-container'); | |
const fileInputContainer = document.getElementById('file-input-container'); | |
const inputTypeRadios = document.querySelectorAll('input[name="input_type"]'); | |
// Event listener for radio buttons | |
inputTypeRadios.forEach(radio => { | |
radio.addEventListener('change', (event) => { | |
if (event.target.value === 'text') { | |
textInputContainer.classList.remove('hidden'); | |
fileInputContainer.classList.add('hidden'); | |
} else { | |
textInputContainer.classList.add('hidden'); | |
fileInputContainer.classList.remove('hidden'); | |
} | |
}); | |
}); | |
reportForm.addEventListener('submit', async (event) => { | |
event.preventDefault(); | |
const selectedType = document.querySelector('input[name="input_type"]:checked').value; | |
let endpoint = ''; | |
let body = null; | |
let headers = {}; | |
if (selectedType === 'text') { | |
const text = reportText.value.trim(); | |
if (!text) { | |
resultContainer.innerHTML = `<p class="text-red-600">Please enter some text from your report.</p>`; | |
return; | |
} | |
endpoint = '/analyze_report_text'; | |
headers = { 'Content-Type': 'application/json' }; | |
body = JSON.stringify({ report_text: text }); | |
} else { // File upload | |
if (reportFile.files.length === 0) { | |
resultContainer.innerHTML = `<p class="text-red-600">Please select a PDF or image file.</p>`; | |
return; | |
} | |
endpoint = '/analyze_report_file'; | |
const formData = new FormData(); | |
formData.append('report_file', reportFile.files[0]); | |
body = formData; | |
// Note: For FormData, the browser sets the Content-Type header automatically. | |
} | |
// Show loading state | |
btnText.textContent = 'Analyzing...'; | |
btnSpinner.classList.remove('hidden'); | |
analyzeBtn.disabled = true; | |
resultContainer.innerHTML = `<p class="text-gray-500">Generating your simplified summary. This may take a moment...</p>`; | |
try { | |
const response = await fetch(endpoint, { method: 'POST', headers: headers, body: body }); | |
const data = await response.json(); | |
if (!response.ok) { | |
throw new Error(data.error || 'An unknown error occurred.'); | |
} | |
resultContainer.innerHTML = data.simplified_report; | |
} catch (error) { | |
console.error("Analysis Error:", error); | |
resultContainer.innerHTML = `<p class="text-red-600"><strong>Error:</strong> ${error.message}</p>`; | |
} finally { | |
// Restore button state | |
btnText.textContent = 'Simplify Report'; | |
btnSpinner.classList.add('hidden'); | |
analyzeBtn.disabled = false; | |
} | |
}); | |
</script> | |
</body> | |
</html> | |