// ===== GLOBAL STATE ===== let currentResults = null; let explanations = null; let sessionId = null; // ===== SECTION NAVIGATION (CRITICAL - MISSING IN YOUR CODE) ===== function showSection(sectionId) { console.log('🔄 Showing section:', sectionId); // Hide all sections document.querySelectorAll('.section').forEach(section => { section.classList.remove('active'); }); // Show target section const targetSection = document.getElementById(sectionId); if (targetSection) { targetSection.classList.add('active'); targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); } else { console.error('❌ Section not found:', sectionId); } } // ===== INITIALIZATION ===== document.addEventListener('DOMContentLoaded', () => { console.log('✅ DOM loaded, initializing...'); initFileUpload(); // Show upload section by default showSection('upload-section'); }); // ===== FILE UPLOAD ===== function initFileUpload() { const fileInput = document.getElementById('fileInput'); const uploadArea = document.getElementById('uploadArea'); if (!fileInput || !uploadArea) { console.error('❌ Upload elements not found'); return; } console.log('✅ Upload initialized'); // Click to upload fileInput.addEventListener('change', handleFileSelect); // Drag and drop uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('border-secondary', 'bg-blue-100'); }); uploadArea.addEventListener('dragleave', () => { uploadArea.classList.remove('border-secondary', 'bg-blue-100'); }); uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.classList.remove('border-secondary', 'bg-blue-100'); const file = e.dataTransfer.files[0]; if (file && file.type === 'application/pdf') { uploadFile(file); } else { showToast('Please upload a PDF file', 'error'); } }); } function handleFileSelect(e) { const file = e.target.files[0]; if (file) { uploadFile(file); } } async function uploadFile(file) { const formData = new FormData(); formData.append('file', file); // Show progress document.getElementById('uploadArea').classList.add('hidden'); document.getElementById('uploadProgress').classList.remove('hidden'); try { console.log('📤 Uploading file:', file.name); const response = await fetch('/api/upload', { method: 'POST', body: formData }); const data = await response.json(); console.log('📥 Upload response:', data); if (!response.ok) { throw new Error(data.error || 'Upload failed'); } // Store session ID and results sessionId = data.session_id; currentResults = data.results; console.log('✅ Session ID:', sessionId); console.log('✅ Results:', currentResults.length); showToast(`✓ Found ${data.count} lab results!`, 'success'); // Display results immediately with template explanations console.log('🎨 Displaying template results...'); displayResults(); updateSummaryStats(); // Show stats and buttons document.getElementById('summaryStats').classList.remove('hidden'); document.getElementById('generateSummaryBtn').classList.remove('hidden'); // Switch to results section setTimeout(() => { showSection('results-section'); }, 500); // Fetch AI explanations in background console.log('🤖 Fetching AI explanations...'); showToast('Generating AI explanations...', 'info'); await generateExplanations(); // Update display with AI explanations console.log('🔄 Updating with AI explanations...'); displayResults(); showToast('✓ AI explanations loaded!', 'success'); } catch (error) { console.error('❌ Upload error:', error); showToast(error.message, 'error'); resetUploadArea(); } } async function generateExplanations() { try { console.log('🔄 Calling /api/explain with session:', sessionId); const response = await fetch('/api/explain', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: sessionId }) }); const data = await response.json(); console.log('📥 Explanation response:', data); if (!response.ok) { throw new Error(data.error || 'Failed to generate explanations'); } explanations = data.explanations; console.log('✅ Explanations loaded:', Object.keys(explanations).length); } catch (error) { console.error('❌ Explanation error:', error); showToast('Using basic explanations', 'info'); } } function displayResults() { const container = document.getElementById('resultsContainer'); console.log('🎯 displayResults() called'); console.log('📊 Results:', currentResults?.length); console.log('💬 Explanations:', explanations ? Object.keys(explanations).length : 0); if (!currentResults || currentResults.length === 0) { console.log('❌ No results to display'); container.innerHTML = '
📊

Upload a lab report to view results here

'; return; } console.log(`✅ Displaying ${currentResults.length} results`); // Clear container container.innerHTML = ''; // Create result cards currentResults.forEach((result, index) => { console.log(`📝 Card ${index + 1}:`, result.test_name, result.status); const card = createResultCard(result, index); container.appendChild(card); }); // Reset upload area resetUploadArea(); console.log('✅ All cards rendered'); } function createResultCard(result, index) { const card = document.createElement('div'); card.className = 'bg-white rounded-xl shadow-md border border-slate-200 p-6 mb-4 hover:shadow-lg transition-shadow'; // Status colors const statusColors = { normal: { bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200' }, high: { bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200' }, low: { bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' }, unknown: { bg: 'bg-slate-50', text: 'text-slate-700', border: 'border-slate-200' } }; const colors = statusColors[result.status] || statusColors.unknown; // Get explanation let explanation = ''; if (explanations && explanations[result.test_name]) { explanation = explanations[result.test_name]; console.log(` ✅ Using AI explanation for ${result.test_name}`); } else { console.log(` ⚠️ Using template for ${result.test_name}`); // Template explanation if (result.status === 'normal') { explanation = `✅ Good news! Your ${result.test_name} level of ${result.value} ${result.unit} is within the normal range (${result.reference_range}).

