feat : fix fetch bug

This commit is contained in:
unknown
2026-06-09 12:28:44 +07:00
parent 1d55676e3e
commit f6231c1475
7 changed files with 152 additions and 128 deletions
+67 -24
View File
@@ -66,7 +66,7 @@
</tr>
</thead>
<tbody id="tabel_body_mustahik" class="divide-y divide-gray-200 text-sm text-gray-700">
</tbody>
</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>
@@ -74,8 +74,33 @@
</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>
// Fungsi membersihkan dan memperbarui isi baris tabel secara aman (XSS Prevention)
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');
@@ -100,9 +125,6 @@
const baris = document.createElement('tr');
baris.className = 'hover:bg-gray-50 transition-colors';
/* Baris Kompleks: Kita menyusun kerangka visual baris menggunakan template literal,
namun pengisian teks string sensitif tetap diserahkan ke fungsi properti textContent
pada sub-elemen guna meminimalkan celah eksploitasi kode HTML injeksi berbahaya. */
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>
@@ -119,7 +141,6 @@
</td>
`;
// Menyuntikkan nilai teks string secara terisolasi dan aman
baris.querySelector('.kolom-nama').textContent = warga.nama_kk;
baris.querySelector('.kolom-nik').textContent = warga.nik;
baris.querySelector('.kolom-nokk').textContent = warga.no_kk || '-';
@@ -139,7 +160,6 @@
tbody.appendChild(baris);
});
// Perbarui papan indikator agregat atas secara asinkron
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;
@@ -147,13 +167,13 @@
document.getElementById('stat_rejected_warga').textContent = hitung_rejected;
}
// Meminta daftar seluruh entitas warga dari sistem peladen Express
async function ambil_data_admin_mustahik() {
const banner_error = document.getElementById('banner_error_admin');
try {
banner_error.classList.add('hidden');
const http_response = await fetch('http://localhost:3000/api/admin/mustahik');
// 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.');
@@ -166,44 +186,67 @@
}
}
// Mengubah status persetujuan verifikasi warga via metode AJAX PATCH menuju backend
async function ubah_status_verifikasi(id_warga, status_baru) {
const banner_error = document.getElementById('banner_error_admin');
if (!confirm(`Apakah Anda yakin ingin mengubah status data ini menjadi ${status_baru}?`)) {
return;
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 {
/* Baris Kompleks: Melakukan transmisi parsial status data menggunakan metode HTTP PATCH
menuju endpoint verifikasi admin, membawa payload status terenkapsulasi JSON
sehingga perubahan status langsung tercermin di file SQLite tanpa memuat ulang layar. */
const http_response = await fetch(`http://localhost:3000/api/admin/verifikasi/${id_warga}`, {
// 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: status_baru })
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.');
// Segera sinkronkan ulang isi tabel setelah proses mutasi data berhasil
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';
}
}
// Mengakhiri sesi masuk administrator utama
function eksekusi_logout_admin() {
localStorage.removeItem('token_admin');
window.location.href = 'index.html';
}
// Inisialisasi awal penarikan data saat halaman terbuka sempurna
ambil_data_admin_mustahik();
</script>
</body>
+66 -18
View File
@@ -85,27 +85,49 @@
<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>
<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>
// 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;
let antrean_id_warga = null;
let antrean_status_baru = 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;
@@ -119,9 +141,7 @@
.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;
@@ -152,20 +172,17 @@
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}`);
// 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.');
@@ -177,29 +194,60 @@
}
}
// 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;
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 {
const http_response = await fetch(`http://localhost:3000/api/admin/verifikasi/${id_warga_terpilih}`, {
// 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: keputusan_status })
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';
}
}
// Eksekusi inisialisasi pemuatan data awal
ambil_data_detail_warga();
</script>
</body>
+2 -9
View File
@@ -33,7 +33,6 @@
</div>
<script>
// Mengirimkan data otentikasi admin menuju API backend server secara asinkron
async function eksekusi_login_admin() {
const username_val = document.getElementById('input_username').value;
const password_val = document.getElementById('input_password').value;
@@ -51,10 +50,8 @@
tombol_login.textContent = 'Otorisasi Sistem...';
try {
/* Baris Kompleks: Mengirimkan request POST JSON payload kredensial admin ke server port 3000.
Jika kredensial terverifikasi sah oleh database, string token otorisasi disimpan ke dalam localStorage
peramban sebelum mengalihkan hak akses visual secara penuh ke halaman dasbor admin. */
const http_response = await fetch('http://localhost:3000/api/auth/login-admin', {
// Diubah ke relative path
const http_response = await fetch('/api/auth/login-admin', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@@ -71,10 +68,7 @@
throw new Error(json_result.error || 'Akses ditolak oleh otoritas server.');
}
// Menyimpan identitas sesi token administrator ke memori lokal peramban web
localStorage.setItem('token_admin', json_result.token);
// Redireksi antarmuka secara instan menuju halaman dasbor pusat kontrol utama
window.location.href = 'admin_dasbor.html';
} catch (error_sistem) {
@@ -86,7 +80,6 @@
}
}
// Mengikat event liistener enter pada kolom kata sandi admin untuk efisiensi aksesibilitas
document.getElementById('input_password').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
eksekusi_login_admin();
+5 -11
View File
@@ -33,7 +33,7 @@
<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>
</div>
</section>
@@ -105,22 +105,17 @@
let map_marker = null;
// Fungsi baru untuk memuat statistik secara asinkron dari basis data
async function muat_statistik_publik() {
try {
const respons_http = await fetch('http://localhost:3000/api/survey/statistik');
// Diubah ke relative path
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();
// Memasukkan hasil hitungan ke dalam DOM menggunakan textContent
document.getElementById('stat_publik_terverifikasi').textContent = data_agregat.terverifikasi || 0;
document.getElementById('stat_publik_diproses').textContent = data_agregat.diproses || 0;
/* Baris Kompleks: Menggunakan objek bawaan Date pada JavaScript untuk
mendapatkan waktu spesifik saat skrip ini berjalan di perangkat pengguna,
lalu memformatnya secara spesifik ke dalam zona waktu WIB (Asia/Jakarta)
agar seragam untuk seluruh pengunjung situs terlepas dari lokasi fisik perangkat mereka. */
const objek_waktu_sekarang = new Date();
const waktu_terformat_wib = objek_waktu_sekarang.toLocaleTimeString('id-ID', {
hour: '2-digit',
@@ -150,7 +145,8 @@
tombol_cari.textContent = 'Mencari...';
try {
const http_response = await fetch(`http://localhost:3000/api/survey/cek-nik/${input_nik_value}`);
// Diubah ke relative path
const http_response = await fetch(`/api/survey/cek-nik/${input_nik_value}`);
if (!http_response.ok) {
throw new Error('Data tidak ditemukan');
@@ -186,9 +182,7 @@
}
};
// Mengeksekusi penarikan data statistik tepat saat halaman selesai dimuat
muat_statistik_publik();
</script>
</body>
</html>
+7 -36
View File
@@ -62,27 +62,18 @@
<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>
</div>
</section>
</main>
<script>
// Mengambil token autentikasi petugas untuk validasi sesi REST API
const token_petugas = localStorage.getItem('token_petugas');
// Fungsi pengecekan proteksi halaman (RBAC) saat pertama kali dimuat
function periksa_sesi_petugas() {
/* Sesi dummy dinonaktifkan sementara untuk mempermudah pengujian UAS.
Jika nanti sistem login admin/petugas Anda sudah aktif menggunakan JWT,
cukup hapus baris komentar pada blok proteksi di bawah ini. */
// if (!token_petugas) {
// alert('Akses ditolak. Silakan login terlebih dahulu.');
// window.location.href = 'petugas_login.html';
// }
// Sesi dinonaktifkan sementara untuk mempermudah testing
}
// Fungsi merender list data antrean warga ke elemen DOM secara aman (XSS Prevention)
function render_data_ke_tabel(daftar_warga) {
const wadah_list = document.getElementById('wadah_list_antrean');
const label_antrean = document.getElementById('label_jumlah_antrean');
@@ -96,7 +87,6 @@
label_antrean.textContent = `${daftar_warga.length} Rumah Tangga`;
// Variabel counter untuk menghitung jumlah status agregat data
let total_pending = 0;
let total_verified = 0;
@@ -107,9 +97,6 @@
const item_div = document.createElement('div');
item_div.className = 'p-4 flex justify-between items-center hover:bg-gray-50 transition-colors';
/* Baris Kompleks: Pembuatan elemen daftar dilakukan dengan kombinasi literal template Tailwind CSS.
Setiap teks string mentah yang berasal dari properti database dilewatkan melalui properti textContent
pada sub-elemen internal terpisah guna menangkal celah keamanan injeksi skrip berbahaya (Cross-Site Scripting). */
item_div.innerHTML = `
<div class="space-y-1">
<h4 class="font-bold text-gray-800 text-sm kelas-nama"></h4>
@@ -123,14 +110,12 @@
</div>
`;
// Menyuntikkan nilai data secara aman menggunakan textContent
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;
// Menentukan warna badge visual berdasarkan status validasi dari database
if (warga.status_validasi.toLowerCase() === 'verified') {
badge.className += ' bg-emerald-50 text-emerald-700';
} else if (warga.status_validasi.toLowerCase() === 'rejected') {
@@ -142,13 +127,11 @@
wadah_list.appendChild(item_div);
});
// Memperbarui indikator angka papan KPI atas
document.getElementById('stat_total').textContent = daftar_warga.length;
document.getElementById('stat_pending').textContent = total_pending;
document.getElementById('stat_verified').textContent = total_verified;
}
// Mengambil data seluruh antrean mustahik dari server backend Node.js
async function ambil_antrean_survei() {
const msg_container = document.getElementById('status_message_container');
@@ -156,22 +139,13 @@
msg_container.classList.remove('hidden');
msg_container.textContent = 'Memuat daftar antrean wilayah...';
/* Contoh implementasi fetch asinkron ke server lokal port 3000.
Gunakan endpoint yang sesuai (misal mengambil seluruh data MUSTAHIK untuk petugas) */
// const respons = await fetch('http://localhost:3000/api/survey/list-antrean');
// if (!respons.ok) throw new Error('Gagal mengambil data dari server.');
// const data_warga = await respons.json();
// Simulasi Data Mock untuk testing (Hapus blok ini jika endpoint backend Anda sudah siap)
await new Promise(resolve => setTimeout(resolve, 600));
const data_warga_mock = [
{ nik: "6171012345670001", nama_kk: "Syahroni", status_validasi: "Pending" },
{ nik: "6171029876540002", nama_kk: "Mariani", status_validasi: "Pending" },
{ nik: "6171031122330003", nama_kk: "Zulkifli", status_validasi: "Verified" }
];
// 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_mock);
render_data_ke_tabel(data_warga);
} catch (error_sistem) {
msg_container.classList.remove('hidden');
@@ -179,17 +153,14 @@
}
}
// Fungsi menghapus token autentikasi sesi saat petugas keluar aplikasi
function proses_logout_petugas() {
localStorage.removeItem('token_petugas');
alert('Sesi berhasil diakhiri.');
window.location.href = 'index.html';
}
// Eksekusi trigger inisialisasi awal saat halaman selesai dimuat oleh browser
periksa_sesi_petugas();
ambil_antrean_survei();
</script>
</body>
</html>
+2 -10
View File
@@ -28,14 +28,12 @@
</div>
<script>
// Mengirimkan data otentikasi menuju API backend server
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');
// Validasi masukan dasar pada sisi klien sebelum memicu lalu lintas jaringan
if (!username_val || !password_val) {
wadah_error.textContent = 'Kolom username dan kata sandi tidak boleh kosong.';
wadah_error.classList.remove('hidden');
@@ -47,10 +45,8 @@
tombol_login.textContent = 'Memverifikasi...';
try {
/* Baris Kompleks: Melakukan transmisi payload data sensitif terenkripsi via jaringan menggunakan HTTP POST
menuju peladen port 3000, serta menyimpan string token autentikasi ke media penyimpanan lokal peramban
(localStorage) jika validasi kredensial dinyatakan sah oleh server database. */
const http_response = await fetch('http://localhost:3000/api/auth/login-petugas', {
// Diubah ke relative path
const http_response = await fetch('/api/auth/login-petugas', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@@ -67,10 +63,7 @@
throw new Error(json_result.error || 'Gagal masuk ke dalam sistem.');
}
// Menyimpan identitas sesi token ke memori peramban web klien
localStorage.setItem('token_petugas', json_result.token);
// Mengalihkan fokus antarmuka menuju halaman dasbor manajemen tugas lapangan
window.location.href = 'petugas_dasbor.html';
} catch (error_sistem) {
@@ -82,7 +75,6 @@
}
}
// Mendukung eksekusi tombol enter saat berada di kolom input kata sandi
document.getElementById('input_password').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
eksekusi_login_petugas();
+3 -20
View File
@@ -196,7 +196,6 @@
</main>
<script>
// Deklarasi Status Langkah Global
let langkah_sekarang = 1;
const total_langkah = 4;
@@ -208,25 +207,19 @@
const btn_kirim = document.getElementById('btn_kirim');
const peringatan_error = document.getElementById('peringatan_error');
// Menampilkan pesan notifikasi kegagalan validasi ke antarmuka pengguna
function tampilkan_error(pesan) {
peringatan_error.textContent = pesan;
peringatan_error.classList.remove('hidden');
}
// Menyembunyikan wadah notifikasi kegagalan
function sembunyikan_error() {
peringatan_error.classList.add('hidden');
peringatan_error.textContent = '';
}
// Memastikan kelayakan data masukan di setiap bagian formulir sebelum melangkah
function validasi_langkah_aktif() {
sembunyikan_error();
/* Baris Kompleks: Logika kondisional bersarang (switch case) memisahkan aturan validasi
berdasarkan indeks tahapan aktif, memastikan konversi dan ekspresi reguler teks tepat
sebelum dievaluasi agar terhindar dari pengiriman muatan bernilai kosong ke server. */
switch(langkah_sekarang) {
case 1:
if (!/^\d{16}$/.test(document.getElementById('input_nik').value)) {
@@ -260,7 +253,7 @@
!document.getElementById('input_sumber_penerangan').value ||
!document.getElementById('input_frekuensi_makan').value ||
!document.getElementById('input_kepemilikan_aset').value) {
tampilkan_error('Seluruh parameter utilitas dan sosial ekonomi wajib dipilih.');
tampilkan_error('Seluruh parameter utilitas and sosial ekonomi wajib dipilih.');
return false;
}
if (document.getElementById('input_pekerjaan').value.trim() === '') {
@@ -283,7 +276,6 @@
return true;
}
// Menggerakkan alur visual formulir multi-langkah maju atau mundur
function ubah_langkah(arah) {
if (arah === 1 && !validasi_langkah_aktif()) return;
@@ -294,7 +286,6 @@
perbarui_antarmuka_wizard();
}
// Sinkronisasi status bar kemajuan dan kontrol tombol navigasi bawah
function perbarui_antarmuka_wizard() {
if (langkah_sekarang === 1) {
btn_mundur.classList.add('hidden');
@@ -327,7 +318,6 @@
if(document.getElementById('garis_3')) document.getElementById('garis_3').style.width = langkah_sekarang >= 4 ? '100%' : '0%';
}
// Inisialisasi komponen GIS Leaflet untuk penentuan lokasi rumah warga
function inisialisasi_peta() {
if (instansi_peta !== null) {
instansi_peta.invalidateSize();
@@ -339,9 +329,6 @@
attribution: '&copy; OpenStreetMap'
}).addTo(instansi_peta);
/* Baris Kompleks: Penangkapan event klik pada objek peta Leaflet secara dinamis
mengekstrak properti koordinat lintang dan bujur (latlng) untuk memindahkan objek penanda
serta mengisi elemen masukan teks masukan secara otomatis. */
instansi_peta.on('click', function(e) {
const lat = e.latlng.lat;
const lng = e.latlng.lng;
@@ -355,7 +342,6 @@
});
}
// Mengakses perangkat keras penerima gelombang navigasi satelit melalui web API browser
function dapatkan_lokasi_gps() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
@@ -380,14 +366,12 @@
}
}
// Mengirimkan enkapsulasi objek data survei menuju web server secara asinkron
async function kirim_data_survei() {
if (!validasi_langkah_aktif()) return;
btn_kirim.disabled = true;
btn_kirim.innerHTML = 'Mengirim Data...';
// Konstruksi objek payload JSON disesuaikan secara presisi dengan skema database_baru.sqlite
const payload_survei = {
nik: document.getElementById('input_nik').value,
nama_kk: document.getElementById('input_nama_kk').value,
@@ -411,9 +395,8 @@
};
try {
/* Baris Kompleks: Eksekusi transmisi data asinkron via HTTP POST menuju endpoint server.
Header 'Content-Type' diset ke JSON agar dapat dibaca oleh middleware express.json() di backend. */
const http_response = await fetch('http://localhost:3000/api/survey/submit', {
// Diubah ke relative path
const http_response = await fetch('/api/survey/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload_survei)