mengubah sistem menjadi lebih terfokus ke poverty
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
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');
|
||||
}
|
||||
Reference in New Issue
Block a user