/** * script.js — WebGIS SPBU * File ini berisi seluruh logika JavaScript: * - Inisialisasi peta Leaflet * - Klik peta → popup form tambah data * - Ambil data dari database & tampilkan marker * - Tambah, edit, hapus data via AJAX/fetch * - Warna marker sesuai kondisi SPBU */ /* ══════════════════════════════════════════════════════════ 1. VARIABEL GLOBAL ══════════════════════════════════════════════════════════ */ // Objek yang menyimpan semua marker di peta, dengan key = id SPBU const markersMap = {}; // Referensi popup sementara saat user klik peta (sebelum data disimpan) let tempPopup = null; /* ══════════════════════════════════════════════════════════ 2. INISIALISASI PETA LEAFLET ══════════════════════════════════════════════════════════ */ // Buat instance peta, pusatkan ke Informatika const informatikaUntan = [-0.05566422376778593, 109.34854622732368]; const map = L.map('map').setView(informatikaUntan, 17); // Tambahkan tile layer OpenStreetMap L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap' }).addTo(map); /* ══════════════════════════════════════════════════════════ 3. LEGEND PETA ══════════════════════════════════════════════════════════ */ const legend = L.control({ position: 'bottomright' }); legend.onAdd = function () { const div = L.DomUtil.create('div', 'legend'); div.innerHTML = `

🗺️ Keterangan Marker

