Kalender Munfik

Kalender Munfiq — PLPK Kedawung

📅 Kalender Munfiq — PLPK Kedawung

Jadwal jemput KOIN: tgl 1–8 setiap bulan • Cetak kalender personal

Opsi Cetak Kalender

info Tips: Pilih tahun, bulan, dan munfiq yang ingin dicetak. Kalender akan otomatis menandai tanggal jemput KOIN.
s/d
Akan di-highlight pada kalender
Tips: Gunakan kertas A4 portrait untuk standard, A5 untuk saku

Preview Kalender

Tambah/Update Munfiq

Daftar Munfiq

NamaAlamatKontakPreferensiCatatanAksi

⚙️ Pengaturan Aplikasi

Maksimal 2MB, akan ditampilkan di header

📦 Kelola Data

info Data tersimpan lokal di perangkat ini. Gunakan fitur ekspor untuk backup dan impor untuk restore data.
'; w.document.write(html); w.document.close(); w.addEventListener('load', () => setTimeout(() => w.print(), 400)); }function openPrintPocketA5(opts) { const { year, monthsSel, munSel, pickStart, pickEnd, waLink, formURL, waQR, formQR, withCover } = opts; const w = window.open('', '_blank'); const styles = ` ${baseStyles('@page { size: A5 portrait; margin: 10mm; }')}`; let html = `Kalender Saku ${year}${styles}`; for (const m of munSel) { if (withCover) html += buildCoverHTML(m, year); for (const mi of monthsSel) { html += monthHTMLBlock({ titleLeft: esc(m.name), monthIdx: mi, year, pickStart, pickEnd, waQR, formQR, waLink, formURL }); } } html += ''; w.document.write(html); w.document.close(); w.addEventListener('load', () => setTimeout(() => w.print(), 400)); }function openPrintTwoPerA4(opts) { const { year, monthsSel, munSel, pickStart, pickEnd, waLink, formURL, waQR, formQR, withCover } = opts; const w = window.open('', '_blank'); const styles = ` ${baseStyles('@page { size: A4 portrait; margin: 10mm; }')}`; let html = `Kalender 2/bln — ${year}${styles}`; for (const m of munSel) { if (withCover) html += buildCoverHTML(m, year); for (let i = 0; i < monthsSel.length; i += 2) { const leftMi = monthsSel[i]; const rightMi = monthsSel[i + 1]; html += `
`; html += monthHTMLBlock({ titleLeft: esc(m.name), monthIdx: leftMi, year, pickStart, pickEnd, waQR, formQR, waLink, formURL }); if (rightMi !== undefined) { html += monthHTMLBlock({ titleLeft: esc(m.name), monthIdx: rightMi, year, pickStart, pickEnd, waQR, formQR, waLink, formURL }); } else { html += `
`; } html += `
`; } } html += ''; w.document.write(html); w.document.close(); w.addEventListener('load', () => setTimeout(() => w.print(), 400)); }function openPrintWall(opts) { const { year, monthsSel, munSel, pickStart, pickEnd, waLink, formURL, waQR, formQR } = opts; const w = window.open('', '_blank'); const styles = ``; let html = `Kalender Dinding ${year}${styles}`; for (const m of munSel) { const logo = state.settings.logoDataURL ? `` : ''; const qrWAHTML = waQR ? `
WA
` : ''; const header = `
${logo ? logo : ''}

${esc(m.name)} — Kalender ${year}

${qrWAHTML}
Ranting ${esc(state.settings.ranting || 'Kedawung')} • PLPK: ${esc(state.settings.plpk || '')} • WA: ${esc(state.settings.wa || '-')} • Jemput: tgl ${pickStart}–${pickEnd}
`; let grid = `
`; for (const mi of monthsSel) { grid += `

${months[mi]} ${year}

${tableHTML(year, mi, pickStart, pickEnd)}
`; } grid += `
`; html += `
${header}${grid}
`; } html += ''; w.document.write(html); w.document.close(); w.addEventListener('load', () => setTimeout(() => w.print(), 400)); }function buildWALink() { const n = (state.settings.wa || '').replace(/\D/g, ''); if (!n) return ''; const msg = encodeURIComponent('Assalamu'alaikum, PLPK. Saya munfiq Kedawung ingin konfirmasi/jadwal jemput. Terima kasih.'); return `https://wa.me/${n}?text=${msg}`; }function makeQRDataURL(text) { return new Promise(resolve => { if (!text || !window.QRCode) { resolve(''); return; } QRCode.toDataURL(text, { width: 128, margin: 1 }, (err, url) => resolve(err ? '' : url)); }); }// Import/Export functionality document.getElementById('exportJSON').addEventListener('click', () => { const blob = new Blob([JSON.stringify(state, null, 2)], { type: 'application/json' }); downloadBlob(blob, `kalender-munfiq-${new Date().toISOString().split('T')[0]}.json`); showToast('✅ Data berhasil diekspor ke file JSON', 'success'); });document.getElementById('importBtn').addEventListener('click', () => { const f = document.getElementById('importFile').files[0]; if (!f) { showToast('⚠️ Pilih file JSON terlebih dahulu', 'warning'); return; } const r = new FileReader(); r.onload = () => { try { const data = JSON.parse(r.result); if (!data || !data.settings || !Array.isArray(data.munfiq)) { throw new Error('Format file tidak valid'); } if (!confirm('Impor akan mengganti semua data yang ada. Lanjutkan?')) return; state = data; saveState(); renderSettings(); showToast('✅ Data berhasil diimpor dari file', 'success'); } catch(e) { showToast('❌ Gagal impor: ' + e.message, 'error'); } }; r.readAsText(f); });document.getElementById('wipeBtn').addEventListener('click', () => { if (!confirm('⚠️ Hapus SEMUA data aplikasi?')) return; if (!confirm('Yakin? Tindakan ini tidak dapat dibatalkan!')) return; localStorage.removeItem(STORAGE_KEY); state = defaultState(); saveState(); renderSettings(); showToast('✅ Semua data telah direset', 'success'); });// Utility functions function uid() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 8); }function esc(s) { return (s ?? '').toString().replace(/[&<>"']/g, m => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[m])); }function downloadBlob(blob, filename) { const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = filename; document.body.appendChild(a); a.click(); URL.revokeObjectURL(a.href); a.remove(); }// Initialize (function() { // Sync pickup range document.getElementById('calPickStart').value = state.settings.pickupStart || 1; document.getElementById('calPickEnd').value = state.settings.pickupEnd || 8; // Initial preview renderMiniPreview(); // Welcome message if (state.munfiq.length === 0) { setTimeout(() => { showToast('👋 Selamat datang! Mulai dengan menambah data munfiq di tab Munfiq', 'success', 5000); }, 1000); } // Add version info console.log('%c📅 Kalender Munfiq v2.0', 'color: #00a884; font-size: 14px; font-weight: bold'); console.log('Dibuat untuk PLPK Ranting Kedawung'); })();