import { NodeViewWrapper } from '@tiptap/react' import { v4 as uuidv4 } from 'uuid' import { twMerge } from 'tailwind-merge' import React from 'react' import { BadgeHelp, Check, Minus, Plus, RefreshCcw } from 'lucide-react' import ReactConfetti from 'react-confetti' import { useEditorProvider } from '@components/Contexts/Editor/EditorContext' 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 editorState = useEditorProvider() as any const isEditable = editorState.isEditable 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