Spaces:
Runtime error
Runtime error
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- 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 |
+
})();
|