LiamKhoaLe commited on
Commit
3a138c2
·
2 Parent(s): aa43da8 11ccbbe

Merge remote changes and resolve chat endpoint conflicts

Browse files

- Resolved merge conflicts in static/js/app.js
- Kept improved chat endpoint logic (create session first, then post messages)
- Incorporated remote changes for other files
- Fixed /chat endpoint issue by using /session/{session_id}/messages

API Endpoint Breakdown.md ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### **Definitive API Endpoint Breakdown (with Model Details)**
2
+
3
+ This document outlines all available API endpoints, their expected inputs, and their potential outputs, including the detailed structure of all data models.
4
+
5
+ #### **Account** (`/account`)
6
+ ---
7
+ - **`GET /account`**
8
+ - **Description:** Retrieves a list of accounts, optionally filtered by a search query.
9
+ - **Inputs (Query):** `q` (optional, `string`), `limit` (optional, `integer`)
10
+ - **Outputs (200 OK):** `list[Account]`
11
+ - **Account Model:**
12
+ ```json
13
+ {
14
+ "id": "string",
15
+ "name": "string",
16
+ "role": "string",
17
+ "specialty": "string | null",
18
+ "created_at": "datetime",
19
+ "updated_at": "datetime",
20
+ "last_seen": "datetime"
21
+ }
22
+ ```
23
+
24
+ - **`POST /account`**
25
+ - **Description:** Creates a new account profile.
26
+ - **Inputs (Body):** `AccountCreateRequest`
27
+ ```json
28
+ {
29
+ "name": "string",
30
+ "role": "string",
31
+ "specialty": "string | null"
32
+ }
33
+ ```
34
+ - **Outputs (201 Created):** `Account` (see model above).
35
+
36
+ - **`GET /account/{account_id}`**
37
+ - **Description:** Retrieves a single account by its unique ID.
38
+ - **Inputs (Path):** `account_id` (required, `string`)
39
+ - **Outputs (200 OK):** `Account` (see model above).
40
+
41
+ #### **Patient** (`/patient`)
42
+ ---
43
+ - **`GET /patient`**
44
+ - **Description:** Searches for patients by name.
45
+ - **Inputs (Query):** `q` (required, `string`)
46
+ - **Outputs (200 OK):** `list[Patient]`
47
+ - **Patient Model:**
48
+ ```json
49
+ {
50
+ "id": "string",
51
+ "name": "string",
52
+ "age": "integer",
53
+ "sex": "string",
54
+ "ethnicity": "string",
55
+ "created_at": "datetime",
56
+ "updated_at": "datetime",
57
+ "address": "string | null",
58
+ "phone": "string | null",
59
+ "email": "string | null",
60
+ "medications": "list[string] | null",
61
+ "past_assessment_summary": "string | null",
62
+ "assigned_doctor_id": "string | null"
63
+ }
64
+ ```
65
+
66
+ - **`POST /patient`**
67
+ - **Description:** Creates a new patient profile.
68
+ - **Inputs (Body):** `PatientCreateRequest` (matches the `Patient` model, but `id`, `created_at`, `updated_at` are not provided).
69
+ - **Outputs (201 Created):** `Patient` (see model above).
70
+
71
+ - **`GET /patient/{patient_id}`**
72
+ - **Description:** Retrieves a single patient by their unique ID.
73
+ - **Inputs (Path):** `patient_id` (required, `string`)
74
+ - **Outputs (200 OK):** `Patient` (see model above).
75
+
76
+ - **`PATCH /patient/{patient_id}`**
77
+ - **Description:** Updates a patient's profile.
78
+ - **Inputs (Path):** `patient_id` (required, `string`)
79
+ - **Inputs (Body):** `PatientUpdateRequest` (all fields from the `Patient` model are optional).
80
+ - **Outputs (200 OK):** `Patient` (the full, updated object).
81
+
82
+ - **`GET /patient/{patient_id}/session`**
83
+ - **Description:** Lists all chat sessions for a specific patient.
84
+ - **Inputs (Path):** `patient_id` (required, `string`)
85
+ - **Outputs (200 OK):** `list[Session]` (see Session model below).
86
+
87
+ #### **Session & Chat** (`/session`)
88
+ ---
89
+ - **`POST /session`**
90
+ - **Description:** Creates a new, empty chat session.
91
+ - **Inputs (Body):** `SessionCreateRequest`
92
+ ```json
93
+ {
94
+ "account_id": "string",
95
+ "patient_id": "string",
96
+ "title": "string | null"
97
+ }
98
+ ```
99
+ - **Outputs (201 Created):** `Session`
100
+ - **Session Model:**
101
+ ```json
102
+ {
103
+ "id": "string",
104
+ "account_id": "string",
105
+ "patient_id": "string",
106
+ "title": "string",
107
+ "created_at": "datetime",
108
+ "updated_at": "datetime",
109
+ "messages": "list[Message]"
110
+ }
111
+ ```
112
+ - **Message Model (nested in Session):**
113
+ ```json
114
+ {
115
+ "id": "integer",
116
+ "sent_by_user": "boolean",
117
+ "content": "string",
118
+ "timestamp": "datetime"
119
+ }
120
+ ```
121
+
122
+ - **`GET /session/{session_id}`**
123
+ - **Description:** Retrieves a session's metadata and all its messages.
124
+ - **Inputs (Path):** `session_id` (required, `string`)
125
+ - **Outputs (200 OK):** `Session` (see model above).
126
+
127
+ - **`DELETE /session/{session_id}`**
128
+ - **Description:** Deletes a chat session.
129
+ - **Inputs (Path):** `session_id` (required, `string`)
130
+ - **Outputs (204 No Content):** Empty response.
131
+
132
+ - **`GET /session/{session_id}/messages`**
133
+ - **Description:** Lists all messages for a session.
134
+ - **Inputs (Path):** `session_id` (required, `string`)
135
+ - **Outputs (200 OK):** `list[Message]` (see model above).
136
+
137
+ - **`POST /session/{session_id}/messages`**
138
+ - **Description:** Posts a message to a session and gets a generated response.
139
+ - **Inputs (Path):** `session_id` (required, `string`)
140
+ - **Inputs (Body):** `ChatRequest`
141
+ ```json
142
+ {
143
+ "account_id": "string",
144
+ "patient_id": "string",
145
+ "message": "string"
146
+ }
147
+ ```
148
+ - **Outputs (200 OK):** `ChatResponse`
149
+ ```json
150
+ {
151
+ "response": "string",
152
+ "session_id": "string",
153
+ "timestamp": "datetime",
154
+ "medical_context": "string | null"
155
+ }
156
+ ```
157
+
158
+ #### **EMR (Electronic Medical Records)** (`/emr`)
159
+ ---
160
+ > **Note:** These endpoints represent an older architectural pattern and have not been integrated into the main `MemoryManager` service.
161
+
162
+ - **`GET /emr/patient/{patient_id}`**: Gets all EMR entries for a patient.
163
+ - **Outputs (200 OK):** `list[EMRResponse]`
164
+ - **EMRResponse Model:**
165
+ ```json
166
+ {
167
+ "emr_id": "string",
168
+ "patient_id": "string",
169
+ "doctor_id": "string",
170
+ "message_id": "string",
171
+ "session_id": "string",
172
+ "original_message": "string",
173
+ "extracted_data": {
174
+ "diagnosis": "list[string]",
175
+ "symptoms": "list[string]",
176
+ "medications": "list[Medication]",
177
+ "vital_signs": "VitalSigns | null",
178
+ "lab_results": "list[LabResult]",
179
+ "procedures": "list[string]",
180
+ "notes": "string | null"
181
+ },
182
+ "confidence_score": "float",
183
+ "created_at": "datetime",
184
+ "updated_at": "datetime"
185
+ }
186
+ ```
187
+ - **`POST /emr/extract`**: Extracts EMR data from a message.
188
+ - **Inputs (Query):** `patient_id`, `doctor_id`, `message_id`, `session_id`, `message` (all `string`).
189
+ - **Outputs (200 OK):** `{"emr_id": "string", "message": "string"}`.
190
+ - *(Other EMR endpoints follow a similar pattern, using the models defined in `emr/models/emr.py`)*
191
+
192
+ #### **Utilities**
193
+ ---
194
+ - **`POST /summarise`**
195
+ - **Description:** Summarises text into a short title.
196
+ - **Inputs (Body):** `SummariseRequest`
197
+ ```json
198
+ {
199
+ "text": "string",
200
+ "max_words": "integer"
201
+ }
202
+ ```
203
+ - **Outputs (200 OK):** `SummariseResponse`
204
+ ```json
205
+ {
206
+ "title": "string"
207
+ }
208
+ ```
209
+
210
+ - **`POST /audio/transcribe`**
211
+ - **Description:** Transcribes an audio file to text.
212
+ - **Inputs (Form Data):** `file` (required, `UploadFile`), `language_code` (optional, `string`).
213
+ - **Outputs (200 OK):** `JSON`
214
+ ```json
215
+ {
216
+ "success": "boolean",
217
+ "transcribed_text": "string",
218
+ "language_code": "string",
219
+ "file_name": "string"
220
+ }
221
+ ```
222
+ - **`GET /audio/supported-formats`**: Returns `{"supported_formats": ["wav", "opus", ...], "description": "string"}`.
223
+
224
+ #### **System & Health**
225
+ ---
226
+ - **`GET /`** & **`GET /system-status`**: Serves static HTML pages.
227
+ - **`GET /system/health`**: Returns a `JSON` object detailing the status of core components (memory, embedding, API keys).
228
+ - **`GET /system/database`**: Returns a detailed `JSON` object describing the state of each MongoDB collection (document counts, indexes, fields).
229
+ - **`GET /audio/health`**: Returns a `JSON` object detailing the status of the audio transcription service.
230
+ - **`GET /emr/health`**: Returns a `JSON` object with the status and document count of the EMR collection.
requirements-dev.txt CHANGED
@@ -10,4 +10,4 @@ python-dotenv
10
  pymongo
