+
}
onClick={handleOnReturnButtonClick}
color="primary"
sx={{ marginLeft: '-0.5rem', fontSize: 16 }}
+ disabled={isSaving} // Disable button while saving
>
- Retour
+ {isSaving ? 'Enregistrement...' : 'Retour'}
- setShowDialog(false)}
- buttonOrderType="warning"
- />
);
};
diff --git a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx
index 89f822a..f6060ca 100644
--- a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx
+++ b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx
@@ -11,12 +11,13 @@ 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, MenuItem, Select, Snackbar } from '@mui/material';
import ReturnButton from 'src/components/ReturnButton/ReturnButton';
import ApiService from '../../../services/ApiService';
import { escapeForGIFT } from '../../../utils/giftUtils';
import { Upload } from '@mui/icons-material';
+import SaveIcon from '@mui/icons-material/Save';
interface EditQuizParams {
id: string;
@@ -29,7 +30,7 @@ const QuizForm: React.FC = () => {
const [filteredValue, setFilteredValue] = useState
([]);
const { id } = useParams();
- const [value, setValue] = useState('');
+ const [values, setValues] = useState([]);
const [isNewQuiz, setNewQuiz] = useState(false);
const [quiz, setQuiz] = useState(null);
const navigate = useNavigate();
@@ -42,6 +43,27 @@ const QuizForm: React.FC = () => {
const [dialogOpen, setDialogOpen] = useState(false);
const [showScrollButton, setShowScrollButton] = useState(false);
+ const [isImagesCollapsed, setIsImagesCollapsed] = useState(true);
+ const [isCheatSheetCollapsed, setIsCheatSheetCollapsed] = useState(true);
+ const [isUploadCollapsed, setIsUploadCollapsed] = useState(true);
+ const [snackbarOpen, setSnackbarOpen] = useState(false);
+ const [snackbarMessage, setSnackbarMessage] = useState('');
+ const [copySuccess, setCopySuccess] = useState(null);
+
+ const QuestionVraiFaux = "::Exemple de question vrai/faux:: \n 2+2 \\= 4 ? {T} //Utilisez les valeurs {T}, {F}, {TRUE} et {FALSE}.";
+ const QuestionChoixMul = "::Ville capitale du Canada:: \nQuelle ville est la capitale du Canada? {\n~ Toronto\n~ Montréal\n= Ottawa #Rétroaction spécifique.\n} // Commentaire non visible (au besoin)";
+ const QuestionChoixMulMany = "::Villes canadiennes:: \n Quelles villes trouve-t-on au Canada? { \n~ %33.3% Montréal \n ~ %33.3% Ottawa \n ~ %33.3% Vancouver \n ~ %-100% New York \n ~ %-100% Paris \n#### Rétroaction globale de la question. \n} // Utilisez tilde (signe de vague) pour toutes les réponses. // On doit indiquer le pourcentage de chaque réponse.";
+ const QuestionCourte = "::Clé et porte:: \n Avec quoi ouvre-t-on une porte? { \n= clé \n= clef \n} // Permet de fournir plusieurs bonnes réponses. // Note: La casse n'est pas prise en compte.";
+ const QuestionNum = "::Question numérique avec marge:: \nQuel est un nombre de 1 à 5 ? {\n#3:2\n}\n \n// Plage mathématique spécifiée avec des points de fin d'intervalle. \n ::Question numérique avec plage:: \n Quel est un nombre de 1 à 5 ? {\n#1..5\n} \n\n// Réponses numériques multiples avec crédit partiel et commentaires.\n::Question numérique avec plusieurs réponses::\nQuand est né Ulysses S. Grant ? {\n# =1822:0 # Correct ! Crédit complet. \n=%50%1822:2 # Il est né en 1822. Demi-crédit pour être proche.\n}";
+
+ const templates = [
+ { label: 'Vrai/Faux', value: QuestionVraiFaux },
+ { label: 'Choix multiples R1', value: QuestionChoixMul },
+ { label: 'Choix multiples R2+', value: QuestionChoixMulMany },
+ { label: 'Réponse courte', value: QuestionCourte },
+ { label: 'Numérique', value: QuestionNum },
+ ];
+
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
@@ -101,7 +123,7 @@ const QuizForm: React.FC = () => {
setQuizTitle(title);
setSelectedFolder(folderId);
setFilteredValue(content);
- setValue(quiz.content.join('\n\n'));
+ setValues(content);
} catch (error) {
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`)
@@ -113,21 +135,17 @@ const QuizForm: React.FC = () => {
fetchData();
}, [id]);
- function handleUpdatePreview(value: string) {
- if (value !== '') {
- setValue(value);
- }
+ const handleAddQuestion = () => {
+ console.log("Adding question");
+ console.log("Current values:", values); // Log current state
+ setValues([...values, '']);
+ console.log("Updated values:", [...values, '']); // Log new state
+ };
- // split value when there is at least one blank line
- const linesArray = value.split(/\n{2,}/);
-
- // if the first item in linesArray is blank, remove it
- if (linesArray[0] === '') linesArray.shift();
-
- if (linesArray[linesArray.length - 1] === '') linesArray.pop();
-
- setFilteredValue(linesArray);
- }
+ const handleUpdatePreview = (newValues: string[]) => {
+ setValues(newValues);
+ setFilteredValue(newValues.filter(value => value.trim() !== ''));
+ };
const handleQuizTitleChange = (event: React.ChangeEvent) => {
setQuizTitle(event.target.value);
@@ -135,7 +153,6 @@ const QuizForm: React.FC = () => {
const handleQuizSave = async () => {
try {
- // check if everything is there
if (quizTitle == '') {
alert("Veuillez choisir un titre");
return;
@@ -154,7 +171,8 @@ const QuizForm: React.FC = () => {
}
}
- navigate('/teacher/dashboard');
+ setSnackbarMessage('Quiz enregistré avec succès!');
+ setSnackbarOpen(true);
} catch (error) {
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`)
console.log(error)
@@ -166,6 +184,10 @@ const QuizForm: React.FC = () => {
return Chargement...
;
}
+ const handleSnackbarClose = () => {
+ setSnackbarOpen(false);
+ };
+
const handleSaveImage = async () => {
try {
const inputElement = document.getElementById('file-input') as HTMLInputElement;
@@ -204,16 +226,49 @@ const QuizForm: React.FC = () => {
navigator.clipboard.writeText(link);
}
+ const handleFocusQuestion = (index: number) => {
+ const previewElement = document.querySelector('.preview-column');
+ if (previewElement) {
+ const questionElements = previewElement.querySelectorAll('.question-item');
+ if (questionElements[index]) {
+ questionElements[index].scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+ }
+ };
+
+ const copyToClipboard = (text: string, label: string) => {
+ navigator.clipboard.writeText(text)
+ .then(() => {
+ setCopySuccess(`Copié dans le presse-papier: ${label}`);
+ })
+ .catch((error) => console.error('Clipboard error:', error));
+ };
+
+ const handleSelectChange = (value: string, label: string) => {
+ copyToClipboard(value, label);
+ };
+
return (
-
+
-
Éditeur de quiz
+
+
+
Éditeur de Quiz
@@ -243,84 +298,163 @@ const QuizForm: React.FC = () => {
))}
-
+
+
+
+
+
+
setCopySuccess(null)}
+ message={copySuccess}
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
+ key={copySuccess ? 'open' : 'close'}
+ />
+
-
-
-
-
-
+
Prévisualisation
@@ -328,7 +462,6 @@ const QuizForm: React.FC = () => {
-
{showScrollButton && (
@@ -342,9 +475,17 @@ const QuizForm: React.FC = () => {
↑
)}
+
+
);
-};
+};
const scrollToTopButtonStyle: CSSProperties = {
position: 'fixed',
diff --git a/client/src/pages/Teacher/EditorQuiz/editorQuiz.css b/client/src/pages/Teacher/EditorQuiz/editorQuiz.css
index 79157ff..299e4ac 100644
--- a/client/src/pages/Teacher/EditorQuiz/editorQuiz.css
+++ b/client/src/pages/Teacher/EditorQuiz/editorQuiz.css
@@ -80,6 +80,24 @@ input[type="file"] {
.quizEditor .editSection .preview {
flex: 50%;
padding: 5px;
-
overflow: auto;
+ height: 100%;
+ position: relative;
}
+
+.quizEditor {
+ margin: 0 -2rem; /* Counteract the padding */
+ width: calc(100% + 4rem); /* Expand to fill padded area */
+}
+
+.sticky-buttons {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 8px;
+ position: sticky;
+ top: 0;
+ background: #fff;
+ z-index: 1000;
+ padding: 8px 0;
+}
\ No newline at end of file