import { NodeViewWrapper } from "@tiptap/react"; import { v4 as uuidv4 } from "uuid"; import { twJoin, twMerge } from 'tailwind-merge' import React from "react"; import { BadgeHelp, Check, Info, Minus, MoreVertical, Plus, RefreshCcw, X } from "lucide-react"; import ReactConfetti from "react-confetti"; interface Answer { answer_id: string; answer: string; correct: boolean; } interface Question { question_id: string; question: string; type: "multiple_choice" | 'custom_answer' answers: Answer[]; } 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 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)); 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) => { props.updateAttributes({ questions: questions, }); setQuestions(questions); }; const addSampleQuestion = () => { const newQuestion = { question_id: uuidv4(), question: "", type: "multiple_choice", answers: [ { answer_id: uuidv4(), answer: "", correct: false }, ] } 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 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 (
{(submitted && submissionMessage == "All answers are correct!") && }

Quiz

{isEditable ?
:
refreshUserSubmission()} className="cursor-pointer px-2">
}
{questions.map((question: Question) => (
{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}

}
{isEditable &&
deleteQuestion(question.question_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">
}
{question.answers.map((answer: 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)}

{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-300 hover:bg-lime-400 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">
}
))} {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">
}
))}
); } export default QuizBlockComponent;