mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Minor fixes
This commit is contained in:
parent
8db658e7a0
commit
490f4dab76
4 changed files with 116 additions and 104 deletions
|
|
@ -19,7 +19,7 @@ interface Props {
|
|||
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||
const { question, showAnswer, handleOnSubmitAnswer, students, isDisplayOnly, passedAnswer } = props;
|
||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
||||
const [pickRates, setPickRates] = useState<number[]>([]);
|
||||
const [pickRates, setPickRates] = useState<{ percentages: number[], counts: number[], totalCount: number }>({ percentages: [], counts: [], totalCount: 0 });
|
||||
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
||||
|
||||
let disableButton = false;
|
||||
|
|
@ -37,20 +37,26 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
|
||||
const calculatePickRates = () => {
|
||||
if (!students || students.length === 0) {
|
||||
setPickRates(new Array(question.choices.length).fill(0)); // Fill with 0 for each choice
|
||||
setPickRates({ percentages: new Array(question.choices.length).fill(0), counts: new Array(question.choices.length).fill(0), totalCount: 0 });
|
||||
return;
|
||||
}
|
||||
|
||||
const rates = question.choices.map(choice => {
|
||||
const choiceAnswers = students.filter(student =>
|
||||
|
||||
const rates: number[] = [];
|
||||
const counts: number[] = [];
|
||||
let totalResponses = 0;
|
||||
|
||||
question.choices.forEach(choice => {
|
||||
const choiceCount = students.filter(student =>
|
||||
student.answers.some(ans =>
|
||||
ans.idQuestion === Number(question.id) && ans.answer === choice.formattedText.text
|
||||
)
|
||||
).length;
|
||||
return (choiceAnswers / students.length) * 100;
|
||||
totalResponses += choiceCount;
|
||||
rates.push((choiceCount / students.length) * 100);
|
||||
counts.push(choiceCount);
|
||||
});
|
||||
|
||||
setPickRates(rates);
|
||||
|
||||
setPickRates({ percentages: rates, counts: counts, totalCount: totalResponses });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -76,7 +82,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
{question.choices.map((choice, i) => {
|
||||
const selected = answer === choice.formattedText.text ? 'selected' : '';
|
||||
const rateStyle = showCorrectAnswers ? {
|
||||
backgroundImage: `linear-gradient(to right, ${choice.isCorrect ? 'royalblue' : 'orange'} ${pickRates[i]}%, transparent ${pickRates[i]}%)`,
|
||||
backgroundImage: `linear-gradient(to right, ${choice.isCorrect ? 'lightgreen' : 'lightcoral'} ${pickRates.percentages[i]}%, transparent ${pickRates.percentages[i]}%)`,
|
||||
color: 'black'
|
||||
} : {};
|
||||
return (
|
||||
|
|
@ -114,7 +120,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedFeedback) }} />
|
||||
</div>
|
||||
)}
|
||||
{showCorrectAnswers && <div className="pick-rate">{choice.isCorrect ? '✅' : '❌'} {pickRates[i].toFixed(1)}%</div>}
|
||||
{showCorrectAnswers && <div className="pick-rate">{choice.isCorrect ? '✅' : '❌'} {`${pickRates.counts[i]}/${pickRates.totalCount} (${pickRates.percentages[i].toFixed(1)}%)`}</div>}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -63,87 +63,90 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="question-wrapper">
|
||||
<div>
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||
</div>
|
||||
{showAnswer ? (
|
||||
<>
|
||||
<div className="correct-answer-text mb-1">
|
||||
<span>
|
||||
<strong>La bonne réponse est: </strong>
|
||||
|
||||
{question.choices.map((choice) => (
|
||||
<div key={choice.text} className="mb-1">
|
||||
{choice.text}
|
||||
</div>
|
||||
))}
|
||||
</span>
|
||||
<span>
|
||||
<strong>Votre réponse est: </strong>{answer}
|
||||
</span>
|
||||
</div>
|
||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||
</div>}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="answer-wrapper mb-1">
|
||||
<TextField
|
||||
type="text"
|
||||
id={question.formattedStem.text}
|
||||
name={question.formattedStem.text}
|
||||
onChange={(e) => {
|
||||
setAnswer(e.target.value);
|
||||
}}
|
||||
disabled={showAnswer}
|
||||
aria-label="short-answer-input"
|
||||
/>
|
||||
</div>
|
||||
{handleOnSubmitAnswer && (
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
answer !== undefined &&
|
||||
handleOnSubmitAnswer &&
|
||||
handleOnSubmitAnswer(answer)
|
||||
}
|
||||
disabled={answer === null || answer === ''}
|
||||
>
|
||||
Répondre
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<><div className="container question-wrapper">
|
||||
<div className="row justify-content-center">
|
||||
<div className="col-auto">
|
||||
<div>
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||
</div>
|
||||
{showAnswer ? (
|
||||
<>
|
||||
<div className="correct-answer-text mb-1">
|
||||
<span>
|
||||
<strong>La bonne réponse est: </strong>
|
||||
|
||||
{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: {submissionCounts.correctSubmissions}/{submissionCounts.totalSubmissions}
|
||||
</div>
|
||||
<div className="progress-bar-container">
|
||||
<div className="progress-bar-fill" style={{ width: `${correctAnswerRate}%` }}></div>
|
||||
<div className="progress-bar-text">
|
||||
{correctAnswerRate.toFixed(1)}%
|
||||
{question.choices.map((choice) => (
|
||||
<div key={choice.text} className="mb-1">
|
||||
{choice.text}
|
||||
</div>
|
||||
))}
|
||||
</span>
|
||||
<span>
|
||||
<strong>Votre réponse est: </strong>{answer}
|
||||
</span>
|
||||
</div>
|
||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||
</div>}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="answer-wrapper mb-1">
|
||||
<TextField
|
||||
type="text"
|
||||
id={question.formattedStem.text}
|
||||
name={question.formattedStem.text}
|
||||
onChange={(e) => {
|
||||
setAnswer(e.target.value);
|
||||
} }
|
||||
disabled={showAnswer}
|
||||
aria-label="short-answer-input" />
|
||||
</div>
|
||||
{handleOnSubmitAnswer && (
|
||||
<div className="col-auto d-flex flex-column align-items-center">
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => answer !== undefined &&
|
||||
handleOnSubmitAnswer &&
|
||||
handleOnSubmitAnswer(answer)}
|
||||
disabled={answer === null || answer === ''}
|
||||
>
|
||||
Répondre
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{isDisplayOnly && (
|
||||
<>
|
||||
<div className="col-auto d-flex flex-column align-items-center">
|
||||
<Button
|
||||
style={{ marginTop: '10px' }}
|
||||
variant="outlined"
|
||||
onClick={toggleShowCorrectAnswers}
|
||||
color="primary"
|
||||
>
|
||||
{showCorrectAnswers ? "Masquer les résultats" : "Afficher les résultats"}
|
||||
</Button>
|
||||
{showCorrectAnswers && (
|
||||
<div>
|
||||
<div>
|
||||
Taux de réponse correcte: {submissionCounts.correctSubmissions}/{submissionCounts.totalSubmissions}
|
||||
</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>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div></>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,13 @@ interface Props {
|
|||
const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||
const { question, showAnswer, handleOnSubmitAnswer, students, passedAnswer, isDisplayOnly } = props;
|
||||
const [answer, setAnswer] = useState<boolean | undefined>(undefined);
|
||||
const [pickRates, setPickRates] = useState<{ trueRate: number, falseRate: number }>({ trueRate: 0, falseRate: 0 });
|
||||
const [pickRates, setPickRates] = useState<{ trueRate: number, falseRate: number, trueCount: number, falseCount: number, totalCount: number }>({
|
||||
trueRate: 0,
|
||||
falseRate: 0,
|
||||
trueCount: 0,
|
||||
falseCount: 0,
|
||||
totalCount: 0
|
||||
});
|
||||
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
||||
|
||||
let disableButton = false;
|
||||
|
|
@ -54,36 +60,33 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
|||
// Calcul le pick rate de chaque réponse
|
||||
const calculatePickRates = () => {
|
||||
if (!students) {
|
||||
setPickRates({ trueRate: 0, falseRate: 0 });
|
||||
setPickRates({ trueRate: 0, falseRate: 0, trueCount: 0, falseCount: 0, totalCount: 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 });
|
||||
}
|
||||
setPickRates({
|
||||
trueRate: (trueAnswers / totalAnswers) * 100,
|
||||
falseRate: (falseAnswers / totalAnswers) * 100,
|
||||
trueCount: trueAnswers,
|
||||
falseCount: falseAnswers,
|
||||
totalCount: totalAnswers
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="question-container">
|
||||
<div className="question content">
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||
</div>
|
||||
<div className="choices-wrapper mb-1">
|
||||
<Button
|
||||
|
|
@ -102,7 +105,7 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
{showCorrectAnswers && (
|
||||
<>
|
||||
<div className="pick-rate">{question.isTrue ? '✅' : '❌'} {pickRates.trueRate.toFixed(1)}%</div>
|
||||
<div className="pick-rate">{question.isTrue ? '✅' : '❌'} {pickRates.trueRate}/{pickRates.totalCount} ({pickRates.trueRate.toFixed(1)}%)</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
@ -131,7 +134,7 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
|||
|
||||
{showCorrectAnswers && (
|
||||
<>
|
||||
<div className="pick-rate">{!question.isTrue ? '✅' : '❌'} {pickRates.falseRate.toFixed(1)}%</div>
|
||||
<div className="pick-rate">{!question.isTrue ? '✅' : '❌'} {pickRates.falseCount}/{pickRates.totalCount} ({pickRates.falseRate.toFixed(1)}%)</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -198,6 +198,6 @@
|
|||
|
||||
.pick-rate{
|
||||
color: rgba(0,0,0,1);
|
||||
min-width: 70px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue