26
import React, { useState, useEffect, useRef } from ‘react’;
import {
BookOpen,
Lock,
Unlock,
ChevronLeft,
CheckCircle2,
XCircle,
ArrowRight,
RotateCcw,
Trophy,
Timer
} from ‘lucide-react’;
// — CONFIGURATION —
const QUIZ_CONFIG = {
SECONDS_PER_QUESTION: 30,
PASSING_PERCENTAGE: 80,
QUESTIONS_PER_LEVEL: 10
};
// — QUESTION BANK DATA —
// Maaari kang magdagdag ng UNLIMITED subjects dito.
const QUESTION_BANK = {
“General Education: English”: [
{ id: 1, q: “Which of the following is an example of a proper noun?”, o: [“City”, “Country”, “Manila”, “Ocean”], a: “Manila”, r: “Proper nouns name specific people, places, or things and always start with a capital letter.” },
{ id: 2, q: “Identify the figure of speech: ‘The stars danced playfully in the moonlit sky.'”, o: [“Simile”, “Metaphor”, “Personification”, “Hyperbole”], a: “Personification”, r: “Giving human qualities to non-human objects is personification.” },
{ id: 3, q: “What is the synonym of ‘Abundant’?”, o: [“Scarce”, “Plentiful”, “Rare”, “Limited”], a: “Plentiful”, r: “Abundant means existing or available in large quantities.” },
{ id: 4, q: “Choose the correct verb: ‘Neither the teacher nor the students ___ here.'”, o: [“is”, “are”, “am”, “was”], a: “are”, r: “In ‘neither/nor’ structures, the verb agrees with the closer subject (students).” },
{ id: 5, q: “Who is the author of ‘The Raven’?”, o: [“Mark Twain”, “Edgar Allan Poe”, “Ernest Hemingway”, “William Shakespeare”], a: “Edgar Allan Poe”, r: “Edgar Allan Poe is famous for his dark, gothic poetry like ‘The Raven’.” },
],
“General Education: Mathematics”: [
{ id: 101, q: “What is the value of 2^5?”, o: [“10”, “16”, “32”, “64”], a: “32”, r: “2 x 2 x 2 x 2 x 2 = 32.” },
{ id: 102, q: “What is the square root of 144?”, o: [“10”, “11”, “12”, “13”], a: “12”, r: “12 times 12 is 144.” },
],
“Professional Education”: [
{ id: 201, q: “Which philosopher is known for ‘Tabula Rasa’?”, o: [“Jean Piaget”, “John Locke”, “Erik Erikson”, “Lev Vygotsky”], a: “John Locke”, r: “John Locke proposed that the mind is a ‘blank slate’ at birth.” },
],
“Filipino”: [
{ id: 301, q: “Sino ang kinikilalang Ama ng Wikang Pambansa?”, o: [“Jose Rizal”, “Manuel L. Quezon”, “Andres Bonifacio”, “Apolinario Mabini”], a: “Manuel L. Quezon”, r: “Si Manuel L. Quezon ang nagsulong na magkaroon ng isang wikang pambansa ang Pilipinas.” },
],
“Social Science”: [
{ id: 401, q: “Who founded the Katipunan?”, o: [“Jose Rizal”, “Andres Bonifacio”, “Emilio Aguinaldo”, “Antonio Luna”], a: “Andres Bonifacio”, r: “Andres Bonifacio is the ‘Supremo’ of the KKK.” },
],
“Biological Science”: [
{ id: 501, q: “What is the powerhouse of the cell?”, o: [“Nucleus”, “Ribosome”, “Mitochondria”, “Golgi Body”], a: “Mitochondria”, r: “Mitochondria generate most of the chemical energy needed to power the cell’s biochemical reactions.” },
]
};
const shuffle = (array) => […array].sort(() => Math.random() – 0.5);
const App = () => {
const [currentSubject, setCurrentSubject] = useState(null);
const [view, setView] = useState(‘subjects’);
const [unlockedLevels, setUnlockedLevels] = useState({});
const [timeLeft, setTimeLeft] = useState(QUIZ_CONFIG.SECONDS_PER_QUESTION);
const [currentQuiz, setCurrentQuiz] = useState({
level: 0,
questions: [],
currentIndex: 0,
answers: [],
showRatio: false
});
const [finalResult, setFinalResult] = useState(null);
const timerRef = useRef(null);
// Load progress
useEffect(() => {
const savedProgress = localStorage.getItem(‘prc_reviewer_progress’);
if (savedProgress) {
setUnlockedLevels(JSON.parse(savedProgress));
} else {
const initial = {};
Object.keys(QUESTION_BANK).forEach(sub => {
initial[sub] = 1;
});
setUnlockedLevels(initial);
}
}, []);
// Save progress
useEffect(() => {
if (Object.keys(unlockedLevels).length > 0) {
localStorage.setItem(‘prc_reviewer_progress’, JSON.stringify(unlockedLevels));
}
}, [unlockedLevels]);
// Timer logic
useEffect(() => {
if (view === ‘quiz’ && !currentQuiz.showRatio && timeLeft > 0) {
timerRef.current = setInterval(() => {
setTimeLeft(prev => prev – 1);
}, 1000);
} else if (timeLeft === 0 && !currentQuiz.showRatio) {
handleAnswer(null);
}
return () => clearInterval(timerRef.current);
}, [view, timeLeft, currentQuiz.showRatio]);
const startQuiz = (subject, level) => {
const pool = QUESTION_BANK[subject] || [];
const shuffledPool = shuffle(pool);
const selectedCount = Math.min(shuffledPool.length, QUIZ_CONFIG.QUESTIONS_PER_LEVEL);
const selected = shuffledPool.slice(0, selectedCount).map(q => ({
…q,
shuffledOptions: shuffle(q.o)
}));
setCurrentQuiz({
level,
questions: selected,
currentIndex: 0,
answers: [],
showRatio: false
});
setTimeLeft(QUIZ_CONFIG.SECONDS_PER_QUESTION);
setCurrentSubject(subject);
setView(‘quiz’);
};
const handleAnswer = (option) => {
clearInterval(timerRef.current);
const currentQ = currentQuiz.questions[currentQuiz.currentIndex];
const isCorrect = option === currentQ.a;
const newAnswer = {
questionId: currentQ.id,
selected: option,
isCorrect,
ratio: currentQ.r,
correctAnswer: currentQ.a
};
setCurrentQuiz(prev => ({
…prev,
answers: […prev.answers, newAnswer],
showRatio: true
}));
};
const nextQuestion = () => {
if (currentQuiz.currentIndex < currentQuiz.questions.length - 1) {
setCurrentQuiz(prev => ({
…prev,
currentIndex: prev.currentIndex + 1,
showRatio: false
}));
setTimeLeft(QUIZ_CONFIG.SECONDS_PER_QUESTION);
} else {
finishQuiz();
}
};
const finishQuiz = () => {
const score = currentQuiz.answers.filter(a => a.isCorrect).length;
const total = currentQuiz.questions.length;
const percentage = total > 0 ? (score / total) * 100 : 0;
const passed = percentage >= QUIZ_CONFIG.PASSING_PERCENTAGE;
if (passed && currentQuiz.level === (unlockedLevels[currentSubject] || 1)) {
setUnlockedLevels(prev => ({
…prev,
[currentSubject]: (prev[currentSubject] || 1) + 1
}));
}
setFinalResult({ score, total, percentage, passed });
setView(‘result’);
};
const resetProgress = () => {
const confirmReset = window.confirm(“Sigurado ka ba? Mabubura ang lahat ng unlocked tables mo.”);
if (confirmReset) {
const initial = {};
Object.keys(QUESTION_BANK).forEach(sub => {
initial[sub] = 1;
});
setUnlockedLevels(initial);
localStorage.removeItem(‘prc_reviewer_progress’);
}
};
const SubjectCard = ({ name }) => (
);
const LevelTable = () => {
const levels = Array.from({ length: 10 }, (_, i) => i + 1);
const maxUnlocked = unlockedLevels[currentSubject] || 1;
return (
);
};
const QuizScreen = () => {
const q = currentQuiz.questions[currentQuiz.currentIndex];
const hasAnswered = currentQuiz.showRatio;
const currentAnswer = currentQuiz.answers[currentQuiz.currentIndex];
if (!q) return null;
return (
)}
);
};
const ResultScreen = () => (
);
return (
{view === ‘subjects’ && (
Progress Auto-Saved
)}
{view === ‘levels’ && }
{view === ‘quiz’ && }
{view === ‘result’ && }
);
};
export default App;
{currentSubject}
Pumili ng Table. Kailangan ng {QUIZ_CONFIG.PASSING_PERCENTAGE}% para ma-unlock ang susunod.
{levels.map(lvl => {
const isLocked = lvl > maxUnlocked;
return (
);
})}
Table {currentQuiz.level}
{timeLeft}s
Tanong {currentQuiz.currentIndex + 1} / {currentQuiz.questions.length}
{q.q}
{q.shuffledOptions.map((opt, idx) => {
let btnClass = “w-full p-4 text-left rounded-xl border-2 transition-all font-medium flex justify-between items-center “;
if (!hasAnswered) {
btnClass += “border-slate-100 hover:border-blue-500 hover:bg-blue-50 bg-white shadow-sm”;
} else {
if (opt === q.a) {
btnClass += “border-green-500 bg-green-50 text-green-700 shadow-sm”;
} else if (currentAnswer?.selected === opt && !currentAnswer.isCorrect) {
btnClass += “border-red-500 bg-red-50 text-red-700 shadow-sm”;
} else {
btnClass += “border-slate-100 bg-white opacity-40 shadow-none”;
}
}
return (
);
})}
{hasAnswered && (
{currentAnswer.selected === null ? ‘Time Out!’ : currentAnswer.isCorrect ? ‘Tama!’ : ‘Mali!’} Rationale:
{q.r}
{finalResult.passed ? : }
{finalResult.passed ? ‘Magaling!’ : ‘Subukan Muli’}
{finalResult.passed ? `Mahusay! Bukas na ang Table ${currentQuiz.level + 1}.` : `Kailangan ng kahit ${QUIZ_CONFIG.PASSING_PERCENTAGE}% para makapasa.`}
Score
{finalResult.score} / {finalResult.total}
Grade
{Math.round(finalResult.percentage)}%
{finalResult.passed && (
)}
Review Dashboard
Pumili ng subject sa ibaba para magsimula.
{Object.keys(QUESTION_BANK).map(sub => (
))}

