mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Gestion des index améliorée
This commit is contained in:
parent
6d714d1d77
commit
7dcbe55776
6 changed files with 160 additions and 27 deletions
|
|
@ -15,7 +15,7 @@ const MultipleChoiceQuestionDisplay: React.FC = () => {
|
||||||
const question = questions[Number(index)].question as MultipleChoiceQuestion;
|
const question = questions[Number(index)].question as MultipleChoiceQuestion;
|
||||||
|
|
||||||
const [actualAnswer, setActualAnswer] = useState<AnswerType>(() => {
|
const [actualAnswer, setActualAnswer] = useState<AnswerType>(() => {
|
||||||
if (answer && answer.length > 0) {
|
if (answer && answer === undefined) {
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -28,7 +28,7 @@ const MultipleChoiceQuestionDisplay: React.FC = () => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(answer));
|
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(answer));
|
||||||
if (answer !== undefined) {
|
if (answer.length !== undefined) {
|
||||||
setActualAnswer(answer);
|
setActualAnswer(answer);
|
||||||
} else {
|
} else {
|
||||||
setActualAnswer([]);
|
setActualAnswer([]);
|
||||||
|
|
@ -120,7 +120,7 @@ const MultipleChoiceQuestionDisplay: React.FC = () => {
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
actualAnswer.length > 0 && submitAnswer && submitAnswer(actualAnswer)
|
actualAnswer.length > 0 && submitAnswer && submitAnswer(actualAnswer)
|
||||||
}
|
}
|
||||||
disabled={answer.length === 0}
|
disabled={actualAnswer.length === 0}
|
||||||
>
|
>
|
||||||
Répondre
|
Répondre
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
// MultipleChoiceQuestionDisplay.tsx
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import '../questionStyle.css';
|
||||||
|
import { Button } from '@mui/material';
|
||||||
|
import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||||
|
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
||||||
|
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: MultipleChoiceQuestion;
|
||||||
|
handleOnSubmitAnswer?: (answer: AnswerType) => void;
|
||||||
|
showAnswer?: boolean;
|
||||||
|
passedAnswer?: AnswerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
|
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
||||||
|
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(passedAnswer));
|
||||||
|
|
||||||
|
const [answer, setAnswer] = useState<AnswerType>(() => {
|
||||||
|
if (passedAnswer && passedAnswer.length > 0) {
|
||||||
|
return passedAnswer;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
let disableButton = false;
|
||||||
|
if (handleOnSubmitAnswer === undefined) {
|
||||||
|
disableButton = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(passedAnswer));
|
||||||
|
if (passedAnswer !== undefined) {
|
||||||
|
setAnswer(passedAnswer);
|
||||||
|
} else {
|
||||||
|
setAnswer([]);
|
||||||
|
}
|
||||||
|
}, [passedAnswer, question.id]);
|
||||||
|
|
||||||
|
const handleOnClickAnswer = (choice: string) => {
|
||||||
|
setAnswer((prevAnswer) => {
|
||||||
|
console.log(`handleOnClickAnswer -- setAnswer(): prevAnswer: ${prevAnswer}, choice: ${choice}`);
|
||||||
|
const correctAnswersCount = question.choices.filter((c) => c.isCorrect).length;
|
||||||
|
|
||||||
|
if (correctAnswersCount === 1) {
|
||||||
|
// If only one correct answer, replace the current selection
|
||||||
|
return prevAnswer.includes(choice) ? [] : [choice];
|
||||||
|
} else {
|
||||||
|
// Allow multiple selections if there are multiple correct answers
|
||||||
|
if (prevAnswer.includes(choice)) {
|
||||||
|
// Remove the choice if it's already selected
|
||||||
|
return prevAnswer.filter((selected) => selected !== choice);
|
||||||
|
} else {
|
||||||
|
// Add the choice if it's not already selected
|
||||||
|
return [...prevAnswer, choice];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const alpha = Array.from(Array(26)).map((_e, i) => i + 65);
|
||||||
|
const alphabet = alpha.map((x) => String.fromCharCode(x));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="question-container">
|
||||||
|
<div className="question content">
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
|
</div>
|
||||||
|
<div className="choices-wrapper mb-1">
|
||||||
|
{question.choices.map((choice, i) => {
|
||||||
|
console.log(`answer: ${answer}, choice: ${choice.formattedText.text}`);
|
||||||
|
const selected = answer.includes(choice.formattedText.text) ? 'selected' : '';
|
||||||
|
return (
|
||||||
|
<div key={choice.formattedText.text + i} className="choice-container">
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
className="button-wrapper"
|
||||||
|
disabled={disableButton}
|
||||||
|
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}
|
||||||
|
>
|
||||||
|
{showAnswer ? (
|
||||||
|
<div>{choice.isCorrect ? '✅' : '❌'}</div>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
||||||
|
<div className={`answer-text ${selected}`}>
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: FormattedTextTemplate(choice.formattedText),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{choice.formattedFeedback && showAnswer && (
|
||||||
|
<div className="feedback-container mb-1 mt-1/2">
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: FormattedTextTemplate(choice.formattedFeedback),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{question.formattedGlobalFeedback && showAnswer && (
|
||||||
|
<div className="global-feedback mb-2">
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: FormattedTextTemplate(question.formattedGlobalFeedback),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!showAnswer && handleOnSubmitAnswer && (
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() =>
|
||||||
|
answer.length > 0 && handleOnSubmitAnswer && handleOnSubmitAnswer(answer)
|
||||||
|
}
|
||||||
|
disabled={answer.length === 0}
|
||||||
|
>
|
||||||
|
Répondre
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MultipleChoiceQuestionDisplay;
|
||||||
|
|
@ -1,30 +1,31 @@
|
||||||
// StudentModeQuiz.tsx
|
// StudentModeQuiz.tsx
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import QuestionDisplay from '../QuestionsDisplay/QuestionDisplay';
|
import QuestionDisplay from '../QuestionsDisplay/QuestionDisplay';
|
||||||
import '../../pages/Student/JoinRoom/joinRoom.css';
|
import '../../pages/Student/JoinRoom/joinRoom.css';
|
||||||
import { QuestionType } from '../../Types/QuestionType';
|
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||||
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||||
|
|
||||||
const StudentModeQuiz: React.FC = () => {
|
const StudentModeQuiz: React.FC = () => {
|
||||||
const { questions, answers, setIsQuestionSent, disconnectWebSocket, setShowAnswer } = useQuizContext(); // Access setShowAnswer from context
|
const { questions, answers, setIsQuestionSent, disconnectWebSocket, setShowAnswer, index, updateIndex } = useQuizContext(); // Access setShowAnswer from context
|
||||||
|
|
||||||
const [questionInfos, setQuestion] = useState<QuestionType>(questions[0]);
|
|
||||||
|
|
||||||
const previousQuestion = () => {
|
const previousQuestion = () => {
|
||||||
setQuestion(questions[Number(questionInfos.question?.id) - 2]);
|
updateIndex(Number(index) - 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedAnswer = answers[Number(questionInfos.question.id) - 1]?.answer;
|
let savedAnswer = undefined;
|
||||||
|
console.log(`StudentModeQuiz: useEffect: index: ${index}`);
|
||||||
|
if (answers.length === 0) {
|
||||||
|
savedAnswer = answers[Number(index) - 1]?.answer;}
|
||||||
|
|
||||||
console.log(`StudentModeQuiz: useEffect: savedAnswer: ${savedAnswer}`);
|
console.log(`StudentModeQuiz: useEffect: savedAnswer: ${savedAnswer}`);
|
||||||
setIsQuestionSent(savedAnswer !== undefined);
|
setIsQuestionSent(savedAnswer !== undefined);
|
||||||
setShowAnswer(savedAnswer !== undefined); // Update showAnswer in context
|
setShowAnswer(savedAnswer !== undefined); // Update showAnswer in context
|
||||||
}, [questionInfos.question, answers, setShowAnswer]);
|
}, [index, answers, setShowAnswer]);
|
||||||
|
|
||||||
const nextQuestion = () => {
|
const nextQuestion = () => {
|
||||||
setQuestion(questions[Number(questionInfos.question?.id)]);
|
updateIndex(Number(index)+1);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -36,7 +37,7 @@ const StudentModeQuiz: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<b>Question {questionInfos.question.id}/{questions.length}</b>
|
<b>Question {Number(index) +1}/{questions.length}</b>
|
||||||
</div>
|
</div>
|
||||||
<div className="overflow-auto">
|
<div className="overflow-auto">
|
||||||
<div className="question-component-container">
|
<div className="question-component-container">
|
||||||
|
|
@ -47,7 +48,7 @@ const StudentModeQuiz: React.FC = () => {
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={previousQuestion}
|
onClick={previousQuestion}
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={Number(questionInfos.question.id) <= 1}
|
disabled={Number(index) +1 <= 1}
|
||||||
>
|
>
|
||||||
Question précédente
|
Question précédente
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -57,7 +58,7 @@ const StudentModeQuiz: React.FC = () => {
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={nextQuestion}
|
onClick={nextQuestion}
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={Number(questionInfos.question.id) >= questions.length}
|
disabled={Number(index) >= questions.length -1}
|
||||||
>
|
>
|
||||||
Question suivante
|
Question suivante
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ const JoinRoom: React.FC = () => {
|
||||||
console.log('on(launch-teacher-mode): Received launch-teacher-mode:', questions);
|
console.log('on(launch-teacher-mode): Received launch-teacher-mode:', questions);
|
||||||
setQuizMode('teacher');
|
setQuizMode('teacher');
|
||||||
setIsWaitingForTeacher(true);
|
setIsWaitingForTeacher(true);
|
||||||
updateIndex(null);
|
updateIndex(0);
|
||||||
setQuestions(questions);
|
setQuestions(questions);
|
||||||
// wait for next-question
|
// wait for next-question
|
||||||
});
|
});
|
||||||
|
|
@ -92,6 +92,7 @@ const JoinRoom: React.FC = () => {
|
||||||
setQuestions([]); // clear out from last time (in case quiz is repeated)
|
setQuestions([]); // clear out from last time (in case quiz is repeated)
|
||||||
setQuestions(questions);
|
setQuestions(questions);
|
||||||
updateIndex(0);
|
updateIndex(0);
|
||||||
|
console.log('on(launch-student-mode): setQuestions:', index);
|
||||||
});
|
});
|
||||||
socket.on('end-quiz', () => {
|
socket.on('end-quiz', () => {
|
||||||
disconnect();
|
disconnect();
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,10 @@ export const QuizProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||||
const [disconnectWebSocket, setDisconnectWebSocket] = useState<() => void>(() => () => {});
|
const [disconnectWebSocket, setDisconnectWebSocket] = useState<() => void>(() => () => {});
|
||||||
|
|
||||||
|
|
||||||
const updateIndex = (questionId: number | null) => {
|
const updateIndex = (questionId?: number | null) => {
|
||||||
const questionIndex = questions.findIndex((q) => q.question.id === String(questionId));
|
|
||||||
setIndex(questionIndex >= 0 ? questionIndex : null);
|
setIndex(questionId ?? null);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to handle answer submission
|
// Function to handle answer submission
|
||||||
|
|
@ -42,9 +43,7 @@ export const QuizProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||||
if (!idQuestion) {
|
if (!idQuestion) {
|
||||||
setAnswer(answer);
|
setAnswer(answer);
|
||||||
setIsQuestionSent(true);
|
setIsQuestionSent(true);
|
||||||
console.log('index',index);
|
|
||||||
} else {
|
} else {
|
||||||
console.info(`QuizProvider: submitAnswer: answer: ${answer}, idQuestion: ${idQuestion}`);
|
|
||||||
const answerData: AnswerSubmissionToBackendType = {
|
const answerData: AnswerSubmissionToBackendType = {
|
||||||
roomName: roomName,
|
roomName: roomName,
|
||||||
answer: answer,
|
answer: answer,
|
||||||
|
|
@ -59,7 +58,6 @@ export const QuizProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||||
return newAnswers; // Return the new array
|
return newAnswers; // Return the new array
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`QuizProvider: submitAnswer: answers: ${JSON.stringify(answers)}`);
|
|
||||||
|
|
||||||
// Submit the answer to the WebSocket service
|
// Submit the answer to the WebSocket service
|
||||||
webSocketService.submitAnswer(answerData);
|
webSocketService.submitAnswer(answerData);
|
||||||
|
|
|
||||||
|
|
@ -255,10 +255,10 @@ const ManageRoom: React.FC = () => {
|
||||||
const nextQuestion = () => {
|
const nextQuestion = () => {
|
||||||
if (!questions || !index || !quiz?.content) return;
|
if (!questions || !index || !quiz?.content) return;
|
||||||
|
|
||||||
const nextQuestionIndex = index;
|
const nextQuestionIndex = index +1;
|
||||||
|
|
||||||
if (nextQuestionIndex === undefined || nextQuestionIndex > questions.length - 1) return;
|
if (nextQuestionIndex === undefined || nextQuestionIndex > questions.length - 1) return;
|
||||||
|
console.log('nextQuestionIndex:', questions);
|
||||||
updateIndex(nextQuestionIndex);
|
updateIndex(nextQuestionIndex);
|
||||||
webSocketService.nextQuestion({roomName: formattedRoomName,
|
webSocketService.nextQuestion({roomName: formattedRoomName,
|
||||||
questions: questions,
|
questions: questions,
|
||||||
|
|
@ -410,7 +410,7 @@ const ManageRoom: React.FC = () => {
|
||||||
<div className="title center-h-align mb-2">{quiz?.title}</div>
|
<div className="title center-h-align mb-2">{quiz?.title}</div>
|
||||||
{index && (
|
{index && (
|
||||||
<strong className="number of questions">
|
<strong className="number of questions">
|
||||||
Question {index+1}/
|
Question {index +1}/
|
||||||
{questions?.length}
|
{questions?.length}
|
||||||
</strong>
|
</strong>
|
||||||
)}
|
)}
|
||||||
|
|
@ -451,7 +451,7 @@ const ManageRoom: React.FC = () => {
|
||||||
<Button
|
<Button
|
||||||
onClick={previousQuestion}
|
onClick={previousQuestion}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
disabled={index !== null && index <= 1}
|
disabled={index !== null && index <= 0}
|
||||||
>
|
>
|
||||||
Question précédente
|
Question précédente
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -462,7 +462,7 @@ const ManageRoom: React.FC = () => {
|
||||||
variant="contained"
|
variant="contained"
|
||||||
disabled={
|
disabled={
|
||||||
index !== null &&
|
index !== null &&
|
||||||
index >= questions.length
|
index >= questions.length -1
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Prochaine question
|
Prochaine question
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue