Compare commits

...

2 commits

Author SHA1 Message Date
JubaAzul
6d714d1d77 [FEATURE] Zoomer sur les images en cliquant dessus
Fixes #306
2025-04-23 21:46:19 -04:00
JubaAzul
e61c855492 Compatibilité de showAnswer avec TeacherModeQuiz
Fixes #309
2025-04-10 17:41:25 -04:00
12 changed files with 507 additions and 484 deletions

View file

@ -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 */}

View file

@ -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,70 +60,74 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
const alphabet = alpha.map((x) => String.fromCharCode(x));
return (
<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),
}}
/>
<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>
{choice.formattedFeedback && showAnswer && (
<div className="feedback-container mb-1 mt-1/2">
<div
dangerouslySetInnerHTML={{
__html: FormattedTextTemplate(choice.formattedFeedback),
}}
/>
</div>
)}
</Button>
);
})}
</div>
{question.formattedGlobalFeedback && showAnswer && (
<div className="global-feedback mb-2">
<div
dangerouslySetInnerHTML={{
__html: FormattedTextTemplate(question.formattedGlobalFeedback),
}}
/>
</div>
);
})}
</div>
{question.formattedGlobalFeedback && showAnswer && (
<div className="global-feedback mb-2">
<div
dangerouslySetInnerHTML={{
__html: FormattedTextTemplate(question.formattedGlobalFeedback),
}}
/>
)}
{!showAnswer && submitAnswer && (
<Button
variant="contained"
onClick={() =>
actualAnswer.length > 0 && submitAnswer && submitAnswer(actualAnswer)
}
disabled={answer.length === 0}
>
Répondre
</Button>
)}
</div>
)}
{!showAnswer && handleOnSubmitAnswer && (
<Button
variant="contained"
onClick={() =>
answer.length > 0 && handleOnSubmitAnswer && handleOnSubmitAnswer(answer)
}
disabled={answer.length === 0}
>
Répondre
</Button>
)}
</div>
</QuizContext.Consumer>
);
};

View file

@ -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);
}
}, [passedAnswer]);
if (answer !== null && answer !== undefined) {
setActualAnswer(answer);
}
}, [answer]);
//const isSingleAnswer = correctAnswers.length === 1;
@ -44,57 +41,62 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
}
return (
<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' }}
/>
<QuizContext.Consumer>
{({ showAnswer }) => (
<div className="question-wrapper">
<div>
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
</div>
{question.formattedGlobalFeedback && showAnswer && (
<div className="global-feedback mb-2">
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
</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>
)}
</>
)}
{handleOnSubmitAnswer && (
<Button
variant="contained"
onClick={() =>
answer !== undefined &&
handleOnSubmitAnswer &&
handleOnSubmitAnswer(answer)
}
disabled={answer === undefined || answer === null || isNaN(answer[0] as number)}
>
Répondre
</Button>
)}
</>
</div>
)}
</div>
</QuizContext.Consumer>
);
};

View file

@ -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,33 +22,20 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
questionTypeComponent = (
<MultipleChoiceQuestionDisplay
question={question}
handleOnSubmitAnswer={handleOnSubmitAnswer}
showAnswer={showAnswer}
passedAnswer={answer}
/>
);
break;
case 'Numerical':
if (question.choices) {
questionTypeComponent = (
<NumericalQuestionDisplay
question={question}
handleOnSubmitAnswer={handleOnSubmitAnswer}
showAnswer={showAnswer}
passedAnswer={answer}
/>
);
questionTypeComponent = (
<NumericalQuestionDisplay
/>
);
}
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 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);
}
}, [passedAnswer]);
console.log("Answer" , answer);
if (answer !== undefined) {
setActualAnswer(answer);
}
}, [answer]);
console.log("Answer", actualAnswer);
return (
<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>
<QuizContext.Consumer>
{({ showAnswer }) => (
<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}
{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>
))}
</span>
<span>
<strong>Votre réponse est: </strong>{answer}
</span>
</div>
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
</div>}
</>
) : (
<>
<div className="answer-wrapper mb-1">
<TextField
type="text"
id={question.formattedStem.text}
name={question.formattedStem.text}
onChange={(e) => {
setAnswer([e.target.value]);
}}
disabled={showAnswer}
aria-label="short-answer-input"
/>
</div>
{handleOnSubmitAnswer && (
<Button
variant="contained"
onClick={() =>
answer !== undefined &&
handleOnSubmitAnswer &&
handleOnSubmitAnswer(answer)
}
disabled={answer === null || answer === undefined || answer.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) => {
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>
)}
</>
)}
</>
</div>
)}
</div>
</QuizContext.Consumer>
);
};

View file

@ -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>

View file

@ -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

View file

@ -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,
answers,
submitAnswer,
disconnectWebSocket
}) => {
const TeacherModeQuiz: React.FC = () => {
const {
questions,
answers,
setShowAnswer,
disconnectWebSocket,
index,
isQuestionSent,
setIsQuestionSent,
answer,
setAnswer,
} = useQuizContext();
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: ${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>

View file

@ -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();
@ -102,10 +118,10 @@ const JoinRoom: React.FC = () => {
};
const disconnect = () => {
// localStorage.clear();
// 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:

View file

@ -1,18 +1,55 @@
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,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>
);

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,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