11
  pandas
12
  python-multipart
13
- pytest
 
10
  pymongo
11
  pandas
12
  python-multipart
13
+ pytest
src/models/common.py CHANGED
@@ -8,10 +8,11 @@ PyObjectId = Annotated[str, BeforeValidator(str)]
8
 
9
  class BaseMongoModel(BaseModel):
10
  """A base Pydantic model for all MongoDB documents."""
11
- id: PyObjectId = Field(..., alias="_id")
12
 
13
  model_config = ConfigDict(
14
  frozen=True,
15
  from_attributes=True,
16
- arbitrary_types_allowed=True
 
17
  )
 
8
 
9
  class BaseMongoModel(BaseModel):
10
  """A base Pydantic model for all MongoDB documents."""
11
+ id: PyObjectId = Field(..., validation_alias="_id")
12
 
13
  model_config = ConfigDict(
14
  frozen=True,
15
  from_attributes=True,
16
+ arbitrary_types_allowed=True,
17
+ populate_by_name=True
18
  )
src/models/session.py CHANGED
@@ -16,7 +16,11 @@ class Message(BaseModel):
16
  timestamp: datetime
17
 
18
  # Use a standard config for this sub-model
19
- model_config = ConfigDict(frozen=True, from_attributes=True)
 
 
 
 
20
 
21
  class Session(BaseMongoModel):
22
  """A Pydantic model for a chat session, including nested messages."""
 
16
  timestamp: datetime
17
 
18
  # Use a standard config for this sub-model
19
+ model_config = ConfigDict(
20
+ frozen=True,
21
+ from_attributes=True,
22
+ populate_by_name=True
23
+ )
24
 
25
  class Session(BaseMongoModel):
26
  """A Pydantic model for a chat session, including nested messages."""
static/index.html CHANGED
@@ -1,326 +1,326 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Medical AI Assistant</title>
7
- <link rel="icon" type="image/svg+xml" href="/static/icon.svg">
8
- <link rel="stylesheet" href="/static/css/styles.css">
9
- <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
10
  </head>
11
  <body>
12
- <div class="app-container">
13
- <div class="app-overlay" id="appOverlay"></div>
14
- <!-- Sidebar -->
15
- <div class="sidebar" id="sidebar">
16
- <div class="sidebar-header">
17
- <button class="new-chat-btn" id="newChatBtn">
18
- <i class="fas fa-plus"></i>
19
- New Chat
20
- </button>
21
- </div>
22
 
23
- <div class="user-section">
24
- <div class="user-profile" id="userProfile">
25
- <div class="user-avatar">
26
- <i class="fas fa-user-md"></i>
27
- </div>
28
- <div class="user-info">
29
- <div class="user-name" id="userName">Anonymous</div>
30
- <div class="user-status" id="userStatus">Medical Professional</div>
31
- </div>
32
- <button class="user-menu-btn" id="userMenuBtn">
33
- <i class="fas fa-ellipsis-v"></i>
34
- </button>
35
- </div>
36
- </div>
37
 
38
- <!-- Patient Login Section -->
39
- <div class="patient-section">
40
- <div class="patient-header">Patient</div>
41
- <div class="patient-input-group patient-typeahead">
42
- <input type="text" id="patientIdInput" class="patient-input" placeholder="Search patient by name or ID">
43
- <div id="patientSuggestions" class="patient-suggestions" style="display:none;"></div>
44
- <button class="patient-load-btn" id="loadPatientBtn" title="Load Patient">
45
- <i class="fas fa-arrow-right"></i>
46
- </button>
47
- <a class="patient-create-link" id="createPatientLink" href="/static/patient.html" title="Create new patient">
48
- <i class="fas fa-user-plus"></i>
49
- </a>
50
- </div>
51
- <div class="patient-status" id="patientStatus">No patient selected</div>
52
- <div class="patient-actions" id="patientActions" style="display: none;">
53
- <a href="/static/emr.html" id="emrLink" class="emr-link">
54
- <i class="fas fa-file-medical"></i> EMR
55
- </a>
56
- </div>
57
- </div>
58
 
59
- <div class="chat-sessions" id="chatSessions">
60
- <!-- Chat sessions will be populated here -->
61
- </div>
62
 
63
- <div class="sidebar-footer">
64
- <button class="settings-btn" id="settingsBtn">
65
- <i class="fas fa-cog"></i>
66
- Settings
67
- </button>
68
- </div>
69
- </div>
70
 
71
- <!-- Main Chat Area -->
72
- <div class="main-content">
73
- <div class="chat-header">
74
- <button class="sidebar-toggle" id="sidebarToggle">
75
- <i class="fas fa-bars"></i>
76
- </button>
77
- <div class="chat-title" id="chatTitle">Medical AI Assistant</div>
78
- <div class="chat-actions">
79
- <button class="action-btn" id="exportBtn" title="Export Chat">
80
- <i class="fas fa-download"></i>
81
- </button>
82
- <button class="action-btn" id="clearBtn" title="Clear Chat">
83
- <i class="fas fa-trash"></i>
84
- </button>
85
- </div>
86
- </div>
87
 
88
- <div class="chat-messages" id="chatMessages">
89
- <!-- Welcome message -->
90
- <div class="message assistant-message">
91
- <div class="message-avatar">
92
- <i class="fas fa-robot"></i>
93
- </div>
94
- <div class="message-content">
95
- <div class="message-text">
96
- 👋 Welcome to Medical AI Assistant
97
 
98
- <div class="tagged-title">
99
- <div class="tagged-title-bar"></div>
100
- <div class="tagged-title-content">
101
- <h1>Who am I, and what can I do?</h1>
102
- </div>
103
- </div>
104
- <p>I'm here to help you with medical questions, diagnosis assistance, and healthcare information. I can:</p>
105
- <div class="tagged-title">
106
- <div class="tagged-title-bar"></div>
107
- <div class="tagged-title-content">
108
- <h1>Key Features</h1>
109
- </div>
110
- </div>
111
- <p><span class="blue-bubble">Medical Information:</span> I can provide evidence-based medical information and explanations.</p>
112
- <p><span class="blue-bubble">Symptom Analysis:</span> I can help analyze symptoms and suggest possible conditions.</p>
113
- <p><span class="blue-bubble">Treatment Guidance:</span> I can explain treatments, medications, and procedures.</p>
114
- <p><span class="blue-bubble">Important:</span> This is for informational purposes only. Always consult with qualified healthcare professionals for medical advice.</p>
115
- <p>How can I assist you today?</p>
116
- </div>
117
- <div class="message-time">Just now</div>
118
- </div>
119
- </div>
120
- </div>
121
 
122
- <div class="chat-input-container">
123
- <div class="input-wrapper">
124
- <textarea
125
- class="chat-input"
126
- id="chatInput"
127
- placeholder="Ask me about any medical topic..."
128
- rows="1"
129
- ></textarea>
130
- <button class="send-btn" id="sendBtn">
131
- <i class="fas fa-paper-plane"></i>
132
- </button>
133
- </div>
134
- <div class="input-footer">
135
- <div class="disclaimer">
136
- <i class="fas fa-info-circle"></i>
137
- AI-powered medical information. Not a substitute for professional medical advice.
138
- </div>
139
- <div class="input-actions">
140
- <button class="microphone-btn" id="microphoneBtn" title="Voice Input">
141
- <i class="fas fa-microphone"></i>
142
- </button>
143
- <button class="attachment-btn" id="attachmentBtn" title="Attach Files">
144
- <i class="fas fa-paperclip"></i>
145
- </button>
146
- </div>
147
- </div>
148
- </div>
149
- </div>
150
- </div>
151
 
152
- <!-- Modals -->
153
- <div class="modal" id="userModal">
154
- <div class="modal-content">
155
- <div class="modal-header">
156
- <h3>Doctor Profile</h3>
157
- <button class="modal-close" id="userModalClose">&times;</button>
158
- </div>
159
- <div class="modal-body">
160
- <div class="form-group">
161
- <label for="profileNameSelect">Name:</label>
162
- <select id="profileNameSelect"></select>
163
- </div>
164
- <div class="form-group" id="newDoctorSection" style="display:none;">
165
- <label for="newDoctorName">Doctor name</label>
166
- <input type="text" id="newDoctorName" placeholder="Enter doctor name">
167
- <div style="display:flex; gap:8px; margin-top:8px;">
168
- <button type="button" class="btn btn-secondary" id="cancelNewDoctor">Cancel</button>
169
- <button type="button" class="btn btn-primary" id="confirmNewDoctor">Confirm</button>
170
- </div>
171
- </div>
172
- <div class="form-group">
173
- <label for="profileRole">Medical Role:</label>
174
- <select id="profileRole">
175
- <option value="Doctor">Doctor</option>
176
- <option value="Healthcare Prof">Healthcare Prof</option>
177
- <option value="Nurse">Nurse</option>
178
- <option value="Caregiver">Caregiver</option>
179
- <option value="Physician">Physician</option>
180
- <option value="Medical Student">Medical Student</option>
181
- <option value="Other">Other</option>
182
- </select>
183
- </div>
184
- <div class="form-group">
185
- <label for="profileSpecialty">Specialty (Optional):</label>
186
- <input type="text" id="profileSpecialty" placeholder="e.g., Cardiology, Pediatrics">
187
- </div>
188
- </div>
189
- <div class="modal-footer">
190
- <button class="btn btn-secondary" id="userModalCancel">Cancel</button>
191
- <button class="btn btn-primary" id="userModalSave">Save</button>
192
- </div>
193
- </div>
194
- </div>
195
 
