SIAPDes v1.0

Pemerintah Desa

SIAPDes - Sistem Informasi Arsip Pemerintah Desa

Lupa Password?
© 2026 Pemerintah Desa. All Rights Reserved.
Hak Cipta © 2026 Evhan Syahreza

Dashboard Utama

Ringkasan statistik dan aktivitas arsip desa terkini.

👋 Selamat Datang di SIAPDes

Dashboard SIAPDes - Sistem Informasi Arsip Pemerintah Desa

Total Arsip

0

Arsip Hari Ini

0

Arsip Bulan Ini

0

Penting / Favorit

0

Tren Statistik Arsip per Bulan & Tahun

Arsip Per Kategori Utama

Arsip Terbaru di Upload

No. Arsip Nama Dokumen Kategori Tanggal Upload Status Aksi

Aktivitas Terakhir (Audit Log)

`, 'application/vnd.ms-excel'); } showToast('Ekspor Berhasil', `${rows.length} data arsip berhasil diekspor ke ${format.toUpperCase()}.`, 'success'); } function renderSuperAdminDashboard() { if (!document.getElementById('sa-page-dashboard') || typeof db_villages === 'undefined' || typeof USER_CREDENTIALS === 'undefined') return; const users = Object.entries(USER_CREDENTIALS).filter(([key, user]) => key !== 'superadmin' && user.role !== 'superadmin'); const activeUsers = users.filter(([, user]) => user.active !== false).length; const inactiveUsers = users.filter(([, user]) => user.active === false).length; const totalUsers = users.length; const activePercent = totalUsers ? Math.round(activeUsers / totalUsers * 100) : 0; const inactivePercent = totalUsers ? Math.round(inactiveUsers / totalUsers * 100) : 0; document.getElementById('sa-stat-villages').innerText = db_villages.length; document.getElementById('sa-stat-users').innerText = totalUsers; document.getElementById('sa-stat-active').innerText = activeUsers; document.getElementById('sa-stat-inactive').innerText = inactiveUsers; document.getElementById('sa-stat-active-percent').innerText = `${activePercent}% dari seluruh User`; document.getElementById('sa-stat-inactive-percent').innerText = `${inactivePercent}% dari seluruh User`; const statusCanvas = document.getElementById('sa-chart-status'); const statusEmpty = document.getElementById('sa-chart-status-empty'); if (statusCanvas && typeof Chart !== 'undefined') { statusEmpty?.classList.toggle('hidden', totalUsers > 0); if (saChartStatus) saChartStatus.destroy(); saChartStatus = new Chart(statusCanvas, { type: 'doughnut', data: { labels: ['Aktif', 'Nonaktif'], datasets: [{ data: [activeUsers, inactiveUsers], backgroundColor: ['#10b981', '#f43f5e'], borderColor: '#ffffff', borderWidth: 4, hoverOffset: 8 }] }, options: { responsive: true, maintainAspectRatio: false, cutout: '68%', plugins: { legend: { position: 'bottom', labels: { usePointStyle: true, padding: 18, font: { size: 11 } } }, tooltip: { callbacks: { label: context => ` ${context.label}: ${context.raw} User` } } } } }); } const villageLabels = db_villages.map(v => v.name); const villageCounts = db_villages.map(v => users.filter(([, user]) => user.villageId === v.id).length); const villageCanvas = document.getElementById('sa-chart-villages'); if (villageCanvas && typeof Chart !== 'undefined') { if (saChartVillages) saChartVillages.destroy(); saChartVillages = new Chart(villageCanvas, { type: 'bar', data: { labels: villageLabels, datasets: [{ label: 'Jumlah User', data: villageCounts, backgroundColor: ['#10b981', '#3b82f6', '#8b5cf6', '#f59e0b', '#06b6d4', '#f43f5e'], borderRadius: 7, maxBarThickness: 36 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, ticks: { precision: 0, stepSize: 1, font: { size: 10 } }, grid: { color: '#f1f5f9' } }, x: { ticks: { font: { size: 10 }, maxRotation: 35, minRotation: 0 }, grid: { display: false } } }, plugins: { legend: { display: false }, tooltip: { callbacks: { label: context => ` ${context.raw} User` } } } } }); } const summary = document.getElementById('sa-village-summary'); if (summary) summary.innerHTML = db_villages.map(v => { const villageUsers = users.filter(([, user]) => user.villageId === v.id); const status = v.status === 'inactive' ? 'Nonaktif' : 'Aktif'; return `${escapeHtml(v.name)}${escapeHtml(v.kec || '')}, ${escapeHtml(v.kab || '')}${villageUsers.length}${status}`; }).join(''); const activity = document.getElementById('sa-recent-activity'); if (activity) { const logs = Array.isArray(audit_logs) ? audit_logs.slice(0, 8) : []; activity.innerHTML = logs.length ? logs.map(log => `

${escapeHtml(log.aksi)}

${escapeHtml(log.user)} · ${escapeHtml(log.waktu)}

`).join('') : '

Belum ada aktivitas.

'; } } function renderSuperAdmin() { renderSuperAdminDashboard(); const body = document.getElementById('superadmin-table-body'); if (!body) return; const search = (document.getElementById('sa-search')?.value || '').trim().toLowerCase(); const statusFilter = document.getElementById('sa-status-filter')?.value || ''; const users = Object.entries(USER_CREDENTIALS).filter(([key, user]) => key !== 'superadmin' && user.role !== 'superadmin'); const rows = []; db_villages.forEach(village => { const linkedUsers = users.filter(([, user]) => user.villageId === village.id); const candidates = linkedUsers.length ? linkedUsers : [[null, null]]; candidates.forEach(([key, user]) => { const haystack = [village.name, village.kec, village.kab, key, user?.nama, user?.jabatan, user?.badge].join(' ').toLowerCase(); const accountStatus = user ? (user.active === false ? 'inactive' : 'active') : village.status; if (search && !haystack.includes(search)) return; if (statusFilter && accountStatus !== statusFilter) return; rows.push({ village, key, user }); }); }); const superUser = USER_CREDENTIALS.superadmin; if (superUser && (!statusFilter || (superUser.active === false ? 'inactive' : 'active') === statusFilter) && (!search || ['super admin','superadmin',superUser.nama].join(' ').toLowerCase().includes(search))) rows.unshift({ village: { name: 'Semua Desa', id: '*' }, key: 'superadmin', user: superUser }); body.innerHTML = rows.map(({ village, key, user }) => { const isSuper = key === 'superadmin'; const active = user ? user.active !== false : village.status !== 'inactive'; const password = user ? (isSuper ? '••••••••' : (superPasswordVisible[key] ? escapeHtml(user.pass) : '••••••••')) : '—'; const statusLabel = active ? 'Aktif' : 'Nonaktif'; const encodedKey = key ? encodeURIComponent(key) : ''; const encodedVillage = encodeURIComponent(village.id); return `
${escapeHtml(village.name)}
${escapeHtml(village.kec || '')}, ${escapeHtml(village.kab || '')}
${escapeHtml(user?.nama || 'Belum ada akun')}
${escapeHtml(user?.jabatan || '—')}
${escapeHtml(key || '—')} ${password}${user && !isSuper ? `` : ''} ${escapeHtml(user?.badge || '—')} ${statusLabel}
${`${!isSuper && village.id !== '*' ? `` : ''}`}${user && !isSuper ? `` : ''}
`; }).join(''); document.getElementById('superadmin-empty')?.classList.toggle('hidden', rows.length !== 0); const allUsers = users.length; document.getElementById('sa-stat-villages').innerText = db_villages.length; document.getElementById('sa-stat-users').innerText = allUsers; document.getElementById('sa-stat-active').innerText = users.filter(([, u]) => u.active !== false).length; document.getElementById('sa-stat-inactive').innerText = users.filter(([, u]) => u.active === false).length; } function openSuperVillageModal(villageId = '') { if (currentRole !== 'superadmin') { showToast('Akses Ditolak', 'Hanya Super Admin yang dapat mengelola Desa.', 'warning'); return; } populateVillageSelects(); const modal = document.getElementById('modal-super-village'); const village = db_villages.find(v => v.id === villageId); document.getElementById('form-super-village').reset(); document.getElementById('super-village-id').value = village?.id || ''; document.getElementById('super-village-name').value = village?.name || ''; document.getElementById('super-village-kec').value = village?.kec || ''; document.getElementById('super-village-kab').value = village?.kab || ''; document.getElementById('super-village-address').value = village?.address || ''; document.getElementById('super-village-status').value = village?.status || 'active'; const linked = village ? Object.entries(USER_CREDENTIALS).find(([key, u]) => key !== 'superadmin' && u.villageId === village.id) : null; document.getElementById('super-user-name').value = linked?.[1]?.nama || ''; document.getElementById('super-user-username').value = linked?.[0] || ''; document.getElementById('super-user-jabatan').value = linked?.[1]?.jabatan || ''; document.getElementById('super-user-role').value = linked?.[1]?.role || 'admin'; document.getElementById('super-user-password').value = linked?.[1]?.pass || ''; const editing = Boolean(village); document.getElementById('super-village-modal-title').innerHTML = `${editing ? 'Edit Data Desa' : 'Tambah Desa & Akun User'}`; document.getElementById('super-user-username').required = !editing; document.getElementById('super-user-password').required = !editing; modal.classList.remove('hidden'); setTimeout(() => { modal.classList.remove('opacity-0'); modal.querySelector('div').classList.remove('scale-95'); }, 10); } function closeSuperVillageModal() { const modal = document.getElementById('modal-super-village'); modal.classList.add('opacity-0'); modal.querySelector('div').classList.add('scale-95'); setTimeout(() => modal.classList.add('hidden'), 300); } function saveSuperVillage(e) { e.preventDefault(); if (currentRole !== 'superadmin') return; const id = document.getElementById('super-village-id').value; const name = document.getElementById('super-village-name').value.trim(); const kec = document.getElementById('super-village-kec').value.trim(); const kab = document.getElementById('super-village-kab').value.trim(); const address = document.getElementById('super-village-address').value.trim(); const status = document.getElementById('super-village-status').value; if (!name || !kec || !kab) { showToast('Data Belum Lengkap', 'Nama Desa, Kecamatan, dan Kabupaten wajib diisi.', 'warning'); return; } if (id) { const village = db_villages.find(v => v.id === id); if (!village) return; Object.assign(village, { name, kec, kab, address, status, updatedAt: new Date().toISOString() }); const username = document.getElementById('super-user-username').value.trim().toLowerCase(); const nameUser = document.getElementById('super-user-name').value.trim(); const password = document.getElementById('super-user-password').value; if (username || nameUser || password) { const linked = Object.entries(USER_CREDENTIALS).find(([key, u]) => key !== 'superadmin' && u.villageId === id); const targetKey = linked?.[0] || username; if (!targetKey || (!linked && USER_CREDENTIALS[targetKey])) { showToast('Username Dipakai', 'Username tersebut sudah terdaftar.', 'warning'); return; } const old = USER_CREDENTIALS[targetKey] || {}; const role = document.getElementById('super-user-role').value; const meta = ROLE_META[role]; USER_CREDENTIALS[targetKey] = { ...old, role, pass: password || old.pass || 'ubah-password', nama: nameUser || old.nama || targetKey, jabatan: document.getElementById('super-user-jabatan').value.trim() || old.jabatan || 'Administrator Desa', badge: meta.badge, color: meta.color, active: old.active !== false, villageId: id }; } showToast('Desa Diperbarui', `Data Desa ${name} berhasil disimpan.`, 'success'); } else { if (db_villages.some(v => v.name.toLowerCase() === name.toLowerCase())) { showToast('Desa Sudah Ada', 'Nama Desa tersebut sudah terdaftar.', 'warning'); return; } const username = document.getElementById('super-user-username').value.trim().toLowerCase(); const password = document.getElementById('super-user-password').value; const userName = document.getElementById('super-user-name').value.trim(); if (!username || !password || !userName) { showToast('Akun Belum Lengkap', 'Nama user, username, dan password wajib diisi saat menambah Desa.', 'warning'); return; } if (USER_CREDENTIALS[username]) { showToast('Username Dipakai', 'Username tersebut sudah terdaftar.', 'warning'); return; } const villageId = makeVillageId(); db_villages.push({ id: villageId, name, kec, kab, address, status, createdAt: new Date().toISOString() }); const role = document.getElementById('super-user-role').value; const meta = ROLE_META[role]; USER_CREDENTIALS[username] = { role, pass: password, nama: userName, jabatan: document.getElementById('super-user-jabatan').value.trim() || 'Administrator Desa', badge: meta.badge, color: meta.color, active: true, villageId }; showToast('Berhasil', `Desa ${name} dan akun ${username} berhasil ditambahkan.`, 'success'); } saveSuperAdminData(); populateVillageSelects(); renderSuperAdmin(); updateSidebarRoleDropdown(); closeSuperVillageModal(); logActivity(document.getElementById('user-display').innerText, id ? `Mengubah data Desa: ${name}` : `Menambahkan Desa: ${name}`); } function deleteSuperVillage(villageId) { if (currentRole !== 'superadmin') return; const village = db_villages.find(v => v.id === villageId); if (!village) return; if (!confirm(`Hapus Desa "${village.name}"? Akun yang terkait akan tetap disimpan tetapi relasinya dilepas.`)) return; db_villages = db_villages.filter(v => v.id !== villageId); Object.values(USER_CREDENTIALS).forEach(user => { if (user.villageId === villageId) user.villageId = ''; }); saveSuperAdminData(); populateVillageSelects(); renderSuperAdmin(); showToast('Desa Dihapus', 'Desa dihapus dan akun terkait tetap dipertahankan.', 'warning'); } function toggleSuperUserStatus(key) { if (currentRole !== 'superadmin' || !USER_CREDENTIALS[key] || key === 'superadmin') return; const session = JSON.parse(sessionStorage.getItem('siaddes_session') || 'null'); if (session && session.villageId === USER_CREDENTIALS[key].villageId && session.pass === USER_CREDENTIALS[key].pass && session.role === USER_CREDENTIALS[key].role) { showToast('Aksi Ditolak', 'Tidak dapat menonaktifkan akun yang sedang aktif.', 'warning'); return; } USER_CREDENTIALS[key].active = USER_CREDENTIALS[key].active === false; saveSuperAdminData(); renderSuperAdmin(); updateSidebarRoleDropdown(); showToast('Status Diperbarui', `Akun ${key} sekarang ${USER_CREDENTIALS[key].active ? 'aktif' : 'nonaktif'}.`, 'success'); } function toggleSuperPassword(key) { if (currentRole !== 'superadmin' || !USER_CREDENTIALS[key]) return; superPasswordVisible[key] = !superPasswordVisible[key]; renderSuperAdmin(); } function togglePasswordField(inputId, button) { const input = document.getElementById(inputId); if (!input) return; input.type = input.type === 'password' ? 'text' : 'password'; const icon = button?.querySelector('i'); if (icon) { icon.classList.toggle('fa-eye'); icon.classList.toggle('fa-eye-slash'); } } // ================= END SUPER ADMIN MODULE ================= function populateSubKategori(catValue) { const dropdown = document.getElementById('form-subkategori'); dropdown.innerHTML = ''; if(SUB_KATEGORI_MAP[catValue]) { SUB_KATEGORI_MAP[catValue].forEach(sub => { dropdown.innerHTML += ``; }); } } function populateEditSubKategori(catValue) { const dropdown = document.getElementById('edit-subkategori'); dropdown.innerHTML = ''; if(SUB_KATEGORI_MAP[catValue]) { SUB_KATEGORI_MAP[catValue].forEach(sub => { dropdown.innerHTML += ``; }); } } function logActivity(user, action) { const now = new Date(); const timeStr = now.toLocaleDateString('id-ID', {day:'2-digit', month:'short'}) + ' - ' + now.toLocaleTimeString('id-ID', {hour:'2-digit', minute:'2-digit'}); audit_logs.unshift({ waktu: timeStr, user, aksi: action }); renderDashboard(); } function renderDashboard() { const active = db_archives.filter(a => !a.deleted); document.getElementById('count-total').innerText = active.length; document.getElementById('count-today').innerText = active.filter(a => a.tgl_upload === new Date().toISOString().split('T')[0]).length; document.getElementById('count-month').innerText = active.filter(a => a.tgl_upload.startsWith(new Date().toISOString().substring(0,7))).length; document.getElementById('count-fav').innerText = active.filter(a => a.favorit).length; const recentContainer = document.getElementById('dash-recent-table'); recentContainer.innerHTML = ''; const recent = active.slice(0, 5); if (recent.length === 0) { recentContainer.innerHTML = `Belum ada dokumen.`; } else { recent.forEach(doc => { let statusBadge = doc.status === 'Penting' ? 'bg-amber-100 text-amber-800' : (doc.status === 'Proses' ? 'bg-blue-100 text-blue-800' : 'bg-emerald-100 text-emerald-800'); recentContainer.innerHTML += ` ${doc.no_dokumen} ${doc.nama} ${doc.kategori} ${doc.tgl_upload} ${doc.status} `; }); } const auditContainer = document.getElementById('dash-audit-logs'); if (auditContainer) { auditContainer.innerHTML = ''; audit_logs.forEach(log => { auditContainer.innerHTML += `
${log.user} ${log.waktu}

