From f93fd96bb5cebd72319c8acb47aa58fc99722c9a Mon Sep 17 00:00:00 2001 From: swve Date: Thu, 14 Sep 2023 18:55:24 +0200 Subject: [PATCH 1/4] feat: init editable quiz --- .../Editor/Extensions/Quiz/QuizBlock.ts | 4 +- .../Extensions/Quiz/QuizBlockComponent.tsx | 317 ++++++++++-------- .../Objects/Editor/Toolbar/ToolbarButtons.tsx | 12 +- 3 files changed, 187 insertions(+), 146 deletions(-) diff --git a/front/components/Objects/Editor/Extensions/Quiz/QuizBlock.ts b/front/components/Objects/Editor/Extensions/Quiz/QuizBlock.ts index 36d7706a..b6af7d51 100644 --- a/front/components/Objects/Editor/Extensions/Quiz/QuizBlock.ts +++ b/front/components/Objects/Editor/Extensions/Quiz/QuizBlock.ts @@ -6,7 +6,6 @@ import QuizBlockComponent from "./QuizBlockComponent"; export default Node.create({ name: "blockQuiz", group: "block", - atom: true, addAttributes() { @@ -14,6 +13,9 @@ export default Node.create({ quizId: { value: null, }, + questions: { + default: [], + }, }; }, diff --git a/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx b/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx index 9b1f3ec2..c2532242 100644 --- a/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx +++ b/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx @@ -1,164 +1,203 @@ import { NodeViewWrapper } from "@tiptap/react"; import { v4 as uuidv4 } from "uuid"; import React from "react"; -import styled from "styled-components"; -import { submitQuizBlock } from "@services/blocks/Quiz/quiz"; +import { BadgeHelp, Check, Minus, MoreVertical, Plus, X } from "lucide-react"; -function ImageBlockComponent(props: any) { - const [questions, setQuestions] = React.useState([]) as any; - const [answers, setAnswers] = React.useState([]) as any; +interface Answer { + answer_id: string; + answer: string; + correct: boolean; +} +interface Question { + question_id: string; + question: string; + type: "multiple_choice" | 'custom_answer' + answers: Answer[]; +} - function addSampleQuestion() { - setQuestions([ - ...questions, - { - question_id: "question_" + uuidv4(), - question_value: "", - options: [ - { - option_id: "option_" + uuidv4(), - option_data: "", - option_type: "text", - }, - ], - }, - ]); +function QuizBlockComponent(props: any) { + const [questions, setQuestions] = React.useState(props.node.attrs.questions) as [Question[], any]; + const isEditable = props.extension.options.editable; + + const getAlphabetFromIndex = (index: number) => { + const alphabet = Array.from({ length: 26 }, (_, i) => String.fromCharCode('A'.charCodeAt(0) + i)); + return alphabet[index]; } - const deleteQuestion = (index: number) => { - let modifiedQuestions = [...questions]; - modifiedQuestions.splice(index, 1); - setQuestions(modifiedQuestions); - - - // remove the answers from the answers array - let modifiedAnswers = [...answers]; - modifiedAnswers = modifiedAnswers.filter((answer: any) => answer.question_id !== questions[index].question_id); - setAnswers(modifiedAnswers); - }; - - const onQuestionChange = (e: any, index: number) => { - let modifiedQuestions = [...questions]; - modifiedQuestions[index].question_value = e.target.value; - setQuestions(modifiedQuestions); - }; - - const addOption = (question_id: string) => { - // find the question index from the question_id and add the option to that question index - let modifiedQuestions = [...questions]; - let questionIndex = modifiedQuestions.findIndex((question: any) => question.question_id === question_id); - modifiedQuestions[questionIndex].options.push({ - option_id: "option_" + uuidv4(), - option_data: "", - option_type: "text", + const saveQuestions = (questions: any) => { + props.updateAttributes({ + questions: questions, }); - setQuestions(modifiedQuestions); + setQuestions(questions); + }; - - const deleteOption = (question_id: string, option_id: string) => { - // find the option index from the option_id and delete the option from that option index - let modifiedQuestions = [...questions]; - let questionIndex = modifiedQuestions.findIndex((question: any) => question.question_id === question_id); - let optionIndex = modifiedQuestions[questionIndex].options.findIndex((option: any) => option.option_id === option_id); - modifiedQuestions[questionIndex].options.splice(optionIndex, 1); - setQuestions(modifiedQuestions); - - // remove the answer from the answers array - let answerIndex = answers.findIndex((answer: any) => answer.option_id === option_id); - if (answerIndex !== -1) { - let modifiedAnswers = [...answers]; - modifiedAnswers.splice(answerIndex, 1); - setAnswers(modifiedAnswers); - } - }; - - const markOptionAsCorrect = (question_id: string, option_id: string) => { - // find the option index from the option_id and mark the option as correct - let answer = { - question_id: question_id, - option_id: option_id, - }; - setAnswers([...answers, answer]); - - }; - - const saveQuiz = async () => { - // save the questions and answers to the backend - - - - try { - let res = await submitQuizBlock(props.extension.options.activity.activity_id, {questions : questions , answers : answers}) - - props.updateAttributes({ - quizId: { - value : res.block_id + const addSampleQuestion = () => { + const newQuestion = { + question_id: uuidv4(), + question: "", + type: "multiple_choice", + answers: [ + { + answer_id: uuidv4(), + answer: "", + correct: false }, - }); - + ] } - catch (error) { - + setQuestions([...questions, newQuestion]); + } + + const addAnswer = (question_id: string) => { + const newAnswer = { + answer_id: uuidv4(), + answer: "", + correct: false } - - }; + // check if there is already more thqn 5 answers + const question: any = questions.find((question: Question) => question.question_id === question_id); + if (question.answers.length >= 5) { + return; + } - const onOptionChange = (e: any, questionIndex: number, optionIndex: number) => { - let modifiedQuestions = [...questions]; - modifiedQuestions[questionIndex].options[optionIndex].option_data = e.target.value; - setQuestions(modifiedQuestions); - }; - React.useEffect(() => { - // fetch the questions and options from the backend - - - - }, [questions, answers]); + + const newQuestions = questions.map((question: Question) => { + if (question.question_id === question_id) { + question.answers.push(newAnswer); + } + return question; + }); + + saveQuestions(newQuestions); + } + + const changeAnswerValue = (question_id: string, answer_id: string, value: string) => { + const newQuestions = questions.map((question: Question) => { + if (question.question_id === question_id) { + question.answers.map((answer: Answer) => { + if (answer.answer_id === answer_id) { + answer.answer = value; + } + return answer; + }); + } + return question; + }); + saveQuestions(newQuestions); + } + + const changeQuestionValue = (question_id: string, value: string) => { + const newQuestions = questions.map((question: Question) => { + if (question.question_id === question_id) { + question.question = value; + } + return question; + }); + saveQuestions(newQuestions); + } + + const deleteQuestion = (question_id: string) => { + const newQuestions = questions.filter((question: Question) => question.question_id !== question_id); + saveQuestions(newQuestions); + } + + const deleteAnswer = (question_id: string, answer_id: string) => { + const newQuestions = questions.map((question: Question) => { + if (question.question_id === question_id) { + question.answers = question.answers.filter((answer: Answer) => answer.answer_id !== answer_id); + } + return question; + }); + saveQuestions(newQuestions); + } + + const markAnswerCorrect = (question_id: string, answer_id: string) => { + const newQuestions = questions.map((question: Question) => { + if (question.question_id === question_id) { + question.answers.map((answer: Answer) => { + if (answer.answer_id === answer_id) { + answer.correct = true; + } else { + answer.correct = false; + } + return answer; + }); + } + return question; + }); + saveQuestions(newQuestions); + } + return ( - - Questions -
- {questions.map((question: any, qIndex: number) => ( - <> -
- Question : onQuestionChange(e, qIndex)} /> - -
- Answers :
- {question.options.map((option: any, oIndex: number) => ( - <> -
- onOptionChange(e, qIndex, oIndex)} /> +
+
+
+ +

Quiz

+
+
+ +
+
- - - // check if checkbox is checked or not - // if checked then add the answer to the answers array - // if unchecked then remove the answer from the answers array - e.target.checked ? markOptionAsCorrect(question.question_id, option.option_id) : null - } - /> + {questions.map((question: Question) => ( +
+
+
+
+ changeQuestionValue(question.question_id, e.target.value)} className="text-slate-800 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-md font-bold w-full">
- - ))} - - +
+ deleteQuestion(question.question_id)} + className="mx-auto text-red-200" size={12} /> +
+
+
+ {question.answers.map((answer: Answer) => ( +
+
+

{getAlphabetFromIndex(question.answers.indexOf(answer))}

+
+ changeAnswerValue(question.question_id, answer.answer_id, e.target.value)} placeholder="Answer" className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-sm font-bold"> + +
+
markAnswerCorrect(question.question_id, answer.answer_id)} + className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-green-200 hover:bg-green-300 transition-all ease-linear text-sm cursor-pointer "> + +
+ +
deleteAnswer(question.question_id, answer.answer_id)} + className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-red-600 hover:bg-red-700 text-sm transition-all ease-linear cursor-pointer"> + +
+
+ +
+ ))} +
addAnswer(question.question_id)} className="outline outline-3 shadow w-[30px] flex-none flex items-center h-[30px] outline-white hover:bg-opacity-100 hover:shadow-md rounded-lg bg-white text-sm hover:scale-105 active:scale-110 duration-150 cursor-pointer ease-linear"> + +
+
+
+
))} - + +
); } -const QuizBlockWrapper = styled.div` - background-color: #0000001d; - border-radius: 5px; - padding: 20px; - height: 100%; -`; -export default ImageBlockComponent; + +export default QuizBlockComponent; diff --git a/front/components/Objects/Editor/Toolbar/ToolbarButtons.tsx b/front/components/Objects/Editor/Toolbar/ToolbarButtons.tsx index d5ebc0b9..22d5804f 100644 --- a/front/components/Objects/Editor/Toolbar/ToolbarButtons.tsx +++ b/front/components/Objects/Editor/Toolbar/ToolbarButtons.tsx @@ -1,6 +1,6 @@ import styled from "styled-components"; import { FontBoldIcon, FontItalicIcon, StrikethroughIcon, ArrowLeftIcon, ArrowRightIcon, OpacityIcon, DividerVerticalIcon, ListBulletIcon } from "@radix-ui/react-icons"; -import { AlertCircle, AlertTriangle, FileText, GraduationCap, ImagePlus, Info, Sigma, Video, Youtube } from "lucide-react"; +import { AlertCircle, AlertTriangle, FileText, GraduationCap, HelpCircle, ImagePlus, Info, Sigma, Video, Youtube } from "lucide-react"; import ToolTip from "@components/StyledElements/Tooltip/Tooltip"; export const ToolbarButtons = ({ editor, props }: any) => { @@ -59,7 +59,7 @@ export const ToolbarButtons = ({ editor, props }: any) => { {/* TODO: fix this : toggling only works one-way */} - + editor.chain().focus().toggleNode("calloutInfo").run()}> @@ -113,7 +113,7 @@ export const ToolbarButtons = ({ editor, props }: any) => { .chain() .focus() .insertContent({ - type: "blockMathEquation", + type: "blockMathEquation", }) .run() } @@ -136,7 +136,7 @@ export const ToolbarButtons = ({ editor, props }: any) => { - {/* + editor @@ -148,9 +148,9 @@ export const ToolbarButtons = ({ editor, props }: any) => { .run() } > - + - */} + ); }; From 8d29c5cddd2295c23fcac347c43b352d4dc31354 Mon Sep 17 00:00:00 2001 From: swve Date: Thu, 14 Sep 2023 21:49:17 +0200 Subject: [PATCH 2/4] feat: implement quiz logic and improve design --- .../activity/[activityid]/activity.tsx | 6 +- .../Activities/DocumentPdf/DocumentPdf.tsx | 0 .../Activities/DynamicCanva/DynamicCanva.tsx | 5 + .../Activities/Video/Video.tsx | 0 .../Extensions/Quiz/QuizBlockComponent.tsx | 189 ++++++++++++++---- front/package.json | 2 + 6 files changed, 164 insertions(+), 38 deletions(-) rename front/components/{Pages => Objects}/Activities/DocumentPdf/DocumentPdf.tsx (100%) rename front/components/{Pages => Objects}/Activities/DynamicCanva/DynamicCanva.tsx (93%) rename front/components/{Pages => Objects}/Activities/Video/Video.tsx (100%) diff --git a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/activity.tsx b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/activity.tsx index 85fbb22e..596dcf6f 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/activity.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/activity.tsx @@ -1,11 +1,11 @@ "use client"; import Link from "next/link"; import { getUriWithOrg } from "@services/config/config"; -import Canva from "@components/Pages/Activities/DynamicCanva/DynamicCanva"; -import VideoActivity from "@components/Pages/Activities/Video/Video"; +import Canva from "@components/Objects/Activities/DynamicCanva/DynamicCanva"; +import VideoActivity from "@components/Objects/Activities/Video/Video"; import { Check } from "lucide-react"; import { markActivityAsComplete } from "@services/courses/activity"; -import DocumentPdfActivity from "@components/Pages/Activities/DocumentPdf/DocumentPdf"; +import DocumentPdfActivity from "@components/Objects/Activities/DocumentPdf/DocumentPdf"; import ActivityIndicators from "@components/Pages/Courses/ActivityIndicators"; import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper"; import { useRouter } from "next/navigation"; diff --git a/front/components/Pages/Activities/DocumentPdf/DocumentPdf.tsx b/front/components/Objects/Activities/DocumentPdf/DocumentPdf.tsx similarity index 100% rename from front/components/Pages/Activities/DocumentPdf/DocumentPdf.tsx rename to front/components/Objects/Activities/DocumentPdf/DocumentPdf.tsx diff --git a/front/components/Pages/Activities/DynamicCanva/DynamicCanva.tsx b/front/components/Objects/Activities/DynamicCanva/DynamicCanva.tsx similarity index 93% rename from front/components/Pages/Activities/DynamicCanva/DynamicCanva.tsx rename to front/components/Objects/Activities/DynamicCanva/DynamicCanva.tsx index 69ebf363..4c945527 100644 --- a/front/components/Pages/Activities/DynamicCanva/DynamicCanva.tsx +++ b/front/components/Objects/Activities/DynamicCanva/DynamicCanva.tsx @@ -10,6 +10,7 @@ import VideoBlock from "@components/Objects/Editor/Extensions/Video/VideoBlock"; import MathEquationBlock from "@components/Objects/Editor/Extensions/MathEquation/MathEquationBlock"; import PDFBlock from "@components/Objects/Editor/Extensions/PDF/PDFBlock"; import { OrderedList } from "@tiptap/extension-ordered-list"; +import QuizBlock from "@components/Objects/Editor/Extensions/Quiz/QuizBlock"; interface Editor { content: string; @@ -46,6 +47,10 @@ function Canva(props: Editor) { editable: true, activity: props.activity, }), + QuizBlock.configure({ + editable: isEditable, + activity: props.activity, + }), Youtube.configure({ controls: true, modestBranding: true, diff --git a/front/components/Pages/Activities/Video/Video.tsx b/front/components/Objects/Activities/Video/Video.tsx similarity index 100% rename from front/components/Pages/Activities/Video/Video.tsx rename to front/components/Objects/Activities/Video/Video.tsx diff --git a/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx b/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx index c2532242..0a654655 100644 --- a/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx +++ b/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx @@ -1,7 +1,9 @@ import { NodeViewWrapper } from "@tiptap/react"; import { v4 as uuidv4 } from "uuid"; +import { twJoin, twMerge } from 'tailwind-merge' import React from "react"; -import { BadgeHelp, Check, Minus, MoreVertical, Plus, X } from "lucide-react"; +import { BadgeHelp, Check, Info, Minus, MoreVertical, Plus, RefreshCcw, X } from "lucide-react"; +import ReactConfetti from "react-confetti"; interface Answer { answer_id: string; @@ -17,11 +19,80 @@ interface Question { function QuizBlockComponent(props: any) { const [questions, setQuestions] = React.useState(props.node.attrs.questions) as [Question[], any]; + const [userAnswers, setUserAnswers] = React.useState([]) as [any[], any]; + const [submitted, setSubmitted] = React.useState(false) as [boolean, any]; + const [submissionMessage, setSubmissionMessage] = React.useState("") as [string, any]; const isEditable = props.extension.options.editable; - const getAlphabetFromIndex = (index: number) => { + const handleAnswerClick = (question_id: string, answer_id: string) => { + // if the quiz is submitted, do nothing + if (submitted) { + return; + } + + const userAnswer = { + question_id: question_id, + answer_id: answer_id + } + const newAnswers = [...userAnswers, userAnswer]; + + // only accept one answer per question + const filteredAnswers = newAnswers.filter((answer: any) => answer.question_id !== question_id); + + setUserAnswers([...filteredAnswers, userAnswer]); + + } + + const refreshUserSubmission = () => { + setUserAnswers([]); + setSubmitted(false); + } + + const handleUserSubmission = () => { + + if (userAnswers.length === 0) { + setSubmissionMessage("Please answer at least one question!"); + return; + } + + setSubmitted(true); + + // check if all submitted answers are correct + const correctAnswers = questions.map((question: Question) => { + const correctAnswer: any = question.answers.find((answer: Answer) => answer.correct); + const userAnswer = userAnswers.find((userAnswer: any) => userAnswer.question_id === question.question_id); + if (correctAnswer.answer_id === userAnswer.answer_id) { + return true; + } else { + return false; + } + }); + + // check if all answers are correct + const allCorrect = correctAnswers.every((answer: boolean) => answer === true); + + if (allCorrect) { + setSubmissionMessage("All answers are correct!"); + console.log("All answers are correct!"); + } + else { + setSubmissionMessage("Some answers are incorrect!"); + console.log("Some answers are incorrect!"); + } + + + + } + + const getAnswerID = (answerIndex: number, questionId : string) => { const alphabet = Array.from({ length: 26 }, (_, i) => String.fromCharCode('A'.charCodeAt(0) + i)); - return alphabet[index]; + let alphabetID = alphabet[answerIndex]; + + // Get question index + const questionIndex = questions.findIndex((question: Question) => question.question_id === questionId); + let questionID = questionIndex + 1; + + return `${alphabetID}`; } const saveQuestions = (questions: any) => { @@ -121,6 +192,7 @@ function QuizBlockComponent(props: any) { } else { answer.correct = false; } + return answer; }); } @@ -132,18 +204,38 @@ function QuizBlockComponent(props: any) { return ( +
-
-
+
+ {(submitted && submissionMessage == "All answers are correct!") && + + } +

