rajkhanke commited on
Commit
a049d29
·
verified ·
1 Parent(s): cf10f76

Update templates/doctor_dashboard.html

Browse files
Files changed (1) hide show
  1. templates/doctor_dashboard.html +126 -118
templates/doctor_dashboard.html CHANGED
@@ -18,23 +18,19 @@
18
  --light-bg: #f8f9fc; /* Light Grey Background */
19
  --border-color: #e3e6f0; /* Light Border */
20
  }
21
-
22
  body {
23
  font-family: 'Nunito', sans-serif;
24
  background-color: var(--light-bg);
25
  color: var(--dark-color);
26
  }
27
-
28
  .navbar {
29
  background-color: white;
30
  box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);
31
  }
32
-
33
  .navbar-brand {
34
  font-weight: 800;
35
  color: var(--primary-color) !important; /* Override default link color */
36
  }
37
-
38
  .card {
39
  border: none;
40
  box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);
@@ -42,7 +38,6 @@
42
  margin-bottom: 1.5rem;
43
  background-color: white;
44
  }
45
-
46
  .card-header {
47
  background-color: var(--light-bg);
48
  border-bottom: 1px solid var(--border-color);
@@ -50,7 +45,6 @@
50
  color: var(--dark-color);
51
  padding: 1rem 1.25rem;
52
  }
53
-
54
  .btn-primary {
55
  background-color: var(--primary-color);
56
  border-color: var(--primary-color);
@@ -60,22 +54,18 @@
60
  background-color: #2e59d9; /* Darker blue */
61
  border-color: #2653d4;
62
  }
63
-
64
  .btn-success {
65
  background-color: var(--secondary-color);
66
  border-color: var(--secondary-color);
67
  }
68
-
69
  .btn-danger {
70
  background-color: var(--danger-color);
71
  border-color: var(--danger-color);
72
  }
73
-
74
  .role-toggle {
75
  display: flex;
76
  align-items: center;
77
  }
78
-
79
  .role-toggle-btn {
80
  display: flex;
81
  align-items: center;
@@ -89,28 +79,23 @@
89
  transition: all 0.3s ease;
90
  text-decoration: none; /* Remove underline */
91
  }
92
-
93
  .role-toggle-btn:hover {
94
  background-color: #edf2ff; /* Lighter blue */
95
  }
96
-
97
  .role-toggle-btn.active {
98
  background-color: var(--primary-color);
99
  color: white;
100
  border-color: var(--primary-color);
101
  }
102
-
103
  .main-content {
104
  padding: 1.5rem; /* Reduced padding */
105
  }
106
-
107
  .patient-list {
108
  list-style: none;
109
  padding: 0;
110
  max-height: calc(100vh - 250px); /* Adjust height to fit viewport */
111
  overflow-y: auto; /* Add scroll for long lists */
112
  }
113
-
114
  .patient-item {
115
  background-color: white;
116
  border-radius: 0.35rem;
@@ -121,35 +106,32 @@
121
  cursor: pointer;
122
  transition: all 0.2s ease;
123
  }
124
-
125
  .patient-item:hover {
126
  transform: translateY(-3px);
127
  box-shadow: 0 0.3rem 0.8rem 0 rgba(58, 59, 69, 0.1);
128
  background-color: #fcfcfc; /* Slightly lighter hover background */
129
  }
130
-
131
  .patient-item.active {
132
  border: 1px solid var(--primary-color); /* Highlight active item */
133
  border-left: 4px solid var(--primary-color);
134
  background-color: #eef2fc; /* Subtle background for active */
135
  }
136
-
137
-
138
  .patient-item.emergency {
139
  border-left-color: var(--danger-color);
140
  }
141
-
142
  .patient-item.deteriorating {
143
  border-left-color: var(--warning-color);
144
  }
145
-
146
  .patient-item.improving {
147
  border-left-color: var(--secondary-color);
148
  }
149
-
150
  .patient-item.stable {
151
  border-left-color: var(--info-color);
152
  }
 
 
 
153
 
154
  .patient-status {
155
  padding: 0.3rem 0.8rem; /* Slightly larger padding */
@@ -160,20 +142,16 @@
160
  min-width: 80px; /* Ensure minimum width */
161
  text-align: center;
162
  }
163
-
164
  .status-emergency {
165
  background-color: var(--danger-color);
166
  }
167
-
168
  .status-deteriorating {
169
  background-color: var(--warning-color);
170
  color: #212529; /* Dark text for yellow background */
171
  }
172
-
173
  .status-improving {
174
  background-color: var(--secondary-color);
175
  }
176
-
177
  .status-stable {
178
  background-color: var(--info-color);
179
  }
@@ -181,12 +159,9 @@
181
  background-color: var(--dark-color);
182
  color: white;
183
  }
