mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Compare commits
2 commits
f468b6d9f2
...
338f18c1cd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
338f18c1cd | ||
|
|
0941a44e50 |
2 changed files with 170 additions and 110 deletions
|
|
@ -11,7 +11,16 @@ import GIFTTemplatePreview from 'src/components/GiftTemplate/GIFTTemplatePreview
|
|||
import { QuizType } from '../../../Types/QuizType';
|
||||
|
||||
import './editorQuiz.css';
|
||||
import { Button, TextField, NativeSelect, Divider, Dialog, DialogTitle, DialogActions, DialogContent } from '@mui/material';
|
||||
import {
|
||||
Button,
|
||||
TextField,
|
||||
NativeSelect,
|
||||
Divider,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
DialogContent
|
||||
} from '@mui/material';
|
||||
import ReturnButton from 'src/components/ReturnButton/ReturnButton';
|
||||
|
||||
import ApiService from '../../../services/ApiService';
|
||||
|
|
@ -62,7 +71,7 @@ const QuizForm: React.FC = () => {
|
|||
};
|
||||
}, []);
|
||||
|
||||
const scrollToImagesSection = (event: { preventDefault: () => void; }) => {
|
||||
const scrollToImagesSection = (event: { preventDefault: () => void }) => {
|
||||
event.preventDefault();
|
||||
const section = document.getElementById('images-section');
|
||||
if (section) {
|
||||
|
|
@ -87,10 +96,12 @@ const QuizForm: React.FC = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
const quiz = await ApiService.getQuiz(id) as QuizType;
|
||||
const quiz = (await ApiService.getQuiz(id)) as QuizType;
|
||||
|
||||
if (!quiz) {
|
||||
window.alert(`Une erreur est survenue.\n Le quiz ${id} n'a pas été trouvé\nVeuillez réessayer plus tard`)
|
||||
window.alert(
|
||||
`Une erreur est survenue.\n Le quiz ${id} n'a pas été trouvé\nVeuillez réessayer plus tard`
|
||||
);
|
||||
console.error('Quiz not found for id:', id);
|
||||
navigate('/teacher/dashboard');
|
||||
return;
|
||||
|
|
@ -103,9 +114,8 @@ const QuizForm: React.FC = () => {
|
|||
setSelectedFolder(folderId);
|
||||
setFilteredValue(content);
|
||||
setValue(quiz.content.join('\n\n'));
|
||||
|
||||
} catch (error) {
|
||||
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`)
|
||||
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`);
|
||||
console.error('Error fetching quiz:', error);
|
||||
navigate('/teacher/dashboard');
|
||||
}
|
||||
|
|
@ -138,12 +148,12 @@ const QuizForm: React.FC = () => {
|
|||
try {
|
||||
// check if everything is there
|
||||
if (quizTitle == '') {
|
||||
alert("Veuillez choisir un titre");
|
||||
alert('Veuillez choisir un titre');
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedFolder == '') {
|
||||
alert("Veuillez choisir un dossier");
|
||||
alert('Veuillez choisir un dossier');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -157,8 +167,8 @@ const QuizForm: React.FC = () => {
|
|||
|
||||
navigate('/teacher/dashboard');
|
||||
} catch (error) {
|
||||
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`)
|
||||
console.log(error)
|
||||
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`);
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -177,107 +187,122 @@ const QuizForm: React.FC = () => {
|
|||
}
|
||||
|
||||
if (!inputElement.files || inputElement.files.length === 0) {
|
||||
window.alert("Veuillez d'abord choisir une image à téléverser.")
|
||||
window.alert("Veuillez d'abord choisir une image à téléverser.");
|
||||
return;
|
||||
}
|
||||
|
||||
const imageUrl = await ApiService.uploadImage(inputElement.files[0]);
|
||||
|
||||
// Check for errors
|
||||
if(imageUrl.indexOf("ERROR") >= 0) {
|
||||
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`)
|
||||
if (imageUrl.indexOf('ERROR') >= 0) {
|
||||
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`);
|
||||
return;
|
||||
}
|
||||
|
||||
setImageLinks(prevLinks => [...prevLinks, imageUrl]);
|
||||
setImageLinks((prevLinks) => [...prevLinks, imageUrl]);
|
||||
|
||||
// Reset the file input element
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
} catch (error) {
|
||||
window.alert(`Une erreur est survenue.\n${error}\nVeuillez réessayer plus tard.`)
|
||||
|
||||
window.alert(`Une erreur est survenue.\n${error}\nVeuillez réessayer plus tard.`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCopyToClipboard = async (link: string) => {
|
||||
navigator.clipboard.writeText(link);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='quizEditor'>
|
||||
|
||||
<div className='editHeader'>
|
||||
<div className="quizEditor">
|
||||
<div
|
||||
className="editHeader"
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: '32px'
|
||||
}}
|
||||
>
|
||||
<ReturnButton
|
||||
askConfirm
|
||||
message={`Êtes-vous sûr de vouloir quitter l'éditeur sans sauvegarder le questionnaire?`}
|
||||
/>
|
||||
|
||||
<div className='title'>Éditeur de quiz</div>
|
||||
|
||||
<div className='dumb'></div>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleQuizSave}
|
||||
sx={{ display: 'flex', alignItems: 'center' }}
|
||||
>
|
||||
<SaveIcon sx={{ fontSize: 20 }} />
|
||||
Enregistrer
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* <h2 className="subtitle">Éditeur</h2> */}
|
||||
<div style={{ textAlign: 'center', marginTop: '30px' }}>
|
||||
<div className="title">Éditeur de quiz</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<TextField
|
||||
onChange={handleQuizTitleChange}
|
||||
value={quizTitle}
|
||||
placeholder="Titre du quiz"
|
||||
label="Titre du quiz"
|
||||
fullWidth
|
||||
sx={{ width: '200px', marginTop: '50px' }}
|
||||
/>
|
||||
<label>Choisir un dossier:
|
||||
|
||||
<NativeSelect
|
||||
id="select-folder"
|
||||
color="primary"
|
||||
value={selectedFolder}
|
||||
onChange={handleSelectFolder}
|
||||
disabled={!isNewQuiz}
|
||||
style={{ marginBottom: '16px' }} // Ajout de marge en bas
|
||||
style={{ marginBottom: '16px', width: '200px', marginTop: '50px' }}
|
||||
>
|
||||
<option disabled value=""> Choisir un dossier... </option>
|
||||
|
||||
<option disabled value="">
|
||||
Choisir un dossier...
|
||||
</option>
|
||||
{folders.map((folder: FolderType) => (
|
||||
<option value={folder._id} key={folder._id}> {folder.title} </option>
|
||||
<option value={folder._id} key={folder._id}>
|
||||
{folder.title}
|
||||
</option>
|
||||
))}
|
||||
</NativeSelect></label>
|
||||
|
||||
<Button variant="contained" onClick={handleQuizSave}>
|
||||
{<SaveIcon sx={{ fontSize: 20, marginRight: '8px' }} />}
|
||||
Enregistrer
|
||||
</Button>
|
||||
</NativeSelect>
|
||||
</div>
|
||||
|
||||
<Divider style={{ margin: '16px 0' }} />
|
||||
|
||||
<div className='editSection'>
|
||||
|
||||
<div className='edit'>
|
||||
<div className="editSection">
|
||||
<div className="edit">
|
||||
<Editor
|
||||
label="Contenu GIFT du quiz:"
|
||||
initialValue={value}
|
||||
onEditorChange={handleUpdatePreview} />
|
||||
onEditorChange={handleUpdatePreview}
|
||||
/>
|
||||
|
||||
<div className='images'>
|
||||
<div className='upload'>
|
||||
<div className="images">
|
||||
<div className="upload">
|
||||
<label className="dropArea">
|
||||
<input type="file" id="file-input" className="file-input"
|
||||
<input
|
||||
type="file"
|
||||
id="file-input"
|
||||
className="file-input"
|
||||
accept="image/jpeg, image/png"
|
||||
multiple
|
||||
ref={fileInputRef} />
|
||||
ref={fileInputRef}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
aria-label='Téléverser'
|
||||
onClick={handleSaveImage}>
|
||||
aria-label="Téléverser"
|
||||
onClick={handleSaveImage}
|
||||
>
|
||||
Téléverser <Upload />
|
||||
</Button>
|
||||
|
||||
</label>
|
||||
<Dialog
|
||||
open={dialogOpen}
|
||||
onClose={() => setDialogOpen(false)} >
|
||||
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)}>
|
||||
<DialogTitle>Erreur</DialogTitle>
|
||||
<DialogContent>
|
||||
Veuillez d'abord choisir une image à téléverser.
|
||||
|
|
@ -293,22 +318,31 @@ const QuizForm: React.FC = () => {
|
|||
<h4>Mes images :</h4>
|
||||
<div>
|
||||
<div>
|
||||
<div style={{ display: "inline" }}>(Voir section </div>
|
||||
<a href="#images-section"style={{ textDecoration: "none" }} onClick={scrollToImagesSection}>
|
||||
<u><em><h4 style={{ display: "inline" }}> 9. Images </h4></em></u>
|
||||
<div style={{ display: 'inline' }}>(Voir section </div>
|
||||
<a
|
||||
href="#images-section"
|
||||
style={{ textDecoration: 'none' }}
|
||||
onClick={scrollToImagesSection}
|
||||
>
|
||||
<u>
|
||||
<em>
|
||||
<h4 style={{ display: 'inline' }}> 9. Images </h4>
|
||||
</em>
|
||||
</u>
|
||||
</a>
|
||||
<div style={{ display: "inline" }}> ci-dessous</div>
|
||||
<div style={{ display: "inline" }}>)</div>
|
||||
<div style={{ display: 'inline' }}> ci-dessous</div>
|
||||
<div style={{ display: 'inline' }}>)</div>
|
||||
<br />
|
||||
<em> - Cliquez sur un lien pour le copier</em>
|
||||
</div>
|
||||
<ul>
|
||||
{imageLinks.map((link, index) => {
|
||||
const imgTag = `} "texte de l'infobulle")`;
|
||||
const imgTag = `} "texte de l'infobulle")`;
|
||||
return (
|
||||
<li key={index}>
|
||||
<code
|
||||
onClick={() => handleCopyToClipboard(imgTag)}>
|
||||
<code onClick={() => handleCopyToClipboard(imgTag)}>
|
||||
{imgTag}
|
||||
</code>
|
||||
</li>
|
||||
|
|
@ -319,10 +353,9 @@ const QuizForm: React.FC = () => {
|
|||
</div>
|
||||
|
||||
<GiftCheatSheet />
|
||||
|
||||
</div>
|
||||
|
||||
<div className='preview'>
|
||||
<div className="preview">
|
||||
<div className="preview-column">
|
||||
<h4>Prévisualisation</h4>
|
||||
<div>
|
||||
|
|
@ -330,7 +363,6 @@ const QuizForm: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{showScrollButton && (
|
||||
|
|
@ -358,7 +390,7 @@ const scrollToTopButtonStyle: CSSProperties = {
|
|||
backgroundColor: '#5271ff',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
zIndex: 1000,
|
||||
zIndex: 1000
|
||||
};
|
||||
|
||||
export default QuizForm;
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ const ManageRoom: React.FC = () => {
|
|||
const navigate = useNavigate();
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
const [students, setStudents] = useState<StudentType[]>([]);
|
||||
const { quizId = '', roomName = '' } = useParams<{ quizId: string, roomName: string }>();
|
||||
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 [formattedRoomName, setFormattedRoomName] = useState('');
|
||||
const [newlyConnectedUser, setNewlyConnectedUser] = useState<StudentType | null>(null);
|
||||
|
||||
// Handle the newly connected user in useEffect, because it needs state info
|
||||
|
|
@ -179,7 +179,6 @@ const ManageRoom: React.FC = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (socket) {
|
||||
console.log(`Listening for submit-answer-room in room ${formattedRoomName}`);
|
||||
socket.on('submit-answer-room', (answerData: AnswerReceptionFromBackendType) => {
|
||||
|
|
@ -253,10 +252,12 @@ const ManageRoom: React.FC = () => {
|
|||
if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return;
|
||||
|
||||
setCurrentQuestion(quizQuestions[nextQuestionIndex]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName,
|
||||
webSocketService.nextQuestion({
|
||||
roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: nextQuestionIndex,
|
||||
isLaunch: false});
|
||||
isLaunch: false
|
||||
});
|
||||
};
|
||||
|
||||
const previousQuestion = () => {
|
||||
|
|
@ -266,7 +267,12 @@ const ManageRoom: React.FC = () => {
|
|||
|
||||
if (prevQuestionIndex === undefined || prevQuestionIndex < 0) return;
|
||||
setCurrentQuestion(quizQuestions[prevQuestionIndex]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: prevQuestionIndex, isLaunch: false});
|
||||
webSocketService.nextQuestion({
|
||||
roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: prevQuestionIndex,
|
||||
isLaunch: false
|
||||
});
|
||||
};
|
||||
|
||||
const initializeQuizQuestion = () => {
|
||||
|
|
@ -294,7 +300,12 @@ const ManageRoom: React.FC = () => {
|
|||
}
|
||||
|
||||
setCurrentQuestion(quizQuestions[0]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: 0, isLaunch: true});
|
||||
webSocketService.nextQuestion({
|
||||
roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: 0,
|
||||
isLaunch: true
|
||||
});
|
||||
};
|
||||
|
||||
const launchStudentMode = () => {
|
||||
|
|
@ -331,7 +342,12 @@ const ManageRoom: React.FC = () => {
|
|||
if (quiz?.content && quizQuestions) {
|
||||
setCurrentQuestion(quizQuestions[questionIndex]);
|
||||
if (quizMode === 'teacher') {
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex, isLaunch: false});
|
||||
webSocketService.nextQuestion({
|
||||
roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex,
|
||||
isLaunch: false
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -365,7 +381,34 @@ const ManageRoom: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="room">
|
||||
<h1>Salle : {formattedRoomName}</h1>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
marginBottom: '10px'
|
||||
}}
|
||||
>
|
||||
<h1 style={{ margin: 0, display: 'flex', alignItems: 'center' }}>
|
||||
Salle : {formattedRoomName}
|
||||
<div
|
||||
className="userCount subtitle"
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 'bold',
|
||||
marginLeft: '10px',
|
||||
marginBottom: '0px'
|
||||
}}
|
||||
>
|
||||
<GroupIcon style={{ marginRight: '5px', verticalAlign: 'middle' }} />{' '}
|
||||
{students.length}/60
|
||||
</div>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="roomHeader">
|
||||
<DisconnectButton
|
||||
onReturn={handleReturn}
|
||||
|
|
@ -381,21 +424,7 @@ const ManageRoom: React.FC = () => {
|
|||
alignItems: 'center',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
{
|
||||
<div
|
||||
className="userCount subtitle smallText"
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<GroupIcon style={{ marginRight: '5px' }} />
|
||||
{students.length}/60
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
></div>
|
||||
|
||||
<div className="dumb"></div>
|
||||
</div>
|
||||
|
|
@ -429,7 +458,6 @@ const ManageRoom: React.FC = () => {
|
|||
<QuestionDisplay
|
||||
showAnswer={false}
|
||||
question={currentQuestion?.question as Question}
|
||||
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue