<!-- CONFIG & INITIAL VARIABLES -->
<script>
const APP_URL = "https://script.google.com/macros/s/AKfycby4OOQSjzy0CBqhHubhFkVzkhD5pN3EhrxJYhOMbQcpgW-1NwpjliocEm1-3p_uxDv6Bw/exec"; /* GANTI */
const PREVIEW_MODE = false; /* GANTI */
</script>
<!-- TOAST NOTIFICATION CONTAINER -->
<div id="toast-container" class="fixed top-5 right-5 z-[9999] flex flex-col gap-2 pointer-events-none"></div>
<!-- LOGIN SCREEN -->
<div id="login-view" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/80 backdrop-blur-md">
<div class="w-full max-w-md bg-white rounded-3xl shadow-2xl p-8 border border-slate-100 transition-all">
<div class="text-center mb-8">
<div class="w-16 h-16 bg-primary-100 text-primary-700 rounded-2xl flex items-center justify-center mx-auto mb-4 shadow-sm">
<i class='bx bxs-institution text-3xl'></i>
</div>
<h1 class="text-2xl font-bold text-slate-900">BaitulMal Digital</h1>
<p class="text-slate-500 text-sm mt-1">Sistem Keuangan Majelis Taklim</p>
</div>
<form id="login-form" onsubmit="handleLogin(event)" class="space-y-5">
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Username</label>
<div class="relative">
<i class='bx bx-user absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 text-lg'></i>
<input type="text" id="login-username" value="admin" required class="w-full pl-11 pr-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:bg-white focus:ring-2 focus:ring-primary-500 outline-none text-slate-800 transition text-sm">
</div>
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Password</label>
<div class="relative">
<i class='bx bx-lock-alt absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 text-lg'></i>
<input type="password" id="login-password" value="123" required class="w-full pl-11 pr-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:bg-white focus:ring-2 focus:ring-primary-500 outline-none text-slate-800 transition text-sm">
</div>
</div>
<button type="submit" id="btn-login" class="w-full py-3.5 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-xl shadow-lg shadow-primary-600/30 transition duration-200 active:scale-95 flex items-center justify-center gap-2">
<span>Masuk ke System</span>
<i class='bx bx-right-arrow-alt text-xl'></i>
</button>
</form>
<div class="mt-6 p-3 bg-slate-50 rounded-xl text-center text-xs text-slate-500">
Akses Default Demo: <span class="font-semibold text-slate-700">admin / 123</span>
</div>
</div>
</div>
<!-- MAIN APP STRUCTURE -->
<div id="main-app" class="w-full h-screen flex overflow-hidden bg-slate-50 relative hidden">
<!-- SIDEBAR OVERLAY FOR MOBILE -->
<div id="sidebar-backdrop" onclick="toggleSidebar(false)" class="bg-slate-900/50 backdrop-blur-xs fixed inset-0 z-40 md:hidden hidden transition-opacity"></div>
<!-- SIDEBAR -->
<aside id="sidebar" class="fixed md:relative inset-y-0 left-0 z-50 w-64 transform -translate-x-full md:translate-x-0 transition-transform duration-300 ease-in-out flex flex-col h-full bg-white border-r border-slate-200/80 shadow-sm">
<!-- Sidebar Header -->
<div class="p-5 flex items-center justify-between border-b border-slate-100">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-primary-600 text-white rounded-xl flex items-center justify-center shadow-md shadow-primary-600/20 font-bold">
<i class='bx bxs-moon text-xl'></i>
</div>
<div>
<h2 class="font-bold text-slate-900 text-base leading-tight">BaitulMal</h2>
<span class="text-xs text-slate-400 font-medium">Digital Admin</span>
</div>
</div>
<button onclick="toggleSidebar(false)" class="md:hidden text-slate-400 hover:text-slate-600 p-1">
<i class='bx bx-x text-2xl'></i>
</button>
</div>
<!-- Navigation Links -->
<nav class="flex-1 p-4 space-y-1 overflow-y-auto">
<!-- PLACEHOLDER: NAVIGATION ITEMS -->
<button onclick="switchTab('dashboard')" id="nav-dashboard" class="nav-item w-full flex items-center gap-3 px-4 py-3 rounded-xl font-medium text-sm text-slate-600 hover:bg-slate-100 transition">
<i class='bx bxs-grid-alt text-lg'></i> Dashboard
</button>
<button onclick="switchTab('members')" id="nav-members" class="nav-item w-full flex items-center gap-3 px-4 py-3 rounded-xl font-medium text-sm text-slate-600 hover:bg-slate-100 transition">
<i class='bx bxs-group text-lg'></i> Data Anggota & KK
</button>
<button onclick="switchTab('cashin')" id="nav-cashin" class="nav-item w-full flex items-center gap-3 px-4 py-3 rounded-xl font-medium text-sm text-slate-600 hover:bg-slate-100 transition">
<i class='bx bxs-wallet-in text-lg text-emerald-600'></i> Infaq & Sadaqah
</button>
<button onclick="switchTab('cashout')" id="nav-cashout" class="nav-item w-full flex items-center gap-3 px-4 py-3 rounded-xl font-medium text-sm text-slate-600 hover:bg-slate-100 transition">
<i class='bx bxs-wallet-out text-lg text-rose-600'></i> Kas Keluar
</button>
<button onclick="switchTab('rekap')" id="nav-rekap" class="nav-item w-full flex items-center gap-3 px-4 py-3 rounded-xl font-medium text-sm text-slate-600 hover:bg-slate-100 transition">
<i class='bx bxs-file-blank text-lg text-amber-600'></i> Rekap per KK
</button>
<div class="pt-4 mt-4 border-t border-slate-100">
<p class="px-4 text-[10px] font-bold tracking-wider text-slate-400 uppercase mb-2">Roadmap Fitur</p>
<button onclick="showComingSoon('Cetak Kuitansi Digital')" class="w-full flex items-center justify-between px-4 py-2.5 rounded-xl text-sm font-medium text-slate-400 hover:bg-slate-50 transition">
<span class="flex items-center gap-3"><i class='bx bx-printer text-lg'></i> Cetak Kuitansi</span>
<span class="text-[10px] bg-amber-100 text-amber-700 font-bold px-1.5 py-0.5 rounded">V2</span>
</button>
<button onclick="showComingSoon('Broadcast WA Jamaah')" class="w-full flex items-center justify-between px-4 py-2.5 rounded-xl text-sm font-medium text-slate-400 hover:bg-slate-50 transition">
<span class="flex items-center gap-3"><i class='bx bxl-whatsapp text-lg'></i> Broadcast WA</span>
<span class="text-[10px] bg-amber-100 text-amber-700 font-bold px-1.5 py-0.5 rounded">V2</span>
</button>
</div>
</nav>
<!-- Sidebar Footer (User Info & Logout) -->
<div class="p-4 border-t border-slate-100 bg-slate-50/50">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-9 h-9 rounded-full bg-primary-700 text-white flex items-center justify-center font-bold text-sm flex-none">
<span id="user-avatar">A</span>
</div>
<div class="truncate">
<p id="user-name-display" class="text-sm font-semibold text-slate-800 truncate">Administrator</p>
<span class="text-xs text-slate-500">Bendahara Utama</span>
</div>
</div>
<button onclick="handleLogout()" title="Keluar" class="text-slate-400 hover:text-rose-600 p-2 rounded-lg transition">
<i class='bx bx-log-out text-xl'></i>
</button>
</div>
</div>
</aside>
<!-- CONTENT AREA -->
<div class="flex-1 flex flex-col overflow-hidden relative w-full">
<!-- MOBILE HEADER -->
<header class="md:hidden flex-none h-16 bg-white border-b border-slate-200 flex items-center justify-between px-4 z-30">
<button onclick="toggleSidebar(true)" class="p-2 text-slate-600 hover:bg-slate-100 rounded-xl">
<i class='bx bx-menu text-2xl'></i>
</button>
<div class="flex items-center gap-2">
<div class="w-7 h-7 bg-primary-600 text-white rounded-lg flex items-center justify-center font-bold text-xs">
<i class='bx bxs-moon'></i>
</div>
<span class="font-bold text-slate-800 text-sm">BaitulMal</span>
</div>
<div class="w-8"></div>
</header>
<!-- MAIN CONTAINER -->
<main class="flex-1 overflow-y-auto p-4 lg:p-8 space-y-6">
<!-- PLACEHOLDER: MAIN CONTENT VIEWS -->
<!-- VIEW 1: DASHBOARD -->
<section id="view-dashboard" class="space-y-6">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h2 class="text-2xl font-bold text-slate-900">Ringkasan Kas Majelis</h2>
<p class="text-slate-500 text-sm mt-0.5">Pantau arus kas infaq & pengeluaran taklim secara real-time.</p>
</div>
<div class="flex items-center gap-2">
<button onclick="switchTab('cashin')" class="px-4 py-2.5 bg-primary-600 hover:bg-primary-700 text-white font-medium text-sm rounded-xl shadow-sm transition flex items-center gap-2">
<i class='bx bx-plus-circle text-lg'></i> Catat Infaq
</button>
</div>
</div>
<!-- BENTO CARDS STATS -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-5">
<div class="bg-white p-6 rounded-2xl border border-slate-200/80 shadow-sm relative overflow-hidden">
<div class="flex items-center justify-between mb-3">
<span class="text-xs font-bold uppercase tracking-wider text-slate-400">Total Kas Saat Ini</span>
<div class="w-10 h-10 bg-primary-50 text-primary-600 rounded-xl flex items-center justify-center">
<i class='bx bxs-vault text-xl'></i>
</div>
</div>
<h3 id="stat-total-kas" class="text-2xl lg:text-3xl font-extrabold text-slate-900">Rp 0</h3>
<p class="text-xs text-emerald-600 mt-2 font-medium flex items-center gap-1">
<i class='bx bx-trending-up'></i> Keuangan Stabil
</p>
</div>
<div class="bg-white p-6 rounded-2xl border border-slate-200/80 shadow-sm">
<div class="flex items-center justify-between mb-3">
<span class="text-xs font-bold uppercase tracking-wider text-slate-400">Infaq & Sadaqah Masuk</span>
<div class="w-10 h-10 bg-emerald-50 text-emerald-600 rounded-xl flex items-center justify-center">
<i class='bx bxs-left-arrow-circle text-xl'></i>
</div>
</div>
<h3 id="stat-kas-masuk" class="text-2xl lg:text-3xl font-extrabold text-slate-900">Rp 0</h3>
<p class="text-xs text-slate-400 mt-2">Total akumulasi penerimaan</p>
</div>
<div class="bg-white p-6 rounded-2xl border border-slate-200/80 shadow-sm">
<div class="flex items-center justify-between mb-3">
<span class="text-xs font-bold uppercase tracking-wider text-slate-400">Kas Keluar / Operasional</span>
<div class="w-10 h-10 bg-rose-50 text-rose-600 rounded-xl flex items-center justify-center">
<i class='bx bxs-right-arrow-circle text-xl'></i>
</div>
</div>
<h3 id="stat-kas-keluar" class="text-2xl lg:text-3xl font-extrabold text-slate-900">Rp 0</h3>
<p class="text-xs text-slate-400 mt-2">Total pengeluaran majelis</p>
</div>
</div>
<!-- RECENT TRANSACTIONS TABLE -->
<div class="bg-white rounded-2xl border border-slate-200/80 shadow-sm p-6">
<div class="flex items-center justify-between mb-5">
<h3 class="font-bold text-slate-900 text-lg">Transaksi Terakhir</h3>
<button onclick="switchTab('rekap')" class="text-sm font-semibold text-primary-600 hover:text-primary-700">Lihat Semua</button>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-slate-50 text-slate-500 uppercase text-[11px] font-bold">
<tr>
<th class="p-3.5 rounded-l-xl">Tanggal</th>
<th class="p-3.5">Tipe</th>
<th class="p-3.5">Keterangan / Penyerah</th>
<th class="p-3.5 text-right rounded-r-xl">Nominal</th>
</tr>
</thead>
<tbody id="recent-transactions-list" class="divide-y divide-slate-100">
<!-- Rendered via JS -->
</tbody>
</table>
</div>
</div>
</section>
<!-- VIEW 2: DATA ANGGOTA & KK -->
<section id="view-members" class="space-y-6 hidden">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h2 class="text-2xl font-bold text-slate-900">Data Anggota & Kepala Keluarga</h2>
<p class="text-slate-500 text-sm mt-0.5">Kelola data jamaah berdasarkan struktur keluarga.</p>
</div>
<button onclick="openModalMember()" class="px-4 py-2.5 bg-primary-600 hover:bg-primary-700 text-white font-medium text-sm rounded-xl shadow-sm transition flex items-center gap-2">
<i class='bx bx-user-plus text-lg'></i> Tambah Anggota
</button>
</div>
<div class="bg-white rounded-2xl border border-slate-200/80 shadow-sm p-6">
<div class="flex items-center gap-3 mb-6">
<div class="relative flex-1">
<i class='bx bx-search absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 text-lg'></i>
<input type="text" id="search-member" oninput="renderMembers()" placeholder="Cari nama anggota, KK, atau status..." class="w-full pl-11 pr-4 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:bg-white focus:ring-2 focus:ring-primary-500 outline-none">
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-slate-50 text-slate-500 uppercase text-[11px] font-bold">
<tr>
<th class="p-3.5 rounded-l-xl">Nama Jamaah</th>
<th class="p-3.5">Kepala Keluarga (KK)</th>
<th class="p-3.5">Status Hubungan</th>
<th class="p-3.5">No. WhatsApp</th>
<th class="p-3.5 text-center rounded-r-xl">Aksi</th>
</tr>
</thead>
<tbody id="member-table-body" class="divide-y divide-slate-100">
<!-- Rendered via JS -->
</tbody>
</table>
</div>
</div>
</section>
<!-- VIEW 3: INFAQ & SADAQAH (KAS MASUK) -->
<section id="view-cashin" class="space-y-6 hidden">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h2 class="text-2xl font-bold text-slate-900">Pencatatan Infaq & Sadaqah</h2>
<p class="text-slate-500 text-sm mt-0.5">Catat kas masuk dari jamaah atau hamba Allah.</p>
</div>
<button onclick="openModalCashIn()" class="px-4 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white font-medium text-sm rounded-xl shadow-sm transition flex items-center gap-2">
<i class='bx bx-plus-circle text-lg'></i> Catat Kas Masuk
</button>
</div>
<div class="bg-white rounded-2xl border border-slate-200/80 shadow-sm p-6">
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-slate-50 text-slate-500 uppercase text-[11px] font-bold">
<tr>
<th class="p-3.5 rounded-l-xl">Tanggal</th>
<th class="p-3.5">Penyerah (Nama / KK)</th>
<th class="p-3.5">Jenis Infaq</th>
<th class="p-3.5">Keterangan</th>
<th class="p-3.5 text-right rounded-r-xl">Nominal</th>
</tr>
</thead>
<tbody id="cashin-table-body" class="divide-y divide-slate-100">
<!-- Rendered via JS -->
</tbody>
</table>
</div>
</div>
</section>
<!-- VIEW 4: KAS KELUAR -->
<section id="view-cashout" class="space-y-6 hidden">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h2 class="text-2xl font-bold text-slate-900">Pengeluaran & Operasional</h2>
<p class="text-slate-500 text-sm mt-0.5">Pencatatan kas keluar majelis taklim.</p>
</div>
<button onclick="openModalCashOut()" class="px-4 py-2.5 bg-rose-600 hover:bg-rose-700 text-white font-medium text-sm rounded-xl shadow-sm transition flex items-center gap-2">
<i class='bx bx-minus-circle text-lg'></i> Catat Kas Keluar
</button>
</div>
<div class="bg-white rounded-2xl border border-slate-200/80 shadow-sm p-6">
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-slate-50 text-slate-500 uppercase text-[11px] font-bold">
<tr>
<th class="p-3.5 rounded-l-xl">Tanggal</th>
<th class="p-3.5">Kategori</th>
<th class="p-3.5">Keperluan / Keterangan</th>
<th class="p-3.5 text-right rounded-r-xl">Nominal Pengeluaran</th>
</tr>
</thead>
<tbody id="cashout-table-body" class="divide-y divide-slate-100">
<!-- Rendered via JS -->
</tbody>
</table>
</div>
</div>
</section>
<!-- VIEW 5: REKAP BULANAN PER KK -->
<section id="view-rekap" class="space-y-6 hidden">
<div>
<h2 class="text-2xl font-bold text-slate-900">Rekap Infaq per Kepala Keluarga</h2>
<p class="text-slate-500 text-sm mt-0.5">Rekapitulasi kontribusi jamaah terkelompokkan per KK.</p>
</div>
<div class="bg-white rounded-2xl border border-slate-200/80 shadow-sm p-6">
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-slate-50 text-slate-500 uppercase text-[11px] font-bold">
<tr>
<th class="p-3.5 rounded-l-xl">Kepala Keluarga</th>
<th class="p-3.5">Jumlah Anggota</th>
<th class="p-3.5">Total Infaq Masuk</th>
<th class="p-3.5 text-center rounded-r-xl">Status Keaktifan</th>
</tr>
</thead>
<tbody id="rekap-table-body" class="divide-y divide-slate-100">
<!-- Rendered via JS -->
</tbody>
</table>
</div>
</div>
</section>
</main>
</div>
</div>
<!-- MODAL: ADD/EDIT MEMBER -->
<div id="modal-member" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-xs hidden">
<div class="w-full max-w-lg bg-white rounded-3xl shadow-xl border border-slate-100 p-6 space-y-5">
<div class="flex items-center justify-between pb-3 border-b border-slate-100">
<h3 class="font-bold text-slate-900 text-lg" id="modal-member-title">Tambah Jamaah Baru</h3>
<button onclick="closeModal('modal-member')" class="text-slate-400 hover:text-slate-600"><i class='bx bx-x text-2xl'></i></button>
</div>
<form id="form-member" onsubmit="saveMember(event)" class="space-y-4">
<input type="hidden" id="member-id">
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Nama Lengkap Jamaah</label>
<input type="text" id="member-nama" required placeholder="Contoh: Ahmad Subagja" class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
</div>
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Nama Kepala Keluarga (KK)</label>
<input type="text" id="member-kk" required placeholder="Contoh: Ahmad Subagja" class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Status Hubungan</label>
<select id="member-status" required class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
<option value="Kepala Keluarga">Kepala Keluarga</option>
<option value="Istri">Istri</option>
<option value="Anak">Anak</option>
<option value="Orang Tua/Lainnya">Orang Tua/Lainnya</option>
</select>
</div>
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">No. WhatsApp</label>
<input type="tel" id="member-hp" placeholder="0812xxxx" class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
</div>
</div>
<div class="flex justify-end gap-2 pt-3">
<button type="button" onclick="closeModal('modal-member')" class="px-4 py-2.5 text-slate-600 bg-slate-100 hover:bg-slate-200 rounded-xl text-sm font-semibold">Batal</button>
<button type="submit" class="px-5 py-2.5 bg-primary-600 hover:bg-primary-700 text-white rounded-xl text-sm font-semibold shadow-sm">Simpan Data</button>
</div>
</form>
</div>
</div>
<!-- MODAL: ADD CASH IN -->
<div id="modal-cashin" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-xs hidden">
<div class="w-full max-w-lg bg-white rounded-3xl shadow-xl border border-slate-100 p-6 space-y-5">
<div class="flex items-center justify-between pb-3 border-b border-slate-100">
<h3 class="font-bold text-slate-900 text-lg">Catat Infaq / Sadaqah</h3>
<button onclick="closeModal('modal-cashin')" class="text-slate-400 hover:text-slate-600"><i class='bx bx-x text-2xl'></i></button>
</div>
<form id="form-cashin" onsubmit="saveCashIn(event)" class="space-y-4">
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Pilih Jamaah / Penyerah</label>
<select id="cashin-member" required class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
<!-- Populated via JS -->
</select>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Jenis Penerimaan</label>
<select id="cashin-jenis" required class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
<option value="Infaq Wajib Taklim">Infaq Wajib Taklim</option>
<option value="Sadaqah Sukarela">Sadaqah Sukarela</option>
<option value="Donasi Acara">Donasi Acara</option>
<option value="Hamba Allah">Hamba Allah</option>
</select>
</div>
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Nominal (Rp)</label>
<input type="number" id="cashin-nominal" min="1000" required placeholder="50000" class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
</div>
</div>
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Keterangan Tambahan</label>
<input type="text" id="cashin-ket" placeholder="Contoh: Infaq pengajian pekan ke-2" class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
</div>
<div class="flex justify-end gap-2 pt-3">
<button type="button" onclick="closeModal('modal-cashin')" class="px-4 py-2.5 text-slate-600 bg-slate-100 hover:bg-slate-200 rounded-xl text-sm font-semibold">Batal</button>
<button type="submit" class="px-5 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white rounded-xl text-sm font-semibold shadow-sm">Simpan Infaq</button>
</div>
</form>
</div>
</div>
<!-- MODAL: ADD CASH OUT -->
<div id="modal-cashout" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-xs hidden">
<div class="w-full max-w-lg bg-white rounded-3xl shadow-xl border border-slate-100 p-6 space-y-5">
<div class="flex items-center justify-between pb-3 border-b border-slate-100">
<h3 class="font-bold text-slate-900 text-lg">Catat Kas Keluar</h3>
<button onclick="closeModal('modal-cashout')" class="text-slate-400 hover:text-slate-600"><i class='bx bx-x text-2xl'></i></button>
</div>
<form id="form-cashout" onsubmit="saveCashOut(event)" class="space-y-4">
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Kategori Pengeluaran</label>
<select id="cashout-kategori" required class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
<option value="Operasional Pengajian">Operasional Pengajian</option>
<option value="Bisyaroh Ustadz">Bisyaroh Ustadz</option>
<option value="Konsumsi Jamaah">Konsumsi Jamaah</option>
<option value="Bantuan Sosial">Bantuan Sosial</option>
<option value="Lain-lain">Lain-lain</option>
</select>
</div>
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Nominal Pengeluaran (Rp)</label>
<input type="number" id="cashout-nominal" min="1000" required placeholder="150000" class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
</div>
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Rincian Keperluan</label>
<input type="text" id="cashout-ket" required placeholder="Contoh: Konsumsisnack ustadz & air mineral" class="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none focus:bg-white focus:ring-2 focus:ring-primary-500">
</div>
<div class="flex justify-end gap-2 pt-3">
<button type="button" onclick="closeModal('modal-cashout')" class="px-4 py-2.5 text-slate-600 bg-slate-100 hover:bg-slate-200 rounded-xl text-sm font-semibold">Batal</button>
<button type="submit" class="px-5 py-2.5 bg-rose-600 hover:bg-rose-700 text-white rounded-xl text-sm font-semibold shadow-sm">Catat Pengeluaran</button>
</div>
</form>
</div>
</div>
<!-- MAIN JAVASCRIPT LOGIC -->
<script>
// APP STATE
let state = {
user: null,
activeTab: 'dashboard',
members: [],
cashIn: [],
cashOut: []
};
// MOCK DATA FOR PREVIEW MODE (Minimal items to prevent cutoff)
const mockMembers = [
{ id: 'M-1', nama: 'Ustadz Abdullah', kk: 'Abdullah', status: 'Kepala Keluarga', hp: '08123456789' },
{ id: 'M-2', nama: 'Siti Aminah', kk: 'Abdullah', status: 'Istri', hp: '08123456788' },
{ id: 'M-3', nama: 'Hasan Basri', kk: 'Hasan Basri', status: 'Kepala Keluarga', hp: '08198765432' },
{ id: 'M-4', nama: 'Ahmad Dahlan', kk: 'Ahmad Dahlan', status: 'Kepala Keluarga', hp: '08571234567' }
];
const mockCashIn = [
{ id: 'CI-1', tgl: '2026-09-01', memberNama: 'Ustadz Abdullah', kk: 'Abdullah', jenis: 'Infaq Wajib Taklim', nominal: 100000, ket: 'Infaq Rutin' },
{ id: 'CI-2', tgl: '2026-09-05', memberNama: 'Hasan Basri', kk: 'Hasan Basri', jenis: 'Sadaqah Sukarela', nominal: 250000, ket: 'Sadaqah Syukuran' },
{ id: 'CI-3', tgl: '2026-09-08', memberNama: 'Ahmad Dahlan', kk: 'Ahmad Dahlan', jenis: 'Infaq Wajib Taklim', nominal: 100000, ket: 'Infaq Rutin' }
];
const mockCashOut = [
{ id: 'CO-1', tgl: '2026-09-02', kategori: 'Bisyaroh Ustadz', nominal: 200000, ket: 'Pengajian Rutin Pekan 1' },
{ id: 'CO-2', tgl: '2026-09-06', kategori: 'Konsumsi Jamaah', nominal: 85000, ket: 'Snack & Air Mineral' }
];
// INIT APP
window.addEventListener('DOMContentLoaded', () => {
if (PREVIEW_MODE) {
state.members = mockMembers;
state.cashIn = mockCashIn;
state.cashOut = mockCashOut;
}
checkSession();
});
// TOAST SYSTEM
function showToast(msg, type = 'success') {
const container = document.getElementById('toast-container');
const toast = document.createElement('div');
const bgClass = type === 'success' ? 'bg-slate-900 text-white' : 'bg-rose-600 text-white';
const icon = type === 'success' ? 'bx-check-circle text-emerald-400' : 'bx-error-circle text-white';
toast.className = `flex items-center gap-3 px-4 py-3 rounded-2xl shadow-xl ${bgClass} pointer-events-auto transition-all duration-300 transform translate-y-2 text-sm font-medium`;
toast.innerHTML = `<i class='bx ${icon} text-xl'></i> <span>${msg}</span>`;
container.appendChild(toast);
setTimeout(() => {
toast.classList.add('opacity-0', '-translate-y-2');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
// LOGIN / LOGOUT
function handleLogin(e) {
e.preventDefault();
const u = document.getElementById('login-username').value;
const p = document.getElementById('login-password').value;
if (u === 'admin' && p === '123') {
state.user = { name: 'Administrator', username: u };
localStorage.setItem('bm_user', JSON.stringify(state.user));
document.getElementById('login-view').classList.add('hidden');
document.getElementById('main-app').classList.remove('hidden');
showToast('Selamat datang kembali, Bendahara!');
renderAllData();
} else {
showToast('Username atau password salah!', 'error');
}
}
function checkSession() {
const saved = localStorage.getItem('bm_user');
if (saved) {
state.user = JSON.parse(saved);
document.getElementById('login-view').classList.add('hidden');
document.getElementById('main-app').classList.remove('hidden');
renderAllData();
}
}
function handleLogout() {
localStorage.removeItem('bm_user');
state.user = null;
document.getElementById('main-app').classList.add('hidden');
document.getElementById('login-view').classList.remove('hidden');
showToast('Berhasil keluar sistem');
}
// NAVIGATION & TABS
function switchTab(tabId) {
state.activeTab = tabId;
document.querySelectorAll('main > section').forEach(sec => sec.classList.add('hidden'));
document.getElementById(`view-${tabId}`).classList.remove('hidden');
document.querySelectorAll('.nav-item').forEach(btn => {
btn.classList.remove('bg-primary-50', 'text-primary-700', 'font-bold');
btn.classList.add('text-slate-600');
});
const activeBtn = document.getElementById(`nav-${tabId}`);
if (activeBtn) {
activeBtn.classList.add('bg-primary-50', 'text-primary-700', 'font-bold');
}
toggleSidebar(false);
}
function toggleSidebar(show) {
const sb = document.getElementById('sidebar');
const bd = document.getElementById('sidebar-backdrop');
if (show) {
sb.classList.remove('-translate-x-full');
bd.classList.remove('hidden');
} else {
sb.classList.add('-translate-x-full');
bd.classList.add('hidden');
}
}
function showComingSoon(feature) {
showToast(` Fitur [${feature}] akan hadir di versi V2!`);
}
// FORMAT CURRENCY
function formatRp(val) {
return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(val);
}
// RENDER DATA ENGINE
function renderAllData() {
renderDashboard();
renderMembers();
renderCashIn();
renderCashOut();
renderRekap();
populateMemberDropdown();
}
function renderDashboard() {
const totalIn = state.cashIn.reduce((acc, curr) => acc + Number(curr.nominal), 0);
const totalOut = state.cashOut.reduce((acc, curr) => acc + Number(curr.nominal), 0);
const saldo = totalIn - totalOut;
document.getElementById('stat-total-kas').innerText = formatRp(saldo);
document.getElementById('stat-kas-masuk').innerText = formatRp(totalIn);
document.getElementById('stat-kas-keluar').innerText = formatRp(totalOut);
// Recent Transactions (Combined & sorted)
const combined = [
...state.cashIn.map(i => ({ ...i, type: 'IN' })),
...state.cashOut.map(o => ({ ...o, type: 'OUT' }))
].sort((a, b) => new Date(b.tgl) - new Date(a.tgl)).slice(0, 5);
const tbody = document.getElementById('recent-transactions-list');
if (combined.length === 0) {
tbody.innerHTML = `<tr><td colspan="4" class="p-4 text-center text-slate-400">Belum ada transaksi</td></tr>`;
return;
}
tbody.innerHTML = combined.map(item => `
<tr class="hover:bg-slate-50/80 transition">
<td class="p-3.5 text-slate-500">${item.tgl}</td>
<td class="p-3.5">
<span class="px-2.5 py-1 rounded-full text-xs font-semibold ${item.type === 'IN' ? 'bg-emerald-100 text-emerald-700' : 'bg-rose-100 text-rose-700'}">
${item.type === 'IN' ? 'Infaq' : 'Pengeluaran'}
</span>
</td>
<td class="p-3.5 font-medium text-slate-800">${item.type === 'IN' ? (item.memberNama + ' (KK ' + item.kk + ')') : item.kategori + ' - ' + item.ket}</td>
<td class="p-3.5 text-right font-bold ${item.type === 'IN' ? 'text-emerald-600' : 'text-rose-600'}">
${item.type === 'IN' ? '+' : '-'}${formatRp(item.nominal)}
</td>
</tr>
`).join('');
}
function renderMembers() {
const search = document.getElementById('search-member')?.value.toLowerCase() || '';
const filtered = state.members.filter(m =>
m.nama.toLowerCase().includes(search) ||
m.kk.toLowerCase().includes(search) ||
m.status.toLowerCase().includes(search)
);
const tbody = document.getElementById('member-table-body');
if (filtered.length === 0) {
tbody.innerHTML = `<tr><td colspan="5" class="p-4 text-center text-slate-400">Data jamaah tidak ditemukan</td></tr>`;
return;
}
tbody.innerHTML = filtered.map(m => `
<tr class="hover:bg-slate-50/80 transition">
<td class="p-3.5 font-bold text-slate-800">${m.nama}</td>
<td class="p-3.5 text-slate-600">KK. ${m.kk}</td>
<td class="p-3.5">
<span class="px-2.5 py-1 rounded-lg text-xs font-medium ${m.status === 'Kepala Keluarga' ? 'bg-amber-100 text-amber-800 font-semibold' : 'bg-slate-100 text-slate-600'}">
${m.status}
</span>
</td>
<td class="p-3.5 text-slate-500">${m.hp || '-'}</td>
<td class="p-3.5 text-center">
<button onclick="deleteMember('${m.id}')" class="text-rose-500 hover:text-rose-700 p-1.5 rounded-lg hover:bg-rose-50 transition" title="Hapus">
<i class='bx bx-trash text-lg'></i>
</button>
</td>
</tr>
`).join('');
}
function renderCashIn() {
const tbody = document.getElementById('cashin-table-body');
if (state.cashIn.length === 0) {
tbody.innerHTML = `<tr><td colspan="5" class="p-4 text-center text-slate-400">Belum ada riwayat infaq</td></tr>`;
return;
}
tbody.innerHTML = state.cashIn.map(c => `
<tr class="hover:bg-slate-50/80 transition">
<td class="p-3.5 text-slate-500">${c.tgl}</td>
<td class="p-3.5 font-semibold text-slate-800">${c.memberNama} <span class="text-xs font-normal text-slate-400">(KK ${c.kk})</span></td>
<td class="p-3.5"><span class="px-2.5 py-1 bg-emerald-50 text-emerald-700 rounded-lg text-xs font-medium">${c.jenis}</span></td>
<td class="p-3.5 text-slate-600">${c.ket || '-'}</td>
<td class="p-3.5 text-right font-extrabold text-emerald-600">${formatRp(c.nominal)}</td>
</tr>
`).join('');
}
function renderCashOut() {
const tbody = document.getElementById('cashout-table-body');
if (state.cashOut.length === 0) {
tbody.innerHTML = `<tr><td colspan="4" class="p-4 text-center text-slate-400">Belum ada data kas keluar</td></tr>`;
return;
}
tbody.innerHTML = state.cashOut.map(o => `
<tr class="hover:bg-slate-50/80 transition">
<td class="p-3.5 text-slate-500">${o.tgl}</td>
<td class="p-3.5 font-semibold text-slate-800">${o.kategori}</td>
<td class="p-3.5 text-slate-600">${o.ket}</td>
<td class="p-3.5 text-right font-extrabold text-rose-600">${formatRp(o.nominal)}</td>
</tr>
`).join('');
}
function renderRekap() {
// Group cashIn by KK
const kkMap = {};
state.members.forEach(m => {
if (!kkMap[m.kk]) {
kkMap[m.kk] = { count: 0, totalInfaq: 0 };
}
kkMap[m.kk].count += 1;
});
state.cashIn.forEach(c => {
if (kkMap[c.kk]) {
kkMap[c.kk].totalInfaq += Number(c.nominal);
}
});
const tbody = document.getElementById('rekap-table-body');
const entries = Object.entries(kkMap);
if (entries.length === 0) {
tbody.innerHTML = `<tr><td colspan="4" class="p-4 text-center text-slate-400">Data rekap tidak tersedia</td></tr>`;
return;
}
tbody.innerHTML = entries.map(([kkName, info]) => `
<tr class="hover:bg-slate-50/80 transition">
<td class="p-3.5 font-bold text-slate-800">KK. ${kkName}</td>
<td class="p-3.5 text-slate-600">${info.count} Orang</td>
<td class="p-3.5 font-extrabold text-primary-700">${formatRp(info.totalInfaq)}</td>
<td class="p-3.5 text-center">
<span class="px-3 py-1 rounded-full text-xs font-semibold ${info.totalInfaq > 0 ? 'bg-emerald-100 text-emerald-700' : 'bg-amber-100 text-amber-700'}">
${info.totalInfaq > 0 ? 'Aktif Berinfaq' : 'Belum Ada Infaq'}
</span>
</td>
</tr>
`).join('');
}
function populateMemberDropdown() {
const select = document.getElementById('cashin-member');
if (!select) return;
select.innerHTML = state.members.map(m => `<option value="${m.id}">${m.nama} (KK ${m.kk})</option>`).join('');
}
// MODAL HANDLERS
function openModalMember() { document.getElementById('modal-member').classList.remove('hidden'); }
function openModalCashIn() { populateMemberDropdown(); document.getElementById('modal-cashin').classList.remove('hidden'); }
function openModalCashOut() { document.getElementById('modal-cashout').classList.remove('hidden'); }
function closeModal(id) { document.getElementById(id).classList.add('hidden'); }
// CRUD ACTIONS (Optimistic UI)
function saveMember(e) {
e.preventDefault();
const newMem = {
id: 'M-' + Date.now(),
nama: document.getElementById('member-nama').value,
kk: document.getElementById('member-kk').value,
status: document.getElementById('member-status').value,
hp: document.getElementById('member-hp').value
};
state.members.push(newMem);
renderAllData();
closeModal('modal-member');
showToast('Jamaah berhasil ditambahkan!');
document.getElementById('form-member').reset();
}
function deleteMember(id) {
if (confirm('Yakin ingin menghapus jamaah ini?')) {
state.members = state.members.filter(m => m.id !== id);
renderAllData();
showToast('Data jamaah terhapus');
}
}
function saveCashIn(e) {
e.preventDefault();
const memberId = document.getElementById('cashin-member').value;
const selectedMem = state.members.find(m => m.id === memberId) || { nama: 'Hamba Allah', kk: 'Lainnya' };
const newIn = {
id: 'CI-' + Date.now(),
tgl: new Date().toISOString().split('T')[0],
memberNama: selectedMem.nama,
kk: selectedMem.kk,
jenis: document.getElementById('cashin-jenis').value,
nominal: Number(document.getElementById('cashin-nominal').value),
ket: document.getElementById('cashin-ket').value
};
state.cashIn.unshift(newIn);
renderAllData();
closeModal('modal-cashin');
showToast('Infaq berhasil dicatat!');
document.getElementById('form-cashin').reset();
}
function saveCashOut(e) {
e.preventDefault();
const newOut = {
id: 'CO-' + Date.now(),
tgl: new Date().toISOString().split('T')[0],
kategori: document.getElementById('cashout-kategori').value,
nominal: Number(document.getElementById('cashout-nominal').value),
ket: document.getElementById('cashout-ket').value
};
state.cashOut.unshift(newOut);
renderAllData();
closeModal('modal-cashout');
showToast('Pengeluaran berhasil dicatat!');
document.getElementById('form-cashout').reset();
}
/* PLACEHOLDER: JS BARU */
</script>