This indicates healthy levels. Keep up your current health habits!`; } else if (result.status === 'high') { explanation = `⚠️ Your ${result.test_name} level of ${result.value} ${result.unit} is above the normal range (${result.reference_range}).

This may require attention. Please consult your healthcare provider.`; } else if (result.status === 'low') { explanation = `⚠️ Your ${result.test_name} level of ${result.value} ${result.unit} is below the normal range (${result.reference_range}).

This may require attention. Please consult your healthcare provider.`; } else { explanation = `Your ${result.test_name} result is ${result.value} ${result.unit}.

Reference range: ${result.reference_range}`; } } card.innerHTML = `

${escapeHtml(result.test_name)}

Reference: ${escapeHtml(result.reference_range || 'N/A')}

${escapeHtml(result.value)} ${escapeHtml(result.unit)}
${result.status.toUpperCase()}
💡 What does this mean?
${explanation}
`; return card; } function updateSummaryStats() { const stats = { normal: 0, high: 0, low: 0 }; currentResults.forEach(result => { if (result.status in stats) { stats[result.status]++; } }); document.getElementById('normalCount').textContent = stats.normal; document.getElementById('highCount').textContent = stats.high; document.getElementById('lowCount').textContent = stats.low; console.log('📊 Stats updated:', stats); } function resetUploadArea() { document.getElementById('uploadArea').classList.remove('hidden'); document.getElementById('uploadProgress').classList.add('hidden'); document.getElementById('fileInput').value = ''; } // ===== CHAT FUNCTIONALITY ===== async function askQuestion() { const input = document.getElementById('chatInput'); const question = input.value.trim(); if (!question) return; if (!sessionId) { showToast('Please upload a lab report first', 'error'); return; } console.log('💬 Asking:', question); // Clear input input.value = ''; // Add user message addChatMessage(question, 'user'); // Add loading message const loadingId = addChatMessage('Thinking...', 'assistant', true); try { const response = await fetch('/api/ask', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ question: question, session_id: sessionId }) }); const data = await response.json(); console.log('💬 Answer:', data); if (!response.ok) { throw new Error(data.error || 'Failed to get answer'); } // Remove loading message document.getElementById(loadingId).remove(); // Add assistant response addChatMessage(data.answer, 'assistant'); } catch (error) { console.error('❌ Chat error:', error); document.getElementById(loadingId).remove(); addChatMessage(`Sorry, I encountered an error: ${error.message}`, 'assistant'); showToast(error.message, 'error'); } } function addChatMessage(text, sender, isLoading = false) { const messagesContainer = document.getElementById('chatMessages'); // Remove placeholder text if exists const placeholder = messagesContainer.querySelector('.text-slate-400'); if (placeholder && placeholder.closest('.text-center')) { placeholder.closest('.text-center').remove(); } const messageId = `msg-${Date.now()}-${Math.random()}`; const messageDiv = document.createElement('div'); messageDiv.id = messageId; messageDiv.className = `mb-4 ${sender === 'user' ? 'text-right' : 'text-left'}`; const bubbleClass = sender === 'user' ? 'inline-block bg-secondary text-white px-4 py-2 rounded-2xl rounded-tr-sm max-w-[80%]' : 'inline-block bg-slate-100 text-slate-800 px-4 py-2 rounded-2xl rounded-tl-sm max-w-[80%]'; messageDiv.innerHTML = `
${escapeHtml(text)}
`; messagesContainer.appendChild(messageDiv); messagesContainer.scrollTop = messagesContainer.scrollHeight; return messageId; } // ===== SUMMARY GENERATION ===== async function generateSummary() { if (!sessionId) { showToast('Please upload a lab report first', 'error'); return; } console.log('📊 Generating summary...'); const summaryContent = document.getElementById('summaryContent'); summaryContent.innerHTML = '

Generating summary...

'; try { const response = await fetch('/api/summary', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: sessionId }) }); const data = await response.json(); console.log('📊 Summary:', data); if (!response.ok) { throw new Error(data.error || 'Failed to generate summary'); } // Display summary summaryContent.innerHTML = `
${escapeHtml(data.summary)}
`; showToast('✓ Summary generated!', 'success'); } catch (error) { console.error('❌ Summary error:', error); summaryContent.innerHTML = '

Error generating summary. Please try again.

'; showToast('Error: ' + error.message, 'error'); } } // ===== TOAST NOTIFICATIONS ===== function showToast(message, type = 'info') { const toast = document.getElementById('toast'); const colors = { success: 'bg-green-600', error: 'bg-red-600', info: 'bg-blue-600' }; toast.textContent = message; toast.className = `fixed bottom-5 right-5 ${colors[type] || colors.info} text-white px-6 py-3 rounded-lg shadow-2xl z-50 transform transition-all duration-300`; toast.style.transform = 'translateY(0)'; toast.style.opacity = '1'; setTimeout(() => { toast.style.transform = 'translateY(100px)'; toast.style.opacity = '0'; }, 4000); } // ===== UTILITY FUNCTIONS ===== function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }