Files
2026-06-11 18:14:21 +07:00

561 lines
26 KiB
PHP

<?php
require_once '../config.php';
require_once '../auth/helper.php';
require_once '../koneksi.php';
if (!is_logged_in() || !has_role('administrator')) { header('Location: ../auth/login.php'); exit; }
require_password_changed('../auth/change_password.php');
$page_title = 'Rumah Ibadah';
$page_subtitle = 'Kelola data rumah ibadah dan radius pelayanan';
$active_nav = 'ibadah';
include '../includes/page-start.php';
?>
<!-- Leaflet -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" crossorigin="">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" crossorigin=""></script>
<style>
.ib-filter-bar { display:flex; flex-direction:column; gap:8px; margin-bottom:16px; }
.ib-search { width:100%; box-sizing:border-box; padding:8px 12px; border:1px solid var(--card-border); border-radius:8px; font-family:var(--font); font-size:13px; background:#fafaf9; color:var(--text-primary); outline:none; }
.ib-search:focus { border-color:#0d7490; }
.ib-filter-chips { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.ib-chip { width:auto; height:auto; padding:7px 11px; border:1px solid var(--card-border); border-radius:8px; font-family:var(--font); font-size:13px; background:#fafaf9; color:var(--text-primary); cursor:pointer; white-space:nowrap; }
.ib-chip:focus { outline:none; border-color:#0d7490; }
.modal-dark { position:fixed; inset:0; background:rgba(32,21,21,.55); backdrop-filter:blur(4px); display:flex; align-items:center; justify-content:center; padding:20px; z-index:9999; opacity:0; pointer-events:none; transition:opacity .2s; }
.modal-dark.show { opacity:1; pointer-events:auto; }
.modal-box { width:min(600px,100%); background:#fafaf9; border:1px solid var(--card-border); border-radius:12px; padding:28px; box-shadow:0 4px 16px rgba(32,21,21,.1); max-height:90vh; overflow-y:auto; }
.modal-box h3 { font-size:16px; font-weight:600; margin-bottom:20px; color:var(--text-primary); }
.mfg { margin-bottom:14px; }
.mfg label { display:block; font-size:11px; font-weight:600; color:var(--text-secondary); text-transform:uppercase; letter-spacing:.5px; margin-bottom:5px; }
.mfg input, .mfg select { width:100%; padding:9px 12px; border:1px solid var(--card-border); border-radius:8px; font-family:var(--font); font-size:13px; color:var(--text-primary); background:#fafaf9; outline:none; transition:border-color .2s; }
.mfg input:focus, .mfg select:focus { border-color:#0d7490; }
.mfg small { font-size:11px; color:var(--text-muted); margin-top:4px; display:block; }
.modal-actions { display:flex; gap:10px; justify-content:flex-end; margin-top:20px; }
.btn-cancel { padding:9px 16px; background:transparent; border:1px solid var(--card-border); border-radius:8px; font-family:var(--font); font-size:13px; font-weight:600; color:var(--text-secondary); cursor:pointer; transition:background .15s; }
.btn-cancel:hover { background:#ede8e2; }
.btn-submit { padding:9px 18px; background:#0d7490; border:none; border-radius:8px; font-family:var(--font); font-size:14px; font-weight:600; color:#fff; cursor:pointer; transition:background .15s; }
.btn-submit:hover { background:#0a5f7a; }
.btn-submit:disabled { opacity:.5; cursor:not-allowed; }
.btn-danger-submit { padding:9px 18px; background:#ef4444; border:none; border-radius:8px; font-family:var(--font); font-size:13px; font-weight:600; color:#fff; cursor:pointer; transition:background .15s; }
.btn-danger-submit:hover { background:#dc2626; }
.msg-err { font-size:12px; color:#ef4444; margin-top:8px; min-height:16px; }
/* ── Map picker ── */
.map-picker-wrap { border:1px solid var(--card-border); border-radius:10px; overflow:hidden; position:relative; }
.map-picker-wrap #ibMapPicker { height:260px; cursor:crosshair; }
.map-picker-hint { display:flex; align-items:center; gap:6px; font-size:11px; color:var(--text-muted); margin-top:6px; }
.map-geocoding { font-size:11px; color:var(--text-muted); margin-top:5px; min-height:16px; font-style:italic; }
/* Override Leaflet z-index agar tidak keluar modal */
#ibMapPicker .leaflet-control-zoom { display:block; }
</style>
<!-- Toolbar -->
<div class="ib-filter-bar">
<input type="text" class="ib-search" id="ibSearch" placeholder="Cari nama rumah ibadah..." oninput="filterTable()">
<div class="ib-filter-chips">
<select class="ib-chip" id="ibFilterJenis" onchange="filterTable()">
<option value="">Semua Jenis</option>
<option>Masjid</option><option>Mushola</option><option>Gereja</option>
<option>Pura</option><option>Vihara</option><option>Klenteng</option>
</select>
<div style="width:1px;height:20px;background:var(--card-border);margin:0 2px;align-self:center;flex-shrink:0;"></div>
<select class="ib-chip" id="ibSort" onchange="filterTable()">
<option value="">Urutkan</option>
<option value="nama_asc">Nama A→Z</option>
<option value="nama_desc">Nama Z→A</option>
<option value="radius_desc">Radius Terbesar</option>
<option value="radius_asc">Radius Terkecil</option>
<option value="kk_desc">KK Binaan Terbanyak</option>
<option value="kk_asc">KK Binaan Tersedikit</option>
<option value="jiwa_desc">Jiwa Terbanyak</option>
<option value="jiwa_asc">Jiwa Tersedikit</option>
</select>
<div style="flex:1;"></div>
<a href="map.php" class="btn-see-all" style="text-decoration:none;padding:6px 12px;border-radius:8px;font-size:12px;font-weight:600;display:inline-flex;align-items:center;gap:4px;white-space:nowrap;"><i data-lucide="map"></i> Lihat di Peta</a>
<button class="btn-submit" onclick="openAdd()" style="white-space:nowrap;">+ Tambah Ibadah</button>
</div>
</div>
<!-- Table -->
<div class="table-card">
<div class="table-card-header">
<div>
<div class="table-card-title">Daftar Rumah Ibadah</div>
<div class="table-card-count" id="ibCount"></div>
</div>
</div>
<div style="overflow-x:auto;">
<table class="admin-table">
<thead>
<tr>
<th style="width:44px;">No</th>
<th>Nama</th>
<th>Jenis</th>
<th style="text-align:right;">Radius (m)</th>
<th style="text-align:right;">KK Binaan</th>
<th style="text-align:right;">Jiwa</th>
<th>Kontak</th>
<th style="width:100px;text-align:center;">Aksi</th>
</tr>
</thead>
<tbody id="ibTbody">
<tr><td colspan="8" style="text-align:center;padding:28px;color:var(--text-muted);"><span class="spinner"></span> Memuat...</td></tr>
</tbody>
</table>
</div>
</div>
<!-- Modal Add/Edit -->
<div class="modal-dark" id="ibModal">
<div class="modal-box">
<h3 id="ibModalTitle">Tambah Rumah Ibadah</h3>
<input type="hidden" id="ibEditId">
<div class="mfg">
<label>Nama Rumah Ibadah</label>
<input type="text" id="ibNama" placeholder="Masjid Al-Ikhlas">
</div>
<div class="mfg">
<label>Jenis</label>
<select id="ibJenis">
<option>Masjid</option><option>Mushola</option><option>Gereja</option>
<option>Pura</option><option>Vihara</option><option>Klenteng</option>
</select>
</div>
<div class="mfg">
<label>Alamat</label>
<input type="text" id="ibAlamat" placeholder="Jl. Contoh No. 1">
</div>
<div class="mfg">
<label>Kontak Pengurus</label>
<input type="text" id="ibKontak" placeholder="08xx-xxxx-xxxx">
</div>
<!-- Map Picker -->
<div class="mfg">
<label>Lokasi di Peta <span style="font-weight:400;color:var(--text-muted);text-transform:none;letter-spacing:0;">— klik untuk set titik, drag marker untuk geser</span></label>
<div class="map-picker-wrap">
<div id="ibMapPicker"></div>
</div>
<div class="map-picker-hint" style="display:flex;align-items:center;gap:12px;">
<span style="display:inline-flex;align-items:center;gap:4px;"><i data-lucide="mouse-pointer"></i> Klik peta</span>
<span style="color:var(--card-border);">·</span>
<span style="display:inline-flex;align-items:center;gap:4px;"><i data-lucide="map-pin"></i> Drag marker</span>
<span style="color:var(--card-border);">·</span>
<span style="display:inline-flex;align-items:center;gap:4px;"><i data-lucide="zoom-in"></i> Scroll untuk zoom</span>
</div>
<div class="map-geocoding" id="ibGeocodingStatus"></div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div class="mfg">
<label>Latitude</label>
<input type="number" id="ibLat" step="any" placeholder="-0.0557"
oninput="onCoordsManualChange()">
</div>
<div class="mfg">
<label>Longitude</label>
<input type="number" id="ibLng" step="any" placeholder="109.3487"
oninput="onCoordsManualChange()">
</div>
</div>
<div class="mfg">
<label>Radius Pelayanan</label>
<div style="display:flex;align-items:center;gap:12px;">
<input type="range" id="ibRadius" value="500" min="100" max="5000" step="50"
oninput="onRadiusChange(this.value)"
style="flex:1;height:4px;accent-color:#111111;cursor:pointer;">
<span id="ibRadiusVal"
style="min-width:56px;text-align:right;font-family:var(--font);
font-size:14px;font-weight:600;color:#374151;">500m</span>
</div>
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--text-muted);margin-top:3px;">
<span>100m</span><span>500m</span><span>1km</span><span>2.5km</span><span>5km</span>
</div>
</div>
<div class="msg-err" id="ibMsg"></div>
<div class="modal-actions">
<button class="btn-cancel" onclick="closeIbModal()">Batal</button>
<button class="btn-submit" id="ibSaveBtn" onclick="saveIbadah()">Simpan</button>
</div>
</div>
</div>
<!-- Modal Konfirmasi Hapus (2-step) -->
<div class="modal-dark" id="ibHapusModal">
<div class="modal-box" style="max-width:440px;">
<h3 id="ibHapusTitle">Konfirmasi Hapus</h3>
<div id="ibHapusWarning" style="font-size:13px;color:var(--text-secondary);line-height:1.6;margin-bottom:16px;"></div>
<input type="hidden" id="ibHapusId">
<input type="hidden" id="ibHapusConfirmed" value="0">
<div class="modal-actions">
<button class="btn-cancel" onclick="closeHapusModal()">Batal</button>
<button class="btn-danger-submit" id="ibHapusBtn" onclick="doHapus()">Ya, Hapus</button>
</div>
</div>
</div>
<script>
let _ibAll = [];
function esc(s) {
const d = document.createElement('div');
d.appendChild(document.createTextNode(s ?? ''));
return d.innerHTML;
}
function attr(s) {
return String(s ?? '').replace(/&/g, '&amp;').replace(/"/g, '&quot;')
.replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
async function loadIbadah() {
try {
const r = await fetch('../api/ibadah/ambil.php?_=' + Date.now());
const j = await r.json();
if (j.status !== 'success') return;
_ibAll = j.data;
document.getElementById('ibCount').textContent = j.total + ' rumah ibadah';
filterTable();
} catch(e) {
document.getElementById('ibTbody').innerHTML =
'<tr><td colspan="8" style="text-align:center;padding:24px;color:#ef4444;">Gagal memuat data.</td></tr>';
}
}
function filterTable() {
const q = document.getElementById('ibSearch').value.toLowerCase();
const jenis = document.getElementById('ibFilterJenis').value;
const sort = document.getElementById('ibSort').value;
const rows = _ibAll.filter(r =>
(!q || r.nama.toLowerCase().includes(q)) &&
(!jenis || r.jenis === jenis)
);
if (sort) {
rows.sort((a, b) => {
switch (sort) {
case 'nama_asc': return (a.nama||'').localeCompare(b.nama||'', 'id');
case 'nama_desc': return (b.nama||'').localeCompare(a.nama||'', 'id');
case 'radius_desc': return (b.radius_m||0) - (a.radius_m||0);
case 'radius_asc': return (a.radius_m||0) - (b.radius_m||0);
case 'kk_desc': return (b.total_kk||0) - (a.total_kk||0);
case 'kk_asc': return (a.total_kk||0) - (b.total_kk||0);
case 'jiwa_desc': return (b.total_jiwa||0) - (a.total_jiwa||0);
case 'jiwa_asc': return (a.total_jiwa||0) - (b.total_jiwa||0);
default: return 0;
}
});
}
const jenisMap = { Masjid:'masjid', Mushola:'mushola', Gereja:'gereja', Pura:'pura', Vihara:'vihara', Klenteng:'klenteng' };
const tbody = document.getElementById('ibTbody');
if (!rows.length) {
tbody.innerHTML = '<tr><td colspan="8" style="text-align:center;padding:24px;color:var(--text-muted);">Tidak ada data.</td></tr>';
return;
}
tbody.innerHTML = rows.map((r, i) => `<tr>
<td style="color:var(--text-muted);font-size:12px;">${i + 1}</td>
<td style="font-weight:600;">${esc(r.nama)}</td>
<td><span class="jenis-badge ${jenisMap[r.jenis] || ''}">${esc(r.jenis)}</span></td>
<td style="text-align:right;font-family:var(--font-mono);">${esc(String(r.radius_m))}</td>
<td style="text-align:right;font-family:var(--font-mono);">${esc(String(r.total_kk || 0))}</td>
<td style="text-align:right;font-family:var(--font-mono);">${esc(String(r.total_jiwa || 0))}</td>
<td style="color:var(--text-muted);font-size:12px;">${esc(r.kontak || '—')}</td>
<td style="text-align:center;display:flex;align-items:center;justify-content:center;gap:4px;">
<button class="tbl-action-btn" title="Edit" data-id="${attr(parseInt(r.id, 10) || 0)}" onclick="openEditById(this.dataset.id)"><i data-lucide="pencil"></i></button>
<button class="tbl-action-btn" title="Hapus" style="color:#ef4444;" data-id="${attr(parseInt(r.id, 10) || 0)}" onclick="openHapus(this.dataset.id)"><i data-lucide="trash-2"></i></button>
</td>
</tr>`).join('');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function openAdd() {
document.getElementById('ibEditId').value = '';
document.getElementById('ibModalTitle').textContent = 'Tambah Rumah Ibadah';
['ibNama', 'ibAlamat', 'ibKontak', 'ibLat', 'ibLng'].forEach(id => {
const el = document.getElementById(id);
el.value = '';
delete el.dataset.userEdited;
});
document.getElementById('ibRadius').value = 500;
document.getElementById('ibRadiusVal').textContent = '500m';
document.getElementById('ibJenis').value = 'Masjid';
document.getElementById('ibMsg').textContent = '';
document.getElementById('ibGeocodingStatus').textContent = '';
document.getElementById('ibSaveBtn').textContent = 'Simpan';
document.getElementById('ibModal').classList.add('show');
// Init map tanpa koordinat awal — center ke default wilayah
initMapPicker(null, null);
}
function openEdit(r) {
document.getElementById('ibEditId').value = r.id;
document.getElementById('ibModalTitle').textContent = 'Edit Rumah Ibadah';
document.getElementById('ibNama').value = r.nama;
document.getElementById('ibJenis').value = r.jenis;
document.getElementById('ibKontak').value = r.kontak || '';
document.getElementById('ibLat').value = r.lat;
document.getElementById('ibLng').value = r.lng;
const rv = parseInt(r.radius_m) || 500;
document.getElementById('ibRadius').value = rv;
document.getElementById('ibRadiusVal').textContent = rv >= 1000
? (rv / 1000).toFixed(rv % 1000 === 0 ? 0 : 1) + 'km' : rv + 'm';
document.getElementById('ibMsg').textContent = '';
document.getElementById('ibGeocodingStatus').textContent = '';
document.getElementById('ibSaveBtn').textContent = 'Simpan';
// Set alamat dan tandai sudah diedit (jangan di-overwrite geocoding)
const alamatEl = document.getElementById('ibAlamat');
alamatEl.value = r.alamat || '';
alamatEl.dataset.userEdited = r.alamat ? '1' : '';
document.getElementById('ibModal').classList.add('show');
// Init map pada koordinat yang sudah ada
initMapPicker(parseFloat(r.lat), parseFloat(r.lng));
}
function openEditById(id) {
const row = _ibAll.find(r => String(r.id) === String(id));
if (row) openEdit(row);
}
function closeIbModal() {
document.getElementById('ibModal').classList.remove('show');
clearTimeout(_geocodeTimer);
if (_ibMap) { _ibMap.remove(); _ibMap = null; _ibMarker = null; _ibCircle = null; }
}
async function saveIbadah() {
const id = document.getElementById('ibEditId').value;
const btn = document.getElementById('ibSaveBtn');
const msg = document.getElementById('ibMsg');
btn.disabled = true;
btn.innerHTML = '<span class="spinner"></span> Menyimpan...';
msg.textContent = '';
const namaVal = document.getElementById('ibNama').value.trim();
const latVal = document.getElementById('ibLat').value.trim();
const lngVal = document.getElementById('ibLng').value.trim();
if (!namaVal) {
msg.textContent = 'Nama wajib diisi.';
btn.disabled = false; btn.textContent = 'Simpan'; return;
}
if (!latVal || !lngVal) {
msg.textContent = 'Koordinat latitude dan longitude wajib diisi.';
btn.disabled = false; btn.textContent = 'Simpan'; return;
}
const fd = new FormData();
if (id) fd.append('id', id);
fd.append('nama', document.getElementById('ibNama').value.trim());
fd.append('jenis', document.getElementById('ibJenis').value);
fd.append('alamat', document.getElementById('ibAlamat').value.trim());
fd.append('kontak', document.getElementById('ibKontak').value.trim());
fd.append('lat', document.getElementById('ibLat').value);
fd.append('lng', document.getElementById('ibLng').value);
fd.append('radius_m', document.getElementById('ibRadius').value);
const url = id ? '../api/ibadah/update.php' : '../api/ibadah/simpan.php';
try {
const r = await fetch(url, { method: 'POST', body: appendCsrf(fd) });
const j = await r.json();
btn.disabled = false;
btn.textContent = 'Simpan';
if (j.status === 'success') { closeIbModal(); loadIbadah(); }
else { msg.textContent = j.message || 'Terjadi kesalahan.'; }
} catch(e) {
btn.disabled = false;
btn.textContent = 'Simpan';
msg.textContent = 'Gagal terhubung ke server.';
}
}
function openHapus(id) {
document.getElementById('ibHapusId').value = id;
document.getElementById('ibHapusConfirmed').value = '0';
document.getElementById('ibHapusTitle').textContent = 'Konfirmasi Hapus';
document.getElementById('ibHapusWarning').textContent =
'Yakin ingin menghapus rumah ibadah ini? Semua data proximity akan dihitung ulang.';
document.getElementById('ibHapusBtn').textContent = 'Ya, Hapus';
document.getElementById('ibHapusModal').classList.add('show');
}
function closeHapusModal() {
document.getElementById('ibHapusModal').classList.remove('show');
}
async function doHapus() {
const id = document.getElementById('ibHapusId').value;
const confirmed = document.getElementById('ibHapusConfirmed').value;
const btn = document.getElementById('ibHapusBtn');
btn.disabled = true;
btn.innerHTML = '<span class="spinner"></span> Menghapus...';
const fd = new FormData();
fd.append('id', id);
if (confirmed === '1') fd.append('confirmed', '1');
try {
const r = await fetch('../api/ibadah/hapus.php', { method: 'POST', body: appendCsrf(fd) });
const j = await r.json();
btn.disabled = false;
if (j.status === 'warning') {
const opNames = (j.operators || []).map(o => esc(o.nama_lengkap)).join(', ');
document.getElementById('ibHapusTitle').innerHTML = '<i data-lucide="alert-triangle" style="color:#f59e0b;vertical-align:middle;"></i> Peringatan';
document.getElementById('ibHapusWarning').innerHTML =
`Rumah ibadah ini terhubung dengan Operator: <strong>${opNames}</strong>.<br>
Operator tidak bisa login sampai dikaitkan ke ibadah lain.<br><br>Tetap hapus?`;
document.getElementById('ibHapusConfirmed').value = '1';
document.getElementById('ibHapusBtn').textContent = 'Ya, Tetap Hapus';
if (typeof lucide !== 'undefined') lucide.createIcons();
} else if (j.status === 'success') {
closeHapusModal();
loadIbadah();
} else {
document.getElementById('ibHapusWarning').innerHTML =
`<span style="color:#ef4444;">${esc(j.message || 'Terjadi kesalahan.')}</span>`;
btn.textContent = 'Ya, Hapus';
}
} catch(e) {
btn.disabled = false;
btn.textContent = 'Ya, Hapus';
document.getElementById('ibHapusWarning').innerHTML =
'<span style="color:#ef4444;">Gagal terhubung ke server.</span>';
}
}
// ── Map Picker ──────────────────────────────────────────────
const DEFAULT_LAT = <?= CENTER_LAT ?>;
const DEFAULT_LNG = <?= CENTER_LNG ?>;
const DEFAULT_ZOOM = <?= ZOOM_LEVEL ?>;
let _ibMap = null;
let _ibMarker = null;
let _ibCircle = null;
let _geocodeTimer = null;
function initMapPicker(lat, lng) {
const centerLat = lat || DEFAULT_LAT;
const centerLng = lng || DEFAULT_LNG;
const zoom = lat ? 17 : DEFAULT_ZOOM;
if (_ibMap) {
_ibMap.remove();
_ibMap = null;
_ibMarker = null;
}
_ibMap = L.map('ibMapPicker', { zoomControl: true }).setView([centerLat, centerLng], zoom);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(_ibMap);
// Marker awal jika ada koordinat
if (lat && lng) {
placeMarker(lat, lng);
}
// Klik peta → set lokasi
_ibMap.on('click', function (e) {
placeMarker(e.latlng.lat, e.latlng.lng);
fillCoords(e.latlng.lat, e.latlng.lng);
scheduleGeocode(e.latlng.lat, e.latlng.lng);
});
// Fix ukuran setelah modal terlihat
setTimeout(() => _ibMap && _ibMap.invalidateSize(), 80);
}
function placeMarker(lat, lng) {
if (_ibMarker) {
_ibMarker.setLatLng([lat, lng]);
} else {
_ibMarker = L.marker([lat, lng], { draggable: true }).addTo(_ibMap);
_ibMarker.on('dragend', function (e) {
const ll = e.target.getLatLng();
fillCoords(ll.lat, ll.lng);
updateCircle(ll.lat, ll.lng);
scheduleGeocode(ll.lat, ll.lng);
});
}
updateCircle(lat, lng);
_ibMap.panTo([lat, lng]);
}
function updateCircle(lat, lng) {
const r = parseInt(document.getElementById('ibRadius').value) || 500;
if (_ibCircle) {
_ibCircle.setLatLng([lat, lng]).setRadius(r);
} else if (_ibMap) {
_ibCircle = L.circle([lat, lng], {
radius: r, color: '#111111', weight: 1.5,
fillColor: '#111111', fillOpacity: 0.05, opacity: 0.6
}).addTo(_ibMap);
}
}
function onRadiusChange(val) {
document.getElementById('ibRadiusVal').textContent = val >= 1000
? (val / 1000).toFixed(val % 1000 === 0 ? 0 : 1) + 'km'
: val + 'm';
if (_ibCircle) _ibCircle.setRadius(parseInt(val));
}
function fillCoords(lat, lng) {
document.getElementById('ibLat').value = lat.toFixed(8);
document.getElementById('ibLng').value = lng.toFixed(8);
}
// Ketik manual di field lat/lng → pindahkan marker
function onCoordsManualChange() {
const lat = parseFloat(document.getElementById('ibLat').value);
const lng = parseFloat(document.getElementById('ibLng').value);
if (!isNaN(lat) && !isNaN(lng) && _ibMap) {
placeMarker(lat, lng);
scheduleGeocode(lat, lng);
}
}
// Debounce reverse geocoding (delay 600ms setelah berhenti drag/klik)
function scheduleGeocode(lat, lng) {
clearTimeout(_geocodeTimer);
const status = document.getElementById('ibGeocodingStatus');
status.innerHTML = '<span class="spinner"></span> Mencari alamat...';
_geocodeTimer = setTimeout(() => reverseGeocode(lat, lng), 600);
}
async function reverseGeocode(lat, lng) {
const status = document.getElementById('ibGeocodingStatus');
const alamatEl = document.getElementById('ibAlamat');
try {
const r = await fetch(
`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=json`,
{ headers: { 'Accept-Language': 'id' } }
);
const j = await r.json();
if (j && j.display_name) {
// Hanya isi alamat jika masih kosong atau user belum mengetik manual
if (!alamatEl.dataset.userEdited) {
alamatEl.value = j.display_name;
}
status.innerHTML = '<i data-lucide="check" style="color:var(--text-success);margin-right:4px;"></i> ' + (j.address?.road || j.address?.suburb || j.display_name.split(',')[0]);
if (typeof lucide !== 'undefined') lucide.createIcons();
} else {
status.textContent = '';
}
} catch {
status.textContent = '';
}
}
// Tandai alamat sudah diedit manual → jangan di-overwrite geocoding
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('ibAlamat').addEventListener('input', function () {
this.dataset.userEdited = '1';
});
});
loadIbadah();
</script>
<?php include '../includes/page-end.php'; ?>