LiamKhoaLe commited on
Commit
3058d07
·
1 Parent(s): c48f9b9

Readd submodules JS func to app.js since FastAPI cannot render modules script type. Refactored session.js and settings.js

Browse files
Files changed (1) hide show
  1. static/js/app.js +228 -5
static/js/app.js CHANGED
@@ -56,6 +56,7 @@ class MedicalChatbotApp {
56
  document.getElementById('sidebarToggle').addEventListener('click', () => {
57
  this.toggleSidebar();
58
  });
 
59
  // Click outside sidebar to close (mobile/overlay behavior)
60
  const overlay = document.getElementById('appOverlay');
61
  console.log('[DEBUG] Overlay element found:', !!overlay);
@@ -255,7 +256,6 @@ class MedicalChatbotApp {
255
  this.updateUserDisplay();
256
  }
257
 
258
-
259
  startNewChat() {
260
  if (this.currentSession) {
261
  // Save current session (local only)
@@ -327,7 +327,6 @@ I'm here to help you with medical questions, diagnosis assistance, and healthcar
327
  How can I assist you today?`;
328
  }
329
 
330
-
331
  clearChatMessages() {
332
  const chatMessages = document.getElementById('chatMessages');
333
  chatMessages.innerHTML = '';
@@ -397,7 +396,6 @@ How can I assist you today?`;
397
  this.hideModal('settingsModal');
398
  }
399
 
400
-
401
  updateUserDisplay() {
402
  document.getElementById('userName').textContent = this.currentUser.name;
403
  document.getElementById('userStatus').textContent = this.currentUser.role;
@@ -407,12 +405,237 @@ How can I assist you today?`;
407
  localStorage.setItem('medicalChatbotUser', JSON.stringify(this.currentUser));
408
  }
409
 
410
-
411
  generateId() {
412
  return Date.now().toString(36) + Math.random().toString(36).substr(2);
413
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  }
415
 
 
 
 
 
 
 
 
 
 
 
416
  // Handle system theme changes
417
  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
418
  const themeSelect = document.getElementById('themeSelect');
@@ -452,4 +675,4 @@ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e)
452
  }
453
  }
454
  });
455
- })();
 
56
  document.getElementById('sidebarToggle').addEventListener('click', () => {
57
  this.toggleSidebar();
58
  });
59
+
60
  // Click outside sidebar to close (mobile/overlay behavior)
61
  const overlay = document.getElementById('appOverlay');
62
  console.log('[DEBUG] Overlay element found:', !!overlay);
 
256
  this.updateUserDisplay();
257
  }
258
 
 
259
  startNewChat() {
260
  if (this.currentSession) {
261
  // Save current session (local only)
 
327
  How can I assist you today?`;
328
  }
329
 
 
330
  clearChatMessages() {
331
  const chatMessages = document.getElementById('chatMessages');
332
  chatMessages.innerHTML = '';
 
396
  this.hideModal('settingsModal');
397
  }
398
 
 
399
  updateUserDisplay() {
400
  document.getElementById('userName').textContent = this.currentUser.name;
401
  document.getElementById('userStatus').textContent = this.currentUser.role;
 
405
  localStorage.setItem('medicalChatbotUser', JSON.stringify(this.currentUser));
406
  }
407
 
 
408
  generateId() {
409
  return Date.now().toString(36) + Math.random().toString(36).substr(2);
410
  }
411
+
412
+ // ----------------------------------------------------------
413
+ // Additional UI setup START
414
+ // Including session.js and settings.js from ui/ and chat/
415
+ // -----------------------------
416
+ // Our submodules aren't lodaed on app.js, so we need to add them here
417
+ // Perhaps this is FastAPI limitation, remove this when proper deploy this
418
+ // On UI specific hosting site.
419
+ // ----------------------------------------------------------
420
+
421
+ // Additional methods that are called by the modules
422
+ loadUserPreferences() {
423
+ const prefs = JSON.parse(localStorage.getItem('medicalChatbotPreferences') || '{}');
424
+ if (prefs.theme) this.setTheme(prefs.theme);
425
+ if (prefs.fontSize) this.setFontSize(prefs.fontSize);
426
+ if (prefs.autoSave !== undefined) document.getElementById('autoSave').checked = prefs.autoSave;
427
+ if (prefs.notifications !== undefined) document.getElementById('notifications').checked = prefs.notifications;
428
+ }
429
+
430
+ setTheme(theme) {
431
+ const root = document.documentElement;
432
+ if (theme === 'auto') {
433
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
434
+ root.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
435
+ } else {
436
+ root.setAttribute('data-theme', theme);
437
+ }
438
+ }
439
+
440
+ setFontSize(size) {
441
+ const root = document.documentElement;
442
+ const sizes = { small: '14px', medium: '16px', large: '18px' };
443
+ root.style.fontSize = sizes[size] || '16px';
444
+ }
445
+
446
+ setupTheme() {
447
+ const themeSelect = document.getElementById('themeSelect');
448
+ if (themeSelect) {
449
+ const prefs = JSON.parse(localStorage.getItem('medicalChatbotPreferences') || '{}');
450
+ themeSelect.value = prefs.theme || 'auto';
451
+ }
452
+ }
453
+
454
+ savePreferences() {
455
+ const preferences = {
456
+ theme: document.getElementById('themeSelect').value,
457
+ fontSize: document.getElementById('fontSize').value,
458
+ autoSave: document.getElementById('autoSave').checked,
459
+ notifications: document.getElementById('notifications').checked
460
+ };
461
+ localStorage.setItem('medicalChatbotPreferences', JSON.stringify(preferences));
462
+ }
463
+
464
+ getChatSessions() {
465
+ const sessions = localStorage.getItem('medicalChatbotSessions');
466
+ return sessions ? JSON.parse(sessions) : [];
467
+ }
468
+
469
+ saveCurrentSession() {
470
+ if (!this.currentSession) return;
471
+ const sessions = this.getChatSessions();
472
+ const existingIndex = sessions.findIndex(s => s.id === this.currentSession.id);
473
+ if (existingIndex >= 0) {
474
+ sessions[existingIndex] = this.currentSession;
475
+ } else {
476
+ sessions.unshift(this.currentSession);
477
+ }
478
+ localStorage.setItem('medicalChatbotSessions', JSON.stringify(sessions));
479
+ }
480
+
481
+ loadChatSessions() {
482
+ const sessionsContainer = document.getElementById('chatSessions');
483
+ if (!sessionsContainer) return;
484
+
485
+ // Combine backend and local sessions
486
+ const allSessions = [...this.backendSessions, ...this.getChatSessions()];
487
+
488
+ // Remove duplicates and sort by last activity
489
+ const uniqueSessions = allSessions.reduce((acc, session) => {
490
+ const existing = acc.find(s => s.id === session.id);
491
+ if (!existing) {
492
+ acc.push(session);
493
+ } else if (session.lastActivity > existing.lastActivity) {
494
+ const index = acc.indexOf(existing);
495
+ acc[index] = session;
496
+ }
497
+ return acc;
498
+ }, []);
499
+
500
+ uniqueSessions.sort((a, b) => new Date(b.lastActivity) - new Date(a.lastActivity));
501
+
502
+ sessionsContainer.innerHTML = '';
503
+ uniqueSessions.forEach(session => {
504
+ const sessionElement = document.createElement('div');
505
+ sessionElement.className = 'chat-session';
506
+ if (this.currentSession && this.currentSession.id === session.id) {
507
+ sessionElement.classList.add('active');
508
+ }
509
+
510
+ const timeAgo = this.formatTime(session.lastActivity);
511
+ sessionElement.innerHTML = `
512
+ <div class="chat-session-row">
513
+ <div class="chat-session-meta">
514
+ <div class="chat-session-title">${session.title}</div>
515
+ <div class="chat-session-time">${timeAgo}</div>
516
+ </div>
517
+ <div class="chat-session-actions">
518
+ <button class="chat-session-menu" onclick="event.stopPropagation(); this.nextElementSibling.classList.toggle('show')">
519
+ <i class="fas fa-ellipsis-v"></i>
520
+ </button>
521
+ <div class="chat-session-menu-popover">
522
+ <div class="chat-session-menu-item" onclick="window.medicalChatbot.renameChatSession('${session.id}')">
523
+ <i class="fas fa-edit"></i> Rename
524
+ </div>
525
+ <div class="chat-session-menu-item" onclick="window.medicalChatbot.deleteChatSession('${session.id}')">
526
+ <i class="fas fa-trash"></i> Delete
527
+ </div>
528
+ </div>
529
+ </div>
530
+ </div>
531
+ `;
532
+
533
+ sessionElement.addEventListener('click', () => {
534
+ this.loadChatSession(session.id);
535
+ });
536
+
537
+ sessionsContainer.appendChild(sessionElement);
538
+ });
539
+ }
540
+
541
+ loadChatSession(sessionId) {
542
+ const allSessions = [...this.backendSessions, ...this.getChatSessions()];
543
+ const session = allSessions.find(s => s.id === sessionId);
544
+ if (!session) return;
545
+
546
+ this.currentSession = session;
547
+ this.clearChatMessages();
548
+
549
+ if (session.source === 'backend') {
550
+ this.hydrateMessagesForSession(sessionId);
551
+ } else {
552
+ session.messages.forEach(m => this.displayMessage(m));
553
+ }
554
+
555
+ this.updateChatTitle();
556
+ this.loadChatSessions();
557
+ }
558
+
559
+ renameChatSession(sessionId, newTitle) {
560
+ const allSessions = [...this.backendSessions, ...this.getChatSessions()];
561
+ const session = allSessions.find(s => s.id === sessionId);
562
+ if (session) {
563
+ session.title = newTitle;
564
+ if (session.source === 'backend') {
565
+ // Update backend session
566
+ this.updateBackendSession(sessionId, { title: newTitle });
567
+ } else {
568
+ // Update local session
569
+ this.saveCurrentSession();
570
+ }
571
+ this.loadChatSessions();
572
+ this.updateChatTitle();
573
+ }
574
+ }
575
+
576
+ deleteChatSession(sessionId) {
577
+ if (confirm('Are you sure you want to delete this chat session?')) {
578
+ const allSessions = [...this.backendSessions, ...this.getChatSessions()];
579
+ const session = allSessions.find(s => s.id === sessionId);
580
+
581
+ if (session && session.source === 'backend') {
582
+ // Delete from backend
583
+ this.deleteBackendSession(sessionId);
584
+ } else {
585
+ // Delete from local storage
586
+ const sessions = this.getChatSessions();
587
+ const filtered = sessions.filter(s => s.id !== sessionId);
588
+ localStorage.setItem('medicalChatbotSessions', JSON.stringify(filtered));
589
+ }
590
+
591
+ if (this.currentSession && this.currentSession.id === sessionId) {
592
+ this.startNewChat();
593
+ } else {
594
+ this.loadChatSessions();
595
+ }
596
+ }
597
+ }
598
+
599
+ updateBackendSession(sessionId, updates) {
600
+ // This would call the backend API to update session metadata
601
+ console.log('Updating backend session:', sessionId, updates);
602
+ }
603
+
604
+ deleteBackendSession(sessionId) {
605
+ // This would call the backend API to delete the session
606
+ console.log('Deleting backend session:', sessionId);
607
+ }
608
+
609
+ showLoading(show) {
610
+ const overlay = document.getElementById('loadingOverlay');
611
+ if (overlay) {
612
+ if (show) {
613
+ overlay.classList.add('show');
614
+ } else {
615
+ overlay.classList.remove('show');
616
+ }
617
+ }
618
+ this.isLoading = show;
619
+ }
620
+
621
+ updateCurrentSession() {
622
+ if (this.currentSession) {
623
+ this.currentSession.lastActivity = new Date().toISOString();
624
+ this.saveCurrentSession();
625
+ }
626
+ }
627
  }
628
 
629
+ // Initialize the app when DOM is loaded
630
+ document.addEventListener('DOMContentLoaded', () => {
631
+ window.medicalChatbot = new MedicalChatbotApp();
632
+ });
633
+
634
+
635
+ // ----------------------------------------------------------
636
+ // Additional UI setup END
637
+ // ----------------------------------------------------------
638
+
639
  // Handle system theme changes
640
  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
641
  const themeSelect = document.getElementById('themeSelect');
 
675
  }
676
  }
677
  });
678
+ })();