184
-
185
-
186
  .care-plan-tabs {
187
  margin-top: 1.5rem;
188
  }
189
-
190
  .care-plan-content {
191
  background-color: var(--light-bg);
192
  border-radius: 0 0 0.35rem 0.35rem; /* Rounded corners at bottom */
@@ -196,14 +171,14 @@
196
  height: 400px; /* Fixed height */
197
  overflow-y: auto; /* Scroll */
198
  white-space: pre-line; /* Preserve line breaks */
 
 
199
  font-size: 1rem;
200
  color: #333;
201
  }
202
-
203
  .patient-details-section {
204
  display: none; /* Hidden by default */
205
  }
206
-
207
  .loader {
208
  border: 5px solid #f3f3f3;
209
  border-top: 5px solid var(--primary-color);
@@ -214,12 +189,10 @@
214
  margin: 2rem auto;
215
  display: none;
216
  }
217
-
218
  @keyframes spin {
219
  0% { transform: rotate(0deg); }
220
  100% { transform: rotate(360deg); }
221
  }
222
-
223
  #no-patient-selected {
224
  min-height: 300px;
225
  display: flex;
@@ -227,7 +200,6 @@
227
  justify-content: center;
228
  align-items: center;
229
  }
230
-
231
  .alert-danger {
232
  color: #721c24;
233
  background-color: #f8d7da;
@@ -236,6 +208,11 @@
236
  .alert-danger strong {
237
  color: var(--danger-color);
238
  }
 
 
 
 
 
239
  </style>
240
  </head>
241
  <body>
@@ -293,6 +270,7 @@
293
  <!-- Patient list will be populated here -->
294
  </ul>
295
  <div id="no-patients-found" class="text-center p-3 text-muted" style="display: none;">No patients found.</div>
 
296
  </div>
297
  </div>
298
  </div>
@@ -327,6 +305,7 @@
327
  <strong>Last Updated:</strong> <span id="patient-timestamp"></span>
328
  </div>
329
 
 
330
  <button class="btn btn-primary" id="download-pdf">
331
  <i class="fas fa-file-download me-2"></i>Download Care Plan PDF
332
  </button>
@@ -369,36 +348,28 @@
369
  $('.role-toggle-btn[data-role="doctor"]').addClass('active');
370
  $('.role-toggle-btn[data-role="patient"]').removeClass('active');
371
 
372
-
373
- // Role toggle buttons - now just links
374
- // They handle the redirection via href
375
-
376
-
377
- // Show patient list loader
378
- $('#patient-list-loader').show();
379
- $('#no-patients-found').hide();
380
-
381
  // Load all patients
382
  function loadPatients() {
383
  console.log("Loading patients...");
 
 
 
 
 
384
  $.ajax({
385
  url: '/get_patients',
386
  type: 'GET',
387
  success: function(response) {
388
  $('#patient-list-loader').hide();
389
-
390
  if (response.success && response.patients.length > 0) {
391
  console.log("Patients loaded:", response.patients.length);
392
  // Update patient count
393
  $('#patient-count').text(response.patients.length);
394
 
395
- // Clear patient list
396
- $('#patient-list').empty();
397
-
398
  // Sort patients by status priority (Emergency first) then timestamp (newest first)
399
  const statusPriority = { 'emergency': 0, 'deteriorating': 1, 'improving': 2, 'stable': 3, 'unknown': 4 };
400
  const sortedPatients = response.patients.sort((a, b) => {
401
- const priorityDiff = statusPriority[a.status] - statusPriority[b.status];
402
  if (priorityDiff !== 0) {
403
  return priorityDiff;
404
  }
@@ -408,7 +379,6 @@
408
  return dateB - dateA; // Newest first
409
  });
410
 
411
-
412
  // Populate emergency alerts
413
  const emergencyPatients = sortedPatients.filter(p => p.status === 'emergency');
414
  if (emergencyPatients.length > 0) {
@@ -432,15 +402,13 @@
432
  'stable': 'STABLE',
433
  'unknown': 'UNKNOWN'
434
  };
435
-
436
- const statusClass = {
437
- 'emergency': 'emergency', // Use for border-left-color
438
  'deteriorating': 'deteriorating',
439
  'improving': 'improving',
440
  'stable': 'stable',
441
- 'unknown': 'stable' // Default to stable color if unknown
442
  };
443
-
444
  const statusBadgeClass = {
445
  'emergency': 'status-emergency',
446
  'deteriorating': 'status-deteriorating',
@@ -448,15 +416,15 @@
448
  'stable': 'status-stable',
449
  'unknown': 'status-unknown'
450
  };
451
-
452
  const patientHtml = `
453
- <li class="patient-item ${statusClass[patient.status]}" data-id="${patient.id}">
454
  <div class="d-flex justify-content-between align-items-center">
455
  <div>
456
  <h6 class="mb-1">${patient.name || 'Unnamed'}</h6>
457
- <small class="text-muted">Age: ${patient.age || 'N/A'} | Gender: ${patient.gender || 'N/A'}</small>
 
458
  </div>
459
- <span class="patient-status ${statusBadgeClass[patient.status]}">${statusText[patient.status]}</span>
460
  </div>
461
  </li>
462
  `;
@@ -467,16 +435,23 @@
467
  $('.patient-item').off('click').on('click', function() { // Use .off().on() to avoid duplicate handlers
468
  const patientId = $(this).data('id');
469
  // Update URL without full page reload
470
- history.pushState({}, '', `/doctor_dashboard?patient_id=${patientId}`);
471
  loadPatientDetails(patientId);
472
  });
473
 
474
- // Check if a patient ID is in the URL on page load
475
  const urlParams = new URLSearchParams(window.location.search);
476
  const selectedPatientId = urlParams.get('patient_id');
477
  if (selectedPatientId) {
478
  console.log("Loading patient details from URL:", selectedPatientId);
479
- loadPatientDetails(selectedPatientId);
 
 
 
 
 
 
 
480
  } else {
481
  $('#no-patient-selected').show();
482
  $('#patient-details-section').hide();
@@ -488,17 +463,19 @@
488
  $('#patient-count').text('0');
489
  $('#patient-list').empty();
490
  $('#emergency-alerts').empty();
491
- $('#no-patients-found').show();
492
- $('#no-patient-selected').show();
493
- $('#patient-details-section').hide();
494
  }
495
  },
496
  error: function(xhr, status, error) {
497
- console.error("Error loading patients:", error);
498
  $('#patient-list-loader').hide();
499
- $('#patient-list').html('<li class="text-center p-3 text-danger">Error loading patients.</li>');
500
- $('#no-patients-found').hide(); // Hide no patients message if there was an error
501
- $('#no-patient-selected').show();
 
 
502
  $('#patient-details-section').hide();
503
  }
504
  });
@@ -515,15 +492,17 @@
515
  $('#no-patient-selected').hide();
516
  $('#patient-details-section').show();
517
 
518
-
519
- // You could add a loader here if the API call was slow
520
- // $('#patient-details-section').addClass('loading');
521
 
522
  $.ajax({
523
  url: `/get_patient/${patientId}`,
524
  type: 'GET',
525
  success: function(response) {
526
- // $('#patient-details-section').removeClass('loading');
 
 
 
527
  if (response.success) {
528
  const patient = response.patient;
529
  console.log("Successfully loaded patient details:", patient.name);
@@ -533,7 +512,7 @@
533
  $('#patient-age').text(patient.age || 'N/A');
534
  $('#patient-gender').text(patient.gender || 'N/A');
535
  $('#patient-feedback').text(patient.feedback || 'No feedback recorded.');
536
- $('#patient-timestamp').text(patient.timestamp || 'N/A');
537
 
538
  // Set status badge
539
  const statusText = {
@@ -543,7 +522,6 @@
543
  'stable': 'STABLE',
544
  'unknown': 'UNKNOWN'
545
  };
546
-
547
  const statusBadgeClass = {
548
  'emergency': 'status-emergency',
549
  'deteriorating': 'status-deteriorating',
@@ -551,22 +529,22 @@
551
  'stable': 'status-stable',
552
  'unknown': 'status-unknown'
553
  };
554
-
555
  $('#patient-status')
556
  .text(statusText[patient.status] || 'UNKNOWN')
557
  .attr('class', 'patient-status ' + (statusBadgeClass[patient.status] || 'status-unknown'));
558
 
559
-
560
  // Update care plans content
561
  $('#updated-plan-content').text(patient.updated_plan || 'No updated care plan available.');
562
- $('#original-plan-content').text(patient.original_plan || 'No original care plan available.');
563
 
564
  // Reset tab to updated plan and show content
565
- $('#carePlanTabs button').removeClass('active');
566
- $('#updated-plan-tab').addClass('active').tab('show');
 
 
567
 
568
 
569
- // Set up PDF download button
570
  $('#download-pdf').off('click').on('click', function() {
571
  console.log("Download button clicked for patient ID:", patient.id);
572
  window.location.href = `/download_pdf/${patient.id}`;
@@ -575,15 +553,22 @@
575
  } else {
576
  console.error('Error loading patient details:', response.error);
577
  alert('Error: ' + response.error);
578
- $('#no-patient-selected').show(); // Show placeholder if patient not found
579
  $('#patient-details-section').hide();
 
 
580
  }
581
  },
582
  error: function(xhr, status, error) {
583
- console.error("API Error loading patient details:", error);
584
- alert('Error loading patient details');
 
 
 
585
  $('#no-patient-selected').show(); // Show placeholder on API error
586
  $('#patient-details-section').hide();
 
 
587
  }
588
  });
589
  }
@@ -592,11 +577,9 @@
592
  $('#patient-search').on('input', function() {
593
  const searchTerm = $(this).val().toLowerCase();
594
  let foundPatients = false;
595
-
596
  $('.patient-item').each(function() {
597
  const patientName = $(this).find('h6').text().toLowerCase();
598
- const patientDetails = $(this).find('small').text().toLowerCase(); // Includes age/gender
599
-
600
  if (patientName.includes(searchTerm) || patientDetails.includes(searchTerm)) {
601
  $(this).show();
602
  foundPatients = true;
@@ -604,7 +587,6 @@
604
  $(this).hide();
605
  }
606
  });
607
-
608
  if (foundPatients) {
609
  $('#no-patients-found').hide();
610
  } else {
@@ -625,59 +607,85 @@
625
  success: function(response) {
626
  if (response.success && response.notifications.length > 0) {
627
  console.log(`Found ${response.notifications.length} emergency notifications.`);
628
- // Update emergency alerts section
629
- const alertCount = response.notifications.length;
630
- let alertHtml = `
631
- <div class="alert alert-danger mb-3">
632
- <i class="fas fa-exclamation-triangle me-2"></i>
633
- <strong>${alertCount} Emergency patient${alertCount > 1 ? 's' : ''} requiring immediate attention!</strong>
634
- </div>
635
- `;
636
- $('#emergency-alerts').html(alertHtml);
637
-
638
- // Reload the patient list to bring emergency patients to the top
639
- // Ensure currently selected patient remains selected if they are still in the list
640
- const currentSelectedId = $('#patient-details-section').is(':visible') ? $('.patient-item.active').data('id') : null;
641
- loadPatients(); // This will re-render the list and handle selection from URL
642
-
643
- // If you want to *only* reload if there's a *new* emergency notification,
644
- // you'd need to track seen notifications or compare list contents.
645
- // For simplicity, we reload the list whenever *any* emergency exists.
 
 
 
 
646
 
647
  } else {
648
  // If no emergencies, clear the alert area
649
  console.log("No emergency notifications.");
650
  $('#emergency-alerts').empty();
651
- // Optionally, reload the patient list if you want to revert sorting,
652
- // but sorting already keeps non-emergency patients sorted by timestamp.
653
- // So no need to reload the full list if only alerts disappear.
654
  }
655
  },
656
  error: function(xhr, status, error) {
657
  console.error("Error checking emergency notifications:", error);
 
 
658
  }
659
  });
660
  }
661
 
662
- // Check for emergency notifications every 30 seconds
 
663
  setInterval(checkEmergencyNotifications, 30000); // 30 seconds
664
 
665
  // Handle browser back/forward button for URL patient ID
666
  window.onpopstate = function(event) {
667
  const urlParams = new URLSearchParams(window.location.search);
668
  const patientIdFromUrl = urlParams.get('patient_id');
669
- if (patientIdFromUrl) {
670
- loadPatientDetails(patientIdFromUrl);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
  } else {
672
- // If no patient ID in URL (e.g., went back to dashboard root)
673
- $('.patient-item').removeClass('active');
674
- $('#patient-details-section').hide();
675
- $('#no-patient-selected').show();
676
- // Optionally refresh list if needed, but not strictly necessary here
 
677
  }
678
  };
679
-
680
-
681
  });
682
  </script>
683
  </body>
 
18
  --light-bg: #f8f9fc; /* Light Grey Background */
19
  --border-color: #e3e6f0; /* Light Border */
20
  }
 
21
  body {
22
  font-family: 'Nunito', sans-serif;
23
  background-color: var(--light-bg);
24
  color: var(--dark-color);
25
  }
 
26
  .navbar {
27
  background-color: white;
28
  box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);
29
  }
 
