mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
[FEATURE] Ajout des rétroactions pour les questions à choix multiples et réponses courtes
Fixes #291
This commit is contained in:
parent
112062c0b2
commit
61a5cf1330
6 changed files with 168 additions and 50 deletions
|
|
@ -16,19 +16,29 @@ interface Props {
|
||||||
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
||||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
||||||
|
const [isGoodAnswer, setisGoodAnswer] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
let disableButton = false;
|
let disableButton = false;
|
||||||
if(handleOnSubmitAnswer === undefined){
|
if (handleOnSubmitAnswer === undefined) {
|
||||||
disableButton = true;
|
disableButton = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (passedAnswer !== undefined) {
|
if (passedAnswer !== undefined) {
|
||||||
setAnswer(passedAnswer);
|
setAnswer(passedAnswer);
|
||||||
}
|
}
|
||||||
}, [passedAnswer]);
|
}, [passedAnswer]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkAnswer();
|
||||||
|
}, [answer]);
|
||||||
|
|
||||||
|
const checkAnswer = () => {
|
||||||
|
const isCorrect = question.choices.some((choice) => choice.formattedText.text === answer as string);
|
||||||
|
setisGoodAnswer(isCorrect);
|
||||||
|
};
|
||||||
|
|
||||||
const handleOnClickAnswer = (choice: string) => {
|
const handleOnClickAnswer = (choice: string) => {
|
||||||
setAnswer(choice);
|
setAnswer(choice);
|
||||||
};
|
};
|
||||||
|
|
@ -36,7 +46,19 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const alphabet = alpha.map((x) => String.fromCharCode(x));
|
const alphabet = alpha.map((x) => String.fromCharCode(x));
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className="question-container">
|
|
||||||
|
<div className="question-wrapper">
|
||||||
|
{showAnswer && (
|
||||||
|
<div>
|
||||||
|
<div className='question-feedback-validation'>
|
||||||
|
{isGoodAnswer ? '✅ Correct! ' : '❌ Incorrect!'}
|
||||||
|
</div>
|
||||||
|
<div className="question-title">
|
||||||
|
Question :
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -51,17 +73,17 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
className="button-wrapper"
|
className="button-wrapper"
|
||||||
disabled={disableButton}
|
disabled={disableButton}
|
||||||
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}>
|
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}>
|
||||||
{showAnswer? (<div> {(choice.isCorrect ? '✅' : '❌')}</div>)
|
{showAnswer ? (<div> {(choice.isCorrect ? '✅' : '❌')}</div>)
|
||||||
:``}
|
: ``}
|
||||||
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
||||||
<div className={`answer-text ${selected}`}>
|
<div className={`answer-text ${selected}`}>
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedText) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedText) }} />
|
||||||
</div>
|
</div>
|
||||||
{choice.formattedFeedback && showAnswer && (
|
{choice.formattedFeedback && showAnswer && (
|
||||||
<div className="feedback-container mb-1 mt-1/2">
|
<div className="feedback-container mb-1 mt-1/2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedFeedback) }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -70,8 +92,8 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
{question.formattedGlobalFeedback && showAnswer && (
|
{question.formattedGlobalFeedback && showAnswer && (
|
||||||
<div className="global-feedback mb-2">
|
<div className="global-feedback mb-2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!showAnswer && handleOnSubmitAnswer && (
|
{!showAnswer && handleOnSubmitAnswer && (
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } =
|
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } =
|
||||||
props;
|
props;
|
||||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
||||||
|
const [isGoodAnswer, setisGoodAnswer] = useState<boolean>(false);
|
||||||
const correctAnswers = question.choices;
|
const correctAnswers = question.choices;
|
||||||
let correctAnswer = '';
|
let correctAnswer = '';
|
||||||
|
|
||||||
|
|
@ -27,6 +28,14 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
}
|
}
|
||||||
}, [passedAnswer]);
|
}, [passedAnswer]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkAnswer();
|
||||||
|
}, [answer]);
|
||||||
|
|
||||||
|
const checkAnswer = () => {
|
||||||
|
const isCorrect = correctAnswer === answer;
|
||||||
|
setisGoodAnswer(isCorrect);
|
||||||
|
};
|
||||||
//const isSingleAnswer = correctAnswers.length === 1;
|
//const isSingleAnswer = correctAnswers.length === 1;
|
||||||
|
|
||||||
if (isSimpleNumericalAnswer(correctAnswers[0])) {
|
if (isSimpleNumericalAnswer(correctAnswers[0])) {
|
||||||
|
|
@ -45,21 +54,40 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="question-wrapper">
|
<div className="question-wrapper">
|
||||||
|
{showAnswer && (
|
||||||
|
<div>
|
||||||
|
<div className='question-feedback-validation'>
|
||||||
|
{isGoodAnswer ? '✅ Correct! ' : '❌ Incorrect!'}
|
||||||
|
</div>
|
||||||
|
<div className="question-title">
|
||||||
|
Question :
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
</div>
|
</div>
|
||||||
{showAnswer ? (
|
{showAnswer ? (
|
||||||
<>
|
<>
|
||||||
<div className="correct-answer-text mb-2">
|
<div className="correct-answer-text mb-1">
|
||||||
<strong>La bonne réponse est: </strong>
|
<div>
|
||||||
{correctAnswer}</div>
|
<div className="question-title">
|
||||||
<span>
|
Réponse(s) accepté(es):
|
||||||
<strong>Votre réponse est: </strong>{answer.toString()}
|
</div>
|
||||||
</span>
|
<div className="accepted-answers">
|
||||||
|
{correctAnswer}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="question-title">
|
||||||
|
Votre réponse est: </div>
|
||||||
|
<div className="accepted-answers">{answer}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -17,34 +17,59 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
||||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
||||||
|
const [isGoodAnswer, setisGoodAnswer] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (passedAnswer !== undefined) {
|
if (passedAnswer !== undefined) {
|
||||||
setAnswer(passedAnswer);
|
setAnswer(passedAnswer);
|
||||||
}
|
}
|
||||||
}, [passedAnswer]);
|
}, [passedAnswer]);
|
||||||
console.log("Answer" , answer);
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkAnswer();
|
||||||
|
}, [answer]);
|
||||||
|
|
||||||
|
const checkAnswer = () => {
|
||||||
|
const isCorrect = question.choices.some((choice) => choice.text.toLowerCase() === (answer as String).toLowerCase());
|
||||||
|
setisGoodAnswer(isCorrect);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className="question-wrapper">
|
<div className="question-wrapper">
|
||||||
<div className="question content">
|
{showAnswer && (
|
||||||
|
<div>
|
||||||
|
<div className='question-feedback-validation'>
|
||||||
|
{isGoodAnswer ? '✅ Correct! ' : '❌ Incorrect!'}
|
||||||
|
</div>
|
||||||
|
<div className="question-title">
|
||||||
|
Question :
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
</div>
|
</div>
|
||||||
{showAnswer ? (
|
{showAnswer ? (
|
||||||
<>
|
<>
|
||||||
<div className="correct-answer-text mb-1">
|
<div className="correct-answer-text mb-1">
|
||||||
<span>
|
<div>
|
||||||
<strong>La bonne réponse est: </strong>
|
<div className="question-title">
|
||||||
|
Réponse(s) accepté(es):
|
||||||
{question.choices.map((choice) => (
|
|
||||||
<div key={choice.text} className="mb-1">
|
|
||||||
{choice.text}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
{question.choices.map((choice) => (
|
||||||
</span>
|
<div key={choice.text} className="accepted-answers">
|
||||||
<span>
|
{choice.text}
|
||||||
<strong>Votre réponse est: </strong>{answer}
|
</div>
|
||||||
</span>
|
))}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="question-title">
|
||||||
|
Votre réponse est: </div>
|
||||||
|
<div className="accepted-answers">{answer}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,19 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const selectedTrue = answer ? 'selected' : '';
|
const selectedTrue = answer ? 'selected' : '';
|
||||||
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
||||||
return (
|
return (
|
||||||
<div className="question-container">
|
<div className="question-wrapper">
|
||||||
|
{showAnswer && (
|
||||||
|
<div>
|
||||||
|
<div className='question-feedback-validation'>
|
||||||
|
{question.isTrue? '✅ Correct! ' : '❌ Incorrect!'}
|
||||||
|
</div>
|
||||||
|
<div className="question-title">
|
||||||
|
Question :
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,7 +21,6 @@
|
||||||
.question-wrapper {
|
.question-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,12 +134,12 @@
|
||||||
/* height: 1em; */
|
/* height: 1em; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.global-feedback {
|
.global-feedback {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
background-color: hsl(43, 100%, 94%);
|
background-color: hsl(43, 100%, 94%);
|
||||||
color: hsl(43, 95%, 9%);
|
color: hsl(43, 95%, 9%);
|
||||||
|
margin-top: 2rem;
|
||||||
border: hsl(36, 84%, 93%) 1px solid;
|
border: hsl(36, 84%, 93%) 1px solid;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0px 2px 5px hsl(0, 0%, 74%);
|
box-shadow: 0px 2px 5px hsl(0, 0%, 74%);
|
||||||
|
|
@ -169,3 +167,39 @@
|
||||||
.choices-wrapper {
|
.choices-wrapper {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.correct-answer-text {
|
||||||
|
text-align: left; /* Align text to the left */
|
||||||
|
width: 100%; /* Ensure it takes the full width of its container */
|
||||||
|
}
|
||||||
|
|
||||||
|
.accepted-answers {
|
||||||
|
display: inline-block; /* Display elements side by side */
|
||||||
|
border-radius: 6px; /* Add rounded corners */
|
||||||
|
padding: 0.5rem; /* Add padding for spacing */
|
||||||
|
margin: 0.5rem; /* Add margin for spacing between elements */
|
||||||
|
background-color: hsl(0, 0%, 87%); /* Optional: Add a background color for emphasis */
|
||||||
|
}
|
||||||
|
.question-feedback-validation{
|
||||||
|
font-size: 1.8rem; /* Increase the font size */
|
||||||
|
font-weight: bold; /* Make the text bold */
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
.question-title{
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-content{
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import QuestionComponent from '../QuestionsDisplay/QuestionDisplay';
|
||||||
import '../../pages/Student/JoinRoom/joinRoom.css';
|
import '../../pages/Student/JoinRoom/joinRoom.css';
|
||||||
import { QuestionType } from '../../Types/QuestionType';
|
import { QuestionType } from '../../Types/QuestionType';
|
||||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
|
import { Dialog, DialogContent, DialogActions, Button } from '@mui/material';
|
||||||
import { Question } from 'gift-pegjs';
|
import { Question } from 'gift-pegjs';
|
||||||
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||||
|
|
@ -94,7 +94,6 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
open={isFeedbackDialogOpen}
|
open={isFeedbackDialogOpen}
|
||||||
onClose={handleFeedbackDialogClose}
|
onClose={handleFeedbackDialogClose}
|
||||||
>
|
>
|
||||||
<DialogTitle>Rétroaction</DialogTitle>
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<div style={{
|
<div style={{
|
||||||
wordWrap: 'break-word',
|
wordWrap: 'break-word',
|
||||||
|
|
@ -102,8 +101,6 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
maxHeight: '400px',
|
maxHeight: '400px',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
}}>
|
}}>
|
||||||
<div style={{ textAlign: 'left', fontWeight: 'bold', marginTop: '10px' }}
|
|
||||||
>Question : </div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<QuestionComponent
|
<QuestionComponent
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue