Skip to content

Commit ebb9e7b

Browse files
feat: quiz progression and completion
1 parent 6e7a213 commit ebb9e7b

File tree

1 file changed

+87
-46
lines changed

1 file changed

+87
-46
lines changed

src/components/tutorial-quiz.tsx

Lines changed: 87 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,117 @@
11
import { Cinzel } from "next/font/google";
22
import { useState } from "react";
33
import { helloWorldQuiz } from "@/data/quizzes/01-hello-world";
4+
45
const cinzel = Cinzel({ subsets: ["latin"], weight: ["700"] });
6+
57
export default function TutorialQuiz() {
68
const [questionNumber, setQuestionNumber] = useState<number>(0);
7-
const currentQuestion = helloWorldQuiz.questions[questionNumber];
89
const [selectedOption, setSelectedOption] = useState<string | null>(null);
910
const [isCorrect, setIsCorrect] = useState<boolean | null>(null);
11+
const [score, setScore] = useState(0);
12+
const [showResults, setShowResults] = useState(false);
13+
14+
const currentQuestion = helloWorldQuiz.questions[questionNumber];
1015

1116
const handleOptionClick = (option: string) => {
12-
// Prevent clicking if already selected
1317
if (selectedOption) return;
14-
1518
setSelectedOption(option);
1619

17-
// Check answer
18-
let correct = null;
19-
if (option === currentQuestion.correctAnswer) {
20-
correct = true;
21-
} else {
22-
correct = false;
23-
}
20+
const correct = option === currentQuestion.correctAnswer;
2421
setIsCorrect(correct);
22+
23+
// 2. Update Score
24+
if (correct) {
25+
setScore((prev) => prev + 1);
26+
}
27+
28+
// 3. Auto-advance timer
29+
setTimeout(() => {
30+
const nextIndex = questionNumber + 1;
31+
32+
if (nextIndex < helloWorldQuiz.questions.length) {
33+
setQuestionNumber(nextIndex);
34+
setSelectedOption(null);
35+
setIsCorrect(null);
36+
} else {
37+
setShowResults(true);
38+
}
39+
}, 2000); // 2 second delay so they can read the feedback
40+
};
41+
42+
const resetQuiz = () => {
43+
setQuestionNumber(0);
44+
setScore(0);
45+
setShowResults(false);
46+
setSelectedOption(null);
47+
setIsCorrect(null);
2548
};
2649

2750
return (
2851
<div className="mt-12 p-8 border-2 border-amber-800/50 rounded-xl bg-amber-50/50 shadow-inner shadow-amber-900/20">
2952
<h2 className={`text-3xl font-bold text-center mb-6 text-amber-900 ${cinzel.className}`}>
30-
Test Your Knowledge, Initiate
53+
{helloWorldQuiz.title}
3154
</h2>
3255

33-
{/* Question Card */}
34-
<div className="space-y-6">
35-
<div className="bg-amber-200/50 p-6 rounded-lg border border-amber-800/30">
36-
{/* Question */}
37-
<h3 className="text-xl font-semibold text-amber-950 mb-4">
38-
{currentQuestion.questionText}
39-
</h3>
56+
{/* 5. Conditional Rendering: Results vs Question */}
57+
{showResults ? (
58+
<div className="text-center space-y-6 animate-fade-in">
59+
<p className="text-2xl text-amber-950">Quest Complete!</p>
60+
<p className="text-xl">
61+
You scored <span className="font-bold">{score}</span> out of{" "}
62+
<span className="font-bold">{helloWorldQuiz.questions.length}</span>
63+
</p>
64+
<button
65+
onClick={resetQuiz}
66+
className="px-6 py-3 bg-amber-900 text-amber-100 font-bold rounded-lg hover:bg-amber-800 border-2 border-amber-950"
67+
>
68+
Try Again
69+
</button>
70+
</div>
71+
) : (
72+
<div className="space-y-6">
73+
{/* Score Tracker Display */}
74+
<div className="flex justify-between text-amber-800 font-serif italic">
75+
<span>
76+
Question {questionNumber + 1} of {helloWorldQuiz.questions.length}
77+
</span>
78+
<span>Score: {score}</span>
79+
</div>
80+
81+
<div className="bg-amber-200/50 p-6 rounded-lg border border-amber-800/30">
82+
<h3 className="text-xl font-semibold text-amber-950 mb-4">
83+
{currentQuestion.questionText}
84+
</h3>
4085

41-
{/* Choices */}
42-
<div className="grid gap-3">
43-
{currentQuestion.options.map((option) => {
44-
// Dynamic Styling Logic ( This is soo cool)
45-
let buttonStyle = "bg-white hover:bg-amber-100 border-amber-300 text-amber-900";
86+
<div className="grid gap-3">
87+
{currentQuestion.options.map((option) => {
88+
let buttonStyle = "bg-white hover:bg-amber-100 border-amber-300 text-amber-900";
4689

47-
if (selectedOption === option) {
48-
if (isCorrect) {
49-
buttonStyle = "bg-green-200 border-green-500 text-green-900 font-bold";
50-
} else {
51-
buttonStyle = "bg-red-200 border-red-500 text-red-900 font-bold";
90+
if (selectedOption === option) {
91+
buttonStyle = isCorrect
92+
? "bg-green-200 border-green-500 text-green-900 font-bold"
93+
: "bg-red-200 border-red-500 text-red-900 font-bold";
94+
} else if (selectedOption && option === currentQuestion.correctAnswer) {
95+
buttonStyle = "bg-green-100 border-green-300 text-green-800 opacity-75";
5296
}
53-
} else if (selectedOption && option === currentQuestion.correctAnswer) {
54-
// Highlight the correct answer if they got it wrong
55-
buttonStyle = "bg-green-100 border-green-300 text-green-800 opacity-75";
56-
}
5797

58-
return (
59-
<button
60-
key={option}
61-
onClick={() => handleOptionClick(option)}
62-
disabled={selectedOption !== null} // Disables all buttons after selection
63-
className={`w-full text-left p-4 rounded border-2 transition-all hover:cursor-pointer ${buttonStyle} ${
64-
selectedOption === null ? "hover:translate-x-1" : ""
65-
}`}
66-
>
67-
{option}
68-
</button>
69-
);
70-
})}
98+
return (
99+
<button
100+
key={option}
101+
onClick={() => handleOptionClick(option)}
102+
disabled={selectedOption !== null}
103+
className={`w-full text-left p-4 rounded border-2 transition-all ${buttonStyle} ${
104+
selectedOption === null ? "hover:translate-x-1" : ""
105+
}`}
106+
>
107+
{option}
108+
</button>
109+
);
110+
})}
111+
</div>
71112
</div>
72113
</div>
73-
</div>
114+
)}
74115
</div>
75116
);
76117
}

0 commit comments

Comments
 (0)