mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Changed questions display
This commit is contained in:
parent
99c6432105
commit
8db658e7a0
10 changed files with 122 additions and 77 deletions
19
client/package-lock.json
generated
19
client/package-lock.json
generated
|
|
@ -19,6 +19,7 @@
|
||||||
"@mui/material": "^6.4.6",
|
"@mui/material": "^6.4.6",
|
||||||
"@types/uuid": "^9.0.7",
|
"@types/uuid": "^9.0.7",
|
||||||
"axios": "^1.8.1",
|
"axios": "^1.8.1",
|
||||||
|
"bootstrap": "^5.3.3",
|
||||||
"dompurify": "^3.2.3",
|
"dompurify": "^3.2.3",
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"gift-pegjs": "^2.0.0-beta.1",
|
"gift-pegjs": "^2.0.0-beta.1",
|
||||||
|
|
@ -5536,6 +5537,24 @@
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/bootstrap": {
|
||||||
|
"version": "5.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
||||||
|
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/twbs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/bootstrap"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"peerDependencies": {
|
||||||
|
"@popperjs/core": "^2.11.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
"@mui/material": "^6.4.6",
|
"@mui/material": "^6.4.6",
|
||||||
"@types/uuid": "^9.0.7",
|
"@types/uuid": "^9.0.7",
|
||||||
"axios": "^1.8.1",
|
"axios": "^1.8.1",
|
||||||
|
"bootstrap": "^5.3.3",
|
||||||
"dompurify": "^3.2.3",
|
"dompurify": "^3.2.3",
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"gift-pegjs": "^2.0.0-beta.1",
|
"gift-pegjs": "^2.0.0-beta.1",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { TableCell, TableHead, TableRow } from "@mui/material";
|
import { TableCell, TableHead, TableRow } from "@mui/material";
|
||||||
|
|
||||||
interface LiveResultsHeaderProps {
|
interface LiveResultsHeaderProps {
|
||||||
|
|
@ -10,6 +10,12 @@ const LiveResultsTableHeader: React.FC<LiveResultsHeaderProps> = ({
|
||||||
maxQuestions,
|
maxQuestions,
|
||||||
showSelectedQuestion,
|
showSelectedQuestion,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [selectedQuestionIndex, setSelectedQuestionIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const handleQuestionClick = (index: number) => {
|
||||||
|
setSelectedQuestionIndex(index);
|
||||||
|
showSelectedQuestion(index);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableHead>
|
<TableHead>
|
||||||
|
|
@ -25,9 +31,10 @@ const LiveResultsTableHeader: React.FC<LiveResultsHeaderProps> = ({
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: 'rgba(224, 224, 224, 1)'
|
borderColor: 'rgba(224, 224, 224, 1)',
|
||||||
|
backgroundColor: selectedQuestionIndex === index ? '#dedede' : 'transparent'
|
||||||
}}
|
}}
|
||||||
onClick={() => showSelectedQuestion(index)}
|
onClick={() => handleQuestionClick(index)}
|
||||||
>
|
>
|
||||||
<div className="text-base text-bold blue">{`Q${index + 1}`}</div>
|
<div className="text-base text-bold blue">{`Q${index + 1}`}</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ 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-container" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -76,7 +76,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
{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 ? {
|
const rateStyle = showCorrectAnswers ? {
|
||||||
backgroundImage: `linear-gradient(to right, ${choice.isCorrect ? 'lightgreen' : 'lightcoral'} ${pickRates[i]}%, transparent ${pickRates[i]}%)`,
|
backgroundImage: `linear-gradient(to right, ${choice.isCorrect ? 'royalblue' : 'orange'} ${pickRates[i]}%, transparent ${pickRates[i]}%)`,
|
||||||
color: 'black'
|
color: 'black'
|
||||||
} : {};
|
} : {};
|
||||||
return (
|
return (
|
||||||
|
|
@ -114,12 +114,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedFeedback) }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{showCorrectAnswers && <div>{choice.isCorrect ? '✅' : '❌'}</div>}
|
{showCorrectAnswers && <div className="pick-rate">{choice.isCorrect ? '✅' : '❌'} {pickRates[i].toFixed(1)}%</div>}
|
||||||
{showCorrectAnswers && (
|
|
||||||
<div className="pick-rate">
|
|
||||||
{pickRates[i].toFixed(1)}%
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
||||||
const [correctAnswerRate, setCorrectAnswerRate] = useState<number>(0);
|
const [correctAnswerRate, setCorrectAnswerRate] = useState<number>(0);
|
||||||
|
const [submissionCounts, setSubmissionCounts] = useState({
|
||||||
|
correctSubmissions: 0,
|
||||||
|
totalSubmissions: 0
|
||||||
|
});
|
||||||
|
|
||||||
const toggleShowCorrectAnswers = () => {
|
const toggleShowCorrectAnswers = () => {
|
||||||
setShowCorrectAnswers(!showCorrectAnswers);
|
setShowCorrectAnswers(!showCorrectAnswers);
|
||||||
|
|
@ -42,7 +46,7 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
const calculateCorrectAnswerRate = () => {
|
const calculateCorrectAnswerRate = () => {
|
||||||
if (!students || students.length === 0) {
|
if (!students || students.length === 0) {
|
||||||
setCorrectAnswerRate(0); // Safeguard against undefined or empty student array
|
setSubmissionCounts({ correctSubmissions: 0, totalSubmissions: 0 });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,6 +56,12 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
ans.idQuestion === Number(question.id) && ans.isCorrect
|
ans.idQuestion === Number(question.id) && ans.isCorrect
|
||||||
)
|
)
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
|
setSubmissionCounts({
|
||||||
|
correctSubmissions,
|
||||||
|
totalSubmissions
|
||||||
|
});
|
||||||
|
|
||||||
setCorrectAnswerRate((correctSubmissions / totalSubmissions) * 100);
|
setCorrectAnswerRate((correctSubmissions / totalSubmissions) * 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -139,7 +149,7 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
visibility: showCorrectAnswers ? 'visible' : 'hidden'
|
visibility: showCorrectAnswers ? 'visible' : 'hidden'
|
||||||
}}>
|
}}>
|
||||||
<div>
|
<div>
|
||||||
Taux de réponse correcte:
|
Taux de réponse correcte: {submissionCounts.correctSubmissions}/{submissionCounts.totalSubmissions}
|
||||||
</div>
|
</div>
|
||||||
<div className="progress-bar-container">
|
<div className="progress-bar-container">
|
||||||
<div className="progress-bar-fill" style={{ width: `${correctAnswerRate}%` }}></div>
|
<div className="progress-bar-fill" style={{ width: `${correctAnswerRate}%` }}></div>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
||||||
handleOnSubmitAnswer,
|
handleOnSubmitAnswer,
|
||||||
showAnswer,
|
showAnswer,
|
||||||
students,
|
students,
|
||||||
isDisplayOnly = false
|
isDisplayOnly = false,
|
||||||
answer,
|
answer,
|
||||||
}) => {
|
}) => {
|
||||||
// const isMobile = useCheckMobileScreen();
|
// const isMobile = useCheckMobileScreen();
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
||||||
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
const [showCorrectAnswers, setShowCorrectAnswers] = useState(false);
|
||||||
const [correctAnswerRate, setCorrectAnswerRate] = useState<number>(0);
|
const [correctAnswerRate, setCorrectAnswerRate] = useState<number>(0);
|
||||||
|
const [submissionCounts, setSubmissionCounts] = useState({
|
||||||
|
correctSubmissions: 0,
|
||||||
|
totalSubmissions: 0
|
||||||
|
});
|
||||||
|
|
||||||
const toggleShowCorrectAnswers = () => {
|
const toggleShowCorrectAnswers = () => {
|
||||||
setShowCorrectAnswers(!showCorrectAnswers);
|
setShowCorrectAnswers(!showCorrectAnswers);
|
||||||
|
|
@ -39,7 +43,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
const calculateCorrectAnswerRate = () => {
|
const calculateCorrectAnswerRate = () => {
|
||||||
if (!students || students.length === 0) {
|
if (!students || students.length === 0) {
|
||||||
setCorrectAnswerRate(0); // Safeguard against undefined or empty student array
|
setSubmissionCounts({ correctSubmissions: 0, totalSubmissions: 0 });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,63 +53,69 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
ans.idQuestion === Number(question.id) && ans.isCorrect
|
ans.idQuestion === Number(question.id) && ans.isCorrect
|
||||||
)
|
)
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
|
setSubmissionCounts({
|
||||||
|
correctSubmissions,
|
||||||
|
totalSubmissions
|
||||||
|
});
|
||||||
|
|
||||||
setCorrectAnswerRate((correctSubmissions / totalSubmissions) * 100);
|
setCorrectAnswerRate((correctSubmissions / totalSubmissions) * 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="question-wrapper">
|
<div className="question-wrapper">
|
||||||
<div className="question content">
|
<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>
|
<span>
|
||||||
<strong>La bonne réponse est: </strong>
|
<strong>La bonne réponse est: </strong>
|
||||||
|
|
||||||
{question.choices.map((choice) => (
|
{question.choices.map((choice) => (
|
||||||
<div key={choice.text} className="mb-1">
|
<div key={choice.text} className="mb-1">
|
||||||
{choice.text}
|
{choice.text}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<strong>Votre réponse est: </strong>{answer}
|
<strong>Votre réponse est: </strong>{answer}
|
||||||
</span>
|
</span>
|
||||||
</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>}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="answer-wrapper mb-1">
|
<div className="answer-wrapper mb-1">
|
||||||
<TextField
|
<TextField
|
||||||
type="text"
|
type="text"
|
||||||
id={question.formattedStem.text}
|
id={question.formattedStem.text}
|
||||||
name={question.formattedStem.text}
|
name={question.formattedStem.text}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setAnswer(e.target.value);
|
setAnswer(e.target.value);
|
||||||
}}
|
}}
|
||||||
disabled={showAnswer}
|
disabled={showAnswer}
|
||||||
aria-label="short-answer-input"
|
aria-label="short-answer-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{handleOnSubmitAnswer && (
|
{handleOnSubmitAnswer && (
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
answer !== undefined &&
|
answer !== undefined &&
|
||||||
handleOnSubmitAnswer &&
|
handleOnSubmitAnswer &&
|
||||||
handleOnSubmitAnswer(answer)
|
handleOnSubmitAnswer(answer)
|
||||||
}
|
}
|
||||||
disabled={answer === null || answer === ''}
|
disabled={answer === null || answer === ''}
|
||||||
>
|
>
|
||||||
Répondre
|
Répondre
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isDisplayOnly && (
|
{isDisplayOnly && (
|
||||||
<>
|
<>
|
||||||
|
|
@ -122,7 +132,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
visibility: showCorrectAnswers ? 'visible' : 'hidden'
|
visibility: showCorrectAnswers ? 'visible' : 'hidden'
|
||||||
}}>
|
}}>
|
||||||
<div>
|
<div>
|
||||||
Taux de réponse correcte:
|
Taux de réponse correcte: {submissionCounts.correctSubmissions}/{submissionCounts.totalSubmissions}
|
||||||
</div>
|
</div>
|
||||||
<div className="progress-bar-container">
|
<div className="progress-bar-container">
|
||||||
<div className="progress-bar-fill" style={{ width: `${correctAnswerRate}%` }}></div>
|
<div className="progress-bar-fill" style={{ width: `${correctAnswerRate}%` }}></div>
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("passedAnswer", passedAnswer);
|
|
||||||
if (passedAnswer === true || passedAnswer === false) {
|
if (passedAnswer === true || passedAnswer === false) {
|
||||||
setAnswer(passedAnswer);
|
setAnswer(passedAnswer);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -96,15 +95,14 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
<div className={`circle ${selectedTrue}`}>V</div>
|
<div className={`circle ${selectedTrue}`}>V</div>
|
||||||
<div className={`answer-text ${selectedTrue}`}
|
<div className={`answer-text ${selectedTrue}`}
|
||||||
style={showCorrectAnswers ? {
|
style={showCorrectAnswers ? {
|
||||||
backgroundImage: `linear-gradient(to right, ${question.isTrue ? 'lightgreen' : 'lightcoral'} ${pickRates.trueRate}%, transparent ${pickRates.trueRate}%)`
|
backgroundImage: `linear-gradient(to right, ${question.isTrue ? 'royalblue' : 'orange'} ${pickRates.trueRate}%, transparent ${pickRates.trueRate}%)`
|
||||||
} : {}}
|
} : {}}
|
||||||
>
|
>
|
||||||
Vrai
|
Vrai
|
||||||
</div>
|
</div>
|
||||||
{showCorrectAnswers && (
|
{showCorrectAnswers && (
|
||||||
<>
|
<>
|
||||||
<div>{question.isTrue ? '✅' : '❌'}</div>
|
<div className="pick-rate">{question.isTrue ? '✅' : '❌'} {pickRates.trueRate.toFixed(1)}%</div>
|
||||||
<div className="pick-rate">{pickRates.trueRate.toFixed(1)}%</div>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -125,7 +123,7 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
<div
|
<div
|
||||||
className={`answer-text ${selectedFalse}`}
|
className={`answer-text ${selectedFalse}`}
|
||||||
style={showCorrectAnswers ? {
|
style={showCorrectAnswers ? {
|
||||||
backgroundImage: `linear-gradient(to right, ${!question.isTrue ? 'lightgreen' : 'lightcoral'} ${pickRates.falseRate}%, transparent ${pickRates.falseRate}%)`,
|
backgroundImage: `linear-gradient(to right, ${!question.isTrue ? 'royalblue' : 'orange'} ${pickRates.falseRate}%, transparent ${pickRates.falseRate}%)`,
|
||||||
} : {}}
|
} : {}}
|
||||||
>
|
>
|
||||||
Faux
|
Faux
|
||||||
|
|
@ -133,8 +131,7 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
{showCorrectAnswers && (
|
{showCorrectAnswers && (
|
||||||
<>
|
<>
|
||||||
<div>{!question.isTrue ? '✅' : '❌'}</div>
|
<div className="pick-rate">{!question.isTrue ? '✅' : '❌'} {pickRates.falseRate.toFixed(1)}%</div>
|
||||||
<div className="pick-rate">{pickRates.falseRate.toFixed(1)}%</div>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@
|
||||||
|
|
||||||
.progress-bar-fill {
|
.progress-bar-fill {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: lightgreen;
|
background-color: royalblue;
|
||||||
width: 0%;
|
width: 0%;
|
||||||
transition: width 0.6s ease;
|
transition: width 0.6s ease;
|
||||||
}
|
}
|
||||||
|
|
@ -196,3 +196,8 @@
|
||||||
color: Black;
|
color: Black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pick-rate{
|
||||||
|
color: rgba(0,0,0,1);
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { BrowserRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import { ThemeProvider, createTheme } from '@mui/material';
|
import { ThemeProvider, createTheme } from '@mui/material';
|
||||||
import '@fortawesome/fontawesome-free/css/all.min.css';
|
import '@fortawesome/fontawesome-free/css/all.min.css';
|
||||||
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
|
||||||
import './cssReset.css';
|
import './cssReset.css';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue