LiamKhoaLe commited on
Commit
c6d658a
·
1 Parent(s): 012d58e

Upd dynamic state change and repolling conversation on page refresh

Browse files
Files changed (1) hide show
  1. static/js/app.js +105 -16
static/js/app.js CHANGED
@@ -5,17 +5,25 @@ class MedicalChatbotApp {
5
  this.currentUser = null; // doctor
6
  this.currentPatientId = null;
7
  this.currentSession = null;
 
8
  this.memory = new Map(); // In-memory storage for demo
9
  this.isLoading = false;
10
 
11
  this.init();
12
  }
13
 
14
- init() {
15
  this.setupEventListeners();
16
  this.loadUserPreferences();
17
  this.initializeUser();
18
- // Ensure a session exists and is displayed immediately
 
 
 
 
 
 
 
19
  this.ensureStartupSession();
20
  this.loadChatSessions();
21
  this.setupTheme();
@@ -91,6 +99,28 @@ class MedicalChatbotApp {
91
  });
92
  }
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  setupModalEvents() {
95
  // User modal
96
  document.getElementById('userModalClose').addEventListener('click', () => {
@@ -217,7 +247,7 @@ class MedicalChatbotApp {
217
 
218
  startNewChat() {
219
  if (this.currentSession) {
220
- // Save current session
221
  this.saveCurrentSession();
222
  }
223
 
@@ -245,6 +275,10 @@ class MedicalChatbotApp {
245
  }
246
 
247
  ensureStartupSession() {
 
 
 
 
248
  const sessions = this.getChatSessions();
249
  if (sessions.length === 0) {
250
  // Create a new session immediately so it shows in sidebar
@@ -291,8 +325,8 @@ How can I assist you today?`;
291
  status.style.color = 'var(--warning-color)';
292
  return;
293
  }
294
- // For now we accept ID and load sessions from backend
295
  this.currentPatientId = id;
 
296
  status.textContent = `Patient: ${id}`;
297
  status.style.color = 'var(--text-secondary)';
298
  await this.fetchAndRenderPatientSessions();
@@ -301,18 +335,59 @@ How can I assist you today?`;
301
  async fetchAndRenderPatientSessions() {
302
  if (!this.currentPatientId) return;
303
  try {
304
- const resp = await fetch(`/sessions/patients/${this.currentPatientId}/sessions`.replace('/sessions/', '/sessions/'));
305
  if (resp.ok) {
306
  const data = await resp.json();
307
- // Map to sidebar session cards if needed. For now, rely on local sessions until full backend sync is added.
308
- // Future: hydrate local UI from data.sessions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  }
310
  } catch (e) {
311
  console.error('Failed to load patient sessions', e);
 
312
  }
313
  this.loadChatSessions();
314
  }
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  async sendMessage() {
317
  const input = document.getElementById('chatInput');
318
  const message = input.value.trim();
@@ -587,8 +662,10 @@ How can I assist you today?`;
587
  const sessionsContainer = document.getElementById('chatSessions');
588
  sessionsContainer.innerHTML = '';
589
 
590
- // Get sessions from localStorage
591
- const sessions = this.getChatSessions();
 
 
592
 
593
  if (sessions.length === 0) {
594
  sessionsContainer.innerHTML = '<div class="no-sessions">No chat sessions yet</div>';
@@ -598,8 +675,13 @@ How can I assist you today?`;
598
  sessions.forEach(session => {
599
  const sessionElement = document.createElement('div');
600
  sessionElement.className = `chat-session ${session.id === this.currentSession?.id ? 'active' : ''}`;
601
- sessionElement.addEventListener('click', () => {
602
- this.loadChatSession(session.id);
 
 
 
 
 
603
  });
604
 
605
  const time = this.formatTime(session.lastActivity);
@@ -620,12 +702,18 @@ How can I assist you today?`;
620
 
621
  sessionsContainer.appendChild(sessionElement);
622
 
623
- // Wire 3-dot menu
624
  const menuBtn = sessionElement.querySelector('.chat-session-menu');
625
- menuBtn.addEventListener('click', (e) => {
626
- e.stopPropagation();
627
- this.showSessionMenu(e.currentTarget, session.id);
628
- });
 
 
 
 
 
 
629
  });
630
  }
631
 
@@ -697,6 +785,7 @@ How can I assist you today?`;
697
 
698
  saveCurrentSession() {
699
  if (!this.currentSession) return;
 
700
 
701
  const sessions = this.getChatSessions();
702
  const existingIndex = sessions.findIndex(s => s.id === this.currentSession.id);
 
5
  this.currentUser = null; // doctor
6
  this.currentPatientId = null;
7
  this.currentSession = null;
8
+ this.backendSessions = [];
9
  this.memory = new Map(); // In-memory storage for demo
10
  this.isLoading = false;
11
 
12
  this.init();
13
  }
14
 
15
+ async init() {
16
  this.setupEventListeners();
17
  this.loadUserPreferences();
18
  this.initializeUser();
19
+ this.loadSavedPatientId();
20
+
21
+ // If a patient is selected, fetch sessions from backend first
22
+ if (this.currentPatientId) {
23
+ await this.fetchAndRenderPatientSessions();
24
+ }
25
+
26
+ // Ensure a session exists and is displayed immediately if nothing to show
27
  this.ensureStartupSession();
28
  this.loadChatSessions();
29
  this.setupTheme();
 
99
  });
100
  }
101
 
102
+ loadSavedPatientId() {
103
+ const pid = localStorage.getItem('medicalChatbotPatientId');
104
+ if (pid && /^\d{8}$/.test(pid)) {
105
+ this.currentPatientId = pid;
106
+ const status = document.getElementById('patientStatus');
107
+ if (status) {
108
+ status.textContent = `Patient: ${pid}`;
109
+ status.style.color = 'var(--text-secondary)';
110
+ }
111
+ const input = document.getElementById('patientIdInput');
112
+ if (input) input.value = pid;
113
+ }
114
+ }
115
+
116
+ savePatientId() {
117
+ if (this.currentPatientId) {
118
+ localStorage.setItem('medicalChatbotPatientId', this.currentPatientId);
119
+ } else {
120
+ localStorage.removeItem('medicalChatbotPatientId');
121
+ }
122
+ }
123
+
124
  setupModalEvents() {
125
  // User modal
126
  document.getElementById('userModalClose').addEventListener('click', () => {
 
247
 
248
  startNewChat() {
249
  if (this.currentSession) {
250
+ // Save current session (local only)
251
  this.saveCurrentSession();
252
  }
253
 
 
275
  }
276
 
277
  ensureStartupSession() {
278
+ // If we already have backend sessions for selected patient, do not create a local one
279
+ if (this.backendSessions && this.backendSessions.length > 0) {
280
+ return;
281
+ }
282
  const sessions = this.getChatSessions();
283
  if (sessions.length === 0) {
284
  // Create a new session immediately so it shows in sidebar
 
325
  status.style.color = 'var(--warning-color)';
326
  return;
327
  }
 
328
  this.currentPatientId = id;
329
+ this.savePatientId();
330
  status.textContent = `Patient: ${id}`;
331
  status.style.color = 'var(--text-secondary)';
332
  await this.fetchAndRenderPatientSessions();
 
335
  async fetchAndRenderPatientSessions() {
336
  if (!this.currentPatientId) return;
337
  try {
338
+ const resp = await fetch(`/patients/${this.currentPatientId}/sessions`);
339
  if (resp.ok) {
340
  const data = await resp.json();
341
+ const sessions = Array.isArray(data.sessions) ? data.sessions : [];
342
+ this.backendSessions = sessions.map(s => ({
343
+ id: s.session_id,
344
+ title: s.title || 'New Chat',
345
+ messages: [],
346
+ createdAt: s.created_at || new Date().toISOString(),
347
+ lastActivity: s.last_activity || new Date().toISOString(),
348
+ source: 'backend'
349
+ }));
350
+ // Prefer backend sessions if present
351
+ if (this.backendSessions.length > 0) {
352
+ this.currentSession = this.backendSessions[0];
353
+ await this.hydrateMessagesForSession(this.currentSession.id);
354
+ }
355
+ } else {
356
+ console.warn('Failed to fetch patient sessions', resp.status);
357
+ this.backendSessions = [];
358
  }
359
  } catch (e) {
360
  console.error('Failed to load patient sessions', e);
361
+ this.backendSessions = [];
362
  }
363
  this.loadChatSessions();
364
  }
365
 
366
+ async hydrateMessagesForSession(sessionId) {
367
+ try {
368
+ const resp = await fetch(`/sessions/${sessionId}/messages?limit=1000`);
369
+ if (!resp.ok) return;
370
+ const data = await resp.json();
371
+ const msgs = Array.isArray(data.messages) ? data.messages : [];
372
+ const normalized = msgs.map(m => ({
373
+ id: m._id || this.generateId(),
374
+ role: m.role,
375
+ content: m.content,
376
+ timestamp: m.timestamp
377
+ }));
378
+ // set into currentSession if matched
379
+ if (this.currentSession && this.currentSession.id === sessionId) {
380
+ this.currentSession.messages = normalized;
381
+ // Render
382
+ this.clearChatMessages();
383
+ this.currentSession.messages.forEach(m => this.displayMessage(m));
384
+ this.updateChatTitle();
385
+ }
386
+ } catch (e) {
387
+ console.error('Failed to hydrate session messages', e);
388
+ }
389
+ }
390
+
391
  async sendMessage() {
392
  const input = document.getElementById('chatInput');
393
  const message = input.value.trim();
 
662
  const sessionsContainer = document.getElementById('chatSessions');
663
  sessionsContainer.innerHTML = '';
664
 
665
+ // Prefer backend sessions if a patient is selected and sessions are available
666
+ const sessions = (this.backendSessions && this.backendSessions.length > 0)
667
+ ? this.backendSessions
668
+ : this.getChatSessions();
669
 
670
  if (sessions.length === 0) {
671
  sessionsContainer.innerHTML = '<div class="no-sessions">No chat sessions yet</div>';
 
675
  sessions.forEach(session => {
676
  const sessionElement = document.createElement('div');
677
  sessionElement.className = `chat-session ${session.id === this.currentSession?.id ? 'active' : ''}`;
678
+ sessionElement.addEventListener('click', async () => {
679
+ if (session.source === 'backend') {
680
+ this.currentSession = { ...session };
681
+ await this.hydrateMessagesForSession(session.id);
682
+ } else {
683
+ this.loadChatSession(session.id);
684
+ }
685
  });
686
 
687
  const time = this.formatTime(session.lastActivity);
 
702
 
703
  sessionsContainer.appendChild(sessionElement);
704
 
705
+ // Wire 3-dot menu (local sessions only for now)
706
  const menuBtn = sessionElement.querySelector('.chat-session-menu');
707
+ if (session.source !== 'backend') {
708
+ menuBtn.addEventListener('click', (e) => {
709
+ e.stopPropagation();
710
+ this.showSessionMenu(e.currentTarget, session.id);
711
+ });
712
+ } else {
713
+ menuBtn.disabled = true;
714
+ menuBtn.style.opacity = 0.5;
715
+ menuBtn.title = 'Options available for local sessions only';
716
+ }
717
  });
718
  }
719
 
 
785
 
786
  saveCurrentSession() {
787
  if (!this.currentSession) return;
788
+ if (this.currentSession.source === 'backend') return; // do not persist backend sessions locally here
789
 
790
  const sessions = this.getChatSessions();
791
  const existingIndex = sessions.findIndex(s => s.id === this.currentSession.id);