Files
UAS-SIG/ui/index.html
T
2026-06-09 13:49:51 +07:00

179 lines
9.8 KiB
HTML

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Peta Bantuan Sosial - Dinsos Pontianak</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=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Poppins', sans-serif; }
</style>
</head>
<body class="bg-gray-50 flex flex-col min-h-screen">
<nav class="bg-white p-4 shadow-sm z-30 flex justify-between items-center px-8 sticky top-0">
<h1 class="text-xl font-bold text-gray-800">SIG Dinsos Pontianak</h1>
<a href="petugas_login.html" class="text-sm font-semibold text-emerald-600 hover:text-emerald-800 transition">Login Petugas</a>
</nav>
<main class="flex-grow flex flex-col gap-10 pb-12">
<section class="bg-emerald-600 text-white px-6 py-12 text-center rounded-b-[3rem] shadow-md">
<div class="max-w-3xl mx-auto">
<h2 class="text-3xl font-bold mb-4">Cek Status Bantuan Sosial Anda</h2>
<p class="text-emerald-100 mb-8 font-light">Sistem Informasi Geografis terintegrasi untuk transparansi validasi data kemiskinan masyarakat Kota Pontianak.</p>
<div class="bg-white p-2 rounded-2xl shadow-xl flex flex-col sm:flex-row gap-2 max-w-xl mx-auto border border-emerald-100">
<input type="text" id="nik_input" placeholder="Masukkan 16 digit NIK..." maxlength="16" class="w-full p-4 rounded-xl bg-transparent focus:outline-none text-gray-800">
<button onclick="cek_status_bantuan()" id="btn_cari" class="bg-emerald-600 text-white px-8 py-4 rounded-xl font-bold hover:bg-emerald-700 transition flex-shrink-0">Cari Data</button>
</div>
<div id="hasil_pencarian" class="mt-6 hidden p-4 rounded-xl border max-w-xl mx-auto text-left bg-white text-gray-800 shadow-lg transition-all">
<p id="pesan_hasil" class="text-sm font-semibold"></p>
</div>
</div>
</section>
<section class="px-6">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-5xl mx-auto">
<div class="bg-white p-6 rounded-2xl shadow-sm border border-gray-100 text-center">
<h3 class="text-gray-500 text-sm font-semibold uppercase tracking-wider mb-2">Total Terverifikasi</h3>
<p id="stat_publik_terverifikasi" class="text-3xl font-bold text-emerald-600">0</p>
</div>
<div class="bg-white p-6 rounded-2xl shadow-sm border border-gray-100 text-center">
<h3 class="text-gray-500 text-sm font-semibold uppercase tracking-wider mb-2">Sedang Diproses</h3>
<p id="stat_publik_diproses" class="text-3xl font-bold text-yellow-500">0</p>
</div>
<div class="bg-white p-6 rounded-2xl shadow-sm border border-gray-100 text-center">
<h3 class="text-gray-500 text-sm font-semibold uppercase tracking-wider mb-2">Pembaruan Terakhir</h3>
<p id="stat_publik_waktu" class="text-lg font-bold text-gray-700 mt-2">Menghitung...</p>
</div>
</div>
</section>
<section class="px-6">
<div class="h-[400px] w-full max-w-5xl mx-auto rounded-2xl overflow-hidden shadow-sm border border-gray-200 z-0 relative">
<div id="map_container" class="absolute inset-0"></div>
</div>
</section>
</main>
<script>
const leaflet_map = L.map('map_container').setView([-0.0227, 109.3425], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap contributors'
}).addTo(leaflet_map);
let map_marker_warga = null;
let map_marker_pangkalan = null;
let map_garis_hubung = null;
// Custom ikon untuk pangkalan distribusi (Rumah Ibadah)
const ikon_pangkalan = L.icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
async function muat_statistik_publik() {
try {
const respons_http = await fetch('/api/survey/statistik');
if (!respons_http.ok) throw new Error('Gagal mengambil data statistik');
const data_agregat = await respons_http.json();
document.getElementById('stat_publik_terverifikasi').textContent = data_agregat.terverifikasi || 0;
document.getElementById('stat_publik_diproses').textContent = data_agregat.diproses || 0;
const waktu_terformat_wib = new Date().toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Jakarta' });
document.getElementById('stat_publik_waktu').textContent = `Hari ini, ${waktu_terformat_wib} WIB`;
} catch (error_sistem) {
document.getElementById('stat_publik_waktu').textContent = 'Sistem Terhubung';
}
}
const cek_status_bantuan = async () => {
const input_nik_value = document.getElementById('nik_input').value;
const kotak_hasil = document.getElementById('hasil_pencarian');
const pesan_hasil = document.getElementById('pesan_hasil');
const tombol_cari = document.getElementById('btn_cari');
if (!/^\d{16}$/.test(input_nik_value)) return alert('Masukkan NIK yang valid (16 digit angka).');
tombol_cari.disabled = true;
tombol_cari.textContent = 'Mencari...';
try {
const http_response = await fetch(`/api/survey/cek-nik/${input_nik_value}`);
if (!http_response.ok) throw new Error('Data tidak ditemukan');
const json_data = await http_response.json();
kotak_hasil.classList.remove('hidden', 'bg-red-50', 'bg-emerald-50', 'border-red-200', 'border-emerald-200');
kotak_hasil.classList.add('bg-emerald-50', 'border-emerald-200');
pesan_hasil.className = 'text-emerald-700';
let teks_respons = `Data ditemukan: ${json_data.nama_kk} | Status: ${json_data.status_validasi}`;
if (json_data.rumah_ibadah_nama) {
teks_respons += `<br><span class="text-xs text-emerald-600 font-normal mt-1 block">📍 Tempat Penyaluran: ${json_data.rumah_ibadah_nama}</span>`;
}
pesan_hasil.innerHTML = teks_respons;
// Menghapus layer peta sebelumnya secara bersih
if (map_marker_warga) leaflet_map.removeLayer(map_marker_warga);
if (map_marker_pangkalan) leaflet_map.removeLayer(map_marker_pangkalan);
if (map_garis_hubung) leaflet_map.removeLayer(map_garis_hubung);
if (json_data.lat && json_data.lng) {
const koordinat_warga = [parseFloat(json_data.lat), parseFloat(json_data.lng)];
// Merender titik lokasi rumah mustahik (ikon biru bawaan)
map_marker_warga = L.marker(koordinat_warga).addTo(leaflet_map)
.bindPopup(`<b>${json_data.nama_kk}</b><br>Status: ${json_data.status_validasi}`)
.openPopup();
// Baris Kompleks: Jika data pangkalan silang (JOIN) tersedia, kita merender ikon hijau khusus
// pada koordinat rumah ibadah, lalu menggambar objek vektor polyline merah putus-putus
// untuk memvisualisasikan jarak dan rute dari rumah warga menuju pangkalan penyaluran.
if (json_data.rumah_ibadah_lat && json_data.rumah_ibadah_lng) {
const koordinat_pangkalan = [parseFloat(json_data.rumah_ibadah_lat), parseFloat(json_data.rumah_ibadah_lng)];
map_marker_pangkalan = L.marker(koordinat_pangkalan, { icon: ikon_pangkalan }).addTo(leaflet_map)
.bindPopup(`<b>Pusat Penyaluran</b><br>${json_data.rumah_ibadah_nama}`);
map_garis_hubung = L.polyline([koordinat_warga, koordinat_pangkalan], {
color: 'red',
weight: 3,
dashArray: '10, 10',
opacity: 0.7
}).addTo(leaflet_map);
// Menyesuaikan bidang pandang kamera agar kedua titik terlihat di layar
leaflet_map.fitBounds(map_garis_hubung.getBounds(), { padding: [50, 50] });
} else {
leaflet_map.setView(koordinat_warga, 16);
}
}
} catch (error_object) {
kotak_hasil.classList.remove('hidden', 'bg-red-50', 'bg-emerald-50', 'border-red-200', 'border-emerald-200');
kotak_hasil.classList.add('bg-red-50', 'border-red-200');
pesan_hasil.className = 'text-red-700';
pesan_hasil.textContent = 'Mohon maaf, NIK tidak terdaftar atau terjadi kesalahan sistem.';
} finally {
tombol_cari.disabled = false;
tombol_cari.textContent = 'Cari Data';
}
};
muat_statistik_publik();
</script>
</body>
</html>