Compare commits

..

No commits in common. "6d714d1d77fcfd542afe8de0f5e48e01e674db1e" and "128ab6662e3a2d0ed08076b5b4adf9e9b8d34e90" have entirely different histories.

12 changed files with 484 additions and 507 deletions

View file

@ -13,7 +13,6 @@ 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';
@ -26,7 +25,6 @@ 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());
@ -80,13 +78,13 @@ const App: React.FC = () => {
/>
<Route
path="/teacher/manage-room/:quizId/:roomName"
element={isTeacherAuthenticated ? <QuizProvider><ManageRoom /></QuizProvider> : <Navigate to="/login" />}
element={isTeacherAuthenticated ? <ManageRoom /> : <Navigate to="/login" />}
/>
{/* Pages espace étudiant */}
<Route
path="/student/join-room"
element={( !isRoomRequireAuthentication || isAuthenticated ) ? <QuizProvider><JoinRoom /></QuizProvider> : <Navigate to="/login" />}
element={( !isRoomRequireAuthentication || isAuthenticated ) ? <JoinRoom /> : <Navigate to="/login" />}
/>
{/* Pages authentification */}

View file

@ -5,52 +5,55 @@ 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';
const MultipleChoiceQuestionDisplay: React.FC = () => {
const { questions, index, answer, submitAnswer } = useQuizContext();
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(answer));
interface Props {
question: MultipleChoiceQuestion;
handleOnSubmitAnswer?: (answer: AnswerType) => void;
showAnswer?: boolean;
passedAnswer?: AnswerType;
}
const question = questions[Number(index)].question as MultipleChoiceQuestion;
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(passedAnswer));
const [actualAnswer, setActualAnswer] = useState<AnswerType>(() => {
if (answer && answer.length > 0) {
return answer;
const [answer, setAnswer] = useState<AnswerType>(() => {
if (passedAnswer && passedAnswer.length > 0) {
return passedAnswer;
}
return [];
});
let disableButton = false;
if (submitAnswer === undefined) {
if (handleOnSubmitAnswer === undefined) {
disableButton = true;
}
useEffect(() => {
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(answer));
if (answer !== undefined) {
setActualAnswer(answer);
console.log('MultipleChoiceQuestionDisplay: passedAnswer', JSON.stringify(passedAnswer));
if (passedAnswer !== undefined) {
setAnswer(passedAnswer);
} else {
setActualAnswer([]);
setAnswer([]);
}
}, [answer, index]);
}, [passedAnswer, question.id]);
const handleOnClickAnswer = (choice: string) => {
setActualAnswer((answer) => {
console.log(`handleOnClickAnswer -- setAnswer(): prevAnswer: ${answer}, choice: ${choice}`);
setAnswer((prevAnswer) => {
console.log(`handleOnClickAnswer -- setAnswer(): prevAnswer: ${prevAnswer}, choice: ${choice}`);
const correctAnswersCount = question.choices.filter((c) => c.isCorrect).length;
if (correctAnswersCount === 1) {
// If only one correct answer, replace the current selection
return answer.includes(choice) ? [] : [choice];
return prevAnswer.includes(choice) ? [] : [choice];
} else {
// Allow multiple selections if there are multiple correct answers
if (answer.includes(choice)) {
if (prevAnswer.includes(choice)) {
// Remove the choice if it's already selected
return answer.filter((selected) => selected !== choice);
return prevAnswer.filter((selected) => selected !== choice);
} else {
// Add the choice if it's not already selected
return [...answer, choice];
return [...prevAnswer, choice];
}
}
});
@ -60,74 +63,70 @@ const MultipleChoiceQuestionDisplay: React.FC = () => {
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: ${actualAnswer}, choice: ${choice.formattedText.text}`);
const selected = actualAnswer.includes(choice.formattedText.text) ? 'selected' : '';
return (
<div key={choice.formattedText.text + i} className="choice-container">
<Button
variant="text"
className="button-wrapper"
disabled={disableButton}
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}
>
{showAnswer ? (
<div>{choice.isCorrect ? '✅' : '❌'}</div>
) : (
''
)}
<div className={`circle ${selected}`}>{alphabet[i]}</div>
<div className={`answer-text ${selected}`}>
<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>
)}
</Button>
<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' : '';
return (
<div key={choice.formattedText.text + i} className="choice-container">
<Button
variant="text"
className="button-wrapper"
disabled={disableButton}
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}
>
{showAnswer ? (
<div>{choice.isCorrect ? '✅' : '❌'}</div>
) : (
''
)}
<div className={`circle ${selected}`}>{alphabet[i]}</div>
<div className={`answer-text ${selected}`}>
<div
dangerouslySetInnerHTML={{
__html: FormattedTextTemplate(choice.formattedText),
}}
/>
</div>
);
})}
</div>
{question.formattedGlobalFeedback && showAnswer && (
<div className="global-feedback mb-2">
<div
dangerouslySetInnerHTML={{
__html: FormattedTextTemplate(question.formattedGlobalFeedback),
}}
/>
{choice.formattedFeedback && showAnswer && (
<div className="feedback-container mb-1 mt-1/2">
<div
dangerouslySetInnerHTML={{
__html: FormattedTextTemplate(choice.formattedFeedback),
}}
/>
</div>
)}
</Button>
</div>
)}
{!showAnswer && submitAnswer && (
<Button
variant="contained"
onClick={() =>
actualAnswer.length > 0 && submitAnswer && submitAnswer(actualAnswer)
}
disabled={answer.length === 0}
>
Répondre
</Button>
)}
);
})}
</div>
{question.formattedGlobalFeedback && showAnswer && (
<div className="global-feedback mb-2">
<div
dangerouslySetInnerHTML={{
__html: FormattedTextTemplate(question.formattedGlobalFeedback),
}}
/>
</div>
)}
</QuizContext.Consumer>
{!showAnswer && handleOnSubmitAnswer && (
<Button
variant="contained"
onClick={() =>
answer.length > 0 && handleOnSubmitAnswer && handleOnSubmitAnswer(answer)
}
disabled={answer.length === 0}
>
Répondre
</Button>
)}
</div>
);
};

View file

@ -6,24 +6,27 @@ 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';
const NumericalQuestionDisplay: React.FC = () => {
const { questions, index, answer, submitAnswer } = useQuizContext();
interface Props {
question: NumericalQuestion;
handleOnSubmitAnswer?: (answer: AnswerType) => void;
showAnswer?: boolean;
passedAnswer?: AnswerType;
}
const question = questions[Number(index)].question as NumericalQuestion;
const [actualAnswer, setActualAnswer] = useState<AnswerType>(answer || []);
const NumericalQuestionDisplay: React.FC<Props> = (props) => {
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } =
props;
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || []);
const correctAnswers = question.choices;
let correctAnswer = '';
useEffect(() => {
if (answer !== null && answer !== undefined) {
setActualAnswer(answer);
}
}, [answer]);
if (passedAnswer !== null && passedAnswer !== undefined) {
setAnswer(passedAnswer);
}
}, [passedAnswer]);
//const isSingleAnswer = correctAnswers.length === 1;
if (isSimpleNumericalAnswer(correctAnswers[0])) {
@ -41,62 +44,57 @@ const NumericalQuestionDisplay: React.FC = () => {
}
return (
<QuizContext.Consumer>
{({ showAnswer }) => (
<div className="question-wrapper">
<div>
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
<div className="question-wrapper">
<div>
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
</div>
{showAnswer ? (
<>
<div className="correct-answer-text mb-2">
<strong>La bonne réponse est: </strong>
{correctAnswer}</div>
<span>
<strong>Votre réponse est: </strong>{answer.toString()}
</span>
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
</div>}
</>
) : (
<>
<div className="answer-wrapper mb-1">
<TextField
type="number"
id={question.formattedStem.text}
name={question.formattedStem.text}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAnswer([e.target.valueAsNumber]);
}}
inputProps={{ 'data-testid': 'number-input' }}
/>
</div>
{showAnswer ? (
<>
<div className="correct-answer-text mb-2">
<strong>La bonne réponse est: </strong>
{correctAnswer}</div>
<span>
<strong>Votre réponse est: </strong>{actualAnswer.toString()}
</span>
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
</div>}
</>
) : (
<>
<div className="answer-wrapper mb-1">
<TextField
type="number"
id={question.formattedStem.text}
name={question.formattedStem.text}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setActualAnswer([e.target.valueAsNumber]);
}}
inputProps={{ 'data-testid': 'number-input' }}
/>
</div>
{question.formattedGlobalFeedback && showAnswer && (
<div className="global-feedback mb-2">
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
</div>
)}
{submitAnswer && (
<Button
variant="contained"
onClick={() =>
actualAnswer !== undefined &&
submitAnswer &&
submitAnswer(actualAnswer)
}
disabled={actualAnswer === undefined || actualAnswer === null || isNaN(actualAnswer[0] as number)}
>
Répondre
</Button>
)}
</>
{question.formattedGlobalFeedback && showAnswer && (
<div className="global-feedback mb-2">
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
</div>
)}
</div>
{handleOnSubmitAnswer && (
<Button
variant="contained"
onClick={() =>
answer !== undefined &&
handleOnSubmitAnswer &&
handleOnSubmitAnswer(answer)
}
disabled={answer === undefined || answer === null || isNaN(answer[0] as number)}
>
Répondre
</Button>
)}
</>
)}
</QuizContext.Consumer>
</div>
);
};

View file

@ -1,41 +1,74 @@
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 { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
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]);
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;
case 'MC':
questionTypeComponent = (
<MultipleChoiceQuestionDisplay
question={question}
handleOnSubmitAnswer={handleOnSubmitAnswer}
showAnswer={showAnswer}
passedAnswer={answer}
/>
);
break;
case 'Numerical':
if (question.choices) {
questionTypeComponent = (
<NumericalQuestionDisplay
/>
);
questionTypeComponent = (
<NumericalQuestionDisplay
question={question}
handleOnSubmitAnswer={handleOnSubmitAnswer}
showAnswer={showAnswer}
passedAnswer={answer}
/>
);
}
break;
case 'Short':
questionTypeComponent = (
<ShortAnswerQuestionDisplay
question={question}
handleOnSubmitAnswer={handleOnSubmitAnswer}
showAnswer={showAnswer}
passedAnswer={answer}
/>
);
break;

View file

@ -4,82 +4,82 @@ 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 { questions, index, answer, submitAnswer } = useQuizContext();
const [actualAnswer, setActualAnswer] = useState<AnswerType>(answer || []);
const question = questions[Number(index)].question as ShortAnswerQuestion;
const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || []);
useEffect(() => {
if (answer !== undefined) {
setActualAnswer(answer);
}
}, [answer]);
console.log("Answer", actualAnswer);
if (passedAnswer !== undefined) {
setAnswer(passedAnswer);
}
}, [passedAnswer]);
console.log("Answer" , answer);
return (
<QuizContext.Consumer>
{({ showAnswer }) => (
<div className="question-wrapper">
<div className="question content">
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
<div className="question-wrapper">
<div className="question content">
<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>
{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>{actualAnswer}
</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) => {
setActualAnswer([e.target.value]);
}}
disabled={showAnswer}
aria-label="short-answer-input"
/>
</div>
{submitAnswer && (
<Button
variant="contained"
onClick={() =>
actualAnswer !== undefined &&
submitAnswer &&
submitAnswer(actualAnswer)
}
disabled={actualAnswer === null || actualAnswer === undefined || actualAnswer.length === 0}
>
Répondre
</Button>
)}
</>
{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 === undefined || answer.length === 0}
>
Répondre
</Button>
)}
</div>
</>
)}
</QuizContext.Consumer>
</div>
);
};

View file

@ -4,43 +4,51 @@ 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 = () => {
const { questions, index, answer, submitAnswer } = useQuizContext();
const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
const { question,
// showAnswer,
handleOnSubmitAnswer, passedAnswer } =
props;
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];
const [answer, setAnswer] = useState<boolean | undefined>(() => {
if (passedAnswer && (passedAnswer[0] === true || passedAnswer[0] === false)) {
return passedAnswer[0];
}
return undefined;
});
let disableButton = false;
if (submitAnswer === undefined) {
if (handleOnSubmitAnswer === undefined) {
disableButton = true;
}
useEffect(() => {
console.log("passedAnswer", answer);
if (answer && (answer[0] === true || answer[0] === false)) {
setActualAnswer(answer[0]);
console.log("passedAnswer", passedAnswer);
if (passedAnswer && (passedAnswer[0] === true || passedAnswer[0] === false)) {
setAnswer(passedAnswer[0]);
} else {
setActualAnswer(undefined);
setAnswer(undefined);
}
}, [answer, index]);
}, [passedAnswer, question.id]);
const handleOnClickAnswer = (choice: boolean) => {
setActualAnswer(choice);
setAnswer(choice);
};
const selectedTrue = actualAnswer ? 'selected' : '';
const selectedFalse = actualAnswer !== undefined && !actualAnswer ? 'selected' : '';
const selectedTrue = answer ? 'selected' : '';
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
return (
<QuizContext.Consumer>
@ -68,7 +76,7 @@ const TrueFalseQuestionDisplay: React.FC = () => {
<div className={`circle ${selectedTrue}`}>V</div>
<div className={`answer-text ${selectedTrue}`}>Vrai</div>
{showAnswer && actualAnswer && question.trueFormattedFeedback && (
{showAnswer && answer && question.trueFormattedFeedback && (
<div className="true-feedback mb-2">
<div
dangerouslySetInnerHTML={{
@ -94,7 +102,7 @@ const TrueFalseQuestionDisplay: React.FC = () => {
<div className={`circle ${selectedFalse}`}>F</div>
<div className={`answer-text ${selectedFalse}`}>Faux</div>
{showAnswer && !actualAnswer && question.falseFormattedFeedback && (
{showAnswer && !answer && question.falseFormattedFeedback && (
<div className="false-feedback mb-2">
<div
dangerouslySetInnerHTML={{
@ -118,15 +126,15 @@ const TrueFalseQuestionDisplay: React.FC = () => {
/>
</div>
)}
{!showAnswer && submitAnswer && (
{!showAnswer && handleOnSubmitAnswer && (
<Button
variant="contained"
onClick={() =>
actualAnswer !== undefined &&
submitAnswer &&
submitAnswer([actualAnswer])
answer !== undefined &&
handleOnSubmitAnswer &&
handleOnSubmitAnswer([answer])
}
disabled={actualAnswer === undefined}
disabled={answer === undefined}
>
Répondre
</Button>

View file

@ -1,16 +1,32 @@
// StudentModeQuiz.tsx
import React, { useEffect, useState } from 'react';
import QuestionDisplay from '../QuestionsDisplay/QuestionDisplay';
import QuestionComponent 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';
const StudentModeQuiz: React.FC = () => {
const { questions, answers, setIsQuestionSent, disconnectWebSocket, setShowAnswer } = useQuizContext(); // Access setShowAnswer from context
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 [questionInfos, setQuestion] = useState<QuestionType>(questions[0]);
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
const previousQuestion = () => {
setQuestion(questions[Number(questionInfos.question?.id) - 2]);
@ -19,7 +35,7 @@ const StudentModeQuiz: React.FC = () => {
useEffect(() => {
const savedAnswer = answers[Number(questionInfos.question.id) - 1]?.answer;
console.log(`StudentModeQuiz: useEffect: savedAnswer: ${savedAnswer}`);
setIsQuestionSent(savedAnswer !== undefined);
setIsAnswerSubmitted(savedAnswer !== undefined);
setShowAnswer(savedAnswer !== undefined); // Update showAnswer in context
}, [questionInfos.question, answers, setShowAnswer]);
@ -27,6 +43,13 @@ const StudentModeQuiz: React.FC = () => {
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">
@ -40,7 +63,12 @@ const StudentModeQuiz: React.FC = () => {
</div>
<div className="overflow-auto">
<div className="question-component-container">
<QuestionDisplay/>
<QuestionComponent
handleOnSubmitAnswer={handleOnSubmitAnswer}
question={questionInfos.question as Question}
showAnswer={isAnswerSubmitted} // Local state for showing answers
answer={answers[Number(questionInfos.question.id) - 1]?.answer}
/>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '1rem' }}>
<div>
<Button

View file

@ -1,55 +1,64 @@
// TeacherModeQuiz.tsx
import React, { useEffect, useState } from 'react';
import QuestionDisplay from '../QuestionsDisplay/QuestionDisplay';
import QuestionComponent 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 { useQuizContext } from 'src/pages/Student/JoinRoom/QuizContext';
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';
interface TeacherModeQuizProps {
questionInfos: QuestionType;
answers: AnswerSubmissionToBackendType[];
submitAnswer: (_answer: AnswerType, _idQuestion: number) => void;
disconnectWebSocket: () => void;
}
const TeacherModeQuiz: React.FC = () => {
const {
questions,
answers,
setShowAnswer,
disconnectWebSocket,
index,
isQuestionSent,
setIsQuestionSent,
answer,
setAnswer,
} = useQuizContext();
const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
questionInfos,
answers,
submitAnswer,
disconnectWebSocket
}) => {
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
const [isFeedbackDialogOpen, setIsFeedbackDialogOpen] = useState(false);
const [answer, setAnswer] = useState<AnswerType>();
// 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: ${index} answer: ${answer}`);
const oldAnswer = answers[Number(index) -1 ]?.answer;
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: oldAnswer: ${oldAnswer}`);
setAnswer(oldAnswer);
setShowAnswer(false);
setIsQuestionSent(false);
}, [questions[Number(index)].question, answers]);
setIsFeedbackDialogOpen(false);
}, [questionInfos.question, answers]);
// handle showing the feedback dialog
useEffect(() => {
console.log(`TeacherModeQuiz: useEffect: answer: ${answer}`);
setIsAnswerSubmitted(answer !== undefined);
setIsQuestionSent(answer !== undefined);
setIsFeedbackDialogOpen(answer !== undefined);
}, [answer]);
useEffect(() => {
console.log(`TeacherModeQuiz: useEffect: isAnswerSubmitted: ${isAnswerSubmitted}`);
setIsQuestionSent(isAnswerSubmitted);
setShowAnswer(isAnswerSubmitted);
setIsFeedbackDialogOpen(isAnswerSubmitted);
}, [isAnswerSubmitted]);
const handleOnSubmitAnswer = (answer: AnswerType) => {
const idQuestion = Number(questionInfos.question.id) || -1;
submitAnswer(answer, idQuestion);
// setAnswer(answer);
setIsFeedbackDialogOpen(true);
};
const handleFeedbackDialogClose = () => {
setIsQuestionSent(false);
setIsFeedbackDialogOpen(false);
setIsAnswerSubmitted(true);
};
@ -62,7 +71,7 @@ const TeacherModeQuiz: React.FC = () => {
message={`Êtes-vous sûr de vouloir quitter?`} />
<div className='centerTitle'>
<div className='title'>Question {index}</div>
<div className='title'>Question {questionInfos.question.id}</div>
</div>
<div className='dumb'></div>
@ -74,11 +83,15 @@ const TeacherModeQuiz: React.FC = () => {
En attente pour la prochaine question...
</div>
) : (
<QuestionDisplay/>
<QuestionComponent
handleOnSubmitAnswer={handleOnSubmitAnswer}
question={questionInfos.question as Question}
answer={answer}
/>
)}
<Dialog
open={isQuestionSent}
open={isFeedbackDialogOpen}
onClose={handleFeedbackDialogClose}
>
<DialogTitle>Rétroaction</DialogTitle>
@ -93,8 +106,12 @@ const TeacherModeQuiz: React.FC = () => {
>Question : </div>
</div>
<QuestionDisplay
//showAnswer={true}
<QuestionComponent
handleOnSubmitAnswer={handleOnSubmitAnswer}
question={questionInfos.question as Question}
showAnswer={true}
answer={answer}
/>
</DialogContent>
<DialogActions>

View file

@ -15,38 +15,23 @@ import LoadingButton from '@mui/lab/LoadingButton';
import LoginContainer from 'src/components/LoginContainer/LoginContainer'
import { useQuizContext } from './QuizContext';
import ApiService from '../../../services/ApiService'
import { QuizProvider } from './QuizProvider';
export type AnswerType = Array<string | number | boolean>;
const JoinRoom: React.FC = () => {
const {
setQuestions,
setAnswers,
questions,
username,
setUsername,
setDisconnectWebSocket,
roomName,
setRoomName,
index,
updateIndex,
} = useQuizContext();
const [roomName, setRoomName] = useState('');
const [username, setUsername] = useState(ApiService.getUsername());
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 () => {
@ -57,7 +42,6 @@ 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]);
@ -74,13 +58,13 @@ const JoinRoom: React.FC = () => {
console.log('JoinRoom: on(next-question): Received next-question:', question);
setQuizMode('teacher');
setIsWaitingForTeacher(false);
updateIndex(Number(question.question.id));
setQuestion(question);
});
socket.on('launch-teacher-mode', (questions: QuestionType[]) => {
console.log('on(launch-teacher-mode): Received launch-teacher-mode:', questions);
setQuizMode('teacher');
setIsWaitingForTeacher(true);
updateIndex(null);
setQuestions([]); // clear out from last time (in case quiz is repeated)
setQuestions(questions);
// wait for next-question
});
@ -91,7 +75,7 @@ const JoinRoom: React.FC = () => {
setIsWaitingForTeacher(false);
setQuestions([]); // clear out from last time (in case quiz is repeated)
setQuestions(questions);
updateIndex(0);
setQuestion(questions[0]);
});
socket.on('end-quiz', () => {
disconnect();
@ -118,10 +102,10 @@ const JoinRoom: React.FC = () => {
};
const disconnect = () => {
// localStorage.clear();
// localStorage.clear();
webSocketService.disconnect();
setSocket(null);
updateIndex(null);
setQuestion(undefined);
setIsWaitingForTeacher(false);
setQuizMode('');
setRoomName('');
@ -143,6 +127,25 @@ 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();
@ -173,16 +176,26 @@ const JoinRoom: React.FC = () => {
}
switch (quizMode) {
case 'student':
return (
<StudentModeQuiz />
<QuizProvider>
<StudentModeQuiz
questions={questions}
answers={answers}
submitAnswer={handleOnSubmitAnswer}
disconnectWebSocket={disconnect}
/>
</QuizProvider>
);
case 'teacher':
return (
index != null && (
<TeacherModeQuiz />
question && (
<TeacherModeQuiz
questionInfos={question}
answers={answers}
submitAnswer={handleOnSubmitAnswer}
disconnectWebSocket={disconnect}
/>
)
);
default:

View file

@ -1,55 +1,18 @@
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 = () => {
const context = useContext(QuizContext);
if (!context) {
throw new Error('useQuizContext must be used within a QuizProvider');
}
return context;
};
const context = useContext(QuizContext);
if (!context) {
throw new Error('useQuizContext must be used within a QuizProvider');
}
return context;
};

View file

@ -1,96 +1,19 @@
import React, { useState } from 'react';
import React, { useEffect, 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);
// State for managing the list of questions
const [questions, setQuestions] = useState<QuestionType[]>([]);
console.log('QuizProvider: 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);
}
};
useEffect(() => {
console.log('QuizProvider: showAnswer:', showAnswer);
}, [showAnswer]);
return (
<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,
}}
>
<QuizContext.Provider value={{ showAnswer, setShowAnswer }}>
{children}
</QuizContext.Provider>
);
};
};

View file

@ -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,24 +20,17 @@ 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);
@ -58,12 +51,12 @@ const ManageRoom: React.FC = () => {
if (quizMode === 'teacher') {
webSocketService.nextQuestion({
roomName: formattedRoomName,
questions: questions,
questionIndex: Number(index),
questions: quizQuestions,
questionIndex: Number(currentQuestion?.question.id) - 1,
isLaunch: true // started late
});
} else if (quizMode === 'student') {
webSocketService.launchStudentModeQuiz(formattedRoomName, questions);
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
} else {
console.error('Invalid quiz mode:', quizMode);
}
@ -133,8 +126,8 @@ const ManageRoom: React.FC = () => {
webSocketService.endQuiz(formattedRoomName);
webSocketService.disconnect();
setSocket(null);
setQuestions([]);
updateIndex(null);
setQuizQuestions(undefined);
setCurrentQuestion(undefined);
setStudents(new Array<StudentType>());
}
};
@ -194,7 +187,7 @@ const ManageRoom: React.FC = () => {
console.log(
`Received answer from ${username} for question ${idQuestion}: ${answer}`
);
if (!questions) {
if (!quizQuestions) {
console.log('Quiz questions not found (cannot update answers without them).');
return;
}
@ -225,7 +218,7 @@ const ManageRoom: React.FC = () => {
isCorrect: checkIfIsCorrect(
answer,
idQuestion,
questions!
quizQuestions!
)
}
: ans;
@ -234,7 +227,7 @@ const ManageRoom: React.FC = () => {
const newAnswer = {
idQuestion,
answer,
isCorrect: checkIfIsCorrect(answer, idQuestion, questions!)
isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!)
};
updatedAnswers = [...student.answers, newAnswer];
}
@ -250,30 +243,30 @@ const ManageRoom: React.FC = () => {
});
setSocket(socket);
}
}, [socket, index, questions]);
}, [socket, currentQuestion, quizQuestions]);
const nextQuestion = () => {
if (!questions || !index || !quiz?.content) return;
if (!quizQuestions || !currentQuestion || !quiz?.content) return;
const nextQuestionIndex = index;
const nextQuestionIndex = Number(currentQuestion?.question.id);
if (nextQuestionIndex === undefined || nextQuestionIndex > questions.length - 1) return;
if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return;
updateIndex(nextQuestionIndex);
setCurrentQuestion(quizQuestions[nextQuestionIndex]);
webSocketService.nextQuestion({roomName: formattedRoomName,
questions: questions,
questions: quizQuestions,
questionIndex: nextQuestionIndex,
isLaunch: false});
};
const previousQuestion = () => {
if (!questions || !index || !quiz?.content) return;
if (!quizQuestions || !currentQuestion || !quiz?.content) return;
const prevQuestionIndex = index - 1; // -2 because question.id starts at index 1
const prevQuestionIndex = Number(currentQuestion?.question.id) - 2; // -2 because question.id starts at index 1
if (prevQuestionIndex === undefined || prevQuestionIndex < 0) return;
updateIndex(prevQuestionIndex);
webSocketService.nextQuestion({roomName: formattedRoomName, questions: questions, questionIndex: prevQuestionIndex, isLaunch: false});
setCurrentQuestion(quizQuestions[prevQuestionIndex]);
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: prevQuestionIndex, isLaunch: false});
};
const initializeQuizQuestion = () => {
@ -287,33 +280,33 @@ const ManageRoom: React.FC = () => {
});
if (parsedQuestions.length === 0) return null;
setQuestions(parsedQuestions);
setQuizQuestions(parsedQuestions);
return parsedQuestions;
};
const launchTeacherMode = () => {
const questions = initializeQuizQuestion();
console.log('launchTeacherMode - quizQuestions:', questions);
const quizQuestions = initializeQuizQuestion();
console.log('launchTeacherMode - quizQuestions:', quizQuestions);
if (!questions) {
if (!quizQuestions) {
console.log('Error launching quiz (launchTeacherMode). No questions found.');
return;
}
updateIndex(0);
webSocketService.nextQuestion({roomName: formattedRoomName, questions: questions, questionIndex: 0, isLaunch: true});
setCurrentQuestion(quizQuestions[0]);
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: 0, isLaunch: true});
};
const launchStudentMode = () => {
const questions = initializeQuizQuestion();
console.log('launchStudentMode - quizQuestions:', questions);
const quizQuestions = initializeQuizQuestion();
console.log('launchStudentMode - quizQuestions:', quizQuestions);
if (!questions) {
if (!quizQuestions) {
console.log('Error launching quiz (launchStudentMode). No questions found.');
return;
}
setQuestions(questions);
webSocketService.launchStudentModeQuiz(formattedRoomName, questions);
setQuizQuestions(quizQuestions);
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
};
const launchQuiz = () => {
@ -335,10 +328,10 @@ const ManageRoom: React.FC = () => {
};
const showSelectedQuestion = (questionIndex: number) => {
if (quiz?.content && questions) {
updateIndex(questionIndex);
if (quiz?.content && quizQuestions) {
setCurrentQuestion(quizQuestions[questionIndex]);
if (quizMode === 'teacher') {
webSocketService.nextQuestion({roomName: formattedRoomName, questions: questions, questionIndex, isLaunch: false});
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex, isLaunch: false});
}
}
};
@ -405,13 +398,13 @@ const ManageRoom: React.FC = () => {
{/* the following breaks the css (if 'room' classes are nested) */}
<div className="">
{questions.length > 0 ? (
{quizQuestions ? (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<div className="title center-h-align mb-2">{quiz?.title}</div>
{index && (
{!isNaN(Number(currentQuestion?.question.id)) && (
<strong className="number of questions">
Question {index+1}/
{questions?.length}
Question {Number(currentQuestion?.question.id)}/
{quizQuestions?.length}
</strong>
)}
@ -428,14 +421,18 @@ const ManageRoom: React.FC = () => {
<div className="mb-2 flex-column-wrapper">
<div className="preview-and-result-container">
{index && (
<QuestionDisplay/>
{currentQuestion && (
<QuestionDisplay
showAnswer={false}
question={currentQuestion?.question as Question}
/>
)}
<LiveResultsComponent
quizMode={quizMode}
socket={socket}
questions={questions}
questions={quizQuestions}
showSelectedQuestion={showSelectedQuestion}
students={students}
></LiveResultsComponent>
@ -451,7 +448,7 @@ const ManageRoom: React.FC = () => {
<Button
onClick={previousQuestion}
variant="contained"
disabled={index !== null && index <= 1}
disabled={Number(currentQuestion?.question.id) <= 1}
>
Question précédente
</Button>
@ -461,8 +458,8 @@ const ManageRoom: React.FC = () => {
onClick={nextQuestion}
variant="contained"
disabled={
index !== null &&
index >= questions.length
Number(currentQuestion?.question.id) >=
quizQuestions.length
}
>
Prochaine question