253 lines
14 KiB
HTML
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> |