Bilangan Bulat

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}

💡 Makna:

{data.meaning}

); 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 */}

Pertanyaan

{question}

{/* BODY: STEPS FLOW */}
{/* History Items */} {history.map((h, i) => (
{h.instruction}
{h.userAnswer === ‘ok’ ? ‘Lanjut’ : h.userAnswer}
))} {/* Current Step Card */}
{currentStep + 1}
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) => ( ))}
) : (
setInputVal(e.target.value)} placeholder=”Ketik jawaban di sini…” className=”w-full p-4 bg-slate-50 border-2 border-slate-200 rounded-xl focus:border-indigo-500 focus:bg-white outline-none font-bold text-lg text-center text-slate-800 placeholder:text-slate-400 transition-all” onKeyDown={(e) => e.key === ‘Enter’ && checkAnswer(inputVal)} autoFocus />
)}
); }; 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’ ? (
Tanah (0)
{val}
) : (
= 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.
setNum1(parseInt(e.target.value)||0)} className=”w-20 p-2 border-2 border-slate-200 rounded text-center”/>+setNum2(parseInt(e.target.value)||0)} className=”w-20 p-2 border-2 border-slate-200 rounded text-center”/>
{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’ ? (

Mesin Pola Perkalian

setA(parseInt(e.target.value)||0)} className=”w-16 text-center border-2 border-slate-200 p-2 rounded-lg font-bold”/>×setB(parseInt(e.target.value)||0)} className=”w-16 text-center border-2 border-slate-200 p-2 rounded-lg font-bold”/>

Aturan Tanda

0 && b>0 ? ‘bg-green-200 font-bold’:”}`}>(+) x (+) = Positif
(-) x (-) = Positif
0 && b<0)||(a<0 && b>0) ? ‘bg-red-200 font-bold’:”}`}>Beda Tanda = Negatif
Hasil Akhir
{a * b}
) : ()}
); }; 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}
))}
) : (
Permukaan (0m)
🚢
setDepth(parseInt(e.target.value))} className=”w-full h-2 bg-slate-300 rounded-lg appearance-none accent-blue-600″/>
Kedalaman: {depth} meter
)}
) : ()}
); }; // — 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) => ())}
) : (
handleAnswer(e.target.value)}/>

*Tekan di luar kotak untuk cek

)} {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}

MindMath Pro © 2025

Created By Amiruddin, S.Pd.

); }; export default App;