mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-31 21:11:44 +02:00
wip
This commit is contained in:
@@ -4,6 +4,7 @@ import { AIMCQQuestion } from './AIMCQQuestion';
|
|||||||
import { AIOpenEndedQuestion } from './AIOpenEndedQuestion';
|
import { AIOpenEndedQuestion } from './AIOpenEndedQuestion';
|
||||||
import { QuizTopNavigation } from './QuizTopNavigation';
|
import { QuizTopNavigation } from './QuizTopNavigation';
|
||||||
import { getPercentage } from '../../lib/number';
|
import { getPercentage } from '../../lib/number';
|
||||||
|
import { AIQuizResults } from './AIQuizResults';
|
||||||
|
|
||||||
export type QuestionState = {
|
export type QuestionState = {
|
||||||
isSubmitted: boolean;
|
isSubmitted: boolean;
|
||||||
@@ -25,10 +26,11 @@ type AIQuizContentProps = {
|
|||||||
quizSlug?: string;
|
quizSlug?: string;
|
||||||
questions: QuizQuestion[];
|
questions: QuizQuestion[];
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
|
onNewQuiz?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function AIQuizContent(props: AIQuizContentProps) {
|
export function AIQuizContent(props: AIQuizContentProps) {
|
||||||
const { quizSlug, questions, isLoading } = props;
|
const { quizSlug, questions, isLoading, onNewQuiz } = props;
|
||||||
|
|
||||||
const [activeQuestionIndex, setActiveQuestionIndex] = useState(0);
|
const [activeQuestionIndex, setActiveQuestionIndex] = useState(0);
|
||||||
const activeQuestion = questions[activeQuestionIndex];
|
const activeQuestion = questions[activeQuestionIndex];
|
||||||
@@ -43,12 +45,10 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
|
|
||||||
const handleSubmit = (status: QuestionState['status']) => {
|
const handleSubmit = (status: QuestionState['status']) => {
|
||||||
setQuestionStates((prev) => {
|
setQuestionStates((prev) => {
|
||||||
const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE;
|
|
||||||
|
|
||||||
const newSelectedOptions = {
|
const newSelectedOptions = {
|
||||||
...prev,
|
...prev,
|
||||||
[activeQuestionIndex]: {
|
[activeQuestionIndex]: {
|
||||||
...oldState,
|
...activeQuestionState,
|
||||||
isSubmitted: true,
|
isSubmitted: true,
|
||||||
status,
|
status,
|
||||||
},
|
},
|
||||||
@@ -62,12 +62,10 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
|
|
||||||
const handleSetUserAnswer = (userAnswer: string) => {
|
const handleSetUserAnswer = (userAnswer: string) => {
|
||||||
setQuestionStates((prev) => {
|
setQuestionStates((prev) => {
|
||||||
const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE;
|
|
||||||
|
|
||||||
const newSelectedOptions = {
|
const newSelectedOptions = {
|
||||||
...prev,
|
...prev,
|
||||||
[activeQuestionIndex]: {
|
[activeQuestionIndex]: {
|
||||||
...oldState,
|
...activeQuestionState,
|
||||||
userAnswer,
|
userAnswer,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -78,12 +76,10 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
|
|
||||||
const handleSetCorrectAnswer = (correctAnswer: string) => {
|
const handleSetCorrectAnswer = (correctAnswer: string) => {
|
||||||
setQuestionStates((prev) => {
|
setQuestionStates((prev) => {
|
||||||
const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE;
|
|
||||||
|
|
||||||
const newSelectedOptions = {
|
const newSelectedOptions = {
|
||||||
...prev,
|
...prev,
|
||||||
[activeQuestionIndex]: {
|
[activeQuestionIndex]: {
|
||||||
...oldState,
|
...activeQuestionState,
|
||||||
correctAnswer,
|
correctAnswer,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -94,12 +90,10 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
|
|
||||||
const handleSelectOptions = (options: number[]) => {
|
const handleSelectOptions = (options: number[]) => {
|
||||||
setQuestionStates((prev) => {
|
setQuestionStates((prev) => {
|
||||||
const oldState = activeQuestionState ?? DEFAULT_QUESTION_STATE;
|
|
||||||
|
|
||||||
const newSelectedOptions = {
|
const newSelectedOptions = {
|
||||||
...prev,
|
...prev,
|
||||||
[activeQuestionIndex]: {
|
[activeQuestionIndex]: {
|
||||||
...oldState,
|
...activeQuestionState,
|
||||||
selectedOptions: options,
|
selectedOptions: options,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -112,6 +106,12 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
setActiveQuestionIndex(activeQuestionIndex + 1);
|
setActiveQuestionIndex(activeQuestionIndex + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRetry = () => {
|
||||||
|
setActiveQuestionIndex(0);
|
||||||
|
setQuestionStates({});
|
||||||
|
setIsAllQuestionsSubmitted(false);
|
||||||
|
};
|
||||||
|
|
||||||
const totalQuestions = questions?.length ?? 0;
|
const totalQuestions = questions?.length ?? 0;
|
||||||
const progressPercentage = isLoading
|
const progressPercentage = isLoading
|
||||||
? 0
|
? 0
|
||||||
@@ -119,15 +119,24 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-lg py-10">
|
<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 && (
|
{!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' && (
|
{activeQuestion && activeQuestion.type === 'mcq' && (
|
||||||
<AIMCQQuestion
|
<AIMCQQuestion
|
||||||
|
89
src/components/AIQuiz/AIQuizResults.tsx
Normal file
89
src/components/AIQuiz/AIQuizResults.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@@ -214,11 +214,6 @@ export function generateAiQuizQuestions(questionData: string): QuizQuestion[] {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('-'.repeat(20));
|
|
||||||
console.log('CONTEXT:', context);
|
|
||||||
console.log('LINE:', line);
|
|
||||||
console.log('-'.repeat(20));
|
|
||||||
|
|
||||||
if (context === 'question') {
|
if (context === 'question') {
|
||||||
currentQuestion.title += `\n${line}`;
|
currentQuestion.title += `\n${line}`;
|
||||||
} else if (context === 'explanation') {
|
} else if (context === 'explanation') {
|
||||||
|
Reference in New Issue
Block a user