diff --git a/MEMORY.md b/MEMORY.md index 971427e..9f3fd0d 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -28,7 +28,12 @@ Ce projet est un POC de professeur virtuel pour enfants, avec: - Les reponses aux mini-tests sont maintenant aussi enregistrees dans `messages` avec le role `user`. - Le frontend recharge l'historique quand un eleve est selectionne, mais ne met plus automatiquement la derniere ancienne phrase au centre. - Au demarrage d'une nouvelle seance, Professeur TOP doit faire une courte reprise de l'historique puis annoncer le cours du jour. -- L'interface de seance est maintenant en hauteur fixe `100vh`: pas de scroll global, cours central au milieu, historique transforme en journal compact. +- L'interface de seance est maintenant en hauteur fixe `100vh`: pas de scroll global, cours central au milieu, pas de conversation visible. +- La conversation reste stockee en base pour analyse, mais l'ecran de cours remplace la consigne/reponse courante au lieu d'empiler des bulles. +- Les reponses de mini-test utilisent la meme zone de reponse eleve en bas; le texte du prof reste au-dessus et pourra accueillir une image sous le texte. +- Cote eleve, le bouton principal bascule entre `Demarrer la seance` et `Fin de la seance`. +- Cote eleve, la colonne gauche affiche les themes du jour: une card lecon avec pourcentage local, une card exercice avec statut. +- Le bilan complet de progression reste reserve aux vues prof/admin. - Le vrai `docker-compose.yml` de production n'est pas dans ce repo. Il est situe un niveau au-dessus sur le serveur. - La conf nginx reelle route: - `/api/` vers `tutor-backend:8000` diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 35db720..e0b60c5 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -183,6 +183,9 @@ function TutorApp({ currentUser, onLogout }) { }) const [messages, setMessages] = useState([]) const [currentInstruction, setCurrentInstruction] = useState('') + const [sessionActive, setSessionActive] = useState(false) + const [currentFocusSkill, setCurrentFocusSkill] = useState(null) + const [exerciseStatus, setExerciseStatus] = useState('not-started') const [input, setInput] = useState('') const [progress, setProgress] = useState([]) const [assessment, setAssessment] = useState(null) @@ -232,6 +235,21 @@ function TutorApp({ currentUser, onLogout }) { return Math.round(total / progress.length) }, [progress]) + const lessonFocus = useMemo(() => { + if (currentFocusSkill) return currentFocusSkill + if (!progress.length) return null + return [...progress].sort((a, b) => a.mastery_score - b.mastery_score)[0] + }, [currentFocusSkill, progress]) + + const lessonPercent = lessonFocus ? Math.round(lessonFocus.mastery_score) : averageScore + + const exerciseLabel = { + 'not-started': 'Pas encore lance', + 'in-progress': 'En cours', + resolved: 'Resolu', + 'needs-review': 'A revoir', + }[exerciseStatus] + useEffect(() => { loadStudents() }, [currentUser]) @@ -260,6 +278,11 @@ function TutorApp({ currentUser, onLogout }) { useEffect(() => { setCurrentInstruction('') + setSessionActive(false) + setAssessment(null) + setAssessmentAnswer('') + setCurrentFocusSkill(null) + setExerciseStatus('not-started') if (selectedStudentId) { loadProgress(selectedStudentId) loadMessages(selectedStudentId) @@ -368,6 +391,9 @@ function TutorApp({ currentUser, onLogout }) { const data = await apiFetch(`/session/start?student_id=${selectedStudentId}`, { method: 'POST' }) appendMessage('assistant', data.reply) setCurrentInstruction(data.reply) + setSessionActive(true) + setExerciseStatus('not-started') + setCurrentFocusSkill((current) => current || lessonFocus) speak(data.reply) await loadProgress(selectedStudentId) } catch (error) { @@ -397,6 +423,15 @@ function TutorApp({ currentUser, onLogout }) { setAssessment(data) setAssessmentAnswer('') setCurrentInstruction(data.question) + setSessionActive(true) + setExerciseStatus('in-progress') + setCurrentFocusSkill( + progress.find((item) => item.code === data.skill_code) || { + code: data.skill_code, + label: data.skill_label, + mastery_score: lessonPercent, + } + ) } catch (error) { setErrorMessage(error.message || 'Impossible de charger le mini-test.') } @@ -419,6 +454,7 @@ function TutorApp({ currentUser, onLogout }) { appendMessage('user', `Reponse au mini-test: ${assessmentAnswer}`) appendMessage('assistant', data.feedback) setCurrentInstruction(data.feedback) + setExerciseStatus(data.correct ? 'resolved' : 'needs-review') speak(data.feedback) setAssessment(null) setAssessmentAnswer('') @@ -441,6 +477,24 @@ function TutorApp({ currentUser, onLogout }) { setSpeaking(false) } + function endSession() { + stopSpeechPlayback() + deactivateAutoListening(false) + setSessionActive(false) + setCurrentInstruction('') + setAssessment(null) + setAssessmentAnswer('') + setInput('') + } + + async function toggleSession() { + if (sessionActive) { + endSession() + return + } + await startSession() + } + async function loadMessages(studentId) { try { setErrorMessage('') @@ -805,7 +859,7 @@ function TutorApp({ currentUser, onLogout }) { } return ( -
+