import React, { useState, useEffect } from 'react'; import { initializeApp } from 'firebase/app'; import { getFirestore, collection, doc, setDoc, getDoc, onSnapshot, addDoc, updateDoc, deleteDoc, query, orderBy } from 'firebase/firestore'; import { getAuth, signInWithCustomToken, signInAnonymously, onAuthStateChanged, signOut } from 'firebase/auth'; // --- INITIALIZATION --- 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 : 'kronika-lms-v3-smk'; // --- CUSTOM SVG ICONS --- const Icon = ({ name, size = 24, className = "" }) => { const paths = { book: , users: ( <> ), award: ( <> ), dashboard: ( <> ), logout: ( <> ), file: ( <> ), chevron: , plus: ( <> ), lock: <>, cap: ( <> ), clipboard: , pen: , check: , clock: <>, alert: <> }; return ( {paths[name] || } ); }; // --- APP COMPONENT --- export default function App() { const [user, setUser] = useState(null); const [userData, setUserData] = useState(null); const [loading, setLoading] = useState(true); const [view, setView] = useState('landing'); const [materials, setMaterials] = useState([]); const [quizzes, setQuizzes] = useState([]); const [assignments, setAssignments] = useState([]); const [allStudents, setAllStudents] = useState([]); const [selectedItem, setSelectedItem] = useState(null); const [isSidebarOpen, setIsSidebarOpen] = useState(true); // States for Forms const [regForm, setRegForm] = useState({ name: '', class: '10', nisn: '' }); const [guruPass, setGuruPass] = useState(''); const [authError, setAuthError] = useState(''); // States for Quiz Taking const [currentQuiz, setCurrentQuiz] = useState(null); const [answers, setAnswers] = useState({}); const [quizScore, setQuizScore] = useState(null); // 1. Firebase Auth useEffect(() => { const init = 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 Init Error:", err); setAuthError("Gagal menghubungkan ke server autentikasi."); } }; init(); const unsub = onAuthStateChanged(auth, async (u) => { setUser(u); if (u) { try { const userRef = doc(db, 'artifacts', appId, 'public', 'data', 'users', u.uid); const snap = await getDoc(userRef); if (snap.exists()) { setUserData(snap.data()); setView('dashboard'); } } catch (err) { console.error("Fetch User Data Error:", err); } } setLoading(false); }); return () => unsub(); }, []); // 2. Real-time Listeners useEffect(() => { if (!user || !userData) return; // Listen to Materials const unsubM = onSnapshot(collection(db, 'artifacts', appId, 'public', 'data', 'materials'), (snap) => { setMaterials(snap.docs.map(d => ({ id: d.id, ...d.data() }))); }, (err) => console.error("Materials Listener Error:", err)); // Listen to Quizzes const unsubQ = onSnapshot(collection(db, 'artifacts', appId, 'public', 'data', 'quizzes'), (snap) => { setQuizzes(snap.docs.map(d => ({ id: d.id, ...d.data() }))); }, (err) => console.error("Quizzes Listener Error:", err)); // Listen to Assignments const unsubA = onSnapshot(collection(db, 'artifacts', appId, 'public', 'data', 'assignments'), (snap) => { setAssignments(snap.docs.map(d => ({ id: d.id, ...d.data() }))); }, (err) => console.error("Assignments Listener Error:", err)); // Listen to Students (Teacher Only) let unsubS = () => {}; if (userData.role === 'guru') { unsubS = onSnapshot(collection(db, 'artifacts', appId, 'public', 'data', 'users'), (snap) => { const list = snap.docs.map(d => ({ id: d.id, ...d.data() })); setAllStudents(list.filter(s => s.role === 'siswa')); }, (err) => console.error("Students Listener Error:", err)); } return () => { unsubM(); unsubQ(); unsubA(); unsubS(); }; }, [user, userData]); // 3. Actions const handleRegisterSiswa = async (e) => { e.preventDefault(); if (!regForm.name || !regForm.nisn) return setAuthError("Semua field wajib diisi."); // Safety check for user UID const currentUid = user?.uid || auth.currentUser?.uid; if (!currentUid) { return setAuthError("Sesi belum siap. Silakan coba sesaat lagi."); } setLoading(true); const profile = { uid: currentUid, name: String(regForm.name), nisn: String(regForm.nisn), class: String(regForm.class), role: 'siswa', points: 0, progress: 0, joinedAt: new Date().toISOString() }; try { await setDoc(doc(db, 'artifacts', appId, 'public', 'data', 'users', currentUid), profile); setUserData(profile); setView('dashboard'); } catch (err) { console.error("Register Error:", err); setAuthError("Gagal mendaftar ke database."); } finally { setLoading(false); } }; const handleLoginGuru = async (e) => { e.preventDefault(); if (guruPass === 'GURU123') { const currentUid = user?.uid || auth.currentUser?.uid; if (!currentUid) return setAuthError("Sesi belum siap."); const profile = { uid: currentUid, name: "Admin Guru Sejarah", role: 'guru', joinedAt: new Date().toISOString() }; try { await setDoc(doc(db, 'artifacts', appId, 'public', 'data', 'users', currentUid), profile); setUserData(profile); setView('dashboard'); } catch (err) { console.error("Guru Login Save Error:", err); setAuthError("Gagal mengaktifkan mode guru."); } } else { setAuthError("Password salah."); } }; const addMaterial = async () => { if (!userData || userData.role !== 'guru') return; const title = prompt("Judul Materi:"); if (!title) return; try { await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'materials'), { title, content: "Konten materi sejarah baru...", level: "10", createdAt: new Date().toISOString(), author: userData.name }); } catch (err) { console.error("Add Material Error:", err); } }; const addQuiz = async () => { if (!userData || userData.role !== 'guru') return; const title = prompt("Judul Kuis:"); if (!title) return; try { await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'quizzes'), { title, level: "10", questions: [ { q: "Siapa yang menjahit Bendera Pusaka?", a: "Fatmawati", options: ["Fatmawati", "Kartini", "Sayuti Melik", "Sukarni"] }, { q: "Kapan Proklamasi dikumandangkan?", a: "17 Agustus 1945", options: ["17 Agustus 1945", "18 Agustus 1945", "16 Agustus 1945", "1 Juni 1945"] } ], createdAt: new Date().toISOString() }); } catch (err) { console.error("Add Quiz Error:", err); } }; const addAssignment = async () => { if (!userData || userData.role !== 'guru') return; const title = prompt("Judul Penugasan:"); if (!title) return; try { await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'assignments'), { title, description: "Buatlah esai tentang revolusi industri...", level: "10", createdAt: new Date().toISOString() }); } catch (err) { console.error("Add Assignment Error:", err); } }; const submitQuiz = async () => { if (!userData || !user) return; let score = 0; currentQuiz.questions.forEach((q, idx) => { if (answers[idx] === q.a) score += 50; }); setQuizScore(score); const newPoints = (userData.points || 0) + score; try { await updateDoc(doc(db, 'artifacts', appId, 'public', 'data', 'users', user.uid), { points: newPoints }); setUserData({ ...userData, points: newPoints }); } catch (err) { console.error("Update Points Error:", err); } }; const logout = async () => { setLoading(true); try { await signOut(auth); setUserData(null); setUser(null); setView('landing'); } catch (err) { console.error("Logout Error:", err); } setLoading(false); }; // --- UI RENDER LOGIC --- if (loading) return (

