mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Compare commits
2 commits
128ab6662e
...
6d714d1d77
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d714d1d77 | ||
|
|
e61c855492 |
12 changed files with 507 additions and 484 deletions
|
|
@ -13,6 +13,7 @@ import ResetPassword from './pages/AuthManager/providers/SimpleLogin/ResetPasswo
|
|||
import ManageRoom from './pages/Teacher/ManageRoom/ManageRoom';
|
||||
import QuizForm from './pages/Teacher/EditorQuiz/EditorQuiz';
|
||||
|
||||
|
||||
// Pages espace étudiant
|
||||
import JoinRoom from './pages/Student/JoinRoom/JoinRoom';
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ import Footer from './components/Footer/Footer';
|
|||
|
||||
import ApiService from './services/ApiService';
|
||||
import OAuthCallback from './pages/AuthManager/callback/AuthCallback';
|
||||
import { QuizProvider } from './pages/Student/JoinRoom/QuizProvider';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(ApiService.isLoggedIn());
|
||||
|
|
@ -78,13 +80,13 @@ const App: React.FC = () => {
|
|||
/>
|
||||
<Route
|
||||
path="/teacher/manage-room/:quizId/:roomName"
|
||||
element={isTeacherAuthenticated ? <ManageRoom /> : <Navigate to="/login" />}
|
||||
element={isTeacherAuthenticated ? <QuizProvider><ManageRoom /></QuizProvider> : <Navigate to="/login" />}
|
||||
/>
|
||||
|
||||
{/* Pages espace étudiant */}
|
||||
<Route
|
||||
path="/student/join-room"
|
||||
element={( !isRoomRequireAuthentication || isAuthenticated ) ? <JoinRoom /> : <Navigate to="/login" />}
|
||||
element={( !isRoomRequireAuthentication || isAuthenticated ) ? <QuizProvider><JoinRoom /></QuizProvider> : <Navigate to="/login" />}
|
||||
/>
|
||||
|
||||
{/* Pages authentification */}
|
||||
|
|
|
|||
|
|
@ -5,55 +5,52 @@ import { Button } from '@mui/material';
|
|||
import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
import { QuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
|
||||
interface Props {
|
||||
question: MultipleChoiceQuestion;
|
||||
handleOnSubmitAnswer?: (answer: AnswerType) => void;
|
||||
showAnswer?: boolean;
|
||||
passedAnswer?: AnswerType;
|
||||
}
|
||||
const MultipleChoiceQuestionDisplay: React.FC = () => {
|
||||
const { questions, index, answer, submitAnswer } = useQuizContext();
|
||||
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(answer));
|
||||
|
||||
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
||||
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(passedAnswer));
|
||||
const question = questions[Number(index)].question as MultipleChoiceQuestion;
|
||||
|
||||
const [answer, setAnswer] = useState<AnswerType>(() => {
|
||||
if (passedAnswer && passedAnswer.length > 0) {
|
||||
return passedAnswer;
|
||||
const [actualAnswer, setActualAnswer] = useState<AnswerType>(() => {
|
||||
if (answer && answer.length > 0) {
|
||||
return answer;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
let disableButton = false;
|
||||
if (handleOnSubmitAnswer === undefined) {
|
||||
if (submitAnswer === undefined) {
|
||||
disableButton = true;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(passedAnswer));
|
||||
if (passedAnswer !== undefined) {
|
||||
setAnswer(passedAnswer);
|
||||
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(answer));
|
||||
if (answer !== undefined) {
|
||||
setActualAnswer(answer);
|
||||
} else {
|
||||
setAnswer([]);
|
||||
setActualAnswer([]);
|
||||
}
|
||||
}, [passedAnswer, question.id]);
|
||||
}, [answer, index]);
|
||||
|
||||
const handleOnClickAnswer = (choice: string) => {
|
||||
setAnswer((prevAnswer) => {
|
||||
console.log(`handleOnClickAnswer -- setAnswer(): prevAnswer: ${prevAnswer}, choice: ${choice}`);
|
||||
setActualAnswer((answer) => {
|
||||
console.log(`handleOnClickAnswer -- setAnswer(): prevAnswer: ${answer}, 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];
|
||||
return answer.includes(choice) ? [] : [choice];
|
||||
} else {
|
||||
// Allow multiple selections if there are multiple correct answers
|
||||
if (prevAnswer.includes(choice)) {
|
||||
if (answer.includes(choice)) {
|
||||
// Remove the choice if it's already selected
|
||||
return prevAnswer.filter((selected) => selected !== choice);
|
||||
return answer.filter((selected) => selected !== choice);
|
||||
} else {
|
||||
// Add the choice if it's not already selected
|
||||
return [...prevAnswer, choice];
|
||||
return [...answer, choice];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -63,14 +60,16 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
const alphabet = alpha.map((x) => String.fromCharCode(x));
|
||||
|
||||
return (
|
||||
<QuizContext.Consumer>
|
||||
{({ showAnswer }) => (
|
||||
<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' : '';
|
||||
console.log(`answer: ${actualAnswer}, choice: ${choice.formattedText.text}`);
|
||||
const selected = actualAnswer.includes(choice.formattedText.text) ? 'selected' : '';
|
||||
return (
|
||||
<div key={choice.formattedText.text + i} className="choice-container">
|
||||
<Button
|
||||
|
|
@ -115,11 +114,11 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
{!showAnswer && handleOnSubmitAnswer && (
|
||||
{!showAnswer && submitAnswer && (
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
answer.length > 0 && handleOnSubmitAnswer && handleOnSubmitAnswer(answer)
|
||||
actualAnswer.length > 0 && submitAnswer && submitAnswer(actualAnswer)
|
||||
}
|
||||
disabled={answer.length === 0}
|
||||
>
|
||||
|
|
@ -127,6 +126,8 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</QuizContext.Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,26 +6,23 @@ import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemp
|
|||
import { NumericalQuestion, SimpleNumericalAnswer, RangeNumericalAnswer, HighLowNumericalAnswer } from 'gift-pegjs';
|
||||
import { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer, isMultipleNumericalAnswer } from 'gift-pegjs/typeGuards';
|
||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
import { QuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
|
||||
interface Props {
|
||||
question: NumericalQuestion;
|
||||
handleOnSubmitAnswer?: (answer: AnswerType) => void;
|
||||
showAnswer?: boolean;
|
||||
passedAnswer?: AnswerType;
|
||||
}
|
||||
const NumericalQuestionDisplay: React.FC = () => {
|
||||
const { questions, index, answer, submitAnswer } = useQuizContext();
|
||||
|
||||
const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } =
|
||||
props;
|
||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || []);
|
||||
const question = questions[Number(index)].question as NumericalQuestion;
|
||||
|
||||
const [actualAnswer, setActualAnswer] = useState<AnswerType>(answer || []);
|
||||
const correctAnswers = question.choices;
|
||||
let correctAnswer = '';
|
||||
|
||||
useEffect(() => {
|
||||
if (passedAnswer !== null && passedAnswer !== undefined) {
|
||||
setAnswer(passedAnswer);
|
||||
if (answer !== null && answer !== undefined) {
|
||||
setActualAnswer(answer);
|
||||
}
|
||||
}, [passedAnswer]);
|
||||
}, [answer]);
|
||||
|
||||
//const isSingleAnswer = correctAnswers.length === 1;
|
||||
|
||||
|
|
@ -44,6 +41,8 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<QuizContext.Consumer>
|
||||
{({ showAnswer }) => (
|
||||
<div className="question-wrapper">
|
||||
<div>
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||
|
|
@ -54,7 +53,7 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
|||
<strong>La bonne réponse est: </strong>
|
||||
{correctAnswer}</div>
|
||||
<span>
|
||||
<strong>Votre réponse est: </strong>{answer.toString()}
|
||||
<strong>Votre réponse est: </strong>{actualAnswer.toString()}
|
||||
</span>
|
||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||
|
|
@ -69,7 +68,7 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
|||
id={question.formattedStem.text}
|
||||
name={question.formattedStem.text}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAnswer([e.target.valueAsNumber]);
|
||||
setActualAnswer([e.target.valueAsNumber]);
|
||||
}}
|
||||
inputProps={{ 'data-testid': 'number-input' }}
|
||||
/>
|
||||
|
|
@ -79,22 +78,25 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
|||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||
</div>
|
||||
)}
|
||||
{handleOnSubmitAnswer && (
|
||||
{submitAnswer && (
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
answer !== undefined &&
|
||||
handleOnSubmitAnswer &&
|
||||
handleOnSubmitAnswer(answer)
|
||||
actualAnswer !== undefined &&
|
||||
submitAnswer &&
|
||||
submitAnswer(actualAnswer)
|
||||
}
|
||||
disabled={answer === undefined || answer === null || isNaN(answer[0] as number)}
|
||||
disabled={actualAnswer === undefined || actualAnswer === null || isNaN(actualAnswer[0] as number)}
|
||||
>
|
||||
Répondre
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
</div>
|
||||
)}
|
||||
</QuizContext.Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,40 +1,20 @@
|
|||
import React from 'react';
|
||||
import { Question } from 'gift-pegjs';
|
||||
|
||||
import TrueFalseQuestionDisplay from './TrueFalseQuestionDisplay/TrueFalseQuestionDisplay';
|
||||
import MultipleChoiceQuestionDisplay from './MultipleChoiceQuestionDisplay/MultipleChoiceQuestionDisplay';
|
||||
import NumericalQuestionDisplay from './NumericalQuestionDisplay/NumericalQuestionDisplay';
|
||||
import ShortAnswerQuestionDisplay from './ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay';
|
||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
// import useCheckMobileScreen from '../../services/useCheckMobileScreen';
|
||||
|
||||
interface QuestionProps {
|
||||
question: Question;
|
||||
handleOnSubmitAnswer?: (answer: AnswerType) => void;
|
||||
showAnswer?: boolean;
|
||||
answer?: AnswerType;
|
||||
|
||||
}
|
||||
const QuestionDisplay: React.FC<QuestionProps> = ({
|
||||
question,
|
||||
handleOnSubmitAnswer,
|
||||
showAnswer,
|
||||
answer,
|
||||
}) => {
|
||||
// const isMobile = useCheckMobileScreen();
|
||||
// const imgWidth = useMemo(() => {
|
||||
// return isMobile ? '100%' : '20%';
|
||||
// }, [isMobile]);
|
||||
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
|
||||
const QuestionDisplay: React.FC = () => {
|
||||
const { questions, index } = useQuizContext();
|
||||
const question = questions[Number(index)].question as Question;
|
||||
let questionTypeComponent = null;
|
||||
|
||||
switch (question?.type) {
|
||||
case 'TF':
|
||||
questionTypeComponent = (
|
||||
<TrueFalseQuestionDisplay
|
||||
question={question}
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
// showAnswer={showAnswer}
|
||||
passedAnswer={answer}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
|
@ -42,10 +22,6 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
|||
|
||||
questionTypeComponent = (
|
||||
<MultipleChoiceQuestionDisplay
|
||||
question={question}
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
showAnswer={showAnswer}
|
||||
passedAnswer={answer}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
|
@ -53,11 +29,6 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
|||
if (question.choices) {
|
||||
questionTypeComponent = (
|
||||
<NumericalQuestionDisplay
|
||||
question={question}
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
showAnswer={showAnswer}
|
||||
passedAnswer={answer}
|
||||
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -65,10 +36,6 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
|||
case 'Short':
|
||||
questionTypeComponent = (
|
||||
<ShortAnswerQuestionDisplay
|
||||
question={question}
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
showAnswer={showAnswer}
|
||||
passedAnswer={answer}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -4,28 +4,26 @@ import { Button, TextField } from '@mui/material';
|
|||
import { FormattedTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { ShortAnswerQuestion } from 'gift-pegjs';
|
||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
import { QuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
|
||||
interface Props {
|
||||
question: ShortAnswerQuestion;
|
||||
handleOnSubmitAnswer?: (answer: AnswerType) => void;
|
||||
showAnswer?: boolean;
|
||||
passedAnswer?: AnswerType;
|
||||
|
||||
}
|
||||
const ShortAnswerQuestionDisplay: React.FC = () => {
|
||||
|
||||
const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||
|
||||
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || []);
|
||||
const { questions, index, answer, submitAnswer } = useQuizContext();
|
||||
const [actualAnswer, setActualAnswer] = useState<AnswerType>(answer || []);
|
||||
const question = questions[Number(index)].question as ShortAnswerQuestion;
|
||||
|
||||
useEffect(() => {
|
||||
if (passedAnswer !== undefined) {
|
||||
setAnswer(passedAnswer);
|
||||
if (answer !== undefined) {
|
||||
setActualAnswer(answer);
|
||||
}
|
||||
}, [passedAnswer]);
|
||||
console.log("Answer" , answer);
|
||||
}, [answer]);
|
||||
console.log("Answer", actualAnswer);
|
||||
|
||||
return (
|
||||
<QuizContext.Consumer>
|
||||
{({ showAnswer }) => (
|
||||
<div className="question-wrapper">
|
||||
<div className="question content">
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||
|
|
@ -43,7 +41,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
|||
))}
|
||||
</span>
|
||||
<span>
|
||||
<strong>Votre réponse est: </strong>{answer}
|
||||
<strong>Votre réponse est: </strong>{actualAnswer}
|
||||
</span>
|
||||
</div>
|
||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||
|
|
@ -58,21 +56,21 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
|||
id={question.formattedStem.text}
|
||||
name={question.formattedStem.text}
|
||||
onChange={(e) => {
|
||||
setAnswer([e.target.value]);
|
||||
setActualAnswer([e.target.value]);
|
||||
}}
|
||||
disabled={showAnswer}
|
||||
aria-label="short-answer-input"
|
||||
/>
|
||||
</div>
|
||||
{handleOnSubmitAnswer && (
|
||||
{submitAnswer && (
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
answer !== undefined &&
|
||||
handleOnSubmitAnswer &&
|
||||
handleOnSubmitAnswer(answer)
|
||||
actualAnswer !== undefined &&
|
||||
submitAnswer &&
|
||||
submitAnswer(actualAnswer)
|
||||
}
|
||||
disabled={answer === null || answer === undefined || answer.length === 0}
|
||||
disabled={actualAnswer === null || actualAnswer === undefined || actualAnswer.length === 0}
|
||||
>
|
||||
Répondre
|
||||
</Button>
|
||||
|
|
@ -80,6 +78,8 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</QuizContext.Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,51 +4,43 @@ import '../questionStyle.css';
|
|||
import { Button } from '@mui/material';
|
||||
import { TrueFalseQuestion } from 'gift-pegjs';
|
||||
import { FormattedTextTemplate } from 'src/components/GiftTemplate/templates/TextTypeTemplate';
|
||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
import { QuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
|
||||
interface Props {
|
||||
question: TrueFalseQuestion;
|
||||
handleOnSubmitAnswer?: (answer: AnswerType) => void;
|
||||
// showAnswer?: boolean;
|
||||
passedAnswer?: AnswerType;
|
||||
}
|
||||
|
||||
const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||
const { question,
|
||||
// showAnswer,
|
||||
handleOnSubmitAnswer, passedAnswer } =
|
||||
props;
|
||||
const TrueFalseQuestionDisplay: React.FC = () => {
|
||||
|
||||
const [answer, setAnswer] = useState<boolean | undefined>(() => {
|
||||
if (passedAnswer && (passedAnswer[0] === true || passedAnswer[0] === false)) {
|
||||
return passedAnswer[0];
|
||||
const { questions, index, answer, submitAnswer } = useQuizContext();
|
||||
|
||||
const question = questions[Number(index)].question as TrueFalseQuestion;
|
||||
|
||||
const [actualAnswer, setActualAnswer] = useState<boolean | undefined>(() => {
|
||||
if (answer && (answer[0] === true || answer[0] === false)) {
|
||||
return answer[0];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
let disableButton = false;
|
||||
if (handleOnSubmitAnswer === undefined) {
|
||||
if (submitAnswer === undefined) {
|
||||
disableButton = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
console.log("passedAnswer", passedAnswer);
|
||||
if (passedAnswer && (passedAnswer[0] === true || passedAnswer[0] === false)) {
|
||||
setAnswer(passedAnswer[0]);
|
||||
console.log("passedAnswer", answer);
|
||||
if (answer && (answer[0] === true || answer[0] === false)) {
|
||||
setActualAnswer(answer[0]);
|
||||
} else {
|
||||
setAnswer(undefined);
|
||||
setActualAnswer(undefined);
|
||||
}
|
||||
}, [passedAnswer, question.id]);
|
||||
}, [answer, index]);
|
||||
|
||||
const handleOnClickAnswer = (choice: boolean) => {
|
||||
setAnswer(choice);
|
||||
setActualAnswer(choice);
|
||||
};
|
||||
|
||||
const selectedTrue = answer ? 'selected' : '';
|
||||
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
||||
const selectedTrue = actualAnswer ? 'selected' : '';
|
||||
const selectedFalse = actualAnswer !== undefined && !actualAnswer ? 'selected' : '';
|
||||
|
||||
return (
|
||||
<QuizContext.Consumer>
|
||||
|
|
@ -76,7 +68,7 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
|||
<div className={`circle ${selectedTrue}`}>V</div>
|
||||
<div className={`answer-text ${selectedTrue}`}>Vrai</div>
|
||||
|
||||
{showAnswer && answer && question.trueFormattedFeedback && (
|
||||
{showAnswer && actualAnswer && question.trueFormattedFeedback && (
|
||||
<div className="true-feedback mb-2">
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
|
|
@ -102,7 +94,7 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
|||
<div className={`circle ${selectedFalse}`}>F</div>
|
||||
<div className={`answer-text ${selectedFalse}`}>Faux</div>
|
||||
|
||||
{showAnswer && !answer && question.falseFormattedFeedback && (
|
||||
{showAnswer && !actualAnswer && question.falseFormattedFeedback && (
|
||||
<div className="false-feedback mb-2">
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
|
|
@ -126,15 +118,15 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
{!showAnswer && handleOnSubmitAnswer && (
|
||||
{!showAnswer && submitAnswer && (
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
answer !== undefined &&
|
||||
handleOnSubmitAnswer &&
|
||||
handleOnSubmitAnswer([answer])
|
||||
actualAnswer !== undefined &&
|
||||
submitAnswer &&
|
||||
submitAnswer([actualAnswer])
|
||||
}
|
||||
disabled={answer === undefined}
|
||||
disabled={actualAnswer === undefined}
|
||||
>
|
||||
Répondre
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,32 +1,16 @@
|
|||
// StudentModeQuiz.tsx
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import QuestionComponent from '../QuestionsDisplay/QuestionDisplay';
|
||||
import QuestionDisplay from '../QuestionsDisplay/QuestionDisplay';
|
||||
import '../../pages/Student/JoinRoom/joinRoom.css';
|
||||
import { QuestionType } from '../../Types/QuestionType';
|
||||
import { Button } from '@mui/material';
|
||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||
import { Question } from 'gift-pegjs';
|
||||
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
|
||||
interface StudentModeQuizProps {
|
||||
questions: QuestionType[];
|
||||
answers: AnswerSubmissionToBackendType[];
|
||||
submitAnswer: (_answer: AnswerType, _idQuestion: number) => void;
|
||||
disconnectWebSocket: () => void;
|
||||
}
|
||||
|
||||
const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
||||
questions,
|
||||
answers,
|
||||
submitAnswer,
|
||||
disconnectWebSocket
|
||||
}) => {
|
||||
const { setShowAnswer } = useQuizContext(); // Access setShowAnswer from context
|
||||
const StudentModeQuiz: React.FC = () => {
|
||||
const { questions, answers, setIsQuestionSent, disconnectWebSocket, setShowAnswer } = useQuizContext(); // Access setShowAnswer from context
|
||||
|
||||
const [questionInfos, setQuestion] = useState<QuestionType>(questions[0]);
|
||||
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
|
||||
|
||||
const previousQuestion = () => {
|
||||
setQuestion(questions[Number(questionInfos.question?.id) - 2]);
|
||||
|
|
@ -35,7 +19,7 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
|||
useEffect(() => {
|
||||
const savedAnswer = answers[Number(questionInfos.question.id) - 1]?.answer;
|
||||
console.log(`StudentModeQuiz: useEffect: savedAnswer: ${savedAnswer}`);
|
||||
setIsAnswerSubmitted(savedAnswer !== undefined);
|
||||
setIsQuestionSent(savedAnswer !== undefined);
|
||||
setShowAnswer(savedAnswer !== undefined); // Update showAnswer in context
|
||||
}, [questionInfos.question, answers, setShowAnswer]);
|
||||
|
||||
|
|
@ -43,13 +27,6 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
|||
setQuestion(questions[Number(questionInfos.question?.id)]);
|
||||
};
|
||||
|
||||
const handleOnSubmitAnswer = (answer: AnswerType) => {
|
||||
const idQuestion = Number(questionInfos.question.id) || -1;
|
||||
submitAnswer(answer, idQuestion);
|
||||
setIsAnswerSubmitted(true);
|
||||
setShowAnswer(true); // Update showAnswer in context when an answer is submitted
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="room">
|
||||
<div className="roomHeader">
|
||||
|
|
@ -63,12 +40,7 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
|||
</div>
|
||||
<div className="overflow-auto">
|
||||
<div className="question-component-container">
|
||||
<QuestionComponent
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
question={questionInfos.question as Question}
|
||||
showAnswer={isAnswerSubmitted} // Local state for showing answers
|
||||
answer={answers[Number(questionInfos.question.id) - 1]?.answer}
|
||||
/>
|
||||
<QuestionDisplay/>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '1rem' }}>
|
||||
<div>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -1,64 +1,55 @@
|
|||
// TeacherModeQuiz.tsx
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import QuestionComponent from '../QuestionsDisplay/QuestionDisplay';
|
||||
import QuestionDisplay from '../QuestionsDisplay/QuestionDisplay';
|
||||
import '../../pages/Student/JoinRoom/joinRoom.css';
|
||||
import { QuestionType } from '../../Types/QuestionType';
|
||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
|
||||
import { Question } from 'gift-pegjs';
|
||||
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
// import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
|
||||
interface TeacherModeQuizProps {
|
||||
questionInfos: QuestionType;
|
||||
answers: AnswerSubmissionToBackendType[];
|
||||
submitAnswer: (_answer: AnswerType, _idQuestion: number) => void;
|
||||
disconnectWebSocket: () => void;
|
||||
}
|
||||
|
||||
const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||
questionInfos,
|
||||
const TeacherModeQuiz: React.FC = () => {
|
||||
const {
|
||||
questions,
|
||||
answers,
|
||||
submitAnswer,
|
||||
disconnectWebSocket
|
||||
}) => {
|
||||
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
|
||||
const [isFeedbackDialogOpen, setIsFeedbackDialogOpen] = useState(false);
|
||||
const [answer, setAnswer] = useState<AnswerType>();
|
||||
setShowAnswer,
|
||||
disconnectWebSocket,
|
||||
index,
|
||||
isQuestionSent,
|
||||
setIsQuestionSent,
|
||||
answer,
|
||||
setAnswer,
|
||||
} = useQuizContext();
|
||||
|
||||
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
|
||||
|
||||
// arrive here the first time after waiting for next question
|
||||
useEffect(() => {
|
||||
console.log(`QuestionDisplay: questions: ${JSON.stringify(questions)}`);
|
||||
|
||||
console.log(`TeacherModeQuiz: useEffect: answers: ${JSON.stringify(answers)}`);
|
||||
console.log(`TeacherModeQuiz: useEffect: questionInfos.question.id: ${questionInfos.question.id} answer: ${answer}`);
|
||||
const oldAnswer = answers[Number(questionInfos.question.id) -1 ]?.answer;
|
||||
console.log(`TeacherModeQuiz: useEffect: questionInfos.question.id: ${index} answer: ${answer}`);
|
||||
const oldAnswer = answers[Number(index) -1 ]?.answer;
|
||||
console.log(`TeacherModeQuiz: useEffect: oldAnswer: ${oldAnswer}`);
|
||||
setAnswer(oldAnswer);
|
||||
setIsFeedbackDialogOpen(false);
|
||||
}, [questionInfos.question, answers]);
|
||||
setShowAnswer(false);
|
||||
setIsQuestionSent(false);
|
||||
}, [questions[Number(index)].question, answers]);
|
||||
|
||||
// handle showing the feedback dialog
|
||||
useEffect(() => {
|
||||
console.log(`TeacherModeQuiz: useEffect: answer: ${answer}`);
|
||||
setIsAnswerSubmitted(answer !== undefined);
|
||||
setIsFeedbackDialogOpen(answer !== undefined);
|
||||
setIsQuestionSent(answer !== undefined);
|
||||
}, [answer]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(`TeacherModeQuiz: useEffect: isAnswerSubmitted: ${isAnswerSubmitted}`);
|
||||
setIsFeedbackDialogOpen(isAnswerSubmitted);
|
||||
setIsQuestionSent(isAnswerSubmitted);
|
||||
setShowAnswer(isAnswerSubmitted);
|
||||
}, [isAnswerSubmitted]);
|
||||
|
||||
const handleOnSubmitAnswer = (answer: AnswerType) => {
|
||||
const idQuestion = Number(questionInfos.question.id) || -1;
|
||||
submitAnswer(answer, idQuestion);
|
||||
// setAnswer(answer);
|
||||
setIsFeedbackDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleFeedbackDialogClose = () => {
|
||||
setIsFeedbackDialogOpen(false);
|
||||
setIsQuestionSent(false);
|
||||
setIsAnswerSubmitted(true);
|
||||
};
|
||||
|
||||
|
|
@ -71,7 +62,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
|||
message={`Êtes-vous sûr de vouloir quitter?`} />
|
||||
|
||||
<div className='centerTitle'>
|
||||
<div className='title'>Question {questionInfos.question.id}</div>
|
||||
<div className='title'>Question {index}</div>
|
||||
</div>
|
||||
|
||||
<div className='dumb'></div>
|
||||
|
|
@ -83,15 +74,11 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
|||
En attente pour la prochaine question...
|
||||
</div>
|
||||
) : (
|
||||
<QuestionComponent
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
question={questionInfos.question as Question}
|
||||
answer={answer}
|
||||
/>
|
||||
<QuestionDisplay/>
|
||||
)}
|
||||
|
||||
<Dialog
|
||||
open={isFeedbackDialogOpen}
|
||||
open={isQuestionSent}
|
||||
onClose={handleFeedbackDialogClose}
|
||||
>
|
||||
<DialogTitle>Rétroaction</DialogTitle>
|
||||
|
|
@ -106,12 +93,8 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
|||
>Question : </div>
|
||||
</div>
|
||||
|
||||
<QuestionComponent
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
question={questionInfos.question as Question}
|
||||
showAnswer={true}
|
||||
answer={answer}
|
||||
|
||||
<QuestionDisplay
|
||||
//showAnswer={true}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
|
|
|||
|
|
@ -15,23 +15,38 @@ import LoadingButton from '@mui/lab/LoadingButton';
|
|||
|
||||
import LoginContainer from 'src/components/LoginContainer/LoginContainer'
|
||||
|
||||
import ApiService from '../../../services/ApiService'
|
||||
import { QuizProvider } from './QuizProvider';
|
||||
import { useQuizContext } from './QuizContext';
|
||||
|
||||
|
||||
export type AnswerType = Array<string | number | boolean>;
|
||||
|
||||
const JoinRoom: React.FC = () => {
|
||||
const [roomName, setRoomName] = useState('');
|
||||
const [username, setUsername] = useState(ApiService.getUsername());
|
||||
const {
|
||||
setQuestions,
|
||||
setAnswers,
|
||||
questions,
|
||||
username,
|
||||
setUsername,
|
||||
setDisconnectWebSocket,
|
||||
roomName,
|
||||
setRoomName,
|
||||
index,
|
||||
updateIndex,
|
||||
|
||||
} = useQuizContext();
|
||||
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
const [isWaitingForTeacher, setIsWaitingForTeacher] = useState(false);
|
||||
const [question, setQuestion] = useState<QuestionType>();
|
||||
const [quizMode, setQuizMode] = useState<string>();
|
||||
const [questions, setQuestions] = useState<QuestionType[]>([]);
|
||||
const [answers, setAnswers] = useState<AnswerSubmissionToBackendType[]>([]);
|
||||
const [connectionError, setConnectionError] = useState<string>('');
|
||||
const [isConnecting, setIsConnecting] = useState<boolean>(false);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Set the disconnectWebSocket function in the context
|
||||
setDisconnectWebSocket(() => disconnect);
|
||||
}, [socket]);
|
||||
|
||||
useEffect(() => {
|
||||
handleCreateSocket();
|
||||
return () => {
|
||||
|
|
@ -42,6 +57,7 @@ const JoinRoom: React.FC = () => {
|
|||
useEffect(() => {
|
||||
console.log(`JoinRoom: useEffect: questions: ${JSON.stringify(questions)}`);
|
||||
setAnswers(questions ? Array(questions.length).fill({} as AnswerSubmissionToBackendType) : []);
|
||||
console.log(`JoinRoom: useEffect: answers: ${JSON.stringify(questions)}`);
|
||||
}, [questions]);
|
||||
|
||||
|
||||
|
|
@ -58,13 +74,13 @@ const JoinRoom: React.FC = () => {
|
|||
console.log('JoinRoom: on(next-question): Received next-question:', question);
|
||||
setQuizMode('teacher');
|
||||
setIsWaitingForTeacher(false);
|
||||
setQuestion(question);
|
||||
updateIndex(Number(question.question.id));
|
||||
});
|
||||
socket.on('launch-teacher-mode', (questions: QuestionType[]) => {
|
||||
console.log('on(launch-teacher-mode): Received launch-teacher-mode:', questions);
|
||||
setQuizMode('teacher');
|
||||
setIsWaitingForTeacher(true);
|
||||
setQuestions([]); // clear out from last time (in case quiz is repeated)
|
||||
updateIndex(null);
|
||||
setQuestions(questions);
|
||||
// wait for next-question
|
||||
});
|
||||
|
|
@ -75,7 +91,7 @@ const JoinRoom: React.FC = () => {
|
|||
setIsWaitingForTeacher(false);
|
||||
setQuestions([]); // clear out from last time (in case quiz is repeated)
|
||||
setQuestions(questions);
|
||||
setQuestion(questions[0]);
|
||||
updateIndex(0);
|
||||
});
|
||||
socket.on('end-quiz', () => {
|
||||
disconnect();
|
||||
|
|
@ -105,7 +121,7 @@ const JoinRoom: React.FC = () => {
|
|||
// localStorage.clear();
|
||||
webSocketService.disconnect();
|
||||
setSocket(null);
|
||||
setQuestion(undefined);
|
||||
updateIndex(null);
|
||||
setIsWaitingForTeacher(false);
|
||||
setQuizMode('');
|
||||
setRoomName('');
|
||||
|
|
@ -127,25 +143,6 @@ const JoinRoom: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleOnSubmitAnswer = (answer: AnswerType, idQuestion: number) => {
|
||||
console.info(`JoinRoom: handleOnSubmitAnswer: answer: ${answer}, idQuestion: ${idQuestion}`);
|
||||
const answerData: AnswerSubmissionToBackendType = {
|
||||
roomName: roomName,
|
||||
answer: answer,
|
||||
username: username,
|
||||
idQuestion: idQuestion
|
||||
};
|
||||
// localStorage.setItem(`Answer${idQuestion}`, JSON.stringify(answer));
|
||||
setAnswers((prevAnswers) => {
|
||||
console.log(`JoinRoom: handleOnSubmitAnswer: prevAnswers: ${JSON.stringify(prevAnswers)}`);
|
||||
const newAnswers = [...prevAnswers]; // Create a copy of the previous answers array
|
||||
newAnswers[idQuestion - 1] = answerData; // Update the specific answer
|
||||
return newAnswers; // Return the new array
|
||||
});
|
||||
console.log(`JoinRoom: handleOnSubmitAnswer: answers: ${JSON.stringify(answers)}`);
|
||||
webSocketService.submitAnswer(answerData);
|
||||
};
|
||||
|
||||
const handleReturnKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter' && username && roomName) {
|
||||
handleSocket();
|
||||
|
|
@ -176,26 +173,16 @@ const JoinRoom: React.FC = () => {
|
|||
}
|
||||
|
||||
switch (quizMode) {
|
||||
|
||||
|
||||
case 'student':
|
||||
return (
|
||||
<QuizProvider>
|
||||
<StudentModeQuiz
|
||||
questions={questions}
|
||||
answers={answers}
|
||||
submitAnswer={handleOnSubmitAnswer}
|
||||
disconnectWebSocket={disconnect}
|
||||
/>
|
||||
</QuizProvider>
|
||||
<StudentModeQuiz />
|
||||
);
|
||||
case 'teacher':
|
||||
return (
|
||||
question && (
|
||||
<TeacherModeQuiz
|
||||
questionInfos={question}
|
||||
answers={answers}
|
||||
submitAnswer={handleOnSubmitAnswer}
|
||||
disconnectWebSocket={disconnect}
|
||||
/>
|
||||
index != null && (
|
||||
<TeacherModeQuiz />
|
||||
)
|
||||
);
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,12 +1,49 @@
|
|||
import React, { Dispatch, SetStateAction, useContext } from 'react';
|
||||
|
||||
import { QuestionType } from '../../../Types/QuestionType';
|
||||
import { AnswerSubmissionToBackendType } from '../../../services/WebsocketService';
|
||||
import { AnswerType } from './JoinRoom';
|
||||
|
||||
export const QuizContext = React.createContext<{
|
||||
showAnswer: boolean;
|
||||
setShowAnswer: Dispatch<SetStateAction<boolean>>;
|
||||
questions: QuestionType[];
|
||||
setQuestions: Dispatch<SetStateAction<QuestionType[]>>;
|
||||
answers: AnswerSubmissionToBackendType[];
|
||||
setAnswers: Dispatch<SetStateAction<AnswerSubmissionToBackendType[]>>;
|
||||
answer : AnswerType;
|
||||
setAnswer: Dispatch<SetStateAction<AnswerType>>;
|
||||
index: number | null; // Add index to the context
|
||||
updateIndex: (questionId: number | null) => void; // Add a function to update the index
|
||||
submitAnswer: (answer: AnswerType, idQuestion?: number) => void; // Updated submitAnswer signature
|
||||
isQuestionSent: boolean;
|
||||
setIsQuestionSent: Dispatch<SetStateAction<boolean>>;
|
||||
roomName: string;
|
||||
setRoomName: Dispatch<SetStateAction<string>>;
|
||||
username: string; // Username of the user
|
||||
setUsername: Dispatch<SetStateAction<string>>; // Setter for username
|
||||
disconnectWebSocket: () => void; // Function to disconnect the WebSocket
|
||||
setDisconnectWebSocket: Dispatch<SetStateAction<() => void>>; // Setter for disconnectWebSocket
|
||||
|
||||
}>({
|
||||
showAnswer: false,
|
||||
setShowAnswer: () => {},
|
||||
questions: [],
|
||||
setQuestions: () => {},
|
||||
answers: [],
|
||||
setAnswers: () => {},
|
||||
answer: [], // Default value for answer
|
||||
setAnswer: () => {}, // Default no-op function
|
||||
index: null, // Default value for index
|
||||
updateIndex: () => {}, // Default no-op function
|
||||
submitAnswer: () => {}, // Default no-op function
|
||||
isQuestionSent: false,
|
||||
setIsQuestionSent: () => {},
|
||||
username: '', // Default value for username
|
||||
setUsername: () => {}, // Default no-op function
|
||||
roomName: '', // Default value for roomName
|
||||
setRoomName: () => {}, // Default no-op function
|
||||
disconnectWebSocket: () => {}, // Default no-op function
|
||||
setDisconnectWebSocket: () => {}, // Default no-op function
|
||||
});
|
||||
|
||||
export const useQuizContext = () => {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,95 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { QuizContext } from './QuizContext';
|
||||
import { QuestionType } from '../../../Types/QuestionType';
|
||||
import { AnswerSubmissionToBackendType } from '../../../services/WebsocketService';
|
||||
import { AnswerType } from './JoinRoom';
|
||||
import webSocketService from '../../../services/WebsocketService';
|
||||
import ApiService from '../../../services/ApiService'
|
||||
|
||||
|
||||
export const QuizProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
|
||||
// State for showing answers
|
||||
const [showAnswer, setShowAnswer] = useState(false);
|
||||
|
||||
console.log('QuizProvider: showAnswer:', showAnswer);
|
||||
// State for managing the list of questions
|
||||
const [questions, setQuestions] = useState<QuestionType[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('QuizProvider: showAnswer:', showAnswer);
|
||||
}, [showAnswer]);
|
||||
// State for managing the list of answers
|
||||
const [answers, setAnswers] = useState<AnswerSubmissionToBackendType[]>([]);
|
||||
|
||||
// Calculate the index based on the current question's ID
|
||||
const [index, setIndex] = useState<number | null>(null);
|
||||
|
||||
const [answer, setAnswer] = useState<AnswerType>([]); // Initialize answer as an empty array
|
||||
|
||||
const [isQuestionSent, setIsQuestionSent] = useState(false);
|
||||
|
||||
const [username, setUsername] = useState<string>(ApiService.getUsername());
|
||||
|
||||
const [roomName, setRoomName] = useState<string>(''); // Add roomName state
|
||||
|
||||
const [disconnectWebSocket, setDisconnectWebSocket] = useState<() => void>(() => () => {});
|
||||
|
||||
|
||||
const updateIndex = (questionId: number | null) => {
|
||||
const questionIndex = questions.findIndex((q) => q.question.id === String(questionId));
|
||||
setIndex(questionIndex >= 0 ? questionIndex : null);
|
||||
};
|
||||
|
||||
// Function to handle answer submission
|
||||
const submitAnswer = (answer: AnswerType, idQuestion?: number) => {
|
||||
if (!idQuestion) {
|
||||
setAnswer(answer);
|
||||
setIsQuestionSent(true);
|
||||
console.log('index',index);
|
||||
} else {
|
||||
console.info(`QuizProvider: submitAnswer: answer: ${answer}, idQuestion: ${idQuestion}`);
|
||||
const answerData: AnswerSubmissionToBackendType = {
|
||||
roomName: roomName,
|
||||
answer: answer,
|
||||
username: username,
|
||||
idQuestion: idQuestion,
|
||||
};
|
||||
|
||||
// Update the answers state
|
||||
setAnswers((prevAnswers) => {
|
||||
const newAnswers = [...prevAnswers]; // Create a copy of the previous answers array
|
||||
newAnswers[idQuestion - 1] = answerData; // Update the specific answer
|
||||
return newAnswers; // Return the new array
|
||||
});
|
||||
|
||||
console.log(`QuizProvider: submitAnswer: answers: ${JSON.stringify(answers)}`);
|
||||
|
||||
// Submit the answer to the WebSocket service
|
||||
webSocketService.submitAnswer(answerData);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<QuizContext.Provider value={{ showAnswer, setShowAnswer }}>
|
||||
<QuizContext.Provider
|
||||
value={{
|
||||
showAnswer,
|
||||
setShowAnswer,
|
||||
questions,
|
||||
setQuestions,
|
||||
answers,
|
||||
setAnswers,
|
||||
answer,
|
||||
setAnswer,
|
||||
index, // Expose the index in the context
|
||||
updateIndex, // Expose a function to update the index
|
||||
submitAnswer, // Expose submitAnswer in the context
|
||||
isQuestionSent,
|
||||
setIsQuestionSent,
|
||||
username,
|
||||
setUsername,
|
||||
roomName,
|
||||
setRoomName,
|
||||
disconnectWebSocket,
|
||||
setDisconnectWebSocket,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</QuizContext.Provider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Socket } from 'socket.io-client';
|
||||
import { BaseQuestion, parse, Question } from 'gift-pegjs';
|
||||
import { BaseQuestion, parse /*Question*/ } from 'gift-pegjs';
|
||||
import LiveResultsComponent from 'src/components/LiveResults/LiveResults';
|
||||
import webSocketService, {
|
||||
AnswerReceptionFromBackendType
|
||||
|
|
@ -20,17 +20,24 @@ import ApiService from '../../../services/ApiService';
|
|||
import { QuestionType } from 'src/Types/QuestionType';
|
||||
import { Button } from '@mui/material';
|
||||
import { checkIfIsCorrect } from './useRooms';
|
||||
import { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
|
||||
|
||||
const ManageRoom: React.FC = () => {
|
||||
const {
|
||||
questions,
|
||||
setQuestions,
|
||||
index,
|
||||
updateIndex,
|
||||
} = useQuizContext();
|
||||
|
||||
|
||||
const navigate = useNavigate();
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
const [students, setStudents] = useState<StudentType[]>([]);
|
||||
const { quizId = '', roomName = '' } = useParams<{ quizId: string, roomName: string }>();
|
||||
const [quizQuestions, setQuizQuestions] = useState<QuestionType[] | undefined>();
|
||||
const [quiz, setQuiz] = useState<QuizType | null>(null);
|
||||
const [quizMode, setQuizMode] = useState<'teacher' | 'student'>('teacher');
|
||||
const [connectingError, setConnectingError] = useState<string>('');
|
||||
const [currentQuestion, setCurrentQuestion] = useState<QuestionType | undefined>(undefined);
|
||||
const [quizStarted, setQuizStarted] = useState<boolean>(false);
|
||||
const [formattedRoomName, setFormattedRoomName] = useState("");
|
||||
const [newlyConnectedUser, setNewlyConnectedUser] = useState<StudentType | null>(null);
|
||||
|
|
@ -51,12 +58,12 @@ const ManageRoom: React.FC = () => {
|
|||
if (quizMode === 'teacher') {
|
||||
webSocketService.nextQuestion({
|
||||
roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: Number(currentQuestion?.question.id) - 1,
|
||||
questions: questions,
|
||||
questionIndex: Number(index),
|
||||
isLaunch: true // started late
|
||||
});
|
||||
} else if (quizMode === 'student') {
|
||||
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
|
||||
webSocketService.launchStudentModeQuiz(formattedRoomName, questions);
|
||||
} else {
|
||||
console.error('Invalid quiz mode:', quizMode);
|
||||
}
|
||||
|
|
@ -126,8 +133,8 @@ const ManageRoom: React.FC = () => {
|
|||
webSocketService.endQuiz(formattedRoomName);
|
||||
webSocketService.disconnect();
|
||||
setSocket(null);
|
||||
setQuizQuestions(undefined);
|
||||
setCurrentQuestion(undefined);
|
||||
setQuestions([]);
|
||||
updateIndex(null);
|
||||
setStudents(new Array<StudentType>());
|
||||
}
|
||||
};
|
||||
|
|
@ -187,7 +194,7 @@ const ManageRoom: React.FC = () => {
|
|||
console.log(
|
||||
`Received answer from ${username} for question ${idQuestion}: ${answer}`
|
||||
);
|
||||
if (!quizQuestions) {
|
||||
if (!questions) {
|
||||
console.log('Quiz questions not found (cannot update answers without them).');
|
||||
return;
|
||||
}
|
||||
|
|
@ -218,7 +225,7 @@ const ManageRoom: React.FC = () => {
|
|||
isCorrect: checkIfIsCorrect(
|
||||
answer,
|
||||
idQuestion,
|
||||
quizQuestions!
|
||||
questions!
|
||||
)
|
||||
}
|
||||
: ans;
|
||||
|
|
@ -227,7 +234,7 @@ const ManageRoom: React.FC = () => {
|
|||
const newAnswer = {
|
||||
idQuestion,
|
||||
answer,
|
||||
isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!)
|
||||
isCorrect: checkIfIsCorrect(answer, idQuestion, questions!)
|
||||
};
|
||||
updatedAnswers = [...student.answers, newAnswer];
|
||||
}
|
||||
|
|
@ -243,30 +250,30 @@ const ManageRoom: React.FC = () => {
|
|||
});
|
||||
setSocket(socket);
|
||||
}
|
||||
}, [socket, currentQuestion, quizQuestions]);
|
||||
}, [socket, index, questions]);
|
||||
|
||||
const nextQuestion = () => {
|
||||
if (!quizQuestions || !currentQuestion || !quiz?.content) return;
|
||||
if (!questions || !index || !quiz?.content) return;
|
||||
|
||||
const nextQuestionIndex = Number(currentQuestion?.question.id);
|
||||
const nextQuestionIndex = index;
|
||||
|
||||
if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return;
|
||||
if (nextQuestionIndex === undefined || nextQuestionIndex > questions.length - 1) return;
|
||||
|
||||
setCurrentQuestion(quizQuestions[nextQuestionIndex]);
|
||||
updateIndex(nextQuestionIndex);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questions: questions,
|
||||
questionIndex: nextQuestionIndex,
|
||||
isLaunch: false});
|
||||
};
|
||||
|
||||
const previousQuestion = () => {
|
||||
if (!quizQuestions || !currentQuestion || !quiz?.content) return;
|
||||
if (!questions || !index || !quiz?.content) return;
|
||||
|
||||
const prevQuestionIndex = Number(currentQuestion?.question.id) - 2; // -2 because question.id starts at index 1
|
||||
const prevQuestionIndex = index - 1; // -2 because question.id starts at index 1
|
||||
|
||||
if (prevQuestionIndex === undefined || prevQuestionIndex < 0) return;
|
||||
setCurrentQuestion(quizQuestions[prevQuestionIndex]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: prevQuestionIndex, isLaunch: false});
|
||||
updateIndex(prevQuestionIndex);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: questions, questionIndex: prevQuestionIndex, isLaunch: false});
|
||||
};
|
||||
|
||||
const initializeQuizQuestion = () => {
|
||||
|
|
@ -280,33 +287,33 @@ const ManageRoom: React.FC = () => {
|
|||
});
|
||||
if (parsedQuestions.length === 0) return null;
|
||||
|
||||
setQuizQuestions(parsedQuestions);
|
||||
setQuestions(parsedQuestions);
|
||||
return parsedQuestions;
|
||||
};
|
||||
|
||||
const launchTeacherMode = () => {
|
||||
const quizQuestions = initializeQuizQuestion();
|
||||
console.log('launchTeacherMode - quizQuestions:', quizQuestions);
|
||||
const questions = initializeQuizQuestion();
|
||||
console.log('launchTeacherMode - quizQuestions:', questions);
|
||||
|
||||
if (!quizQuestions) {
|
||||
if (!questions) {
|
||||
console.log('Error launching quiz (launchTeacherMode). No questions found.');
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentQuestion(quizQuestions[0]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: 0, isLaunch: true});
|
||||
updateIndex(0);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: questions, questionIndex: 0, isLaunch: true});
|
||||
};
|
||||
|
||||
const launchStudentMode = () => {
|
||||
const quizQuestions = initializeQuizQuestion();
|
||||
console.log('launchStudentMode - quizQuestions:', quizQuestions);
|
||||
const questions = initializeQuizQuestion();
|
||||
console.log('launchStudentMode - quizQuestions:', questions);
|
||||
|
||||
if (!quizQuestions) {
|
||||
if (!questions) {
|
||||
console.log('Error launching quiz (launchStudentMode). No questions found.');
|
||||
return;
|
||||
}
|
||||
setQuizQuestions(quizQuestions);
|
||||
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
|
||||
setQuestions(questions);
|
||||
webSocketService.launchStudentModeQuiz(formattedRoomName, questions);
|
||||
};
|
||||
|
||||
const launchQuiz = () => {
|
||||
|
|
@ -328,10 +335,10 @@ const ManageRoom: React.FC = () => {
|
|||
};
|
||||
|
||||
const showSelectedQuestion = (questionIndex: number) => {
|
||||
if (quiz?.content && quizQuestions) {
|
||||
setCurrentQuestion(quizQuestions[questionIndex]);
|
||||
if (quiz?.content && questions) {
|
||||
updateIndex(questionIndex);
|
||||
if (quizMode === 'teacher') {
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex, isLaunch: false});
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: questions, questionIndex, isLaunch: false});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -398,13 +405,13 @@ const ManageRoom: React.FC = () => {
|
|||
|
||||
{/* the following breaks the css (if 'room' classes are nested) */}
|
||||
<div className="">
|
||||
{quizQuestions ? (
|
||||
{questions.length > 0 ? (
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<div className="title center-h-align mb-2">{quiz?.title}</div>
|
||||
{!isNaN(Number(currentQuestion?.question.id)) && (
|
||||
{index && (
|
||||
<strong className="number of questions">
|
||||
Question {Number(currentQuestion?.question.id)}/
|
||||
{quizQuestions?.length}
|
||||
Question {index+1}/
|
||||
{questions?.length}
|
||||
</strong>
|
||||
)}
|
||||
|
||||
|
|
@ -421,18 +428,14 @@ const ManageRoom: React.FC = () => {
|
|||
|
||||
<div className="mb-2 flex-column-wrapper">
|
||||
<div className="preview-and-result-container">
|
||||
{currentQuestion && (
|
||||
<QuestionDisplay
|
||||
showAnswer={false}
|
||||
question={currentQuestion?.question as Question}
|
||||
|
||||
/>
|
||||
{index && (
|
||||
<QuestionDisplay/>
|
||||
)}
|
||||
|
||||
<LiveResultsComponent
|
||||
quizMode={quizMode}
|
||||
socket={socket}
|
||||
questions={quizQuestions}
|
||||
questions={questions}
|
||||
showSelectedQuestion={showSelectedQuestion}
|
||||
students={students}
|
||||
></LiveResultsComponent>
|
||||
|
|
@ -448,7 +451,7 @@ const ManageRoom: React.FC = () => {
|
|||
<Button
|
||||
onClick={previousQuestion}
|
||||
variant="contained"
|
||||
disabled={Number(currentQuestion?.question.id) <= 1}
|
||||
disabled={index !== null && index <= 1}
|
||||
>
|
||||
Question précédente
|
||||
</Button>
|
||||
|
|
@ -458,8 +461,8 @@ const ManageRoom: React.FC = () => {
|
|||
onClick={nextQuestion}
|
||||
variant="contained"
|
||||
disabled={
|
||||
Number(currentQuestion?.question.id) >=
|
||||
quizQuestions.length
|
||||
index !== null &&
|
||||
index >= questions.length
|
||||
}
|
||||
>
|
||||
Prochaine question
|
||||
|
|
|
|||
Loading…
Reference in a new issue