Utilisation liste room

This commit is contained in:
NouhailaAater 2025-02-20 00:37:01 -05:00
parent a9743ad5d4
commit c3e56502d8
2 changed files with 108 additions and 94 deletions

View file

@ -3,10 +3,16 @@ import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import { ParsedGIFTQuestion, BaseQuestion, parse, Question } from 'gift-pegjs'; import { ParsedGIFTQuestion, BaseQuestion, parse, Question } from 'gift-pegjs';
import { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer } from "gift-pegjs/typeGuards"; import {
isSimpleNumericalAnswer,
isRangeNumericalAnswer,
isHighLowNumericalAnswer
} from 'gift-pegjs/typeGuards';
import LiveResultsComponent from 'src/components/LiveResults/LiveResults'; import LiveResultsComponent from 'src/components/LiveResults/LiveResults';
// import { QuestionService } from '../../../services/QuestionService'; // import { QuestionService } from '../../../services/QuestionService';
import webSocketService, { AnswerReceptionFromBackendType } from '../../../services/WebsocketService'; import webSocketService, {
AnswerReceptionFromBackendType
} from '../../../services/WebsocketService';
import { QuizType } from '../../../Types/QuizType'; import { QuizType } from '../../../Types/QuizType';
import GroupIcon from '@mui/icons-material/Group'; import GroupIcon from '@mui/icons-material/Group';
@ -22,16 +28,8 @@ import QuestionDisplay from 'src/components/QuestionsDisplay/QuestionDisplay';
import ApiService from '../../../services/ApiService'; import ApiService from '../../../services/ApiService';
import { QuestionType } from 'src/Types/QuestionType'; import { QuestionType } from 'src/Types/QuestionType';
import { RoomType } from 'src/Types/RoomType'; import { RoomType } from 'src/Types/RoomType';
import { import { IconButton, Button, Tooltip, NativeSelect } from '@mui/material';
IconButton, import { Add } from '@mui/icons-material';
Button,
Tooltip,
NativeSelect,
} from '@mui/material';
import {
Add
} from '@mui/icons-material';
const ManageRoom: React.FC = () => { const ManageRoom: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -51,15 +49,13 @@ const ManageRoom: React.FC = () => {
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
if (!ApiService.isLoggedIn()) { if (!ApiService.isLoggedIn()) {
navigate("/teacher/login"); navigate('/teacher/login');
return; return;
} } else {
else {
const userFolders = await ApiService.getUserRooms(); const userFolders = await ApiService.getUserRooms();
setFolders(userFolders as RoomType[]); setFolders(userFolders as RoomType[]);
} }
}; };
fetchData(); fetchData();
@ -69,30 +65,15 @@ const ManageRoom: React.FC = () => {
setSelectedRoomId(event.target.value); setSelectedRoomId(event.target.value);
}; };
const handleCreateRoom = async () => {
try {
const roomTitle = prompt('Titre de la salle');
if (roomTitle) {
await ApiService.createFolder(roomTitle);
const userFolders = await ApiService.getUserFolders();
setFolders(userFolders as RoomType[]);
const newlyCreatedFolder = userFolders[userFolders.length - 1] as RoomType;
setSelectedRoomId(newlyCreatedFolder._id);
}
} catch (error) {
console.error('Error creating folder:', error);
}
};
useEffect(() => { useEffect(() => {
if (quizId.id) { if (quizId.id) {
const fetchquiz = async () => { const fetchquiz = async () => {
const quiz = await ApiService.getQuiz(quizId.id as string); const quiz = await ApiService.getQuiz(quizId.id as string);
if (!quiz) { if (!quiz) {
window.alert(`Une erreur est survenue.\n Le quiz ${quizId.id} n'a pas été trouvé\nVeuillez réessayer plus tard`) window.alert(
`Une erreur est survenue.\n Le quiz ${quizId.id} n'a pas été trouvé\nVeuillez réessayer plus tard`
);
console.error('Quiz not found for id:', quizId.id); console.error('Quiz not found for id:', quizId.id);
navigate('/teacher/dashboard'); navigate('/teacher/dashboard');
return; return;
@ -111,9 +92,10 @@ const ManageRoom: React.FC = () => {
}; };
fetchquiz(); fetchquiz();
} else { } else {
window.alert(`Une erreur est survenue.\n Le quiz ${quizId.id} n'a pas été trouvé\nVeuillez réessayer plus tard`) window.alert(
`Une erreur est survenue.\n Le quiz ${quizId.id} n'a pas été trouvé\nVeuillez réessayer plus tard`
);
console.error('Quiz not found for id:', quizId.id); console.error('Quiz not found for id:', quizId.id);
navigate('/teacher/dashboard'); navigate('/teacher/dashboard');
return; return;
@ -138,9 +120,8 @@ const ManageRoom: React.FC = () => {
const socket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_SOCKET_URL); const socket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_SOCKET_URL);
socket.on('connect', () => { socket.on('connect', () => {
webSocketService.createRoom(); webSocketService.createRoom(selectedRoomId);
console.error('socket.on(connect:)'); console.error('socket.on(connect:)');
}); });
socket.on('connect_error', (error) => { socket.on('connect_error', (error) => {
setConnectingError('Erreur lors de la connexion... Veuillez réessayer'); setConnectingError('Erreur lors de la connexion... Veuillez réessayer');
@ -149,7 +130,6 @@ const ManageRoom: React.FC = () => {
socket.on('create-success', (roomName: string) => { socket.on('create-success', (roomName: string) => {
setRoomName(roomName); setRoomName(roomName);
console.error('create-success', roomName); console.error('create-success', roomName);
}); });
socket.on('create-failure', () => { socket.on('create-failure', () => {
console.log('Error creating room.'); console.log('Error creating room.');
@ -195,7 +175,9 @@ const ManageRoom: React.FC = () => {
console.log(`Listening for submit-answer-room in room ${roomName}`); console.log(`Listening for submit-answer-room in room ${roomName}`);
socket.on('submit-answer-room', (answerData: AnswerReceptionFromBackendType) => { socket.on('submit-answer-room', (answerData: AnswerReceptionFromBackendType) => {
const { answer, idQuestion, idUser, username } = answerData; const { answer, idQuestion, idUser, username } = answerData;
console.log(`Received answer from ${username} for question ${idQuestion}: ${answer}`); console.log(
`Received answer from ${username} for question ${idQuestion}: ${answer}`
);
if (!quizQuestions) { if (!quizQuestions) {
console.log('Quiz questions not found (cannot update answers without them).'); console.log('Quiz questions not found (cannot update answers without them).');
return; return;
@ -214,17 +196,33 @@ const ManageRoom: React.FC = () => {
console.log(`Comparing ${student.id} to ${idUser}`); console.log(`Comparing ${student.id} to ${idUser}`);
if (student.id === idUser) { if (student.id === idUser) {
foundStudent = true; foundStudent = true;
const existingAnswer = student.answers.find((ans) => ans.idQuestion === idQuestion); const existingAnswer = student.answers.find(
(ans) => ans.idQuestion === idQuestion
);
let updatedAnswers: Answer[] = []; let updatedAnswers: Answer[] = [];
if (existingAnswer) { if (existingAnswer) {
// Update the existing answer // Update the existing answer
updatedAnswers = student.answers.map((ans) => { updatedAnswers = student.answers.map((ans) => {
console.log(`Comparing ${ans.idQuestion} to ${idQuestion}`); console.log(`Comparing ${ans.idQuestion} to ${idQuestion}`);
return (ans.idQuestion === idQuestion ? { ...ans, answer, isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!) } : ans); return ans.idQuestion === idQuestion
? {
...ans,
answer,
isCorrect: checkIfIsCorrect(
answer,
idQuestion,
quizQuestions!
)
}
: ans;
}); });
} else { } else {
// Add a new answer // Add a new answer
const newAnswer = { idQuestion, answer, isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!) }; const newAnswer = {
idQuestion,
answer,
isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!)
};
updatedAnswers = [...student.answers, newAnswer]; updatedAnswers = [...student.answers, newAnswer];
} }
return { ...student, answers: updatedAnswers }; return { ...student, answers: updatedAnswers };
@ -239,7 +237,6 @@ const ManageRoom: React.FC = () => {
}); });
setSocket(socket); setSocket(socket);
} }
}, [socket, currentQuestion, quizQuestions]); }, [socket, currentQuestion, quizQuestions]);
const nextQuestion = () => { const nextQuestion = () => {
@ -306,7 +303,9 @@ const ManageRoom: React.FC = () => {
const launchQuiz = () => { const launchQuiz = () => {
if (!socket || !roomName || !quiz?.content || quiz?.content.length === 0) { if (!socket || !roomName || !quiz?.content || quiz?.content.length === 0) {
// TODO: This error happens when token expires! Need to handle it properly // TODO: This error happens when token expires! Need to handle it properly
console.log(`Error launching quiz. socket: ${socket}, roomName: ${roomName}, quiz: ${quiz}`); console.log(
`Error launching quiz. socket: ${socket}, roomName: ${roomName}, quiz: ${quiz}`
);
setQuizStarted(true); setQuizStarted(true);
return; return;
@ -318,7 +317,6 @@ const ManageRoom: React.FC = () => {
case 'teacher': case 'teacher':
setQuizStarted(true); setQuizStarted(true);
return launchTeacherMode(); return launchTeacherMode();
} }
}; };
@ -337,7 +335,11 @@ const ManageRoom: React.FC = () => {
navigate('/teacher/dashboard'); navigate('/teacher/dashboard');
}; };
function checkIfIsCorrect(answer: string | number | boolean, idQuestion: number, questions: QuestionType[]): boolean { function checkIfIsCorrect(
answer: string | number | boolean,
idQuestion: number,
questions: QuestionType[]
): boolean {
const questionInfo = questions.find((q) => const questionInfo = questions.find((q) =>
q.question.id ? q.question.id === idQuestion.toString() : false q.question.id ? q.question.id === idQuestion.toString() : false
) as QuestionType | undefined; ) as QuestionType | undefined;
@ -360,8 +362,7 @@ const ManageRoom: React.FC = () => {
const answerNumber = parseFloat(answerText); const answerNumber = parseFloat(answerText);
if (!isNaN(answerNumber)) { if (!isNaN(answerNumber)) {
return ( return (
answerNumber <= choice.numberHigh && answerNumber <= choice.numberHigh && answerNumber >= choice.numberLow
answerNumber >= choice.numberLow
); );
} }
} }
@ -391,7 +392,6 @@ const ManageRoom: React.FC = () => {
return false; return false;
} }
if (!roomName) { if (!roomName) {
return ( return (
<div className="center"> <div className="center">
@ -415,34 +415,42 @@ const ManageRoom: React.FC = () => {
} }
return ( return (
<div className='room'> <div className="room">
<div className='roomHeader'> <div className="roomHeader">
<DisconnectButton <DisconnectButton
onReturn={handleReturn} onReturn={handleReturn}
askConfirm askConfirm
message={`Êtes-vous sûr de vouloir quitter?`} /> message={`Êtes-vous sûr de vouloir quitter?`}
/>
<div
className="headerContent"
style={{
<div className='headerContent' style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}> display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%'
}}
>
<div style={{ flex: 1, display: 'flex', justifyContent: 'center' }}> <div style={{ flex: 1, display: 'flex', justifyContent: 'center' }}>
<div className='title'>Salle: {roomName}</div> <div className="title">Salle: {roomName}</div>
</div> </div>
{quizStarted && ( {quizStarted && (
<div className='userCount subtitle smallText' style={{ display: 'flex', alignItems: 'center' }}> <div
className="userCount subtitle smallText"
style={{ display: 'flex', alignItems: 'center' }}
>
<GroupIcon style={{ marginRight: '5px' }} /> <GroupIcon style={{ marginRight: '5px' }} />
{students.length}/60 {students.length}/60
</div> </div>
)} )}
</div> </div>
<div className='dumb'></div> <div className="dumb"></div>
</div> </div>
{/* bloc Room */} {/* bloc Room */}
<div className='room'> <div className="room">
<div className='select'> <div className="select">
<NativeSelect <NativeSelect
id="select-room" id="select-room"
color="primary" color="primary"
@ -451,34 +459,36 @@ const ManageRoom: React.FC = () => {
> >
<option value=""> Sélectionner une salle </option> <option value=""> Sélectionner une salle </option>
{rooms.map((room: RoomType) => ( {rooms.map((room: RoomType) => (
<option value={room._id} key={room._id}> {room.title} </option> <option value={room.title} key={room.title}>
{' '}
{room.title}{' '}
</option>
))} ))}
</NativeSelect> </NativeSelect>
</div> </div>
<div className='actions'> <div className="actions">
<Tooltip title="Ajouter room" placement="top"> <Tooltip title="Créer la salle" placement="top">
<IconButton color="primary" onClick={handleCreateRoom}> <IconButton color="primary" onClick={createWebSocketRoom}>
<Add /> <Add />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</div> </div>
</div> </div>
{/* the following breaks the css (if 'room' classes are nested) */} {/* the following breaks the css (if 'room' classes are nested) */}
<div className=''> <div className="">
{quizQuestions ? ( {quizQuestions ? (
<div style={{ display: 'flex', flexDirection: 'column' }}> <div style={{ display: 'flex', flexDirection: 'column' }}>
<div className="title center-h-align mb-2">{quiz?.title}</div> <div className="title center-h-align mb-2">{quiz?.title}</div>
{!isNaN(Number(currentQuestion?.question.id)) && ( {!isNaN(Number(currentQuestion?.question.id)) && (
<strong className='number of questions'> <strong className="number of questions">
Question {Number(currentQuestion?.question.id)}/{quizQuestions?.length} Question {Number(currentQuestion?.question.id)}/
{quizQuestions?.length}
</strong> </strong>
)} )}
{quizMode === 'teacher' && ( {quizMode === 'teacher' && (
<div className="mb-1"> <div className="mb-1">
{/* <QuestionNavigation {/* <QuestionNavigation
currentQuestionId={Number(currentQuestion?.question.id)} currentQuestionId={Number(currentQuestion?.question.id)}
@ -487,12 +497,10 @@ const ManageRoom: React.FC = () => {
nextQuestion={nextQuestion} nextQuestion={nextQuestion}
/> */} /> */}
</div> </div>
)} )}
<div className="mb-2 flex-column-wrapper"> <div className="mb-2 flex-column-wrapper">
<div className="preview-and-result-container"> <div className="preview-and-result-container">
{currentQuestion && ( {currentQuestion && (
<QuestionDisplay <QuestionDisplay
showAnswer={false} showAnswer={false}
@ -507,39 +515,44 @@ const ManageRoom: React.FC = () => {
showSelectedQuestion={showSelectedQuestion} showSelectedQuestion={showSelectedQuestion}
students={students} students={students}
></LiveResultsComponent> ></LiveResultsComponent>
</div> </div>
</div> </div>
{quizMode === 'teacher' && ( {quizMode === 'teacher' && (
<div className="questionNavigationButtons" style={{ display: 'flex', justifyContent: 'center' }}> <div
className="questionNavigationButtons"
style={{ display: 'flex', justifyContent: 'center' }}
>
<div className="previousQuestionButton"> <div className="previousQuestionButton">
<Button onClick={previousQuestion} <Button
onClick={previousQuestion}
variant="contained" variant="contained"
disabled={Number(currentQuestion?.question.id) <= 1}> disabled={Number(currentQuestion?.question.id) <= 1}
>
Question précédente Question précédente
</Button> </Button>
</div> </div>
<div className="nextQuestionButton"> <div className="nextQuestionButton">
<Button onClick={nextQuestion} <Button
onClick={nextQuestion}
variant="contained" variant="contained"
disabled={Number(currentQuestion?.question.id) >= quizQuestions.length} disabled={
Number(currentQuestion?.question.id) >=
quizQuestions.length
}
> >
Prochaine question Prochaine question
</Button> </Button>
</div> </div>
</div>)} </div>
)}
</div> </div>
) : ( ) : (
<StudentWaitPage <StudentWaitPage
students={students} students={students}
launchQuiz={launchQuiz} launchQuiz={launchQuiz}
setQuizMode={setQuizMode} setQuizMode={setQuizMode}
/> />
)} )}
</div> </div>
</div> </div>

View file

@ -46,9 +46,10 @@ class WebSocketService {
} }
} }
createRoom() { createRoom(roomName: string) {
if (this.socket) { if (this.socket) {
this.socket.emit('create-room'); const nameToSend = roomName;
this.socket.emit('create-room', nameToSend);
} }
} }