${log.aksi}

`; }); } updateCharts(); } function initCharts() { const ctxTren = document.getElementById('chartTren'); if (ctxTren) { chart1 = new Chart(ctxTren, { type: 'line', data: { labels: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'], datasets: [{ label: 'Jumlah Arsip', data: [5, 8, 12, 10, 15, 9, db_archives.length], borderColor: '#059669', backgroundColor: 'rgba(5, 150, 105, 0.1)', fill: true, tension: 0.3 }] }, options: { responsive: true, maintainAspectRatio: false } }); } const ctxKategori = document.getElementById('chartKategori'); if (ctxKategori) { chart2 = new Chart(ctxKategori, { type: 'doughnut', data: { labels: ['Perencanaan', 'Keuangan', 'Produk Hukum', 'Pemerintahan', 'Lainnya'], datasets: [{ data: [1, 2, 1, 0, 0], backgroundColor: ['#0ea5e9', '#059669', '#f59e0b', '#6366f1', '#ec4899'] }] }, options: { responsive: true, maintainAspectRatio: false } }); } updateCharts(); } function updateCharts() { if (!chart1 || !chart2) return; const active = db_archives.filter(a => !a.deleted); const catCounts = { 'Perencanaan': 0, 'Keuangan': 0, 'Produk Hukum': 0, 'Pemerintahan': 0, 'Lainnya': 0 }; active.forEach(a => { if (catCounts[a.kategori] !== undefined) { catCounts[a.kategori]++; } else { catCounts['Lainnya']++; } }); chart2.data.datasets[0].data = Object.values(catCounts); chart2.update(); } function renderArchiveTable(archives) { const tbody = document.getElementById('master-arsip-table'); if (!tbody) return; tbody.innerHTML = ''; const isReadOnly = (currentRole === 'kades' || currentRole === 'bpd'); const active = archives.filter(a => !a.deleted); if (active.length === 0) { tbody.innerHTML = `Tidak ada data arsip yang ditemukan.`; return; } active.forEach(doc => { let statusBadge = doc.status === 'Penting' ? 'bg-amber-100 text-amber-800' : (doc.status === 'Proses' ? 'bg-blue-100 text-blue-800' : 'bg-emerald-100 text-emerald-800'); let favIcon = doc.favorit ? 'fa-solid text-amber-400' : 'fa-regular text-gray-300'; tbody.innerHTML += ` ${doc.no_dokumen} ${doc.nama} ${doc.kategori} ${doc.tahun} / ${doc.tgl_dokumen} ${doc.penginput} ${doc.status}
`; }); } function applyFilters() { const search = document.getElementById('filter-search')?.value.toLowerCase() || ''; const kat = document.getElementById('filter-kategori')?.value || ''; const thn = document.getElementById('filter-tahun')?.value || ''; const startDate = document.getElementById('filter-start-date')?.value || ''; const endDate = document.getElementById('filter-end-date')?.value || ''; const bulan = document.getElementById('filter-bulan')?.value || ''; const penginput = document.getElementById('filter-penginput')?.value.toLowerCase() || ''; const keyword = document.getElementById('filter-keyword')?.value.toLowerCase() || ''; const filtered = db_archives.filter(doc => { if (doc.deleted) return false; if (search && !(doc.no_dokumen.toLowerCase().includes(search) || doc.nama.toLowerCase().includes(search))) return false; if (kat && doc.kategori !== kat) return false; if (thn && doc.tahun.toString() !== thn) return false; if (startDate && doc.tgl_dokumen < startDate) return false; if (endDate && doc.tgl_dokumen > endDate) return false; if (bulan && (new Date(doc.tgl_dokumen).getMonth() + 1).toString() !== bulan) return false; if (penginput && !doc.penginput.toLowerCase().includes(penginput)) return false; if (keyword && !(doc.keywords && doc.keywords.toLowerCase().includes(keyword))) return false; return true; }); renderArchiveTable(filtered); } function quickSearch(val) { if (document.getElementById('filter-search')) { document.getElementById('filter-search').value = val; applyFilters(); } } function toggleFavorite(id) { const doc = db_archives.find(a => a.id === id); if (doc) { doc.favorit = !doc.favorit; localStorage.setItem('siaddes_arsip', JSON.stringify(db_archives)); applyFilters(); renderDashboard(); } } function openUploadModal() { if (currentRole === 'kades' || currentRole === 'bpd') { showToast("Akses Terbatas", "Anda tidak memiliki hak untuk menambah dokumen.", "warning"); return; } const modal = document.getElementById('modal-upload'); modal.classList.remove('hidden'); setTimeout(() => { modal.classList.remove('opacity-0'); modal.querySelector('div').classList.remove('scale-95'); }, 10); } function closeUploadModal() { const modal = document.getElementById('modal-upload'); modal.classList.add('opacity-0'); modal.querySelector('div').classList.add('scale-95'); setTimeout(() => { modal.classList.add('hidden'); document.getElementById('form-arsip').reset(); document.getElementById('selected-files-list').innerHTML = ''; document.getElementById('selected-files-list').classList.add('hidden'); }, 300); } let uploadedFileUrl = null; function handleFileSelect(e) { const file = e.target.files[0]; const list = document.getElementById('selected-files-list'); if (file) { list.innerText = `Terpilih: ${file.name} (${(file.size/1024).toFixed(1)} KB)`; list.classList.remove('hidden'); const reader = new FileReader(); reader.onload = function(evt) { uploadedFileUrl = evt.target.result; }; reader.readAsDataURL(file); } else { list.innerText = ''; list.classList.add('hidden'); uploadedFileUrl = null; } } function saveNewArchive(e) { e.preventDefault(); const no_dokumen = document.getElementById('form-no-dokumen').value.trim(); const nama = document.getElementById('form-nama-dokumen').value.trim(); const kategori = document.getElementById('form-kategori').value; const sub_kategori = document.getElementById('form-subkategori').value; const tahun = parseInt(document.getElementById('form-tahun').value); const tgl_dokumen = document.getElementById('form-tgl-dokumen').value; const keywords = document.getElementById('form-keywords').value; const ringkasan = document.getElementById('form-ringkasan').value; const penginput = document.getElementById('user-display').innerText; const newId = "ARS-" + tahun + "-" + String(db_archives.length + 1).padStart(3, '0'); const todayStr = new Date().toISOString().split('T')[0]; const newDoc = { id: newId, no_dokumen, nama, kategori, sub_kategori, tahun, tgl_dokumen, tgl_upload: todayStr, penginput, status: "Selesai", keywords, ringkasan, favorit: false, deleted: false, fileUrl: uploadedFileUrl }; db_archives.unshift(newDoc); localStorage.setItem('siaddes_arsip', JSON.stringify(db_archives)); logActivity(penginput, `Mengunggah arsip baru: ${nama}`); showToast("Berhasil", "Arsip dokumen berhasil disimpan!", "success"); closeUploadModal(); applyFilters(); renderDashboard(); generateReportPreview(); } // --- EDIT ARSIP FUNCTIONS --- function openEditArchiveModal(id) { const doc = db_archives.find(a => a.id === id); if (!doc) return; document.getElementById('edit-arsip-id').value = doc.id; document.getElementById('edit-no-dokumen').value = doc.no_dokumen; document.getElementById('edit-nama-dokumen').value = doc.nama; document.getElementById('edit-kategori').value = doc.kategori; populateEditSubKategori(doc.kategori); document.getElementById('edit-subkategori').value = doc.sub_kategori; document.getElementById('edit-tahun').value = doc.tahun; document.getElementById('edit-tgl-dokumen').value = doc.tgl_dokumen; document.getElementById('edit-keywords').value = doc.keywords || ''; document.getElementById('edit-status').value = doc.status || 'Selesai'; document.getElementById('edit-ringkasan').value = doc.ringkasan || ''; const modal = document.getElementById('modal-edit-arsip'); modal.classList.remove('hidden'); setTimeout(() => { modal.classList.remove('opacity-0'); modal.querySelector('div').classList.remove('scale-95'); }, 10); } function closeEditArchiveModal() { const modal = document.getElementById('modal-edit-arsip'); modal.classList.add('opacity-0'); modal.querySelector('div').classList.add('scale-95'); setTimeout(() => modal.classList.add('hidden'), 300); } function saveEditedArchive(e) { e.preventDefault(); const id = document.getElementById('edit-arsip-id').value; const docIndex = db_archives.findIndex(a => a.id === id); if (docIndex === -1) return; db_archives[docIndex].no_dokumen = document.getElementById('edit-no-dokumen').value.trim(); db_archives[docIndex].nama = document.getElementById('edit-nama-dokumen').value.trim(); db_archives[docIndex].kategori = document.getElementById('edit-kategori').value; db_archives[docIndex].sub_kategori = document.getElementById('edit-subkategori').value; db_archives[docIndex].tahun = parseInt(document.getElementById('edit-tahun').value); db_archives[docIndex].tgl_dokumen = document.getElementById('edit-tgl-dokumen').value; db_archives[docIndex].keywords = document.getElementById('edit-keywords').value; db_archives[docIndex].status = document.getElementById('edit-status').value; db_archives[docIndex].ringkasan = document.getElementById('edit-ringkasan').value; localStorage.setItem('siaddes_arsip', JSON.stringify(db_archives)); logActivity(document.getElementById('user-display').innerText, `Mengubah data arsip: ${db_archives[docIndex].nama}`); showToast("Berhasil", "Data arsip berhasil diubah!", "success"); closeEditArchiveModal(); applyFilters(); renderDashboard(); generateReportPreview(); } // --- RECYCLE BIN FUNCTIONS --- function deleteArchive(id) { const doc = db_archives.find(a => a.id === id); if (doc) { if (confirm(`Apakah Anda yakin ingin memindahkan arsip "${doc.nama}" ke Recycle Bin?`)) { doc.deleted = true; localStorage.setItem('siaddes_arsip', JSON.stringify(db_archives)); logActivity(document.getElementById('user-display').innerText, `Memindahkan arsip ke Recycle Bin: ${doc.nama}`); showToast("Peringatan", "Arsip dipindahkan ke Recycle Bin.", "warning"); applyFilters(); renderDashboard(); renderRecycleBin(); generateReportPreview(); } } } function renderRecycleBin() { const tbody = document.getElementById('recycle-arsip-table'); if (!tbody) return; tbody.innerHTML = ''; const deletedDocs = db_archives.filter(a => a.deleted); const isReadOnly = (currentRole === 'kades' || currentRole === 'bpd'); if (deletedDocs.length === 0) { tbody.innerHTML = `Recycle Bin kosong.`; return; } deletedDocs.forEach(doc => { tbody.innerHTML += ` ${doc.no_dokumen} ${doc.nama} ${doc.kategori} ${doc.penginput}
`; }); } function restoreArchive(id) { const doc = db_archives.find(a => a.id === id); if (doc) { doc.deleted = false; localStorage.setItem('siaddes_arsip', JSON.stringify(db_archives)); logActivity(document.getElementById('user-display').innerText, `Memulihkan arsip: ${doc.nama}`); showToast("Berhasil", "Arsip dipulihkan.", "success"); applyFilters(); renderDashboard(); renderRecycleBin(); generateReportPreview(); } } function deleteArchivePermanently(id) { const index = db_archives.findIndex(a => a.id === id); if (index !== -1) { const doc = db_archives[index]; if (confirm(`Tindakan ini tidak bisa dibatalkan! Hapus secara permanen arsip "${doc.nama}"?`)) { db_archives.splice(index, 1); localStorage.setItem('siaddes_arsip', JSON.stringify(db_archives)); logActivity(document.getElementById('user-display').innerText, `Menghapus permanen arsip: ${doc.nama}`); showToast("Terhapus", "Arsip dihapus secara permanen.", "warning"); applyFilters(); renderDashboard(); renderRecycleBin(); generateReportPreview(); } } } // --- SETTINGS CONFIG FUNCTIONS --- function saveSettings(e) { e.preventDefault(); const config = { desa: document.getElementById('set-desa').value.trim(), kec: document.getElementById('set-kec').value.trim(), kab: document.getElementById('set-kab').value.trim(), kades: document.getElementById('set-kades').value.trim(), sekdes: document.getElementById('set-sekdes').value.trim(), alamat: document.getElementById('set-alamat').value.trim(), telp: document.getElementById('set-telp').value.trim(), email: document.getElementById('set-email').value.trim() }; localStorage.setItem('config_desa', JSON.stringify(config)); if (base64Logo) { localStorage.setItem('desa_logo_base64', base64Logo); } document.getElementById('sb-desa-name').innerText = "Desa " + config.desa; document.getElementById('pr-desa').innerText = "PEMERINTAH DESA " + config.desa.toUpperCase(); document.getElementById('pr-kec').innerText = "KECAMATAN " + config.kec.toUpperCase(); document.getElementById('pr-kab').innerText = "PEMERINTAH KABUPATEN " + config.kab.toUpperCase(); document.getElementById('pr-alamat').innerText = "Alamat Kantor Desa: " + config.alamat; document.getElementById('print-sign-kades').innerText = config.kades.toUpperCase(); document.getElementById('login-desa-title').innerText = "Pemerintah Desa " + config.desa; document.getElementById('login-copyright-desa').innerText = "Pemerintah Desa " + config.desa; applyLogoState(); showToast("Berhasil", "Konfigurasi Pemerintah Desa disimpan!", "success"); logActivity(document.getElementById('user-display').innerText, "Mengubah konfigurasi profil desa"); } // --- LAPORAN & CETAK FUNCTIONS --- function generateReportPreview() { const year = document.getElementById('lap-tahun').value; const cat = document.getElementById('lap-kategori').value; const status = document.getElementById('lap-status').value; const filtered = db_archives.filter(doc => { if (doc.deleted) return false; if (year !== "Semua" && doc.tahun.toString() !== year) return false; if (cat !== "Semua" && doc.kategori !== cat) return false; if (status !== "Semua" && doc.status !== status) return false; return true; }); const tbody = document.getElementById('lap-preview-table'); if(!tbody) return; tbody.innerHTML = ''; document.getElementById('lap-total-row').innerText = `Total: ${filtered.length} Data`; if (filtered.length === 0) { tbody.innerHTML = `Tidak ada data sesuai filter laporan.`; return; } filtered.forEach((doc, idx) => { let statusBadge = doc.status === 'Penting' ? 'bg-amber-100 text-amber-800' : (doc.status === 'Proses' ? 'bg-blue-100 text-blue-800' : 'bg-emerald-100 text-emerald-800'); tbody.innerHTML += ` ${idx + 1} ${doc.no_dokumen} ${doc.nama} ${doc.kategori} ${doc.tgl_upload} ${doc.status} `; }); } function printReport(type) { const today = new Date(); document.getElementById('print-current-date').innerText = today.toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' }); const titleEl = document.getElementById('print-report-title'); if (type === 'all') { titleEl.innerText = "LAPORAN REKAPAN SELURUH DATA ARSIP DOKUMEN DESA"; applyFilters(); } else if (type === 'laporan') { const year = document.getElementById('lap-tahun').value; const cat = document.getElementById('lap-kategori').value; titleEl.innerText = `LAPORAN REKAPAN DATA ARSIP DESA (${cat.toUpperCase()} - TAHUN ${year})`; generateReportPreview(); } window.print(); } function simulateExport(format) { showToast(`Ekspor ${format}`, `Sedang memproses dokumen ekspor ke format ${format}...`, "info"); setTimeout(() => { showToast("Selesai", `Dokumen berhasil diekspor sebagai file ${format}!`, "success"); }, 1500); } // --- BACKUP & RESTORE FUNCTIONS --- function triggerBackup(format) { try { const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(db_archives, null, 2)); const downloadAnchor = document.createElement('a'); downloadAnchor.setAttribute("href", dataStr); downloadAnchor.setAttribute("download", `SIAPDes_Backup_${new Date().toISOString().slice(0,10)}.json`); document.body.appendChild(downloadAnchor); downloadAnchor.click(); downloadAnchor.remove(); const nowStr = new Date().toLocaleString('id-ID'); localStorage.setItem('siaddes_last_backup', nowStr); document.getElementById('lbl-last-backup').innerText = nowStr; showToast("Backup Berhasil", "File cadangan JSON berhasil diunduh.", "success"); logActivity(document.getElementById('user-display').innerText, "Melakukan Backup Database Sistem"); } catch (err) { showToast("Gagal Backup", "Terjadi kesalahan saat mengunduh data.", "warning"); } } function triggerRestore() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = e => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = readerEvent => { try { const content = JSON.parse(readerEvent.target.result); if (Array.isArray(content)) { if (confirm("Apakah Anda yakin ingin me-restore data? Tindakan ini akan menimpa seluruh data arsip saat ini!")) { db_archives = content; localStorage.setItem('siaddes_arsip', JSON.stringify(db_archives)); showToast("Restore Berhasil", "Sistem berhasil dipulihkan ke titik cadangan.", "success"); logActivity(document.getElementById('user-display').innerText, "Melakukan Restore Database Sistem"); applyFilters(); renderDashboard(); generateReportPreview(); } } else { showToast("Format Salah", "Isi file cadangan tidak valid.", "warning"); } } catch (err) { showToast("Gagal Membaca", "File tidak terbaca atau rusak.", "warning"); } }; reader.readAsText(file); }; input.click(); }