|
|
import React, { useState, useEffect } from 'react'; |
|
|
import DocumentUpload from './components/DocumentUpload'; |
|
|
import DocumentList from './components/DocumentList'; |
|
|
import ChatInterface from './components/ChatInterface'; |
|
|
import DebugConsole from './components/DebugConsole'; |
|
|
import './App.css'; |
|
|
|
|
|
function App() { |
|
|
const [documents, setDocuments] = useState([]); |
|
|
const [selectedDocument, setSelectedDocument] = useState(null); |
|
|
const [isBackendReady, setIsBackendReady] = useState(false); |
|
|
const [ws, setWs] = useState(null); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
const connectWebSocket = () => { |
|
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; |
|
|
|
|
|
|
|
|
let wsUrl; |
|
|
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { |
|
|
|
|
|
wsUrl = `${protocol}//localhost:8000/ws`; |
|
|
} else { |
|
|
|
|
|
wsUrl = `${protocol}//${window.location.host}/ws`; |
|
|
} |
|
|
|
|
|
console.log('Tentative de connexion WebSocket:', wsUrl); |
|
|
|
|
|
const websocket = new WebSocket(wsUrl); |
|
|
|
|
|
websocket.onopen = () => { |
|
|
console.log('WebSocket connecté sur:', wsUrl); |
|
|
setWs(websocket); |
|
|
}; |
|
|
|
|
|
websocket.onmessage = (event) => { |
|
|
console.log('Message WebSocket reçu dans App.js:', event.data); |
|
|
try { |
|
|
const data = JSON.parse(event.data); |
|
|
console.log('Message parsé:', data); |
|
|
} catch (e) { |
|
|
console.log('Message brut (non-JSON):', event.data); |
|
|
} |
|
|
}; |
|
|
|
|
|
websocket.onclose = () => { |
|
|
console.log('WebSocket fermé, tentative de reconnexion...'); |
|
|
setWs(null); |
|
|
setTimeout(connectWebSocket, 3000); |
|
|
}; |
|
|
|
|
|
websocket.onerror = (error) => { |
|
|
console.error('Erreur WebSocket:', error); |
|
|
}; |
|
|
}; |
|
|
|
|
|
connectWebSocket(); |
|
|
|
|
|
return () => { |
|
|
if (ws) { |
|
|
ws.close(); |
|
|
} |
|
|
}; |
|
|
}, []); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
const checkBackendHealth = async () => { |
|
|
try { |
|
|
const response = await fetch('/api/health'); |
|
|
setIsBackendReady(response.ok); |
|
|
} catch (error) { |
|
|
console.error('Backend non disponible:', error); |
|
|
setIsBackendReady(false); |
|
|
} |
|
|
}; |
|
|
|
|
|
checkBackendHealth(); |
|
|
const interval = setInterval(checkBackendHealth, 10000); |
|
|
return () => clearInterval(interval); |
|
|
}, []); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
loadDocuments(); |
|
|
}, []); |
|
|
|
|
|
const loadDocuments = async () => { |
|
|
try { |
|
|
const response = await fetch('/api/documents'); |
|
|
if (response.ok) { |
|
|
const data = await response.json(); |
|
|
|
|
|
const docs = Array.isArray(data) ? data : (data.documents && Array.isArray(data.documents) ? data.documents : []); |
|
|
setDocuments(docs); |
|
|
} else { |
|
|
console.warn('Erreur HTTP lors du chargement des documents:', response.status); |
|
|
setDocuments([]); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Erreur chargement documents:', error); |
|
|
setDocuments([]); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleDocumentUploaded = (newDoc) => { |
|
|
setDocuments(prev => [...prev, newDoc]); |
|
|
loadDocuments(); |
|
|
}; |
|
|
|
|
|
const handleDocumentAnalyzed = (documentId) => { |
|
|
loadDocuments(); |
|
|
}; |
|
|
|
|
|
const handleDocumentSelected = (doc) => { |
|
|
setSelectedDocument(doc); |
|
|
}; |
|
|
|
|
|
const handleDocumentDeleted = (documentId) => { |
|
|
setDocuments(prev => prev.filter(doc => doc.document_id !== documentId)); |
|
|
if (selectedDocument?.document_id === documentId) { |
|
|
setSelectedDocument(null); |
|
|
} |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<div className="App"> |
|
|
{/* Header */} |
|
|
<header className="app-header"> |
|
|
<h1>RAG CHU - Assistant Médical IA</h1> |
|
|
<p> |
|
|
Analysez vos documents médicaux et posez vos questions |
|
|
{isBackendReady ? ( |
|
|
<span style={{ color: '#10b981', marginLeft: '1rem' }}> |
|
|
● Backend connecté |
|
|
</span> |
|
|
) : ( |
|
|
<span style={{ color: '#ef4444', marginLeft: '1rem' }}> |
|
|
● Backend déconnecté |
|
|
</span> |
|
|
)} |
|
|
</p> |
|
|
</header> |
|
|
|
|
|
{/* Layout principal en 3 colonnes */} |
|
|
<div className="app-grid"> |
|
|
|
|
|
{/* Colonne gauche - Documents (étroite) */} |
|
|
<div className="left-column"> |
|
|
<DocumentUpload |
|
|
onDocumentUploaded={handleDocumentUploaded} |
|
|
onDocumentAnalyzed={handleDocumentAnalyzed} |
|
|
/> |
|
|
|
|
|
<DocumentList |
|
|
documents={documents} |
|
|
selectedDocument={selectedDocument} |
|
|
onDocumentSelected={handleDocumentSelected} |
|
|
onDocumentDeleted={handleDocumentDeleted} |
|
|
onRefresh={loadDocuments} |
|
|
/> |
|
|
</div> |
|
|
|
|
|
{/* Colonne centre - Chat (large) */} |
|
|
<div className="center-column"> |
|
|
<ChatInterface |
|
|
selectedDocument={selectedDocument} |
|
|
ws={ws} |
|
|
/> |
|
|
</div> |
|
|
|
|
|
{/* Colonne droite - Console Debug (fixe) */} |
|
|
<div className="right-column"> |
|
|
<DebugConsole ws={ws} /> |
|
|
</div> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
} |
|
|
|
|
|
export default App; |