import React, { useState, useEffect } from 'react'; import { initializeApp } from 'firebase/app'; import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth'; import { getFirestore, doc, setDoc, getDoc, collection, onSnapshot, addDoc, deleteDoc, query } from 'firebase/firestore'; import { Home, Users, FileText, Settings, Plus, Trash2, Printer, Eye } from 'lucide-react'; // Konfigurasi Firebase dari environment 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 : 'mts-al-washliyah-16'; export default function App() { const [activeTab, setActiveTab] = useState('dashboard'); const [user, setUser] = useState(null); const [guru, setGuru] = useState([]); const [gaji, setGaji] = useState([]); const [settings, setSettings] = useState({ nama: 'MTs Al Washliyah 16', lokasi: 'Perbaungan', alamat: 'Jl. Melati No. 16, Perbaungan', bendahara: '', logo: '' }); const [formGaji, setFormGaji] = useState({ guruId: '', bulan: '', pokok: 0, tunjangan: 0, piket: 0, infaq: 0, koperasi: 0 }); const [previewData, setPreviewData] = useState(null); const [isModalGuruOpen, setIsModalGuruOpen] = useState(false); const [newGuru, setNewGuru] = useState({ nama: '', nuptk: '', jabatan: '', baseGaji: 0 }); // 1. Setup Autentikasi (Rule 3) useEffect(() => { const initAuth = 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 failed:", err); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, setUser); return () => unsubscribe(); }, []); // 2. Listener Data (Rule 1 & 2) useEffect(() => { if (!user) return; // Path genap untuk dokumen: artifacts/{id}/public/data/settings/config const settingsRef = doc(db, 'artifacts', appId, 'public', 'data', 'settings', 'config'); const unsubSettings = onSnapshot(settingsRef, (docSnap) => { if (docSnap.exists()) { setSettings(docSnap.data()); } }, (err) => console.error("Settings error:", err)); const guruRef = collection(db, 'artifacts', appId, 'public', 'data', 'guru'); const unsubGuru = onSnapshot(guruRef, (snapshot) => { setGuru(snapshot.docs.map(d => ({ id: d.id, ...d.data() }))); }, (err) => console.error("Guru error:", err)); const gajiRef = collection(db, 'artifacts', appId, 'public', 'data', 'gaji'); const unsubGaji = onSnapshot(gajiRef, (snapshot) => { setGaji(snapshot.docs.map(d => ({ id: d.id, ...d.data() }))); }, (err) => console.error("Gaji error:", err)); return () => { unsubSettings(); unsubGuru(); unsubGaji(); }; }, [user]); // Fungsi utilitas const formatIDR = (val) => new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(val || 0); const terbilang = (n) => { const b = ["", "satu", "dua", "tiga", "empat", "lima", "enam", "tujuh", "delapan", "sembilan", "sepuluh", "sebelas"]; if (n < 12) return b[n]; if (n < 20) return terbilang(n - 10) + " belas"; if (n < 100) return terbilang(Math.floor(n / 10)) + " puluh " + terbilang(n % 10); if (n < 200) return "seratus " + terbilang(n - 100); if (n < 1000) return terbilang(Math.floor(n / 100)) + " ratus " + terbilang(n % 100); if (n < 2000) return "seribu " + terbilang(n - 1000); if (n < 1000000) return terbilang(Math.floor(n / 1000)) + " ribu " + terbilang(n % 1000); return "terlalu besar"; }; const saveSettings = async () => { if (!user) return; try { const settingsRef = doc(db, 'artifacts', appId, 'public', 'data', 'settings', 'config'); await setDoc(settingsRef, settings); } catch (err) { console.error(err); } }; const handlePreview = () => { const selectedGuru = guru.find(g => g.id === formGaji.guruId); if (!selectedGuru || !formGaji.bulan) return; const dateObj = new Date(formGaji.bulan + "-01"); const bulanStr = dateObj.toLocaleDateString('id-ID', { month: 'long', year: 'numeric' }).toUpperCase(); const totalPlus = Number(formGaji.pokok) + Number(formGaji.tunjangan) + Number(formGaji.piket); const totalMinus = Number(formGaji.infaq) + Number(formGaji.koperasi); const netto = totalPlus - totalMinus; setPreviewData({ guru: selectedGuru, bulan: bulanStr, pokok: Number(formGaji.pokok), tunjangan: Number(formGaji.tunjangan), piket: Number(formGaji.piket), infaq: Number(formGaji.infaq), koperasi: Number(formGaji.koperasi), totalPlus, totalMinus, netto, terbilang: terbilang(netto) + " Rupiah" }); }; if (!user) { return (
Menghubungkan ke Server...
{guru.length}
{gaji.length}
| Nama | Jabatan | Base Gaji | Aksi |
|---|---|---|---|
| {g.nama} | {g.jabatan} | {formatIDR(g.baseGaji)} |
{settings.alamat}
Penerima
{previewData.guru.nama}
{previewData.guru.jabatan}
{previewData.bulan}
Slip Gaji Digital
PENERIMAAN
POTONGAN
Terbilang: {previewData.terbilang}