Skip to content

Commit ca1c289

Browse files
Merge pull request #43 from Eric-Zhang-Developer/feat/tutorial-quiz-part-1
Feat/tutorial quiz part 1
2 parents ad81c79 + 543fb40 commit ca1c289

File tree

4 files changed

+256
-39
lines changed

4 files changed

+256
-39
lines changed

src/app/tutorial-hello-world/page.tsx

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,51 @@
22

33
import BackToDashBoardLink from "@/components/back-to-dashboard-link";
44
import YouTube from "react-youtube";
5-
import { Cinzel } from 'next/font/google';
5+
import Quiz from "@/components/tutorial-quiz";
6+
7+
import { Cinzel } from "next/font/google";
8+
import { helloWorldQuiz } from "@/data/quizzes/01-hello-world";
69

710
const cinzel = Cinzel({
811
subsets: ["latin"],
912
weight: ["400", "700"],
1013
});
1114

1215
// body text (Times New Roman= more readable)
13-
const bodyFontClass = "font-serif text-amber-950";
16+
const bodyFontClass = "font-serif text-amber-950";
1417
// titles (Cinzel font)
15-
const cinzelTitleClass = cinzel.className;
18+
const cinzelTitleClass = cinzel.className;
1619

1720
export default function TutorialHelloWorld() {
1821
return (
19-
// Background of scroll
22+
// Background of scroll
2023
<div
2124
className="min-h-screen p-4 md:p-12"
2225
style={{
23-
backgroundImage: "url('/geminiblurred.png')",
24-
backgroundSize: 'cover',
25-
backgroundPosition: 'center',
26-
backgroundColor: '#fef3c7',
26+
backgroundImage: "url('/geminiblurred.png')",
27+
backgroundSize: "cover",
28+
backgroundPosition: "center",
29+
backgroundColor: "#fef3c7",
2730
}}
2831
>
29-
<div
30-
className="inline-block p-4"
31-
style={{ zIndex: 10 }}
32-
>
32+
<div className="inline-block p-4" style={{ zIndex: 10 }}>
3333
<BackToDashBoardLink />
3434
</div>
3535

3636
{/* "scroll"*/}
37-
<article className={`max-w-4xl mx-auto bg-amber-100 p-8 md:p-12 shadow-2xl shadow-amber-950/70 space-y-8
37+
<article
38+
className={`max-w-4xl mx-auto bg-amber-100 p-8 md:p-12 shadow-2xl shadow-amber-950/70 space-y-8
3839
${bodyFontClass} border border-amber-800 transform rotate-[-0.5deg]
39-
rounded-t-[4rem] rounded-b-lg`}>
40-
40+
rounded-t-[4rem] rounded-b-lg`}
41+
>
4142
{/* title*/}
42-
<h1 className={`text-4xl md:text-5xl font-bold ${cinzelTitleClass}
43-
border-b-4 border-amber-900 pb-4 mb-8 text-center uppercase`}>
43+
<h1
44+
className={`text-4xl md:text-5xl font-bold ${cinzelTitleClass}
45+
border-b-4 border-amber-900 pb-4 mb-8 text-center uppercase`}
46+
>
4447
Quest: The Oracle&apos;s First Greeting
4548
</h1>
46-
49+
4750
<p className="text-xl italic text-amber-900">
4851
Welcome, brave coder! Your journey into the mystical lands of programming begins now.
4952
Before you can conjure complex spells or build mighty digital fortresses, every adventurer
@@ -56,7 +59,7 @@ export default function TutorialHelloWorld() {
5659
</p>
5760

5861
<hr className="my-8 border-amber-900/50" />
59-
62+
6063
{/* section*/}
6164
<section className="space-y-4">
6265
<h2 className={`text-3xl font-semibold ${cinzelTitleClass}`}>
@@ -79,7 +82,7 @@ export default function TutorialHelloWorld() {
7982
</section>
8083

8184
<hr className="my-8 border-amber-900/50" />
82-
85+
8386
{/* section */}
8487
<section className="space-y-4">
8588
<h2 className={`text-3xl font-semibold ${cinzelTitleClass}`}>
@@ -88,27 +91,27 @@ export default function TutorialHelloWorld() {
8891
<p className="text-lg">Why start with something so simple?</p>
8992
<ul className="list-disc list-inside space-y-3 text-lg pl-4">
9093
<li>
91-
<strong className="font-semibold text-amber-950">It&apos;s Tradition:</strong> Like a knight
92-
receiving their first sword, programmers worldwide start here. It connects you to
93-
generations of coders before you!
94+
<strong className="font-semibold text-amber-950">It&apos;s Tradition:</strong> Like a
95+
knight receiving their first sword, programmers worldwide start here. It connects you
96+
to generations of coders before you!
9497
</li>
9598
<li>
96-
<strong className="font-semibold text-amber-950">It&apos;s a Test:</strong> Successfully making the
97-
machine say &quot;Hello World&quot; proves your basic setup is working. Your coding
98-
environment, the language you&apos;re using - they&apos;re all aligned and ready for
99-
more complex commands. It&apos;s like checking if your magic wand sparks before trying
100-
to levitate a mountain.
99+
<strong className="font-semibold text-amber-950">It&apos;s a Test:</strong>{" "}
100+
Successfully making the machine say &quot;Hello World&quot; proves your basic setup is
101+
working. Your coding environment, the language you&apos;re using - they&apos;re all
102+
aligned and ready for more complex commands. It&apos;s like checking if your magic
103+
wand sparks before trying to levitate a mountain.
101104
</li>
102105
<li>
103-
<strong className="font-semibold text-amber-950">It&apos;s Confidence:</strong> Seeing your very
104-
first command come to life, even a simple one, is a powerful boost! It&apos;s the
105-
first step on your epic journey.
106+
<strong className="font-semibold text-amber-950">It&apos;s Confidence:</strong> Seeing
107+
your very first command come to life, even a simple one, is a powerful boost!
108+
It&apos;s the first step on your epic journey.
106109
</li>
107110
</ul>
108111
</section>
109-
112+
110113
<hr className="my-8 border-amber-900/50" />
111-
114+
112115
{/* section*/}
113116
<section className="space-y-4">
114117
<h2 className={`text-3xl font-semibold ${cinzelTitleClass}`}>
@@ -124,17 +127,27 @@ export default function TutorialHelloWorld() {
124127
<p className="text-lg">
125128
Different programming languages (spellbooks) have slightly different words for the
126129
&apos;Display&apos; command (like{" "}
127-
<code className="bg-amber-200 px-1 rounded text-amber-950 font-mono border border-amber-800">print</code>,{" "}
128-
<code className="bg-amber-200 px-1 rounded text-amber-950 font-mono border border-amber-800">console.log</code>,{" "}
129-
<code className="bg-amber-200 px-1 rounded text-amber-950 font-mono border border-amber-800">echo</code>), but the core
130-
idea is the same: tell the machine to show those exact words.
130+
<code className="bg-amber-200 px-1 rounded text-amber-950 font-mono border border-amber-800">
131+
print
132+
</code>
133+
,{" "}
134+
<code className="bg-amber-200 px-1 rounded text-amber-950 font-mono border border-amber-800">
135+
console.log
136+
</code>
137+
,{" "}
138+
<code className="bg-amber-200 px-1 rounded text-amber-950 font-mono border border-amber-800">
139+
echo
140+
</code>
141+
), but the core idea is the same: tell the machine to show those exact words.
131142
</p>
132143
</section>
133-
144+
134145
<hr className="my-8 border-amber-900/50" />
135146

136147
<YouTube videoId="hp4pYFASTrc"></YouTube>
148+
149+
<Quiz quizData={helloWorldQuiz}></Quiz>
137150
</article>
138151
</div>
139152
);
140-
}
153+
}

src/components/tutorial-quiz.tsx

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { Cinzel } from "next/font/google";
2+
import { useState } from "react";
3+
import { QuizData } from "@/lib/types/types";
4+
5+
const cinzel = Cinzel({ subsets: ["latin"], weight: ["700"] });
6+
7+
type QuizProps = {
8+
quizData: QuizData;
9+
};
10+
11+
export default function Quiz({ quizData }: QuizProps) {
12+
const [questionNumber, setQuestionNumber] = useState<number>(0);
13+
const [selectedOption, setSelectedOption] = useState<string | null>(null);
14+
const [isCorrect, setIsCorrect] = useState<boolean | null>(null);
15+
const [score, setScore] = useState(0);
16+
const [showResults, setShowResults] = useState(false);
17+
18+
const currentQuestion = quizData.questions[questionNumber];
19+
20+
const handleOptionClick = (option: string) => {
21+
if (selectedOption) return;
22+
setSelectedOption(option);
23+
24+
// 1. Check Correctness
25+
const correct = option === currentQuestion.correctAnswer;
26+
setIsCorrect(correct);
27+
28+
// 2. Update Score
29+
if (correct) {
30+
setScore((prev) => prev + 1);
31+
}
32+
33+
// 3. Auto-advance timer
34+
setTimeout(() => {
35+
const nextIndex = questionNumber + 1;
36+
37+
if (nextIndex < quizData.questions.length) {
38+
setQuestionNumber(nextIndex);
39+
setSelectedOption(null);
40+
setIsCorrect(null);
41+
} else {
42+
setShowResults(true);
43+
}
44+
}, 2000); // 2 second delay so they can read the feedback
45+
};
46+
47+
const resetQuiz = () => {
48+
setQuestionNumber(0);
49+
setScore(0);
50+
setShowResults(false);
51+
setSelectedOption(null);
52+
setIsCorrect(null);
53+
};
54+
55+
return (
56+
<div className="mt-12 p-8 border-2 border-amber-800/50 rounded-xl bg-amber-50/50 shadow-inner shadow-amber-900/20">
57+
<h2 className={`text-3xl font-bold text-center mb-6 text-amber-900 ${cinzel.className}`}>
58+
{quizData.title}
59+
</h2>
60+
61+
{showResults ? (
62+
<div className="text-center space-y-6 animate-fade-in">
63+
{/* Results */}
64+
<p className="text-2xl text-amber-950">Quest Complete!</p>
65+
<p className="text-xl">
66+
You scored <span className="font-bold">{score}</span> out of{" "}
67+
<span className="font-bold">{quizData.questions.length}</span>
68+
</p>
69+
<button
70+
onClick={resetQuiz}
71+
className="px-6 py-3 bg-amber-900 text-amber-100 font-bold rounded-lg hover:bg-amber-800 border-2 border-amber-950"
72+
>
73+
Try Again
74+
</button>
75+
</div>
76+
) : (
77+
<div className="space-y-6">
78+
{/* Score Tracker Display */}
79+
<div className="flex justify-between text-amber-800 font-serif italic">
80+
<span>
81+
Question {questionNumber + 1} of {quizData.questions.length}
82+
</span>
83+
<span>Score: {score}</span>
84+
</div>
85+
86+
<div className="bg-amber-200/50 p-6 rounded-lg border border-amber-800/30">
87+
<h3 className="text-xl font-semibold text-amber-950 mb-4">
88+
{currentQuestion.questionText}
89+
</h3>
90+
91+
<div className="grid gap-3">
92+
{currentQuestion.options.map((option) => {
93+
let buttonStyle = "bg-white hover:bg-amber-100 border-amber-300 text-amber-900";
94+
95+
if (selectedOption === option) {
96+
buttonStyle = isCorrect
97+
? "bg-green-200 border-green-500 text-green-900 font-bold"
98+
: "bg-red-200 border-red-500 text-red-900 font-bold";
99+
} else if (selectedOption && option === currentQuestion.correctAnswer) {
100+
buttonStyle = "bg-green-100 border-green-300 text-green-800 opacity-75";
101+
}
102+
103+
return (
104+
<button
105+
key={option}
106+
onClick={() => handleOptionClick(option)}
107+
disabled={selectedOption !== null}
108+
className={`w-full text-left p-4 rounded border-2 transition-all ${buttonStyle} ${
109+
selectedOption === null ? "hover:translate-x-1" : ""
110+
}`}
111+
>
112+
{option}
113+
</button>
114+
);
115+
})}
116+
</div>
117+
</div>
118+
</div>
119+
)}
120+
</div>
121+
);
122+
}

src/data/quizzes/01-hello-world.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { QuizData } from "@/lib/types/types";
2+
3+
export const helloWorldQuiz: QuizData = {
4+
title: "The Oracle's First Greeting",
5+
questions: [
6+
{
7+
questionText: "What is the primary purpose of a 'Hello World' program?",
8+
options: [
9+
"To summon a digital dragon",
10+
"To verify that your coding environment is set up correctly",
11+
"To hack into the mainframe",
12+
"To write a poem for the computer",
13+
],
14+
correctAnswer: "To verify that your coding environment is set up correctly",
15+
},
16+
{
17+
questionText:
18+
"True or False: 'Hello World' is only used in one specific programming language.",
19+
options: ["True", "False"],
20+
correctAnswer: "False",
21+
},
22+
{
23+
questionText: "Which of the following is a common command to display text?",
24+
options: ["print", "shout", "whisper", "cast_spell"],
25+
correctAnswer: "print",
26+
},
27+
{
28+
questionText:
29+
"In the tutorial, teaching the computer to say 'Hello World' is compared to what analogy?",
30+
options: [
31+
"Training a dragon to fly",
32+
"Teaching a parrot its first words",
33+
"Building a fortress from scratch",
34+
"Brewing a complex potion",
35+
],
36+
correctAnswer: "Teaching a parrot its first words",
37+
},
38+
{
39+
questionText:
40+
"Which of these commands was NOT mentioned as a way to display text in different languages?",
41+
options: ["console.log", "echo", "print", "shout_loudly"],
42+
correctAnswer: "shout_loudly",
43+
},
44+
{
45+
questionText: "Why is the 'Hello World' program considered a 'tradition'?",
46+
options: [
47+
"It connects you to generations of coders before you",
48+
"It requires a high-level wizard to perform",
49+
"It is the only program that never has bugs",
50+
"It costs gold coins to run",
51+
],
52+
correctAnswer: "It connects you to generations of coders before you",
53+
},
54+
{
55+
questionText:
56+
"What does the 'Hello World' program prove about your 'magic wand' (coding setup)?",
57+
options: [
58+
"That it can levitate mountains",
59+
"That it is broken",
60+
"That it sparks and is ready for more complex commands",
61+
"That it needs to be recharged",
62+
],
63+
correctAnswer: "That it sparks and is ready for more complex commands",
64+
},
65+
{
66+
questionText:
67+
"True or False: Even though the command looks different in different languages (like 'echo' or 'console.log'), the core idea is always to display the specific words.",
68+
options: ["True", "False"],
69+
correctAnswer: "True",
70+
},
71+
],
72+
};

src/lib/types/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export type Question = {
2+
questionText: string;
3+
options: string[];
4+
correctAnswer: string;
5+
};
6+
7+
export type QuizData = {
8+
title: string;
9+
questions: Question[];
10+
};

0 commit comments

Comments
 (0)