Ramai · 24 Jam · Buka
Tidak Ramai · 24 Jam · Buka
Ramai · Tidak 24 Jam · Buka
Tidak Ramai · Tidak 24 Jam · Buka
Tutup (apapun kondisinya)
`; return div; }; legend.addTo(map); /* ══════════════════════════════════════════════════════════ 4. FUNGSI UTILITAS ══════════════════════════════════════════════════════════ */ /** * Tampilkan toast notification. * @param {string} pesan - Teks yang ditampilkan * @param {string} tipe - 'success' | 'error' */ function tampilkanToast(pesan, tipe = 'success') { const toast = document.getElementById('toast'); toast.textContent = pesan; toast.className = `show toast-${tipe}`; setTimeout(() => { toast.className = ''; }, 3000); } /** Tampilkan loading spinner */ function showLoading() { document.getElementById('loading').classList.add('show'); } /** Sembunyikan loading spinner */ function hideLoading() { document.getElementById('loading').classList.remove('show'); } /** * Tentukan warna marker berdasarkan kondisi SPBU. * Aturan: * - tutup → hitam * - ramai + 24jam + buka → orange * - tidak_ramai + 24jam + buka → hijau * - ramai + tidak 24jam + buka → merah * - tidak_ramai + tidak 24jam + buka → biru */ function getWarnaMarker(spbu) { if (spbu.status_operasional === 'tutup') return 'black'; if (spbu.status_ramai === 'ramai' && spbu.buka_24_jam === 'ya') return 'orange'; if (spbu.status_ramai === 'tidak_ramai' && spbu.buka_24_jam === 'ya') return 'green'; if (spbu.status_ramai === 'ramai' && spbu.buka_24_jam === 'tidak') return 'red'; if (spbu.status_ramai === 'tidak_ramai' && spbu.buka_24_jam === 'tidak') return 'blue'; return 'gray'; // fallback } /** * Buat ikon marker berwarna menggunakan SVG inline. * @param {string} warna - Nama warna CSS */ function buatIkon(warna) { // Palet warna hex const palet = { orange: '#f97316', green: '#22c55e', red: '#ef4444', blue: '#3b82f6', black: '#1f2937', gray: '#9ca3af' }; const hex = palet[warna] || '#9ca3af'; // SVG pin marker const svg = ` `; return L.divIcon({ html: svg, className: '', // Hapus class default Leaflet iconSize: [28, 42], iconAnchor: [14, 42], // Titik anchor di ujung bawah pin popupAnchor: [0, -42], // Popup muncul di atas marker }); } /** * Format label yang lebih ramah untuk ditampilkan. */ function labelRamai(val) { return val === 'ramai' ? 'Ramai' : 'Tidak Ramai'; } function labelBuka24(val) { return val === 'ya' ? 'Ya' : 'Tidak'; } function labelOperasional(val) { return val === 'buka' ? 'Buka' : 'Tutup'; } function badgeRamai(val) { return val === 'ramai' ? `Ramai` : `Tidak Ramai`; } function badgeBuka24(val) { return val === 'ya' ? `Ya (24 Jam)` : `Tidak`; } function badgeOperasional(val) { return val === 'buka' ? `Buka` : `Tutup`; } /* ══════════════════════════════════════════════════════════ 5. BUAT KONTEN POPUP DETAIL DATA SPBU ══════════════════════════════════════════════════════════ */ /** * Menghasilkan HTML untuk popup detail SPBU. * Ditampilkan ketika marker di-klik. */ function buatPopupDetail(spbu) { return ` `; } /* ══════════════════════════════════════════════════════════ 6. AMBIL DATA DARI DATABASE & TAMPILKAN MARKER ══════════════════════════════════════════════════════════ */ /** * Ambil semua data SPBU dari server lalu tampilkan sebagai marker di peta. */ async function muatSemuaMarker() { try { const res = await fetch('ambil_data.php'); const json = await res.json(); if (json.status !== 'success') { console.error('Gagal ambil data:', json.message); return; } // Hapus marker lama yang sudah ada di peta Object.values(markersMap).forEach(m => map.removeLayer(m)); // Kosongkan objek referensi marker for (const key in markersMap) delete markersMap[key]; // Buat marker baru untuk setiap SPBU json.data.forEach(spbu => { tambahMarker(spbu); }); } catch (err) { console.error('Error fetch ambil_data:', err); } } /** * Tambahkan satu marker ke peta dan daftarkan ke markersMap. * @param {object} spbu - Data SPBU dari database */ function tambahMarker(spbu) { const warna = getWarnaMarker(spbu); const ikon = buatIkon(warna); const marker = L.marker([spbu.latitude, spbu.longitude], { icon: ikon }) .addTo(map) .bindPopup(buatPopupDetail(spbu), { maxWidth: 320 }); // Simpan referensi marker markersMap[spbu.id] = marker; } /* ══════════════════════════════════════════════════════════ 7. KLIK PETA → FORM TAMBAH DATA SPBU ══════════════════════════════════════════════════════════ */ // Event: saat user klik di peta map.on('click', function (e) { const lat = e.latlng.lat.toFixed(7); const lng = e.latlng.lng.toFixed(7); // Tutup popup sementara yang mungkin masih terbuka if (tempPopup) { map.closePopup(tempPopup); tempPopup = null; } // Konten form tambah data (lat & lng disembunyikan dari user) const formHtml = ` `; // Buka popup di lokasi yang diklik tempPopup = L.popup({ maxWidth: 340 }) .setLatLng(e.latlng) .setContent(formHtml) .openOn(map); }); /* ══════════════════════════════════════════════════════════ 8. SIMPAN DATA SPBU BARU (TAMBAH) ══════════════════════════════════════════════════════════ */ /** * Kirim data form tambah ke server via fetch (AJAX). * Dipanggil oleh tombol "Simpan" di popup form tambah. * @param {number} lat - Latitude dari klik peta * @param {number} lng - Longitude dari klik peta */ async function simpanData(lat, lng) { // Ambil nilai dari form const nama = document.getElementById('f-nama')?.value.trim(); const ramai = document.getElementById('f-ramai')?.value; const jam24 = document.getElementById('f-24jam')?.value; const operasional = document.getElementById('f-operasional')?.value; const hp = document.getElementById('f-hp')?.value.trim(); // Validasi input tidak boleh kosong if (!nama || !ramai || !jam24 || !operasional || !hp) { tampilkanToast('⚠️ Semua field wajib diisi!', 'error'); return; } // Siapkan data untuk dikirim const formData = new FormData(); formData.append('nama_spbu', nama); formData.append('status_ramai', ramai); formData.append('buka_24_jam', jam24); formData.append('status_operasional', operasional); formData.append('nomor_hp', hp); formData.append('latitude', lat); formData.append('longitude', lng); showLoading(); try { const res = await fetch('tambah.php', { method: 'POST', body: formData }); const json = await res.json(); if (json.status === 'success') { // Tutup popup form map.closePopup(); tempPopup = null; // Langsung tambah marker baru tanpa perlu refresh halaman tambahMarker(json.data); // Geser peta ke lokasi SPBU baru dan buka popupnya map.panTo([json.data.latitude, json.data.longitude]); markersMap[json.data.id].openPopup(); tampilkanToast('✅ Data SPBU berhasil ditambahkan!', 'success'); } else { tampilkanToast('❌ ' + json.message, 'error'); } } catch (err) { tampilkanToast('❌ Gagal terhubung ke server.', 'error'); console.error(err); } finally { hideLoading(); } } /* ══════════════════════════════════════════════════════════ 9. BUKA FORM EDIT DATA SPBU ══════════════════════════════════════════════════════════ */ /** * Ganti isi popup marker dengan form edit. * Latitude dan longitude TIDAK ditampilkan dan TIDAK bisa diedit. * @param {number} id - ID SPBU yang akan diedit */ async function bukaFormEdit(id) { showLoading(); try { // Ambil data terbaru dari server const res = await fetch('ambil_data.php'); const json = await res.json(); hideLoading(); if (json.status !== 'success') { tampilkanToast('❌ Gagal mengambil data.', 'error'); return; } // Cari data SPBU yang sesuai ID const spbu = json.data.find(s => s.id == id); if (!spbu) { tampilkanToast('❌ Data tidak ditemukan.', 'error'); return; } // Buat konten form edit (lat & lng disembunyikan) const formEditHtml = ` `; // Update isi popup yang sedang terbuka markersMap[id].setPopupContent(formEditHtml).openPopup(); } catch (err) { hideLoading(); tampilkanToast('❌ Gagal terhubung ke server.', 'error'); console.error(err); } } /* ══════════════════════════════════════════════════════════ 10. SIMPAN PERUBAHAN EDIT DATA SPBU ══════════════════════════════════════════════════════════ */ /** * Kirim data form edit ke server. * Latitude & longitude tidak dikirim → tidak berubah di database. * @param {number} id - ID SPBU yang diedit */ async function simpanEdit(id) { const nama = document.getElementById('e-nama')?.value.trim(); const ramai = document.getElementById('e-ramai')?.value; const jam24 = document.getElementById('e-24jam')?.value; const operasional = document.getElementById('e-operasional')?.value; const hp = document.getElementById('e-hp')?.value.trim(); if (!nama || !ramai || !jam24 || !operasional || !hp) { tampilkanToast('⚠️ Semua field wajib diisi!', 'error'); return; } const formData = new FormData(); formData.append('id', id); formData.append('nama_spbu', nama); formData.append('status_ramai', ramai); formData.append('buka_24_jam', jam24); formData.append('status_operasional', operasional); formData.append('nomor_hp', hp); // ⚠️ latitude & longitude sengaja TIDAK dikirim showLoading(); try { const res = await fetch('edit.php', { method: 'POST', body: formData }); const json = await res.json(); if (json.status === 'success') { const spbuBaru = json.data; // Perbarui ikon marker sesuai kondisi baru const warnaBar = getWarnaMarker(spbuBaru); markersMap[id].setIcon(buatIkon(warnaBar)); // Perbarui isi popup dengan data terbaru markersMap[id].setPopupContent(buatPopupDetail(spbuBaru)); markersMap[id].openPopup(); tampilkanToast('✅ Data berhasil diperbarui!', 'success'); } else { tampilkanToast('❌ ' + json.message, 'error'); } } catch (err) { tampilkanToast('❌ Gagal terhubung ke server.', 'error'); console.error(err); } finally { hideLoading(); } } /* ══════════════════════════════════════════════════════════ 11. HAPUS DATA SPBU ══════════════════════════════════════════════════════════ */ /** * Hapus data SPBU dari database dan marker dari peta. * @param {number} id - ID SPBU yang dihapus * @param {string} namaSpbu - Nama SPBU untuk konfirmasi */ async function hapusData(id, namaSpbu) { // Konfirmasi sebelum menghapus if (!confirm(`Yakin ingin menghapus SPBU "${namaSpbu}"?\nTindakan ini tidak dapat dibatalkan.`)) { return; } const formData = new FormData(); formData.append('id', id); showLoading(); try { const res = await fetch('hapus.php', { method: 'POST', body: formData }); const json = await res.json(); if (json.status === 'success') { // Hapus marker dari peta if (markersMap[id]) { map.removeLayer(markersMap[id]); delete markersMap[id]; } tampilkanToast('🗑️ Data SPBU berhasil dihapus.', 'success'); } else { tampilkanToast('❌ ' + json.message, 'error'); } } catch (err) { tampilkanToast('❌ Gagal terhubung ke server.', 'error'); console.error(err); } finally { hideLoading(); } } /* ══════════════════════════════════════════════════════════ 12. HELPER: Tampilkan detail dari ID (tombol Batal di form edit) ══════════════════════════════════════════════════════════ */ async function tampilkanDetailDariId(id) { try { const res = await fetch('ambil_data.php'); const json = await res.json(); const spbu = json.data.find(s => s.id == id); if (spbu && markersMap[id]) { markersMap[id].setPopupContent(buatPopupDetail(spbu)).openPopup(); } } catch (err) { console.error(err); } } /* ══════════════════════════════════════════════════════════ 13. INISIALISASI: Muat semua marker saat halaman dibuka ══════════════════════════════════════════════════════════ */ // Panggil fungsi ini sekali saat halaman selesai dimuat muatSemuaMarker();