196
- <div class="modal" id="settingsModal">
197
- <div class="modal-content">
198
- <div class="modal-header">
199
- <h3>Settings</h3>
200
- <button class="modal-close" id="settingsModalClose">&times;</button>
201
- </div>
202
- <div class="modal-body">
203
- <div class="form-group">
204
- <label for="themeSelect">Theme:</label>
205
- <select id="themeSelect">
206
- <option value="light">Light</option>
207
- <option value="dark">Dark</option>
208
- <option value="auto">Auto</option>
209
- </select>
210
- </div>
211
- <div class="form-group">
212
- <label for="fontSize">Font Size:</label>
213
- <select id="fontSize">
214
- <option value="small">Small</option>
215
- <option value="medium" selected>Medium</option>
216
- <option value="large">Large</option>
217
- </select>
218
- </div>
219
- <div class="form-group">
220
- <label>
221
- <input type="checkbox" id="autoSave"> Auto-save conversations
222
- </label>
223
- </div>
224
- <div class="form-group">
225
- <label>
226
- <input type="checkbox" id="notifications"> Enable notifications
227
- </label>
228
- </div>
229
- </div>
230
- <div class="modal-footer">
231
- <button class="btn btn-secondary" id="settingsModalCancel">Cancel</button>
232
- <button class="btn btn-primary" id="settingsModalSave">Save</button>
233
- </div>
234
- <div class="system-access">
235
- <button class="system-access-btn" id="systemAccessBtn">System status</button>
236
- </div>
237
- </div>
238
- </div>
239
 
240
- <!-- Edit Session Title Modal -->
241
- <div class="modal" id="editTitleModal">
242
- <div class="modal-content">
243
- <div class="modal-header">
244
- <h3>Edit Chat Name</h3>
245
- <button class="modal-close" id="editTitleModalClose">&times;</button>
246
- </div>
247
- <div class="modal-body">
248
- <div class="form-group">
249
- <label for="editSessionTitleInput">New name</label>
250
- <input type="text" id="editSessionTitleInput" placeholder="Enter new chat name">
251
- </div>
252
- </div>
253
- <div class="modal-footer">
254
- <button class="btn btn-secondary" id="editTitleModalCancel">Cancel</button>
255
- <button class="btn btn-primary" id="editTitleModalSave">Save</button>
256
- </div>
257
- </div>
258
- </div>
259
 
260
- <!-- Patient Modal -->
261
- <div class="modal" id="patientModal">
262
- <div class="modal-content">
263
- <div class="modal-header">
264
- <h3>Patient Profile</h3>
265
- <button class="modal-close" id="patientModalClose">&times;</button>
266
- </div>
267
- <div class="modal-body">
268
- <div class="patient-summary" id="patientSummary"></div>
269
- <div class="patient-details">
270
- <div><strong>Medications:</strong> <span id="patientMedications">-</span></div>
271
- <div><strong>Past Assessment:</strong> <span id="patientAssessment">-</span></div>
272
- </div>
273
- </div>
274
- <div class="modal-footer">
275
- <button id="patientLogoutBtn" class="btn-danger">Log out patient</button>
276
- <a id="patientCreateBtn" class="btn-primary" href="/static/patient.html">Create new patient</a>
277
- </div>
278
- </div>
279
- </div>
280
 
281
- <!-- Audio Recording Modal -->
282
- <div class="modal" id="audioRecordingModal">
283
- <div class="modal-content audio-recording-modal">
284
- <div class="modal-header">
285
- <h3>Voice Recording</h3>
286
- <button class="modal-close" id="audioRecordingModalClose">&times;</button>
287
- </div>
288
- <div class="modal-body">
289
- <div class="recording-container">
290
- <div class="microphone-icon">
291
- <i class="fas fa-microphone"></i>
292
- </div>
293
- <div class="wave-animation" id="waveAnimation">
294
- <div class="wave-bar"></div>
295
- <div class="wave-bar"></div>
296
- <div class="wave-bar"></div>
297
- <div class="wave-bar"></div>
298
- <div class="wave-bar"></div>
299
- <div class="wave-bar"></div>
300
- <div class="wave-bar"></div>
301
- <div class="wave-bar"></div>
302
- </div>
303
- <div class="recording-status" id="recordingStatus">Listening...</div>
304
- <div class="recording-timer" id="recordingTimer">00:00</div>
305
- </div>
306
- </div>
307
- <div class="modal-footer">
308
- <button class="btn btn-secondary" id="stopRecordingBtn">Stop Recording</button>
309
- </div>
310
- </div>
311
- </div>
312
 
313
- <!-- Loading overlay -->
314
- <div class="loading-overlay" id="loadingOverlay">
315
- <div class="loading-spinner">
316
- <i class="fas fa-heartbeat fa-spin"></i>
317
- <div class="loading-text">Processing your medical query...</div>
318
- </div>
319
- </div>
320
 
321
- <!-- Sidebar overlay for outside-click close -->
322
- <div id="sidebarOverlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.35);z-index:900;"></div>
323
 
324
- <script type="module" src="/static/js/app.js"></script>
325
  </body>
326
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Medical AI Assistant</title>
7
+ <link rel="icon" type="image/svg+xml" href="/static/icon.svg">
8
+ <link rel="stylesheet" href="/static/css/styles.css">
9
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
10
  </head>
11
  <body>
12
+ <div class="app-container">
13
+ <div class="app-overlay" id="appOverlay"></div>
14
+ <!-- Sidebar -->
15
+ <div class="sidebar" id="sidebar">
16
+ <div class="sidebar-header">
17
+ <button class="new-chat-btn" id="newChatBtn">
18
+ <i class="fas fa-plus"></i>
19
+ New Chat
20
+ </button>
21
+ </div>
22
 
23
+ <div class="user-section">
24
+ <div class="user-profile" id="userProfile">
25
+ <div class="user-avatar">
26
+ <i class="fas fa-user-md"></i>
27
+ </div>
28
+ <div class="user-info">
29
+ <div class="user-name" id="userName">Anonymous</div>
30
+ <div class="user-status" id="userStatus">Medical Professional</div>
31
+ </div>
32
+ <button class="user-menu-btn" id="userMenuBtn">
33
+ <i class="fas fa-ellipsis-v"></i>
34
+ </button>
35
+ </div>
36
+ </div>
37
 
38
+ <!-- Patient Login Section -->
39
+ <div class="patient-section">
40
+ <div class="patient-header">Patient</div>
41
+ <div class="patient-input-group patient-typeahead">
42
+ <input type="text" id="patientIdInput" class="patient-input" placeholder="Search patient by name or ID">
43
+ <div id="patientSuggestions" class="patient-suggestions" style="display:none;"></div>
44
+ <button class="patient-load-btn" id="loadPatientBtn" title="Load Patient">
45
+ <i class="fas fa-arrow-right"></i>
46
+ </button>
47
+ <a class="patient-create-link" id="createPatientLink" href="/static/patient.html" title="Create new patient">
48
+ <i class="fas fa-user-plus"></i>
49
+ </a>
50
+ </div>
51
+ <div class="patient-status" id="patientStatus">No patient selected</div>
52
+ <div class="patient-actions" id="patientActions" style="display: none;">
53
+ <a href="/static/emr.html" id="emrLink" class="emr-link">
54
+ <i class="fas fa-file-medical"></i> EMR
55
+ </a>
56
+ </div>
57
+ </div>
58
 
59
+ <div class="chat-sessions" id="chatSessions">
60
+ <!-- Chat sessions will be populated here -->
61
+ </div>
62
 
63
+ <div class="sidebar-footer">
64
+ <button class="settings-btn" id="settingsBtn">
65
+ <i class="fas fa-cog"></i>
66
+ Settings
67
+ </button>
68
+ </div>
69
+ </div>
70
 
71
+ <!-- Main Chat Area -->
72
+ <div class="main-content">
73
+ <div class="chat-header">
74
+ <button class="sidebar-toggle" id="sidebarToggle">
75
+ <i class="fas fa-bars"></i>
76
+ </button>
77
+ <div class="chat-title" id="chatTitle">Medical AI Assistant</div>
78
+ <div class="chat-actions">
79
+ <button class="action-btn" id="exportBtn" title="Export Chat">
80
+ <i class="fas fa-download"></i>
81
+ </button>
82
+ <button class="action-btn" id="clearBtn" title="Clear Chat">
83
+ <i class="fas fa-trash"></i>
84
+ </button>
85
+ </div>
86
+ </div>
87
 
88
+ <div class="chat-messages" id="chatMessages">
89
+ <!-- Welcome message -->
90
+ <div class="message assistant-message">
91
+ <div class="message-avatar">
92
+ <i class="fas fa-robot"></i>
93
+ </div>
94
+ <div class="message-content">
95
+ <div class="message-text">
96
+ 👋 Welcome to Medical AI Assistant
97
 
98
+ <div class="tagged-title">
99
+ <div class="tagged-title-bar"></div>
100
+ <div class="tagged-title-content">
101
+ <h1>Who am I, and what can I do?</h1>
102
+ </div>
103
+ </div>
104
+ <p>I'm here to help you with medical questions, diagnosis assistance, and healthcare information. I can:</p>
105
+ <div class="tagged-title">
106
+ <div class="tagged-title-bar"></div>
107
+ <div class="tagged-title-content">
108
+ <h1>Key Features</h1>
109
+ </div>
110
+ </div>
111
+ <p><span class="blue-bubble">Medical Information:</span> I can provide evidence-based medical information and explanations.</p>
112
+ <p><span class="blue-bubble">Symptom Analysis:</span> I can help analyze symptoms and suggest possible conditions.</p>
113
+ <p><span class="blue-bubble">Treatment Guidance:</span> I can explain treatments, medications, and procedures.</p>
114
+ <p><span class="blue-bubble">Important:</span> This is for informational purposes only. Always consult with qualified healthcare professionals for medical advice.</p>
115
+ <p>How can I assist you today?</p>
116
+ </div>
117
+ <div class="message-time">Just now</div>
118
+ </div>
119
+ </div>
120
+ </div>
121
 
