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 (
);
};
// --- 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 (
);
if (view === 'landing') return (
KRONIKA LMS
Sistem Manajemen Pembelajaran Sejarah SMK Terintegrasi Kurikulum Merdeka.
);
if (view === 'selection') return (
Masuk Sebagai
);
if (view === 'register') return (
);
if (view === 'login_guru') return (
);
// --- 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)}
))}
Statistik Global
Pantau keterlibatan siswa secara menyeluruh di platform Kronika.
) : (
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">
{String(m.title)}
{String(m.content)}
))}
{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">
{String(a.title)}
{String(a.description)}
))}
{(materials.length === 0 && assignments.length === 0) && (
Belum ada konten pembelajaran yang diunggah guru.
)}
)}
)}
);
}