30
  .navbar-brand {
31
  font-weight: 800;
32
  color: var(--primary-color) !important; /* Override default link color */
33
  }
 
34
  .card {
35
  border: none;
36
  box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);
 
38
  margin-bottom: 1.5rem;
39
  background-color: white;
40
  }
 
41
  .card-header {
42
  background-color: var(--light-bg);
43
  border-bottom: 1px solid var(--border-color);
 
45
  color: var(--dark-color);
46
  padding: 1rem 1.25rem;
47
  }
 
48
  .btn-primary {
49
  background-color: var(--primary-color);
50
  border-color: var(--primary-color);
 
54
  background-color: #2e59d9; /* Darker blue */
55
  border-color: #2653d4;
56
  }
 
57
  .btn-success {
58
  background-color: var(--secondary-color);
59
  border-color: var(--secondary-color);
60
  }
 
61
  .btn-danger {
62
  background-color: var(--danger-color);
63
  border-color: var(--danger-color);
64
  }
 
65
  .role-toggle {
66
  display: flex;
67
  align-items: center;
68
  }
 
69
  .role-toggle-btn {
70
  display: flex;
71
  align-items: center;
 
79
  transition: all 0.3s ease;
80
  text-decoration: none; /* Remove underline */
81
  }
 
82
  .role-toggle-btn:hover {
83
  background-color: #edf2ff; /* Lighter blue */
84
  }
 
