Hanan-Alnakhal commited on
Commit
182ca99
Β·
verified Β·
1 Parent(s): d220f1d

Update static/script.js

Browse files
Files changed (1) hide show
  1. static/script.js +165 -307
static/script.js CHANGED
@@ -1,48 +1,12 @@
1
  // ===== GLOBAL STATE =====
2
  let currentResults = null;
3
  let explanations = null;
4
- let sessionId = null; // Store session ID
5
 
6
- // ===== INITIALIZATION =====
7
- document.addEventListener('DOMContentLoaded', () => {
8
- initNavigation();
9
- initFileUpload();
10
- initScrollAnimations();
11
- });
12
-
13
- // ===== NAVIGATION =====
14
- function initNavigation() {
15
- const navLinks = document.querySelectorAll('.nav-link');
16
-
17
- navLinks.forEach(link => {
18
- link.addEventListener('click', (e) => {
19
- e.preventDefault();
20
- const target = link.getAttribute('href');
21
-
22
- // Update active state
23
- navLinks.forEach(l => l.classList.remove('active'));
24
- link.classList.add('active');
25
-
26
- // Smooth scroll
27
- document.querySelector(target).scrollIntoView({
28
- behavior: 'smooth',
29
- block: 'start'
30
- });
31
- });
32
- });
33
-
34
- // Mobile menu toggle
35
- const mobileToggle = document.querySelector('.mobile-menu-toggle');
36
- const navMenu = document.querySelector('.nav-menu');
37
-
38
- if (mobileToggle) {
39
- mobileToggle.addEventListener('click', () => {
40
- navMenu.classList.toggle('active');
41
- });
42
- }
43
- }
44
- // Add this function for section navigation
45
  function showSection(sectionId) {
 
 
46
  // Hide all sections
47
  document.querySelectorAll('.section').forEach(section => {
48
  section.classList.remove('active');
@@ -52,38 +16,32 @@ function showSection(sectionId) {
52
  const targetSection = document.getElementById(sectionId);
53
  if (targetSection) {
54
  targetSection.classList.add('active');
 
 
 
55
  }
56
  }
57
 
58
- // Add this function to match your HTML button
59
- function askQuestion() {
60
- sendChatMessage(); // Just call your existing function
61
- }
62
- // ===== SCROLL ANIMATIONS =====
63
- function initScrollAnimations() {
64
- const observer = new IntersectionObserver((entries) => {
65
- entries.forEach(entry => {
66
- if (entry.isIntersecting) {
67
- entry.target.style.opacity = '1';
68
- entry.target.style.transform = 'translateY(0)';
69
- }
70
- });
71
- }, { threshold: 0.1 });
72
 
73
- document.querySelectorAll('.about-card, .feature-card').forEach(card => {
74
- card.style.opacity = '0';
75
- card.style.transform = 'translateY(20px)';
76
- card.style.transition = 'all 0.6s ease-out';
77
- observer.observe(card);
78
- });
79
- }
80
 
81
  // ===== FILE UPLOAD =====
82
  function initFileUpload() {
83
  const fileInput = document.getElementById('fileInput');
84
  const uploadArea = document.getElementById('uploadArea');
85
 
86
- if (!fileInput || !uploadArea) return;
 
 
 
 
 
87
 
88
  // Click to upload
89
  fileInput.addEventListener('change', handleFileSelect);
@@ -91,16 +49,16 @@ function initFileUpload() {
91
  // Drag and drop
92
  uploadArea.addEventListener('dragover', (e) => {
93
  e.preventDefault();
94
- uploadArea.classList.add('dragover');
95
  });
96
 
97
  uploadArea.addEventListener('dragleave', () => {
98
- uploadArea.classList.remove('dragover');
99
  });
100
 
101
  uploadArea.addEventListener('drop', (e) => {
102
  e.preventDefault();
103
- uploadArea.classList.remove('dragover');
104
 
105
  const file = e.dataTransfer.files[0];
106
  if (file && file.type === 'application/pdf') {
@@ -117,6 +75,7 @@ function handleFileSelect(e) {
117
  uploadFile(file);
118
  }
119
  }
 
120
  async function uploadFile(file) {
121
  const formData = new FormData();
122
  formData.append('file', file);
@@ -126,7 +85,7 @@ async function uploadFile(file) {
126
  document.getElementById('uploadProgress').classList.remove('hidden');
127
 
128
  try {
129
- console.log('πŸ“€ Uploading file...');
130
 
131
  const response = await fetch('/api/upload', {
132
  method: 'POST',
@@ -145,25 +104,28 @@ async function uploadFile(file) {
145
  currentResults = data.results;
146
 
147
  console.log('βœ… Session ID:', sessionId);
148
- console.log('βœ… Results count:', currentResults.length);
149
 
150
  showToast(`βœ“ Found ${data.count} lab results!`, 'success');
151
 
152
- // IMPORTANT: Show results immediately with template explanations
153
- console.log('🎨 Displaying results with template explanations...');
154
  displayResults();
 
 
 
 
 
155
 
156
- // Scroll to results
157
  setTimeout(() => {
158
- const resultsEl = document.getElementById('results');
159
- if (resultsEl) {
160
- resultsEl.scrollIntoView({ behavior: 'smooth' });
161
- console.log('πŸ“ Scrolled to results section');
162
- }
163
  }, 500);
164
 
165
- // Then fetch AI explanations in background
166
  console.log('πŸ€– Fetching AI explanations...');
 
 
167
  await generateExplanations();
168
 
169
  // Update display with AI explanations
@@ -179,281 +141,168 @@ async function uploadFile(file) {
179
  }
180
  }
181
 
182
-
183
  async function generateExplanations() {
184
- showLoading('Generating AI explanations... (10-20 seconds)');
185
-
186
  try {
187
- // Add timeout to prevent hanging
188
- const controller = new AbortController();
189
- const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 second timeout
190
 
191
  const response = await fetch('/api/explain', {
192
  method: 'POST',
193
- headers: {
194
- 'Content-Type': 'application/json'
195
- },
196
- body: JSON.stringify({ session_id: sessionId }),
197
- signal: controller.signal
198
  });
199
 
200
- clearTimeout(timeoutId);
201
-
202
  const data = await response.json();
 
203
 
204
  if (!response.ok) {
205
  throw new Error(data.error || 'Failed to generate explanations');
206
  }
207
 
208
  explanations = data.explanations;
209
- console.log('Explanations generated:', Object.keys(explanations).length);
210
 
211
  } catch (error) {
212
- if (error.name === 'AbortError') {
213
- console.log('Explanation generation timed out - showing basic results');
214
- showToast('Loading took longer than expected. Showing basic results.', 'info');
215
- } else {
216
- console.error('Error generating explanations:', error);
217
- showToast('Some explanations may be unavailable', 'info');
218
- }
219
- // Continue anyway to show results
220
- } finally {
221
- hideLoading();
222
  }
223
  }
 
224
  function displayResults() {
225
- const resultsSection = document.getElementById('results-section');
226
  const container = document.getElementById('resultsContainer');
227
 
228
  console.log('🎯 displayResults() called');
229
- console.log('πŸ“Š currentResults:', currentResults);
230
- console.log('πŸ’¬ explanations:', explanations);
231
 
232
  if (!currentResults || currentResults.length === 0) {
233
  console.log('❌ No results to display');
234
- container.innerHTML = '<p style="text-align:center;color:#94a3b8;padding:3rem;">No results found</p>';
235
  return;
236
  }
237
 
238
  console.log(`βœ… Displaying ${currentResults.length} results`);
239
 
240
- // Show results section
241
- resultsSection.classList.remove('hidden');
242
-
243
- // Update summary stats
244
- updateSummaryStats();
245
-
246
  // Clear container
247
  container.innerHTML = '';
248
 
249
  // Create result cards
250
  currentResults.forEach((result, index) => {
251
- console.log(`πŸ“ Creating card ${index + 1}: ${result.test_name} (${result.status})`);
252
- try {
253
- const card = createResultCard(result, index);
254
- container.appendChild(card);
255
- console.log(` βœ… Card added to DOM`);
256
- } catch (error) {
257
- console.error(` ❌ Error creating card:`, error);
258
- }
259
  });
260
 
261
  // Reset upload area
262
  resetUploadArea();
263
 
264
- console.log('βœ… All results displayed successfully');
265
- }
266
-
267
- function updateSummaryStats() {
268
- const stats = {
269
- normal: 0,
270
- high: 0,
271
- low: 0
272
- };
273
-
274
- currentResults.forEach(result => {
275
- if (result.status in stats) {
276
- stats[result.status]++;
277
- }
278
- });
279
-
280
- document.getElementById('normalCount').textContent = stats.normal;
281
- document.getElementById('highCount').textContent = stats.high;
282
- document.getElementById('lowCount').textContent = stats.low;
283
  }
284
 
285
  function createResultCard(result, index) {
286
- console.log(` Creating card for: ${result.test_name}`);
287
-
288
  const card = document.createElement('div');
289
- card.className = 'result-card';
290
- card.style.setProperty('--i', index + 1);
 
 
 
 
 
 
 
291
 
292
- // Determine status class and color
293
- const statusClass = `status-${result.status || 'unknown'}`;
294
- let statusColor = '#64748b'; // gray for unknown
295
- if (result.status === 'normal') statusColor = '#10b981';
296
- if (result.status === 'high') statusColor = '#ef4444';
297
- if (result.status === 'low') statusColor = '#f59e0b';
298
 
299
  // Get explanation
300
  let explanation = '';
301
 
302
  if (explanations && explanations[result.test_name]) {
303
  explanation = explanations[result.test_name];
304
- console.log(` βœ… Using AI explanation (${explanation.length} chars)`);
305
  } else {
306
- console.log(` ⚠️ No AI explanation yet, using template`);
307
- // Template explanation based on status
308
  if (result.status === 'normal') {
309
  explanation = `βœ… <strong>Good news!</strong> Your ${result.test_name} level of ${result.value} ${result.unit} is within the normal range (${result.reference_range}).<br><br>This indicates healthy levels. Keep up your current health habits!`;
310
  } else if (result.status === 'high') {
311
- explanation = `⚠️ Your ${result.test_name} level of ${result.value} ${result.unit} is <strong>above</strong> the normal range (${result.reference_range}).<br><br>This may require attention. Please consult your healthcare provider to discuss these results and any necessary steps.`;
312
  } else if (result.status === 'low') {
313
- explanation = `⚠️ Your ${result.test_name} level of ${result.value} ${result.unit} is <strong>below</strong> the normal range (${result.reference_range}).<br><br>This may require attention. Please consult your healthcare provider to discuss these results and any necessary steps.`;
314
  } else {
315
- explanation = `Your ${result.test_name} result is ${result.value} ${result.unit}.<br><br>Reference range: ${result.reference_range}<br><br>Please discuss this result with your healthcare provider for proper interpretation.`;
316
  }
317
  }
318
 
319
- // Build card HTML
320
  card.innerHTML = `
321
- <div class="result-header">
322
  <div>
323
- <h3 class="result-name">${escapeHtml(result.test_name)}</h3>
324
- <p class="result-range">Reference: ${escapeHtml(result.reference_range || 'N/A')}</p>
325
  </div>
326
- <div class="result-value-container">
327
- <div class="result-value">${escapeHtml(result.value)} ${escapeHtml(result.unit)}</div>
328
- <span class="result-status ${statusClass}" style="background: ${statusColor}20; color: ${statusColor}; border: 2px solid ${statusColor};">${result.status || 'unknown'}</span>
 
 
329
  </div>
330
  </div>
331
- <div class="result-explanation">
332
- <strong>πŸ’‘ What does this mean?</strong><br><br>
333
- <div style="line-height: 1.8;">${explanation}</div>
334
  </div>
335
  `;
336
 
337
- console.log(` βœ… Card HTML created`);
338
  return card;
339
  }
340
 
341
- function resetUploadArea() {
342
- document.getElementById('uploadArea').classList.remove('hidden');
343
- document.getElementById('uploadProgress').classList.add('hidden');
344
- document.getElementById('fileInput').value = '';
345
- }
346
-
347
- // ===== SUMMARY GENERATION =====
348
- async function generateSummary() {
349
- if (!sessionId) {
350
- showToast('Please upload a lab report first', 'error');
351
- return;
352
- }
353
-
354
- showLoading('Generating comprehensive summary...');
355
-
356
- try {
357
- const response = await fetch('/api/summary', {
358
- method: 'POST',
359
- headers: {
360
- 'Content-Type': 'application/json'
361
- },
362
- body: JSON.stringify({ session_id: sessionId })
363
- });
364
-
365
- const data = await response.json();
366
-
367
- if (!response.ok) {
368
- throw new Error(data.error || 'Failed to generate summary');
369
- }
370
-
371
- // Show summary modal
372
- showSummaryModal(data.summary, data.stats);
373
-
374
- } catch (error) {
375
- showToast('Error: ' + error.message, 'error');
376
- } finally {
377
- hideLoading();
378
- }
379
- }
380
-
381
- function showSummaryModal(summary, stats) {
382
- const modal = document.createElement('div');
383
- modal.className = 'chat-modal';
384
- modal.innerHTML = `
385
- <div class="chat-modal-content">
386
- <div class="chat-header">
387
- <h3>πŸ“Š Complete Summary</h3>
388
- <button class="chat-close" onclick="this.closest('.chat-modal').remove()">&times;</button>
389
- </div>
390
- <div class="chat-messages">
391
- <div class="chat-message assistant">
392
- <div class="chat-bubble">
393
- <p>${escapeHtml(summary).replace(/\n/g, '<br><br>')}</p>
394
- </div>
395
- </div>
396
- </div>
397
- </div>
398
- `;
399
- document.body.appendChild(modal);
400
 
401
- // Close on click outside
402
- modal.addEventListener('click', (e) => {
403
- if (e.target === modal) {
404
- modal.remove();
405
  }
406
  });
407
- }
408
-
409
- // ===== CHAT FUNCTIONALITY =====
410
- function openChat() {
411
- if (!sessionId) {
412
- showToast('Please upload a lab report first', 'error');
413
- return;
414
- }
415
 
416
- const modal = document.getElementById('chatModal');
417
- modal.classList.remove('hidden');
418
- document.getElementById('chatInput').focus();
419
- }
420
-
421
- function closeChat() {
422
- document.getElementById('chatModal').classList.add('hidden');
423
  }
424
 
425
- function handleChatKeypress(event) {
426
- if (event.key === 'Enter') {
427
- sendChatMessage();
428
- }
429
  }
430
 
431
- async function sendChatMessage() {
 
432
  const input = document.getElementById('chatInput');
433
  const question = input.value.trim();
434
 
435
  if (!question) return;
436
 
437
  if (!sessionId) {
438
- showToast('Session expired. Please upload your report again.', 'error');
439
  return;
440
  }
441
 
 
 
442
  // Clear input
443
  input.value = '';
444
 
445
  // Add user message
446
  addChatMessage(question, 'user');
447
 
448
- // Show loading message
449
  const loadingId = addChatMessage('Thinking...', 'assistant', true);
450
 
451
  try {
452
  const response = await fetch('/api/ask', {
453
  method: 'POST',
454
- headers: {
455
- 'Content-Type': 'application/json'
456
- },
457
  body: JSON.stringify({
458
  question: question,
459
  session_id: sessionId
@@ -461,6 +310,7 @@ async function sendChatMessage() {
461
  });
462
 
463
  const data = await response.json();
 
464
 
465
  if (!response.ok) {
466
  throw new Error(data.error || 'Failed to get answer');
@@ -473,6 +323,7 @@ async function sendChatMessage() {
473
  addChatMessage(data.answer, 'assistant');
474
 
475
  } catch (error) {
 
476
  document.getElementById(loadingId).remove();
477
  addChatMessage(`Sorry, I encountered an error: ${error.message}`, 'assistant');
478
  showToast(error.message, 'error');
@@ -482,19 +333,22 @@ async function sendChatMessage() {
482
  function addChatMessage(text, sender, isLoading = false) {
483
  const messagesContainer = document.getElementById('chatMessages');
484
 
485
- // Remove welcome message if exists
486
- const welcome = messagesContainer.querySelector('.chat-welcome');
487
- if (welcome) {
488
- welcome.remove();
489
  }
490
 
491
- const messageId = `msg-${Date.now()}`;
492
  const messageDiv = document.createElement('div');
493
  messageDiv.id = messageId;
494
- messageDiv.className = `chat-message ${sender}`;
 
 
 
 
495
 
496
- const bubbleClass = isLoading ? 'chat-bubble loading' : 'chat-bubble';
497
- messageDiv.innerHTML = `<div class="${bubbleClass}">${escapeHtml(text)}</div>`;
498
 
499
  messagesContainer.appendChild(messageDiv);
500
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
@@ -502,30 +356,66 @@ function addChatMessage(text, sender, isLoading = false) {
502
  return messageId;
503
  }
504
 
505
- // ===== LOADING OVERLAY =====
506
- function showLoading(message = 'Processing...') {
507
- const overlay = document.getElementById('loadingOverlay');
508
- if (overlay) {
509
- overlay.querySelector('p').textContent = message;
510
- overlay.classList.remove('hidden');
511
  }
512
- }
513
-
514
- function hideLoading() {
515
- const overlay = document.getElementById('loadingOverlay');
516
- if (overlay) {
517
- overlay.classList.add('hidden');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  }
519
  }
520
 
521
  // ===== TOAST NOTIFICATIONS =====
522
  function showToast(message, type = 'info') {
523
  const toast = document.getElementById('toast');
 
 
 
 
 
 
 
524
  toast.textContent = message;
525
- toast.className = `toast ${type} show`;
 
 
526
 
527
  setTimeout(() => {
528
- toast.classList.remove('show');
 
529
  }, 4000);
530
  }
531
 
@@ -535,36 +425,4 @@ function escapeHtml(text) {
535
  const div = document.createElement('div');
536
  div.textContent = text;
537
  return div.innerHTML;
538
- }
539
-
540
- // ===== SMOOTH SCROLL FOR ALL LINKS =====
541
- document.querySelectorAll('a[href^="#"]').forEach(anchor => {
542
- anchor.addEventListener('click', function (e) {
543
- e.preventDefault();
544
- const target = document.querySelector(this.getAttribute('href'));
545
- if (target) {
546
- target.scrollIntoView({
547
- behavior: 'smooth',
548
- block: 'start'
549
- });
550
- }
551
- });
552
- });
553
-
554
- // ===== CLOSE MODALS ON ESC =====
555
- document.addEventListener('keydown', (e) => {
556
- if (e.key === 'Escape') {
557
- const chatModal = document.getElementById('chatModal');
558
- if (chatModal && !chatModal.classList.contains('hidden')) {
559
- closeChat();
560
- }
561
- }
562
- });
563
-
564
- // ===== CLOSE MODALS ON OUTSIDE CLICK =====
565
- document.addEventListener('click', (e) => {
566
- const chatModal = document.getElementById('chatModal');
567
- if (e.target === chatModal) {
568
- closeChat();
569
- }
570
- });
 
1
  // ===== GLOBAL STATE =====
2
  let currentResults = null;
3
  let explanations = null;
4
+ let sessionId = null;
5
 
6
+ // ===== SECTION NAVIGATION (CRITICAL - MISSING IN YOUR CODE) =====
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  function showSection(sectionId) {
8
+ console.log('πŸ”„ Showing section:', sectionId);
9
+
10
  // Hide all sections
11
  document.querySelectorAll('.section').forEach(section => {
12
  section.classList.remove('active');
 
16
  const targetSection = document.getElementById(sectionId);
17
  if (targetSection) {
18
  targetSection.classList.add('active');
19
+ targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
20
+ } else {
21
+ console.error('❌ Section not found:', sectionId);
22
  }
23
  }
24
 
25
+ // ===== INITIALIZATION =====
26
+ document.addEventListener('DOMContentLoaded', () => {
27
+ console.log('βœ… DOM loaded, initializing...');
28
+ initFileUpload();
 
 
 
 
 
 
 
 
 
 
29
 
30
+ // Show upload section by default
31
+ showSection('upload-section');
32
+ });
 
 
 
 
33
 
34
  // ===== FILE UPLOAD =====
35
  function initFileUpload() {
36
  const fileInput = document.getElementById('fileInput');
37
  const uploadArea = document.getElementById('uploadArea');
38
 
39
+ if (!fileInput || !uploadArea) {
40
+ console.error('❌ Upload elements not found');
41
+ return;
42
+ }
43
+
44
+ console.log('βœ… Upload initialized');
45
 
46
  // Click to upload
47
  fileInput.addEventListener('change', handleFileSelect);
 
49
  // Drag and drop
50
  uploadArea.addEventListener('dragover', (e) => {
51
  e.preventDefault();
52
+ uploadArea.classList.add('border-secondary', 'bg-blue-100');
53
  });
54
 
55
  uploadArea.addEventListener('dragleave', () => {
56
+ uploadArea.classList.remove('border-secondary', 'bg-blue-100');
57
  });
58
 
59
  uploadArea.addEventListener('drop', (e) => {
60
  e.preventDefault();
61
+ uploadArea.classList.remove('border-secondary', 'bg-blue-100');
62
 
63
  const file = e.dataTransfer.files[0];
64
  if (file && file.type === 'application/pdf') {
 
75
  uploadFile(file);
76
  }
77
  }
78
+
79
  async function uploadFile(file) {
80
  const formData = new FormData();
81
  formData.append('file', file);
 
85
  document.getElementById('uploadProgress').classList.remove('hidden');
86
 
87
  try {
88
+ console.log('πŸ“€ Uploading file:', file.name);
89
 
90
  const response = await fetch('/api/upload', {
91
  method: 'POST',
 
104
  currentResults = data.results;
105
 
106
  console.log('βœ… Session ID:', sessionId);
107
+ console.log('βœ… Results:', currentResults.length);
108
 
109
  showToast(`βœ“ Found ${data.count} lab results!`, 'success');
110
 
111
+ // Display results immediately with template explanations
112
+ console.log('🎨 Displaying template results...');
113
  displayResults();
114
+ updateSummaryStats();
115
+
116
+ // Show stats and buttons
117
+ document.getElementById('summaryStats').classList.remove('hidden');
118
+ document.getElementById('generateSummaryBtn').classList.remove('hidden');
119
 
120
+ // Switch to results section
121
  setTimeout(() => {
122
+ showSection('results-section');
 
 
 
 
123
  }, 500);
124
 
125
+ // Fetch AI explanations in background
126
  console.log('πŸ€– Fetching AI explanations...');
127
+ showToast('Generating AI explanations...', 'info');
128
+
129
  await generateExplanations();
130
 
131
  // Update display with AI explanations
 
141
  }
142
  }
143
 
 
144
  async function generateExplanations() {
 
 
145
  try {
146
+ console.log('πŸ”„ Calling /api/explain with session:', sessionId);
 
 
147
 
148
  const response = await fetch('/api/explain', {
149
  method: 'POST',
150
+ headers: { 'Content-Type': 'application/json' },
151
+ body: JSON.stringify({ session_id: sessionId })
 
 
 
152
  });
153
 
 
 
154
  const data = await response.json();
155
+ console.log('πŸ“₯ Explanation response:', data);
156
 
157
  if (!response.ok) {
158
  throw new Error(data.error || 'Failed to generate explanations');
159
  }
160
 
161
  explanations = data.explanations;
162
+ console.log('βœ… Explanations loaded:', Object.keys(explanations).length);
163
 
164
  } catch (error) {
165
+ console.error('❌ Explanation error:', error);
166
+ showToast('Using basic explanations', 'info');
 
 
 
 
 
 
 
 
167
  }
168
  }
169
+
170
  function displayResults() {
 
171
  const container = document.getElementById('resultsContainer');
172
 
173
  console.log('🎯 displayResults() called');
174
+ console.log('πŸ“Š Results:', currentResults?.length);
175
+ console.log('πŸ’¬ Explanations:', explanations ? Object.keys(explanations).length : 0);
176
 
177
  if (!currentResults || currentResults.length === 0) {
178
  console.log('❌ No results to display');
179
+ container.innerHTML = '<div class="flex flex-col items-center justify-center h-full text-slate-400 py-20"><div class="text-6xl mb-4 opacity-20">πŸ“Š</div><p>Upload a lab report to view results here</p></div>';
180
  return;
181
  }
182
 
183
  console.log(`βœ… Displaying ${currentResults.length} results`);
184
 
 
 
 
 
 
 
185
  // Clear container
186
  container.innerHTML = '';
187
 
188
  // Create result cards
189
  currentResults.forEach((result, index) => {
190
+ console.log(`πŸ“ Card ${index + 1}:`, result.test_name, result.status);
191
+ const card = createResultCard(result, index);
192
+ container.appendChild(card);
 
 
 
 
 
193
  });
194
 
195
  // Reset upload area
196
  resetUploadArea();
197
 
198
+ console.log('βœ… All cards rendered');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
200
 
201
  function createResultCard(result, index) {
 
 
202
  const card = document.createElement('div');
203
+ card.className = 'bg-white rounded-xl shadow-md border border-slate-200 p-6 mb-4 hover:shadow-lg transition-shadow';
204
+
205
+ // Status colors
206
+ const statusColors = {
207
+ normal: { bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200' },
208
+ high: { bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200' },
209
+ low: { bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' },
210
+ unknown: { bg: 'bg-slate-50', text: 'text-slate-700', border: 'border-slate-200' }
211
+ };
212
 
213
+ const colors = statusColors[result.status] || statusColors.unknown;
 
 
 
 
 
214
 
215
  // Get explanation
216
  let explanation = '';
217
 
218
  if (explanations && explanations[result.test_name]) {
219
  explanation = explanations[result.test_name];
220
+ console.log(` βœ… Using AI explanation for ${result.test_name}`);
221
  } else {
222
+ console.log(` ⚠️ Using template for ${result.test_name}`);
223
+ // Template explanation
224
  if (result.status === 'normal') {
225
  explanation = `βœ… <strong>Good news!</strong> Your ${result.test_name} level of ${result.value} ${result.unit} is within the normal range (${result.reference_range}).<br><br>This indicates healthy levels. Keep up your current health habits!`;
226
  } else if (result.status === 'high') {
227
+ explanation = `⚠️ Your ${result.test_name} level of ${result.value} ${result.unit} is <strong>above</strong> the normal range (${result.reference_range}).<br><br>This may require attention. Please consult your healthcare provider.`;
228
  } else if (result.status === 'low') {
229
+ explanation = `⚠️ Your ${result.test_name} level of ${result.value} ${result.unit} is <strong>below</strong> the normal range (${result.reference_range}).<br><br>This may require attention. Please consult your healthcare provider.`;
230
  } else {
231
+ explanation = `Your ${result.test_name} result is ${result.value} ${result.unit}.<br><br>Reference range: ${result.reference_range}`;
232
  }
233
  }
234
 
 
235
  card.innerHTML = `
236
+ <div class="flex justify-between items-start mb-4">
237
  <div>
238
+ <h3 class="text-xl font-bold text-slate-800">${escapeHtml(result.test_name)}</h3>
239
+ <p class="text-sm text-slate-500">Reference: ${escapeHtml(result.reference_range || 'N/A')}</p>
240
  </div>
241
+ <div class="text-right">
242
+ <div class="text-2xl font-bold text-slate-800">${escapeHtml(result.value)} <span class="text-lg text-slate-500">${escapeHtml(result.unit)}</span></div>
243
+ <span class="inline-block mt-2 px-3 py-1 rounded-full text-sm font-medium ${colors.bg} ${colors.text} border ${colors.border}">
244
+ ${result.status.toUpperCase()}
245
+ </span>
246
  </div>
247
  </div>
248
+ <div class="mt-4 p-4 bg-blue-50 rounded-lg border border-blue-100">
249
+ <div class="font-semibold text-blue-900 mb-2">πŸ’‘ What does this mean?</div>
250
+ <div class="text-sm text-slate-700 leading-relaxed">${explanation}</div>
251
  </div>
252
  `;
253
 
 
254
  return card;
255
  }
256
 
257
+ function updateSummaryStats() {
258
+ const stats = { normal: 0, high: 0, low: 0 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
+ currentResults.forEach(result => {
261
+ if (result.status in stats) {
262
+ stats[result.status]++;
 
263
  }
264
  });
 
 
 
 
 
 
 
 
265
 
266
+ document.getElementById('normalCount').textContent = stats.normal;
267
+ document.getElementById('highCount').textContent = stats.high;
268
+ document.getElementById('lowCount').textContent = stats.low;
269
+
270
+ console.log('πŸ“Š Stats updated:', stats);
 
 
271
  }
272
 
273
+ function resetUploadArea() {
274
+ document.getElementById('uploadArea').classList.remove('hidden');
275
+ document.getElementById('uploadProgress').classList.add('hidden');
276
+ document.getElementById('fileInput').value = '';
277
  }
278
 
279
+ // ===== CHAT FUNCTIONALITY =====
280
+ async function askQuestion() {
281
  const input = document.getElementById('chatInput');
282
  const question = input.value.trim();
283
 
284
  if (!question) return;
285
 
286
  if (!sessionId) {
287
+ showToast('Please upload a lab report first', 'error');
288
  return;
289
  }
290
 
291
+ console.log('πŸ’¬ Asking:', question);
292
+
293
  // Clear input
294
  input.value = '';
295
 
296
  // Add user message
297
  addChatMessage(question, 'user');
298
 
299
+ // Add loading message
300
  const loadingId = addChatMessage('Thinking...', 'assistant', true);
301
 
302
  try {
303
  const response = await fetch('/api/ask', {
304
  method: 'POST',
305
+ headers: { 'Content-Type': 'application/json' },
 
 
306
  body: JSON.stringify({
307
  question: question,
308
  session_id: sessionId
 
310
  });
311
 
312
  const data = await response.json();
313
+ console.log('πŸ’¬ Answer:', data);
314
 
315
  if (!response.ok) {
316
  throw new Error(data.error || 'Failed to get answer');
 
323
  addChatMessage(data.answer, 'assistant');
324
 
325
  } catch (error) {
326
+ console.error('❌ Chat error:', error);
327
  document.getElementById(loadingId).remove();
328
  addChatMessage(`Sorry, I encountered an error: ${error.message}`, 'assistant');
329
  showToast(error.message, 'error');
 
333
  function addChatMessage(text, sender, isLoading = false) {
334
  const messagesContainer = document.getElementById('chatMessages');
335
 
336
+ // Remove placeholder text if exists
337
+ const placeholder = messagesContainer.querySelector('.text-slate-400');
338
+ if (placeholder && placeholder.closest('.text-center')) {
339
+ placeholder.closest('.text-center').remove();
340
  }
341
 
342
+ const messageId = `msg-${Date.now()}-${Math.random()}`;
343
  const messageDiv = document.createElement('div');
344
  messageDiv.id = messageId;
345
+ messageDiv.className = `mb-4 ${sender === 'user' ? 'text-right' : 'text-left'}`;
346
+
347
+ const bubbleClass = sender === 'user'
348
+ ? 'inline-block bg-secondary text-white px-4 py-2 rounded-2xl rounded-tr-sm max-w-[80%]'
349
+ : 'inline-block bg-slate-100 text-slate-800 px-4 py-2 rounded-2xl rounded-tl-sm max-w-[80%]';
350
 
351
+ messageDiv.innerHTML = `<div class="${bubbleClass} ${isLoading ? 'animate-pulse' : ''}">${escapeHtml(text)}</div>`;
 
352
 
353
  messagesContainer.appendChild(messageDiv);
354
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
 
356
  return messageId;
357
  }
358
 
359
+ // ===== SUMMARY GENERATION =====
360
+ async function generateSummary() {
361
+ if (!sessionId) {
362
+ showToast('Please upload a lab report first', 'error');
363
+ return;
 
364
  }
365
+
366
+ console.log('πŸ“Š Generating summary...');
367
+
368
+ const summaryContent = document.getElementById('summaryContent');
369
+ summaryContent.innerHTML = '<div class="text-center py-10"><div class="inline-block animate-spin rounded-full h-8 w-8 border-4 border-blue-200 border-t-secondary"></div><p class="text-secondary mt-3">Generating summary...</p></div>';
370
+
371
+ try {
372
+ const response = await fetch('/api/summary', {
373
+ method: 'POST',
374
+ headers: { 'Content-Type': 'application/json' },
375
+ body: JSON.stringify({ session_id: sessionId })
376
+ });
377
+
378
+ const data = await response.json();
379
+ console.log('πŸ“Š Summary:', data);
380
+
381
+ if (!response.ok) {
382
+ throw new Error(data.error || 'Failed to generate summary');
383
+ }
384
+
385
+ // Display summary
386
+ summaryContent.innerHTML = `
387
+ <div class="prose max-w-none">
388
+ <div class="text-slate-700 leading-relaxed whitespace-pre-wrap">${escapeHtml(data.summary)}</div>
389
+ </div>
390
+ `;
391
+
392
+ showToast('βœ“ Summary generated!', 'success');
393
+
394
+ } catch (error) {
395
+ console.error('❌ Summary error:', error);
396
+ summaryContent.innerHTML = '<div class="text-center text-slate-400 py-10"><p>Error generating summary. Please try again.</p></div>';
397
+ showToast('Error: ' + error.message, 'error');
398
  }
399
  }
400
 
401
  // ===== TOAST NOTIFICATIONS =====
402
  function showToast(message, type = 'info') {
403
  const toast = document.getElementById('toast');
404
+
405
+ const colors = {
406
+ success: 'bg-green-600',
407
+ error: 'bg-red-600',
408
+ info: 'bg-blue-600'
409
+ };
410
+
411
  toast.textContent = message;
412
+ 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`;
413
+ toast.style.transform = 'translateY(0)';
414
+ toast.style.opacity = '1';
415
 
416
  setTimeout(() => {
417
+ toast.style.transform = 'translateY(100px)';
418
+ toast.style.opacity = '0';
419
  }, 4000);
420
  }
421
 
 
425
  const div = document.createElement('div');
426
  div.textContent = text;
427
  return div.innerHTML;
428
+ }