Files
2026-06-11 02:38:02 +00:00

253 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dasbor Admin - Dinsos Pontianak</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; }
</style>
</head>
<body class="bg-gray-100 text-gray-900 min-h-screen flex flex-col">
<nav class="bg-white border-b border-gray-200 p-4 sticky top-0 z-30 flex justify-between items-center px-8">
<div class="flex items-center gap-3">
<div class="w-3 h-8 bg-gray-800 rounded-full"></div>
<h1 class="text-lg font-bold tracking-tight text-gray-800">Dinsos Admin Dashboard</h1>
</div>
<div class="flex items-center gap-6">
<span class="text-sm font-medium text-gray-600">Administrator Utama</span>
<button onclick="eksekusi_logout_admin()" class="text-xs font-bold text-red-600 hover:bg-red-50 px-3 py-2 rounded-lg transition-colors">Keluar Sistem</button>
</div>
</nav>
<main class="flex-grow p-6 max-w-6xl mx-auto w-full space-y-8">
<div id="banner_error_admin" class="hidden p-4 bg-red-50 border border-red-200 text-red-700 rounded-xl text-sm font-medium"></div>
<section class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-white p-5 rounded-xl border border-gray-200 shadow-sm">
<span class="text-xs text-gray-400 font-bold uppercase tracking-wider block mb-1">Total Baseline</span>
<p class="text-3xl font-bold text-gray-800" id="stat_total_warga">0</p>
</div>
<div class="bg-white p-5 rounded-xl border border-gray-200 shadow-sm border-l-4 border-l-yellow-500">
<span class="text-xs text-yellow-600 font-bold uppercase tracking-wider block mb-1">Menunggu Validasi</span>
<p class="text-3xl font-bold text-gray-800" id="stat_pending_warga">0</p>
</div>
<div class="bg-white p-5 rounded-xl border border-gray-200 shadow-sm border-l-4 border-l-emerald-600">
<span class="text-xs text-emerald-600 font-bold uppercase tracking-wider block mb-1">Disetujui (Verified)</span>
<p class="text-3xl font-bold text-gray-800" id="stat_verified_warga">0</p>
</div>
<div class="bg-white p-5 rounded-xl border border-gray-200 shadow-sm border-l-4 border-l-red-600">
<span class="text-xs text-red-600 font-bold uppercase tracking-wider block mb-1">Ditolak (Rejected)</span>
<p class="text-3xl font-bold text-gray-800" id="stat_rejected_warga">0</p>
</div>
</section>
<section class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<div class="p-5 border-b border-gray-200 bg-gray-50 flex justify-between items-center">
<h3 class="font-bold text-gray-800 text-sm">Verifikasi Data Kemiskinan Masyarakat</h3>
<span id="label_total_baris" class="text-xs bg-gray-200 text-gray-700 px-2.5 py-1 rounded-md font-semibold">0 Rekaman</span>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead>
<tr class="bg-gray-100 border-b border-gray-200 text-[11px] font-bold uppercase text-gray-500 tracking-wider">
<th class="p-4">No</th>
<th class="p-4">Nama KK</th>
<th class="p-4">NIK</th>
<th class="p-4">No KK</th>
<th class="p-4">Tanggungan</th>
<th class="p-4 text-center">Status</th>
<th class="p-4 text-center">Aksi Manajemen</th>
</tr>
</thead>
<tbody id="tabel_body_mustahik" class="divide-y divide-gray-200 text-sm text-gray-700">
</tbody>
</table>
</div>
<div id="pesan_tabel_kosong" class="p-10 text-center text-gray-400 text-sm hidden">Tidak ada rekaman data warga di dalam sistem.</div>
</section>
</main>
<div id="modal_konfirmasi" class="fixed inset-0 z-50 flex items-center justify-center hidden bg-gray-900 bg-opacity-50 backdrop-blur-sm transition-opacity">
<div class="bg-white p-6 rounded-2xl shadow-2xl w-full max-w-sm border border-gray-200 transform transition-all">
<div class="flex items-center gap-4 mb-4">
<div class="w-12 h-12 rounded-full bg-yellow-50 flex items-center justify-center text-yellow-500 flex-shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-900">Konfirmasi Aksi</h3>
<p class="text-xs text-gray-500 mt-1">Tindakan ini akan mengubah basis data.</p>
</div>
</div>
<p id="teks_pesan_modal" class="text-sm text-gray-700 mb-6 py-2 border-y border-gray-100">
Apakah Anda yakin ingin mengubah status data ini?
</p>
<div class="flex justify-end gap-3">
<button onclick="tutup_modal()" class="px-4 py-2.5 text-xs font-bold text-gray-600 bg-gray-100 hover:bg-gray-200 rounded-xl transition-colors">Batalkan</button>
<button onclick="eksekusi_perubahan_status()" id="btn_modal_setuju" class="px-4 py-2.5 text-xs font-bold text-white bg-gray-900 hover:bg-gray-800 rounded-xl transition-colors shadow-sm">Ya, Lanjutkan</button>
</div>
</div>
</div>
<script>
let antrean_id_warga = null;
let antrean_status_baru = null;
function muat_data_ke_tabel_admin(daftar_mustahik) {
const tbody = document.getElementById('tabel_body_mustahik');
const pesan_kosong = document.getElementById('pesan_tabel_kosong');
tbody.innerHTML = '';
if (daftar_mustahik.length === 0) {
pesan_kosong.classList.remove('hidden');
return;
}
pesan_kosong.classList.add('hidden');
let hitung_pending = 0;
let hitung_verified = 0;
let hitung_rejected = 0;
daftar_mustahik.forEach((warga, indeks) => {
if (warga.status_validasi.toLowerCase() === 'pending') hitung_pending++;
if (warga.status_validasi.toLowerCase() === 'verified') hitung_verified++;
if (warga.status_validasi.toLowerCase() === 'rejected') hitung_rejected++;
const baris = document.createElement('tr');
baris.className = 'hover:bg-gray-50 transition-colors';
baris.innerHTML = `
<td class="p-4 text-xs font-mono text-gray-400">${indeks + 1}</td>
<td class="p-4 font-semibold text-gray-900 kolom-nama"></td>
<td class="p-4 text-xs font-mono kolom-nik"></td>
<td class="p-4 text-xs font-mono kolom-nokk"></td>
<td class="p-4 text-center kolom-tanggungan"></td>
<td class="p-4 text-center"><span class="inline-block px-2.5 py-1 text-[10px] font-bold rounded-md kolom-badge"></span></td>
<td class="p-4">
<div class="flex justify-center gap-2">
<a href="admin_detail.html?id=${warga.id}" class="px-2.5 py-1.5 bg-blue-50 text-blue-700 hover:bg-blue-100 border border-blue-200 text-xs font-medium rounded-lg transition-colors">Detail</a>
<button onclick="ubah_status_verifikasi(${warga.id}, 'Verified')" class="px-2.5 py-1.5 bg-emerald-50 text-emerald-700 hover:bg-emerald-100 border border-emerald-200 text-xs font-medium rounded-lg transition-colors">Setuju</button>
<button onclick="ubah_status_verifikasi(${warga.id}, 'Rejected')" class="px-2.5 py-1.5 bg-red-50 text-red-700 hover:bg-red-100 border border-red-200 text-xs font-medium rounded-lg transition-colors">Tolak</button>
</div>
</td>
`;
baris.querySelector('.kolom-nama').textContent = warga.nama_kk;
baris.querySelector('.kolom-nik').textContent = warga.nik;
baris.querySelector('.kolom-nokk').textContent = warga.no_kk || '-';
baris.querySelector('.kolom-tanggungan').textContent = warga.jumlah_tanggungan;
const badge = baris.querySelector('.kolom-badge');
badge.textContent = warga.status_validasi;
if (warga.status_validasi.toLowerCase() === 'verified') {
badge.className += ' bg-emerald-50 text-emerald-700 border border-emerald-200';
} else if (warga.status_validasi.toLowerCase() === 'rejected') {
badge.className += ' bg-red-50 text-red-700 border border-red-200';
} else {
badge.className += ' bg-yellow-50 text-yellow-700 border border-yellow-200';
}
tbody.appendChild(baris);
});
document.getElementById('label_total_baris').textContent = `${daftar_mustahik.length} Rekaman`;
document.getElementById('stat_total_warga').textContent = daftar_mustahik.length;
document.getElementById('stat_pending_warga').textContent = hitung_pending;
document.getElementById('stat_verified_warga').textContent = hitung_verified;
document.getElementById('stat_rejected_warga').textContent = hitung_rejected;
}
async function ambil_data_admin_mustahik() {
const banner_error = document.getElementById('banner_error_admin');
try {
banner_error.classList.add('hidden');
// Diubah ke relative path
const http_response = await fetch('/api/admin/mustahik');
const json_result = await http_response.json();
if (!http_response.ok) throw new Error(json_result.error || 'Gagal menarik data dari server.');
muat_data_ke_tabel_admin(json_result);
} catch (error_sistem) {
banner_error.textContent = 'Kesalahan Komunikasi Server: ' + error_sistem.message;
banner_error.classList.remove('hidden');
}
}
function ubah_status_verifikasi(id_warga, status_baru) {
antrean_id_warga = id_warga;
antrean_status_baru = status_baru;
const teks_pesan = document.getElementById('teks_pesan_modal');
teks_pesan.innerHTML = `Apakah Anda yakin ingin mengubah status validasi menjadi <span class="font-bold text-gray-900">${status_baru}</span>?`;
const tombol_setuju = document.getElementById('btn_modal_setuju');
if (status_baru === 'Verified') {
tombol_setuju.className = 'px-4 py-2.5 text-xs font-bold text-white bg-emerald-600 hover:bg-emerald-700 rounded-xl transition-colors shadow-sm';
} else {
tombol_setuju.className = 'px-4 py-2.5 text-xs font-bold text-white bg-red-600 hover:bg-red-700 rounded-xl transition-colors shadow-sm';
}
document.getElementById('modal_konfirmasi').classList.remove('hidden');
}
function tutup_modal() {
document.getElementById('modal_konfirmasi').classList.add('hidden');
antrean_id_warga = null;
antrean_status_baru = null;
}
async function eksekusi_perubahan_status() {
if (!antrean_id_warga || !antrean_status_baru) return;
const banner_error = document.getElementById('banner_error_admin');
const tombol_setuju = document.getElementById('btn_modal_setuju');
tombol_setuju.disabled = true;
tombol_setuju.textContent = 'Memproses...';
try {
// Diubah ke relative path
const http_response = await fetch(`/api/admin/verifikasi/${antrean_id_warga}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status_validasi: antrean_status_baru })
});
const json_result = await http_response.json();
if (!http_response.ok) throw new Error(json_result.error || 'Gagal mengubah status verifikasi.');
tutup_modal();
ambil_data_admin_mustahik();
} catch (error_sistem) {
tutup_modal();
banner_error.textContent = 'Gagal memproses mutasi data: ' + error_sistem.message;
banner_error.classList.remove('hidden');
} finally {
tombol_setuju.disabled = false;
tombol_setuju.textContent = 'Ya, Lanjutkan';
}
}
function eksekusi_logout_admin() {
localStorage.removeItem('token_admin');
window.location.href = 'index.html';
}
ambil_data_admin_mustahik();
</script>
</body>
</html>