Session terminée
Votre présentation a été évaluée. Vous pouvez télécharger l'enregistrement audio avant de consulter le rapport.
Durée enregistrée :{' '} {session.recording.durationSeconds.toFixed(1)} s
/** * Page /simulation/eo/t1/live/dialogue — phase de dialogue live T1 (Sprint 7b). * * Démarre la session WS au mount, pilote l'UI selon l'état machine T1, affiche le * timer 3:00 et l'indicateur d'état. Spécificité T1 : l'examinateur peut * INTERROMPRE le monologue de façon NON DÉTERMINISTE (état `interrupted`) — l'UI * ne suppose JAMAIS qu'une relance suit. À la fin (REPORT_READY), écran terminal * avec « Télécharger l'audio » + « Voir le rapport ». */ import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import { Mic, Volume2, Download, FileText, Loader2 } from 'lucide-react' import { Button } from '@/shared/ui/Button' import { Card } from '@/shared/ui/Card' import { T1SpeakingIndicator } from '../components/T1SpeakingIndicator' import { useT1LiveSession } from '../hooks/useT1LiveSession' const DIALOGUE_SECONDS = 180 // 3:00 function formatMmSs(totalSeconds: number): string { const remaining = Math.max(0, totalSeconds) const m = Math.floor(remaining / 60) const s = remaining % 60 return `${m}:${s.toString().padStart(2, '0')}` } export function T1DialoguePage() { const navigate = useNavigate() const [autoStarted, setAutoStarted] = useState(false) const session = useT1LiveSession() // Démarrer le dialogue automatiquement au mount (la prépa est déjà finie). useEffect(() => { if (autoStarted) return setAutoStarted(true) void session.startDialogue() // eslint-disable-next-line react-hooks/exhaustive-deps }, [autoStarted]) const remaining = DIALOGUE_SECONDS - session.elapsedSeconds const stateLabel = (() => { switch (session.state) { case 'idle': case 'connecting': return 'Connexion à l’examinateur…' case 'presenting': return 'À vous — présentez-vous.' case 'interrupted': return 'L’examinateur vous interrompt — répondez-lui.' case 'processing': return 'Évaluation en cours…' case 'ended': return 'Session terminée.' case 'error': return 'Erreur.' case 'preparing': return 'Préparation…' } })() function handleDownload() { const blob = session.recording.exportWAV() const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `expria-t1-${new Date().toISOString().slice(0, 10)}.wav` document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) } function handleViewReport() { if (!session.simulationId) return navigate(`/rapport/${session.simulationId}`) } function handleRestart() { navigate('/simulation/eo/t1/live/preparation') } // Abandon : ferme la session sans évaluation (cancelDialogue ne déclenche ni // correction ni persistance), puis sortie du flux. function handleCancel() { session.cancelDialogue() navigate('/simulation/eo') } // « Annuler » / « Terminer » n'ont de sens que pendant la session active // (connexion, présentation ou interruption), pas en évaluation. const canCancel = session.state === 'connecting' || session.state === 'presenting' || session.state === 'interrupted' // ── État terminal : rapport prêt ───────────────────────────────────────── if (session.state === 'ended') { return (
Votre présentation a été évaluée. Vous pouvez télécharger l'enregistrement audio avant de consulter le rapport.
Durée enregistrée :{' '} {session.recording.durationSeconds.toFixed(1)} s
{stateLabel}
Présentez-vous naturellement. L'examinateur peut vous interrompre à tout moment.
« Annuler » abandonne la session sans rapport. « Terminer » lance l'évaluation.