Spaces:
Sleeping
Sleeping
| <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> | |