122
+ <div class="chat-input-container">
123
+ <div class="input-wrapper">
124
+ <textarea
125
+ class="chat-input"
126
+ id="chatInput"
127
+ placeholder="Ask me about any medical topic..."
128
+ rows="1"
129
+ ></textarea>
130
+ <button class="send-btn" id="sendBtn">
131
+ <i class="fas fa-paper-plane"></i>
132
+ </button>
133
+ </div>
134
+ <div class="input-footer">
135
+ <div class="disclaimer">
136
+ <i class="fas fa-info-circle"></i>
137
+ AI-powered medical information. Not a substitute for professional medical advice.
138
+ </div>
139
+ <div class="input-actions">
140
+ <button class="microphone-btn" id="microphoneBtn" title="Voice Input">
141
+ <i class="fas fa-microphone"></i>
142
+ </button>
143
+ <button class="attachment-btn" id="attachmentBtn" title="Attach Files">
144
+ <i class="fas fa-paperclip"></i>
145
+ </button>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ </div>
151
 
152
+ <!-- Modals -->
153
+ <div class="modal" id="userModal">
154
+ <div class="modal-content">
155
+ <div class="modal-header">
156
+ <h3>Doctor Profile</h3>
157
+ <button class="modal-close" id="userModalClose">&times;</button>
158
+ </div>
159
+ <div class="modal-body">
160
+ <div class="form-group">
161
+ <label for="profileNameSelect">Name:</label>
162
+ <select id="profileNameSelect"></select>
163
+ </div>
164
+ <div class="form-group" id="newDoctorSection" style="display:none;">
165
+ <label for="newDoctorName">Doctor name</label>
166
+ <input type="text" id="newDoctorName" placeholder="Enter doctor name">
167
+ <div style="display:flex; gap:8px; margin-top:8px;">
168
+ <button type="button" class="btn btn-secondary" id="cancelNewDoctor">Cancel</button>
169
+ <button type="button" class="btn btn-primary" id="confirmNewDoctor">Confirm</button>
170
+ </div>
171
+ </div>
172
+ <div class="form-group">
173
+ <label for="profileRole">Medical Role:</label>
174
+ <select id="profileRole">
175
+ <option value="Doctor">Doctor</option>
176
+ <option value="Healthcare Prof">Healthcare Prof</option>
177
+ <option value="Nurse">Nurse</option>
178
+ <option value="Caregiver">Caregiver</option>
179
+ <option value="Physician">Physician</option>
180
+ <option value="Medical Student">Medical Student</option>
181
+ <option value="Other">Other</option>
182
+ </select>
183
+ </div>
184
+ <div class="form-group">
185
+ <label for="profileSpecialty">Specialty (Optional):</label>
186
+ <input type="text" id="profileSpecialty" placeholder="e.g., Cardiology, Pediatrics">
187
+ </div>
188
+ </div>
189
+ <div class="modal-footer">
190
+ <button class="btn btn-secondary" id="userModalCancel">Cancel</button>
191
+ <button class="btn btn-primary" id="userModalSave">Save</button>
192
+ </div>
193
+ </div>
194
+ </div>
195
 
196
+ <div class="modal" id="settingsModal">
197
+ <div class="modal-content">
198
+ <div class="modal-header">
199
+ <h3>Settings</h3>
200
+ <button class="modal-close" id="settingsModalClose">&times;</button>
201
+ </div>
202
+ <div class="modal-body">
203
+ <div class="form-group">
204
+ <label for="themeSelect">Theme:</label>
205
+ <select id="themeSelect">
206
+ <option value="light">Light</option>
207
+ <option value="dark">Dark</option>
208
+ <option value="auto">Auto</option>
209
+ </select>
210
+ </div>
211
+ <div class="form-group">
212
+ <label for="fontSize">Font Size:</label>
213
+ <select id="fontSize">
214
+ <option value="small">Small</option>
215
+ <option value="medium" selected>Medium</option>
216
+ <option value="large">Large</option>
217
+ </select>
218
+ </div>
219
+ <div class="form-group">
220
+ <label>
221
+ <input type="checkbox" id="autoSave"> Auto-save conversations
222
+ </label>
223
+ </div>
224
+ <div class="form-group">
225
+ <label>
226
+ <input type="checkbox" id="notifications"> Enable notifications
227
+ </label>
228
+ </div>
229
+ </div>
230
+ <div class="modal-footer">
231
+ <button class="btn btn-secondary" id="settingsModalCancel">Cancel</button>
232
+ <button class="btn btn-primary" id="settingsModalSave">Save</button>
233
+ </div>
234
+ <div class="system-access">
235
+ <button class="system-access-btn" id="systemAccessBtn">System status</button>
236
+ </div>
237
+ </div>
238
+ </div>
239
 
240
+ <!-- Edit Session Title Modal -->
241
+ <div class="modal" id="editTitleModal">
242
+ <div class="modal-content">
243
+ <div class="modal-header">
244
+ <h3>Edit Chat Name</h3>
245
+ <button class="modal-close" id="editTitleModalClose">&times;</button>
246
+ </div>
247
+ <div class="modal-body">
248
+ <div class="form-group">
249
+ <label for="editSessionTitleInput">New name</label>
250
+ <input type="text" id="editSessionTitleInput" placeholder="Enter new chat name">
251
+ </div>
252
+ </div>
253
+ <div class="modal-footer">
254
+ <button class="btn btn-secondary" id="editTitleModalCancel">Cancel</button>
255
+ <button class="btn btn-primary" id="editTitleModalSave">Save</button>
256
+ </div>
257
+ </div>
258
+ </div>
259
 
260
+ <!-- Patient Modal -->
261
+ <div class="modal" id="patientModal">
262
+ <div class="modal-content">
263
+ <div class="modal-header">
264
+ <h3>Patient Profile</h3>
265
+ <button class="modal-close" id="patientModalClose">&times;</button>
266
+ </div>
267
+ <div class="modal-body">
268
+ <div class="patient-summary" id="patientSummary"></div>
269
+ <div class="patient-details">
270
+ <div><strong>Medications:</strong> <span id="patientMedications">-</span></div>
271
+ <div><strong>Past Assessment:</strong> <span id="patientAssessment">-</span></div>
272
+ </div>
273
+ </div>
274
+ <div class="modal-footer">
275
+ <button id="patientLogoutBtn" class="btn-danger">Log out patient</button>
276
+ <a id="patientCreateBtn" class="btn-primary" href="/static/patient.html">Create new patient</a>
277
+ </div>
278
+ </div>
279
+ </div>
280
 
281
+ <!-- Audio Recording Modal -->
282
+ <div class="modal" id="audioRecordingModal">
283
+ <div class="modal-content audio-recording-modal">
284
+ <div class="modal-header">
285
+ <h3>Voice Recording</h3>
286
+ <button class="modal-close" id="audioRecordingModalClose">&times;</button>
287
+ </div>
288
+ <div class="modal-body">
289
+ <div class="recording-container">
290
+ <div class="microphone-icon">
291
+ <i class="fas fa-microphone"></i>
292
+ </div>
293
+ <div class="wave-animation" id="waveAnimation">
294
+ <div class="wave-bar"></div>
295
+ <div class="wave-bar"></div>
296
+ <div class="wave-bar"></div>
297
+ <div class="wave-bar"></div>
298
+ <div class="wave-bar"></div>
299
+ <div class="wave-bar"></div>
300
+ <div class="wave-bar"></div>
301
+ <div class="wave-bar"></div>
302
+ </div>
303
+ <div class="recording-status" id="recordingStatus">Listening...</div>
304
+ <div class="recording-timer" id="recordingTimer">00:00</div>
305
+ </div>
306
+ </div>
307
+ <div class="modal-footer">
308
+ <button class="btn btn-secondary" id="stopRecordingBtn">Stop Recording</button>
309
+ </div>
310
+ </div>
311
+ </div>
312
 
313
+ <!-- Loading overlay -->
314
+ <div class="loading-overlay" id="loadingOverlay">
315
+ <div class="loading-spinner">
316
+ <i class="fas fa-heartbeat fa-spin"></i>
317
+ <div class="loading-text">Processing your medical query...</div>
318
+ </div>
319
+ </div>
320
 
321
+ <!-- Sidebar overlay for outside-click close -->
322
+ <div id="sidebarOverlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.35);z-index:900;"></div>
323
 
324
+ <script type="module" src="/static/js/app.js"></script>
325
  </body>
326
  </html>
static/js/app.js CHANGED
@@ -613,10 +613,10 @@ How can I assist you today?`;
613
  } else {
614
  // If doctor not found in local list, try to fetch from backend
615
  try {
616
- const resp = await fetch(`/account/search?q=${encodeURIComponent(selectedName)}&limit=1`);
617
  if (resp.ok) {
618
  const data = await resp.json();
619
- const doctor = data.results && data.results[0];
620
  if (doctor) {
621
  if (doctor.role) {
622
  roleEl.value = doctor.role;
@@ -744,12 +744,10 @@ How can I assist you today?`;
744
  const sessionElement = document.createElement('div');
745
  sessionElement.className = `chat-session ${session.id === this.currentSession?.id ? 'active' : ''}`;
746
  sessionElement.addEventListener('click', async () => {
747
- if (session.source === 'backend') {
748
- this.currentSession = { ...session };
749
- await this.hydrateMessagesForSession(session.id);
750
- } else {
751
- this.loadChatSession(session.id);
752
- }
753
  });
754
  const time = this.formatTime(session.lastActivity);
755
  sessionElement.innerHTML = `
@@ -867,9 +865,6 @@ How can I assist you today?`;
867
  throw new Error(`HTTP ${resp.status}`);
868
  }
869
 
870
- const result = await resp.json();
871
- console.log('[DEBUG] Backend deletion result:', result);
872
-
873
  // Remove from backend sessions
874
  this.backendSessions = this.backendSessions.filter(s => s.id !== sessionId);
875
 
