Thursday, July 23, 2026

game edukasi pai

import React, { useState, useEffect } from 'react'; class SoundManager { constructor() { this.ctx = null; } init() { if (!this.ctx) { this.ctx = new (window.AudioContext || window.webkitAudioContext)(); } } playCorrect() { this.init(); if (!this.ctx) return; const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); osc.connect(gain); gain.connect(this.ctx.destination); const now = this.ctx.currentTime; osc.type = 'sine'; osc.frequency.setValueAtTime(523.25, now); // C5 osc.frequency.setValueAtTime(659.25, now + 0.1); // E5 osc.frequency.setValueAtTime(783.99, now + 0.2); // G5 gain.gain.setValueAtTime(0, now); gain.gain.linearRampToValueAtTime(0.25, now + 0.05); gain.gain.linearRampToValueAtTime(0, now + 0.35); osc.start(now); osc.stop(now + 0.35); } playWrong() { this.init(); if (!this.ctx) return; const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); osc.connect(gain); gain.connect(this.ctx.destination); const now = this.ctx.currentTime; osc.type = 'sawtooth'; osc.frequency.setValueAtTime(150, now); osc.frequency.linearRampToValueAtTime(95, now + 0.25); gain.gain.setValueAtTime(0, now); gain.gain.linearRampToValueAtTime(0.3, now + 0.05); gain.gain.linearRampToValueAtTime(0, now + 0.3); osc.start(now); osc.stop(now + 0.3); } playLevelUp() { this.init(); if (!this.ctx) return; const now = this.ctx.currentTime; const notes = [523.25, 587.33, 659.25, 698.46, 783.99, 880.00, 987.77, 1046.50]; notes.forEach((freq, idx) => { const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); osc.connect(gain); gain.connect(this.ctx.destination); osc.type = 'triangle'; osc.frequency.setValueAtTime(freq, now + idx * 0.08); gain.gain.setValueAtTime(0, now + idx * 0.08); gain.gain.linearRampToValueAtTime(0.2, now + idx * 0.08 + 0.02); gain.gain.linearRampToValueAtTime(0, now + idx * 0.08 + 0.12); osc.start(now + idx * 0.08); osc.stop(now + idx * 0.08 + 0.15); }); } } const sounds = new SoundManager(); const subjectsList = [ { id: 'PAI', name: 'Pendidikan Agama', icon: '🕌', color: 'border-emerald-500 text-emerald-700' }, { id: 'Pancasila', name: 'Pendidikan Pancasila', icon: '🦅', color: 'border-red-500 text-red-600' }, { id: 'BIndo', name: 'Bahasa Indonesia', icon: '🇮🇩', color: 'border-rose-500 text-rose-600' }, { id: 'Matematika', name: 'Matematika', icon: '🧮', color: 'border-blue-500 text-blue-600' }, { id: 'IPA', name: 'IPA', icon: '🔬', color: 'border-teal-500 text-teal-600' }, { id: 'IPS', name: 'IPS', icon: '🌍', color: 'border-amber-600 text-amber-700' }, { id: 'BInggris', name: 'Bahasa Inggris', icon: '🇬🇧', color: 'border-indigo-500 text-indigo-600' }, { id: 'PJOK', name: 'PJOK', icon: '⚽', color: 'border-green-500 text-green-600' }, { id: 'Informatika', name: 'Informatika', icon: '💻', color: 'border-cyan-500 text-cyan-600' }, { id: 'SeniBudaya', name: 'Seni Budaya', icon: '🎨', color: 'border-fuchsia-500 text-fuchsia-600' }, { id: 'BSunda', name: 'Bahasa Sunda', icon: '🥥', color: 'border-orange-500 text-orange-600' }, { id: 'Jepang', name: 'Jepang', icon: '🇯🇵', color: 'border-pink-500 text-pink-600' }, { id: 'Arab', name: 'Arab', icon: '🗣️', color: 'border-emerald-600 text-emerald-800' }, { id: 'Nahwu', name: 'Nahwu', icon: '📖', color: 'border-violet-500 text-violet-600' }, { id: 'Shorof', name: 'Shorof', icon: '✍️', color: 'border-purple-500 text-purple-600' }, { id: 'Fiqh', name: 'Fiqh', icon: '⚖️', color: 'border-sky-500 text-sky-600' }, { id: 'Tauhid', name: 'Tauhid', icon: '☝️', color: 'border-yellow-600 text-yellow-700' } ]; const fallbackDatabase = { getChallenges: (subject, material) => { return { quiz: [ { question: `Pertanyaan dasar mengenai ${material} pada mata pelajaran ${subject}?`, options: ["Pilihan Jawaban A", "Pilihan Jawaban B", "Pilihan Jawaban C", "Pilihan Jawaban D"], correctAnswer: "Pilihan Jawaban A", explanation: `Ini adalah penjelasan dasar materi ${material} untuk mata pelajaran ${subject}.` }, { question: `Manakah definisi yang paling tepat mengenai aspek krusial dari ${material}?`, options: ["Konsep Pertama", "Konsep Kedua", "Konsep Ketiga", "Konsep Keempat"], correctAnswer: "Konsep Pertama", explanation: `Penjelasan mengenai struktur penting pada pembelajaran ${material}.` } ], classification: { title: `Klasifikasi Istilah ${material}`, categories: [ { id: 'cat1', name: 'Kategori Esensial', desc: 'Aspek inti materi ini.', color: 'bg-emerald-500' }, { id: 'cat2', name: 'Kategori Pendukung', desc: 'Aspek pelengkap materi.', color: 'bg-orange-500' } ], items: [ { char: 'Istilah Kunci A', category: 'cat1', hint: 'Termasuk dalam pilar utama bahasan.' }, { char: 'Istilah Kunci B', category: 'cat2', hint: 'Merupakan elemen pendukung tambahan.' } ] }, arrange: [ { name: `Urutan Definisi ${material}`, correctOrder: ["Belajar", "Dengan", "Gembira", "Pasti", "Bisa"], translation: "Slogan motivasi belajar untuk menguasai materi secara mendalam." } ] }; } }; export default function App() { const [users, setUsers] = useState(() => { const saved = localStorage.getItem('edu_multimapel_users'); if (saved) return JSON.parse(saved); // Seed default simulated accounts for instant testing const defaultUsers = [ { username: "Budi Pratama", phone: "081234567890", joinedAt: new Date(Date.now() - 30 * 60 * 60 * 1000).toISOString(), // Joined 30 hours ago (Trial Expired!) trialExpiresAt: new Date(Date.now() - 6 * 60 * 60 * 1000).toISOString(), isPremium: false, paymentStatus: "none" }, { username: "Aisyah Zahra", phone: "087778626570", joinedAt: new Date().toISOString(), // Joined now (Active 1 Day Trial) trialExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), isPremium: false, paymentStatus: "none" } ]; localStorage.setItem('edu_multimapel_users', JSON.stringify(defaultUsers)); return defaultUsers; }); const [currentUser, setCurrentUser] = useState(() => { const savedSession = sessionStorage.getItem('edu_multimapel_session'); return savedSession ? JSON.parse(savedSession) : null; }); const [isAdmin, setIsAdmin] = useState(() => { return sessionStorage.getItem('edu_multimapel_is_admin') === 'true'; }); // Auth Forms State const [authMode, setAuthMode] = useState('login'); // login, register, admin_login const [loginPhone, setLoginPhone] = useState(''); const [loginPassword, setLoginPassword] = useState(''); const [regName, setRegName] = useState(''); const [regPhone, setRegPhone] = useState(''); const [regPassword] = useState('mbr123'); // Fixed member standard password const [adminUser, setAdminUser] = useState(''); const [adminPass, setAdminPass] = useState(''); // Custom Modal/Alert State (Instead of browser alerts) const [customAlert, setCustomAlert] = useState(null); // { type: 'success'|'error', text: 'string' } // Navigation, Curriculums & Setup Selection State const [setup, setSetup] = useState({ phase: 'Fase A', // Fase A to Fase F subject: 'PAI', material: '' // Hand-filled manually (Kolom materi diisi manual) }); const [isSetupComplete, setIsSetupComplete] = useState(false); const [activeTab, setActiveTab] = useState('menu'); // menu, quiz, classification, arrange, library const [apiLoading, setApiLoading] = useState(false); // Gamification Metrics const [score, setScore] = useState(0); const [streak, setStreak] = useState(0); const [level, setLevel] = useState(1); const [showConfetti, setShowConfetti] = useState(false); const [gameFeedback, setGameFeedback] = useState(null); // { isCorrect: bool, text: string } // Game Core Data State (Loaded dynamically via Gemini API or fallback) const [challenges, setChallenges] = useState(null); const [quizIndex, setQuizIndex] = useState(0); const [currentClassifyItem, setCurrentClassifyItem] = useState(null); const [arrangeIndex, setArrangeIndex] = useState(0); const [scrambledWords, setScrambledWords] = useState([]); const [userSelectionWords, setUserSelectionWords] = useState([]); useEffect(() => { localStorage.setItem('edu_multimapel_users', JSON.stringify(users)); }, [users]); // Handle active session sync useEffect(() => { if (currentUser) { const liveUser = users.find(u => u.phone === currentUser.phone); if (liveUser) { sessionStorage.setItem('edu_multimapel_session', JSON.stringify(liveUser)); } } }, [users, currentUser]); const getSubscriptionStatus = (user) => { if (!user) return { status: 'none', timeLeft: 0 }; if (user.isPremium) { return { status: 'premium', timeLeft: 99999 }; } const timeLeftMs = new Date(user.trialExpiresAt) - new Date(); const hoursLeft = Math.max(0, Math.ceil(timeLeftMs / (1000 * 60 * 60))); return hoursLeft > 0 ? { status: 'trial', hoursLeft } : { status: 'expired_trial', hoursLeft: 0 }; }; const currentStatus = getSubscriptionStatus(currentUser); const isAksesBlocked = currentUser && currentStatus.status === 'expired_trial'; const triggerAlert = (type, text) => { setCustomAlert({ type, text }); setTimeout(() => setCustomAlert(null), 5000); }; const handleRegister = (e) => { e.preventDefault(); if (!regName.trim() || !regPhone.trim()) { triggerAlert('error', 'Harap lengkapi semua kolom pendaftaran.'); return; } const cleanPhone = regPhone.replace(/\D/g, ''); if (users.some(u => u.phone === cleanPhone)) { triggerAlert('error', 'Nomor HP WhatsApp ini telah terdaftar.'); return; } if (regPassword !== 'mbr123') { triggerAlert('error', 'Password standar wajib menggunakan: mbr123'); return; } const newUser = { username: regName.trim(), phone: cleanPhone, joinedAt: new Date().toISOString(), trialExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 1 Day Free Trial isPremium: false, paymentStatus: 'none' }; setUsers([...users, newUser]); triggerAlert('success', 'Pendaftaran Berhasil! Silakan masuk dengan nomor HP Anda.'); setAuthMode('login'); setLoginPhone(cleanPhone); }; const handleLogin = (e) => { e.preventDefault(); const cleanPhone = loginPhone.replace(/\D/g, ''); const user = users.find(u => u.phone === cleanPhone); if (!user) { triggerAlert('error', 'Akun tidak terdaftar. Silakan lakukan pendaftaran baru.'); return; } if (loginPassword !== 'mbr123') { triggerAlert('error', 'Kata sandi salah! Gunakan password standar member: mbr123'); return; } setCurrentUser(user); sessionStorage.setItem('edu_multimapel_session', JSON.stringify(user)); setIsAdmin(false); sessionStorage.removeItem('edu_multimapel_is_admin'); if (setup.material) { setIsSetupComplete(true); } }; const handleAdminLogin = (e) => { e.preventDefault(); if (adminUser === 'admin' && adminPass === 'admin123') { setIsAdmin(true); sessionStorage.setItem('edu_multimapel_is_admin', 'true'); setCurrentUser(null); sessionStorage.removeItem('edu_multimapel_session'); triggerAlert('success', 'Berhasil masuk sebagai Administrator!'); } else { triggerAlert('error', 'Username atau password admin tidak valid.'); } }; const handleLogout = () => { setCurrentUser(null); setIsAdmin(false); sessionStorage.clear(); setIsSetupComplete(false); setActiveTab('menu'); }; const initiatePayment = () => { if (!currentUser) return; const updated = users.map(u => { if (u.phone === currentUser.phone) { return { ...u, paymentStatus: 'pending' }; } return u; }); setUsers(updated); const updatedUserObj = updated.find(u => u.phone === currentUser.phone); setCurrentUser(updatedUserObj); const message = `Halo Admin Game Edukasi, saya ingin aktivasi akses premium seumur hidup (Lifetime) untuk akun: - Nama: ${currentUser.username} - No. HP WA: ${currentUser.phone} - Tagihan: Rp 10.000 (Akses Selamanya) Mohon segera disetujui aktivasinya. Terima kasih!`; const waUrl = `https://wa.me/6287778626570?text=${encodeURIComponent(message)}`; window.open(waUrl, '_blank'); }; // Admin Actions const approveUserPayment = (phone) => { const updated = users.map(u => { if (u.phone === phone) { return { ...u, isPremium: true, paymentStatus: 'approved' }; } return u; }); setUsers(updated); sounds.playLevelUp(); triggerAlert('success', 'Aktivasi premium seumur hidup sukses!'); }; const simulateExpiry = (phone) => { const updated = users.map(u => { if (u.phone === phone) { return { ...u, isPremium: false, paymentStatus: 'none', trialExpiresAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString() // Expired 2 days ago }; } return u; }); setUsers(updated); triggerAlert('success', 'Simulasi kedaluwarsa uji coba berhasil diset!'); }; const deleteUser = (phone) => { setUsers(users.filter(u => u.phone !== phone)); triggerAlert('success', 'Data member berhasil dihapus.'); }; const generateAIGameChallenges = async (e) => { e.preventDefault(); if (!setup.phase || !setup.subject || !setup.material.trim()) { triggerAlert('error', 'Lengkapi seluruh pilihan fase, mapel, dan kolom materi terlebih dahulu.'); return; } setApiLoading(true); const subjectLabel = subjectsList.find(s => s.id === setup.subject)?.name || setup.subject; // AI Generation Prompt const systemPrompt = `Kamu adalah pakar kurikulum Merdeka dan game desainer edukasi anak sekolah dasar hingga menengah di Indonesia. Buat satu set tantangan interaktif menarik dalam bahasa Indonesia untuk siswa tingkat ${setup.phase}, mata pelajaran ${subjectLabel}, fokus pada topik spesifik: "${setup.material}". Hasilkan JSON terstruktur rapi sesuai dengan schema yang ditentukan tanpa teks pengantar di luar JSON.`; const userQuery = `Buatkan konten tantangan dengan format JSON tepat seperti schema ini: { "quiz": [ { "question": "Pertanyaan kuis yang menantang dan edukatif", "options": ["Opsi A", "Opsi B", "Opsi C", "Opsi D"], "correctAnswer": "Opsi A (harus persis sama dengan salah satu nilai di array options)", "explanation": "Penjelasan ilmiah/akademis yang ringkas dan mudah dipahami" } ], "classification": { "title": "Judul Klasifikasi Materi", "categories": [ {"id": "cat1", "name": "Nama Kategori 1", "desc": "Deskripsi singkat Kategori 1"}, {"id": "cat2", "name": "Nama Kategori 2", "desc": "Deskripsi singkat Kategori 2"} ], "items": [ {"char": "Nama benda/konsep/contoh", "category": "cat1", "hint": "Petunjuk mengapa masuk kategori ini"} ] }, "arrange": [ { "name": "Nama tantangan susun kalimat/urutan proses", "correctOrder": ["Kata1", "Kata2", "Kata3", "Kata4"], "translation": "Kalimat utuh atau arti penting dari konsep tersebut" } ] } Sediakan minimal 3 pertanyaan quiz, 4-6 items untuk klasifikasi (dengan 2-3 kategori), dan minimal 2 tantangan arrange.`; try { const apiKey = ""; // Canvas provides API key at runtime if set to empty string const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent?key=${apiKey}`; const payload = { contents: [{ parts: [{ text: userQuery }] }], generationConfig: { responseMimeType: "application/json" }, systemInstruction: { parts: [{ text: systemPrompt }] } }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const result = await response.json(); const generatedText = result.candidates?.[0]?.content?.parts?.[0]?.text; if (generatedText) { const parsedChallenges = JSON.parse(generatedText); setChallenges(parsedChallenges); setIsSetupComplete(true); setActiveTab('menu'); setQuizIndex(0); setArrangeIndex(0); setCurrentClassifyItem(null); sounds.playLevelUp(); triggerAlert('success', 'Tantangan game belajar telah siap diluncurkan!'); } else { throw new Error("No text returned from Gemini"); } } catch (err) { console.warn("Gemini API Error, using highly smart local fallback database...", err); // Fallback content loading const fallbackData = fallbackDatabase.getChallenges(subjectLabel, setup.material); setChallenges(fallbackData); setIsSetupComplete(true); setActiveTab('menu'); setQuizIndex(0); setArrangeIndex(0); setCurrentClassifyItem(null); triggerAlert('success', 'Tantangan belajar siap (mode offline)!'); } finally { setApiLoading(false); } }; // Game 2 (Classification Sorter) initializer useEffect(() => { if (activeTab === 'classification' && challenges?.classification && !currentClassifyItem) { getNewClassificationItem(); } }, [activeTab, challenges, currentClassifyItem]); const getNewClassificationItem = () => { if (!challenges?.classification?.items?.length) return; const items = challenges.classification.items; const random = items[Math.floor(Math.random() * items.length)]; setCurrentClassifyItem(random); }; // Game 3 (Word / Equation Arranger) initializer useEffect(() => { if (activeTab === 'arrange' && challenges?.arrange?.length) { const activeObj = challenges.arrange[arrangeIndex % challenges.arrange.length]; const original = [...activeObj.correctOrder]; let shuffled = []; do { shuffled = [...original].sort(() => Math.random() - 0.5); } while (shuffled.join(' ') === original.join(' ') && original.length > 1); setScrambledWords(shuffled); setUserSelectionWords([]); } }, [arrangeIndex, activeTab, challenges]); const handleQuizAnswer = (option) => { sounds.init(); const currentQ = challenges.quiz[quizIndex % challenges.quiz.length]; const isCorrect = option === currentQ.correctAnswer; if (isCorrect) { sounds.playCorrect(); setScore(prev => prev + 25); setStreak(prev => prev + 1); setGameFeedback({ isCorrect: true, text: `Maa Syaa Allah, Jawabanmu Benar! ${currentQ.explanation}` }); if (streak + 1 >= 3) { setLevel(prev => prev + 1); setShowConfetti(true); setTimeout(() => setShowConfetti(false), 3000); } } else { sounds.playWrong(); setStreak(0); setGameFeedback({ isCorrect: false, text: `Ups! Jawaban yang benar adalah: "${currentQ.correctAnswer}". ${currentQ.explanation}` }); } }; const handleClassifyAnswer = (catId) => { sounds.init(); const isCorrect = currentClassifyItem.category === catId; const correctCategoryObj = challenges.classification.categories.find(c => c.id === currentClassifyItem.category); if (isCorrect) { sounds.playCorrect(); setScore(prev => prev + 20); setStreak(prev => prev + 1); setGameFeedback({ isCorrect: true, text: `Hebat! "${currentClassifyItem.char}" berasosiasi tepat ke "${correctCategoryObj.name}". ${currentClassifyItem.hint || ''}` }); if (streak + 1 >= 3) { setLevel(prev => prev + 1); setShowConfetti(true); setTimeout(() => setShowConfetti(false), 3000); } } else { sounds.playWrong(); setStreak(0); setGameFeedback({ isCorrect: false, text: `Belum tepat. "${currentClassifyItem.char}" seharusnya berada pada kategori "${correctCategoryObj.name}".` }); } }; const selectWordTile = (word) => { setUserSelectionWords([...userSelectionWords, word]); setScrambledWords(scrambledWords.filter(w => w !== word)); }; const deselectWordTile = (word) => { setScrambledWords([...scrambledWords, word]); setUserSelectionWords(userSelectionWords.filter(w => w !== word)); }; const verifyArrangement = () => { sounds.init(); const activeObj = challenges.arrange[arrangeIndex % challenges.arrange.length]; const isCorrect = userSelectionWords.join(' ') === activeObj.correctOrder.join(' '); if (isCorrect) { sounds.playCorrect(); setScore(prev => prev + 30); setStreak(prev => prev + 1); setGameFeedback({ isCorrect: true, text: `Luar Biasa! Susunanmu Sempurna! Clue: ${activeObj.translation}` }); if (streak + 1 >= 3) { setLevel(prev => prev + 1); setShowConfetti(true); setTimeout(() => setShowConfetti(false), 3000); } } else { sounds.playWrong(); setStreak(0); setGameFeedback({ isCorrect: false, text: 'Rangkaian elemen belum tepat. Perhatikan kaidah logika atau tata bahasanya!' }); } }; return (
{/* HEADER BAR */}
{currentUser && ( <>
👤 {currentUser.username}
{currentStatus.status === 'trial' && (
⏱️ Trial: {currentStatus.hoursLeft} Jam Lagi
)} {currentStatus.status === 'premium' && (
👑 Premium Selamanya
)}
{score} Pts
)} {isAdmin && ( <>
⚙️ PANEL KONTROL ADMIN
)}
{/* CUSTOM ALERT COMPONENT */} {customAlert && (
{customAlert.type === 'success' ? '✅' : '⚠️'}

{customAlert.text}

)} {/* CONFETTI SELEBRASI LEVEL UP */} {showConfetti && (
🎉
MAA SYAA ALLAH! LEVEL UP!

LEVEL SEKARANG: LEVEL {level}

)} {/* MAIN CONTENT AREA */}
{/* VIEW 1: AUTH GATE (LOGIN / REGISTER MEMBER & ADMIN) */} {!currentUser && !isAdmin && (
{/* Logo and Greeting */}
🧠

Aplikasi Game Edukasi

Bermain Sambil Belajar Seluruh Mapel dengan Nilai Islami

{/* Auth Tab Selectors */}
{/* Tab Form Containers */}
{/* MODE: MEMBER LOGIN */} {authMode === 'login' && (
setLoginPhone(e.target.value)} placeholder="Contoh: 081234567890" className="w-full px-4 py-3 rounded-xl border border-slate-200 focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 outline-none transition text-sm font-semibold" required />
setLoginPassword(e.target.value)} placeholder="Masukkan password standar (mbr123)" className="w-full px-4 py-3 rounded-xl border border-slate-200 focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 outline-none transition text-sm font-semibold" required />

Sandi default akses member baru adalah: mbr123

)} {/* MODE: REGISTER MEMBER */} {authMode === 'register' && (
setRegName(e.target.value)} placeholder="Contoh: Muhammad Farhan" className="w-full px-4 py-3 rounded-xl border border-slate-200 focus:ring-2 focus:ring-orange-500 focus:border-orange-500 outline-none transition text-sm font-semibold" required />
setRegPhone(e.target.value)} placeholder="Contoh: 081234567890" className="w-full px-4 py-3 rounded-xl border border-slate-200 focus:ring-2 focus:ring-orange-500 focus:border-orange-500 outline-none transition text-sm font-semibold" required />

Sandi diatur otomatis ke "mbr123" sesuai standarisasi akses aplikasi.

🎁 Pendaftaran baru otomatis mendapatkan **Akses Uji Coba Gratis selama 1 Hari**. Setelah uji coba berakhir, aplikasi akan mengunci kecuali membeli akses premium selamanya seharga **Rp 10.000**.

)} {/* MODE: ADMIN LOGIN */} {authMode === 'admin_login' && (

🔑 Akses Admin Demo:
Username: admin | Password: admin123

setAdminUser(e.target.value)} placeholder="Masukkan username admin" className="w-full px-4 py-3 rounded-xl border border-slate-200 focus:ring-2 focus:ring-slate-800 focus:border-slate-800 outline-none transition text-sm font-semibold" required />
setAdminPass(e.target.value)} placeholder="Masukkan password admin" className="w-full px-4 py-3 rounded-xl border border-slate-200 focus:ring-2 focus:ring-slate-800 focus:border-slate-800 outline-none transition text-sm font-semibold" required />
)}
)} {/* VIEW 2: LOGGED IN MEMBER / TRIAL EXPIRED / KUNCI AKSES */} {currentUser && isAksesBlocked && (
🔒

Masa Akses Aplikasi Habis!

Assalamu'alaikum, {currentUser.username}. Masa uji coba gratis 1 hari Anda telah berakhir. Untuk terus menikmati tantangan game edukasi semua mata pelajaran, silakan lakukan aktivasi paket premium selamanya.

{/* Lifetime Access Ticket Card */}
LIFETIME ACCESS
PROMO BELAJAR PINTAR

Akses Premium Selamanya

Membuka seluruh game kuis, klasifikasi sorter, dan perangkai kata untuk 17 mata pelajaran.

Rp 50.000

Rp 10.000

Bayar 1x Gratis Selamanya
{currentUser.paymentStatus === 'pending' && (
🔄 Menunggu Persetujuan Aktivasi Admin

Kami mendeteksi konfirmasi pembayaran Anda. Silakan hubungi admin WhatsApp 087778626570 untuk proses verifikasi kilat.

)}

Setelah konfirmasi transfer sukses, admin akan segera menyetujui akun Anda.

)} {/* VIEW 3: SETUP SELECTIONS (FASE, SUBJECT, MANUAL MATERI) */} {currentUser && !isAksesBlocked && !isSetupComplete && (
🎯

Set Up Tantangan Belajarmu

Pilih kurikulum dan mapelmu, lalu ketik materi belajarmu hari ini.

{/* Loading Screen */} {apiLoading ? (

Merancang Tantangan Game Belajar Anda...

Menggunakan kecerdasan buatan Gemini AI untuk merangkai kuis, klasifikasi, dan puzzle dari materi yang kamu masukkan.

) : (
{/* Phase Selection */}
{['Fase A (Kls 1-2)', 'Fase B (Kls 3-4)', 'Fase C (Kls 5-6)', 'Fase D (Kls 7-9)', 'Fase E (Kls 10)', 'Fase F (Kls 11-12)'].map((p) => ( ))}
{/* 17 Subjects Grid Selector */}
{subjectsList.map((s) => ( ))}
{/* Manual Material Input (Kolom materi diisi manual) */}

Masukkan topik spesifik yang sedang dipelajari di sekolah agar AI merancang tantangan khusus (misal: "Fotosintesis", "Al-Fatihah", "Aljabar", "Hukum Pascal", "Teks Narasi").

setSetup(prev => ({ ...prev, material: e.target.value }))} placeholder="Ketik topik materi belajarmu disini... ✍️" className="w-full px-4 py-3 rounded-xl border-2 border-slate-200 focus:ring-2 focus:ring-orange-500 focus:border-orange-500 outline-none text-sm font-semibold transition" required />
)}
)} {/* VIEW 4: ACTIVE GAME PLAYBOARD */} {currentUser && !isAksesBlocked && isSetupComplete && challenges && (
{/* Active Curriculum, Subject, and Material Info Bar */}
Kurikulum & Materi Aktif

{setup.phase} {subjectsList.find(s => s.id === setup.subject)?.icon} {subjectsList.find(s => s.id === setup.subject)?.name} {setup.material}

{/* TAB NAVIGATION */} {activeTab !== 'menu' && (
)} {/* TAB 4.1: CHOOSE GAME MENU */} {activeTab === 'menu' && (
{/* Greeting Card */}
🚀

Siap Mengalahkan Tantangan, {currentUser.username}?

Ayo selesaikan 3 game interaktif di bawah ini untuk menguasai topik "{setup.material}" secara menyeluruh!

🧠 Level {level}
🔥 {streak} Streak
{score} Pts
{/* 3 Games Selection Cards */}
{/* Game 1: Quiz */}
🔍

Detektif Cerdas (Quiz)

Uji pengetahuan faktual dan logika analisismu dari materi terkait.

{/* Game 2: Category Sorter */}
🧬

Klasifikasi Pintar

Pilah elemen dan istilah kunci materi ke dalam wadah klasifikasi yang tepat.

{/* Game 3: Sentence/Process Arranger */}
🧩

Susun Elemen Belajar

Urutkan proses kejadian, alur definisi, atau rangkaian kata agar tersusun sempurna.

)} {/* TAB 4.2: DETEKTIF CERDAS (QUIZ GAMEPLAY) */} {activeTab === 'quiz' && challenges.quiz && challenges.quiz.length > 0 && (
Misi: Detektif Cerdas Tantangan {quizIndex % challenges.quiz.length + 1} dari {challenges.quiz.length}
{/* Question Display Screen */}

{challenges.quiz[quizIndex % challenges.quiz.length].question}

{gameFeedback ? (
{gameFeedback.isCorrect ? '✅ Hebat!' : '❌ Terlewat!'}

{gameFeedback.text}

) : (
{challenges.quiz[quizIndex % challenges.quiz.length].options.map((option, idx) => ( ))}
)}
)} {/* TAB 4.3: KLASIFIKASI PINTAR GAMEPLAY */} {activeTab === 'classification' && challenges.classification && currentClassifyItem && (
Misi: Klasifikasi Cerdas Kelompokkan Istilah

{challenges.classification.title}

{currentClassifyItem.char}
Seret atau ketuk kategori yang sesuai di bawah untuk mengelompokkannya.
{gameFeedback ? (
{gameFeedback.isCorrect ? '✅ Betul Sekali!' : '💡 Pikirkan Lagi!'}

{gameFeedback.text}

) : (
{challenges.classification.categories.map((cat) => ( ))}
)}
)} {/* TAB 4.4: SUSUN ELEMEN (ARRANGE GAMEPLAY) */} {activeTab === 'arrange' && challenges.arrange && challenges.arrange.length > 0 && (
Misi: Urutkan Elemen Susun Elemen Runtut
Topik Puzzle

{challenges.arrange[arrangeIndex % challenges.arrange.length].name}

Petunjuk: "{challenges.arrange[arrangeIndex % challenges.arrange.length].translation}"

{/* Target Drop Zone */}
{userSelectionWords.length === 0 && (
Ketuk potongan kata di bawah dengan urutan yang logis
)} {userSelectionWords.map((word, idx) => ( ))}
{/* Selection Word Pool */} {!gameFeedback && (
{scrambledWords.map((word, idx) => ( ))}
)} {gameFeedback ? (
{gameFeedback.isCorrect ? '🎉 Alhamdulillah!' : '🔍 Urutkan Ulang!'}

{gameFeedback.text}

) : (
)}
)}
)} {/* VIEW 5: ADMINISTRATOR BACKEND CONTROL DASHBOARD */} {isAdmin && (

⚙️ Panel Pengendali Admin (Sistem Billing & Member)

Lihat member terdaftar, setujui pembayaran premium seumur hidup, atau set kedaluwarsa uji coba untuk pengetesan.

Total Member: {users.length} Akun
{/* Members Data Table */}
{users.map((m, idx) => { const statusObj = getSubscriptionStatus(m); return ( ); })}
Nama Lengkap No. HP WA Status & Sisa Akses Status Bayar Tindakan Admin
{m.username} {m.phone} {statusObj.status === 'premium' && 👑 Premium Selamanya} {statusObj.status === 'trial' && ⏱️ Trial ({statusObj.hoursLeft} Jam)} {statusObj.status === 'expired_trial' && ❌ Trial Habis (Terkunci)} {m.paymentStatus === 'pending' && ( ⏳ MENUNGGU WA (10.000) )} {m.paymentStatus === 'approved' && ( ✓ SUKSES AKTIF )} {m.paymentStatus === 'none' && Belum Mengirim}
{/* Simulation Instructions */}

💡 Cara Mencoba Penguncian Billing WhatsApp & Sistem Uji Coba:

1. Daftarkan akun baru di menu **Daftar Baru** (Password otomatis: `mbr123`). Anda akan masuk dalam masa trial gratis 1 hari.
2. Untuk memicu alur billing: Keluar akun, masuk ke **Admin** (User: `admin`, Pass: `admin123`).
3. Klik tombol **"Simulasikan Kedaluwarsa"** pada akun baru tersebut.
4. Kembali login sebagai member tadi. Akun Anda akan terkunci penuh dan menampilkan tiket pembayaran premium seumur hidup senilai **Rp 10.000**.
5. Member mengirim pesan pembayaran ke WA **087778626570**, lalu Anda (Admin) tinggal menekan tombol **"Setujui Premium"** di dashboard kontrol untuk membukanya selamanya!

)}
{/* FOOTER */}

© 2026 Platform Game Edukasi Pintar. Seluruh hak cipta dilindungi.

Konfirmasi WA Admin: 087778626570
); }

Wednesday, May 20, 2026

uji coba cbt

CBT OLIMPIADE PAI - MGMP PAI SMP KABUPATEN PURWAKARTA

CBT OLIMPIADE PAI

MGMP PAI SMP KABUPATEN PURWAKARTA

بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ

Pendaftaran & Login Peserta

Masukkan identitas lengkap sesuai data sekolah

© 2026 MGMP PAI SMP KABUPATEN PURWAKARTA. Powered by Google Apps Script CBT.