From adfbb818a6f99227d6f6a6a37012daa4daf77b4f Mon Sep 17 00:00:00 2001 From: Arik Chakma Date: Wed, 2 Jul 2025 20:56:32 +0600 Subject: [PATCH] wip --- src/components/AIQuiz/AIMCQQuestion.tsx | 16 +- src/components/AIQuiz/AIOpenEndedQuestion.tsx | 19 +-- src/components/AIQuiz/AIQuizContent.tsx | 148 ++++++------------ src/components/AIQuiz/QuizTopNavigation.tsx | 74 +++++++++ 4 files changed, 138 insertions(+), 119 deletions(-) create mode 100644 src/components/AIQuiz/QuizTopNavigation.tsx diff --git a/src/components/AIQuiz/AIMCQQuestion.tsx b/src/components/AIQuiz/AIMCQQuestion.tsx index 3cd94cbc0..5686cf686 100644 --- a/src/components/AIQuiz/AIMCQQuestion.tsx +++ b/src/components/AIQuiz/AIMCQQuestion.tsx @@ -9,24 +9,20 @@ export const markdownClassName = type AIMCQQuestionProps = { question: QuizQuestion; - selectedOptions: number[]; + questionState: QuestionState; + setSelectedOptions: (options: number[]) => void; - isSubmitted: boolean; onSubmit: (status: QuestionState['status']) => void; onNext: () => void; }; export function AIMCQQuestion(props: AIMCQQuestionProps) { - const { - question, - selectedOptions, - setSelectedOptions, - isSubmitted, - onSubmit, - onNext, - } = props; + const { question, questionState, setSelectedOptions, onSubmit, onNext } = + props; const { title: questionText, options, answerExplanation } = question; + const { isSubmitted, selectedOptions = [], status } = questionState; + const canSubmitMultipleAnswers = options.filter((option) => option.isCorrect).length > 1; diff --git a/src/components/AIQuiz/AIOpenEndedQuestion.tsx b/src/components/AIQuiz/AIOpenEndedQuestion.tsx index 74cb61e99..512d54c01 100644 --- a/src/components/AIQuiz/AIOpenEndedQuestion.tsx +++ b/src/components/AIQuiz/AIOpenEndedQuestion.tsx @@ -17,33 +17,34 @@ type VerifyQuizAnswerResponse = { type AIOpenEndedQuestionProps = { quizSlug: string; question: QuizQuestion; - isSubmitted: boolean; + questionState: QuestionState; + onSubmit: (status: QuestionState['status']) => void; onNext: () => void; - userAnswer: string; setUserAnswer: (answer: string) => void; - correctAnswer: string; setCorrectAnswer: (answer: string) => void; - - status: QuestionState['status']; }; export function AIOpenEndedQuestion(props: AIOpenEndedQuestionProps) { const { quizSlug, question, - isSubmitted, + questionState, onSubmit, onNext, - userAnswer, setUserAnswer, - correctAnswer, setCorrectAnswer, - status, } = props; const { title: questionText } = question; + const { + isSubmitted, + userAnswer = '', + correctAnswer = '', + status, + } = questionState; + const { mutate: verifyAnswer, isPending: isVerifying, diff --git a/src/components/AIQuiz/AIQuizContent.tsx b/src/components/AIQuiz/AIQuizContent.tsx index 8b2d4e75c..f61537f42 100644 --- a/src/components/AIQuiz/AIQuizContent.tsx +++ b/src/components/AIQuiz/AIQuizContent.tsx @@ -1,12 +1,8 @@ import { useState } from 'react'; import type { QuizQuestion } from '../../queries/ai-quiz'; -import { - ChevronLeftIcon, - ChevronRightIcon, - type LucideIcon, -} from 'lucide-react'; import { AIMCQQuestion } from './AIMCQQuestion'; import { AIOpenEndedQuestion } from './AIOpenEndedQuestion'; +import { QuizTopNavigation } from './QuizTopNavigation'; export type QuestionState = { isSubmitted: boolean; @@ -39,27 +35,18 @@ export function AIQuizContent(props: AIQuizContentProps) { const [selectedOptions, setSelectedOptions] = useState< Record >({}); - - const hasMoreQuestions = activeQuestionIndex < questions.length - 1; - const hasPreviousQuestions = activeQuestionIndex > 0; + const [isAllQuestionsSubmitted, setIsAllQuestionsSubmitted] = useState(false); const activeQuestionState = selectedOptions[activeQuestionIndex] ?? DEFAULT_QUESTION_STATE; - const activeQuestionSelectedOptions = - activeQuestionState.selectedOptions ?? []; - const activeQuestionIsSubmitted = activeQuestionState.isSubmitted ?? false; - - const handleSubmit = ( - questionIndex: number, - status: QuestionState['status'], - ) => { + const handleSubmit = (status: QuestionState['status']) => { setSelectedOptions((prev) => { const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE; const newSelectedOptions = { ...prev, - [questionIndex]: { + [activeQuestionIndex]: { ...oldState, isSubmitted: true, status, @@ -68,15 +55,17 @@ export function AIQuizContent(props: AIQuizContentProps) { return newSelectedOptions; }); + + setIsAllQuestionsSubmitted(activeQuestionIndex === questions.length - 1); }; - const handleSetUserAnswer = (questionIndex: number, userAnswer: string) => { + const handleSetUserAnswer = (userAnswer: string) => { setSelectedOptions((prev) => { const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE; const newSelectedOptions = { ...prev, - [questionIndex]: { + [activeQuestionIndex]: { ...oldState, userAnswer, }, @@ -86,16 +75,13 @@ export function AIQuizContent(props: AIQuizContentProps) { }); }; - const handleSetCorrectAnswer = ( - questionIndex: number, - correctAnswer: string, - ) => { + const handleSetCorrectAnswer = (correctAnswer: string) => { setSelectedOptions((prev) => { const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE; const newSelectedOptions = { ...prev, - [questionIndex]: { + [activeQuestionIndex]: { ...oldState, correctAnswer, }, @@ -105,13 +91,13 @@ export function AIQuizContent(props: AIQuizContentProps) { }); }; - const handleSelectOptions = (questionIndex: number, options: number[]) => { + const handleSelectOptions = (options: number[]) => { setSelectedOptions((prev) => { const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE; const newSelectedOptions = { ...prev, - [questionIndex]: { + [activeQuestionIndex]: { ...oldState, selectedOptions: options, }, @@ -121,88 +107,50 @@ export function AIQuizContent(props: AIQuizContentProps) { }); }; + const handleNext = () => { + setActiveQuestionIndex(activeQuestionIndex + 1); + }; + + const totalQuestions = questions?.length ?? 0; const progressPercentage = isLoading ? 0 - : Math.min(((activeQuestionIndex + 1) / questions.length) * 100, 100); + : Math.min(((activeQuestionIndex + 1) / totalQuestions) * 100, 100); return (
-
- - Question {activeQuestionIndex + 1} of {questions.length} - + setActiveQuestionIndex(activeQuestionIndex - 1)} + onNext={() => setActiveQuestionIndex(activeQuestionIndex + 1)} + /> -
-
-
+ {!isAllQuestionsSubmitted && ( + <> + {activeQuestion && activeQuestion.type === 'mcq' && ( + + )} - setActiveQuestionIndex(activeQuestionIndex - 1)} - icon={ChevronLeftIcon} - /> - setActiveQuestionIndex(activeQuestionIndex + 1)} - icon={ChevronRightIcon} - /> -
- - {activeQuestion && activeQuestion.type === 'mcq' && ( - - handleSelectOptions(activeQuestionIndex, options) - } - onSubmit={(status) => handleSubmit(activeQuestionIndex, status)} - onNext={() => setActiveQuestionIndex(activeQuestionIndex + 1)} - /> - )} - - {activeQuestion && activeQuestion.type === 'open-ended' && ( - handleSubmit(activeQuestionIndex, status)} - onNext={() => setActiveQuestionIndex(activeQuestionIndex + 1)} - userAnswer={activeQuestionState.userAnswer ?? ''} - setUserAnswer={(userAnswer) => - handleSetUserAnswer(activeQuestionIndex, userAnswer) - } - correctAnswer={activeQuestionState.correctAnswer ?? ''} - setCorrectAnswer={(correctAnswer) => - handleSetCorrectAnswer(activeQuestionIndex, correctAnswer) - } - status={activeQuestionState.status} - /> + {activeQuestion && activeQuestion.type === 'open-ended' && ( + + )} + )}
); } - -type NavigationButtonProps = { - disabled: boolean; - onClick: () => void; - icon: LucideIcon; -}; - -function NavigationButton(props: NavigationButtonProps) { - const { disabled, onClick, icon: Icon } = props; - return ( - - ); -} diff --git a/src/components/AIQuiz/QuizTopNavigation.tsx b/src/components/AIQuiz/QuizTopNavigation.tsx new file mode 100644 index 000000000..5e264f58e --- /dev/null +++ b/src/components/AIQuiz/QuizTopNavigation.tsx @@ -0,0 +1,74 @@ +import { + ChevronLeftIcon, + ChevronRightIcon, + type LucideIcon, +} from 'lucide-react'; + +type QuizTopNavigationProps = { + activeQuestionIndex: number; + totalQuestions: number; + progressPercentage: number; + + onPrevious: () => void; + onNext: () => void; +}; + +export function QuizTopNavigation(props: QuizTopNavigationProps) { + const { + activeQuestionIndex, + totalQuestions, + progressPercentage, + onPrevious, + onNext, + } = props; + + const hasPreviousQuestions = activeQuestionIndex > 0; + const hasMoreQuestions = activeQuestionIndex < totalQuestions - 1; + + return ( +
+ + Question {activeQuestionIndex + 1} of {totalQuestions} + + +
+
+
+ + + +
+ ); +} + +type NavigationButtonProps = { + disabled: boolean; + onClick: () => void; + icon: LucideIcon; +}; + +function NavigationButton(props: NavigationButtonProps) { + const { disabled, onClick, icon: Icon } = props; + return ( + + ); +}