85
  .role-toggle-btn.active {
86
  background-color: var(--primary-color);
87
  color: white;
88
  border-color: var(--primary-color);
89
  }
 
90
  .main-content {
91
  padding: 1.5rem; /* Reduced padding */
92
  }
 
93
  .patient-list {
94
  list-style: none;
95
  padding: 0;
96
  max-height: calc(100vh - 250px); /* Adjust height to fit viewport */
97
  overflow-y: auto; /* Add scroll for long lists */
98
  }
 
99
  .patient-item {
100
  background-color: white;
101
  border-radius: 0.35rem;
 
106
  cursor: pointer;
107
  transition: all 0.2s ease;
108
  }
 
109
  .patient-item:hover {
110
  transform: translateY(-3px);
111
  box-shadow: 0 0.3rem 0.8rem 0 rgba(58, 59, 69, 0.1);
112
  background-color: #fcfcfc; /* Slightly lighter hover background */
113
  }
114
+
115
  .patient-item.active {
116
  border: 1px solid var(--primary-color); /* Highlight active item */
117
  border-left: 4px solid var(--primary-color);
118
  background-color: #eef2fc; /* Subtle background for active */
119
  }
 
 
120
  .patient-item.emergency {
121
  border-left-color: var(--danger-color);
122
  }
 
