1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-08-31 21:11:44 +02:00
This commit is contained in:
Arik Chakma
2025-07-02 21:12:19 +06:00
parent fff19eb566
commit 8bb4f0a913
3 changed files with 119 additions and 26 deletions

View File

@@ -4,6 +4,7 @@ import { AIMCQQuestion } from './AIMCQQuestion';
import { AIOpenEndedQuestion } from './AIOpenEndedQuestion';
import { QuizTopNavigation } from './QuizTopNavigation';
import { getPercentage } from '../../lib/number';
import { AIQuizResults } from './AIQuizResults';
export type QuestionState = {
isSubmitted: boolean;
@@ -25,10 +26,11 @@ type AIQuizContentProps = {
quizSlug?: string;
questions: QuizQuestion[];
isLoading?: boolean;
onNewQuiz?: () => void;
};
export function AIQuizContent(props: AIQuizContentProps) {
const { quizSlug, questions, isLoading } = props;
const { quizSlug, questions, isLoading, onNewQuiz } = props;
const [activeQuestionIndex, setActiveQuestionIndex] = useState(0);
const activeQuestion = questions[activeQuestionIndex];
@@ -43,12 +45,10 @@ export function AIQuizContent(props: AIQuizContentProps) {
const handleSubmit = (status: QuestionState['status']) => {
setQuestionStates((prev) => {
const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE;
const newSelectedOptions = {
...prev,
[activeQuestionIndex]: {
...oldState,
...activeQuestionState,
isSubmitted: true,
status,
},
@@ -62,12 +62,10 @@ export function AIQuizContent(props: AIQuizContentProps) {
const handleSetUserAnswer = (userAnswer: string) => {
setQuestionStates((prev) => {
const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE;
const newSelectedOptions = {
...prev,
[activeQuestionIndex]: {
...oldState,
...activeQuestionState,
userAnswer,
},
};
@@ -78,12 +76,10 @@ export function AIQuizContent(props: AIQuizContentProps) {
const handleSetCorrectAnswer = (correctAnswer: string) => {
setQuestionStates((prev) => {
const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE;
const newSelectedOptions = {
...prev,
[activeQuestionIndex]: {
...oldState,
...activeQuestionState,
correctAnswer,
},
};
@@ -94,12 +90,10 @@ export function AIQuizContent(props: AIQuizContentProps) {
const handleSelectOptions = (options: number[]) => {
setQuestionStates((prev) => {
const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE;
const newSelectedOptions = {
...prev,
[activeQuestionIndex]: {
...oldState,
...activeQuestionState,
selectedOptions: options,
},
};
@@ -112,6 +106,12 @@ export function AIQuizContent(props: AIQuizContentProps) {
setActiveQuestionIndex(activeQuestionIndex + 1);
};
const handleRetry = () => {
setActiveQuestionIndex(0);
setQuestionStates({});
setIsAllQuestionsSubmitted(false);
};
const totalQuestions = questions?.length ?? 0;
const progressPercentage = isLoading
? 0
@@ -119,15 +119,24 @@ export function AIQuizContent(props: AIQuizContentProps) {
return (
<div className="mx-auto w-full max-w-lg py-10">
<QuizTopNavigation
activeQuestionIndex={activeQuestionIndex}
totalQuestions={totalQuestions}
progressPercentage={progressPercentage}
onPrevious={() => setActiveQuestionIndex(activeQuestionIndex - 1)}
onNext={() => setActiveQuestionIndex(activeQuestionIndex + 1)}
/>
{!isAllQuestionsSubmitted && (
<QuizTopNavigation
activeQuestionIndex={activeQuestionIndex}
totalQuestions={totalQuestions}
progressPercentage={progressPercentage}
onPrevious={() => setActiveQuestionIndex(activeQuestionIndex - 1)}
onNext={() => setActiveQuestionIndex(activeQuestionIndex + 1)}
/>
)}
{isAllQuestionsSubmitted ? (
<AIQuizResults
questionStates={questionStates}
totalQuestions={totalQuestions}
onRetry={handleRetry}
onNewQuiz={onNewQuiz ?? (() => {})}
/>
) : (
<>
{activeQuestion && activeQuestion.type === 'mcq' && (
<AIMCQQuestion

View File

@@ -0,0 +1,89 @@
import {
CheckCircle,
PartyPopper,
RefreshCcw,
RotateCcw,
XCircle,
SkipForward,
CheckCircle2Icon,
} from 'lucide-react';
import type { QuestionState } from './AIQuizContent';
import { getPercentage } from '../../lib/number';
type AIQuizResultsProps = {
questionStates: Record<number, QuestionState>;
totalQuestions: number;
onRetry: () => void;
onNewQuiz: () => void;
};
export function AIQuizResults(props: AIQuizResultsProps) {
const { questionStates, totalQuestions, onRetry, onNewQuiz } = props;
const states = Object.values(questionStates);
const correctCount = states.filter(
(state) => state.status === 'correct',
).length;
const incorrectCount = states.filter(
(state) => state.status === 'incorrect',
).length;
const skippedCount = states.filter(
(state) => state.status === 'skipped',
).length;
const accuracy = getPercentage(correctCount, totalQuestions);
return (
<div className="flex flex-col items-center justify-center p-8 text-center">
<PartyPopper className="mb-6 h-16 w-16 text-gray-400" />
<div className="mb-2 text-4xl font-bold">
{correctCount}/{totalQuestions}
</div>
<p className="mb-8 text-lg text-gray-600">
Great job! You answered {correctCount} out of {totalQuestions} questions
correctly that's {accuracy}% accuracy!
</p>
<div className="mb-8 grid w-full max-w-sm grid-cols-3 gap-4">
<div className="flex flex-col items-center rounded-xl bg-green-50 p-4 text-green-700">
<CheckCircle2Icon className="mb-2 h-6 w-6" />
<div className="text-xl font-semibold">{correctCount}</div>
<div className="text-sm">Correct</div>
</div>
<div className="flex flex-col items-center rounded-xl bg-red-50 p-4 text-red-700">
<XCircle className="mb-2 h-6 w-6" />
<div className="text-xl font-semibold">{incorrectCount}</div>
<div className="text-sm">Incorrect</div>
</div>
<div className="flex flex-col items-center rounded-xl bg-gray-50 p-4 text-gray-700">
<SkipForward className="mb-2 h-6 w-6" />
<div className="text-xl font-semibold">{skippedCount}</div>
<div className="text-sm">Skipped</div>
</div>
</div>
<div className="flex gap-4">
<button
onClick={onRetry}
className="flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"
>
<RotateCcw className="h-4 w-4" />
Try Again
</button>
<button
onClick={onNewQuiz}
className="flex items-center gap-2 rounded-lg bg-black px-4 py-2 text-sm font-medium text-white hover:bg-gray-900"
>
<RefreshCcw className="h-4 w-4" />
New Quiz
</button>
</div>
</div>
);
}

View File

@@ -214,11 +214,6 @@ export function generateAiQuizQuestions(questionData: string): QuizQuestion[] {
continue;
}
console.log('-'.repeat(20));
console.log('CONTEXT:', context);
console.log('LINE:', line);
console.log('-'.repeat(20));
if (context === 'question') {
currentQuestion.title += `\n${line}`;
} else if (context === 'explanation') {