// ===== GLOBAL STATE ===== let currentResults = null; let explanations = null; let sessionId = null; // Store session ID // ===== INITIALIZATION ===== document.addEventListener('DOMContentLoaded', () => { initNavigation(); initFileUpload(); initScrollAnimations(); }); // ===== NAVIGATION ===== function initNavigation() { const navLinks = document.querySelectorAll('.nav-link'); navLinks.forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const target = link.getAttribute('href'); // Update active state navLinks.forEach(l => l.classList.remove('active')); link.classList.add('active'); // Smooth scroll document.querySelector(target).scrollIntoView({ behavior: 'smooth', block: 'start' }); }); }); // Mobile menu toggle const mobileToggle = document.querySelector('.mobile-menu-toggle'); const navMenu = document.querySelector('.nav-menu'); if (mobileToggle) { mobileToggle.addEventListener('click', () => { navMenu.classList.toggle('active'); }); } } // ===== SCROLL ANIMATIONS ===== function initScrollAnimations() { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, { threshold: 0.1 }); document.querySelectorAll('.about-card, .feature-card').forEach(card => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; card.style.transition = 'all 0.6s ease-out'; observer.observe(card); }); } // ===== FILE UPLOAD ===== function initFileUpload() { const fileInput = document.getElementById('fileInput'); const uploadArea = document.getElementById('uploadArea'); if (!fileInput || !uploadArea) return; // Click to upload fileInput.addEventListener('change', handleFileSelect); // Drag and drop uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('dragover'); }); uploadArea.addEventListener('dragleave', () => { uploadArea.classList.remove('dragover'); }); uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.classList.remove('dragover'); 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 { const response = await fetch('/api/upload', { method: 'POST', body: formData }); const data = await response.json(); 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); showToast(`✓ Found ${data.count} lab results!`, 'success'); // Generate explanations await generateExplanations(); // Display results displayResults(); // Scroll to results setTimeout(() => { document.getElementById('results').scrollIntoView({ behavior: 'smooth' }); }, 500); } catch (error) { showToast(error.message, 'error'); resetUploadArea(); } } async function generateExplanations() { showLoading('Generating AI explanations... This may take 30-60 seconds.'); try { const response = await fetch('/api/explain', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: sessionId }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Failed to generate explanations'); } explanations = data.explanations; console.log('Explanations:', explanations); } catch (error) { showToast('Error generating explanations: ' + error.message, 'error'); // Continue anyway to show results } finally { hideLoading(); } } function displayResults() { const resultsSection = document.getElementById('results'); const container = document.getElementById('resultsContainer'); if (!currentResults || currentResults.length === 0) { container.innerHTML = '

No results found

'; return; } // Show results section resultsSection.classList.remove('hidden'); // Update summary stats updateSummaryStats(); // Clear container container.innerHTML = ''; // Create result cards currentResults.forEach((result, index) => { const card = createResultCard(result, index); container.appendChild(card); }); // Reset upload area resetUploadArea(); } 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; } function createResultCard(result, index) { const card = document.createElement('div'); card.className = 'result-card'; card.style.setProperty('--i', index + 1); const statusClass = `status-${result.status}`; const explanation = explanations && explanations[result.test_name] ? explanations[result.test_name] : 'Explanation is being generated. Click "Ask Questions" to learn more about this result.'; card.innerHTML = `

${escapeHtml(result.test_name)}

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

${escapeHtml(result.value)} ${escapeHtml(result.unit)}
${result.status}
💡 What does this mean?

${escapeHtml(explanation)}

`; return card; } function resetUploadArea() { document.getElementById('uploadArea').classList.remove('hidden'); document.getElementById('uploadProgress').classList.add('hidden'); document.getElementById('fileInput').value = ''; } // ===== SUMMARY GENERATION ===== async function generateSummary() { if (!sessionId) { showToast('Please upload a lab report first', 'error'); return; } showLoading('Generating comprehensive 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(); if (!response.ok) { throw new Error(data.error || 'Failed to generate summary'); } // Show summary modal showSummaryModal(data.summary, data.stats); } catch (error) { showToast('Error: ' + error.message, 'error'); } finally { hideLoading(); } } function showSummaryModal(summary, stats) { const modal = document.createElement('div'); modal.className = 'chat-modal'; modal.innerHTML = `

📊 Complete Summary

${escapeHtml(summary).replace(/\n/g, '

')}

`; document.body.appendChild(modal); // Close on click outside modal.addEventListener('click', (e) => { if (e.target === modal) { modal.remove(); } }); } // ===== CHAT FUNCTIONALITY ===== function openChat() { if (!sessionId) { showToast('Please upload a lab report first', 'error'); return; } const modal = document.getElementById('chatModal'); modal.classList.remove('hidden'); document.getElementById('chatInput').focus(); } function closeChat() { document.getElementById('chatModal').classList.add('hidden'); } function handleChatKeypress(event) { if (event.key === 'Enter') { sendChatMessage(); } } async function sendChatMessage() { const input = document.getElementById('chatInput'); const question = input.value.trim(); if (!question) return; if (!sessionId) { showToast('Session expired. Please upload your report again.', 'error'); return; } // Clear input input.value = ''; // Add user message addChatMessage(question, 'user'); // Show 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(); 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) { 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 welcome message if exists const welcome = messagesContainer.querySelector('.chat-welcome'); if (welcome) { welcome.remove(); } const messageId = `msg-${Date.now()}`; const messageDiv = document.createElement('div'); messageDiv.id = messageId; messageDiv.className = `chat-message ${sender}`; const bubbleClass = isLoading ? 'chat-bubble loading' : 'chat-bubble'; messageDiv.innerHTML = `
${escapeHtml(text)}
`; messagesContainer.appendChild(messageDiv); messagesContainer.scrollTop = messagesContainer.scrollHeight; return messageId; } // ===== LOADING OVERLAY ===== function showLoading(message = 'Processing...') { const overlay = document.getElementById('loadingOverlay'); if (overlay) { overlay.querySelector('p').textContent = message; overlay.classList.remove('hidden'); } } function hideLoading() { const overlay = document.getElementById('loadingOverlay'); if (overlay) { overlay.classList.add('hidden'); } } // ===== TOAST NOTIFICATIONS ===== function showToast(message, type = 'info') { const toast = document.getElementById('toast'); toast.textContent = message; toast.className = `toast ${type} show`; setTimeout(() => { toast.classList.remove('show'); }, 4000); } // ===== UTILITY FUNCTIONS ===== function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // ===== SMOOTH SCROLL FOR ALL LINKS ===== document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // ===== CLOSE MODALS ON ESC ===== document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { const chatModal = document.getElementById('chatModal'); if (chatModal && !chatModal.classList.contains('hidden')) { closeChat(); } } }); // ===== CLOSE MODALS ON OUTSIDE CLICK ===== document.addEventListener('click', (e) => { const chatModal = document.getElementById('chatModal'); if (e.target === chatModal) { closeChat(); } });