11import { useParams , useNavigate } from 'react-router-dom' ;
2- import { useState , useEffect , useCallback } from 'react' ;
2+ import { useState , useEffect , useCallback , useRef } from 'react' ;
33import { ArrowLeft , Clock , AlertCircle , CheckCircle , Send } from 'lucide-react' ;
44import Button from '../../components/ui/Button/Button' ;
55import {
@@ -35,15 +35,17 @@ export default function TakeAssessmentPage() {
3535 QuestionForStudent [ ]
3636 > ( [ ] ) ;
3737
38+ const hasAutoSubmittedRef = useRef ( false ) ;
39+
3840 const { data : assessment , isLoading } = useAssessment ( assessmentId ) ;
3941 const startAttemptMutation = useStartAttempt ( ) ;
4042 const saveAnswersMutation = useSaveAnswers ( ) ;
4143 const submitAttemptMutation = useSubmitAttempt ( ) ;
4244
43- const handleSubmit = useCallback ( async ( ) => {
45+ const handleSubmit = useCallback ( async ( forceSubmit = false ) => {
4446 if ( ! attemptId ) return ;
4547
46- if ( Object . keys ( answers ) . length === 0 ) {
48+ if ( ! forceSubmit && Object . keys ( answers ) . length === 0 ) {
4749 alert ( 'Debes responder al menos una pregunta para enviar la evaluación.' ) ;
4850 return ;
4951 }
@@ -54,19 +56,29 @@ export default function TakeAssessmentPage() {
5456 answer,
5557 } )
5658 ) ;
57-
59+
5860 try {
59- await submitAttemptMutation . mutateAsync ( {
60- attemptId,
61- submitData : { attemptId, answers : answersArray } ,
62- } ) ;
61+ if ( answersArray . length > 0 ) {
62+ await submitAttemptMutation . mutateAsync ( {
63+ attemptId,
64+ submitData : { attemptId, answers : answersArray } ,
65+ } ) ;
66+
67+ navigate (
68+ `/courses/${ courseId } /assessments/${ assessmentId } /results/${ attemptId } `
69+ ) ;
70+ } else if ( forceSubmit ) {
71+ toast . error ( "El tiempo se agotó y no hubo respuestas registradas." ) ;
72+ navigate ( `/courses/${ courseId } /assessments` ) ;
73+ }
6374
64- navigate (
65- `/courses/${ courseId } /assessments/${ assessmentId } /results/${ attemptId } `
66- ) ;
6775 } catch ( error ) {
6876 console . error ( 'Error submitting attempt:' , error ) ;
69- alert ( 'Hubo un error al enviar la evaluación.' ) ;
77+ if ( forceSubmit ) {
78+ navigate ( `/courses/${ courseId } /assessments` ) ;
79+ } else {
80+ toast . error ( 'Hubo un error al enviar la evaluación.' ) ;
81+ }
7082 }
7183 } , [
7284 attemptId ,
@@ -91,13 +103,12 @@ export default function TakeAssessmentPage() {
91103 } ) ;
92104 setAttemptId ( attempt . id ) ;
93105 setHasStarted ( true ) ;
106+ hasAutoSubmittedRef . current = false ;
94107
95- // Guardamos las preguntas del intento (SIN respuestas correctas)
96108 if ( attempt . assessment ?. questions ) {
97109 setAttemptQuestions ( attempt . assessment . questions ) ;
98110 }
99111
100- // Si hay respuestas previas guardadas (auto-save), las cargamos
101112 if ( attempt . answers && attempt . answers . length > 0 ) {
102113 const savedAnswers : Record < string , number | string > = { } ;
103114 attempt . answers . forEach ( ( answer ) => {
@@ -152,18 +163,27 @@ export default function TakeAssessmentPage() {
152163 } , [ hasStarted , assessment , timeLeft ] ) ;
153164
154165 useEffect ( ( ) => {
166+ if ( hasAutoSubmittedRef . current ) return ;
167+
155168 if ( timeLeft !== null && timeLeft > 0 ) {
156169 const timer = setInterval ( ( ) => {
157- setTimeLeft ( ( prev ) => ( prev !== null ? prev - 1 : null ) ) ;
170+ setTimeLeft ( ( prev ) => {
171+ if ( prev !== null && prev <= 1 ) {
172+ clearInterval ( timer ) ;
173+ return 0 ;
174+ }
175+ return prev !== null ? prev - 1 : null ;
176+ } ) ;
158177 } , 1000 ) ;
159178 return ( ) => clearInterval ( timer ) ;
160179 } else if ( timeLeft === 0 ) {
161- handleSubmit ( ) ;
180+ hasAutoSubmittedRef . current = true ;
181+ handleSubmit ( true ) ;
162182 }
163183 } , [ timeLeft , handleSubmit ] ) ;
164184
165185 useEffect ( ( ) => {
166- if ( hasStarted && attemptId && Object . keys ( answers ) . length > 0 ) {
186+ if ( hasStarted && attemptId && Object . keys ( answers ) . length > 0 && ! hasAutoSubmittedRef . current ) {
167187 const autoSave = setInterval ( ( ) => {
168188 const answersArray = Object . entries ( answers ) . map (
169189 ( [ questionId , answer ] ) => ( {
@@ -334,8 +354,8 @@ export default function TakeAssessmentPage() {
334354 </ div >
335355 ) }
336356 < Button
337- onClick = { handleSubmit }
338- disabled = { submitAttemptMutation . isPending }
357+ onClick = { ( ) => handleSubmit ( false ) } // Envío manual (no forzado)
358+ disabled = { submitAttemptMutation . isPending || hasAutoSubmittedRef . current }
339359 size = "sm"
340360 className = "bg-gradient-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800 flex-1 sm:flex-initial"
341361 >
@@ -389,6 +409,7 @@ export default function TakeAssessmentPage() {
389409 parseInt ( e . target . value , 10 )
390410 )
391411 }
412+ disabled = { hasAutoSubmittedRef . current }
392413 className = "w-5 h-5 text-purple-600 focus:ring-purple-500"
393414 />
394415 < span className = "text-slate-700" > { option . text } </ span >
@@ -401,6 +422,7 @@ export default function TakeAssessmentPage() {
401422 onChange = { ( e ) =>
402423 handleAnswerChange ( question . id ! , e . target . value )
403424 }
425+ disabled = { hasAutoSubmittedRef . current }
404426 placeholder = "Escribe tu respuesta aquí..."
405427 className = "w-full min-h-[150px] p-4 border-2 border-slate-200 rounded-lg focus:border-purple-400 focus:ring-2 focus:ring-purple-200 resize-none"
406428 />
@@ -412,8 +434,8 @@ export default function TakeAssessmentPage() {
412434 </ div >
413435 < div className = "mt-8 flex justify-center" >
414436 < Button
415- onClick = { handleSubmit }
416- disabled = { submitAttemptMutation . isPending }
437+ onClick = { ( ) => handleSubmit ( false ) }
438+ disabled = { submitAttemptMutation . isPending || hasAutoSubmittedRef . current }
417439 size = "lg"
418440 className = "bg-gradient-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800 px-8"
419441 >
@@ -426,4 +448,4 @@ export default function TakeAssessmentPage() {
426448 </ div >
427449 </ div >
428450 ) ;
429- }
451+ }
0 commit comments