From 355f9f40ec86a32226bb519f00b7b15ef63fe787 Mon Sep 17 00:00:00 2001 From: Philippe <83185129+phil3838@users.noreply.github.com> Date: Wed, 26 Mar 2025 14:52:09 -0400 Subject: [PATCH] question focus from editor to preview --- client/src/components/Editor/Editor.tsx | 67 +++--- .../GiftTemplate/GIFTTemplatePreview.tsx | 45 ++-- .../pages/Teacher/EditorQuiz/EditorQuiz.tsx | 198 ++++++++++++------ 3 files changed, 202 insertions(+), 108 deletions(-) diff --git a/client/src/components/Editor/Editor.tsx b/client/src/components/Editor/Editor.tsx index 47d9c70..17d5237 100644 --- a/client/src/components/Editor/Editor.tsx +++ b/client/src/components/Editor/Editor.tsx @@ -1,14 +1,16 @@ import React from 'react'; import { TextField, Typography, IconButton, Box } from '@mui/material'; -import DeleteIcon from '@mui/icons-material/Delete'; // Import delete icon +import DeleteIcon from '@mui/icons-material/Delete'; +import VisibilityIcon from '@mui/icons-material/Visibility'; interface EditorProps { label: string; values: string[]; onValuesChange: (values: string[]) => void; + onFocusQuestion?: (index: number) => void; } -const Editor: React.FC = ({ label, values, onValuesChange }) => { +const Editor: React.FC = ({ label, values, onValuesChange, onFocusQuestion }) => { const handleChange = (index: number) => (event: React.ChangeEvent) => { const newValues = [...values]; newValues[index] = event.target.value; @@ -20,36 +22,51 @@ const Editor: React.FC = ({ label, values, onValuesChange }) => { onValuesChange(newValues); }; - return ( + const handleFocusQuestion = (index: number) => () => { + if (onFocusQuestion) { + onFocusQuestion(index); // Call the focus function if provided + } + } + + + return (
- {/* Label with increased margin */} {label} - {/* Map through each question */} {values.map((value, index) => ( - {/* Bold "Question #" title */} - - Question {index + 1} - - - {/* Delete button */} - - - + + Question {index + 1} + + + {/* Focus (Eye) Button */} + + + + {/* Delete Button */} + + + + - {/* TextField for the question */} = ({ label, values, onValuesChange }) => { minRows={4} maxRows={Infinity} variant="outlined" - style={{ overflow: 'auto'}} + style={{ overflow: 'auto' }} /> - - ))}
diff --git a/client/src/components/GiftTemplate/GIFTTemplatePreview.tsx b/client/src/components/GiftTemplate/GIFTTemplatePreview.tsx index 9da21c8..d834343 100644 --- a/client/src/components/GiftTemplate/GIFTTemplatePreview.tsx +++ b/client/src/components/GiftTemplate/GIFTTemplatePreview.tsx @@ -12,43 +12,47 @@ interface GIFTTemplatePreviewProps { const GIFTTemplatePreview: React.FC = ({ questions, - hideAnswers = false + hideAnswers = false, }) => { const [error, setError] = useState(''); const [isPreviewReady, setIsPreviewReady] = useState(false); - const [items, setItems] = useState(''); + const [questionItems, setQuestionItems] = useState([]); // Array of HTML strings for each question useEffect(() => { try { - let previewHTML = ''; - questions.forEach((giftQuestion) => { + const previewItems: string[] = []; + questions.forEach((giftQuestion, index) => { try { const question = parse(giftQuestion); - previewHTML += Template(question[0], { + const html = Template(question[0], { preview: true, - theme: 'light' + theme: 'light', }); + previewItems.push(html); } catch (error) { let errorMsg: string; if (error instanceof UnsupportedQuestionTypeError) { errorMsg = ErrorTemplate(giftQuestion, `Erreur: ${error.message}`); } else if (error instanceof Error) { - errorMsg = ErrorTemplate(giftQuestion, `Erreur GIFT: ${error.message}`); + errorMsg = ErrorTemplate(giftQuestion, `Erreur GIFT: ${error.message}`); } else { - errorMsg = ErrorTemplate(giftQuestion, 'Erreur inconnue'); + errorMsg = ErrorTemplate(giftQuestion, 'Erreur inconnue'); } - previewHTML += `
${errorMsg}
`; + previewItems.push(`
${errorMsg}
`); } }); if (hideAnswers) { - const svgRegex = /]*>([\s\S]*?)<\/svg>/gi; - previewHTML = previewHTML.replace(svgRegex, ''); - const placeholderRegex = /(placeholder=")[^"]*(")/gi; - previewHTML = previewHTML.replace(placeholderRegex, '$1$2'); + previewItems.forEach((item, index) => { + const svgRegex = /]*>([\s\S]*?)<\/svg>/gi; + const placeholderRegex = /(placeholder=")[^"]*(")/gi; + previewItems[index] = item + .replace(svgRegex, '') + .replace(placeholderRegex, '$1$2'); + }); } - setItems(previewHTML); + setQuestionItems(previewItems); setIsPreviewReady(true); } catch (error: unknown) { if (error instanceof Error) { @@ -57,7 +61,7 @@ const GIFTTemplatePreview: React.FC = ({ setError('Une erreur est survenue durant le chargement de la prévisualisation.'); } } - }, [questions]); + }, [questions, hideAnswers]); const PreviewComponent = () => ( @@ -65,8 +69,13 @@ const GIFTTemplatePreview: React.FC = ({
{error}
) : isPreviewReady ? (
- -
+ {questionItems.map((item, index) => ( +
+ ))}
) : (
Chargement de la prévisualisation...
@@ -77,4 +86,4 @@ const GIFTTemplatePreview: React.FC = ({ return ; }; -export default GIFTTemplatePreview; +export default GIFTTemplatePreview; \ No newline at end of file diff --git a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx index 8387be3..f92a8a0 100644 --- a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx +++ b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx @@ -11,7 +11,7 @@ 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, Snackbar } from '@mui/material'; import ReturnButton from 'src/components/ReturnButton/ReturnButton'; import ApiService from '../../../services/ApiService'; @@ -42,6 +42,12 @@ 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 scrollToTop = () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }; @@ -149,7 +155,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) @@ -161,6 +168,10 @@ const QuizForm: React.FC = () => { return
Chargement...
; } + const handleSnackbarClose = () => { + setSnackbarOpen(false); + }; + const handleSaveImage = async () => { try { const inputElement = document.getElementById('file-input') as HTMLInputElement; @@ -199,7 +210,15 @@ 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' }); + } + } + }; return (
@@ -252,75 +271,119 @@ const QuizForm: React.FC = () => { + onValuesChange={handleUpdatePreview} + onFocusQuestion={handleFocusQuestion} /> - -
-
- - setDialogOpen(false)} > - Erreur - - Veuillez d'abord choisir une image à téléverser. - - - - - + onClick={() => setIsUploadCollapsed(!isUploadCollapsed)} + style={{ padding: '4px 8px', fontSize: '12px', marginBottom: '4px', width: '40%' }} + > + {isUploadCollapsed ? 'Afficher Téléverser image' : 'Masquer Téléverser image'} + + {!isUploadCollapsed && ( +
+ + setDialogOpen(false)}> + Erreur + + Veuillez d'abord choisir une image à téléverser. + + + + + +
+ )}
-

Mes images :

-
+ {/* Collapsible Images Section */} +
+ + {!isImagesCollapsed && (
-
(Voir section
- -

9. Images

-
-
ci-dessous
-
)
-
- - Cliquez sur un lien pour le copier -
-
    - {imageLinks.map((link, index) => { - const imgTag = `![alt_text](${escapeForGIFT(link)} "texte de l'infobulle")`; - return ( -
  • - handleCopyToClipboard(imgTag)}> - {imgTag} - -
  • - ); - })} -
+

Mes images :

+
+
+
(Voir section
+ + + +

9. Images

+
+
+
+
ci-dessous
+
)
+
+ - Cliquez sur un lien pour le copier +
+
    + {imageLinks.map((link, index) => { + const imgTag = `![alt_text](${escapeForGIFT(link)} "texte de l'infobulle")`; + return ( +
  • + handleCopyToClipboard(imgTag)}> + {imgTag} + +
  • + ); + })} +
+
+
+ )} +
+ + {/* Collapsible CheatSheet Section */} +
+ + {!isCheatSheetCollapsed && }
- - -
-
+

Prévisualisation

@@ -328,7 +391,6 @@ const QuizForm: React.FC = () => {
-
{showScrollButton && ( @@ -342,9 +404,17 @@ const QuizForm: React.FC = () => { ↑ )} + +
); -}; +}; const scrollToTopButtonStyle: CSSProperties = { position: 'fixed',