@@ -985,13 +980,13 @@ How can I assist you today?`;
985
  const resp = await fetch('/account');
986
  if (resp.ok) {
987
  const data = await resp.json();
988
- this.doctors = data.results || [];
989
  // Ensure each doctor has role and specialty information
990
  this.doctors = this.doctors.map(doctor => ({
991
  name: doctor.name,
992
  role: doctor.role || 'Medical Professional',
993
  specialty: doctor.specialty || '',
994
- _id: doctor._id // API returns _id, not doctor_id
995
  }));
996
  // Also save to localStorage for offline access
997
  localStorage.setItem('medicalChatbotDoctors', JSON.stringify(this.doctors));
@@ -1018,10 +1013,10 @@ How can I assist you today?`;
1018
 
1019
  async searchDoctors(query) {
1020
  try {
1021
- const resp = await fetch(`/account/search?q=${encodeURIComponent(query)}&limit=10`);
1022
  if (resp.ok) {
1023
  const data = await resp.json();
1024
- return data.results || [];
1025
  }
1026
  } catch (e) {
1027
  console.warn('Doctor search failed:', e);
@@ -1039,8 +1034,7 @@ How can I assist you today?`;
1039
  if (resp.ok) {
1040
  const data = await resp.json();
1041
  // Add to local doctors list
1042
- // FIX: Use account_id from the response
1043
- this.doctors.push({ name: data.name, _id: data.account_id });
1044
  this.saveDoctors();
1045
  return data;
1046
  }
@@ -1103,26 +1097,25 @@ How can I assist you today?`;
1103
  if (!this.doctors.find(d => d.name === name)) {
1104
  // Get current role and specialty from the form
1105
  const role = document.getElementById('profileRole').value || 'Medical Professional';
1106
- const specialty = document.getElementById('profileSpecialty').value.trim() || '';
1107
 
1108
  // Create doctor in MongoDB
1109
  const result = await this.createDoctor({
1110
  name,
1111
  role,
1112
- specialty,
1113
- medical_roles: [role]
1114
  });
1115
- if (result && result.account_id) {
1116
  this.doctors.unshift({
1117
  name,
1118
  role,
1119
  specialty,
1120
- _id: result.account_id // FIX: Use account_id from response
1121
  });
1122
  this.saveDoctors();
1123
 
1124
  // Update current user profile
1125
- this.currentUser.id = result.account_id; // FIX: CRITICAL - Update the user ID
1126
  this.currentUser.name = name;
1127
  this.currentUser.role = role;
1128
  this.currentUser.specialty = specialty;
@@ -1148,10 +1141,13 @@ How can I assist you today?`;
1148
  }
1149
 
1150
  // Check if doctor exists in MongoDB first
1151
- let doctorExists = false;
1152
  try {
1153
- const resp = await fetch(`/account/${encodeURIComponent(name)}`);
1154
- doctorExists = resp.ok;
 
 
 
1155
  } catch (e) {
1156
  console.warn('Failed to check doctor existence:', e);
1157
  }
@@ -1162,12 +1158,11 @@ How can I assist you today?`;
1162
  this.currentUser.specialty = specialty;
1163
 
1164
  // Only create new doctor in MongoDB if it doesn't exist
1165
- if (!doctorExists) {
1166
  const doctorPayload = {
1167
  name: name,
1168
  role: role,
1169
- specialty: specialty || null,
1170
- medical_roles: [role]
1171
  };
1172
 
1173
  try {
@@ -1181,17 +1176,16 @@ How can I assist you today?`;
1181
  const data = await resp.json();
1182
  console.log('[Doctor] Created new doctor in backend:', data);
1183
 
1184
- // FIX: CRITICAL - Update the current user's ID with the one from the database
1185
- if (data.account_id) {
1186
- this.currentUser.id = data.account_id;
1187
  }
1188
 
1189
  // Update local doctor list with the ID from backend
1190
  const existingDoctorIndex = this.doctors.findIndex(d => d.name === name);
1191
  if (existingDoctorIndex === -1) {
1192
- this.doctors.unshift({ name, role, specialty, _id: data.account_id });
1193
  } else {
1194
- this.doctors[existingDoctorIndex]._id = data.account_id;
1195
  }
1196
 
1197
  } catch (err) {
@@ -1199,9 +1193,8 @@ How can I assist you today?`;
1199
  }
1200
  } else {
1201
  // If doctor exists, find their ID and update currentUser
1202
- const existingDoctor = this.doctors.find(d => d.name === name);
1203
- if (existingDoctor && existingDoctor._id) {
1204
- this.currentUser.id = existingDoctor._id;
1205
  }
1206
  console.log('[Doctor] Doctor already exists in backend, no creation needed');
1207
  }
@@ -1222,8 +1215,7 @@ How can I assist you today?`;
1222
  const storedPatients = JSON.parse(localStorage.getItem('medicalChatbotPatients') || '[]');
1223
  return storedPatients.filter(p => {
1224
  const nameMatch = p.name.toLowerCase().includes(query.toLowerCase());
1225
- // FIX: Check _id field which is what the API returns
1226
- const idMatch = p._id && p._id.includes(query);
1227
  return nameMatch || idMatch;
1228
  });
1229
  } catch (e) {
@@ -1236,14 +1228,12 @@ How can I assist you today?`;
1236
  const resultMap = new Map();
1237
  // Add MongoDB results first (they take priority)
1238
  mongoResults.forEach(patient => {
1239
- // FIX: Use _id as the unique key
1240
- if(patient._id) resultMap.set(patient._id, patient);
1241
  });
1242
  // Add localStorage results only if not already present
1243
  localResults.forEach(patient => {
1244
- // FIX: Use _id as the unique key
1245
- if (patient._id && !resultMap.has(patient._id)) {
1246
- resultMap.set(patient._id, patient);
1247
  }
1248
  });
1249
  return Array.from(resultMap.values());
@@ -1279,7 +1269,7 @@ How can I assist you today?`;
1279
  const resp = await fetch(`/patient/${pid}`);
1280
  if (resp.ok) {
1281
  const patient = await resp.json();
1282
- status.textContent = `Patient: ${patient.name || 'Unknown'} (${patient._id})`;
1283
  } else {
1284
  status.textContent = `Patient: ${pid}`;
1285
  }
@@ -1344,18 +1334,18 @@ How can I assist you today?`;
1344
  // Search for patient by name or ID
1345
  console.log('[DEBUG] Searching for patient');
1346
  try {
1347
- const resp = await fetch(`/patient/search?q=${encodeURIComponent(value)}&limit=1`);
1348
  console.log('[DEBUG] Search response status:', resp.status);
1349
  if (resp.ok) {
1350
  const data = await resp.json();
1351
  console.log('[DEBUG] Search results:', data);
1352
- const first = (data.results || [])[0];
1353
- if (first && first._id) { // FIX: Check for _id
1354
  console.log('[DEBUG] Found patient, setting as current:', first);
1355
- this.currentPatientId = first._id; // FIX: Use _id from search result
1356
  this.savePatientId();
1357
- input.value = first._id;
1358
- this.updatePatientDisplay(first._id, first.name || 'Unknown');
1359
  await this.fetchAndRenderPatientSessions();
1360
  return;
1361
  }
@@ -1398,7 +1388,7 @@ How can I assist you today?`;
1398
  const resp = await fetch(`/patient/${this.currentPatientId}/session`);
1399
  if (resp.ok) {
1400
  const data = await resp.json();
1401
- sessions = Array.isArray(data.sessions) ? data.sessions : [];
1402
 
1403
  // Cache the sessions
1404
  localStorage.setItem(cacheKey, JSON.stringify({
@@ -1416,12 +1406,11 @@ How can I assist you today?`;
1416
 
1417
  // Process sessions
1418
  this.backendSessions = sessions.map(s => ({
1419
- // FIX: Add a fallback to _id to make the frontend more robust
1420
- id: s.session_id || s._id,
1421
  title: s.title || 'New Chat',
1422
  messages: [],
1423
  createdAt: s.created_at || new Date().toISOString(),
1424
- lastActivity: s.last_activity || new Date().toISOString(),
1425
  source: 'backend'
1426
  }));
1427
 
@@ -1434,7 +1423,6 @@ How can I assist you today?`;
1434
  }
1435
 
1436
  hydrateMessagesForSession = async function (sessionId) {
1437
- // FIX: Add a guard clause to prevent calling the API with an invalid ID
1438
  if (!sessionId || sessionId === 'undefined') {
1439
  console.error('[DEBUG] hydrateMessagesForSession was called with an invalid session ID:', sessionId);
1440
  return;
@@ -1462,16 +1450,16 @@ How can I assist you today?`;
1462
 
1463
  // If no cache or cache is stale, fetch from backend
1464
  if (messages.length === 0) {
1465
- const resp = await fetch(`/session/${sessionId}/messages?limit=1000`);
1466
  if (!resp.ok) {
1467
  console.warn(`Failed to fetch messages for session ${sessionId}:`, resp.status);
1468
  return;
1469
  }
1470
  const data = await resp.json();
1471
- const msgs = Array.isArray(data.messages) ? data.messages : [];
1472
  messages = msgs.map(m => ({
1473
- id: m._id || this.generateId(),
1474
- role: m.role,
1475
  content: m.content,
1476
  timestamp: m.timestamp
1477
  }));
@@ -1526,15 +1514,13 @@ How can I assist you today?`;
1526
  items.forEach(p => {
1527
  const div = document.createElement('div');
1528
  div.className = 'patient-suggestion';
1529
- // FIX: Use _id for display as it's the consistent identifier
1530
- div.textContent = `${p.name || 'Unknown'} (${p._id})`;
1531
  div.addEventListener('click', async () => {
1532
- // FIX: Use _id from the patient object
1533
- this.currentPatientId = p._id;
1534
  this.savePatientId();
1535
- patientInput.value = p._id;
1536
  hideSuggestions();
1537
- this.updatePatientDisplay(p._id, p.name || 'Unknown');
1538
  await this.fetchAndRenderPatientSessions();
1539
  });
1540
  suggestionsEl.appendChild(div);
@@ -1549,7 +1535,7 @@ How can I assist you today?`;
1549
  debounceTimer = setTimeout(async () => {
1550
  try {
1551
  console.log('[DEBUG] Searching patients with query:', q);
1552
- const url = `/patient/search?q=${encodeURIComponent(q)}&limit=8`;
1553
  console.log('[DEBUG] Search URL:', url);
1554
  const resp = await fetch(url);
1555
  console.log('[DEBUG] Search response status:', resp.status);
@@ -1557,7 +1543,7 @@ How can I assist you today?`;
1557
  let mongoResults = [];
1558
  if (resp.ok) {
1559
  const data = await resp.json();
1560
- mongoResults = data.results || [];
1561
  console.log('[DEBUG] MongoDB search results:', mongoResults);
1562
  } else {
1563
  console.warn('MongoDB search request failed', resp.status);
@@ -1710,7 +1696,6 @@ How can I assist you today?`;
1710
  this.showLoading(true);
1711
  try {
1712
  const responseData = await this.callMedicalAPI(message);
1713
-
1714
  this.addMessage('assistant', responseData.response || 'I apologize, but I received an empty response. Please try again.');
1715
  this.updateCurrentSession();
1716
 
@@ -1744,20 +1729,22 @@ How can I assist you today?`;
1744
  }
1745
  }
1746
 
1747
- const payload = {
1748
  account_id: this.currentUser.id,
1749
  patient_id: this.currentPatientId,
1750
  message: message
1751
  };
1752
 
1753
- const response = await fetch(`/session/${sessionId}/messages`, {
1754
  method: 'POST',
1755
  headers: { 'Content-Type': 'application/json' },
1756
- body: JSON.stringify(payload)
1757
  });
1758
- if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
1759
- const data = await response.json();
 
1760
  return data;
 
1761
  } catch (error) {
1762
  console.error('API call failed:', error);
1763
  console.error('Error details:', {
@@ -1877,15 +1864,15 @@ How can I assist you today?`;
1877
  messageElement.id = `message-${message.id}`;
1878
  const avatar = message.role === 'user' ? '<i class="fas fa-user"></i>' : '<i class="fas fa-robot"></i>';
1879
  const time = this.formatTime(message.timestamp);
1880
-
1881
  // Add EMR icon for assistant messages (system-generated)
1882
- const emrIcon = message.role === 'assistant' ?
1883
  `<div class="message-actions">
1884
  <button class="emr-extract-btn" onclick="app.extractEMR('${message.id}')" title="Extract to EMR" data-message-id="${message.id}">
1885
  <i class="fas fa-file-medical"></i>
1886
  </button>
1887
  </div>` : '';
1888
-
1889
  messageElement.innerHTML = `
1890
  <div class="message-avatar">${avatar}</div>
1891
  <div class="message-content">
@@ -1896,11 +1883,6 @@ How can I assist you today?`;
1896
  chatMessages.appendChild(messageElement);
1897
  chatMessages.scrollTop = chatMessages.scrollHeight;
1898
  if (this.currentSession) this.currentSession.lastActivity = new Date().toISOString();
1899
-
1900
- // Check EMR status for assistant messages
1901
- if (message.role === 'assistant' && this.currentPatientId) {
1902
- this.checkEMRStatus(message.id);
1903
- }
1904
  }
1905
 
1906
  formatMessageContent(content) {
@@ -1967,25 +1949,28 @@ How can I assist you today?`;
1967
  button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
1968
  }
1969
 
 
 
 
 
 
 
 
 
 
 
1970
  // Call EMR extraction API
1971
- const response = await fetch('/emr/extract', {
1972
  method: 'POST',
1973
  headers: {
1974
- 'Content-Type': 'application/json',
1975
- },
1976
- body: JSON.stringify({
1977
- patient_id: this.currentPatientId,
1978
- doctor_id: this.currentUser.id || 'default-doctor',
1979
- message_id: messageId,
1980
- session_id: this.currentSession?.id || 'default-session',
1981
- message: message.content
1982
- })
1983
  });
1984
 
1985
  if (response.ok) {
1986
  const result = await response.json();
1987
  console.log('EMR extraction successful:', result);
1988
-
1989
  // Show success message
1990
  if (button) {
1991
  button.innerHTML = '<i class="fas fa-check"></i>';
@@ -1996,7 +1981,7 @@ How can I assist you today?`;
1996
  button.disabled = false;
1997
  }, 2000);
1998
  }
1999
-
2000
  // Show notification
2001
  this.showNotification('EMR data extracted successfully!', 'success');
2002
  } else {
@@ -2007,37 +1992,19 @@ How can I assist you today?`;
2007
 
2008
  } catch (error) {
2009
  console.error('Error extracting EMR:', error);
2010
-
2011
  // Reset button state
2012
  const button = document.querySelector(`[onclick="app.extractEMR('${messageId}')"]`);
2013
  if (button) {
2014
  button.innerHTML = '<i class="fas fa-file-medical"></i>';
2015
  button.disabled = false;
2016
  }
2017
-
2018
  // Show error message
2019
  this.showNotification('Failed to extract EMR data. Please try again.', 'error');
2020
  }
2021
  }
2022
 
2023
- async checkEMRStatus(messageId) {
2024
- try {
2025
- const response = await fetch(`/emr/check/${messageId}`);
2026
- if (response.ok) {
2027
- const result = await response.json();
2028
- const button = document.querySelector(`[data-message-id="${messageId}"]`);
2029
- if (button && result.emr_exists) {
2030
- button.innerHTML = '<i class="fas fa-check"></i>';
2031
- button.style.color = 'var(--success-color)';
2032
- button.title = 'EMR data already extracted';
2033
- button.disabled = true;
2034
- }
2035
- }
2036
- } catch (error) {
2037
- console.warn('Could not check EMR status:', error);
2038
- }
2039
- }
2040
-
2041
  showNotification(message, type = 'info') {
2042
  // Create notification element
2043
  const notification = document.createElement('div');
@@ -2048,13 +2015,13 @@ How can I assist you today?`;
2048
  <span>${message}</span>
2049
  </div>
2050
  `;
2051
-
2052
  // Add to page
2053
  document.body.appendChild(notification);
2054
-
2055
  // Show notification
2056
  setTimeout(() => notification.classList.add('show'), 100);
2057
-
2058
  // Remove after 3 seconds
2059
  setTimeout(() => {
2060
  notification.classList.remove('show');
 
613
  } else {
614
  // If doctor not found in local list, try to fetch from backend
615
  try {
616
+ const resp = await fetch(`/account?q=${encodeURIComponent(selectedName)}&limit=1`);
617
  if (resp.ok) {
618
  const data = await resp.json();
619
+ const doctor = data && data[0];
620
  if (doctor) {
621
  if (doctor.role) {
622
  roleEl.value = doctor.role;
 
744
  const sessionElement = document.createElement('div');
745
  sessionElement.className = `chat-session ${session.id === this.currentSession?.id ? 'active' : ''}`;
746
  sessionElement.addEventListener('click', async () => {
747
+ // Avoid re-loading if the session is already active
748
+ if (session.id === this.currentSession?.id) return;
749
+
750
+ await this.switchToSession(session);
 
 
751
  });
752
  const time = this.formatTime(session.lastActivity);
753
  sessionElement.innerHTML = `
 
865
  throw new Error(`HTTP ${resp.status}`);
866
  }
867
 
 
 
 
868
  // Remove from backend sessions
869
  this.backendSessions = this.backendSessions.filter(s => s.id !== sessionId);
870
 
 
980
  const resp = await fetch('/account');
981
  if (resp.ok) {
982
  const data = await resp.json();
983
+ this.doctors = data || [];
984
  // Ensure each doctor has role and specialty information
985
  this.doctors = this.doctors.map(doctor => ({
986
  name: doctor.name,
987
  role: doctor.role || 'Medical Professional',
988
  specialty: doctor.specialty || '',
989
+ id: doctor.id
990
  }));
991
  // Also save to localStorage for offline access
992
  localStorage.setItem('medicalChatbotDoctors', JSON.stringify(this.doctors));
 
1013
 
1014
  async searchDoctors(query) {
1015
  try {
1016
+ const resp = await fetch(`/account?q=${encodeURIComponent(query)}&limit=10`);
1017
  if (resp.ok) {
1018
  const data = await resp.json();
1019
+ return data || [];
1020
  }
1021
  } catch (e) {
1022
  console.warn('Doctor search failed:', e);
 
1034
  if (resp.ok) {
1035
  const data = await resp.json();
1036
  // Add to local doctors list
1037
+ this.doctors.push({ name: data.name, id: data.id });
 
1038
  this.saveDoctors();
1039
  return data;
1040
  }
 
1097
  if (!this.doctors.find(d => d.name === name)) {
1098
  // Get current role and specialty from the form
1099
  const role = document.getElementById('profileRole').value || 'Medical Professional';
1100
+ const specialty = document.getElementById('profileSpecialty').value.trim() || null;
1101
 
1102
  // Create doctor in MongoDB
1103
  const result = await this.createDoctor({
1104
  name,
1105
  role,
1106
+ specialty
 
1107
  });
1108
+ if (result && result.id) {
1109
  this.doctors.unshift({
1110
  name,
1111
  role,
1112
  specialty,
1113
+ id: result.id
1114
  });
1115
  this.saveDoctors();
1116
 
1117
  // Update current user profile
1118
+ this.currentUser.id = result.id;
1119
  this.currentUser.name = name;
1120
  this.currentUser.role = role;
1121
  this.currentUser.specialty = specialty;
 
1141
  }
1142
 
1143
  // Check if doctor exists in MongoDB first
1144
+ let existingDoctor = null;
1145
  try {
1146
+ const resp = await fetch(`/account?q=${encodeURIComponent(name)}`);
1147
+ if(resp.ok) {
1148
+ const accounts = await resp.json();
1149
+ existingDoctor = accounts.find(acc => acc.name === name);
1150
+ }
1151
  } catch (e) {
1152
  console.warn('Failed to check doctor existence:', e);
1153
  }
 
1158
  this.currentUser.specialty = specialty;
1159
 
1160
  // Only create new doctor in MongoDB if it doesn't exist
1161
+ if (!existingDoctor) {
1162
  const doctorPayload = {
1163
  name: name,
1164
  role: role,
1165
+ specialty: specialty || null
 
1166
  };
1167
 
1168
  try {
 
1176
  const data = await resp.json();
1177
  console.log('[Doctor] Created new doctor in backend:', data);
1178
 
1179
+ if (data.id) {
1180
+ this.currentUser.id = data.id;
 
1181
  }
1182
 
1183
  // Update local doctor list with the ID from backend
1184
  const existingDoctorIndex = this.doctors.findIndex(d => d.name === name);
1185
  if (existingDoctorIndex === -1) {
1186
+ this.doctors.unshift({ name, role, specialty, id: data.id });
1187
  } else {
1188
+ this.doctors[existingDoctorIndex].id = data.id;
1189
  }
1190
 
1191
  } catch (err) {
 
1193
  }
1194
  } else {
1195
  // If doctor exists, find their ID and update currentUser
1196
+ if (existingDoctor && existingDoctor.id) {
1197
+ this.currentUser.id = existingDoctor.id;
 
1198
  }
1199
  console.log('[Doctor] Doctor already exists in backend, no creation needed');
1200
  }
 
1215
  const storedPatients = JSON.parse(localStorage.getItem('medicalChatbotPatients') || '[]');
1216
  return storedPatients.filter(p => {
1217
  const nameMatch = p.name.toLowerCase().includes(query.toLowerCase());
1218
+ const idMatch = p.id && p.id.includes(query);
 
1219
  return nameMatch || idMatch;
1220
  });
1221
  } catch (e) {
 
1228
  const resultMap = new Map();
1229
  // Add MongoDB results first (they take priority)
1230
  mongoResults.forEach(patient => {
1231
+ if(patient.id) resultMap.set(patient.id, patient);
 
1232
  });
1233
  // Add localStorage results only if not already present
1234
  localResults.forEach(patient => {
1235
+ if (patient.id && !resultMap.has(patient.id)) {
1236
+ resultMap.set(patient.id, patient);
 
1237
  }
1238
  });
1239
  return Array.from(resultMap.values());
 
1269
  const resp = await fetch(`/patient/${pid}`);
1270
  if (resp.ok) {
1271
  const patient = await resp.json();
1272
+ status.textContent = `Patient: ${patient.name || 'Unknown'} (${patient.id})`;
1273
  } else {
1274
  status.textContent = `Patient: ${pid}`;
1275
  }
 
1334
  // Search for patient by name or ID
1335
  console.log('[DEBUG] Searching for patient');
1336
  try {
1337
+ const resp = await fetch(`/patient?q=${encodeURIComponent(value)}&limit=1`);
1338
  console.log('[DEBUG] Search response status:', resp.status);
1339
  if (resp.ok) {
1340
  const data = await resp.json();
1341
  console.log('[DEBUG] Search results:', data);
1342
+ const first = (data || [])[0];
1343
+ if (first && first.id) {
1344
  console.log('[DEBUG] Found patient, setting as current:', first);
1345
+ this.currentPatientId = first.id;
1346
  this.savePatientId();
1347
+ input.value = first.id;
1348
+ this.updatePatientDisplay(first.id, first.name || 'Unknown');
1349
  await this.fetchAndRenderPatientSessions();
1350
  return;
1351
  }
 
1388
  const resp = await fetch(`/patient/${this.currentPatientId}/session`);
1389
  if (resp.ok) {
1390
  const data = await resp.json();
1391
+ sessions = Array.isArray(data) ? data : [];
1392
 
1393
  // Cache the sessions
1394
  localStorage.setItem(cacheKey, JSON.stringify({
 
1406
 
1407
  // Process sessions
1408
  this.backendSessions = sessions.map(s => ({
1409
+ id: s.id,
 
1410
  title: s.title || 'New Chat',
1411
  messages: [],
1412
  createdAt: s.created_at || new Date().toISOString(),
1413
+ lastActivity: s.updated_at || new Date().toISOString(),
1414
  source: 'backend'
1415
  }));
1416
 
 
1423
  }
1424
 
1425
  hydrateMessagesForSession = async function (sessionId) {
 
1426
  if (!sessionId || sessionId === 'undefined') {
1427
  console.error('[DEBUG] hydrateMessagesForSession was called with an invalid session ID:', sessionId);
1428
  return;
 
1450
 
1451
  // If no cache or cache is stale, fetch from backend
1452
  if (messages.length === 0) {
1453
+ const resp = await fetch(`/session/${sessionId}/messages`);
1454
  if (!resp.ok) {
1455
  console.warn(`Failed to fetch messages for session ${sessionId}:`, resp.status);
1456
  return;
1457
  }
1458
  const data = await resp.json();
1459
+ const msgs = Array.isArray(data) ? data : [];
1460
  messages = msgs.map(m => ({
1461
+ id: m.id || this.generateId(),
1462
+ role: m.sent_by_user ? 'user' : 'assistant',
1463
  content: m.content,
1464
  timestamp: m.timestamp
1465
  }));
 
1514
  items.forEach(p => {
1515
  const div = document.createElement('div');
1516
  div.className = 'patient-suggestion';
1517
+ div.textContent = `${p.name || 'Unknown'} (${p.id})`;
 
1518
  div.addEventListener('click', async () => {
1519
+ this.currentPatientId = p.id;
 
1520
  this.savePatientId();
1521
+ patientInput.value = p.id;
1522
  hideSuggestions();
1523
+ this.updatePatientDisplay(p.id, p.name || 'Unknown');
1524
  await this.fetchAndRenderPatientSessions();
1525
  });
1526
  suggestionsEl.appendChild(div);
 
1535
  debounceTimer = setTimeout(async () => {
1536
  try {
1537
  console.log('[DEBUG] Searching patients with query:', q);
1538
+ const url = `/patient?q=${encodeURIComponent(q)}&limit=8`;
1539
  console.log('[DEBUG] Search URL:', url);
1540
  const resp = await fetch(url);
1541
  console.log('[DEBUG] Search response status:', resp.status);
 
1543
  let mongoResults = [];
1544
  if (resp.ok) {
1545
  const data = await resp.json();
1546
+ mongoResults = data || [];
1547
  console.log('[DEBUG] MongoDB search results:', mongoResults);
1548
  } else {
1549
  console.warn('MongoDB search request failed', resp.status);
 
1696
  this.showLoading(true);
1697
  try {
1698
  const responseData = await this.callMedicalAPI(message);
 
1699
  this.addMessage('assistant', responseData.response || 'I apologize, but I received an empty response. Please try again.');
1700
  this.updateCurrentSession();
1701
 
 
1729
  }
1730
  }
1731
 
1732
+ const messagePayload = {
1733
  account_id: this.currentUser.id,
1734
  patient_id: this.currentPatientId,
1735
  message: message
1736
  };
1737
 
1738
+ const messageResponse = await fetch(`/session/${sessionId}/messages`, {
1739
  method: 'POST',
1740
  headers: { 'Content-Type': 'application/json' },
1741
+ body: JSON.stringify(messagePayload)
1742
  });
1743
+
1744
+ if (!messageResponse.ok) throw new Error(`HTTP error! status: ${messageResponse.status}`);
1745
+ const data = await messageResponse.json();
1746
  return data;
1747
+
1748
  } catch (error) {
1749
  console.error('API call failed:', error);
1750
  console.error('Error details:', {
 
1864
  messageElement.id = `message-${message.id}`;
1865
  const avatar = message.role === 'user' ? '<i class="fas fa-user"></i>' : '<i class="fas fa-robot"></i>';
1866
  const time = this.formatTime(message.timestamp);
1867
+
1868
  // Add EMR icon for assistant messages (system-generated)
1869
+ const emrIcon = message.role === 'assistant' ?
1870
  `<div class="message-actions">
1871
  <button class="emr-extract-btn" onclick="app.extractEMR('${message.id}')" title="Extract to EMR" data-message-id="${message.id}">
1872
  <i class="fas fa-file-medical"></i>
1873
  </button>
1874
  </div>` : '';
1875
+
1876
  messageElement.innerHTML = `
1877
  <div class="message-avatar">${avatar}</div>
1878
  <div class="message-content">
 
1883
  chatMessages.appendChild(messageElement);
1884
  chatMessages.scrollTop = chatMessages.scrollHeight;
1885
  if (this.currentSession) this.currentSession.lastActivity = new Date().toISOString();
 
 
 
 
 
1886
  }
1887
 
1888
  formatMessageContent(content) {
 
1949
  button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
1950
  }
1951
 
1952
+ // Build URL with query parameters
1953
+ const params = new URLSearchParams({
1954
+ patient_id: this.currentPatientId,
1955
+ doctor_id: this.currentUser.id || 'default-doctor',
1956
+ message_id: messageId,
1957
+ session_id: this.currentSession?.id || 'default-session',
1958
+ message: message.content
1959
+ });
1960
+ const url = `/emr/extract?${params.toString()}`;
1961
+
1962
  // Call EMR extraction API
1963
+ const response = await fetch(url, {
1964
  method: 'POST',
1965
  headers: {
1966
+ 'Content-Type': 'application/json', // Header might still be needed by middleware
1967
+ }
 
 
 
 
 
 
 
1968
  });
1969
 
1970
  if (response.ok) {
1971
  const result = await response.json();
1972
  console.log('EMR extraction successful:', result);
1973
+
1974
  // Show success message
1975
  if (button) {
1976
  button.innerHTML = '<i class="fas fa-check"></i>';
 
1981
  button.disabled = false;
1982
  }, 2000);
1983
  }
1984
+
1985
  // Show notification
1986
  this.showNotification('EMR data extracted successfully!', 'success');
1987
  } else {
 
1992
 
1993
  } catch (error) {
1994
  console.error('Error extracting EMR:', error);
1995
+
1996
  // Reset button state
1997
  const button = document.querySelector(`[onclick="app.extractEMR('${messageId}')"]`);
1998
  if (button) {
1999
  button.innerHTML = '<i class="fas fa-file-medical"></i>';
2000
  button.disabled = false;
2001
  }
2002
+
2003
  // Show error message
2004
  this.showNotification('Failed to extract EMR data. Please try again.', 'error');
2005
  }
2006
  }
2007
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2008
  showNotification(message, type = 'info') {
2009
  // Create notification element
2010
  const notification = document.createElement('div');
 
2015
  <span>${message}</span>
2016
  </div>
2017
  `;
2018
+
2019
  // Add to page
2020
  document.body.appendChild(notification);
2021
+
2022
  // Show notification
2023
  setTimeout(() => notification.classList.add('show'), 100);
2024
+
2025
  // Remove after 3 seconds
2026
  setTimeout(() => {
2027
  notification.classList.remove('show');
static/js/patient.js CHANGED
@@ -15,9 +15,9 @@ document.addEventListener('DOMContentLoaded', () => {
15
 
16
  function getPatientIdFromUrl() {
17
  const urlParams = new URLSearchParams(window.location.search);
 
18
  const pidFromUrl = urlParams.get('patient_id');
19
- if (pidFromUrl && /^\d{8}$/.test(pidFromUrl)) return pidFromUrl;
20
- return null;
21
  }
22
 
23
  async function loadPatientIntoForm(patientId) {
@@ -90,19 +90,19 @@ document.addEventListener('DOMContentLoaded', () => {
90
  });
91
  if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
92
  const data = await resp.json();
93
- const pid = data.patient_id;
94
  localStorage.setItem('medicalChatbotPatientId', pid);
95
 
96
  // Add to localStorage for future suggestions
97
  const existingPatients = JSON.parse(localStorage.getItem('medicalChatbotPatients') || '[]');
98
  const newPatient = {
99
- patient_id: pid,
100
  name: payload.name,
101
  age: payload.age,
102
  sex: payload.sex
103
  };
104
  // Check if patient already exists to avoid duplicates
105
- const exists = existingPatients.some(p => p.patient_id === pid);
106
  if (!exists) {
107
  existingPatients.push(newPatient);
108
  localStorage.setItem('medicalChatbotPatients', JSON.stringify(existingPatients));
@@ -125,7 +125,12 @@ document.addEventListener('DOMContentLoaded', () => {
125
  if (successEdit) successEdit.addEventListener('click', () => {
126
  successModal.classList.remove('show');
127
  const pid = createdIdEl?.textContent?.trim() || localStorage.getItem('medicalChatbotPatientId');
128
- if (pid && /^\d{8}$/.test(pid)) {
 
 
 
 
 
129
  enableEditMode(pid);
130
  loadPatientIntoForm(pid);
131
  }
 
15
 
16
  function getPatientIdFromUrl() {
17
  const urlParams = new URLSearchParams(window.location.search);
18
+ // The API uses a generic string 'id', but we'll keep the client-side validation for now.
19
  const pidFromUrl = urlParams.get('patient_id');
20
+ return pidFromUrl;
 
21
  }
22
 
23
  async function loadPatientIntoForm(patientId) {
 
90
  });
91
  if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
92
  const data = await resp.json();
93
+ const pid = data.id;
94
  localStorage.setItem('medicalChatbotPatientId', pid);
95
 
96
  // Add to localStorage for future suggestions
97
  const existingPatients = JSON.parse(localStorage.getItem('medicalChatbotPatients') || '[]');
98
  const newPatient = {
99
+ id: pid,
100
  name: payload.name,
101
  age: payload.age,
102
  sex: payload.sex
103
  };
104
  // Check if patient already exists to avoid duplicates
105
+ const exists = existingPatients.some(p => p.id === pid);
106
  if (!exists) {
107
  existingPatients.push(newPatient);
108
  localStorage.setItem('medicalChatbotPatients', JSON.stringify(existingPatients));
 
125
  if (successEdit) successEdit.addEventListener('click', () => {
126
  successModal.classList.remove('show');
127
  const pid = createdIdEl?.textContent?.trim() || localStorage.getItem('medicalChatbotPatientId');
128
+ if (pid) {
129
+ // Update URL to reflect edit mode for clarity and reload safety
130
+ const newUrl = new URL(window.location.href);
131
+ newUrl.searchParams.set('patient_id', pid);
132
+ window.history.pushState({ path: newUrl.href }, '', newUrl.href);
133
+
134
  enableEditMode(pid);
135
  loadPatientIntoForm(pid);
136
  }
static/patient.html CHANGED
@@ -1,72 +1,72 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Create Patient</title>
7
- <link rel="stylesheet" href="/static/css/patient.css">
8
- <link rel="icon" type="image/svg+xml" href="/static/icon.svg">
9
- <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
10
  </head>
11
  <body>
12
- <div class="container">
13
- <a href="/" class="back-link"><i class="fas fa-arrow-left"></i> Back to Assistant</a>
14
- <h1>Create Patient</h1>
15
- <form id="patientForm">
16
- <div class="grid">
17
- <!-- Row 1: Name and Age -->
18
- <div class="row">
19
- <label>Name<input type="text" id="name" required></label>
20
- <label>Age<input type="number" id="age" min="0" max="130" required></label>
21
- </div>
22
- <!-- Row 2: Sex and Ethnicity -->
23
- <div class="row">
24
- <label>Sex
25
- <select id="sex" required>
26
- <option value="Male">Male</option>
27
- <option value="Female">Female</option>
28
- <option value="Intersex">Intersex</option>
29
- </select>
30
- </label>
31
- <label>Ethnicity<input type="text" id="ethnicity"></label>
32
- </div>
33
- <!-- Row 3: Contact Information -->
34
- <div class="row contact-info">
35
- <label>Phone<input type="tel" id="phone"></label>
36
- <label>Email<input type="email" id="email"></label>
37
- <label>Address<input type="text" id="address"></label>
38
- </div>
39
- <!-- Row 4: Medical Information -->
40
- <div class="row">
41
- <label>Active Medications<textarea id="medications" placeholder="One per line"></textarea></label>
42
- <label>Past Assessment Summary<textarea id="summary"></textarea></label>
43
- </div>
44
- </div>
45
- <div class="actions">
46
- <button type="button" id="cancelBtn" class="secondary">Cancel</button>
47
- <button type="submit" class="primary">Create</button>
48
- </div>
49
- </form>
50
- <div id="result" class="result"></div>
51
- </div>
52
- <!-- Success Modal -->
53
- <div class="modal" id="patientSuccessModal">
54
- <div class="modal-content">
55
- <div class="modal-header">
56
- <h3>Patient Created</h3>
57
- <button class="modal-close" id="patientSuccessClose">&times;</button>
58
- </div>
59
- <div class="modal-body">
60
- <p>Your new Patient ID is:</p>
61
- <div id="createdPatientId" class="big-id"></div>
62
- </div>
63
- <div class="modal-footer">
64
- <button class="secondary" id="patientSuccessReturn">Return to main page</button>
65
- <button class="primary" id="patientSuccessEdit">Edit patient profile</button>
66
- </div>
67
- </div>
68
- </div>
69
 
70
- <script src="/static/js/patient.js"></script>
71
  </body>
72
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Create Patient</title>
7
+ <link rel="stylesheet" href="/static/css/patient.css">
8
+ <link rel="icon" type="image/svg+xml" href="/static/icon.svg">
9
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
10
  </head>
11
  <body>
12
+ <div class="container">
13
+ <a href="/" class="back-link"><i class="fas fa-arrow-left"></i> Back to Assistant</a>
14
+ <h1>Create Patient</h1>
15
+ <form id="patientForm">
16
+ <div class="grid">
17
+ <!-- Row 1: Name and Age -->
18
+ <div class="row">
19
+ <label>Name<input type="text" id="name" required></label>
20
+ <label>Age<input type="number" id="age" min="0" max="130" required></label>
21
+ </div>
22
+ <!-- Row 2: Sex and Ethnicity -->
23
+ <div class="row">
24
+ <label>Sex
25
+ <select id="sex" required>
26
+ <option value="Male">Male</option>
27
+ <option value="Female">Female</option>
28
+ <option value="Intersex">Intersex</option>
29
+ </select>
30
+ </label>
31
+ <label>Ethnicity<input type="text" id="ethnicity"></label>
32
+ </div>
33
+ <!-- Row 3: Contact Information -->
34
+ <div class="row contact-info">
35
+ <label>Phone<input type="tel" id="phone"></label>
36
+ <label>Email<input type="email" id="email"></label>
37
+ <label>Address<input type="text" id="address"></label>
38
+ </div>
39
+ <!-- Row 4: Medical Information -->
40
+ <div class="row">
41
+ <label>Active Medications<textarea id="medications" placeholder="One per line"></textarea></label>
42
+ <label>Past Assessment Summary<textarea id="summary"></textarea></label>
43
+ </div>
44
+ </div>
45
+ <div class="actions">
46
+ <button type="button" id="cancelBtn" class="secondary">Cancel</button>
47
+ <button type="submit" class="primary">Create</button>
48
+ </div>
49
+ </form>
50
+ <div id="result" class="result"></div>
51
+ </div>
52
+ <!-- Success Modal -->
53
+ <div class="modal" id="patientSuccessModal">
54
+ <div class="modal-content">
55
+ <div class="modal-header">
56
+ <h3>Patient Created</h3>
57
+ <button class="modal-close" id="patientSuccessClose">&times;</button>
58
+ </div>
59
+ <div class="modal-body">
60
+ <p>Your new Patient ID is:</p>
61
+ <div id="createdPatientId" class="big-id"></div>
62
+ </div>
63
+ <div class="modal-footer">
64
+ <button class="secondary" id="patientSuccessReturn">Return to main page</button>
65
+ <button class="primary" id="patientSuccessEdit">Edit patient profile</button>
66
+ </div>
67
+ </div>
68
+ </div>
69
 
70
+ <script src="/static/js/patient.js"></script>
71
  </body>
72
  </html>