import React, { useState, useEffect, useMemo } from 'react';
import { initializeApp } from 'firebase/app';
import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth';
import { getFirestore, collection, addDoc, serverTimestamp } from 'firebase/firestore';
import {
ChevronLeft,
ChevronRight,
Send,
Clock,
CheckCircle2,
LayoutGrid,
AlertCircle,
Trophy,
History,
User,
BookOpen
} from 'lucide-react';
// Firebase Configuration
const firebaseConfig = JSON.parse(__firebase_config);
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const appId = typeof __app_id !== 'undefined' ? __app_id : 'cbt-naratif-sma-x';
// Data Soal (40 Soal Teks Naratif)
const QUESTIONS_DATA = [
// Legend: Malin Kundang
{
id: 1,
text: "Once upon a time, on the north coast of Sumatra lived a poor woman and his son, Malin Kundang. What is the 'Orientation' of a narrative text?",
options: ["The ending of the story", "The introduction of characters and setting", "The conflict of the story", "The moral lesson of the story"],
answer: 1
},
{
id: 2,
text: "Malin Kundang's mother was poor. The word 'poor' is an example of...",
options: ["Adverb", "Noun", "Adjective", "Conjunction"],
answer: 2
},
{
id: 3,
text: "Why did Malin Kundang decide to go sailing?",
options: ["To find his father", "To seek for a better life", "To marry a princess", "To run away from his mother"],
answer: 1
},
{
id: 4,
text: "What is the social function of a narrative text?",
options: ["To describe a specific place", "To persuade the reader to do something", "To amuse or entertain the readers", "To explain how something is made"],
answer: 2
},
{
id: 5,
text: "Many years later, Malin Kundang became rich. 'Many years later' is a...",
options: ["Time connective", "Place connector", "Personal pronoun", "Action verb"],
answer: 0
},
// Legend: Sangkuriang
{
id: 6,
text: "In the story of Sangkuriang, who was Dayang Sumbi?",
options: ["Sangkuriang's daughter", "Sangkuriang's wife", "Sangkuriang's mother", "Sangkuriang's sister"],
answer: 2
},
{
id: 7,
text: "What did Dayang Sumbi ask Sangkuriang to build in one night?",
options: ["A big palace", "A massive bridge", "A vast lake and a boat", "A high mountain"],
answer: 2
},
{
id: 8,
text: "Sangkuriang failed to finish the boat because...",
options: ["He was tired", "The sun rose early because of Dayang Sumbi's trick", "His tools were broken", "Tumang helped him"],
answer: 1
},
{
id: 9,
text: "The boat that Sangkuriang kicked became a mountain named...",
options: ["Mount Bromo", "Mount Merapi", "Mount Tangkuban Perahu", "Mount Gede"],
answer: 2
},
{
id: 10,
text: "What is the 'Complication' in a narrative text?",
options: ["Where the characters are introduced", "Where the problems in the story developed", "Where the problem is solved", "The moral of the story"],
answer: 1
},
// Fable: The Lion and The Mouse
{
id: 11,
text: "A small mouse accidentally woke up a sleeping lion. The lion was angry. What genre is this story?",
options: ["Myth", "Legend", "Fable", "Fairy Tale"],
answer: 2
},
{
id: 12,
text: "The lion laughed when the mouse said he would help him. 'Laughed' is an...",
options: ["Action verb", "Thinking verb", "Linking verb", "Adverb"],
answer: 0
},
{
id: 13,
text: "Later, the lion was caught in a hunter's net. The mouse helped by...",
options: ["Calling other lions", "Gnawing the ropes with his teeth", "Scaring the hunters", "Hiding the lion"],
answer: 1
},
{
id: 14,
text: "What is the lesson from 'The Lion and the Mouse'?",
options: ["Big animals are always stronger", "Little friends may prove great friends", "Never wake up a lion", "Hunters are dangerous"],
answer: 1
},
{
id: 15,
text: "Which tense is mostly used in narrative texts?",
options: ["Simple Present Tense", "Present Continuous Tense", "Simple Past Tense", "Future Tense"],
answer: 2
},
// Fairy Tale: Cinderella
{
id: 16,
text: "Cinderella lived with her stepmother and stepsisters. This part of the story is called...",
options: ["Resolution", "Complication", "Orientation", "Re-orientation"],
answer: 2
},
{
id: 17,
text: "How did Cinderella go to the ball?",
options: ["She walked alone", "With the help of a fairy godmother", "By riding a horse", "Her stepsisters took her"],
answer: 1
},
{
id: 18,
text: "What happened at midnight during the ball?",
options: ["The prince proposed to her", "The magic spell broke", "She lost her ring", "The music stopped"],
answer: 1
},
{
id: 19,
text: "The prince found the girl he was looking for because of the...",
options: ["Glass slipper", "Golden necklace", "Silk dress", "Beautiful hair"],
answer: 0
},
{
id: 20,
text: "The resolution of Cinderella's story is...",
options: ["She married the prince", "She stayed with her stepmother", "She became a fairy", "She worked in the palace"],
answer: 0
},
// Theory & Generic Structure
{
id: 21,
text: "Which of the following is NOT a type of narrative text?",
options: ["Folklore", "Legend", "Report Text", "Myth"],
answer: 2
},
{
id: 22,
text: "The part of the story where the character solves the problem is...",
options: ["Orientation", "Complication", "Resolution", "Coda"],
answer: 2
},
{
id: 23,
text: "What is 'Coda' in a narrative text?",
options: ["The climax", "The setting", "The moral value or lesson", "The first paragraph"],
answer: 2
},
{
id: 24,
text: "Identify the time connective: 'Suddenly, a giant appeared.'",
options: ["Giant", "Appeared", "Suddenly", "A"],
answer: 2
},
{
id: 25,
text: "In a narrative, 'The Golden Egg' is categorized as a...",
options: ["Legend", "Fable", "Fairy Tale", "Biography"],
answer: 1
},
// Additional Questions 26-40
{
id: 26,
text: "Direct speech in narrative often uses...",
options: ["Quotation marks", "Brackets", "Bold text", "Exclamation marks only"],
answer: 0
},
{
id: 27,
text: "Which one is an example of a legend from East Java?",
options: ["The Legend of Toba Lake", "The Legend of Banyuwangi", "The Legend of Prambanan", "The Legend of Batu Menangis"],
answer: 1
},
{
id: 28,
text: "What is the opposite of 'Protagonist' in a story?",
options: ["Sidekick", "Antagonist", "Deuteragonist", "Narrator"],
answer: 1
},
{
id: 29,
text: "The phrase 'Long ago' usually indicates...",
options: ["Present time", "Future time", "Indefinite time in the past", "Specific date"],
answer: 2
},
{
id: 30,
text: "Characters in a Fable are usually...",
options: ["Humans", "Plants", "Animals", "Inanimate objects"],
answer: 2
},
{
id: 31,
text: "Which linguistic feature is used to sequence events?",
options: ["Adjectives", "Nouns", "Conjunctions of time", "Modal verbs"],
answer: 2
},
{
id: 32,
text: "A story about gods or how the world began is a...",
options: ["Fable", "Legend", "Myth", "Short Story"],
answer: 2
},
{
id: 33,
text: "The climax of a story usually happens in the...",
options: ["Orientation", "Complication", "Resolution", "Introduction"],
answer: 1
},
{
id: 34,
text: "Choose the correct past tense form: 'The hunter ___ the deer.'",
options: ["Catch", "Catches", "Caught", "Catching"],
answer: 2
},
{
id: 35,
text: "What is the purpose of 'Re-orientation'?",
options: ["To introduce new characters", "To give a summary or personal comment", "To start a new conflict", "To describe the setting"],
answer: 1
},
{
id: 36,
text: "Snow White ran into the woods. The word 'woods' means...",
options: ["Beach", "River", "Forest", "Mountain"],
answer: 2
},
{
id: 37,
text: "Which word is a mental verb?",
options: ["Ran", "Thought", "Killed", "Jumped"],
answer: 1
},
{
id: 38,
text: "An example of a 'Moral Value' in Toba Lake is...",
options: ["Don't go fishing", "Always keep your promise", "Fish can turn into women", "Lakes are beautiful"],
answer: 1
},
{
id: 39,
text: "In the story 'The Tortoise and the Hare', the Hare lost because he was...",
options: ["Slow", "Arrogant and lazy", "Sick", "Afraid"],
answer: 1
},
{
id: 40,
text: "Narrative text structure consists of...",
options: ["Identification - Description", "Orientation - Complication - Resolution", "Thesis - Argument - Reiteration", "Goal - Material - Steps"],
answer: 1
}
];
const App = () => {
const [user, setUser] = useState(null);
const [studentInfo, setStudentInfo] = useState({ name: '', classId: '' });
const [examStarted, setExamStarted] = useState(false);
const [examFinished, setExamFinished] = useState(false);
const [currentIdx, setCurrentIdx] = useState(0);
const [answers, setAnswers] = useState(new Array(QUESTIONS_DATA.length).fill(null));
const [timeLeft, setTimeLeft] = useState(3600); // 60 minutes in seconds
const [isSubmitting, setIsSubmitting] = useState(false);
const [showGrid, setShowGrid] = useState(false);
const [score, setScore] = useState(0);
// Auth initialization
useEffect(() => {
const initAuth = async () => {
try {
if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) {
await signInWithCustomToken(auth, __initial_auth_token);
} else {
await signInAnonymously(auth);
}
} catch (err) {
console.error("Auth Error", err);
}
};
initAuth();
const unsubscribe = onAuthStateChanged(auth, setUser);
return () => unsubscribe();
}, []);
// Timer Logic
useEffect(() => {
let timer;
if (examStarted && !examFinished && timeLeft > 0) {
timer = setInterval(() => {
setTimeLeft(prev => prev - 1);
}, 1000);
} else if (timeLeft === 0 && !examFinished) {
handleSubmit();
}
return () => clearInterval(timer);
}, [examStarted, examFinished, timeLeft]);
const formatTime = (seconds) => {
const m = Math.floor(seconds / 60);
const s = seconds % 60;
return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
};
const handleStartExam = () => {
if (studentInfo.name.trim() && studentInfo.classId.trim()) {
setExamStarted(true);
}
};
const handleSelectOption = (optIdx) => {
const newAnswers = [...answers];
newAnswers[currentIdx] = optIdx;
setAnswers(newAnswers);
};
const calculateFinalScore = () => {
let correct = 0;
answers.forEach((ans, idx) => {
if (ans === QUESTIONS_DATA[idx].answer) {
correct++;
}
});
return (correct / QUESTIONS_DATA.length) * 100;
};
const handleSubmit = async () => {
if (isSubmitting) return;
setIsSubmitting(true);
const finalScore = calculateFinalScore();
setScore(finalScore);
if (user) {
try {
await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'exam_results'), {
studentName: studentInfo.name,
class: studentInfo.classId,
score: finalScore,
answers: answers,
timeTaken: 3600 - timeLeft,
submittedAt: serverTimestamp(),
userId: user.uid
});
} catch (e) {
console.error("Error saving results:", e);
}
}
setExamFinished(true);
setIsSubmitting(false);
};
if (!examStarted) {
return (