import React, { useState, useEffect, useRef } from ‘react’;
import {
ArrowRight, ArrowLeft, Thermometer, Anchor, TrendingUp,
TrendingDown, Award, Volume2, VolumeX, CheckCircle,
XCircle, Calculator, BookOpen, Brain, Play, RefreshCw,
Coins, Building, Zap, Target, Lock, Unlock, Key, Lightbulb, Info,
ChevronRight, Check, HelpCircle, List, Edit3, Link, CheckSquare, FileText
} from ‘lucide-react’;
// ==========================================
// — KONFIGURASI PIN GURU —
const TEACHER_PIN = “1234”;
// ==========================================
/**
* UTILITIES: AUDIO SYNTHESIZER
*/
const playSound = (type) => {
try {
const AudioContext = window.AudioContext || window.webkitAudioContext;
if (!AudioContext) return;
const ctx = new AudioContext();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
const now = ctx.currentTime;
if (type === ‘correct’ || type === ‘step_success’) {
osc.type = ‘sine’;
osc.frequency.setValueAtTime(600, now);
osc.frequency.exponentialRampToValueAtTime(1000, now + 0.1);
gain.gain.setValueAtTime(0.3, now);
gain.gain.linearRampToValueAtTime(0, now + 0.5);
osc.start(now);
osc.stop(now + 0.5);
} else if (type === ‘wrong’) {
osc.type = ‘sawtooth’;
osc.frequency.setValueAtTime(300, now);
osc.frequency.linearRampToValueAtTime(100, now + 0.3);
gain.gain.setValueAtTime(0.3, now);
gain.gain.linearRampToValueAtTime(0, now + 0.5);
osc.start(now);
osc.stop(now + 0.5);
} else if (type === ‘click’) {
osc.type = ‘triangle’;
osc.frequency.setValueAtTime(800, now);
gain.gain.setValueAtTime(0.05, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.1);
osc.start(now);
osc.stop(now + 0.1);
} else if (type === ‘melt’) {
osc.type = ‘sawtooth’;
osc.frequency.setValueAtTime(100, now);
gain.gain.setValueAtTime(0.2, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.8);
osc.start(now);
osc.stop(now + 0.8);
}
} catch (e) {}
};
/**
* DATA PEDAGOGI
*/
const PEDAGOGY_DATA = [
{ id: 0, title: “Konsep Bilangan”, meaning: “Memahami posisi dan nilai relatif benda.”, prereq: “Bilangan cacah”, objective: “Siswa mampu membedakan bilangan positif, negatif, dan nol.”, quote: “Matematika adalah bahasa alam semesta.” },
{ id: 1, title: “Membandingkan”, meaning: “Dasar pengambilan keputusan.”, prereq: “Garis bilangan”, objective: “Siswa mampu mengurutkan bilangan bulat.”, quote: “Bandingkan dirimu dengan dirimu kemarin.” },
{ id: 2, title: “Penjumlahan & Pengurangan”, meaning: “Menghitung perubahan nilai.”, prereq: “Nilai tempat”, objective: “Siswa mampu melakukan operasi tambah kurang.”, quote: “Kesalahan adalah bukti kamu mencoba.” },
{ id: 3, title: “Perkalian & Pembagian”, meaning: “Menghitung kelipatan.”, prereq: “Perkalian dasar”, objective: “Siswa menguasai aturan tanda kali/bagi.”, quote: “Pola adalah inti matematika.” },
{ id: 4, title: “Hitung Campuran”, meaning: “Menyelesaikan masalah kompleks.”, prereq: “4 operasi dasar”, objective: “Siswa menerapkan KaBaTaKu.”, quote: “Keteraturan membawa kejelasan.” },
{ id: 5, title: “Penerapan Nyata”, meaning: “Problem solving sehari-hari.”, prereq: “Semua operasi”, objective: “Siswa memodelkan masalah nyata.”, quote: “Belajar untuk hidup.” }
];
const PedagogyHeader = ({ data }) => (
{data.title}
“{data.quote}”
Prasyarat: {data.prereq}
🎯 Tujuan:{data.objective}
);
const ModeToggle = ({ mode, setMode }) => (
);
// — GUIDED SOLVER (UPDATED: Check Button Below Input) —
const GuidedSolver = ({ question, steps, onComplete }) => {
const [currentStep, setCurrentStep] = useState(0);
const [inputVal, setInputVal] = useState(“”);
const [status, setStatus] = useState(“idle”);
const [history, setHistory] = useState([]);
const scrollRef = useRef(null);
const rawStep = steps[currentStep];
const step = typeof rawStep === ‘string’
? { instruction: rawStep, type: ‘info’, answer: ‘ok’ }
: rawStep;
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTo({ top: scrollRef.current.scrollHeight, behavior: ‘smooth’ });
}
}, [currentStep, history, status]);
const checkAnswer = (val) => {
if (step.type === ‘info’) {
playSound(‘click’); handleNext(step.instruction); return;
}
const cleanVal = String(val).trim().toLowerCase();
const correctVal = String(step.answer).trim().toLowerCase();
if (cleanVal === correctVal) {
playSound(‘step_success’); setStatus(“correct”); handleNext(val);
} else {
playSound(‘wrong’); setStatus(“wrong”);
}
};
const handleNext = (userAns) => {
setHistory([…history, { instruction: step.instruction, userAnswer: userAns }]);
setTimeout(() => {
if (currentStep < steps.length - 1) {
setCurrentStep(currentStep + 1); setInputVal(""); setStatus("idle");
} else {
onComplete();
}
}, step.type === 'info' ? 300 : 1500);
};
if (currentStep >= steps.length) return (
Pembahasan Selesai!
Kamu telah menyelesaikan langkah-langkahnya.
);
return (
{/* HEADER: PERTANYAAN */}
{/* BODY: STEPS FLOW */}
{/* History Items */}
{history.map((h, i) => (
{h.instruction}
{h.userAnswer === ‘ok’ ? ‘Lanjut’ : h.userAnswer}
))}
{/* Current Step Card */}
Langkah {currentStep + 1}
{step.instruction}
{/* In-Context Feedback */}
{status === ‘wrong’ && (
Kurang tepat, coba lagi.
)}
{status === ‘correct’ && (
Benar!
)}
{/* FOOTER: INPUT AREA (OUTSIDE THE BOX – STACKED VERTICALLY) */}
{step.type === ‘info’ ? (
) : step.type === ‘select’ ? (
{step.options.map((opt, idx) => (
))}
) : (
)}
);
};
const TeacherModal = ({ isOpen, onClose, question, steps }) => {
const [pin, setPin] = useState(“”);
const [isUnlocked, setIsUnlocked] = useState(false);
useEffect(() => { if(!isOpen) { setPin(“”); setIsUnlocked(false); } }, [isOpen]);
const handleUnlock = () => { if (pin === TEACHER_PIN) { setIsUnlocked(true); playSound(‘correct’); } else { playSound(‘wrong’); setPin(“”); } };
if (!isOpen) return null;
return (
{isUnlocked ? : } Pembahasan Terbimbing
{!isUnlocked ? (
Mode Guru Terkunci
Masukkan PIN untuk membuka panduan.
setPin(e.target.value)} maxLength={4} className=”text-center text-4xl font-bold border-b-4 border-slate-200 w-40 focus:border-indigo-500 outline-none pb-2 tracking-[0.5em] bg-transparent text-slate-800″ placeholder=”••••”/>
) : (
{}} />
)}
);
};
// — QUIZ CARD (FOR EXPLORE MODE) —
const QuizCard = ({ data, next, onCorrect, onWrong }) => {
const [feedback, setFeedback] = useState(null);
const [showSolution, setShowSolution] = useState(false);
useEffect(() => { setFeedback(null); setShowSolution(false); }, [data]);
if(!data) return
Loading…
;
const check = (val) => {
const correct = val === data.ans || (data.custom && val === data.ans);
if (correct) { setFeedback(‘correct’); playSound(‘correct’); if(onCorrect) onCorrect(); }
else { setFeedback(‘wrong’); playSound(‘wrong’); if(onWrong) onWrong(); }
};
const generateOptions = (ans) => {
const opts = new Set([ans]);
while(opts.size < 4) { opts.add(ans + (Math.floor(Math.random() * 10) - 5) || ans + 1); }
return Array.from(opts).sort(() => Math.random() – 0.5);
};
return (
{data.text}
{data.options.map((opt, i) => ())}
{feedback && (
{feedback === ‘correct’ ? <> Benar! 🎉> : <> Kurang Tepat 🤔>}
)}
setShowSolution(false)} question={data.text} steps={data.steps || []} />
);
};
// — TOPIC COMPONENTS (FULL INTERACTIVE) —
const TopicConcept = () => {
const [mode, setMode] = useState(‘explore’);
const [context, setContext] = useState(‘building’);
const [val, setVal] = useState(0);
const [quizData, setQuizData] = useState(null);
const genQuiz = () => {
const n = Math.floor(Math.random() * 20) – 10;
let text, guidedSteps;
if (n > 0) {
text = `Penyelam berada di ketinggian ${n} meter di atas laut. Ditulis…`;
guidedSteps = [{ instruction: `Kata “di atas laut” positif atau negatif? (Ketik + atau -)`, answer: “+”, type: “select”, options: [“+”, “-“] }, { instruction: `Berapa angkanya?`, answer: n, type: “input” }];
} else if (n < 0) {
text = `Suhu ${Math.abs(n)} derajat di bawah nol. Ditulis...`;
guidedSteps = [{ instruction: `Kata "di bawah nol" positif atau negatif? (Ketik + atau -)`, answer: "-", type: "select", options: ["+", "-"] }, { instruction: `Tulis angkanya dengan tanda (-):`, answer: n, type: "input" }];
} else {
text = "Permukaan laut sebagai titik acuan, nilainya...";
guidedSteps = [{ instruction: "Titik netral adalah...", answer: 0, type: "input" }];
}
setQuizData({ text, ans: n, options: [n, -n, n+1, n-1].sort(()=>Math.random()-.5), steps: guidedSteps });
};
useEffect(() => { if(mode === ‘quiz’) genQuiz(); }, [mode]);
return (
{mode === ‘explore’ ? (
{context === ‘building’ ? (
) : (
= 0 ? ‘bg-red-500’ : ‘bg-blue-500’}`} style={{ height: `${((val + 10) / 20) * 100}%` }}>
{val}°C
)}
setVal(parseInt(e.target.value))} className=”w-full accent-blue-600 h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer”/>
{val}
{val > 0 ? “Positif: Di atas nol / Kanan” : val < 0 ? "Negatif: Di bawah nol / Kiri" : "Nol: Netral"}
) : (
)}
);
};
const TopicCompare = () => {
const [mode, setMode] = useState(‘explore’);
const [a, setA] = useState(5);
const [b, setB] = useState(-3);
const [result, setResult] = useState(null);
const [quizData, setQuizData] = useState(null);
const genQuiz = () => {
const x = Math.floor(Math.random() * 20) – 10;
const y = Math.floor(Math.random() * 20) – 10;
if(x === y) { genQuiz(); return; }
const guidedSteps = [{ instruction: `Mana yang lebih KANAN di garis bilangan?`, answer: Math.max(x, y), type: “select”, options: [x, y] }, { instruction: `Maka tanda yang tepat:`, answer: x > y ? “>” : “<", type: "select", options: [">“, “<"] }];
setQuizData({ text: `Tanda yang tepat: ${x} ... ${y}`, ans: x > y ? ‘>’ : ‘<', options: ['>‘, ‘<', '='], custom: true, steps: guidedSteps });
};
useEffect(() => { if(mode === ‘quiz’) genQuiz(); }, [mode]);
const checkExplore = (op) => { const correct = op === ‘>’ ? a > b : op === ‘<' ? a < b : a === b; setResult(correct ? 'correct' : 'wrong'); playSound(correct ? 'correct' : 'wrong'); };
return (
{mode === ‘explore’ ? (
🐊 Prinsip Buaya Lapar
Buaya selalu makan yang lebih BESAR (Kanan).
{a}
{b}
) : (
)}
);
};
const TopicAddSub = () => {
const [mode, setMode] = useState(‘explore’);
const [num1, setNum1] = useState(3);
const [num2, setNum2] = useState(-2);
const [isAnimating, setIsAnimating] = useState(false);
const [displayCoins, setDisplayCoins] = useState([]);
const [finalResult, setFinalResult] = useState(null);
const [quizData, setQuizData] = useState(null);
const genQuiz = () => {
const a = Math.floor(Math.random() * 20) – 10;
const b = Math.floor(Math.random() * 20) – 10;
const sameSign = (a >= 0 && b >= 0) || (a < 0 && b < 0);
let guidedSteps = [];
if (sameSign) {
guidedSteps = [{ instruction: `Tanda sama. Jumlahkan angkanya: ${Math.abs(a)} + ${Math.abs(b)}`, answer: Math.abs(a)+Math.abs(b), type: "input" }, { instruction: `Tandanya ikut soal (${a>=0?’+’:’-‘}). Hasil:`, answer: a+b, type: “input” }];
} else {
guidedSteps = [{ instruction: `Tanda beda. Cari selisih (Besar – Kecil):`, answer: Math.abs(Math.abs(a)-Math.abs(b)), type: “input” }, { instruction: `Angka yang lebih besar (${Math.abs(a)>Math.abs(b)?Math.abs(a):Math.abs(b)}) tandanya apa?`, answer: Math.abs(a)>Math.abs(b)?(a>0?”+”:”-“):(b>0?”+”:”-“), type: “select”, options: [“+”, “-“] }, { instruction: `Maka hasil akhirnya:`, answer: a+b, type: “input” }];
}
setQuizData({ text: `Hasil dari ${a} + (${b}) adalah…`, ans: a + b, options: [a+b, a-b, b-a, -a-b].sort(()=>Math.random()-.5), steps: guidedSteps });
};
useEffect(() => { if(mode === ‘quiz’) genQuiz(); }, [mode]);
useEffect(() => { resetCoins(); }, [num1, num2]);
const resetCoins = () => {
setFinalResult(null);
const coins = [];
let id = 0;
for (let i = 0; i < Math.abs(num1); i++) coins.push({ id: id++, type: num1 >= 0 ? ‘pos’ : ‘neg’, state: ‘idle’ });
for (let i = 0; i < Math.abs(num2); i++) coins.push({ id: id++, type: num2 >= 0 ? ‘pos’ : ‘neg’, state: ‘idle’ });
setDisplayCoins(coins);
};
const startMelting = () => {
if (isAnimating) return;
setIsAnimating(true);
playSound(‘click’);
const positives = displayCoins.filter(c => c.type === ‘pos’);
const negatives = displayCoins.filter(c => c.type === ‘neg’);
const pairsCount = Math.min(positives.length, negatives.length);
const allMeltIds = new Set([…positives.slice(0, pairsCount).map(c => c.id), …negatives.slice(0, pairsCount).map(c => c.id)]);
setDisplayCoins(prev => prev.map(c => allMeltIds.has(c.id) ? { …c, state: ‘moving’ } : c));
setTimeout(() => { if(pairsCount > 0) playSound(‘melt’); setDisplayCoins(prev => prev.map(c => allMeltIds.has(c.id) ? { …c, state: ‘melted’ } : c)); }, 800);
setTimeout(() => { setDisplayCoins(prev => prev.filter(c => c.state !== ‘melted’)); setFinalResult(num1 + num2); setIsAnimating(false); if(pairsCount > 0) playSound(‘correct’); }, 1800);
};
return (
{mode === ‘explore’ ? (
Metode Koin: Pasangan Positif (+) dan Negatif (-) akan saling menetralkan (lebur) menjadi NOL.
{finalResult !== null && !isAnimating &&
Sisa Koin: {finalResult}
}
{displayCoins.map((c) => (
{c.type === ‘pos’ ? ‘+’ : ‘-‘}
))}
) : (
)}
);
};
const TopicMulDiv = () => {
const [mode, setMode] = useState(‘explore’);
const [a, setA] = useState(3);
const [b, setB] = useState(-2);
const [quizData, setQuizData] = useState(null);
const genQuiz = () => {
const op = Math.random() > 0.5 ? ‘x’ : ‘:’;
let x = Math.floor(Math.random() * 9) + 2;
let y = Math.floor(Math.random() * 9) + 2;
if(Math.random() > 0.5) x = -x; if(Math.random() > 0.5) y = -y;
let ans = (op === ‘x’) ? x * y : x; if (op === ‘:’) x = x * y;
const sameSign = (x>=0 && y>=0) || (x<0 && y<0);
const guidedSteps = [
{ instruction: `Tanda sama atau beda?`, answer: sameSign ? "Sama" : "Beda", type: "select", options: ["Sama", "Beda"] },
{ instruction: `Maka hasilnya positif atau negatif?`, answer: sameSign ? "Positif" : "Negatif", type: "select", options: ["Positif", "Negatif"] },
{ instruction: `Hitung angkanya: ${Math.abs(x)} ${op==='x'?'x':':'} ${Math.abs(y)}`, answer: Math.abs(ans), type: "input" },
{ instruction: `Jawaban akhir:`, answer: ans, type: "input" }
];
setQuizData({ text: `${x} ${op === 'x' ? '×' : ':'} (${y}) = ...`, ans, options: [ans, -ans, ans+1, ans-1].sort(()=>Math.random()-.5), steps: guidedSteps });
};
useEffect(() => { if(mode === ‘quiz’) genQuiz(); }, [mode]);
return (
{mode === ‘explore’ ? (
) : (
)}
);
};
const TopicMixed = () => {
const [mode, setMode] = useState(‘explore’);
const [step, setStep] = useState(0);
const [quizData, setQuizData] = useState(null);
const stepsDemo = [{ t: “Soal: -5 + 3 × 4”, res: “-5 + 3 × 4”, hl: “” }, { t: “1. Prioritas Perkalian (3 x 4)”, res: “-5 + 12”, hl: “Kerjakan dulu kali/bagi sebelum tambah/kurang” }, { t: “2. Penjumlahan (-5 + 12)”, res: “7”, hl: “Sisa hutang 5 dibayar 12” }];
const genQuiz = () => {
const a = Math.floor(Math.random()*5)+2; const b = Math.floor(Math.random()*5)+2; const c = Math.floor(Math.random()*5)+2;
const ans = a + (b * (-c));
const guidedSteps = [{ instruction: `KaBaTaKu: Perkalian dikerjakan dulu. ${b} x (-${c}) = …`, answer: b*(-c), type: “input” }, { instruction: `Sekarang jumlahkan: ${a} + (${b*-c}) = …`, answer: ans, type: “input” }];
setQuizData({ text: `Hitunglah: ${a} + ${b} × ${-c}`, ans: ans, options: [ans, ans+5, ans-5, -ans].sort(()=>Math.random()-.5), steps: guidedSteps });
};
useEffect(() => { if(mode === ‘quiz’) genQuiz(); }, [mode]);
return (
{mode === ‘explore’ ? (
Demo KaBaTaKu
{stepsDemo[step].res}
{stepsDemo[step].t}
{stepsDemo[step].hl}
{stepsDemo.map((_, i) =>
)}
) : (
)}
);
};
const TopicRealWorld = () => {
const [mode, setMode] = useState(‘explore’);
const [subMode, setSubMode] = useState(‘money’);
const [money, setMoney] = useState(0);
const [depth, setDepth] = useState(-50);
const [quizData, setQuizData] = useState(null);
const [history, setHistory] = useState([]);
const genQuiz = () => {
const scenarios = [
{ t: “Suhu -3°C, turun 5°C. Suhu sekarang?”, a: -8, s: [{instruction:”Mula-mula:”, answer:-3, type:”input”}, {instruction:”Turun 5 artinya -5. Hitung -3 – 5:”, answer:-8, type:”input”}] },
{ t: “Kapal di -20m, naik 5m. Posisi?”, a: -15, s: [{instruction:”Posisi awal:”, answer:-20, type:”input”}, {instruction:”Naik 5 artinya +5. Hitung -20 + 5:”, answer:-15, type:”input”}] }
];
const sc = scenarios[Math.floor(Math.random()*scenarios.length)];
setQuizData({ text: sc.t, ans: sc.a, options: [sc.a, sc.a+2, sc.a-2, -sc.a].sort(()=>Math.random()-.5), steps: sc.s });
};
useEffect(() => { if(mode === ‘quiz’) genQuiz(); }, [mode]);
const addTrans = (amount, desc) => { setMoney(money + amount); setHistory(prev => [{id: Date.now(), desc, val: amount}, …prev].slice(0,3)); playSound(amount > 0 ? ‘correct’ : ‘wrong’); };
return (
{mode === ‘explore’ ? (
{subMode === ‘money’ ? (
=0?’text-green-600′:’text-red-600′}`}>Rp {money.toLocaleString()}
Riwayat
{history.map(h => (
{h.desc}0?’text-green-600′:’text-red-600′}>{h.val}
))}
) : (
)}
) : (
)}
);
};
// — BANK SOAL MANAGER (HYBRID) —
const BankSoalManager = () => {
const [activeType, setActiveType] = useState(‘pg’);
const [qData, setQData] = useState(null);
const [feedback, setFeedback] = useState(null);
const [showSolution, setShowSolution] = useState(false);
const [score, setScore] = useState(0);
const generateQuestion = (type) => {
setFeedback(null); setShowSolution(false);
const r = (min, max) => Math.floor(Math.random()*(max-min+1))+min;
let q = { type };
if (type === ‘pg’) {
const subtype = r(1, 3);
if (subtype === 1) {
const a = r(-20, 20), b = r(-20, 20), c = r(-5,5);
q.text = `Hasil dari ${a} – (${b}) + ${c} adalah…`; q.ans = a – b + c; q.options = [q.ans, q.ans+1, q.ans-2, q.ans+10].sort(()=>Math.random()-0.5);
q.steps = [{ instruction: `Langkah 1: Kerjakan pengurangan. ${a} – (${b}) = …`, answer: a-b, type: ‘input’ }, { instruction: `Langkah 2: Tambahkan ${c}. ${a-b} + ${c} = …`, answer: q.ans, type: ‘input’ }];
} else if (subtype === 2) {
const tKulkas = -r(2,8); const tRuang = r(20, 30);
q.text = `Suhu kulkas ${tKulkas}°C. Ruangan ${tRuang}°C. Selisih?`; q.ans = tRuang – tKulkas; q.options = [q.ans, tRuang+tKulkas, q.ans-2, -q.ans].sort(()=>Math.random()-0.5);
q.steps = [{ instruction: “Selisih = Besar – Kecil. Siapa besar?”, answer: tRuang, type: ‘select’, options: [tRuang, tKulkas] }, { instruction: `Hitung: ${tRuang} – (${tKulkas}) = …`, answer: q.ans, type: ‘input’ }];
} else {
q.text = `Lomba: Menang +3, Kalah -1. Menang 5x, kalah 2x. Skor?`; q.ans = 13; q.options = [13, 17, 10, 8].sort(()=>Math.random()-0.5);
q.steps = [{ instruction: “Poin menang: 5 x 3 =”, answer: 15, type: ‘input’ }, { instruction: “Poin kalah: 2 x -1 =”, answer: -2, type: ‘input’ }, { instruction: “Total: 15 + (-2) =”, answer: 13, type: ‘input’ }];
}
} else if (type === ‘complex’) {
q.text = “Pernyataan yang BENAR (Bisa >1)?”; q.ans = [“-5 < 2", "10 > -10″]; q.options = [“-5 < 2", "-5 > 2″, “10 > -10”, “0 < -1"];
q.steps = ["Cek satu per satu.", "-5 ada di kiri 2, jadi -5 < 2 (Benar).", "10 positif, -10 negatif. Positif selalu lebih besar."];
} else if (type === 'input') {
const d = r(50, 200); q.text = `Penyelam di kedalaman ${d} meter. Ditulis...`; q.ans = `-${d}`;
q.steps = [{ instruction: "Di bawah laut berarti negatif. Tulis dengan tanda -", answer: `-${d}`, type: 'input' }];
}
setQData(q);
};
useEffect(() => { generateQuestion(activeType); }, [activeType]);
const handleAnswer = (val) => {
let isCorrect = false;
if (qData.type === ‘pg’) isCorrect = parseInt(val) === parseInt(qData.ans);
else if (qData.type === ‘input’) isCorrect = val === String(qData.ans);
else isCorrect = true;
if (isCorrect) { setFeedback(‘correct’); playSound(‘correct’); setScore(score + 10); }
else { setFeedback(‘wrong’); playSound(‘wrong’); }
};
return (
{[{id:’pg’, icon:List, label:’Pilihan Ganda’}, {id:’complex’, icon:CheckSquare, label:’PG Kompleks’}, {id:’input’, icon:Edit3, label:’Isian’}].map(t => (
))}
Skor: {score}
{qData?.text}
{activeType === ‘pg’ ? (
{qData?.options.map((opt, i) => ())}
) : (
)}
{feedback && (
{feedback===’correct’ ? <> Benar!> : <> Kurang Tepat>}
)}
setShowSolution(false)} question={qData?.text} steps={qData?.steps || []} />
);
};
// — MAIN APP —
const App = () => {
const [activeTab, setActiveTab] = useState(0);
const menu = [
{ id: 0, title: “1. Konsep Bilangan”, icon: Building, component:
},
{ id: 1, title: “2. Membandingkan”, icon: ArrowRight, component:
},
{ id: 2, title: “3. Penjumlahan (+/-)”, icon: Coins, component:
},
{ id: 3, title: “4. Perkalian (x/:)”, icon: Zap, component:
},
{ id: 4, title: “5. Hitung Campuran”, icon: Calculator, component:
},
{ id: 5, title: “6. Penerapan”, icon: Anchor, component:
},
{ id: 6, title: “7. Bank Soal & ANBK”, icon: Brain, component:
},
];
return (
MindMath Pro
Aplikasi Belajar Matematika Lengkap
{menu.map((item, idx) => ())}
{React.createElement(menu[activeTab].icon, { size: 20 })}
{menu[activeTab].title}
{menu[activeTab].component}
);
};
export default App;