From f0e315e787362a89c36190ba40fd739f5fd7d040 Mon Sep 17 00:00:00 2001 From: AndrewVFranco <129307231+AndrewVFranco@users.noreply.github.com> Date: Mon, 20 Apr 2026 22:47:04 -0700 Subject: [PATCH 1/4] Update frontend to include sample questions --- frontend/src/App.css | 7 +++- frontend/src/App.js | 24 +++++++++--- frontend/src/components/ChatWindow.js | 55 +++++++++++++++++++++++++-- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index ae21b79..605d14d 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -425,12 +425,15 @@ aside.sidebar { /* ERROR */ .message-error .bubble { - background: #fef2f2; - border: 1px solid #fecaca; + background: var(--contradicted-bg); + border: 1px solid var(--contradicted-border); color: var(--contradicted); padding: 10px 14px; border-radius: 8px; font-size: 13px; + display: flex; + align-items: flex-start; + gap: 8px; } /* LOADING */ diff --git a/frontend/src/App.js b/frontend/src/App.js index 5532d6d..f8947d2 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -4,6 +4,8 @@ import Sidebar from './components/Sidebar'; import ChatWindow from './components/ChatWindow'; import './App.css'; +const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000'; + function App() { const [query, setQuery] = useState(''); const [apiKey, setApiKey] = useState(''); @@ -40,7 +42,7 @@ function App() { setResult(null); try { - const response = await fetch('http://localhost:8000/query', { + const response = await fetch(`${API_URL}/query`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: userMessage.text, api_key: apiKey, ...fhirData }), @@ -48,14 +50,26 @@ function App() { if (!response.ok) throw new Error('Query failed'); const data = await response.json(); + console.log('full response:', data); + console.log('final_response:', data.final_response); const final = data.final_response; setResult(final); setMessages(prev => [...prev, { role: 'assistant', data: final }]); } catch (err) { - setMessages(prev => [...prev, { - role: 'error', - text: 'Failed to connect to the analysis server. Ensure the backend is running.' - }]); + let errorText = 'Failed to connect to the analysis server. Ensure the backend is running.'; + + if (err.message === 'Query failed') { + errorText = 'The server returned an error. Check your API key or try a different query.'; + } else if (err.message.includes('fetch')) { + errorText = 'Cannot reach the backend server. Ensure it is running on port 8000.'; + } else if (err.message.includes('quota') || err.message.includes('429')) { + errorText = 'API quota exhausted. Please provide your own Gemini API key.'; + } + + setMessages(prev => [...prev, { + role: 'error', + text: errorText + }]); } finally { setLoading(false); } diff --git a/frontend/src/components/ChatWindow.js b/frontend/src/components/ChatWindow.js index 5e66a51..a07aa5d 100644 --- a/frontend/src/components/ChatWindow.js +++ b/frontend/src/components/ChatWindow.js @@ -5,16 +5,47 @@ import DrugCarousel from './DrugCarousel'; const FHIR_RESOURCE_TYPES = ['Condition', 'MedicationRequest', 'DiagnosticReport']; +const SAMPLE_PLACEHOLDERS = [ + 'e.g. What are the evidence-based treatments for heart failure?', + 'e.g. Are there any contraindications for prescribing Tylenol?', + 'e.g. Summarize the latest clinical guidelines for managing Type 2 Diabetes.', + 'e.g. What is the NNT for statins in primary prevention?' +]; + function ChatWindow({ messages, query, onQueryChange, onSubmit, loading }) { const bottomRef = useRef(null); + const textareaRef = useRef(null); const [showFhir, setShowFhir] = useState(false); const [fhirType, setFhirType] = useState('Condition'); const [fhirId, setFhirId] = useState(''); + // Track which placeholder is currently active + const [placeholderIndex, setPlaceholderIndex] = useState(0); + + // Swap the placeholder every 3.5 seconds + useEffect(() => { + // Stop the carousel if a message has already been sent + if (messages.length > 0) return; + + const timer = setInterval(() => { + setPlaceholderIndex((prev) => (prev + 1) % SAMPLE_PLACEHOLDERS.length); + }, 5000); // 5 seconds + + return () => clearInterval(timer); + }, [messages.length]); + useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages, loading]); + useEffect(() => { + const ta = textareaRef.current; + if (ta) { + ta.style.height = 'auto'; + ta.style.height = `${Math.min(ta.scrollHeight, 120)}px`; + } + }, [query]); + const handleKeyDown = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); @@ -33,10 +64,20 @@ function ChatWindow({ messages, query, onQueryChange, onSubmit, loading }) { fhir_resource_id: null, }; onSubmit(fhirData); + if (textareaRef.current) { + textareaRef.current.style.height = 'auto'; + } }; const canSubmit = query.trim() || (showFhir && fhirId.trim()); + // Determine what the current placeholder should be + const currentPlaceholder = showFhir + ? 'Optional: add a specific question about this resource…' + : messages.length > 0 + ? 'Ask another question...' + : SAMPLE_PLACEHOLDERS[placeholderIndex]; + return (
@@ -44,7 +85,7 @@ function ChatWindow({ messages, query, onQueryChange, onSubmit, loading }) {

Ask a clinical question

- The system retrieves PubMed abstracts and verifies each claim + The system retrieves openFDA and PubMed abstracts and verifies each claim against medical literature using NLI scoring. Optionally attach a FHIR resource for structured clinical context.

@@ -63,12 +104,16 @@ function ChatWindow({ messages, query, onQueryChange, onSubmit, loading }) { if (msg.role === 'error') { return (
-
{msg.text}
+
+ + {msg.text} +
); } if (msg.role === 'assistant') { + if (!msg.data) return null; return (
@@ -179,12 +224,14 @@ function ChatWindow({ messages, query, onQueryChange, onSubmit, loading }) { ⬡