Quiz

-
- +
+
+ {isEditable ? +
+ +
+ : +
+
refreshUserSubmission()} className="cursor-pointer px-2"> + +
+ +
+ }
{questions.map((question: Question) => ( @@ -151,44 +243,71 @@ function QuizBlockComponent(props: any) {
- changeQuestionValue(question.question_id, e.target.value)} className="text-slate-800 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-md font-bold w-full"> + {isEditable ? + changeQuestionValue(question.question_id, e.target.value)} className="text-slate-800 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-md font-bold w-full"> + : +

{question.question}

+ }
-
- deleteQuestion(question.question_id)} - className="mx-auto text-red-200" size={12} /> -
+ className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-slate-200 hover:bg-slate-300 text-sm transition-all ease-linear cursor-pointer"> + +
+ }
{question.answers.map((answer: Answer) => ( -
-
-

{getAlphabetFromIndex(question.answers.indexOf(answer))}

+
(userAnswer.question_id === question.question_id && userAnswer.answer_id === answer.answer_id) && !isEditable) ? 'outline-slate-300' : '', + (submitted && answer.correct) ? 'outline-lime-300 text-lime' : '', + (submitted && !answer.correct) && userAnswers.find((userAnswer: any) => userAnswer.question_id === question.question_id && userAnswer.answer_id === answer.answer_id) ? 'outline-red-400' : '', + ) + } + onClick={() => handleAnswerClick(question.question_id, answer.answer_id)} + > +
userAnswer.question_id === question.question_id && userAnswer.answer_id === answer.answer_id) ? 'bg-red-400 text-red-800 outline-none' : '', + )}> +