123
  .patient-item.deteriorating {
124
  border-left-color: var(--warning-color);
125
  }
 
126
  .patient-item.improving {
127
  border-left-color: var(--secondary-color);
128
  }
 
129
  .patient-item.stable {
130
  border-left-color: var(--info-color);
131
  }
132
+ .patient-item.unknown { /* Style for unknown status in list */
133
+ border-left-color: var(--dark-color);
134
+ }
135
 
136
  .patient-status {
137
  padding: 0.3rem 0.8rem; /* Slightly larger padding */
 
142
  min-width: 80px; /* Ensure minimum width */
143
  text-align: center;
144
  }
 
145
  .status-emergency {
146
  background-color: var(--danger-color);
147
  }
 
148
  .status-deteriorating {
149
  background-color: var(--warning-color);
150
  color: #212529; /* Dark text for yellow background */
151
  }
 
152
  .status-improving {
153
  background-color: var(--secondary-color);
154
  }
 
155
  .status-stable {
156
  background-color: var(--info-color);
157
  }
 
159
  background-color: var(--dark-color);
160
  color: white;
161
  }
 
 
162
  .care-plan-tabs {
163
  margin-top: 1.5rem;
164
  }
 
165
  .care-plan-content {
166
  background-color: var(--light-bg);
167
  border-radius: 0 0 0.35rem 0.35rem; /* Rounded corners at bottom */
 
171
  height: 400px; /* Fixed height */
172
  overflow-y: auto; /* Scroll */
173
  white-space: pre-line; /* Preserve line breaks */
174
+ word-wrap: break-word; /* Prevent overflow */
175
+ overflow-x: auto; /* Add horizontal scroll if needed */
176
  font-size: 1rem;
177
  color: #333;
178
  }
 
179
  .patient-details-section {
180
  display: none; /* Hidden by default */
181
  }
 
182
  .loader {
183
  border: 5px solid #f3f3f3;
184
  border-top: 5px solid var(--primary-color);
 
189
  margin: 2rem auto;
190
  display: none;
191
  }
 
192
  @keyframes spin {
193
  0% { transform: rotate(0deg); }
194
  100% { transform: rotate(360deg); }
195
  }
 
196
  #no-patient-selected {
197
  min-height: 300px;
198
  display: flex;
 
200
  justify-content: center;
201
  align-items: center;
202
  }
 
203
  .alert-danger {
204
  color: #721c24;
205
  background-color: #f8d7da;
 
208
  .alert-danger strong {
209
  color: var(--danger-color);
210
  }
211
+ /* Style for feedback block */
212
+ #patient-feedback {
213
+ background-color: #e9ecef; /* Light grey */
214
+ border-color: #dee2e6; /* Light grey border */
215
+ }
216
  </style>
217
  </head>
218
  <body>
 
270
  <!-- Patient list will be populated here -->
271
  </ul>
272
  <div id="no-patients-found" class="text-center p-3 text-muted" style="display: none;">No patients found.</div>
