From 39ce176ae7b57400da0bc2769529ad66438ab5c6 Mon Sep 17 00:00:00 2001 From: NouhailaAater Date: Sun, 23 Feb 2025 22:40:46 -0500 Subject: [PATCH] Ajout de RoomContext et deplacement de choix/creation de liste room dans le dashboard --- client/src/App.tsx | 53 +-- .../StudentWaitPage/StudentWaitPage.tsx | 4 +- .../src/pages/Student/JoinRoom/JoinRoom.tsx | 4 +- .../src/pages/Teacher/Dashboard/Dashboard.tsx | 242 +++++++---- .../pages/Teacher/ManageRoom/ManageRoom.tsx | 384 +++++------------- .../pages/Teacher/ManageRoom/RoomContext.tsx | 53 +++ 6 files changed, 340 insertions(+), 400 deletions(-) create mode 100644 client/src/pages/Teacher/ManageRoom/RoomContext.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 8f8ecf8..7db21db 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -13,6 +13,7 @@ import Register from './pages/Teacher/Register/Register'; import ResetPassword from './pages/Teacher/ResetPassword/ResetPassword'; import ManageRoom from './pages/Teacher/ManageRoom/ManageRoom'; import QuizForm from './pages/Teacher/EditorQuiz/EditorQuiz'; +import { RoomProvider } from './pages/Teacher/ManageRoom/RoomContext'; // Pages espace étudiant import JoinRoom from './pages/Student/JoinRoom/JoinRoom'; @@ -25,42 +26,42 @@ import ApiService from './services/ApiService'; const handleLogout = () => { ApiService.logout(); -} +}; const isLoggedIn = () => { return ApiService.isLoggedIn(); -} +}; function App() { return ( -
- -
+ + {' '} +
+
-
-
- - {/* Page main */} - } /> +
+
+ + {/* Page main */} + } /> - {/* Pages espace enseignant */} - } /> - } /> - } /> - } /> - } /> - } /> - } /> + {/* Pages espace enseignant */} + } /> + } /> + } /> + } /> + } /> + } /> + } /> - {/* Pages espace étudiant */} - } /> - -
+ {/* Pages espace étudiant */} + } /> + +
+
+
-
-
+ ); } diff --git a/client/src/components/StudentWaitPage/StudentWaitPage.tsx b/client/src/components/StudentWaitPage/StudentWaitPage.tsx index c87596a..7f50e2f 100644 --- a/client/src/components/StudentWaitPage/StudentWaitPage.tsx +++ b/client/src/components/StudentWaitPage/StudentWaitPage.tsx @@ -10,15 +10,13 @@ interface Props { students: StudentType[]; launchQuiz: () => void; setQuizMode: (mode: 'student' | 'teacher') => void; - setIsRoomSelectionVisible: (visible: boolean) => void; } -const StudentWaitPage: React.FC = ({ students, launchQuiz, setQuizMode, setIsRoomSelectionVisible }) => { +const StudentWaitPage: React.FC = ({ students, launchQuiz, setQuizMode }) => { const [isDialogOpen, setIsDialogOpen] = useState(false); const handleLaunchClick = () => { setIsDialogOpen(true); - setIsRoomSelectionVisible(false); }; return ( diff --git a/client/src/pages/Student/JoinRoom/JoinRoom.tsx b/client/src/pages/Student/JoinRoom/JoinRoom.tsx index 1e4f6c9..e02391b 100644 --- a/client/src/pages/Student/JoinRoom/JoinRoom.tsx +++ b/client/src/pages/Student/JoinRoom/JoinRoom.tsx @@ -48,6 +48,8 @@ const JoinRoom: React.FC = () => { setQuestion(question); }); socket.on('launch-student-mode', (questions: QuestionType[]) => { + console.log('Received launch-student-mode:', questions); + setQuizMode('student'); setIsWaitingForTeacher(false); setQuestions(questions); @@ -60,8 +62,6 @@ const JoinRoom: React.FC = () => { console.log('Failed to join the room.'); setConnectionError(`Erreur de connexion : ${message}`); setIsConnecting(false); - setRoomName(''); // Réinitialise le nom de la salle - setUsername(''); // Réinitialise le nom d'utilisateur }); socket.on('connect_error', (error) => { switch (error.message) { diff --git a/client/src/pages/Teacher/Dashboard/Dashboard.tsx b/client/src/pages/Teacher/Dashboard/Dashboard.tsx index f920d12..8bcf64d 100644 --- a/client/src/pages/Teacher/Dashboard/Dashboard.tsx +++ b/client/src/pages/Teacher/Dashboard/Dashboard.tsx @@ -12,8 +12,13 @@ import ApiService from '../../../services/ApiService'; import './dashboard.css'; import ImportModal from 'src/components/ImportModal/ImportModal'; //import axios from 'axios'; - +import { RoomType } from 'src/Types/RoomType'; +import { useRooms } from '../ManageRoom/RoomContext'; import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, TextField, IconButton, InputAdornment, @@ -22,7 +27,7 @@ import { Tooltip, NativeSelect, CardContent, - styled, + styled } from '@mui/material'; import { Search, @@ -33,7 +38,7 @@ import { FolderCopy, ContentCopy, Edit, - Share, + Share // DriveFileMove } from '@mui/icons-material'; @@ -43,7 +48,7 @@ const CustomCard = styled(Card)({ position: 'relative', margin: '40px 0 20px 0', // Add top margin to make space for the tab borderRadius: '8px', - paddingTop: '20px', // Ensure content inside the card doesn't overlap with the tab + paddingTop: '20px' // Ensure content inside the card doesn't overlap with the tab }); const Dashboard: React.FC = () => { @@ -53,6 +58,10 @@ const Dashboard: React.FC = () => { const [showImportModal, setShowImportModal] = useState(false); const [folders, setFolders] = useState([]); const [selectedFolderId, setSelectedFolderId] = useState(''); // Selected folder + const [rooms, setRooms] = useState([]); + const [openDialog, setOpenDialog] = useState(false); + const [newRoomTitle, setNewRoomTitle] = useState(''); + const { selectedRoom, selectRoom, createRoom } = useRooms(); // Filter quizzes based on search term // const filteredQuizzes = quizzes.filter(quiz => @@ -65,7 +74,6 @@ const Dashboard: React.FC = () => { ); }, [quizzes, searchTerm]); - // Group quizzes by folder const quizzesByFolder = filteredQuizzes.reduce((acc, quiz) => { if (!acc[quiz.folderName]) { @@ -78,27 +86,34 @@ const Dashboard: React.FC = () => { useEffect(() => { const fetchData = async () => { if (!ApiService.isLoggedIn()) { - navigate("/teacher/login"); + navigate('/teacher/login'); return; - } - else { - const userFolders = await ApiService.getUserFolders(); + } else { + const userRooms = await ApiService.getUserRooms(); + setRooms(userRooms as RoomType[]); + const userFolders = await ApiService.getUserFolders(); setFolders(userFolders as FolderType[]); } - }; fetchData(); }, []); + const handleSubmitRoom = async () => { + if (newRoomTitle.trim()) { + await createRoom(newRoomTitle); + setOpenDialog(false); + setNewRoomTitle(''); + } + }; + const handleSelectFolder = (event: React.ChangeEvent) => { setSelectedFolderId(event.target.value); }; useEffect(() => { const fetchQuizzesForFolder = async () => { - if (selectedFolderId == '') { const folders = await ApiService.getUserFolders(); // HACK force user folders to load on first load //console.log("show all quizzes") @@ -109,33 +124,29 @@ const Dashboard: React.FC = () => { //console.log("folder: ", folder.title, " quiz: ", folderQuizzes); // add the folder.title to the QuizType if the folderQuizzes is an array addFolderTitleToQuizzes(folderQuizzes, folder.title); - quizzes = quizzes.concat(folderQuizzes as QuizType[]) + quizzes = quizzes.concat(folderQuizzes as QuizType[]); } setQuizzes(quizzes as QuizType[]); - } - else { - console.log("show some quizzes") + } else { + console.log('show some quizzes'); const folderQuizzes = await ApiService.getFolderContent(selectedFolderId); - console.log("folderQuizzes: ", folderQuizzes); + console.log('folderQuizzes: ', folderQuizzes); // get the folder title from its id - const folderTitle = folders.find((folder) => folder._id === selectedFolderId)?.title || ''; + const folderTitle = + folders.find((folder) => folder._id === selectedFolderId)?.title || ''; addFolderTitleToQuizzes(folderQuizzes, folderTitle); setQuizzes(folderQuizzes as QuizType[]); } - - }; fetchQuizzesForFolder(); }, [selectedFolderId]); - const handleSearch = (event: React.ChangeEvent) => { setSearchTerm(event.target.value); }; - const handleRemoveQuiz = async (quiz: QuizType) => { try { const confirmed = window.confirm('Voulez-vous vraiment supprimer ce quiz?'); @@ -149,30 +160,27 @@ const Dashboard: React.FC = () => { } }; - const handleDuplicateQuiz = async (quiz: QuizType) => { try { await ApiService.duplicateQuiz(quiz._id); if (selectedFolderId == '') { const folders = await ApiService.getUserFolders(); // HACK force user folders to load on first load - console.log("show all quizzes") + console.log('show all quizzes'); let quizzes: QuizType[] = []; for (const folder of folders as FolderType[]) { const folderQuizzes = await ApiService.getFolderContent(folder._id); - console.log("folder: ", folder.title, " quiz: ", folderQuizzes); + console.log('folder: ', folder.title, ' quiz: ', folderQuizzes); addFolderTitleToQuizzes(folderQuizzes, folder.title); quizzes = quizzes.concat(folderQuizzes as QuizType[]); } setQuizzes(quizzes as QuizType[]); - } - else { - console.log("show some quizzes") + } else { + console.log('show some quizzes'); const folderQuizzes = await ApiService.getFolderContent(selectedFolderId); addFolderTitleToQuizzes(folderQuizzes, selectedFolderId); setQuizzes(folderQuizzes as QuizType[]); - } } catch (error) { console.error('Error duplicating quiz:', error); @@ -181,7 +189,6 @@ const Dashboard: React.FC = () => { const handleOnImport = () => { setShowImportModal(true); - }; const validateQuiz = (questions: string[]) => { @@ -196,7 +203,7 @@ const Dashboard: React.FC = () => { // questions[i] = QuestionService.ignoreImgTags(questions[i]); const parsedItem = parse(questions[i]); Template(parsedItem[0]); - // eslint-disable-next-line @typescript-eslint/no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error) { return false; } @@ -206,9 +213,8 @@ const Dashboard: React.FC = () => { }; const downloadTxtFile = async (quiz: QuizType) => { - try { - const selectedQuiz = await ApiService.getQuiz(quiz._id) as QuizType; + const selectedQuiz = (await ApiService.getQuiz(quiz._id)) as QuizType; //quizzes.find((quiz) => quiz._id === quiz._id); if (!selectedQuiz) { @@ -216,7 +222,7 @@ const Dashboard: React.FC = () => { } //const { title, content } = selectedQuiz; - let quizContent = ""; + let quizContent = ''; const title = selectedQuiz.title; console.log(selectedQuiz.content); selectedQuiz.content.forEach((question, qIndex) => { @@ -231,7 +237,9 @@ const Dashboard: React.FC = () => { }); if (!validateQuiz(selectedQuiz.content)) { - window.alert('Attention! Ce quiz contient des questions invalides selon le format GIFT.'); + window.alert( + 'Attention! Ce quiz contient des questions invalides selon le format GIFT.' + ); } const blob = new Blob([quizContent], { type: 'text/plain' }); const a = document.createElement('a'); @@ -239,8 +247,6 @@ const Dashboard: React.FC = () => { a.download = `${filename}.gift`; a.href = window.URL.createObjectURL(blob); a.click(); - - } catch (error) { console.error('Error exporting selected quiz:', error); } @@ -252,18 +258,16 @@ const Dashboard: React.FC = () => { if (folderTitle) { await ApiService.createFolder(folderTitle); const userFolders = await ApiService.getUserFolders(); - setFolders(userFolders as FolderType[]); + setFolders(userFolders as FolderType[]); const newlyCreatedFolder = userFolders[userFolders.length - 1] as FolderType; setSelectedFolderId(newlyCreatedFolder._id); - } } catch (error) { console.error('Error creating folder:', error); } }; - - const handleDeleteFolder = async () => { + const handleDeleteFolder = async () => { try { const confirmed = window.confirm('Voulez-vous vraiment supprimer ce dossier?'); if (confirmed) { @@ -273,18 +277,17 @@ const Dashboard: React.FC = () => { } const folders = await ApiService.getUserFolders(); // HACK force user folders to load on first load - console.log("show all quizzes") + console.log('show all quizzes'); let quizzes: QuizType[] = []; for (const folder of folders as FolderType[]) { const folderQuizzes = await ApiService.getFolderContent(folder._id); - console.log("folder: ", folder.title, " quiz: ", folderQuizzes); - quizzes = quizzes.concat(folderQuizzes as QuizType[]) + console.log('folder: ', folder.title, ' quiz: ', folderQuizzes); + quizzes = quizzes.concat(folderQuizzes as QuizType[]); } setQuizzes(quizzes as QuizType[]); setSelectedFolderId(''); - } catch (error) { console.error('Error deleting folder:', error); } @@ -294,12 +297,15 @@ const Dashboard: React.FC = () => { try { // folderId: string GET THIS FROM CURRENT FOLDER // currentTitle: string GET THIS FROM CURRENT FOLDER - const newTitle = prompt('Entrée le nouveau nom du fichier', folders.find((folder) => folder._id === selectedFolderId)?.title); + const newTitle = prompt( + 'Entrée le nouveau nom du fichier', + folders.find((folder) => folder._id === selectedFolderId)?.title + ); if (newTitle) { const renamedFolderId = selectedFolderId; const result = await ApiService.renameFolder(selectedFolderId, newTitle); - if (result !== true ) { + if (result !== true) { window.alert(`Une erreur est survenue: ${result}`); return; } @@ -331,46 +337,79 @@ const Dashboard: React.FC = () => { }; const handleCreateQuiz = () => { - navigate("/teacher/editor-quiz/new"); - } + navigate('/teacher/editor-quiz/new'); + }; const handleEditQuiz = (quiz: QuizType) => { navigate(`/teacher/editor-quiz/${quiz._id}`); - } + }; const handleLancerQuiz = (quiz: QuizType) => { navigate(`/teacher/manage-room/${quiz._id}`); - } + }; const handleShareQuiz = async (quiz: QuizType) => { try { - const email = prompt(`Veuillez saisir l'email de la personne avec qui vous souhaitez partager ce quiz`, ""); + const email = prompt( + `Veuillez saisir l'email de la personne avec qui vous souhaitez partager ce quiz`, + '' + ); if (email) { const result = await ApiService.ShareQuiz(quiz._id, email); if (!result) { - window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`) + window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`); return; } - window.alert(`Quiz partagé avec succès!`) + window.alert(`Quiz partagé avec succès!`); } - } catch (error) { console.error('Erreur lors du partage du quiz:', error); } - } - - - + }; return ( -
-
Tableau de bord
+
+ + +
+ + {selectedRoom && ( +
+

Salle sélectionnée: {selectedRoom.title}

+
+ )} + + setOpenDialog(false)}> + Créer une nouvelle salle + + setNewRoomTitle(e.target.value)} + fullWidth + /> + + + + + + +
{ />
-
-
+
+
{ {folders.map((folder: FolderType) => ( - + ))}
-
+
- + + {' '} + {' '} + @@ -418,7 +460,10 @@ const Dashboard: React.FC = () => { color="primary" onClick={handleRenameFolder} disabled={selectedFolderId == ''} // cannot action on all - > + > + {' '} + {' '} + @@ -426,7 +471,10 @@ const Dashboard: React.FC = () => { color="primary" onClick={handleDuplicateFolder} disabled={selectedFolderId == ''} // cannot action on all - > + > + {' '} + {' '} + @@ -435,13 +483,15 @@ const Dashboard: React.FC = () => { color="primary" onClick={handleDeleteFolder} disabled={selectedFolderId == ''} // cannot action on all - > + > + {' '} + {' '} +
-
-
+
-
-
- {Object.keys(quizzesByFolder).map(folderName => ( - -
{folderName}
+
+ {Object.keys(quizzesByFolder).map((folderName) => ( + +
{folderName}
{quizzesByFolder[folderName].map((quiz: QuizType) => ( -
-
+
+
-
+
downloadTxtFile(quiz)} - > + > + {' '} + {' '} + handleEditQuiz(quiz)} - > + > + {' '} + {' '} + handleDuplicateQuiz(quiz)} - > + > + {' '} + {' '} + @@ -507,14 +567,20 @@ const Dashboard: React.FC = () => { aria-label="delete" color="primary" onClick={() => handleRemoveQuiz(quiz)} - > + > + {' '} + {' '} + handleShareQuiz(quiz)} - > + > + {' '} + {' '} +
@@ -529,7 +595,6 @@ const Dashboard: React.FC = () => { handleOnImport={handleOnImport} selectedFolder={selectedFolderId} /> -
); }; @@ -542,4 +607,3 @@ function addFolderTitleToQuizzes(folderQuizzes: string | QuizType[], folderName: console.log(`quiz: ${quiz.title} folder: ${quiz.folderName}`); }); } - diff --git a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx index 556fa55..0619377 100644 --- a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx +++ b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx @@ -23,13 +23,11 @@ import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton'; import QuestionDisplay from 'src/components/QuestionsDisplay/QuestionDisplay'; import ApiService from '../../../services/ApiService'; import { QuestionType } from 'src/Types/QuestionType'; -import { RoomType } from 'src/Types/RoomType'; -import { Button, NativeSelect } from '@mui/material'; -import { Dialog, DialogActions, DialogContent, DialogTitle, TextField } from '@mui/material'; +import { useRooms } from '../ManageRoom/RoomContext'; +import { Button } from '@mui/material'; const ManageRoom: React.FC = () => { const navigate = useNavigate(); - const [roomName, setRoomName] = useState(''); const [socket, setSocket] = useState(null); const [students, setStudents] = useState([]); const quizId = useParams<{ id: string }>(); @@ -39,11 +37,9 @@ const ManageRoom: React.FC = () => { const [connectingError, setConnectingError] = useState(''); const [currentQuestion, setCurrentQuestion] = useState(undefined); const [quizStarted, setQuizStarted] = useState(false); - const [rooms, setRooms] = useState([]); - const [selectedRoomId, setSelectedRoomId] = useState(''); - const [openDialog, setOpenDialog] = useState(false); - const [newRoomTitle, setNewRoomTitle] = useState(''); - const [isRoomSelectionVisible, setIsRoomSelectionVisible] = useState(true); + const { selectedRoom } = useRooms(); + const roomName = selectedRoom?.title || ''; + useEffect(() => { const fetchData = async () => { @@ -51,54 +47,11 @@ const ManageRoom: React.FC = () => { navigate('/teacher/login'); return; } - - const userRooms = await ApiService.getUserRooms(); - setRooms(userRooms as RoomType[]); }; fetchData(); }, []); - const handleSelectRoom = (event: React.ChangeEvent) => { - const roomId = event.target.value; - setSelectedRoomId(roomId); - - const selectedRoom = rooms.find((room) => room._id === roomId); - setRoomName(selectedRoom?.title || ''); - }; - - useEffect(() => { - if (rooms.length > 0 && !selectedRoomId) { - setSelectedRoomId(rooms[0]._id); - } - }, [rooms]); - - const handleDialogClose = () => { - setOpenDialog(false); - }; - - const handleCreateRoom = async () => { - setOpenDialog(true); - }; - const handleSubmitRoom = async () => { - try { - if (newRoomTitle.trim()) { - const createdRoom = await ApiService.createRoom(newRoomTitle); - - const updatedRooms = await ApiService.getUserRooms(); - setRooms(updatedRooms as RoomType[]); - - if (createdRoom) { - setSelectedRoomId(createdRoom); - } - setOpenDialog(false); - setNewRoomTitle(''); - } - } catch (error) { - console.error('Error creating Room::', error); - } - }; - useEffect(() => { if (quizId.id) { const fetchquiz = async () => { @@ -136,25 +89,6 @@ const ManageRoom: React.FC = () => { } }, [quizId]); - useEffect(() => { - if (rooms.length > 0 && !selectedRoomId) { - setSelectedRoomId(rooms[0].title); - } - }, [rooms]); - - useEffect(() => { - if (!newRoomTitle && !selectedRoomId) { - setConnectingError('Aucun nom de salle sélectionné ou créé.'); - } - }, [newRoomTitle, selectedRoomId]); - - useEffect(() => { - if (selectedRoomId && selectedRoomId.trim() !== '') { - console.log(`Sélection d'une nouvelle salle: ${selectedRoomId}`); - createWebSocketRoom(); - } - }, [selectedRoomId]); - const disconnectWebSocket = () => { if (socket) { webSocketService.endQuiz(roomName); @@ -163,162 +97,117 @@ const ManageRoom: React.FC = () => { setQuizQuestions(undefined); setCurrentQuestion(undefined); setStudents(new Array()); - setRoomName(''); } }; const createWebSocketRoom = () => { console.log('Creating WebSocket room...'); - setConnectingError(''); - - const handleRoomCreation = (socket: Socket, roomToCreate?: string) => { - socket.on('connect', () => { - if (roomToCreate) { - webSocketService.createRoom(roomToCreate); - } else { - socket.emit("create-room"); - } - }); - - socket.on('create-success', (createdRoomName: string) => { - console.log('Salle créée/jointe:', createdRoomName); - setRoomName(createdRoomName); - }); - - socket.on('create-failure', (errorMessage: string) => { - setConnectingError(errorMessage); - console.error('Erreur création salle:', errorMessage); - }); - socket.on('user-joined', (student: StudentType) => { - console.log(`Student joined: name = ${student.name}, id = ${student.id}`); - - setStudents((prevStudents) => [...prevStudents, student]); - - if (quizMode === 'teacher') { - webSocketService.nextQuestion(roomName, currentQuestion); - } else if (quizMode === 'student') { - webSocketService.launchStudentModeQuiz(roomName, quizQuestions); - } - }); + if (!selectedRoom) { + setConnectingError('Aucune salle sélectionnée.'); + return; + } - socket.on('join-failure', (message) => { - setConnectingError(message); - setSocket(null); - }); + const socket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_SOCKET_URL); + socket.on('connect', () => { + webSocketService.createRoom(selectedRoom.title); + }); + + socket.on('connect_error', (error) => { + setConnectingError('Erreur lors de la connexion... Veuillez réessayer'); + console.error('ManageRoom: WebSocket connection error:', error); + }); + + socket.on('user-joined', (student: StudentType) => { + console.log(`Student joined: name = ${student.name}, id = ${student.id}`); - socket.on('user-disconnected', (userId: string) => { - console.log(`Student left: id = ${userId}`); - setStudents((prevUsers) => prevUsers.filter((user) => user.id !== userId)); - }); + setStudents((prevStudents) => [...prevStudents, student]); + + if (quizMode === 'teacher') { + webSocketService.nextQuestion(roomName, currentQuestion); + } else if (quizMode === 'student') { + webSocketService.launchStudentModeQuiz(roomName, quizQuestions); + } + }); + socket.on('join-failure', (message) => { + setConnectingError(message); + setSocket(null); + }); + + socket.on('user-disconnected', (userId: string) => { + console.log(`Student left: id = ${userId}`); + setStudents((prevUsers) => prevUsers.filter((user) => user.id !== userId)); + }); + + setSocket(socket); }; - - if (rooms.length === 0) { - console.log('Tentative de création de salle automatique...'); - const newSocket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_SOCKET_URL); - handleRoomCreation(newSocket); - setSocket(newSocket); - } else { - const targetRoom = rooms.find((room) => room._id === selectedRoomId) || rooms[0]; - if (!targetRoom) { - setConnectingError('Aucune salle disponible'); - return; + + useEffect(() => { + // This is here to make sure the correct value is sent when user join + if (socket) { + console.log(`Listening for user-joined in room ${roomName}`); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + socket.on('user-joined', (_student: StudentType) => { + if (quizMode === 'teacher') { + webSocketService.nextQuestion(roomName, currentQuestion); + } else if (quizMode === 'student') { + webSocketService.launchStudentModeQuiz(roomName, quizQuestions); + } + }); } - console.log('Utilisation de la salle:', targetRoom.title); - const newSocket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_SOCKET_URL); - handleRoomCreation(newSocket, targetRoom.title); - setSocket(newSocket); - } - socket?.on('connect_error', (error) => { - setConnectingError('Erreur de connexion au serveur...'); - console.error('Connection error:', error); - }); - }; - - useEffect(() => { - // This is here to make sure the correct value is sent when user join - if (socket) { - console.log(`Listening for user-joined in room ${roomName}`); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - socket.on('user-joined', (_student: StudentType) => { - if (quizMode === 'teacher') { - webSocketService.nextQuestion(roomName, currentQuestion); - } else if (quizMode === 'student') { - webSocketService.launchStudentModeQuiz(roomName, quizQuestions); - } - }); - } - - if (socket) { - // handle the case where user submits an answer - console.log(`Listening for submit-answer-room in room ${roomName}`); - socket.on('submit-answer-room', (answerData: AnswerReceptionFromBackendType) => { - const { answer, idQuestion, idUser, username } = answerData; - console.log( - `Received answer from ${username} for question ${idQuestion}: ${answer}` - ); - if (!quizQuestions) { - console.log('Quiz questions not found (cannot update answers without them).'); - return; - } - - // Update the students state using the functional form of setStudents - setStudents((prevStudents) => { - // print the list of current student names - console.log('Current students:'); - prevStudents.forEach((student) => { - console.log(student.name); - }); - - let foundStudent = false; - const updatedStudents = prevStudents.map((student) => { - console.log(`Comparing ${student.id} to ${idUser}`); - if (student.id === idUser) { - foundStudent = true; - const existingAnswer = student.answers.find( - (ans) => ans.idQuestion === idQuestion - ); - let updatedAnswers: Answer[] = []; - if (existingAnswer) { - // Update the existing answer - updatedAnswers = student.answers.map((ans) => { - console.log(`Comparing ${ans.idQuestion} to ${idQuestion}`); - return ans.idQuestion === idQuestion - ? { - ...ans, - answer, - isCorrect: checkIfIsCorrect( - answer, - idQuestion, - quizQuestions! - ) - } - : ans; - }); - } else { - // Add a new answer - const newAnswer = { - idQuestion, - answer, - isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!) - }; - updatedAnswers = [...student.answers, newAnswer]; - } - return { ...student, answers: updatedAnswers }; - } - return student; - }); - if (!foundStudent) { - console.log(`Student ${username} not found in the list.`); + if (socket) { + // handle the case where user submits an answer + console.log(`Listening for submit-answer-room in room ${roomName}`); + socket.on('submit-answer-room', (answerData: AnswerReceptionFromBackendType) => { + const { answer, idQuestion, idUser, username } = answerData; + console.log(`Received answer from ${username} for question ${idQuestion}: ${answer}`); + if (!quizQuestions) { + console.log('Quiz questions not found (cannot update answers without them).'); + return; } - return updatedStudents; + + // Update the students state using the functional form of setStudents + setStudents((prevStudents) => { + // print the list of current student names + console.log('Current students:'); + prevStudents.forEach((student) => { + console.log(student.name); + }); + + let foundStudent = false; + const updatedStudents = prevStudents.map((student) => { + console.log(`Comparing ${student.id} to ${idUser}`); + if (student.id === idUser) { + foundStudent = true; + const existingAnswer = student.answers.find((ans) => ans.idQuestion === idQuestion); + let updatedAnswers: Answer[] = []; + if (existingAnswer) { + // Update the existing answer + updatedAnswers = student.answers.map((ans) => { + console.log(`Comparing ${ans.idQuestion} to ${idQuestion}`); + return (ans.idQuestion === idQuestion ? { ...ans, answer, isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!) } : ans); + }); + } else { + // Add a new answer + const newAnswer = { idQuestion, answer, isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!) }; + updatedAnswers = [...student.answers, newAnswer]; + } + return { ...student, answers: updatedAnswers }; + } + return student; + }); + if (!foundStudent) { + console.log(`Student ${username} not found in the list.`); + } + return updatedStudents; + }); }); - }); - setSocket(socket); - } - }, [socket, currentQuestion, quizQuestions]); + setSocket(socket); + } + + }, [socket, currentQuestion, quizQuestions]); const nextQuestion = () => { if (!quizQuestions || !currentQuestion || !quiz?.content) return; @@ -497,6 +386,7 @@ const ManageRoom: React.FC = () => { return (
+

Salle : {roomName}

{ width: '100%' }} > -
-
Salle: {roomName}
-
{quizStarted && (
{
- {isRoomSelectionVisible && ( -
-
- - - {rooms.map((room: RoomType) => ( - - ))} - -
- -
- -
- - {/* Dialog pour créer une salle */} - - Créer une nouvelle salle - - setNewRoomTitle(e.target.value)} - /> - - - - - - -
- )} + {/* the following breaks the css (if 'room' classes are nested) */}
@@ -669,7 +494,6 @@ const ManageRoom: React.FC = () => { students={students} launchQuiz={launchQuiz} setQuizMode={setQuizMode} - setIsRoomSelectionVisible={setIsRoomSelectionVisible} /> )}
diff --git a/client/src/pages/Teacher/ManageRoom/RoomContext.tsx b/client/src/pages/Teacher/ManageRoom/RoomContext.tsx new file mode 100644 index 0000000..545400c --- /dev/null +++ b/client/src/pages/Teacher/ManageRoom/RoomContext.tsx @@ -0,0 +1,53 @@ +import { createContext, useContext, useState, useEffect } from 'react'; +import ApiService from '../../../services/ApiService'; +import { RoomType } from 'src/Types/RoomType'; +import React from "react"; + +type RoomContextType = { + rooms: RoomType[]; + selectedRoom: RoomType | null; + selectRoom: (roomId: string) => void; + createRoom: (title: string) => Promise; +}; + +const RoomContext = createContext(undefined); + +export const RoomProvider = ({ children }: { children: React.ReactNode }) => { + const [rooms, setRooms] = useState([]); + const [selectedRoom, setSelectedRoom] = useState(null); + + useEffect(() => { + const loadRooms = async () => { + const userRooms = await ApiService.getUserRooms(); + setRooms(userRooms as RoomType[]); + }; + loadRooms(); + }, []); + + // Sélectionner une salle + const selectRoom = (roomId: string) => { + const room = rooms.find(r => r._id === roomId) || null; + setSelectedRoom(room); + localStorage.setItem('selectedRoomId', roomId); + }; + + // Créer une salle + const createRoom = async (title: string) => { + const newRoomId = await ApiService.createRoom(title); + const updatedRooms = await ApiService.getUserRooms(); + setRooms(updatedRooms as RoomType[]); + selectRoom(newRoomId); + }; + + return ( + + {children} + + ); +}; + +export const useRooms = () => { + const context = useContext(RoomContext); + if (!context) throw new Error('useRooms must be used within a RoomProvider'); + return context; +}; \ No newline at end of file