Spaces:
Sleeping
Sleeping
Commit
·
a84ab49
1
Parent(s):
bb7dc3e
Upd 3 modal handlers for doctor, patient and settings
Browse files- src/data/mongodb.py +20 -11
- static/css/styles.css +21 -0
- static/js/app.js +37 -10
src/data/mongodb.py
CHANGED
|
@@ -424,17 +424,26 @@ def search_patients(query: str, limit: int = 10) -> list[dict[str, Any]]:
|
|
| 424 |
collection = get_collection(PATIENTS_COLLECTION)
|
| 425 |
if not query:
|
| 426 |
return []
|
|
|
|
|
|
|
|
|
|
| 427 |
# Build a regex for name search and patient_id partial match
|
| 428 |
import re
|
| 429 |
pattern = re.compile(re.escape(query), re.IGNORECASE)
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
collection = get_collection(PATIENTS_COLLECTION)
|
| 425 |
if not query:
|
| 426 |
return []
|
| 427 |
+
|
| 428 |
+
logger.info(f"Searching patients with query: '{query}', limit: {limit}")
|
| 429 |
+
|
| 430 |
# Build a regex for name search and patient_id partial match
|
| 431 |
import re
|
| 432 |
pattern = re.compile(re.escape(query), re.IGNORECASE)
|
| 433 |
+
|
| 434 |
+
try:
|
| 435 |
+
cursor = collection.find({
|
| 436 |
+
"$or": [
|
| 437 |
+
{"name": {"$regex": pattern}},
|
| 438 |
+
{"patient_id": {"$regex": pattern}}
|
| 439 |
+
]
|
| 440 |
+
}).sort("name", ASCENDING).limit(limit)
|
| 441 |
+
results = []
|
| 442 |
+
for p in cursor:
|
| 443 |
+
p["_id"] = str(p.get("_id")) if p.get("_id") else None
|
| 444 |
+
results.append(p)
|
| 445 |
+
logger.info(f"Found {len(results)} patients matching query")
|
| 446 |
+
return results
|
| 447 |
+
except Exception as e:
|
| 448 |
+
logger.error(f"Error in search_patients: {e}")
|
| 449 |
+
return []
|
static/css/styles.css
CHANGED
|
@@ -757,7 +757,10 @@ body {
|
|
| 757 |
left: 0;
|
| 758 |
top: 0;
|
| 759 |
height: 100vh;
|
|
|
|
| 760 |
transform: translateX(-100%);
|
|
|
|
|
|
|
| 761 |
}
|
| 762 |
|
| 763 |
.sidebar.show {
|
|
@@ -772,6 +775,24 @@ body {
|
|
| 772 |
font-size: 1rem;
|
| 773 |
}
|
| 774 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 775 |
.modal-content {
|
| 776 |
width: 95%;
|
| 777 |
margin: var(--spacing-md);
|
|
|
|
| 757 |
left: 0;
|
| 758 |
top: 0;
|
| 759 |
height: 100vh;
|
| 760 |
+
width: 300px;
|
| 761 |
transform: translateX(-100%);
|
| 762 |
+
transition: transform 0.3s ease;
|
| 763 |
+
z-index: 1000;
|
| 764 |
}
|
| 765 |
|
| 766 |
.sidebar.show {
|
|
|
|
| 775 |
font-size: 1rem;
|
| 776 |
}
|
| 777 |
|
| 778 |
+
.app-overlay {
|
| 779 |
+
position: fixed;
|
| 780 |
+
top: 0;
|
| 781 |
+
left: 0;
|
| 782 |
+
width: 100%;
|
| 783 |
+
height: 100%;
|
| 784 |
+
background: rgba(0, 0, 0, 0.5);
|
| 785 |
+
z-index: 999;
|
| 786 |
+
display: none;
|
| 787 |
+
opacity: 0;
|
| 788 |
+
transition: opacity 0.3s ease;
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
.app-overlay.show {
|
| 792 |
+
display: block;
|
| 793 |
+
opacity: 1;
|
| 794 |
+
}
|
| 795 |
+
|
| 796 |
.modal-content {
|
| 797 |
width: 95%;
|
| 798 |
margin: var(--spacing-md);
|
static/js/app.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
// Medical AI Assistant - Main Application JavaScript
|
|
|
|
| 2 |
|
| 3 |
// TEMPORARILY DISABLED SUBMODULES
|
| 4 |
// import { attachUIHandlers } from './ui/handlers.js';
|
|
@@ -465,8 +466,13 @@ How can I assist you today?`;
|
|
| 465 |
sel.appendChild(createOpt);
|
| 466 |
}
|
| 467 |
if (sel && !sel.value) sel.value = this.currentUser?.name || '__create__';
|
| 468 |
-
|
| 469 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 470 |
this.showModal('userModal');
|
| 471 |
}
|
| 472 |
|
|
@@ -489,18 +495,29 @@ How can I assist you today?`;
|
|
| 489 |
|
| 490 |
setTheme(theme) {
|
| 491 |
const root = document.documentElement;
|
|
|
|
| 492 |
if (theme === 'auto') {
|
| 493 |
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
| 494 |
root.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
|
|
|
|
| 495 |
} else {
|
| 496 |
root.setAttribute('data-theme', theme);
|
|
|
|
| 497 |
}
|
|
|
|
|
|
|
|
|
|
| 498 |
}
|
| 499 |
|
| 500 |
setFontSize(size) {
|
| 501 |
const root = document.documentElement;
|
| 502 |
const sizes = { small: '14px', medium: '16px', large: '18px' };
|
| 503 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
}
|
| 505 |
|
| 506 |
setupTheme() {
|
|
@@ -571,7 +588,7 @@ How can I assist you today?`;
|
|
| 571 |
this.currentSession = { ...session };
|
| 572 |
await this.hydrateMessagesForSession(session.id);
|
| 573 |
} else {
|
| 574 |
-
|
| 575 |
}
|
| 576 |
});
|
| 577 |
const time = this.formatTime(session.lastActivity);
|
|
@@ -591,10 +608,10 @@ How can I assist you today?`;
|
|
| 591 |
sessionsContainer.appendChild(sessionElement);
|
| 592 |
const menuBtn = sessionElement.querySelector('.chat-session-menu');
|
| 593 |
if (session.source !== 'backend') {
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
} else {
|
| 599 |
menuBtn.disabled = true;
|
| 600 |
menuBtn.style.opacity = 0.5;
|
|
@@ -938,14 +955,24 @@ How can I assist you today?`;
|
|
| 938 |
debounceTimer = setTimeout(async () => {
|
| 939 |
try {
|
| 940 |
console.log('[DEBUG] Searching patients with query:', q);
|
| 941 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 942 |
console.log('[DEBUG] Search response status:', resp.status);
|
|
|
|
| 943 |
if (resp.ok) {
|
| 944 |
const data = await resp.json();
|
| 945 |
console.log('[DEBUG] Search results:', data);
|
| 946 |
renderSuggestions(data.results || []);
|
| 947 |
} else {
|
| 948 |
-
|
|
|
|
| 949 |
}
|
| 950 |
} catch (e) {
|
| 951 |
console.error('[DEBUG] Search error:', e);
|
|
|
|
| 1 |
// Medical AI Assistant - Main Application JavaScript
|
| 2 |
+
// static/js/app.js
|
| 3 |
|
| 4 |
// TEMPORARILY DISABLED SUBMODULES
|
| 5 |
// import { attachUIHandlers } from './ui/handlers.js';
|
|
|
|
| 466 |
sel.appendChild(createOpt);
|
| 467 |
}
|
| 468 |
if (sel && !sel.value) sel.value = this.currentUser?.name || '__create__';
|
| 469 |
+
|
| 470 |
+
// Safely set role and specialty with null checks
|
| 471 |
+
const roleEl = document.getElementById('profileRole');
|
| 472 |
+
const specialtyEl = document.getElementById('profileSpecialty');
|
| 473 |
+
if (roleEl) roleEl.value = this.currentUser?.role || 'Medical Professional';
|
| 474 |
+
if (specialtyEl) specialtyEl.value = this.currentUser?.specialty || '';
|
| 475 |
+
|
| 476 |
this.showModal('userModal');
|
| 477 |
}
|
| 478 |
|
|
|
|
| 495 |
|
| 496 |
setTheme(theme) {
|
| 497 |
const root = document.documentElement;
|
| 498 |
+
console.log('[Theme] Setting theme to:', theme);
|
| 499 |
if (theme === 'auto') {
|
| 500 |
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
| 501 |
root.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
|
| 502 |
+
console.log('[Theme] Auto theme applied:', prefersDark ? 'dark' : 'light');
|
| 503 |
} else {
|
| 504 |
root.setAttribute('data-theme', theme);
|
| 505 |
+
console.log('[Theme] Manual theme applied:', theme);
|
| 506 |
}
|
| 507 |
+
// Force a re-render by toggling a class
|
| 508 |
+
root.classList.add('theme-updated');
|
| 509 |
+
setTimeout(() => root.classList.remove('theme-updated'), 100);
|
| 510 |
}
|
| 511 |
|
| 512 |
setFontSize(size) {
|
| 513 |
const root = document.documentElement;
|
| 514 |
const sizes = { small: '14px', medium: '16px', large: '18px' };
|
| 515 |
+
const fontSize = sizes[size] || '16px';
|
| 516 |
+
console.log('[Font] Setting font size to:', fontSize);
|
| 517 |
+
root.style.fontSize = fontSize;
|
| 518 |
+
// Force a re-render
|
| 519 |
+
root.classList.add('font-updated');
|
| 520 |
+
setTimeout(() => root.classList.remove('font-updated'), 100);
|
| 521 |
}
|
| 522 |
|
| 523 |
setupTheme() {
|
|
|
|
| 588 |
this.currentSession = { ...session };
|
| 589 |
await this.hydrateMessagesForSession(session.id);
|
| 590 |
} else {
|
| 591 |
+
this.loadChatSession(session.id);
|
| 592 |
}
|
| 593 |
});
|
| 594 |
const time = this.formatTime(session.lastActivity);
|
|
|
|
| 608 |
sessionsContainer.appendChild(sessionElement);
|
| 609 |
const menuBtn = sessionElement.querySelector('.chat-session-menu');
|
| 610 |
if (session.source !== 'backend') {
|
| 611 |
+
menuBtn.addEventListener('click', (e) => {
|
| 612 |
+
e.stopPropagation();
|
| 613 |
+
this.showSessionMenu(e.currentTarget, session.id);
|
| 614 |
+
});
|
| 615 |
} else {
|
| 616 |
menuBtn.disabled = true;
|
| 617 |
menuBtn.style.opacity = 0.5;
|
|
|
|
| 955 |
debounceTimer = setTimeout(async () => {
|
| 956 |
try {
|
| 957 |
console.log('[DEBUG] Searching patients with query:', q);
|
| 958 |
+
const url = `/patients/search?q=${encodeURIComponent(q)}&limit=8`;
|
| 959 |
+
console.log('[DEBUG] Search URL:', url);
|
| 960 |
+
const resp = await fetch(url, {
|
| 961 |
+
method: 'GET',
|
| 962 |
+
headers: {
|
| 963 |
+
'Accept': 'application/json',
|
| 964 |
+
'Content-Type': 'application/json'
|
| 965 |
+
}
|
| 966 |
+
});
|
| 967 |
console.log('[DEBUG] Search response status:', resp.status);
|
| 968 |
+
console.log('[DEBUG] Search response headers:', resp.headers);
|
| 969 |
if (resp.ok) {
|
| 970 |
const data = await resp.json();
|
| 971 |
console.log('[DEBUG] Search results:', data);
|
| 972 |
renderSuggestions(data.results || []);
|
| 973 |
} else {
|
| 974 |
+
const errorText = await resp.text();
|
| 975 |
+
console.warn('Search request failed', resp.status, errorText);
|
| 976 |
}
|
| 977 |
} catch (e) {
|
| 978 |
console.error('[DEBUG] Search error:', e);
|