PNM

Perpustakaan Pesantren

المكتبة الرقمية الحديثة · Pelangi Nusa Mandiri Smart App

Total Koleksi Kitab

0

Tersedia di Rak
Santri / Wati Aktif

0

Terdaftar di Sistem
Aktif Mutala'ah

0

Sedang Dipinjam
Rata-Rata Skoring

0

Indeks Kedisiplinan
Fitur Baru: Monitoring Prestasi Santri

Sistem Skoring Kedisiplinan & Penilaian Mutala'ah

Setiap kali santri/wati mengembalikan kitab tepat waktu, poin apresiasi akan bertambah. Hasil skoring ini menjadi salah satu kriteria penentuan Santri Berprestasi Pesantren.

Aktivitas Sirkulasi Terbaru

Real-time Log

Top 3 Santri Teladan

من جاد ساد ومن ساد قاد

"Barangsiapa bersungguh-sungguh, ia akan memimpin."

بارك الله فيكم ونفع بكم الأمة

Sistem Informasi Perpustakaan & Monitoring Santri · Powered by Pelangi Nusa Mandiri

`); w.document.close(); }// ---------- EXPORT CSV HELPER ---------- function downloadCSV(filename, rows){ const csv = rows.map(r => r.map(c => `"${String(c==null?'':c).replace(/"/g,'""')}"`).join(',')).join('\r\n'); const blob = new Blob(['\ufeff'+csv], {type:'text/csv;charset=utf-8;'}); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); link.remove(); URL.revokeObjectURL(url); }$('#export-monitoring').addEventListener('click', ()=>{ const rows = [['Rank','Nama Santri/Wati','Status','NIS','Asrama','Kelas','Total Pinjam','Tepat Waktu','Indeks Skor','Grade','Predikat']]; const items = anggota.filter(a => a.status.startsWith('Santri')).map(a => ({ ...a, stats: calcSantriScore(a.id) })); items.sort((a,b) => b.stats.score - a.stats.score); items.forEach((a, idx) => { rows.push([idx+1, a.nama, a.status, a.nis, a.kamar, a.kelas, a.stats.total, a.stats.tepat, a.stats.score, a.stats.grade, a.stats.predikat]); }); downloadCSV('rapor-monitoring-santri.csv', rows); toast('Rapor kedisiplinan diekspor (CSV)'); });$('#export-buku').addEventListener('click', ()=>{ const rows = [['Judul Kitab','Mu\'allif','Kategori','Rak','Total Stok','Tersedia']]; buku.forEach(b => rows.push([b.judul, b.pengarang, b.kategori, b.rak, b.stok, (+b.stok||0)-bukuDipinjam(b.id)])); downloadCSV('katalog-kitab.csv', rows); toast('Katalog diekspor (CSV)'); });$('#export-transaksi').addEventListener('click', ()=>{ const rows = [['Kitab','Santri','Tgl Pinjam','Jatuh Tempo','Tgl Kembali','Status','Jenis Ta\'zir','Denda Finansial','Ta\'zir Hafalan']]; pinjam.forEach(p => { const a = anggota.find(x=>x.id===p.anggotaId), b = buku.find(x=>x.id===p.bukuId); rows.push([b?b.judul:'?', a?a.nama:'?', p.tglPinjam, p.jatuhTempo, p.kembali||'-', p.kembali?'Selesai':(isOverdue(p)?'Overdue':'Dipinjam'), p.dendaJenis||'-', p.dendaNominal||0, p.dendaEdukatif||'-']); }); downloadCSV('transaksi-peminjaman.csv', rows); toast('Transaksi diekspor (CSV)'); });// ---------- CLOCK & PRAYER REMINDER ---------- let prayers = [{n:'Subuh', h:4, m:30}, {n:'Dzuhur', h:12, m:0}, {n:'Ashar', h:15, m:15}, {n:'Maghrib', h:18, m:0}, {n:'Isya', h:19, m:15}]; function tickClock(){ const now = new Date(); $('#pp-clock').textContent = now.toLocaleTimeString('id-ID',{hour:'2-digit',minute:'2-digit',second:'2-digit'}); $('#pp-date').textContent = now.toLocaleDateString('id-ID',{weekday:'short',day:'numeric',month:'short',year:'numeric'}); } function renderReminder(){ const now = new Date(); const curMin = now.getHours()*60 + now.getMinutes(); let next=null, diff=Infinity; prayers.forEach(p=>{ let pm=p.h*60+p.m; let d=pm-curMin; if(d<0) d+=1440; if(d{ const pm=p.h*60+p.m; const d=pm-curMin; return d>=0 && d<=20; }); const box = $('#pp-reminder'); if(soon){ box.innerHTML = `
Persiapan Jamaah ${soon.n} (${((soon.h*60+soon.m)-curMin)} menit lagi). Mohon selesaikan proses mutala'ah.
`; } else { const hh = String(next.h).padStart(2,'0'), mm = String(next.m).padStart(2,'0'); box.innerHTML = `
Waktu Shalat Berikutnya: ${esc(next.n)} (${hh}:${mm})
`; } }// ---------- THREE.JS BACKGROUND CANVAS ---------- function init3dCanvas(){ const container = $('#pp-3d-canvas-container'); if(!container \vert{}\vert{} typeof THREE === 'undefined') return; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, container.clientWidth / container.clientHeight, 0.1, 1000); camera.position.z = 25; const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setSize(container.clientWidth, container.clientHeight); renderer.setPixelRatio(window.devicePixelRatio); container.appendChild(renderer.domElement); const geometry = new THREE.IcosahedronGeometry(12, 1); const material = new THREE.MeshBasicMaterial({ color: 0x34d399, wireframe: true, transparent: true, opacity: 0.2 }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); function animate(){ requestAnimationFrame(animate); mesh.rotation.x += 0.002; mesh.rotation.y += 0.003; renderer.render(scene, camera); } animate(); } // ---------- TAB DESKTOP LISTENERS ---------- $$('.pp-tab-3d').forEach(btn=>btn.addEventListener('click', ()=>{ switchTab(btn.dataset.tab); }));// ---------- INIT ---------- seed(); refresh(); renderAll(); tickClock(); renderReminder(); setInterval(tickClock, 1000); setInterval(renderReminder, 30000); setTimeout(init3dCanvas, 100); })();
Scroll to Top