{getAnswerID(question.answers.indexOf(answer),question.question_id)}

- changeAnswerValue(question.question_id, answer.answer_id, e.target.value)} placeholder="Answer" className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-sm font-bold"> - -
-
markAnswerCorrect(question.question_id, answer.answer_id)} - className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-green-200 hover:bg-green-300 transition-all ease-linear text-sm cursor-pointer "> - + {isEditable ? + changeAnswerValue(question.question_id, answer.answer_id, e.target.value)} placeholder="Answer" className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-sm font-bold"> + : +

{answer.answer}

+ } + {isEditable && +
+
markAnswerCorrect(question.question_id, answer.answer_id)} + className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-lime-200 hover:bg-lime-300 transition-all ease-linear text-sm cursor-pointer "> + +
+
deleteAnswer(question.question_id, answer.answer_id)} + className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-slate-200 hover:bg-slate-300 text-sm transition-all ease-linear cursor-pointer"> + +
- -
deleteAnswer(question.question_id, answer.answer_id)} - className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-red-600 hover:bg-red-700 text-sm transition-all ease-linear cursor-pointer"> - -
-
+ }
))} -
addAnswer(question.question_id)} className="outline outline-3 shadow w-[30px] flex-none flex items-center h-[30px] outline-white hover:bg-opacity-100 hover:shadow-md rounded-lg bg-white text-sm hover:scale-105 active:scale-110 duration-150 cursor-pointer ease-linear"> - -
+ {isEditable && +
addAnswer(question.question_id)} className="outline outline-3 w-[30px] flex-none flex items-center h-[30px] outline-white hover:bg-opacity-100 hover:shadow-md rounded-lg bg-white text-sm hover:scale-105 active:scale-110 duration-150 cursor-pointer ease-linear"> + +
+ }
diff --git a/front/package.json b/front/package.json index 592c2f7c..604a5242 100644 --- a/front/package.json +++ b/front/package.json @@ -31,6 +31,7 @@ "re-resizable": "^6.9.9", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", + "react-confetti": "^6.1.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", "react-katex": "^3.0.1", @@ -38,6 +39,7 @@ "react-youtube": "^10.1.0", "styled-components": "^6.0.0-beta.9", "swr": "^2.0.1", + "tailwind-merge": "^1.14.0", "uuid": "^9.0.0", "y-indexeddb": "^9.0.9", "y-webrtc": "^10.2.3", From cb772349b9e295fa3b168ba2c8f45e7e8857940e Mon Sep 17 00:00:00 2001 From: swve Date: Thu, 14 Sep 2023 21:58:48 +0200 Subject: [PATCH 3/4] feat: minor quiz edition css changes --- .../Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx b/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx index 0a654655..c3e350f2 100644 --- a/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx +++ b/front/components/Objects/Editor/Extensions/Quiz/QuizBlockComponent.tsx @@ -264,7 +264,7 @@ function QuizBlockComponent(props: any) { key={answer.answer_id} className={twMerge( 'outline outline-3 pr-2 shadow w-full flex items-center space-x-2 h-[30px] bg-opacity-50 hover:bg-opacity-100 hover:shadow-md rounded-s rounded-lg bg-white text-sm hover:scale-105 active:scale-110 duration-150 cursor-pointer ease-linear', - answer.correct && isEditable ? 'outline-lime-200' : 'outline-white', + answer.correct && isEditable ? 'outline-lime-300' : 'outline-white', userAnswers.find((userAnswer: any) => (userAnswer.question_id === question.question_id && userAnswer.answer_id === answer.answer_id) && !isEditable) ? 'outline-slate-300' : '', (submitted && answer.correct) ? 'outline-lime-300 text-lime' : '', (submitted && !answer.correct) && userAnswers.find((userAnswer: any) => userAnswer.question_id === question.question_id && userAnswer.answer_id === answer.answer_id) ? 'outline-red-400' : '', @@ -274,7 +274,8 @@ function QuizBlockComponent(props: any) { >
userAnswer.question_id === question.question_id && userAnswer.answer_id === answer.answer_id) ? 'bg-red-400 text-red-800 outline-none' : '', )}>

