Spaces:
Sleeping
Sleeping
Download frontend/src/ConsultPanel.jsx from anil988/CodeBlue-Context: direct link, hf CLI and curl.
- Browser
- Download file 6.34 kB
-
https://huggingface.co/spaces/anil988/CodeBlue-Context/resolve/main/frontend/src/ConsultPanel.jsx
- Command line
-
hf download hf://spaces/anil988/CodeBlue-Context/frontend/src/ConsultPanel.jsx
-
curl -L -o ConsultPanel.jsx https://huggingface.co/spaces/anil988/CodeBlue-Context/resolve/main/frontend/src/ConsultPanel.jsx
6.34 kB
| import { useState } from 'react'; | |
| import { MessageSquare, Send, Bot, User, Zap, ShieldAlert, FlaskConical, Stethoscope } from 'lucide-react'; | |
| const API_BASE = import.meta.env.VITE_API_BASE || ''; | |
| export default function ConsultPanel({ patientId, previousResults }) { | |
| const [messages, setMessages] = useState([ | |
| { role: 'assistant', content: "Hello Doctor. I've synthesized the team's findings. You can now ask us specific clinical questions about this case.", type: 'initial' } | |
| ]); | |
| const [input, setInput] = useState(''); | |
| const [loading, setLoading] = useState(false); | |
| const sendMessage = async () => { | |
| if (!input.trim() || loading || !patientId) return; | |
| const userMsg = input.trim(); | |
| setMessages(prev => [...prev, { role: 'user', content: userMsg }]); | |
| setInput(''); | |
| setLoading(true); | |
| try { | |
| const geminiKey = localStorage.getItem('gemini_key'); | |
| const headers = { 'Content-Type': 'application/json' }; | |
| if (geminiKey) headers['x-gemini-key'] = geminiKey; | |
| const res = await fetch(`${API_BASE}/consult/${patientId}`, { | |
| method: 'POST', | |
| headers, | |
| body: JSON.stringify({ | |
| question: userMsg, | |
| previous_results: previousResults | |
| }), | |
| }); | |
| const data = await res.json(); | |
| const result = data.result; | |
| if (result) { | |
| setMessages(prev => [...prev, { | |
| role: 'assistant', | |
| content: result.answer, | |
| insights: result.specialist_insights, | |
| consensus: result.team_consensus | |
| }]); | |
| } else { | |
| throw new Error('No result'); | |
| } | |
| } catch (err) { | |
| setMessages(prev => [...prev, { | |
| role: 'assistant', | |
| content: "I'm having trouble reaching the clinical AI team. Please ensure you've provided a Gemini API Key in the settings (top right).", | |
| insights: [ | |
| {"agent": "System", "note": "Connection required for advanced reasoning."} | |
| ] | |
| }]); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div style={{ display: 'flex', flexDirection: 'column', height: '100%', background: 'var(--bg-surface)' }}> | |
| <div style={{ padding: '14px 16px', borderBottom: '1px solid var(--border-subtle)', background: 'var(--bg-surface)' }}> | |
| <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> | |
| <Bot size={18} color="var(--purple-ai)" /> | |
| <span style={{ fontWeight: 700, fontSize: 13, color: 'var(--text-primary)' }}>Multi-Agent Consultation</span> | |
| </div> | |
| </div> | |
| <div style={{ flex: 1, overflowY: 'auto', padding: '16px', display: 'flex', flexDirection: 'column', gap: 16 }}> | |
| {messages.map((m, i) => ( | |
| <div key={i} style={{ | |
| alignSelf: m.role === 'user' ? 'flex-end' : 'flex-start', | |
| maxWidth: '85%', | |
| display: 'flex', gap: 10, | |
| flexDirection: m.role === 'user' ? 'row-reverse' : 'row' | |
| }}> | |
| <div style={{ | |
| width: 28, height: 28, borderRadius: '50%', | |
| background: m.role === 'user' ? 'var(--blue-accent)' : 'var(--purple-ai)15', | |
| display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, | |
| color: m.role === 'user' ? '#fff' : 'var(--purple-ai)' | |
| }}> | |
| {m.role === 'user' ? <User size={14} /> : <Bot size={14} />} | |
| </div> | |
| <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}> | |
| <div style={{ | |
| padding: '10px 14px', borderRadius: 12, | |
| background: m.role === 'user' ? 'var(--blue-accent)' : 'var(--bg-base)', | |
| color: m.role === 'user' ? '#fff' : 'var(--text-primary)', | |
| fontSize: 12, lineHeight: 1.5, | |
| boxShadow: m.role === 'user' ? '0 2px 8px rgba(2, 132, 199, 0.2)' : 'none' | |
| }}> | |
| {m.content} | |
| </div> | |
| {m.insights && ( | |
| <div style={{ display: 'flex', flexDirection: 'column', gap: 4, marginTop: 4 }}> | |
| {m.insights.map((ins, j) => ( | |
| <div key={j} style={{ | |
| fontSize: 10, padding: '6px 8px', borderRadius: 6, | |
| background: 'var(--bg-surface)', border: '1px solid var(--border-subtle)', | |
| color: 'var(--text-secondary)', display: 'flex', gap: 6 | |
| }}> | |
| <span style={{ fontWeight: 800, color: 'var(--purple-ai)' }}>{ins.agent}:</span> | |
| <span>{ins.note}</span> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| ))} | |
| {loading && ( | |
| <div style={{ alignSelf: 'flex-start', display: 'flex', gap: 10 }}> | |
| <div className="spinner" style={{ width: 14, height: 14 }} /> | |
| <span style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>Consulting agents...</span> | |
| </div> | |
| )} | |
| </div> | |
| <div style={{ padding: 16, borderTop: '1px solid var(--border-subtle)' }}> | |
| <div style={{ position: 'relative' }}> | |
| <input | |
| type="text" | |
| placeholder="Ask the team a question..." | |
| value={input} | |
| onChange={e => setInput(e.target.value)} | |
| onKeyDown={e => e.key === 'Enter' && sendMessage()} | |
| style={{ | |
| width: '100%', padding: '12px 45px 12px 14px', borderRadius: 10, | |
| border: '1px solid var(--border-default)', outline: 'none', | |
| fontSize: 12, transition: 'all 0.2s', | |
| background: 'var(--bg-base)' | |
| }} | |
| /> | |
| <button | |
| onClick={sendMessage} | |
| disabled={loading || !input.trim()} | |
| style={{ | |
| position: 'absolute', right: 8, top: '50%', transform: 'translateY(-50%)', | |
| background: 'var(--blue-accent)', color: '#fff', border: 'none', | |
| width: 28, height: 28, borderRadius: 6, display: 'flex', alignItems: 'center', justifyContent: 'center', | |
| cursor: 'pointer', opacity: (loading || !input.trim()) ? 0.5 : 1 | |
| }} | |
| > | |
| <Send size={14} /> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |