Si-TARI - Tabungan TK Shigor Montessori
Si-TARI Tabungan
Sistem Informasi Tabungan Mandiri TK Shigor
Total Kas Tabungan
Rp 0
Sinkronisasi Awan Berjalan
Total Penarikan
Rp 0
Keluar
Murid Terdaftar
0 Anak
Terdata
Butuh Pencatatan Cepat?
Gunakan navigasi di sebelah kiri untuk berpindah modul secara instan.
Klik nama murid untuk meninjau mutasi secara lengkap.
| ID Murid |
Nama Murid |
Kelas |
Wali Murid |
No. WhatsApp |
Saldo Saat Ini |
Aksi |
| Tanggal/Waktu |
ID / Nama Murid |
Mutasi |
Jumlah |
Keterangan |
Konfigurasi |
Montessori A
Nama Murid
ID Akses: S-001
Total Tabungan
Tingkat Capaian Menabung
Sertifikat Tabungan Kidz
0% menuju target
Target otomatis disesuaikan secara dinamis per kelipatan Rp 500.000 untuk memotivasi kebiasaan gemar menabung ananda.
Wali Murid Resmi
-
WhatsApp Terdaftar
-
Riwayat Tabungan Murid
S-001 - Nama Anak
Total Setoran
Rp 0
Total Tarik
Rp 0
Saldo Bersih
Rp 0
Tabel Mutasi Individu
| Tanggal |
Mutasi |
Nominal |
Keterangan |
// CONFIGURATION SWITCHER FOR LIVE DEPLOYMENTS
const IS_PREVIEW = false; // Set false JIKA SUDAH TERHUBUNG KE FIREBASE
const firebaseConfig = {
apiKey: "API_KEY_ANDA",
authDomain: "PROJECT_ID.firebaseapp.com",
projectId: "PROJECT_ID",
storageBucket: "PROJECT_ID.appspot.com",
messagingSenderId: "SENDER_ID",
appId: "APP_ID"
};
const APP_ID = "shigor_montessori_production";
// Global State Pool
window.state = {
currentMode: 'auth', // auth, admin, parent
currentAuthRole: 'parent', // parent, admin
activeAdminSection: 'dashboard',
activeAdminDataTab: 'students',
students: [],
transactions: [],
currentUser: null, // logged-in user profile container
isFirebase: false
};
let db, auth;
// Dynamic System Initialization
const initSystem = async () => {
if (!IS_PREVIEW && firebaseConfig.apiKey !== "API_KEY_ANDA") {
try {
const app = initializeApp(firebaseConfig);
auth = getAuth(app);
db = getFirestore(app);
state.isFirebase = true;
document.getElementById('badge-cloud-status').className = "px-3 py-1 rounded-full text-[10px] font-bold tracking-wide border bg-emerald-50 text-emerald-700 border-emerald-200";
document.getElementById('badge-cloud-status').innerHTML = '
Cloud Database Aktif';
// Synchronize Firebase Auth changes
onAuthStateChanged(auth, async (user) => {
if (user && state.currentAuthRole === 'admin') {
state.currentUser = { email: user.email, name: "Ustadz/Ustadzah", role: "admin" };
state.currentMode = 'admin';
await autoSetupDatabase();
startDataSync();
routeSystemView();
}
});
} catch (err) {
console.error("Gagal inisialisasi Firebase. Beralih ke Demo Mode.", err);
setupSimulatorMode();
}
} else {
setupSimulatorMode();
}
};
const setupSimulatorMode = () => {
state.isFirebase = false;
document.getElementById('badge-cloud-status').className = "px-3 py-1 rounded-full text-[10px] font-bold tracking-wide border bg-amber-50 text-amber-700 border-amber-200";
document.getElementById('badge-cloud-status').innerHTML = '
Sandbox Mode Aktif';
loadSimulatorData();
};
// AUTO SETUP DATABASE (SEEDIING INITIAL DATA IF EMPTY)
const autoSetupDatabase = async () => {
if (!state.isFirebase) return;
try {
const sRef = collection(db, 'artifacts', APP_ID, 'public', 'data', 'students');
const sSnap = await getDocs(sRef);
if (sSnap.empty) {
// Seed Students
const dummies = [
{ id: 'S-001', name: 'Ahmad Fathir Mubarak', class: 'Montessori A', parentName: 'Bunda Fatimah', parentPhone: '628123456789', pin: '123456', balance: 350000, createdAt: Date.now() },
{ id: 'S-002', name: 'Syifa Nur Salsabila', class: 'Montessori B', parentName: 'Ayah Lukman', parentPhone: '628987654321', pin: '111111', balance: 150000, createdAt: Date.now() },
{ id: 'S-003', name: 'Zayyan Al Fatih', class: 'Islamic Playgroup', parentName: 'Bunda Sarah', parentPhone: '62855112233', pin: '222222', balance: 0, createdAt: Date.now() }
];
for (const d of dummies) {
await setDoc(doc(db, 'artifacts', APP_ID, 'public', 'data', 'students', d.id), d);
}
// Seed Transactions
const dummyTx = [
{ studentId: 'S-001', studentName: 'Ahmad Fathir Mubarak', type: 'deposit', amount: 300000, date: Date.now() - 172800000, description: 'Setoran Awal Tabungan' },
{ studentId: 'S-001', studentName: 'Ahmad Fathir Mubarak', type: 'deposit', amount: 50000, date: Date.now() - 86400000, description: 'Saku Senin' },
{ studentId: 'S-002', studentName: 'Syifa Nur Salsabila', type: 'deposit', amount: 200000, date: Date.now() - 86400000, description: 'Tabungan Rutin Bulanan' },
{ studentId: 'S-002', studentName: 'Syifa Nur Salsabila', type: 'withdrawal', amount: 50000, date: Date.now(), description: 'Pembelian Buku Iqro' }
];
for (const t of dummyTx) {
await addDoc(collection(db, 'artifacts', APP_ID, 'public', 'data', 'transactions'), t);
}
showToast("Database Firestore berhasil diinisialisasi secara otomatis!", "success");
}
} catch (err) {
console.error("Gagal mengeksekusi Auto Setup:", err);
}
};
// Realtime Data Sync Pool
const startDataSync = () => {
const studentCol = collection(db, 'artifacts', APP_ID, 'public', 'data', 'students');
const transCol = collection(db, 'artifacts', APP_ID, 'public', 'data', 'transactions');
onSnapshot(studentCol, (snapshot) => {
const students = [];
snapshot.forEach(doc => {
students.push(doc.data());
});
state.students = students;
renderSystem();
});
onSnapshot(transCol, (snapshot) => {
const transactions = [];
snapshot.forEach(doc => {
transactions.push({ id: doc.id, ...doc.data() });
});
state.transactions = transactions.sort((a, b) => b.date - a.date);
renderSystem();
});
};
// Simulator / Local Storage Management
const loadSimulatorData = () => {
const localStudents = JSON.parse(localStorage.getItem('shigor_db_students'));
const localTx = JSON.parse(localStorage.getItem('shigor_db_tx'));
if (!localStudents || !localTx) {
// Initialize Dummies
const dummyStudents = [
{ id: 'S-001', name: 'Ahmad Fathir Mubarak', class: 'Montessori A', parentName: 'Bunda Fatimah', parentPhone: '628123456789', pin: '123456', balance: 350000, createdAt: Date.now() },
{ id: 'S-002', name: 'Syifa Nur Salsabila', class: 'Montessori B', parentName: 'Ayah Lukman', parentPhone: '628987654321', pin: '111111', balance: 150000, createdAt: Date.now() },
{ id: 'S-003', name: 'Zayyan Al Fatih', class: 'Islamic Playgroup', parentName: 'Bunda Sarah', parentPhone: '62855112233', pin: '222222', balance: 0, createdAt: Date.now() }
];
const dummyTransactions = [
{ id: 'tx-1', studentId: 'S-001', studentName: 'Ahmad Fathir Mubarak', type: 'deposit', amount: 300000, date: Date.now() - 172800000, description: 'Setoran Awal Tabungan' },
{ id: 'tx-2', studentId: 'S-001', studentName: 'Ahmad Fathir Mubarak', type: 'deposit', amount: 50000, date: Date.now() - 86400000, description: 'Saku Senin' },
{ id: 'tx-3', studentId: 'S-002', studentName: 'Syifa Nur Salsabila', type: 'deposit', amount: 200000, date: Date.now() - 86400000, description: 'Tabungan Rutin Bulanan' },
{ id: 'tx-4', studentId: 'S-002', studentName: 'Syifa Nur Salsabila', type: 'withdrawal', amount: 50000, date: Date.now(), description: 'Pembelian Buku Iqro' }
];
localStorage.setItem('shigor_db_students', JSON.stringify(dummyStudents));
localStorage.setItem('shigor_db_tx', JSON.stringify(dummyTransactions));
state.students = dummyStudents;
state.transactions = dummyTransactions;
} else {
state.students = localStudents;
state.transactions = localTx.sort((a,b) => b.date - a.date);
}
renderSystem();
};
// WRITE OPERATORS WRAPPERS
window.saveStudentToDB = async (studentData) => {
if (state.isFirebase) {
const sRef = doc(db, 'artifacts', APP_ID, 'public', 'data', 'students', studentData.id);
await setDoc(sRef, studentData);
} else {
state.students.push(studentData);
localStorage.setItem('shigor_db_students', JSON.stringify(state.students));
renderSystem();
}
};
window.saveTransactionToDB = async (txData) => {
if (state.isFirebase) {
const sRef = doc(db, 'artifacts', APP_ID, 'public', 'data', 'students', txData.studentId);
const sSnap = await getDoc(sRef);
if (sSnap.exists()) {
const student = sSnap.data();
const newBalance = txData.type === 'deposit'
? (student.balance || 0) + txData.amount
: (student.balance || 0) - txData.amount;
await addDoc(collection(db, 'artifacts', APP_ID, 'public', 'data', 'transactions'), txData);
await updateDoc(sRef, { balance: newBalance });
}
} else {
const sIdx = state.students.findIndex(s => s.id === txData.studentId);
if (sIdx !== -1) {
const newBalance = txData.type === 'deposit'
? state.students[sIdx].balance + txData.amount
: state.students[sIdx].balance - txData.amount;
state.students[sIdx].balance = newBalance;
txData.id = "tx-" + Math.random().toString(36).substr(2, 9);
state.transactions.unshift(txData);
localStorage.setItem('shigor_db_students', JSON.stringify(state.students));
localStorage.setItem('shigor_db_tx', JSON.stringify(state.transactions));
renderSystem();
}
}
};
window.deleteStudentFromDB = async (studentId) => {
if (state.isFirebase) {
const sRef = doc(db, 'artifacts', APP_ID, 'public', 'data', 'students', studentId);
await deleteDoc(sRef);
} else {
state.students = state.students.filter(s => s.id !== studentId);
localStorage.setItem('shigor_db_students', JSON.stringify(state.students));
renderSystem();
}
};
window.deleteTransactionFromDB = async (txId, studentId, amount, type) => {
if (state.isFirebase) {
const txRef = doc(db, 'artifacts', APP_ID, 'public', 'data', 'transactions', txId);
const sRef = doc(db, 'artifacts', APP_ID, 'public', 'data', 'students', studentId);
const sSnap = await getDoc(sRef);
if (sSnap.exists()) {
const student = sSnap.data();
const revertedBalance = type === 'deposit'
? (student.balance || 0) - amount
: (student.balance || 0) + amount;
await deleteDoc(txRef);
await updateDoc(sRef, { balance: revertedBalance });
}
} else {
state.transactions = state.transactions.filter(t => t.id !== txId);
const sIdx = state.students.findIndex(s => s.id === studentId);
if (sIdx !== -1) {
state.students[sIdx].balance = type === 'deposit'
? state.students[sIdx].balance - amount
: state.students[sIdx].balance + amount;
}
localStorage.setItem('shigor_db_students', JSON.stringify(state.students));
localStorage.setItem('shigor_db_tx', JSON.stringify(state.transactions));
renderSystem();
}
};
// AUTH HANDLERS COMPATIBLE WITH PREVIEW MODE
window.handleAdminAuth = async (e) => {
e.preventDefault();
const email = document.getElementById('admin-login-email').value.trim();
const password = document.getElementById('admin-login-password').value;
if (state.isFirebase) {
try {
await signInWithEmailAndPassword(auth, email, password);
showToast("Login Admin Berhasil", "success");
} catch (err) {
showToast("Gagal masuk: " + err.message, "error");
}
} else {
// Mock Admin Login (Safe Simulator bypass)
if (email === "admin@shigor.com" && password === "shigor123") {
state.currentUser = { email, name: "Ustadz Admin", role: "admin" };
state.currentMode = 'admin';
showToast("Simulasi Admin Aktif", "success");
routeSystemView();
renderSystem();
} else {
showToast("Akun simulasi salah! (Gunakan: admin@shigor.com / shigor123)", "error");
}
}
};
window.handleParentAuth = (e) => {
e.preventDefault();
const id = document.getElementById('parent-login-id').value.trim().toUpperCase();
const pin = document.getElementById('parent-login-pin').value;
const studentMatch = state.students.find(s => s.id === id && s.pin === pin);
if (studentMatch) {
state.currentUser = { id: studentMatch.id, name: "Wali dari " + studentMatch.name, role: "parent", details: studentMatch };
state.currentMode = 'parent';
showToast("Berhasil Masuk Portal Wali Murid", "success");
routeSystemView();
renderSystem();
} else {
showToast("Kredensial salah atau Murid tidak terdaftar!", "error");
}
};
window.handleLogout = async () => {
if (state.isFirebase && state.currentUser?.role === 'admin') {
await signOut(auth);
}
state.currentUser = null;
state.currentMode = 'auth';
showToast("Keluar berhasil.", "success");
routeSystemView();
};
// Initialization
window.addEventListener('DOMContentLoaded', () => {
initSystem();
// Register dynamic 1-file Service Worker
if ('serviceWorker' in navigator) {
const swString = `
const CACHE_NAME = 'sitari-v1';
self.addEventListener('install', e => self.skipWaiting());
self.addEventListener('fetch', e => e.respondWith(fetch(e.request)));
`;
const swBlob = new Blob([swString], { type: 'application/javascript' });
const swUrl = URL.createObjectURL(swBlob);
navigator.serviceWorker.register(swUrl).catch(err => console.warn("PWA SW Bypassed."));
}
});
Posting Komentar