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 = `
${item.title || 'Untitled'}
${item.display_time || 'Unknown date'}
`; 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 } });