mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Display percentage feature in manageRooms
Added the percentage display for the following question types: TrueFalse, MultipleChoice, Numerical and ShortAnswer Modified the signature of each question types to optionally receive students in order to display their pickrate for each option
This commit is contained in:
parent
8240de0a44
commit
3f16a758d8
7 changed files with 323 additions and 23 deletions
|
|
@ -1,28 +1,57 @@
|
||||||
// MultipleChoiceQuestionDisplay.tsx
|
// MultipleChoiceQuestionDisplay.tsx
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import '../questionStyle.css';
|
import '../questionStyle.css';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||||
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
||||||
|
import { StudentType } from 'src/Types/StudentType';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: MultipleChoiceQuestion;
|
question: MultipleChoiceQuestion;
|
||||||
handleOnSubmitAnswer?: (answer: string) => void;
|
handleOnSubmitAnswer?: (answer: string) => void;
|
||||||
showAnswer?: boolean;
|
showAnswer?: boolean;
|
||||||
|
students?: StudentType[];
|
||||||
|
isDisplayOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const { question, showAnswer, handleOnSubmitAnswer } = props;
|
const { question, showAnswer, handleOnSubmitAnswer, students, isDisplayOnly } = props;
|
||||||
const [answer, setAnswer] = useState<string>();
|
const [answer, setAnswer] = useState<string>();
|
||||||
|
const [pickRates, setPickRates] = useState<number[]>([]);
|
||||||
useEffect(() => {
|
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
||||||
setAnswer(undefined);
|
|
||||||
}, [question]);
|
|
||||||
|
|
||||||
const handleOnClickAnswer = (choice: string) => {
|
const handleOnClickAnswer = (choice: string) => {
|
||||||
setAnswer(choice);
|
setAnswer(choice);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleShowCorrectAnswers = () => {
|
||||||
|
setShowCorrectAnswers(!showCorrectAnswers);
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculatePickRates = () => {
|
||||||
|
if (!students || students.length === 0) {
|
||||||
|
setPickRates(new Array(question.choices.length).fill(0)); // Fill with 0 for each choice
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rates = question.choices.map(choice => {
|
||||||
|
const choiceAnswers = students.filter(student =>
|
||||||
|
student.answers.some(ans =>
|
||||||
|
ans.idQuestion === Number(question.id) && ans.answer === choice.formattedText.text
|
||||||
|
)
|
||||||
|
).length;
|
||||||
|
return (choiceAnswers / students.length) * 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
setPickRates(rates);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAnswer(undefined);
|
||||||
|
calculatePickRates();
|
||||||
|
}, [students, question, showCorrectAnswers]);
|
||||||
|
|
||||||
|
|
||||||
const alpha = Array.from(Array(26)).map((_e, i) => i + 65);
|
const alpha = Array.from(Array(26)).map((_e, i) => i + 65);
|
||||||
const alphabet = alpha.map((x) => String.fromCharCode(x));
|
const alphabet = alpha.map((x) => String.fromCharCode(x));
|
||||||
return (
|
return (
|
||||||
|
|
@ -33,9 +62,13 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
<div className="choices-wrapper mb-1">
|
<div className="choices-wrapper mb-1">
|
||||||
{question.choices.map((choice, i) => {
|
{question.choices.map((choice, i) => {
|
||||||
const selected = answer === choice.formattedText.text ? 'selected' : '';
|
const selected = answer === choice.formattedText.text ? 'selected' : '';
|
||||||
|
const rateStyle = showCorrectAnswers ? {
|
||||||
|
backgroundImage: `linear-gradient(to right, ${choice.isCorrect ? 'lightgreen' : 'lightcoral'} ${pickRates[i]}%, transparent ${pickRates[i]}%)`,
|
||||||
|
color: 'black'
|
||||||
|
} : {};
|
||||||
return (
|
return (
|
||||||
<div key={choice.formattedText.text + i} className="choice-container">
|
<div key={choice.formattedText.text + i} className="choice-container">
|
||||||
<Button
|
{/* <Button
|
||||||
variant="text"
|
variant="text"
|
||||||
className="button-wrapper"
|
className="button-wrapper"
|
||||||
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}>
|
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}>
|
||||||
|
|
@ -50,8 +83,29 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedFeedback) }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</Button> */}
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
className={`button-wrapper ${selected}`}
|
||||||
|
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}
|
||||||
|
>
|
||||||
|
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
||||||
|
<div className={`answer-text ${selected}`}
|
||||||
|
style={rateStyle}>
|
||||||
|
<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>
|
||||||
|
)}
|
||||||
|
{showCorrectAnswers && <div>{choice.isCorrect ? '✅' : '❌'}</div>}
|
||||||
|
{showCorrectAnswers && (
|
||||||
|
<div className="pick-rate">
|
||||||
|
{pickRates[i].toFixed(1)}%
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -75,6 +129,18 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isDisplayOnly && (
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={toggleShowCorrectAnswers}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{showCorrectAnswers ? "Masquer les résultats" : "Afficher les résultats"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,22 @@
|
||||||
// NumericalQuestion.tsx
|
// NumericalQuestion.tsx
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import '../questionStyle.css';
|
import '../questionStyle.css';
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||||
import { NumericalQuestion, SimpleNumericalAnswer, RangeNumericalAnswer, HighLowNumericalAnswer } from 'gift-pegjs';
|
import { NumericalQuestion, SimpleNumericalAnswer, RangeNumericalAnswer, HighLowNumericalAnswer } from 'gift-pegjs';
|
||||||
import { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer, isMultipleNumericalAnswer } from 'gift-pegjs/typeGuards';
|
import { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer, isMultipleNumericalAnswer } from 'gift-pegjs/typeGuards';
|
||||||
|
import { StudentType } from 'src/Types/StudentType';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: NumericalQuestion;
|
question: NumericalQuestion;
|
||||||
handleOnSubmitAnswer?: (answer: number) => void;
|
handleOnSubmitAnswer?: (answer: number) => void;
|
||||||
showAnswer?: boolean;
|
showAnswer?: boolean;
|
||||||
|
students?: StudentType[];
|
||||||
|
isDisplayOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const { question, showAnswer, handleOnSubmitAnswer } =
|
const { question, showAnswer, handleOnSubmitAnswer, students, isDisplayOnly } =
|
||||||
props;
|
props;
|
||||||
|
|
||||||
const [answer, setAnswer] = useState<number>();
|
const [answer, setAnswer] = useState<number>();
|
||||||
|
|
@ -21,6 +24,34 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const correctAnswers = question.choices;
|
const correctAnswers = question.choices;
|
||||||
let correctAnswer = '';
|
let correctAnswer = '';
|
||||||
|
|
||||||
|
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
||||||
|
const [correctAnswerRate, setCorrectAnswerRate] = useState<number>(0);
|
||||||
|
|
||||||
|
const toggleShowCorrectAnswers = () => {
|
||||||
|
setShowCorrectAnswers(!showCorrectAnswers);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (showCorrectAnswers && students) {
|
||||||
|
calculateCorrectAnswerRate();
|
||||||
|
}
|
||||||
|
}, [showCorrectAnswers, students]);
|
||||||
|
|
||||||
|
const calculateCorrectAnswerRate = () => {
|
||||||
|
if (!students || students.length === 0) {
|
||||||
|
setCorrectAnswerRate(0); // Safeguard against undefined or empty student array
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalSubmissions = students.length;
|
||||||
|
const correctSubmissions = students.filter(student =>
|
||||||
|
student.answers.some(ans =>
|
||||||
|
ans.idQuestion === Number(question.id) && ans.isCorrect
|
||||||
|
)
|
||||||
|
).length;
|
||||||
|
setCorrectAnswerRate((correctSubmissions / totalSubmissions) * 100);
|
||||||
|
};
|
||||||
|
|
||||||
//const isSingleAnswer = correctAnswers.length === 1;
|
//const isSingleAnswer = correctAnswers.length === 1;
|
||||||
|
|
||||||
if (isSimpleNumericalAnswer(correctAnswers[0])) {
|
if (isSimpleNumericalAnswer(correctAnswers[0])) {
|
||||||
|
|
@ -82,6 +113,34 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
{isDisplayOnly && (
|
||||||
|
<>
|
||||||
|
<div style={{ marginTop: '10px' }}>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={toggleShowCorrectAnswers}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{showCorrectAnswers ? "Masquer les résultats" : "Afficher les résultats"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
visibility: showCorrectAnswers ? 'visible' : 'hidden'
|
||||||
|
}}>
|
||||||
|
<div>
|
||||||
|
Taux de réponse correcte:
|
||||||
|
</div>
|
||||||
|
<div className="progress-bar-container">
|
||||||
|
<div className="progress-bar-fill" style={{ width: `${correctAnswerRate}%` }}></div>
|
||||||
|
<div className="progress-bar-text">
|
||||||
|
{correctAnswerRate.toFixed(1)}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,21 @@ import NumericalQuestionDisplay from './NumericalQuestionDisplay/NumericalQuesti
|
||||||
import ShortAnswerQuestionDisplay from './ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay';
|
import ShortAnswerQuestionDisplay from './ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay';
|
||||||
// import useCheckMobileScreen from '../../services/useCheckMobileScreen';
|
// import useCheckMobileScreen from '../../services/useCheckMobileScreen';
|
||||||
|
|
||||||
|
import { StudentType } from '../../Types/StudentType';
|
||||||
|
|
||||||
interface QuestionProps {
|
interface QuestionProps {
|
||||||
question: Question;
|
question: Question;
|
||||||
handleOnSubmitAnswer?: (answer: string | number | boolean) => void;
|
handleOnSubmitAnswer?: (answer: string | number | boolean) => void;
|
||||||
showAnswer?: boolean;
|
showAnswer?: boolean;
|
||||||
|
students?: StudentType[];
|
||||||
|
isDisplayOnly?: boolean;
|
||||||
}
|
}
|
||||||
const QuestionDisplay: React.FC<QuestionProps> = ({
|
const QuestionDisplay: React.FC<QuestionProps> = ({
|
||||||
question,
|
question,
|
||||||
handleOnSubmitAnswer,
|
handleOnSubmitAnswer,
|
||||||
showAnswer,
|
showAnswer,
|
||||||
|
students,
|
||||||
|
isDisplayOnly = false
|
||||||
}) => {
|
}) => {
|
||||||
// const isMobile = useCheckMobileScreen();
|
// const isMobile = useCheckMobileScreen();
|
||||||
// const imgWidth = useMemo(() => {
|
// const imgWidth = useMemo(() => {
|
||||||
|
|
@ -30,6 +36,8 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
||||||
question={question}
|
question={question}
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
showAnswer={showAnswer}
|
showAnswer={showAnswer}
|
||||||
|
students={students}
|
||||||
|
isDisplayOnly={isDisplayOnly}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
@ -39,6 +47,8 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
||||||
question={question}
|
question={question}
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
showAnswer={showAnswer}
|
showAnswer={showAnswer}
|
||||||
|
students={students}
|
||||||
|
isDisplayOnly={isDisplayOnly}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
@ -50,6 +60,8 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
||||||
question={question}
|
question={question}
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
showAnswer={showAnswer}
|
showAnswer={showAnswer}
|
||||||
|
students={students}
|
||||||
|
isDisplayOnly={isDisplayOnly}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -58,6 +70,8 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
||||||
question={question}
|
question={question}
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
showAnswer={showAnswer}
|
showAnswer={showAnswer}
|
||||||
|
students={students}
|
||||||
|
isDisplayOnly={isDisplayOnly}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -69,6 +83,8 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
||||||
question={question}
|
question={question}
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
showAnswer={showAnswer}
|
showAnswer={showAnswer}
|
||||||
|
students={students}
|
||||||
|
isDisplayOnly={isDisplayOnly}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,48 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import '../questionStyle.css';
|
import '../questionStyle.css';
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||||
import { ShortAnswerQuestion } from 'gift-pegjs';
|
import { ShortAnswerQuestion } from 'gift-pegjs';
|
||||||
|
import { StudentType } from 'src/Types/StudentType';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: ShortAnswerQuestion;
|
question: ShortAnswerQuestion;
|
||||||
handleOnSubmitAnswer?: (answer: string) => void;
|
handleOnSubmitAnswer?: (answer: string) => void;
|
||||||
showAnswer?: boolean;
|
showAnswer?: boolean;
|
||||||
|
students?: StudentType[];
|
||||||
|
isDisplayOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const { question, showAnswer, handleOnSubmitAnswer } = props;
|
const { question, showAnswer, handleOnSubmitAnswer, students, isDisplayOnly } = props;
|
||||||
const [answer, setAnswer] = useState<string>();
|
const [answer, setAnswer] = useState<string>();
|
||||||
|
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
||||||
|
const [correctAnswerRate, setCorrectAnswerRate] = useState<number>(0);
|
||||||
|
|
||||||
|
const toggleShowCorrectAnswers = () => {
|
||||||
|
setShowCorrectAnswers(!showCorrectAnswers);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (showCorrectAnswers && students) {
|
||||||
|
calculateCorrectAnswerRate();
|
||||||
|
}
|
||||||
|
}, [showCorrectAnswers, students]);
|
||||||
|
|
||||||
|
const calculateCorrectAnswerRate = () => {
|
||||||
|
if (!students || students.length === 0) {
|
||||||
|
setCorrectAnswerRate(0); // Safeguard against undefined or empty student array
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalSubmissions = students.length;
|
||||||
|
const correctSubmissions = students.filter(student =>
|
||||||
|
student.answers.some(ans =>
|
||||||
|
ans.idQuestion === Number(question.id) && ans.isCorrect
|
||||||
|
)
|
||||||
|
).length;
|
||||||
|
setCorrectAnswerRate((correctSubmissions / totalSubmissions) * 100);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="question-wrapper">
|
<div className="question-wrapper">
|
||||||
|
|
@ -61,6 +91,33 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isDisplayOnly && (
|
||||||
|
<>
|
||||||
|
<div style={{ marginTop: '10px' }}>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={toggleShowCorrectAnswers}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{showCorrectAnswers ? "Masquer les résultats" : "Afficher les résultats"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
visibility: showCorrectAnswers ? 'visible' : 'hidden'
|
||||||
|
}}>
|
||||||
|
<div>
|
||||||
|
Taux de réponse correcte:
|
||||||
|
</div>
|
||||||
|
<div className="progress-bar-container">
|
||||||
|
<div className="progress-bar-fill" style={{ width: `${correctAnswerRate}%` }}></div>
|
||||||
|
<div className="progress-bar-text">
|
||||||
|
{correctAnswerRate.toFixed(1)}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,63 @@ import '../questionStyle.css';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import { TrueFalseQuestion } from 'gift-pegjs';
|
import { TrueFalseQuestion } from 'gift-pegjs';
|
||||||
import { FormattedTextTemplate } from 'src/components/GiftTemplate/templates/TextTypeTemplate';
|
import { FormattedTextTemplate } from 'src/components/GiftTemplate/templates/TextTypeTemplate';
|
||||||
|
import { StudentType } from 'src/Types/StudentType';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: TrueFalseQuestion;
|
question: TrueFalseQuestion;
|
||||||
handleOnSubmitAnswer?: (answer: boolean) => void;
|
handleOnSubmitAnswer?: (answer: boolean) => void;
|
||||||
showAnswer?: boolean;
|
showAnswer?: boolean;
|
||||||
|
students?: StudentType[];
|
||||||
|
isDisplayOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const { question, showAnswer, handleOnSubmitAnswer } =
|
const { question, showAnswer, handleOnSubmitAnswer, students, isDisplayOnly } = props;
|
||||||
props;
|
|
||||||
const [answer, setAnswer] = useState<boolean | undefined>(undefined);
|
const [answer, setAnswer] = useState<boolean | undefined>(undefined);
|
||||||
|
const [pickRates, setPickRates] = useState<{ trueRate: number, falseRate: number }>({ trueRate: 0, falseRate: 0 });
|
||||||
|
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAnswer(undefined);
|
setAnswer(undefined);
|
||||||
}, [question]);
|
calculatePickRates();
|
||||||
|
}, [question, students]);
|
||||||
|
|
||||||
const selectedTrue = answer ? 'selected' : '';
|
const selectedTrue = answer ? 'selected' : '';
|
||||||
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
||||||
|
|
||||||
|
const toggleShowCorrectAnswers = () => {
|
||||||
|
setShowCorrectAnswers(!showCorrectAnswers);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calcul le pick rate de chaque réponse
|
||||||
|
const calculatePickRates = () => {
|
||||||
|
if (!students) {
|
||||||
|
setPickRates({ trueRate: 0, falseRate: 0 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalAnswers = students.length;
|
||||||
|
|
||||||
|
const trueAnswers = students.filter(student =>
|
||||||
|
student.answers.some(ans =>
|
||||||
|
ans.idQuestion === Number(question.id) && ans.answer === true
|
||||||
|
)).length;
|
||||||
|
|
||||||
|
const falseAnswers = students.filter(student =>
|
||||||
|
student.answers.some(ans =>
|
||||||
|
ans.idQuestion === Number(question.id) && ans.answer === false
|
||||||
|
)).length;
|
||||||
|
|
||||||
|
if (totalAnswers > 0) {
|
||||||
|
setPickRates({
|
||||||
|
trueRate: (trueAnswers / totalAnswers) * 100,
|
||||||
|
falseRate: (falseAnswers / totalAnswers) * 100
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setPickRates({ trueRate: 0, falseRate: 0 });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="question-container">
|
<div className="question-container">
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
|
|
@ -29,23 +68,45 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="choices-wrapper mb-1">
|
<div className="choices-wrapper mb-1">
|
||||||
<Button
|
<Button
|
||||||
className="button-wrapper"
|
className={`button-wrapper ${selectedTrue}`}
|
||||||
onClick={() => !showAnswer && setAnswer(true)}
|
onClick={() => !showCorrectAnswers && setAnswer(true)}
|
||||||
fullWidth
|
fullWidth
|
||||||
>
|
>
|
||||||
{showAnswer? (<div> {(question.isTrue ? '✅' : '❌')}</div>):``}
|
|
||||||
<div className={`circle ${selectedTrue}`}>V</div>
|
<div className={`circle ${selectedTrue}`}>V</div>
|
||||||
<div className={`answer-text ${selectedTrue}`}>Vrai</div>
|
<div
|
||||||
|
className={`answer-text ${selectedTrue}`}
|
||||||
|
style={showCorrectAnswers ? {
|
||||||
|
backgroundImage: `linear-gradient(to right, ${question.isTrue ? 'lightgreen' : 'lightcoral'} ${pickRates.trueRate}%, transparent ${pickRates.trueRate}%)`
|
||||||
|
} : {}}
|
||||||
|
>
|
||||||
|
Vrai
|
||||||
|
</div>
|
||||||
|
{showCorrectAnswers && <div>{question.isTrue ? '✅' : '❌'}</div>}
|
||||||
|
{showCorrectAnswers && (
|
||||||
|
<div className="pick-rate">{pickRates.trueRate.toFixed(1)}%</div>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="button-wrapper"
|
className={`button-wrapper ${selectedFalse}`}
|
||||||
onClick={() => !showAnswer && setAnswer(false)}
|
onClick={() => !showCorrectAnswers && setAnswer(false)}
|
||||||
fullWidth
|
fullWidth
|
||||||
>
|
>
|
||||||
{showAnswer? (<div> {(!question.isTrue ? '✅' : '❌')}</div>):``}
|
|
||||||
<div className={`circle ${selectedFalse}`}>F</div>
|
<div className={`circle ${selectedFalse}`}>F</div>
|
||||||
<div className={`answer-text ${selectedFalse}`}>Faux</div>
|
<div
|
||||||
|
className={`answer-text ${selectedFalse}`}
|
||||||
|
style={showCorrectAnswers ? {
|
||||||
|
backgroundImage: `linear-gradient(to right, ${!question.isTrue ? 'lightgreen' : 'lightcoral'} ${pickRates.falseRate}%, transparent ${pickRates.falseRate}%)`,
|
||||||
|
} : {}}
|
||||||
|
>
|
||||||
|
Faux
|
||||||
|
</div>
|
||||||
|
{showCorrectAnswers && <div>{!question.isTrue ? '✅' : '❌'}</div>}
|
||||||
|
{showCorrectAnswers && (
|
||||||
|
<div className="pick-rate">{pickRates.falseRate.toFixed(1)}%</div>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{/* selected TRUE, show True feedback if it exists */}
|
{/* selected TRUE, show True feedback if it exists */}
|
||||||
{showAnswer && answer && question.trueFormattedFeedback && (
|
{showAnswer && answer && question.trueFormattedFeedback && (
|
||||||
|
|
@ -75,6 +136,18 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
Répondre
|
Répondre
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isDisplayOnly && (
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={toggleShowCorrectAnswers}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{showCorrectAnswers ? "Masquer les résultats" : "Afficher les résultats"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -150,3 +150,30 @@
|
||||||
.choices-wrapper {
|
.choices-wrapper {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-bar-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #FEFEFE;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar-fill {
|
||||||
|
height: 100%;
|
||||||
|
background-color: lightgreen;
|
||||||
|
width: 0%;
|
||||||
|
transition: width 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar-text {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
top: 0;
|
||||||
|
line-height: 20px;
|
||||||
|
color: Black;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -485,6 +485,8 @@ const ManageRoom: React.FC = () => {
|
||||||
<QuestionDisplay
|
<QuestionDisplay
|
||||||
showAnswer={false}
|
showAnswer={false}
|
||||||
question={currentQuestion?.question as Question}
|
question={currentQuestion?.question as Question}
|
||||||
|
students={students}
|
||||||
|
isDisplayOnly={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue