Swift-Query / static /script.js
Darshan Kumar R
Deploy to Hugging Face Spaces (Clean)
ed0b977
Raw History Blame Contribute Delete
15.7 kB
document.addEventListener("DOMContentLoaded", () => {
// --- Element Selectors ---
const userInput = document.getElementById("user-input");
const sendButton = document.getElementById("send-button");
const chatHistory = document.getElementById("chat-history");
const menuItems = document.querySelectorAll(".menu-item");
const convoRecord = document.getElementById("convo-record");
// Modal Elements
const patientModal = document.getElementById("patient-modal");
const patientForm = document.getElementById("patient-form");
const patientIdInput = document.getElementById("patient-id-input");
// Track session for backend context
let currentSessionId = window.activeSessionId || null;
let similarSearchActive = false; // --- NEW: Toggle state for similar search
// --- NEW: Function to control chat input state ---
function setChatInputDisabled(disabled) {
if (userInput) {
userInput.disabled = disabled;
userInput.placeholder = disabled ? "Please start a patient session first..." : "Ask Swift Query AI...";
}
if (sendButton) {
sendButton.disabled = disabled;
sendButton.classList.toggle('bg-gray-400', disabled);
sendButton.classList.toggle('bg-primary', !disabled);
sendButton.classList.toggle('hover:bg-orange-600', !disabled);
}
}
// --- Feature Handlers (MODIFIED) ---
const featureHandlers = {
"chat-feature": () => {
if (similarSearchActive) {
// If similar search was active, turn it off
toggleSimilarSearch(document.getElementById('similar-cases-feature'));
}
if (userInput) userInput.focus();
},
"similar-cases-feature": (event) => {
if (!window.activePatientId) {
appendMessage("Please start a patient session to find similar cases.", "ai", new Date());
return;
}
// NEW: Toggle the similar search mode
toggleSimilarSearch(event.currentTarget);
},
"patient-search-feature": () => {
if (patientModal) {
patientModal.classList.remove("hidden");
if (patientIdInput) setTimeout(() => patientIdInput.focus(), 100);
}
},
"clear-chat-feature": () => {
const greeting = chatHistory.firstChild;
chatHistory.innerHTML = '';
if(greeting) chatHistory.appendChild(greeting);
appendMessage("Local chat view cleared.", "ai", new Date());
},
"history-feature": () => {
loadHistoryList();
}
};
// --- NEW: Toggle function for Similar Search ---
function toggleSimilarSearch(buttonElement) {
similarSearchActive = !similarSearchActive;
if (!userInput || !buttonElement) return;
if (similarSearchActive) {
userInput.value = "/similar ";
userInput.focus();
buttonElement.classList.add("active"); // Add visual indicator
appendMessage("**Similar Case Search Activated.**\nPlease describe the symptoms or conditions to find matching cases.", "ai", new Date());
} else {
if (userInput.value.startsWith("/similar ")) {
userInput.value = "";
}
buttonElement.classList.remove("active");
appendMessage("**Similar Case Search Deactivated.**", "ai", new Date());
}
}
// --- Event Listeners ---
menuItems.forEach(item => {
if (item.tagName === 'A' && !item.href.includes('logout')) {
item.addEventListener("click", (e) => {
if (featureHandlers[item.id]) {
e.preventDefault();
// Don't deactivate similar search button when it's clicked
if (item.id !== 'similar-cases-feature') {
const activeItem = document.querySelector(".menu-item.active");
if (activeItem) activeItem.classList.remove("active");
item.classList.add("active");
}
featureHandlers[item.id](e);
}
});
}
});
if (patientForm) {
patientForm.addEventListener("submit", (e) => {
e.preventDefault();
const pid = patientIdInput.value.trim();
if (pid) {
if (patientModal) patientModal.classList.add("hidden");
startPatientSession(pid);
}
});
}
// --- Recording Logic ---
let mediaRecorder = null;
let audioChunks = [];
window.startRecording = async function () {
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) return;
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) audioChunks.push(event.data);
};
mediaRecorder.onstop = async () => {
const audioBlob = new Blob(audioChunks, { type: 'audio/mp3' });
appendMessage("Processing recording... ⏳", "ai", new Date());
const formData = new FormData();
formData.append("audio", audioBlob, "recording.mp3");
formData.append("patient_id", window.activePatientId);
try {
const response = await fetch("/doctor/upload_recording", { method: "POST", body: formData });
const result = await response.json();
if (result.success) {
appendMessage(`### 🎙️ Recording Saved\n\n**Processed Note:**\n${result.data.processed_note}`, "ai", new Date());
} else {
appendMessage(`❌ Recording failed: ${result.error}`, "ai", new Date());
}
} catch (err) {
console.error(err);
appendMessage("❌ Error uploading recording.", "ai", new Date());
}
stream.getTracks().forEach(track => track.stop());
};
mediaRecorder.start();
appendMessage("🎙️ **Recording Started...**", "ai", new Date());
if (convoRecord) convoRecord.checked = true;
} catch (err) {
console.error("Microphone access denied:", err);
if (convoRecord) convoRecord.checked = false;
alert("Microphone access is required to record.");
}
};
window.stopRecording = function () {
if (mediaRecorder && mediaRecorder.state !== "inactive") {
mediaRecorder.stop();
appendMessage("⏹️ **Recording Stopped.** Processing...", "ai", new Date());
if (convoRecord) convoRecord.checked = false;
}
};
if (convoRecord) {
convoRecord.addEventListener("change", async () => {
if (!window.activePatientId) {
convoRecord.checked = false;
alert("Please start a patient session before starting a recording.");
return;
}
if (convoRecord.checked) await window.startRecording();
else window.stopRecording();
});
}
// --- Core Chat Functions ---
if (sendButton) sendButton.addEventListener("click", sendMessage);
if (userInput) {
userInput.addEventListener("keypress", (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
userInput.addEventListener("input", function () {
this.style.height = "50px";
this.style.height = `${Math.min(this.scrollHeight, 200)}px`;
});
}
function startPatientSession(patientId) {
setChatInputDisabled(true);
if (chatHistory) chatHistory.innerHTML = '';
appendMessage(`Starting session for Patient ID: ${patientId}... This may take a moment.`, "ai", new Date());
fetch(`/doctor/start_session/${patientId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
})
.then(res => res.json())
.then(data => {
if (data.success) {
appendMessage(`Session for patient ${data.patient_id} is ready. Reloading dashboard...`, "ai", new Date());
setTimeout(() => window.location.reload(), 500);
} else {
console.error("Error starting session:", data.error);
appendMessage(`Failed to start session: ${data.error}`, "ai", new Date());
setChatInputDisabled(false);
}
})
.catch(err => {
console.error("Network error starting session:", err);
appendMessage("A network error occurred while starting the session.", "ai", new Date());
setChatInputDisabled(false);
});
}
function loadHistoryList() {
chatHistory.innerHTML = '';
appendMessage("Loading history sessions...", "ai", new Date());
fetch("/api/history")
.then(res => res.json())
.then(data => {
chatHistory.innerHTML = '';
if (data.history && data.history.length > 0) {
appendMessage("### Select a Past Session to Resume:", "ai", new Date());
const list = document.createElement("div");
list.className = "flex flex-col gap-2 mt-2";
data.history.forEach(item => {
const btn = document.createElement("button");
btn.className = "bg-white p-3 rounded-lg shadow-sm hover:bg-orange-50 text-left border border-gray-200 transition-colors w-full group";
btn.innerHTML = `<div class='font-bold text-primary flex justify-between'><span>${item.title || 'Untitled'}</span><i class="fas fa-chevron-right text-gray-300 group-hover:text-primary transition-colors"></i></div><div class='text-xs text-gray-500 mt-1'><i class="far fa-clock mr-1"></i> ${item.display_time || 'Unknown date'}</div>`;
btn.onclick = () => window.loadHistoryItem(item.type || 'chat', item.session_id);
list.appendChild(btn);
});
const container = document.createElement("div");
container.className = "w-full max-w-lg mb-4";
container.appendChild(list);
chatHistory.appendChild(container);
chatHistory.scrollTop = chatHistory.scrollHeight;
} else {
appendMessage("No past sessions found.", "ai", new Date());
}
})
.catch(err => {
console.error(err);
appendMessage("Failed to load history.", "ai", new Date());
});
}
window.loadHistoryItem = function (type, id) {
chatHistory.innerHTML = '';
appendMessage("Restoring...", "ai", new Date());
currentSessionId = (type === 'chat') ? id : null;
fetch(`/api/history/${type}/${id}`)
.then(res => res.json())
.then(data => {
chatHistory.innerHTML = '';
if (data.messages && data.messages.length > 0) {
data.messages.forEach(msg => appendMessage(msg.content, msg.sender, msg.timestamp || new Date()));
if (type === 'chat') appendMessage("--- Session Restored ---", "ai", new Date());
} else {
appendMessage("Item is empty.", "ai", new Date());
}
})
.catch(err => {
console.error(err);
appendMessage("Failed to restore item.", "ai", new Date());
});
};
function sendMessage() {
if (!userInput || userInput.disabled) return;
const message = userInput.value.trim();
if (message === "") return;
appendMessage(message, "user", new Date());
userInput.value = "";
userInput.style.height = "50px";
fetchAIResponse(message);
// NEW: Deactivate similar search after sending the query
if (similarSearchActive) {
toggleSimilarSearch(document.getElementById('similar-cases-feature'));
}
}
async function fetchAIResponse(message) {
if (!window.activePatientId || !currentSessionId) {
appendMessage("Cannot send message. No active patient session.", "ai", new Date());
return;
}
try {
const response = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: message, session_id: currentSessionId }),
});
const data = await response.json();
appendMessage(data.response, "ai", new Date());
} catch (error) {
console.error("Error fetching AI response:", error);
appendMessage("Sorry, I'm having trouble connecting to the AI service.", "ai", new Date());
}
}
function appendMessage(message, sender, timestamp) {
if (!chatHistory) return;
const messageWrapper = document.createElement("div");
messageWrapper.classList.add("flex", "flex-col", sender === "user" ? "items-end" : "items-start", "mb-4", "animate-fade-in");
const bubble = document.createElement("div");
bubble.classList.add("rounded-lg", "p-3", "max-w-lg", "shadow-sm", "text-sm", "md:text-base");
if (sender === "user") {
bubble.classList.add("bg-primary", "text-white");
bubble.innerText = message;
} else {
bubble.classList.add("bg-white", "text-text-dark", "prose", "prose-orange", "prose-sm");
const rawHtml = marked.parse(message);
bubble.innerHTML = DOMPurify.sanitize(rawHtml);
}
let timeStr = "";
try {
if (timestamp instanceof Date) {
timeStr = timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
} else if (typeof timestamp === 'string') {
timeStr = new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}
} catch (e) { /* Ignore */ }
const meta = document.createElement("div");
meta.classList.add("text-xs", "text-gray-400", "mt-1", "mx-1");
meta.innerText = timeStr;
messageWrapper.appendChild(bubble);
messageWrapper.appendChild(meta);
chatHistory.appendChild(messageWrapper);
chatHistory.scrollTop = chatHistory.scrollHeight;
}
// --- INITIALIZATION on Page Load ---
if (window.activePatientId) {
console.log(`Session active for patient ${window.activePatientId}`);
setChatInputDisabled(false); // ENABLE chat
if (userInput) userInput.focus();
if (window.activeSessionId && chatHistory.children.length <= 1) {
console.log("Loading active session history:", window.activeSessionId);
loadHistoryItem('chat', window.activeSessionId);
}
} else {
console.log("No active patient session.");
setChatInputDisabled(true); // DISABLE chat by default
}
});