Kronika V3 Memuat...

); if (view === 'landing') return (

KRONIKA LMS

Sistem Manajemen Pembelajaran Sejarah SMK Terintegrasi Kurikulum Merdeka.

); if (view === 'selection') return (

Masuk Sebagai

); if (view === 'register') return (

Pendaftaran

Buat profil belajar sejarahmu.

setRegForm({...regForm, name: e.target.value})} />
setRegForm({...regForm, nisn: e.target.value})} />
{authError &&

{authError}

}
); if (view === 'login_guru') return (

Login Guru

Masukkan sandi khusus administrator.

setGuruPass(e.target.value)} />

Simulasi: GURU123

{authError &&

{authError}

}
); // --- DASHBOARD --- return (
{String(userData?.name?.[0] || '?')}

{String(userData?.name || 'User')}

{String(userData?.role || 'Siswa')}

Kredit Poin

{userData?.points || 0} XP

{currentQuiz ? (

{String(currentQuiz.title)}

{quizScore !== null ? (
{quizScore}

Kuis Selesai!

Poin Anda telah ditambahkan ke profil.

) : (
{currentQuiz.questions.map((q, qIdx) => (

{qIdx + 1}. {String(q.q)}

{q.options.map((opt, oIdx) => ( ))}
))}
)}
) : selectedItem ? (
Modul Kelas {String(selectedItem.level)} Author: {String(selectedItem.author || "Guru")}

{String(selectedItem.title)}

{String(selectedItem.content || selectedItem.description)}
{selectedItem.type === 'assignment' && (
Kumpulkan Tugas
)}
) : (
{userData?.role === 'guru' ? (

Admin Panel Kronika

Kelola seluruh aspek pembelajaran digital sejarah dalam satu kendali terpusat.

Daftar Siswa Aktif ({allStudents.length})
{allStudents.map(s => (

{String(s.name)}

Kelas {String(s.class)} • NISN {String(s.nisn)}

{s.points || 0} XP

Aktif
))}

Statistik Global

Pantau keterlibatan siswa secara menyeluruh di platform Kronika.

{materials.length}

Materi

{quizzes.length}

Kuis

) : (

Eksplorasi Sejarah

"Bangsa yang besar adalah bangsa yang menghargai jasa para pahlawannya." Lanjutkan progres belajarmu hari ini.

Progres Keseluruhan

{userData?.progress || 0}%

Modul & Tugas Terbaru

{materials.map(m => (
setSelectedItem({ type: 'material', ...m })} className="bg-white rounded-[40px] border border-[#EEDC82]/50 p-8 shadow-sm hover:shadow-2xl transition-all cursor-pointer group hover:-translate-y-2">
Materi {m.level}

{String(m.title)}

{String(m.content)}

15 MIN
))} {assignments.map(a => (
setSelectedItem({ type: 'assignment', ...a })} className="bg-[#3E2723] rounded-[40px] p-8 shadow-xl cursor-pointer group hover:-translate-y-2 text-white">
TUGAS

{String(a.title)}

{String(a.description)}

))}
{(materials.length === 0 && assignments.length === 0) && (

Belum ada konten pembelajaran yang diunggah guru.

)}
)}
)}
); }