273
+ <div id="patient-list-error" class="text-center p-3 text-danger" style="display: none;">Error loading patients.</div>
274
  </div>
275
  </div>
276
  </div>
 
305
  <strong>Last Updated:</strong> <span id="patient-timestamp"></span>
306
  </div>
307
 
308
+ <!-- Download button now relies on patient ID -->
309
  <button class="btn btn-primary" id="download-pdf">
310
  <i class="fas fa-file-download me-2"></i>Download Care Plan PDF
311
  </button>
 
348
  $('.role-toggle-btn[data-role="doctor"]').addClass('active');
349
  $('.role-toggle-btn[data-role="patient"]').removeClass('active');
350
 
 
 
 
 
 
 
 
 
 
351
  // Load all patients
352
  function loadPatients() {
353
  console.log("Loading patients...");
354
+ $('#patient-list-loader').show();
355
+ $('#no-patients-found').hide();
356
+ $('#patient-list-error').hide(); // Hide previous error
357
+ $('#patient-list').empty(); // Clear current list
358
+
359
  $.ajax({
360
  url: '/get_patients',
361
  type: 'GET',
362
  success: function(response) {
363
  $('#patient-list-loader').hide();
 
364
  if (response.success && response.patients.length > 0) {
365
  console.log("Patients loaded:", response.patients.length);
366
  // Update patient count
367
  $('#patient-count').text(response.patients.length);
368
 
 
 
 
369
  // Sort patients by status priority (Emergency first) then timestamp (newest first)
370
  const statusPriority = { 'emergency': 0, 'deteriorating': 1, 'improving': 2, 'stable': 3, 'unknown': 4 };
371
  const sortedPatients = response.patients.sort((a, b) => {
372
+ const priorityDiff = (statusPriority[a.status] || 4) - (statusPriority[b.status] || 4); // Handle 'unknown' status
373
  if (priorityDiff !== 0) {
374
  return priorityDiff;
375
  }
 
379
  return dateB - dateA; // Newest first
380
  });
381
 
 
382
  // Populate emergency alerts
383
  const emergencyPatients = sortedPatients.filter(p => p.status === 'emergency');
384
  if (emergencyPatients.length > 0) {
 
402
  'stable': 'STABLE',
403
  'unknown': 'UNKNOWN'
404
  };
405
+ const statusClass = { // Used for border-left-color
406
+ 'emergency': 'emergency',
 
407
  'deteriorating': 'deteriorating',
408
  'improving': 'improving',
409
  'stable': 'stable',
410
+ 'unknown': 'unknown' // Use 'unknown' class for border
411
  };
 
412
  const statusBadgeClass = {
413
  'emergency': 'status-emergency',
414
  'deteriorating': 'status-deteriorating',
 
416
  'stable': 'status-stable',
417
  'unknown': 'status-unknown'
418
  };
 
419
  const patientHtml = `
420
+ <li class="patient-item ${statusClass[patient.status] || 'unknown'}" data-id="${patient.id}">
421
  <div class="d-flex justify-content-between align-items-center">
422
  <div>
423
  <h6 class="mb-1">${patient.name || 'Unnamed'}</h6>
424
+ <small class="text-muted">Age: ${patient.age || 'N/A'} | Gender: ${patient.gender || 'N/A'}</small><br>
425
+ <small class="text-muted">Last Updated: ${patient.timestamp ? new Date(patient.timestamp).toLocaleString() : 'N/A'}</small>
426
  </div>
427
+ <span class="patient-status ${statusBadgeClass[patient.status] || 'status-unknown'}">${statusText[patient.status] || 'UNKNOWN'}</span>
428
  </div>
429
  </li>
430
  `;
 
435
  $('.patient-item').off('click').on('click', function() { // Use .off().on() to avoid duplicate handlers
436
  const patientId = $(this).data('id');
437
  // Update URL without full page reload
438
+ history.pushState({ patient_id: patientId }, '', `/doctor_dashboard?patient_id=${patientId}`);
439
  loadPatientDetails(patientId);
440
  });
441
 
442
+ // After loading the list, check if a patient ID is in the URL
443
  const urlParams = new URLSearchParams(window.location.search);
444
  const selectedPatientId = urlParams.get('patient_id');
445
  if (selectedPatientId) {
446
  console.log("Loading patient details from URL:", selectedPatientId);
447
+ // Check if this ID exists in the loaded list before trying to load details
448
+ if ($(`.patient-item[data-id="${selectedPatientId}"]`).length > 0) {
449
+ loadPatientDetails(selectedPatientId);
450
+ } else {
451
+ console.warn(`Patient ID "${selectedPatientId}" from URL not found in loaded list.`);
452
+ $('#no-patient-selected').show();
453
+ $('#patient-details-section').hide();
454
+ }
455
  } else {
456
  $('#no-patient-selected').show();
457
  $('#patient-details-section').hide();
 
463
  $('#patient-count').text('0');
464
  $('#patient-list').empty();
465
  $('#emergency-alerts').empty();
466
+ $('#no-patients-found').show(); // Show no patients message
467
+ $('#no-patient-selected').show(); // Show placeholder in details area
468
+ $('#patient-details-section').hide();
469
  }
470
  },
471
  error: function(xhr, status, error) {
472
+ console.error("Error loading patients:", error);
473
  $('#patient-list-loader').hide();
474
+ $('#patient-list').empty();
475
+ $('#patient-list-error').show().text('Error loading patients. Please try refreshing.');
476
+ $('#patient-count').text('0');
477
+ $('#emergency-alerts').empty();
478
+ $('#no-patient-selected').show(); // Show placeholder on API error
479
  $('#patient-details-section').hide();
480
  }
481
  });
 
492
  $('#no-patient-selected').hide();
493
  $('#patient-details-section').show();
494
 
495
+ // Optional: Show a loader in the details pane
496
+ // $('#patient-details-section .card-body').html('<div class="loader"></div>'); // Replace content with loader
 
497
 
498
  $.ajax({
499
  url: `/get_patient/${patientId}`,
500
  type: 'GET',
501
  success: function(response) {
502
+ // Hide loader if shown
503
+ // $('#patient-details-section .card-body .loader').remove();
504
+ // You would ideally restore the body content here
505
+
506
  if (response.success) {
507
  const patient = response.patient;
508
  console.log("Successfully loaded patient details:", patient.name);
 
512
  $('#patient-age').text(patient.age || 'N/A');
513
  $('#patient-gender').text(patient.gender || 'N/A');
514
  $('#patient-feedback').text(patient.feedback || 'No feedback recorded.');
515
+ $('#patient-timestamp').text(patient.timestamp ? new Date(patient.timestamp).toLocaleString() : 'N/A');
516
 
517
  // Set status badge
518
  const statusText = {
 
522
  'stable': 'STABLE',
523
  'unknown': 'UNKNOWN'
524
  };
 
525
  const statusBadgeClass = {
526
  'emergency': 'status-emergency',
527
  'deteriorating': 'status-deteriorating',
 
529
  'stable': 'status-stable',
530
  'unknown': 'status-unknown'
531
  };
 
532
  $('#patient-status')
533
  .text(statusText[patient.status] || 'UNKNOWN')
534
  .attr('class', 'patient-status ' + (statusBadgeClass[patient.status] || 'status-unknown'));
535
 
 
536
  // Update care plans content
537
  $('#updated-plan-content').text(patient.updated_plan || 'No updated care plan available.');
538
+ $('#original-plan-content').text(patient.original_plan || 'No original care plan available (or could not be extracted from PDF).');
539
 
540
  // Reset tab to updated plan and show content
541
+ $('#carePlanTabs button').removeClass('active').attr('aria-selected', 'false');
542
+ $('#updated-plan-tab').addClass('active').attr('aria-selected', 'true');
543
+ $('#carePlanTabsContent .tab-pane').removeClass('show active');
544
+ $('#updated-plan').addClass('show active');
545
 
546
 
547
+ // Set up PDF download button with patient ID
548
  $('#download-pdf').off('click').on('click', function() {
549
  console.log("Download button clicked for patient ID:", patient.id);
550
  window.location.href = `/download_pdf/${patient.id}`;
 
553
  } else {
554
  console.error('Error loading patient details:', response.error);
555
  alert('Error: ' + response.error);
556
+ $('#no-patient-selected').show(); // Show placeholder if patient not found or error
557
  $('#patient-details-section').hide();
558
+ // Remove highlight from list item if details couldn't load
559
+ $(`.patient-item[data-id="${patientId}"]`).removeClass('active');
560
  }
561
  },
562
  error: function(xhr, status, error) {
563
+ // Hide loader if shown
564
+ // $('#patient-details-section .card-body .loader').remove();
565
+
566
+ console.error("API Error loading patient details:", error, xhr.responseText);
567
+ alert('Error loading patient details. Patient may no longer exist.'); // More user-friendly error
568
  $('#no-patient-selected').show(); // Show placeholder on API error
569
  $('#patient-details-section').hide();
570
+ // Remove highlight from list item on error
571
+ $(`.patient-item[data-id="${patientId}"]`).removeClass('active');
572
  }
573
  });
574
  }
 
577
  $('#patient-search').on('input', function() {
578
  const searchTerm = $(this).val().toLowerCase();
579
  let foundPatients = false;
 
580
  $('.patient-item').each(function() {
581
  const patientName = $(this).find('h6').text().toLowerCase();
582
+ const patientDetails = $(this).find('small').text().toLowerCase(); // Includes age/gender/timestamp
 
583
  if (patientName.includes(searchTerm) || patientDetails.includes(searchTerm)) {
584
  $(this).show();
585
  foundPatients = true;
 
587
  $(this).hide();
588
  }
589
  });
 
590
  if (foundPatients) {
591
  $('#no-patients-found').hide();
592
  } else {
 
607
  success: function(response) {
608
  if (response.success && response.notifications.length > 0) {
609
  console.log(`Found ${response.notifications.length} emergency notifications.`);
610
+ // Check if the current emergency alert count is different
611
+ const currentAlerts = $('#emergency-alerts .alert').length;
612
+ if (response.notifications.length !== currentAlerts) {
613
+ // Update emergency alerts section
614
+ const alertCount = response.notifications.length;
615
+ let alertHtml = `
616
+ <div class="alert alert-danger mb-3">
617
+ <i class="fas fa-exclamation-triangle me-2"></i>
618
+ <strong>${alertCount} Emergency patient${alertCount > 1 ? 's' : ''} requiring immediate attention!</strong>
619
+ </div>
620
+ `;
621
+ $('#emergency-alerts').html(alertHtml);
622
+ // Reload the patient list to update sorting and potentially highlight new emergencies
623
+ const currentSelectedId = $('#patient-details-section').is(':visible') ? $('.patient-item.active').data('id') : null;
624
+ console.log("Reloading patient list due to new emergency alerts...");
625
+ loadPatients(); // This will re-render the list and handle selection from URL/previously selected ID
626
+ } else {
627
+ // If the count is the same, just ensure the alert is visible if needed
628
+ if (currentAlerts > 0) {
629
+ $('#emergency-alerts').show();
630
+ }
631
+ }
632
 
633
  } else {
634
  // If no emergencies, clear the alert area
635
  console.log("No emergency notifications.");
636
  $('#emergency-alerts').empty();
 
 
 
637
  }
638
  },
639
  error: function(xhr, status, error) {
640
  console.error("Error checking emergency notifications:", error);
641
+ // Optionally display a small error message in the alert area
642
+ // $('#emergency-alerts').html('<div class="text-danger small">Error checking alerts.</div>');
643
  }
644
  });
645
  }
646
 
647
+ // Check for emergency notifications periodically (e.g., every 30 seconds)
648
+ // Note: For a production system with many users, websockets would be more efficient.
649
  setInterval(checkEmergencyNotifications, 30000); // 30 seconds
650
 
651
  // Handle browser back/forward button for URL patient ID
652
  window.onpopstate = function(event) {
653
  const urlParams = new URLSearchParams(window.location.search);
654
  const patientIdFromUrl = urlParams.get('patient_id');
655
+
656
+ // Get the ID of the currently displayed patient (if any)
657
+ const currentDisplayedId = $('#patient-details-section').is(':visible') ? $('.patient-item.active').data('id') : null;
658
+
659
+ if (patientIdFromUrl && patientIdFromUrl !== currentDisplayedId) {
660
+ // If URL has an ID and it's different from the current one, load it
661
+ console.log(`URL changed to patient ID: ${patientIdFromUrl}. Loading details.`);
662
+ // Check if this ID exists in the currently rendered list before calling API
663
+ if ($(`.patient-item[data-id="${patientIdFromUrl}"]`).length > 0) {
664
+ loadPatientDetails(patientIdFromUrl);
665
+ } else {
666
+ console.warn(`Patient ID "${patientIdFromUrl}" from URL not found in current list.`);
667
+ $('.patient-item').removeClass('active'); // Remove highlight
668
+ $('#patient-details-section').hide();
669
+ $('#no-patient-selected').show();
670
+ }
671
+ } else if (!patientIdFromUrl && currentDisplayedId) {
672
+ // If URL has no ID but a patient is currently displayed, hide details
673
+ console.log("URL changed, no patient ID. Hiding details.");
674
+ $('.patient-item').removeClass('active'); // Remove highlight
675
+ $('#patient-details-section').hide();
676
+ $('#no-patient-selected').show();
677
+ } else if (patientIdFromUrl && patientIdFromUrl === currentDisplayedId) {
678
+ // If URL has the same ID as currently displayed, do nothing (user navigated back to the same state)
679
+ console.log("URL changed but same patient ID. No action needed.");
680
  } else {
681
+ // If neither URL nor displayed patient has an ID (e.g., went back to dashboard root)
682
+ console.log("URL changed, no patient selected.");
683
+ $('.patient-item').removeClass('active');
684
+ $('#patient-details-section').hide();
685
+ $('#no-patient-selected').show();
686
+ // Optionally refresh list if needed, but not strictly necessary here
687
  }
688
  };
 
 
689
  });
690
  </script>
691
  </body>