206 lines
12 KiB
HTML
206 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="id">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Detail Warga & Peta Lokal - Admin</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></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-950 min-h-screen flex flex-col">
|
|
|
|
<nav class="bg-white border-b border-gray-200 p-4 flex items-center gap-4 sticky top-0 z-30 px-6">
|
|
<a href="admin_dasbor.html" class="hover:bg-gray-100 p-2 rounded-lg transition-colors text-gray-600">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
|
|
</a>
|
|
<h1 class="text-md font-bold text-gray-800">Detail Rekaman Data Validasi</h1>
|
|
</nav>
|
|
|
|
<main class="flex-grow p-4 md:p-6 max-w-5xl mx-auto w-full grid grid-cols-1 md:grid-cols-3 gap-6 mb-10">
|
|
|
|
<div class="md:col-span-2 space-y-6">
|
|
|
|
<div class="bg-white p-6 rounded-xl shadow-sm border border-gray-200 space-y-4">
|
|
<div class="flex justify-between items-start border-b pb-3">
|
|
<div>
|
|
<h2 id="det_nama_kk" class="text-xl font-bold text-gray-900">-</h2>
|
|
<p class="text-xs text-gray-400 font-mono mt-1">Sistem ID: <span id="det_id_sistem">-</span></p>
|
|
</div>
|
|
<span id="det_badge_status" class="inline-block px-2.5 py-1 text-[10px] font-bold rounded-md">Pending</span>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm">
|
|
<div>
|
|
<span class="text-xs text-gray-400 block">Nomor Induk Kependudukan (NIK)</span>
|
|
<p id="det_nik" class="font-mono font-medium mt-0.5">-</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-xs text-gray-400 block">Nomor Kartu Keluarga (No KK)</span>
|
|
<p id="det_no_kk" class="font-mono font-medium mt-0.5">-</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-xs text-gray-400 block">Jumlah Tanggungan Keluarga</span>
|
|
<p id="det_tanggungan" class="font-medium mt-0.5">-</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
|
<div class="bg-gray-50 p-4 border-b border-gray-200">
|
|
<h3 class="text-xs font-bold uppercase tracking-wider text-gray-500">Evaluasi Parameter Instrumen Kemiskinan BPS</h3>
|
|
</div>
|
|
<div class="divide-y divide-gray-100 text-sm">
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Luas Lantai Per Kapita</span><span id="det_luas_lantai" class="font-semibold text-gray-800">-</span></div>
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Material Bahan Lantai</span><span id="det_mat_lantai" class="font-semibold text-gray-800">-</span></div>
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Material Bahan Dinding</span><span id="det_mat_dinding" class="font-semibold text-gray-800">-</span></div>
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Fasilitas Sanitasi (MCK)</span><span id="det_sanitasi" class="font-semibold text-gray-800">-</span></div>
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Sumber Air Minum Utama</span><span id="det_air_minum" class="font-semibold text-gray-800">-</span></div>
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Sumber Penerangan Rumah</span><span id="det_penerangan" class="font-semibold text-gray-800">-</span></div>
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Pekerjaan Utama Kepala Keluarga</span><span id="det_pekerjaan" class="font-semibold text-gray-800">-</span></div>
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Estimasi Pendapatan Bulanan</span><span id="det_pendapatan" class="font-semibold text-emerald-700">-</span></div>
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Frekuensi Makan Harian</span><span id="det_frekuensi_makan" class="font-semibold text-gray-800">-</span></div>
|
|
<div class="p-4 flex justify-between"><span class="text-gray-500">Kepemilikan Aset Kendaraan</span><span id="det_kepemilikan_aset" class="font-semibold text-gray-800">-</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="space-y-6">
|
|
|
|
<div class="bg-white p-4 rounded-xl shadow-sm border border-gray-200 space-y-3">
|
|
<h3 class="text-xs font-bold uppercase tracking-wider text-gray-400">Verifikasi Lokasi Spasial</h3>
|
|
<div class="h-56 w-full rounded-lg overflow-hidden border border-gray-200 z-0 relative">
|
|
<div id="map_detail_container" class="absolute inset-0"></div>
|
|
</div>
|
|
<div class="text-[11px] font-mono text-gray-500 space-y-0.5">
|
|
<p>Lat: <span id="det_lat">-</span></p>
|
|
<p>Lng: <span id="det_lng">-</span></p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white p-5 rounded-xl shadow-sm border border-gray-200 space-y-3 text-center">
|
|
<h3 class="text-xs font-bold uppercase tracking-wider text-gray-500 mb-2">Aksi Otoritas Dokumen</h3>
|
|
<button onclick="proses_keputusan_admin('Verified')" class="w-full py-3 bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-bold rounded-lg transition-colors shadow-sm">Setujui Sebagai Mustahik</button>
|
|
<button onclick="proses_keputusan_admin('Rejected')" class="w-full py-3 bg-red-600 hover:bg-red-700 text-white text-xs font-bold rounded-lg transition-colors shadow-sm">Tolak Pengajuan Bantuan</button>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</main>
|
|
|
|
<script>
|
|
// Ekstraksi parameter ID dari string URL query browser
|
|
const parameter_url = new URLSearchParams(window.location.search);
|
|
const id_warga_terpilih = parameter_url.get('id');
|
|
|
|
let instansi_peta_detail = null;
|
|
|
|
// Memformat nominal angka reguler menjadi format mata uang Rupiah Indonesia
|
|
function format_mata_uang_rupiah(angka) {
|
|
return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(angka);
|
|
}
|
|
|
|
// Memetakan posisi koordinat spasial warga ke dalam objek Leaflet.js
|
|
function petakan_lokasi_warga(lat, lng, nama) {
|
|
if (!lat || !lng) return;
|
|
|
|
instansi_peta_detail = L.map('map_detail_container').setView([lat, lng], 15);
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© OpenStreetMap contributors'
|
|
}).addTo(instansi_peta_detail);
|
|
|
|
L.marker([lat, lng]).addTo(instansi_peta_detail)
|
|
.bindPopup(`<b>${nama}</b><br>Titik Lokasi Rumah`)
|
|
.openPopup();
|
|
}
|
|
|
|
// Merender seluruh string objek data yang ditarik secara aman ke komponen UI
|
|
function isi_komponen_antarmuka(data) {
|
|
// Keamanan XSS: Memanfaatkan textContent murni untuk mencegah injeksi skrip HTML
|
|
document.getElementById('det_id_sistem').textContent = data.id;
|
|
document.getElementById('det_nama_kk').textContent = data.nama_kk;
|
|
document.getElementById('det_nik').textContent = data.nik;
|
|
document.getElementById('det_no_kk').textContent = data.no_kk || '-';
|
|
document.getElementById('det_tanggungan').textContent = `${data.jumlah_tanggungan} Jiwa`;
|
|
|
|
document.getElementById('det_luas_lantai').textContent = data.luas_lantai_per_kapita || 'Belum diisi';
|
|
document.getElementById('det_mat_lantai').textContent = data.material_lantai || 'Belum diisi';
|
|
document.getElementById('det_mat_dinding').textContent = data.material_dinding || 'Belum diisi';
|
|
document.getElementById('det_sanitasi').textContent = data.fasilitas_sanitasi || 'Belum diisi';
|
|
document.getElementById('det_air_minum').textContent = data.sumber_air_minum || 'Belum diisi';
|
|
document.getElementById('det_penerangan').textContent = data.sumber_penerangan || 'Belum diisi';
|
|
document.getElementById('det_pekerjaan').textContent = data.pekerjaan_utama || 'Belum diisi';
|
|
document.getElementById('det_pendapatan').textContent = data.pendapatan_bulanan ? format_mata_uang_rupiah(data.pendapatan_bulanan) : 'Belum diisi';
|
|
document.getElementById('det_frekuensi_makan').textContent = data.frekuensi_makan || 'Belum diisi';
|
|
document.getElementById('det_kepemilikan_aset').textContent = data.kepemilikan_aset || 'Belum diisi';
|
|
|
|
document.getElementById('det_lat').textContent = data.lat;
|
|
document.getElementById('det_lng').textContent = data.lng;
|
|
|
|
const badge = document.getElementById('det_badge_status');
|
|
badge.textContent = data.status_validasi;
|
|
if (data.status_validasi.toLowerCase() === 'verified') {
|
|
badge.className = 'inline-block px-2.5 py-1 text-[10px] font-bold rounded-md bg-emerald-50 text-emerald-700 border border-emerald-200';
|
|
} else if (data.status_validasi.toLowerCase() === 'rejected') {
|
|
badge.className = 'inline-block px-2.5 py-1 text-[10px] font-bold rounded-md bg-red-50 text-red-700 border border-red-200';
|
|
} else {
|
|
badge.className = 'inline-block px-2.5 py-1 text-[10px] font-bold rounded-md bg-yellow-50 text-yellow-700 border border-yellow-200';
|
|
}
|
|
|
|
// Eksekusi pemetaan visual spasial
|
|
petakan_lokasi_warga(parseFloat(data.lat), parseFloat(data.lng), data.nama_kk);
|
|
}
|
|
|
|
// Meminta muatan data spesifik satu warga ke peladen Express Node.js
|
|
async function ambil_data_detail_warga() {
|
|
if (!id_warga_terpilih) {
|
|
return alert('ID parameter tidak ditemukan pada URL peramban.');
|
|
}
|
|
|
|
try {
|
|
/* Baris Kompleks: Melakukan pemanggilan asinkron GET menuju rute detail REST API
|
|
dengan menyertakan nilai parameter ID target untuk diolah kueri SQL JOIN oleh peladen. */
|
|
const http_response = await fetch(`http://localhost:3000/api/admin/mustahik/${id_warga_terpilih}`);
|
|
const json_result = await http_response.json();
|
|
|
|
if (!http_response.ok) throw new Error(json_result.error || 'Gagal mengambil data detail.');
|
|
|
|
isi_komponen_antarmuka(json_result);
|
|
|
|
} catch (error_sistem) {
|
|
alert('Kegagalan Koneksi Sistem: ' + error_sistem.message);
|
|
}
|
|
}
|
|
|
|
// Memproses mutasi perubahan status persetujuan data dari panel admin ke server
|
|
async function proses_keputusan_admin(keputusan_status) {
|
|
if (!confirm(`Apakah Anda yakin memvalidasi data ini dengan status ${keputusan_status}?`)) return;
|
|
|
|
try {
|
|
const http_response = await fetch(`http://localhost:3000/api/admin/verifikasi/${id_warga_terpilih}`, {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ status_validasi: keputusan_status })
|
|
});
|
|
|
|
const json_result = await http_response.json();
|
|
if (!http_response.ok) throw new Error(json_result.error || 'Gagal mengubah status.');
|
|
|
|
alert('Status otorisasi data berhasil diperbarui!');
|
|
window.location.reload();
|
|
|
|
} catch (error_sistem) {
|
|
alert('Gagal mengeksekusi perintah: ' + error_sistem.message);
|
|
}
|
|
}
|
|
|
|
// Eksekusi inisialisasi pemuatan data awal
|
|
ambil_data_detail_warga();
|
|
</script>
|
|
</body>
|
|
</html> |