generated from MuhammadIqbalMaulana/UAS-SIG
Initial commit
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
<!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>
|
||||
@@ -0,0 +1,254 @@
|
||||
<!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="ubah_status_verifikasi(id_warga_terpilih, '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="ubah_status_verifikasi(id_warga_terpilih, '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>
|
||||
|
||||
<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>
|
||||
const parameter_url = new URLSearchParams(window.location.search);
|
||||
const id_warga_terpilih = parameter_url.get('id');
|
||||
|
||||
let instansi_peta_detail = null;
|
||||
let antrean_id_warga = null;
|
||||
let antrean_status_baru = null;
|
||||
|
||||
function format_mata_uang_rupiah(angka) {
|
||||
return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(angka);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
function isi_komponen_antarmuka(data) {
|
||||
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';
|
||||
}
|
||||
|
||||
petakan_lokasi_warga(parseFloat(data.lat), parseFloat(data.lng), data.nama_kk);
|
||||
}
|
||||
|
||||
async function ambil_data_detail_warga() {
|
||||
if (!id_warga_terpilih) {
|
||||
return alert('ID parameter tidak ditemukan pada URL peramban.');
|
||||
}
|
||||
|
||||
try {
|
||||
// Diubah ke relative path
|
||||
const http_response = await fetch(`/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);
|
||||
}
|
||||
}
|
||||
|
||||
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 memvalidasi data ini dengan status <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 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.');
|
||||
|
||||
tutup_modal();
|
||||
alert('Status otorisasi data berhasil diperbarui!');
|
||||
window.location.reload();
|
||||
|
||||
} catch (error_sistem) {
|
||||
tutup_modal();
|
||||
alert('Gagal mengeksekusi perintah: ' + error_sistem.message);
|
||||
} finally {
|
||||
tombol_setuju.disabled = false;
|
||||
tombol_setuju.textContent = 'Ya, Lanjutkan';
|
||||
}
|
||||
}
|
||||
|
||||
ambil_data_detail_warga();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login Administrator - 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 flex items-center justify-center h-screen p-4">
|
||||
<div class="bg-white p-8 rounded-2xl shadow-xl w-full max-w-sm border border-gray-200">
|
||||
<div class="mb-6">
|
||||
<div class="w-10 h-10 bg-gray-900 rounded-xl mb-4 flex items-center justify-center text-white font-bold text-lg">A</div>
|
||||
<h2 class="text-2xl font-bold text-gray-900 tracking-tight">Dinsos Admin Login</h2>
|
||||
<p class="text-gray-500 text-xs mt-1">Otoritas manajemen data verifikasi kemiskinan Kota Pontianak.</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-500 uppercase tracking-wider mb-1">Username Admin</label>
|
||||
<input type="text" id="input_username" placeholder="Masukkan ID Administrator" class="w-full p-3.5 rounded-lg border border-gray-300 bg-gray-50 outline-none focus:ring-2 focus:ring-gray-800 focus:bg-white transition-all text-gray-900 text-sm font-medium">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-500 uppercase tracking-wider mb-1">Kata Sandi Akun</label>
|
||||
<input type="password" id="input_password" placeholder="••••••••" class="w-full p-3.5 rounded-lg border border-gray-300 bg-gray-50 outline-none focus:ring-2 focus:ring-gray-800 focus:bg-white transition-all text-gray-900 text-sm font-medium">
|
||||
</div>
|
||||
|
||||
<p id="pesan_error_admin" class="text-red-600 text-xs font-semibold px-1 hidden"></p>
|
||||
|
||||
<button onclick="eksekusi_login_admin()" id="btn_submit_admin" class="w-full bg-gray-900 text-white font-bold py-3.5 rounded-lg hover:bg-gray-800 transition shadow-md text-sm tracking-wide">Masuk ke Dasbor</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function eksekusi_login_admin() {
|
||||
const username_val = document.getElementById('input_username').value;
|
||||
const password_val = document.getElementById('input_password').value;
|
||||
const wadah_error = document.getElementById('pesan_error_admin');
|
||||
const tombol_login = document.getElementById('btn_submit_admin');
|
||||
|
||||
if (!username_val || !password_val) {
|
||||
wadah_error.textContent = 'Seluruh kolom input wajib dilengkapi.';
|
||||
wadah_error.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
wadah_error.classList.add('hidden');
|
||||
tombol_login.disabled = true;
|
||||
tombol_login.textContent = 'Otorisasi Sistem...';
|
||||
|
||||
try {
|
||||
// Diubah ke relative path
|
||||
const http_response = await fetch('/api/auth/login-admin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: username_val,
|
||||
password: password_val
|
||||
})
|
||||
});
|
||||
|
||||
const json_result = await http_response.json();
|
||||
|
||||
if (!http_response.ok) {
|
||||
throw new Error(json_result.error || 'Akses ditolak oleh otoritas server.');
|
||||
}
|
||||
|
||||
localStorage.setItem('token_admin', json_result.token);
|
||||
window.location.href = 'admin_dasbor.html';
|
||||
|
||||
} catch (error_sistem) {
|
||||
wadah_error.textContent = error_sistem.message;
|
||||
wadah_error.classList.remove('hidden');
|
||||
} finally {
|
||||
tombol_login.disabled = false;
|
||||
tombol_login.textContent = 'Masuk ke Dasbor';
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('input_password').addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
eksekusi_login_admin();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
<!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: '© 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>
|
||||
@@ -0,0 +1,166 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Dasbor Petugas Lapangan - Dinsos Pontianak</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;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 text-gray-800">
|
||||
|
||||
<nav class="bg-white p-4 shadow-sm z-30 flex justify-between items-center px-6 sticky top-0 border-b border-gray-100">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="bg-emerald-600 text-white p-2 rounded-xl">
|
||||
<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="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-sm font-bold text-gray-800 leading-none">Panel Petugas</h1>
|
||||
<span class="text-[10px] text-gray-500 font-medium">Dinsos Kota Pontianak</span>
|
||||
</div>
|
||||
</div>
|
||||
<button onclick="proses_logout_petugas()" class="text-xs font-semibold text-red-600 hover:bg-red-50 px-3 py-2 rounded-xl transition">Keluar</button>
|
||||
</nav>
|
||||
|
||||
<main class="flex-grow p-4 md:p-6 max-w-4xl mx-auto w-full space-y-6">
|
||||
|
||||
<section class="bg-gradient-to-br from-emerald-600 to-emerald-700 text-white p-6 rounded-2xl shadow-sm flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold">Selamat Datang, Petugas Lapangan</h2>
|
||||
<p class="text-xs text-emerald-100 mt-1 font-light">Tetap teliti dan objektif dalam melakukan validasi indikator kemiskinan warga.</p>
|
||||
</div>
|
||||
<a href="survei_lapangan.html" class="bg-white text-emerald-700 hover:bg-emerald-50 px-5 py-3 rounded-xl text-sm font-bold shadow-md transition-all flex items-center gap-2 w-full sm:w-auto justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
||||
Mulai Survei Baru
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section class="grid grid-cols-3 gap-3 md:gap-6">
|
||||
<div class="bg-white p-4 rounded-xl shadow-sm border border-gray-100 text-center">
|
||||
<span class="text-[10px] text-gray-400 font-bold uppercase tracking-wider block mb-1">Total Tugas</span>
|
||||
<p class="text-2xl font-bold text-gray-800" id="stat_total">0</p>
|
||||
</div>
|
||||
<div class="bg-white p-4 rounded-xl shadow-sm border border-gray-100 text-center">
|
||||
<span class="text-[10px] text-yellow-600 font-bold uppercase tracking-wider block mb-1">Pending</span>
|
||||
<p class="text-2xl font-bold text-yellow-600" id="stat_pending">0</p>
|
||||
</div>
|
||||
<div class="bg-white p-4 rounded-xl shadow-sm border border-gray-100 text-center">
|
||||
<span class="text-[10px] text-emerald-600 font-bold uppercase tracking-wider block mb-1">Disetujui</span>
|
||||
<p class="text-2xl font-bold text-emerald-600" id="stat_verified">0</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="p-4 border-b border-gray-100 flex justify-between items-center bg-gray-50">
|
||||
<h3 class="font-bold text-gray-800 text-sm">Daftar Antrean Survei Wilayah</h3>
|
||||
<span class="text-[11px] bg-emerald-100 text-emerald-800 font-semibold px-2 py-1 rounded-full" id="label_jumlah_antrean">0 Antrean</span>
|
||||
</div>
|
||||
|
||||
<div id="status_message_container" class="p-8 text-center text-sm text-gray-500 hidden"></div>
|
||||
|
||||
<div class="divide-y divide-gray-100" id="wadah_list_antrean">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<script>
|
||||
const token_petugas = localStorage.getItem('token_petugas');
|
||||
|
||||
function periksa_sesi_petugas() {
|
||||
// Sesi dinonaktifkan sementara untuk mempermudah testing
|
||||
}
|
||||
|
||||
function render_data_ke_tabel(daftar_warga) {
|
||||
const wadah_list = document.getElementById('wadah_list_antrean');
|
||||
const label_antrean = document.getElementById('label_jumlah_antrean');
|
||||
|
||||
wadah_list.innerHTML = '';
|
||||
|
||||
if (daftar_warga.length === 0) {
|
||||
wadah_list.innerHTML = '<div class="p-8 text-center text-sm text-gray-400">Tidak ada agenda survei aktif saat ini.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
label_antrean.textContent = `${daftar_warga.length} Rumah Tangga`;
|
||||
|
||||
let total_pending = 0;
|
||||
let total_verified = 0;
|
||||
|
||||
daftar_warga.forEach(warga => {
|
||||
if (warga.status_validasi.toLowerCase() === 'pending') total_pending++;
|
||||
if (warga.status_validasi.toLowerCase() === 'verified') total_verified++;
|
||||
|
||||
const item_div = document.createElement('div');
|
||||
item_div.className = 'p-4 flex justify-between items-center hover:bg-gray-50 transition-colors';
|
||||
|
||||
item_div.innerHTML = `
|
||||
<div class="space-y-1">
|
||||
<h4 class="font-bold text-gray-800 text-sm kelas-nama"></h4>
|
||||
<p class="text-xs text-gray-500">NIK: <span class="kelas-nik"></span></p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="inline-block px-2 py-1 text-[10px] font-bold rounded-full kelas-badge"></span>
|
||||
<a href="survei_lapangan.html?nik=${warga.nik}" class="text-xs bg-gray-100 hover:bg-emerald-50 hover:text-emerald-700 text-gray-600 px-3 py-2 rounded-lg font-medium transition-colors border border-gray-200">
|
||||
Buka Form
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
item_div.querySelector('.kelas-nama').textContent = warga.nama_kk;
|
||||
item_div.querySelector('.kelas-nik').textContent = warga.nik;
|
||||
|
||||
const badge = item_div.querySelector('.kelas-badge');
|
||||
badge.textContent = warga.status_validasi;
|
||||
|
||||
if (warga.status_validasi.toLowerCase() === 'verified') {
|
||||
badge.className += ' bg-emerald-50 text-emerald-700';
|
||||
} else if (warga.status_validasi.toLowerCase() === 'rejected') {
|
||||
badge.className += ' bg-red-50 text-red-700';
|
||||
} else {
|
||||
badge.className += ' bg-yellow-50 text-yellow-700';
|
||||
}
|
||||
|
||||
wadah_list.appendChild(item_div);
|
||||
});
|
||||
|
||||
document.getElementById('stat_total').textContent = daftar_warga.length;
|
||||
document.getElementById('stat_pending').textContent = total_pending;
|
||||
document.getElementById('stat_verified').textContent = total_verified;
|
||||
}
|
||||
|
||||
async function ambil_antrean_survei() {
|
||||
const msg_container = document.getElementById('status_message_container');
|
||||
|
||||
try {
|
||||
msg_container.classList.remove('hidden');
|
||||
msg_container.textContent = 'Memuat daftar antrean wilayah...';
|
||||
|
||||
// Diubah ke relative path dan diaktifkan untuk mengambil data aslinya
|
||||
const respons = await fetch('/api/admin/mustahik');
|
||||
if (!respons.ok) throw new Error('Gagal mengambil data dari server.');
|
||||
const data_warga = await respons.json();
|
||||
|
||||
msg_container.classList.add('hidden');
|
||||
render_data_ke_tabel(data_warga);
|
||||
|
||||
} catch (error_sistem) {
|
||||
msg_container.classList.remove('hidden');
|
||||
msg_container.textContent = 'Gagal terhubung ke server backend: ' + error_sistem.message;
|
||||
}
|
||||
}
|
||||
|
||||
function proses_logout_petugas() {
|
||||
localStorage.removeItem('token_petugas');
|
||||
alert('Sesi berhasil diakhiri.');
|
||||
window.location.href = 'index.html';
|
||||
}
|
||||
|
||||
periksa_sesi_petugas();
|
||||
ambil_antrean_survei();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,85 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login Petugas Lapangan - Dinsos Pontianak</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
||||
<style>body { font-family: 'Poppins', sans-serif; }</style>
|
||||
</head>
|
||||
<body class="bg-emerald-50 flex items-center justify-center h-screen p-4">
|
||||
<div class="bg-white p-8 rounded-3xl shadow-xl w-full max-w-sm border border-emerald-100">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-2">Halo, Petugas!</h2>
|
||||
<p class="text-gray-500 text-sm mb-8">Silakan masuk untuk mulai melakukan survei lapangan hari ini.</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<input type="text" id="input_username" placeholder="Username Petugas" class="w-full p-4 rounded-xl border bg-gray-50 outline-none focus:ring-2 focus:ring-emerald-500 transition-all text-gray-800 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<input type="password" id="input_password" placeholder="Kata Sandi" class="w-full p-4 rounded-xl border bg-gray-50 outline-none focus:ring-2 focus:ring-emerald-500 transition-all text-gray-800 text-sm">
|
||||
</div>
|
||||
|
||||
<p id="pesan_error" class="text-red-500 text-xs font-semibold px-1 hidden"></p>
|
||||
|
||||
<button onclick="eksekusi_login_petugas()" id="btn_submit_login" class="w-full bg-emerald-600 text-white font-bold py-4 rounded-xl hover:bg-emerald-700 transition shadow-md">Masuk ke Survei</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function eksekusi_login_petugas() {
|
||||
const username_val = document.getElementById('input_username').value;
|
||||
const password_val = document.getElementById('input_password').value;
|
||||
const wadah_error = document.getElementById('pesan_error');
|
||||
const tombol_login = document.getElementById('btn_submit_login');
|
||||
|
||||
if (!username_val || !password_val) {
|
||||
wadah_error.textContent = 'Kolom username dan kata sandi tidak boleh kosong.';
|
||||
wadah_error.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
wadah_error.classList.add('hidden');
|
||||
tombol_login.disabled = true;
|
||||
tombol_login.textContent = 'Memverifikasi...';
|
||||
|
||||
try {
|
||||
// Diubah ke relative path
|
||||
const http_response = await fetch('/api/auth/login-petugas', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: username_val,
|
||||
password: password_val
|
||||
})
|
||||
});
|
||||
|
||||
const json_result = await http_response.json();
|
||||
|
||||
if (!http_response.ok) {
|
||||
throw new Error(json_result.error || 'Gagal masuk ke dalam sistem.');
|
||||
}
|
||||
|
||||
localStorage.setItem('token_petugas', json_result.token);
|
||||
window.location.href = 'petugas_dasbor.html';
|
||||
|
||||
} catch (error_sistem) {
|
||||
wadah_error.textContent = error_sistem.message;
|
||||
wadah_error.classList.remove('hidden');
|
||||
} finally {
|
||||
tombol_login.disabled = false;
|
||||
tombol_login.textContent = 'Masuk ke Survei';
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('input_password').addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
eksekusi_login_petugas();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Portofolio Sistem Informasi Geografis - Muhammad Iqbal Maulana</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Plus Jakarta Sans', sans-serif; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-slate-50 text-slate-800 min-h-screen flex flex-col selection:bg-emerald-500 selection:text-white">
|
||||
|
||||
<header class="bg-white border-b border-slate-200/80 sticky top-0 z-50 backdrop-blur-md bg-white/80">
|
||||
<div class="max-w-5xl mx-auto px-6 py-4 flex flex-col sm:flex-row justify-between items-center gap-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-600 rounded-xl flex items-center justify-center text-white font-extrabold shadow-md shadow-emerald-500/20">
|
||||
SIG
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="font-bold text-slate-900 text-sm leading-tight">Portal Tugas SIG</h1>
|
||||
<p class="text-[11px] text-slate-500 font-medium tracking-wide uppercase">Informatika Universitas Tanjungpura</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center sm:text-right">
|
||||
<p class="text-sm font-semibold text-slate-800">Muhammad Iqbal Maulana</p>
|
||||
<p class="text-xs font-mono text-slate-400 mt-0.5">NIM: D1041231047 </p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="flex-grow max-w-5xl mx-auto w-full px-6 py-12 space-y-12">
|
||||
|
||||
<section class="text-center max-w-2xl mx-auto space-y-4">
|
||||
<span class="px-3 py-1 bg-emerald-50 border border-emerald-200 text-emerald-700 text-xs font-bold rounded-full uppercase tracking-wider inline-block">UAS Evaluasi Praktis</span>
|
||||
<h2 class="text-3xl sm:text-4xl font-extrabold text-slate-900 tracking-tight leading-tight">
|
||||
Kompilasi Sistem Informasi Geografis Terapan
|
||||
</h2>
|
||||
<p class="text-sm text-slate-500 font-light max-w-lg mx-auto leading-relaxed">
|
||||
Aplikasi WebGIS produksi terintegrasi yang dikembangkan untuk memenuhi tugas akhir semester, berjalan di atas infrastruktur kontainer peladen Docker Coolify.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 gap-6 pt-4">
|
||||
|
||||
<div class="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm hover:shadow-xl hover:border-slate-300 transition-all duration-300 flex flex-col justify-between group">
|
||||
<div class="space-y-4">
|
||||
<div class="w-12 h-12 bg-amber-50 border border-amber-200 text-amber-600 rounded-2xl flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 22V2a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v20"/><path d="M18 12h4"/><path d="M10 6h4"/><path d="M12 2v4"/><circle cx="12" cy="12" r="2"/></svg>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<h3 class="font-bold text-lg text-slate-900 tracking-tight">Web Pemetaan Pom Bensin & Wilayah</h3>
|
||||
<p class="text-xs font-medium text-amber-600 uppercase tracking-wide">Analisis Geospasial Infrastruktur</p>
|
||||
</div>
|
||||
<p class="text-xs text-slate-400 font-light leading-relaxed">
|
||||
Sistem informasi spasial yang memetakan persebaran Stasiun Pengisian Bahan Bakar Umum (SPBU) di area Pontianak, dilengkapi zonasi wilayah jangkauan pelayanan guna analisis efisiensi fasilitas publik.
|
||||
</p>
|
||||
</div>
|
||||
<div class="pt-6 mt-6 border-t border-slate-100 flex items-center justify-between">
|
||||
<a href="http://e8wwc0wgwsws0w4ggoc8g88g.203.24.51.230.sslip.io/index.html" target="_blank" class="px-4 py-2.5 bg-slate-900 hover:bg-slate-800 text-white text-xs font-bold rounded-xl transition-all shadow-sm flex items-center gap-1">
|
||||
Buka Aplikasi
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm hover:shadow-xl hover:border-slate-300 transition-all duration-300 flex flex-col justify-between group">
|
||||
<div class="space-y-4">
|
||||
<div class="w-12 h-12 bg-emerald-50 border border-emerald-200 text-emerald-600 rounded-2xl flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<h3 class="font-bold text-lg text-slate-900 tracking-tight">WebGIS PovertyMap</h3>
|
||||
<p class="text-xs font-medium text-emerald-600 uppercase tracking-wide">Transparansi Sosial & Validasi BPS</p>
|
||||
</div>
|
||||
<p class="text-xs text-slate-400 font-light leading-relaxed">
|
||||
Sistem Informasi Geografis pemetaan klaster kemiskinan masyarakat Kota Pontianak terintegrasi 10 parameter BPS. Menghubungkan titik hunian warga dengan pangkalan rumah ibadah terdekat secara asinkron.
|
||||
</p>
|
||||
</div>
|
||||
<div class="pt-6 mt-6 border-t border-slate-100 flex items-center justify-between">
|
||||
<a href="http://l08g8oso4gk0wc400cggog4s.203.24.51.230.sslip.io/index.html" target="_blank" class="px-4 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-bold rounded-xl transition-all shadow-sm flex items-center gap-1">
|
||||
Buka Aplikasi
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="bg-white border-t border-slate-200 text-center py-6 text-xs font-medium text-slate-400 mt-auto">
|
||||
© 2026 Jurusan Informatika Universitas Tanjungpura. Dibuat dengan dedikasi penuh.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,437 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Survei Lapangan - 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;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Poppins', sans-serif; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 flex flex-col min-h-screen">
|
||||
|
||||
<nav class="bg-emerald-600 p-4 shadow-md z-30 flex items-center gap-4 text-white sticky top-0">
|
||||
<a href="index.html" class="hover:bg-emerald-700 p-2 rounded-lg transition">
|
||||
<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="m15 18-6-6 6-6"/></svg>
|
||||
</a>
|
||||
<h1 class="text-xl font-bold">Formulir Survei Kemiskinan Kriteria BPS</h1>
|
||||
</nav>
|
||||
|
||||
<main class="flex-grow p-4 md:p-8 flex justify-center items-start">
|
||||
<div class="bg-white w-full max-w-2xl rounded-2xl shadow-lg border border-gray-200 overflow-hidden">
|
||||
|
||||
<div class="bg-gray-50 border-b border-gray-200 p-6 flex justify-between items-center">
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div id="indikator_1" class="w-8 h-8 rounded-full bg-emerald-600 text-white flex justify-center items-center font-bold text-sm transition-colors">1</div>
|
||||
<span class="text-[10px] font-semibold text-gray-600">Identitas</span>
|
||||
</div>
|
||||
<div class="h-1 flex-1 bg-gray-200 mx-1 rounded">
|
||||
<div id="garis_1" class="h-full bg-emerald-600 w-0 transition-all duration-300"></div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div id="indikator_2" class="w-8 h-8 rounded-full bg-gray-300 text-gray-600 flex justify-center items-center font-bold text-sm transition-colors">2</div>
|
||||
<span class="text-[10px] font-semibold text-gray-600">Rumah</span>
|
||||
</div>
|
||||
<div class="h-1 flex-1 bg-gray-200 mx-1 rounded">
|
||||
<div id="garis_2" class="h-full bg-emerald-600 w-0 transition-all duration-300"></div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div id="indikator_3" class="w-8 h-8 rounded-full bg-gray-300 text-gray-600 flex justify-center items-center font-bold text-sm transition-colors">3</div>
|
||||
<span class="text-[10px] font-semibold text-gray-600">Ekonomi</span>
|
||||
</div>
|
||||
<div class="h-1 flex-1 bg-gray-200 mx-1 rounded">
|
||||
<div id="garis_3" class="h-full bg-emerald-600 w-0 transition-all duration-300"></div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div id="indikator_4" class="w-8 h-8 rounded-full bg-gray-300 text-gray-600 flex justify-center items-center font-bold text-sm transition-colors">4</div>
|
||||
<span class="text-[10px] font-semibold text-gray-600">Lokasi</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<div id="peringatan_error" class="hidden mb-4 p-3 bg-red-50 border border-red-200 text-red-700 rounded-lg text-sm font-medium"></div>
|
||||
|
||||
<form id="form_survei" onsubmit="return false;">
|
||||
|
||||
<div id="langkah_1" class="space-y-4">
|
||||
<h2 class="text-lg font-bold text-gray-800 border-b pb-2">Informasi Legalitas Identitas</h2>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nomor Induk Kependudukan (NIK)</label>
|
||||
<input type="text" id="input_nik" maxlength="16" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none" placeholder="16 Digit NIK KTP Kepala Keluarga">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nama Lengkap Kepala Keluarga</label>
|
||||
<input type="text" id="input_nama_kk" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none" placeholder="Masukkan nama lengkap">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nomor Kartu Keluarga (No KK)</label>
|
||||
<input type="text" id="input_no_kk" maxlength="16" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none" placeholder="16 Digit Nomor KK">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Jumlah Anggota Keluarga Tanggungan</label>
|
||||
<input type="number" id="input_jumlah_tanggungan" min="0" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none" placeholder="Contoh: 4">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Agama Kepala Keluarga</label>
|
||||
<select id="input_agama" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none">
|
||||
<option value="">Pilih Agama...</option>
|
||||
<option value="Islam">Islam</option>
|
||||
<option value="Kristen Protestan">Kristen Protestan</option>
|
||||
<option value="Katolik">Katolik</option>
|
||||
<option value="Hindu">Hindu</option>
|
||||
<option value="Buddha">Buddha</option>
|
||||
<option value="Konghucu">Konghucu</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="langkah_2" class="hidden space-y-4">
|
||||
<h2 class="text-lg font-bold text-gray-800 border-b pb-2">Kondisi Fisik Rumah Tinggal</h2>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Luas Lantai Per Kapita (Per Orang)</label>
|
||||
<select id="input_luas_lantai" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none">
|
||||
<option value="">Pilih ukuran luas...</option>
|
||||
<option value="Kurang dari 8m2">Kurang dari 8 meter persegi per orang</option>
|
||||
<option value="8m2 atau Lebih">8 meter persegi atau lebih per orang</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Material Bahan Lantai Terluas</label>
|
||||
<select id="input_material_lantai" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none">
|
||||
<option value="">Pilih jenis material...</option>
|
||||
<option value="Tanah / Bambu">Tanah / Bambu / Kayu Murah</option>
|
||||
<option value="Semen / Keramik">Semen / Ubin / Keramik</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Material Bahan Dinding Terluas</label>
|
||||
<select id="input_material_dinding" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none">
|
||||
<option value="">Pilih jenis dinding...</option>
|
||||
<option value="Bambu / Rumbia / Kayu Murah">Bambu / Rumbia / Kayu Kualitas Rendah</option>
|
||||
<option value="Tembok Permanen">Tembok / Bata / Plesteran</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Fasilitas Sanitasi (MCK)</label>
|
||||
<select id="input_fasilitas_sanitasi" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none">
|
||||
<option value="">Pilih fasilitas sanitasi...</option>
|
||||
<option value="Tidak Ada / Bersama">Tidak Ada / Menggunakan MCK Umum atau Bersama</option>
|
||||
<option value="Milik Sendiri">Ada Pribadi di Dalam Rumah</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="langkah_3" class="hidden space-y-4">
|
||||
<h2 class="text-lg font-bold text-gray-800 border-b pb-2">Kondisi Ekonomi & Fasilitas Utilitas</h2>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Sumber Air Minum Utama</label>
|
||||
<select id="input_sumber_air" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none">
|
||||
<option value="">Pilih sumber air...</option>
|
||||
<option value="Sumur Tak Terlindung / Sungai / Air Hujan">Sumur Tak Terlindung / Air Hujan / Sungai</option>
|
||||
<option value="PDAM / Sumur Terlindung">PDAM / Sumur Bor Terlindung</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Sumber Penerangan Utama</label>
|
||||
<select id="input_sumber_penerangan" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none">
|
||||
<option value="">Pilih jenis penerangan...</option>
|
||||
<option value="Non-PLN / Menumpang">Bukan Listrik PLN / Menumpang Tetangga</option>
|
||||
<option value="PLN Mandiri">Listrik Meteran PLN Sendiri</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Pekerjaan Utama Kepala Keluarga</label>
|
||||
<input type="text" id="input_pekerjaan" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none" placeholder="Contoh: Buruh Harian Lepas, Pemulung">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Estimasi Pendapatan Total Per Bulan (Rp)</label>
|
||||
<input type="number" id="input_pendapatan" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none" placeholder="Contoh: 1200000">
|
||||
</div>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Frekuensi Makan Per Hari</label>
|
||||
<select id="input_frekuensi_makan" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none">
|
||||
<option value="">Pilih frekuensi...</option>
|
||||
<option value="1 Kali atau Kurang">1 Kali Sehari atau Kurang</option>
|
||||
<option value="2 Kali">2 Kali Sehari</option>
|
||||
<option value="3 Kali atau Lebih">3 Kali Sehari atau Lebih</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Kepemilikan Aset Kendaraan</label>
|
||||
<select id="input_kepemilikan_aset" class="w-full p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-emerald-500 outline-none">
|
||||
<option value="">Pilih status kepemilikan...</option>
|
||||
<option value="Tidak Memiliki Motor">Tidak Memiliki Sepeda Motor</option>
|
||||
<option value="Memiliki Motor / Aset Lain">Memiliki Motor atau Aset Berharga Lain</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="langkah_4" class="hidden space-y-4">
|
||||
<h2 class="text-lg font-bold text-gray-800 border-b pb-2">Verifikasi Titik Lokasi Geografis</h2>
|
||||
<button type="button" onclick="dapatkan_lokasi_gps()" class="bg-blue-50 text-blue-700 hover:bg-blue-100 border border-blue-200 px-4 py-3 rounded-xl text-sm font-bold transition w-full">
|
||||
Ambil Koordinat Lewat GPS Perangkat
|
||||
</button>
|
||||
<div class="h-64 w-full rounded-xl overflow-hidden border border-gray-300 relative z-0">
|
||||
<div id="wadah_peta" class="h-full w-full"></div>
|
||||
</div>
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-1">
|
||||
<label class="block text-xs font-medium text-gray-500">Garis Lintang (Latitude)</label>
|
||||
<input type="text" id="input_lat" class="w-full p-2 bg-gray-50 border rounded text-sm text-gray-700" readonly>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<label class="block text-xs font-medium text-gray-500">Garis Bujur (Longitude)</label>
|
||||
<input type="text" id="input_lng" class="w-full p-2 bg-gray-50 border rounded text-sm text-gray-700" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 border-t border-gray-200 p-4 flex justify-between">
|
||||
<button type="button" id="btn_mundur" onclick="ubah_langkah(-1)" class="hidden px-6 py-2 rounded-lg font-bold text-gray-600 hover:bg-gray-200 transition">Sebelumnya</button>
|
||||
<div class="flex-1"></div>
|
||||
<button type="button" id="btn_maju" onclick="ubah_langkah(1)" class="bg-emerald-600 text-white px-8 py-2 rounded-lg font-bold hover:bg-emerald-700 transition shadow-sm">Selanjutnya</button>
|
||||
<button type="button" id="btn_kirim" onclick="kirim_data_survei()" class="hidden bg-emerald-600 text-white px-8 py-2 rounded-lg font-bold hover:bg-emerald-700 transition shadow-sm">Kirim Hasil Survei</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
let langkah_sekarang = 1;
|
||||
const total_langkah = 4;
|
||||
|
||||
let instansi_peta = null;
|
||||
let penanda_peta = null;
|
||||
|
||||
const btn_mundur = document.getElementById('btn_mundur');
|
||||
const btn_maju = document.getElementById('btn_maju');
|
||||
const btn_kirim = document.getElementById('btn_kirim');
|
||||
const peringatan_error = document.getElementById('peringatan_error');
|
||||
|
||||
function tampilkan_error(pesan) {
|
||||
peringatan_error.textContent = pesan;
|
||||
peringatan_error.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function sembunyikan_error() {
|
||||
peringatan_error.classList.add('hidden');
|
||||
peringatan_error.textContent = '';
|
||||
}
|
||||
|
||||
function validasi_langkah_aktif() {
|
||||
sembunyikan_error();
|
||||
switch(langkah_sekarang) {
|
||||
case 1:
|
||||
if (!/^\d{16}$/.test(document.getElementById('input_nik').value)) {
|
||||
tampilkan_error('NIK wajib diisi tepat 16 digit angka.');
|
||||
return false;
|
||||
}
|
||||
if (document.getElementById('input_nama_kk').value.trim().length < 3) {
|
||||
tampilkan_error('Nama lengkap kepala keluarga wajib diisi (minimal 3 karakter).');
|
||||
return false;
|
||||
}
|
||||
if (!/^\d{16}$/.test(document.getElementById('input_no_kk').value)) {
|
||||
tampilkan_error('Nomor Kartu Keluarga wajib diisi tepat 16 digit angka.');
|
||||
return false;
|
||||
}
|
||||
if (document.getElementById('input_jumlah_tanggungan').value === '') {
|
||||
tampilkan_error('Jumlah anggota keluarga tanggungan harus diisi.');
|
||||
return false;
|
||||
}
|
||||
// ==========================================
|
||||
// BARIS BARU: Memastikan agama telah dipilih
|
||||
// ==========================================
|
||||
if (document.getElementById('input_agama').value === '') {
|
||||
tampilkan_error('Agama kepercayaan wajib dipilih untuk penentuan lokasi bantuan.');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!document.getElementById('input_luas_lantai').value ||
|
||||
!document.getElementById('input_material_lantai').value ||
|
||||
!document.getElementById('input_material_dinding').value ||
|
||||
!document.getElementById('input_fasilitas_sanitasi').value) {
|
||||
tampilkan_error('Seluruh parameter kelayakan rumah tinggal wajib dipilih.');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!document.getElementById('input_sumber_air').value ||
|
||||
!document.getElementById('input_sumber_penerangan').value ||
|
||||
!document.getElementById('input_frekuensi_makan').value ||
|
||||
!document.getElementById('input_kepemilikan_aset').value) {
|
||||
tampilkan_error('Seluruh parameter utilitas and sosial ekonomi wajib dipilih.');
|
||||
return false;
|
||||
}
|
||||
if (document.getElementById('input_pekerjaan').value.trim() === '') {
|
||||
tampilkan_error('Pekerjaan utama harus diisi.');
|
||||
return false;
|
||||
}
|
||||
const pendapatan = document.getElementById('input_pendapatan').value;
|
||||
if (pendapatan === '' || Number(pendapatan) < 0) {
|
||||
tampilkan_error('Estimasi nominal pendapatan harus diisi dengan angka valid.');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (!document.getElementById('input_lat').value) {
|
||||
tampilkan_error('Titik koordinat penanda spasial belum terekam di peta.');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function ubah_langkah(arah) {
|
||||
if (arah === 1 && !validasi_langkah_aktif()) return;
|
||||
|
||||
document.getElementById(`langkah_${langkah_sekarang}`).classList.add('hidden');
|
||||
langkah_sekarang += arah;
|
||||
document.getElementById(`langkah_${langkah_sekarang}`).classList.remove('hidden');
|
||||
|
||||
perbarui_antarmuka_wizard();
|
||||
}
|
||||
|
||||
function perbarui_antarmuka_wizard() {
|
||||
if (langkah_sekarang === 1) {
|
||||
btn_mundur.classList.add('hidden');
|
||||
} else {
|
||||
btn_mundur.classList.remove('hidden');
|
||||
}
|
||||
|
||||
if (langkah_sekarang === total_langkah) {
|
||||
btn_maju.classList.add('hidden');
|
||||
btn_kirim.classList.remove('hidden');
|
||||
setTimeout(inisialisasi_peta, 100);
|
||||
} else {
|
||||
btn_maju.classList.remove('hidden');
|
||||
btn_kirim.classList.add('hidden');
|
||||
}
|
||||
|
||||
for (let i = 1; i <= total_langkah; i++) {
|
||||
const indikator = document.getElementById(`indikator_${i}`);
|
||||
if (i <= langkah_sekarang) {
|
||||
indikator.classList.replace('bg-gray-300', 'bg-emerald-600');
|
||||
indikator.classList.replace('text-gray-600', 'text-white');
|
||||
} else {
|
||||
indikator.classList.replace('bg-emerald-600', 'bg-gray-300');
|
||||
indikator.classList.replace('text-white', 'text-gray-600');
|
||||
}
|
||||
}
|
||||
|
||||
if(document.getElementById('garis_1')) document.getElementById('garis_1').style.width = langkah_sekarang >= 2 ? '100%' : '0%';
|
||||
if(document.getElementById('garis_2')) document.getElementById('garis_2').style.width = langkah_sekarang >= 3 ? '100%' : '0%';
|
||||
if(document.getElementById('garis_3')) document.getElementById('garis_3').style.width = langkah_sekarang >= 4 ? '100%' : '0%';
|
||||
}
|
||||
|
||||
function inisialisasi_peta() {
|
||||
if (instansi_peta !== null) {
|
||||
instansi_peta.invalidateSize();
|
||||
return;
|
||||
}
|
||||
|
||||
instansi_peta = L.map('wadah_peta').setView([-0.0227, 109.3425], 13);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap'
|
||||
}).addTo(instansi_peta);
|
||||
|
||||
instansi_peta.on('click', function(e) {
|
||||
const lat = e.latlng.lat;
|
||||
const lng = e.latlng.lng;
|
||||
|
||||
if (penanda_peta) instansi_peta.removeLayer(penanda_peta);
|
||||
penanda_peta = L.marker([lat, lng]).addTo(instansi_peta);
|
||||
|
||||
document.getElementById('input_lat').value = lat.toFixed(6);
|
||||
document.getElementById('input_lng').value = lng.toFixed(6);
|
||||
sembunyikan_error();
|
||||
});
|
||||
}
|
||||
|
||||
function dapatkan_lokasi_gps() {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
function(posisi) {
|
||||
const lat = posisi.coords.latitude;
|
||||
const lng = posisi.coords.longitude;
|
||||
|
||||
document.getElementById('input_lat').value = lat.toFixed(6);
|
||||
document.getElementById('input_lng').value = lng.toFixed(6);
|
||||
|
||||
instansi_peta.setView([lat, lng], 16);
|
||||
if (penanda_peta) instansi_peta.removeLayer(penanda_peta);
|
||||
penanda_peta = L.marker([lat, lng]).addTo(instansi_peta);
|
||||
sembunyikan_error();
|
||||
},
|
||||
function(error) {
|
||||
tampilkan_error("Gagal menangkap koordinat satelit GPS. Pastikan izin lokasi aktif.");
|
||||
}
|
||||
);
|
||||
} else {
|
||||
tampilkan_error("Perangkat peramban tidak mendukung navigasi Geolocation.");
|
||||
}
|
||||
}
|
||||
|
||||
async function kirim_data_survei() {
|
||||
if (!validasi_langkah_aktif()) return;
|
||||
|
||||
btn_kirim.disabled = true;
|
||||
btn_kirim.innerHTML = 'Mengirim Data...';
|
||||
|
||||
const payload_survei = {
|
||||
nik: document.getElementById('input_nik').value,
|
||||
nama_kk: document.getElementById('input_nama_kk').value,
|
||||
no_kk: document.getElementById('input_no_kk').value,
|
||||
jumlah_tanggungan: parseInt(document.getElementById('input_jumlah_tanggungan').value) || 0,
|
||||
agama: document.getElementById('input_agama').value, // <--- Ini yang sebelumnya tertinggal
|
||||
lat: parseFloat(document.getElementById('input_lat').value),
|
||||
lng: parseFloat(document.getElementById('input_lng').value),
|
||||
indikator_bps: {
|
||||
luas_lantai_per_kapita: document.getElementById('input_luas_lantai').value,
|
||||
material_lantai: document.getElementById('input_material_lantai').value,
|
||||
material_dinding: document.getElementById('input_material_dinding').value,
|
||||
fasilitas_sanitasi: document.getElementById('input_fasilitas_sanitasi').value,
|
||||
sumber_air_minum: document.getElementById('input_sumber_air').value,
|
||||
sumber_penerangan: document.getElementById('input_sumber_penerangan').value,
|
||||
pekerjaan_utama: document.getElementById('input_pekerjaan').value,
|
||||
pendapatan_bulanan: parseFloat(document.getElementById('input_pendapatan').value) || 0,
|
||||
frekuensi_makan: document.getElementById('input_frekuensi_makan').value,
|
||||
kepemilikan_aset: document.getElementById('input_kepemilikan_aset').value
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const http_response = await fetch('/api/survey/submit', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload_survei)
|
||||
});
|
||||
|
||||
const json_result = await http_response.json();
|
||||
|
||||
if (!http_response.ok) throw new Error(json_result.error || 'Terjadi kesalahan internal server.');
|
||||
|
||||
alert('Data rekam survei kriteria baru berhasil disimpan ke basis data!');
|
||||
window.location.href = 'index.html';
|
||||
|
||||
} catch (error_sistem) {
|
||||
tampilkan_error(error_sistem.message);
|
||||
btn_kirim.disabled = false;
|
||||
btn_kirim.innerHTML = 'Kirim Hasil Survei';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user