0xKer commited on
Commit
604344d
·
verified ·
1 Parent(s): f188331

Update static/chatbot.js

Browse files
Files changed (1) hide show
  1. static/chatbot.js +97 -7
static/chatbot.js CHANGED
@@ -466,17 +466,13 @@ const Chatbot = (() => {
466
  // Get Gemini AI-generated report if X-ray was uploaded
467
  if (uploadedFiles.length) {
468
  try {
469
- // Show loading spinner in report modal immediately
470
- reportBody.innerHTML = '<div class="report-loading"><span class="report-loading-text">Generating AI clinical report powered by Gemini...<br>This may take a moment.</span></div>';
471
- reportOverlay.style.display = 'flex';
472
 
473
- await botSay('<span class="loader-ring">Generating AI clinical report powered by Gemini...</span>', true, 400);
474
  const formData = new FormData();
475
  uploadedFiles.forEach(f => formData.append('images', f));
476
  const resp = await fetch('/api/ai-report', { method: 'POST', body: formData });
477
  if (resp.ok) {
478
  const aiData = await resp.json();
479
- // Extract the report from the response - it's keyed by filename
480
  if (aiData.results) {
481
  const firstKey = Object.keys(aiData.results)[0];
482
  if (firstKey) {
@@ -484,8 +480,46 @@ const Chatbot = (() => {
484
  }
485
  }
486
  }
487
- reportOverlay.style.display = 'none';
488
- } catch(e) { reportOverlay.style.display = 'none'; console.error('Gemini report error:', e); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  }
490
 
491
  // Build report HTML
@@ -615,6 +649,62 @@ const Chatbot = (() => {
615
  btnTogglePanel.style.display = 'flex';
616
  }
617
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
618
  // --- Event Listeners ---
619
  function init() {
620
  // Text input
 
466
  // Get Gemini AI-generated report if X-ray was uploaded
467
  if (uploadedFiles.length) {
468
  try {
469
+ await botSay('<span class="loader-ring">Generating AI clinical report powered by Gemini... This may take a moment.</span>', true, 400);
 
 
470
 
 
471
  const formData = new FormData();
472
  uploadedFiles.forEach(f => formData.append('images', f));
473
  const resp = await fetch('/api/ai-report', { method: 'POST', body: formData });
474
  if (resp.ok) {
475
  const aiData = await resp.json();
 
476
  if (aiData.results) {
477
  const firstKey = Object.keys(aiData.results)[0];
478
  if (firstKey) {
 
480
  }
481
  }
482
  }
483
+
484
+ // Show AI report as a chat message NOW, before "report ready"
485
+ if (geminiReport && geminiReport.report) {
486
+ let classSummaryHtml = '';
487
+ if (geminiReport.by_class) {
488
+ classSummaryHtml = Object.entries(geminiReport.by_class)
489
+ .map(([cls, count]) => `<span class="severity-badge severity-moderate" style="margin:2px 2px 4px;">${cls}: ${count}</span>`)
490
+ .join(' ');
491
+ }
492
+
493
+ const reportChatHtml = `
494
+ <div style="font-size:12px;line-height:1.7;max-height:420px;overflow-y:auto;padding-right:4px;">
495
+ <div style="font-weight:700;font-size:13px;color:var(--accent);border-bottom:1px solid var(--border);padding-bottom:6px;margin-bottom:10px;">
496
+ 🤖 AI Clinical Report
497
+ <span style="font-size:10px;color:var(--text-muted);font-weight:400;margin-left:6px;">powered by Gemini</span>
498
+ </div>
499
+ ${classSummaryHtml ? `<div style="margin-bottom:10px;display:flex;flex-wrap:wrap;gap:2px;align-items:center;"><strong style="margin-right:6px;font-size:11px;">Detections:</strong>${classSummaryHtml}<span style="color:var(--text-muted);font-size:11px;margin-left:6px;">(${geminiReport.total || 0} total)</span></div>` : ''}
500
+ <div>${geminiReport.report}</div>
501
+ </div>`;
502
+
503
+ addMessage(reportChatHtml, 'bot', true);
504
+
505
+ // PDF download button as a follow-up bot message
506
+ window._geminiReportHtml = geminiReport.report;
507
+ window._geminiByClass = geminiReport.by_class || {};
508
+ window._geminiTotal = geminiReport.total || 0;
509
+ const pdfBtnHtml = `<button onclick="window._downloadGeminiPDF()" style="
510
+ display:inline-flex;align-items:center;gap:6px;
511
+ padding:8px 16px;border-radius:8px;border:none;cursor:pointer;
512
+ background:var(--accent);color:#fff;font-weight:600;font-size:13px;
513
+ box-shadow:0 2px 8px rgba(0,0,0,.25);transition:opacity .2s;
514
+ " onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
515
+ 📥 Download Report as PDF
516
+ </button>`;
517
+ addMessage(pdfBtnHtml, 'bot', true);
518
+ } else if (geminiReport && geminiReport.error) {
519
+ await botSay(`⚠️ AI report note: ${geminiReport.error}`, false, 400);
520
+ }
521
+
522
+ } catch(e) { console.error('Gemini report error:', e); }
523
  }
524
 
525
  // Build report HTML
 
649
  btnTogglePanel.style.display = 'flex';
650
  }
651
 
652
+ // --- PDF Download ---
653
+ window._downloadGeminiPDF = function() {
654
+ const reportHtml = window._geminiReportHtml || '';
655
+ const byClass = window._geminiByClass || {};
656
+ const total = window._geminiTotal || 0;
657
+
658
+ const classBadges = Object.entries(byClass)
659
+ .map(([cls, count]) => `<span style="display:inline-block;padding:2px 8px;border-radius:12px;border:1px solid #0d9488;color:#0d9488;font-size:11px;font-weight:600;margin:2px;">${cls}: ${count}</span>`)
660
+ .join(' ');
661
+
662
+ const printWin = window.open('', '_blank', 'width=850,height=1000');
663
+ if (!printWin) { alert('Please allow popups to download the PDF.'); return; }
664
+
665
+ printWin.document.write(`<!DOCTYPE html><html><head>
666
+ <meta charset="utf-8">
667
+ <title>AI Clinical Report — Assnani Dental</title>
668
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
669
+ <style>
670
+ *{margin:0;padding:0;box-sizing:border-box}
671
+ body{font-family:'Inter',sans-serif;padding:40px;color:#1e293b;font-size:13px;line-height:1.7;background:#fff}
672
+ .header{border-bottom:3px solid #0d9488;padding-bottom:16px;margin-bottom:24px}
673
+ .header h1{font-size:22px;color:#0d9488;margin-bottom:4px}
674
+ .header p{color:#64748b;font-size:12px}
675
+ .detections{margin-bottom:20px;padding:12px;background:#f0fdfa;border-radius:8px;border:1px solid #99f6e4}
676
+ .detections strong{font-size:12px;margin-right:8px}
677
+ .report-content{font-size:13px;line-height:1.8}
678
+ .report-content h1,.report-content h2,.report-content h3{color:#0d9488;margin:16px 0 6px}
679
+ .report-content h1{font-size:16px} .report-content h2{font-size:15px} .report-content h3{font-size:14px}
680
+ .report-content strong{color:#0f172a}
681
+ .report-content table{width:100%;border-collapse:collapse;margin:12px 0}
682
+ .report-content th{background:#f0fdfa;color:#0d9488;padding:8px;border:1px solid #99f6e4;font-size:12px}
683
+ .report-content td{padding:7px 8px;border:1px solid #e2e8f0;font-size:12px}
684
+ .report-content tr:nth-child(even) td{background:#f8fafc}
685
+ .disclaimer{margin-top:32px;padding:12px;border:1px solid #e2e8f0;border-radius:8px;font-size:11px;color:#64748b;text-align:center}
686
+ @media print{
687
+ body{padding:20px}
688
+ .no-print{display:none}
689
+ @page{margin:1cm;size:A4}
690
+ }
691
+ </style>
692
+ </head><body>
693
+ <div class="header">
694
+ <h1>🦷 AI Clinical Dental Report</h1>
695
+ <p>Generated by Assnani AI &nbsp;•&nbsp; ${new Date().toLocaleDateString('en-US',{year:'numeric',month:'long',day:'numeric'})} &nbsp;•&nbsp; Powered by Gemini</p>
696
+ </div>
697
+ ${classBadges ? `<div class="detections"><strong>Findings (${total} total):</strong>${classBadges}</div>` : ''}
698
+ <div class="report-content">${reportHtml}</div>
699
+ <div class="disclaimer">⚠️ This is an AI-assisted preliminary analysis. Final diagnosis must be verified by a licensed dental professional.</div>
700
+ <div class="no-print" style="margin-top:24px;text-align:center">
701
+ <button onclick="window.print()" style="padding:10px 28px;background:#0d9488;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;">🖨️ Save as PDF / Print</button>
702
+ </div>
703
+ </body></html>`);
704
+ printWin.document.close();
705
+ printWin.onload = () => { setTimeout(() => printWin.print(), 300); };
706
+ };
707
+
708
  // --- Event Listeners ---
709
  function init() {
710
  // Text input