151 lines
6.5 KiB
JavaScript
151 lines
6.5 KiB
JavaScript
function haversineDistance(lat1, lng1, lat2, lng2) {
|
||
const R = 6371000;
|
||
const toRad = d => d * Math.PI / 180;
|
||
const dLat = toRad(lat2 - lat1);
|
||
const dLng = toRad(lng2 - lng1);
|
||
const a = Math.sin(dLat/2)**2 +
|
||
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng/2)**2;
|
||
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||
}
|
||
|
||
async function openLogBantuan(pmId, pmNama, pmLat, pmLng) {
|
||
map.closePopup();
|
||
const [logRes, riRes] = await Promise.all([
|
||
callAPI(`log_bantuan.php?penduduk_miskin_id=${pmId}`),
|
||
callAPI('rumah_ibadah.php')
|
||
]);
|
||
const logs = logRes.data || [];
|
||
const ris = riRes.data || [];
|
||
|
||
// --- Determine best default RI ---
|
||
let defaultRiId = ris.length > 0 ? ris[0].id : null;
|
||
|
||
if (pmLat != null && pmLng != null && ris.length > 0) {
|
||
// Find the RI whose circle the resident falls inside (if any), prefer that
|
||
const insideRis = ris.filter(r => {
|
||
const dist = haversineDistance(pmLat, pmLng, r.latitude, r.longitude);
|
||
return dist <= (r.radius_meter || 500);
|
||
});
|
||
|
||
if (insideRis.length > 0) {
|
||
// Among RIs covering this resident, pick the closest center
|
||
insideRis.sort((a, b) =>
|
||
haversineDistance(pmLat, pmLng, a.latitude, a.longitude) -
|
||
haversineDistance(pmLat, pmLng, b.latitude, b.longitude)
|
||
);
|
||
defaultRiId = insideRis[0].id;
|
||
} else {
|
||
// Not within any radius — pick nearest RI geometrically
|
||
const sorted = [...ris].sort((a, b) =>
|
||
haversineDistance(pmLat, pmLng, a.latitude, a.longitude) -
|
||
haversineDistance(pmLat, pmLng, b.latitude, b.longitude)
|
||
);
|
||
defaultRiId = sorted[0].id;
|
||
}
|
||
}
|
||
|
||
// Build RI options with distance label (admin only) or locked (petugas)
|
||
let riSelectHTML;
|
||
if (window.currentUser && window.currentUser.role === 'petugas_lapangan') {
|
||
// Petugas can only see their own RI
|
||
riSelectHTML = `<select class="form-select" id="lb-ri" disabled>
|
||
${ris.filter(r => String(r.id) === String(window.currentUser.rumah_ibadah_id))
|
||
.map(r => `<option value="${r.id}" selected>${esc(r.nama)} (${r.jenis})</option>`).join('')}
|
||
</select>`;
|
||
} else {
|
||
// Admin — show all, sorted by distance, with distance label if coords available
|
||
const sorted = (pmLat != null && pmLng != null)
|
||
? [...ris].sort((a, b) =>
|
||
haversineDistance(pmLat, pmLng, a.latitude, a.longitude) -
|
||
haversineDistance(pmLat, pmLng, b.latitude, b.longitude))
|
||
: ris;
|
||
|
||
riSelectHTML = `<select class="form-select" id="lb-ri">
|
||
${sorted.map(r => {
|
||
const dist = (pmLat != null && pmLng != null)
|
||
? Math.round(haversineDistance(pmLat, pmLng, r.latitude, r.longitude))
|
||
: null;
|
||
const distLabel = dist != null ? ` — ${dist < 1000 ? dist + ' m' : (dist/1000).toFixed(1) + ' km'}` : '';
|
||
const isInside = dist != null && dist <= (r.radius_meter || 500);
|
||
const label = `${esc(r.nama)} (${r.jenis})${distLabel}${isInside ? ' ✅' : ''}`;
|
||
const selected = String(r.id) === String(defaultRiId) ? 'selected' : '';
|
||
return `<option value="${r.id}" ${selected}>${label}</option>`;
|
||
}).join('')}
|
||
</select>
|
||
${pmLat != null ? `<div style="font-size:11px;color:#64748b;margin-top:4px">✅ = dalam radius · Diurutkan berdasarkan jarak terdekat</div>` : ''}`;
|
||
}
|
||
|
||
openModal(`Log Bantuan — ${pmNama}`, `
|
||
<div style="margin-bottom:16px">
|
||
<div class="layer-section-title">Riwayat Bantuan</div>
|
||
${logs.length ? `
|
||
${logs.slice(0, 2).map(l => `
|
||
<div class="log-item ${l.tipe_bantuan === 'Pemberdayaan' ? 'pemberdayaan' : 'konsumtif'}">
|
||
<div class="log-type">${l.tipe_bantuan === 'Pemberdayaan' ? '📚 Pemberdayaan' : '🛍 Konsumtif'}</div>
|
||
<div class="log-detail">${esc(l.sub_kategori) || ''} ${l.keterangan ? '— ' + esc(l.keterangan) : ''}</div>
|
||
<div class="log-date">📅 ${l.tanggal_bantuan} · ${esc(l.nama_rumah_ibadah)}</div>
|
||
</div>`).join('')}
|
||
${logs.length > 2 ? `<div style="font-size:11px;color:#64748b;text-align:center;padding:6px 0;border-top:1px solid #f1f5f9;margin-top:4px">+${logs.length - 2} bantuan lainnya · Lihat detail lengkap di halaman Daftar Penduduk Miskin</div>` : ''}
|
||
` : '<div class="loading">Belum ada riwayat bantuan</div>'}
|
||
</div>
|
||
<div class="layer-section-title">Tambah Bantuan Baru</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Dari Rumah Ibadah *</label>
|
||
${riSelectHTML}
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label class="form-label">Tipe Bantuan *</label>
|
||
<select class="form-select" id="lb-tipe">
|
||
<option value="Pemberdayaan">📚 Pemberdayaan (Pelatihan)</option>
|
||
<option value="Konsumtif">🛍 Konsumtif (Makan/Barang)</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Tanggal *</label>
|
||
<input class="form-input" id="lb-tgl" type="date" value="${new Date().toISOString().slice(0, 10)}"/>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Sub Kategori</label>
|
||
<input class="form-input" id="lb-sub" placeholder="Pelatihan menjahit / Sembako…"/>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label class="form-label">Keterangan</label>
|
||
<input class="form-input" id="lb-ket" placeholder="Opsional"/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Nilai (Rp)</label>
|
||
<input class="form-input" id="lb-nilai" type="number" placeholder="0"/>
|
||
</div>
|
||
</div>`,
|
||
`<button class="btn btn-ghost" onclick="closeModal()">Tutup</button>
|
||
<button class="btn btn-primary" onclick="saveLog(${pmId})">➕ Tambah Log</button>`);
|
||
}
|
||
|
||
|
||
async function saveLog(pmId) {
|
||
const body = {
|
||
penduduk_miskin_id: pmId,
|
||
rumah_ibadah_id: parseInt(document.getElementById('lb-ri').value),
|
||
tipe_bantuan: document.getElementById('lb-tipe').value,
|
||
tanggal_bantuan: document.getElementById('lb-tgl').value,
|
||
sub_kategori: document.getElementById('lb-sub').value,
|
||
keterangan: document.getElementById('lb-ket').value,
|
||
nilai_bantuan: document.getElementById('lb-nilai').value || null
|
||
};
|
||
const res = await callAPI('log_bantuan.php', 'POST', body);
|
||
if (res.status === 'success') {
|
||
toast('Log dicatat!', 'success');
|
||
closeModal();
|
||
switchTab('penduduk_miskin');
|
||
} else {
|
||
toast(res.message, 'error');
|
||
}
|
||
}
|
||
|
||
async function recalcAllProximity() {
|
||
await callAPI('penduduk_miskin.php?action=recalc_all', 'POST');
|
||
}
|