{getAnswerID(question.answers.indexOf(answer),question.question_id)}

@@ -288,7 +289,7 @@ function QuizBlockComponent(props: any) {
markAnswerCorrect(question.question_id, answer.answer_id)} - className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-lime-200 hover:bg-lime-300 transition-all ease-linear text-sm cursor-pointer "> + className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-lime-300 hover:bg-lime-400 transition-all ease-linear text-sm cursor-pointer ">
From e56a49c5636d3ec552bb6d4c13f2d117704941e2 Mon Sep 17 00:00:00 2001 From: swve Date: Thu, 14 Sep 2023 22:00:04 +0200 Subject: [PATCH 4/4] fix: package lock sync --- front/package-lock.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/front/package-lock.json b/front/package-lock.json index 53741010..3e29e5b6 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -30,6 +30,7 @@ "re-resizable": "^6.9.9", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", + "react-confetti": "^6.1.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", "react-katex": "^3.0.1", @@ -37,6 +38,7 @@ "react-youtube": "^10.1.0", "styled-components": "^6.0.0-beta.9", "swr": "^2.0.1", + "tailwind-merge": "^1.14.0", "uuid": "^9.0.0", "y-indexeddb": "^9.0.9", "y-webrtc": "^10.2.3", @@ -7842,6 +7844,20 @@ "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-confetti": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-confetti/-/react-confetti-6.1.0.tgz", + "integrity": "sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==", + "dependencies": { + "tween-functions": "^1.2.0" + }, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.1 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -8750,6 +8766,15 @@ "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/tailwind-merge": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz", + "integrity": "sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", @@ -8922,6 +8947,11 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/tween-functions": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz", + "integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",