325 lines
22 KiB
PHP
325 lines
22 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../config/db.php';
|
||
require_once __DIR__ . '/../config/auth_check.php';
|
||
requireAnyRole(['operator']);
|
||
$pdo = Database::getConnection();
|
||
|
||
$rows = $pdo->query("
|
||
SELECT w.*, wl.nama AS wilayah_nama,
|
||
(SELECT GROUP_CONCAT(jb.kode SEPARATOR ',')
|
||
FROM bantuan_sosial bs JOIN jenis_bantuan jb ON jb.id=bs.jenis_bantuan_id
|
||
WHERE bs.warga_id=w.id AND bs.is_aktif=1) AS bantuan_aktif,
|
||
(SELECT COUNT(*) FROM foto_rumah f WHERE f.warga_id=w.id) AS jumlah_foto
|
||
FROM warga_miskin w
|
||
LEFT JOIN wilayah wl ON wl.id=w.wilayah_id
|
||
ORDER BY w.id DESC
|
||
")->fetchAll();
|
||
|
||
$wilayahList = $pdo->query("SELECT id, nama FROM wilayah ORDER BY nama")->fetchAll();
|
||
$jenisList = $pdo->query("SELECT id, kode, nama FROM jenis_bantuan ORDER BY id")->fetchAll();
|
||
|
||
$pageTitle = 'Data Rumah Tangga Miskin'; $activeNav = 'warga';
|
||
$extraHead = '<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">';
|
||
require_once __DIR__ . '/partials/header.php';
|
||
|
||
function verifBadge($s) {
|
||
return match ($s) {
|
||
'terverifikasi' => '<span class="badge badge-success">✔ Terverifikasi</span>',
|
||
'perlu_tinjauan' => '<span class="badge badge-warning">⚠ Perlu Tinjauan</span>',
|
||
default => '<span class="badge" style="background:#F3F4F6;color:#6B7280;">○ Belum</span>',
|
||
};
|
||
}
|
||
?>
|
||
<div class="page-header">
|
||
<div class="breadcrumb"><span>Operator</span><span class="sep">/</span><span>Rumah Tangga Miskin</span></div>
|
||
<h1>👥 Data Rumah Tangga Miskin</h1>
|
||
<p>Kelola data lengkap rumah tangga, status bantuan, foto kondisi rumah, dan verifikasi (KF-12 s/d 18).</p>
|
||
</div>
|
||
|
||
<div class="page-toolbar" style="flex-wrap:wrap;gap:10px;">
|
||
<div class="search-box"><i class="fas fa-search search-icon"></i><input type="text" id="searchInput" placeholder="Cari nama / NIK..." oninput="filterTable('searchInput','wargaTable')"></div>
|
||
<select id="filterVerif" class="form-control" style="max-width:180px;" onchange="applyVerifFilter()">
|
||
<option value="">Semua status</option>
|
||
<option value="belum">Belum</option>
|
||
<option value="terverifikasi">Terverifikasi</option>
|
||
<option value="perlu_tinjauan">Perlu Tinjauan</option>
|
||
</select>
|
||
<button class="btn btn-ghost" onclick="bulkVerify()"><i class="fas fa-check-double"></i> Verifikasi Terpilih</button>
|
||
<a class="btn btn-ghost" href="<?= app_url('api/warga_export.php?format=csv') ?>"><i class="fas fa-file-csv"></i> Export CSV</a>
|
||
<a class="btn btn-ghost" href="<?= app_url('api/warga_export.php?format=print') ?>" target="_blank"><i class="fas fa-print"></i> Cetak/PDF</a>
|
||
<button class="btn btn-ghost" onclick="openModal('modalImport')"><i class="fas fa-file-import"></i> Import CSV</button>
|
||
<button class="btn btn-primary" onclick="openTambah()"><i class="fas fa-plus"></i> Tambah Data</button>
|
||
</div>
|
||
|
||
<div class="table-wrap">
|
||
<table class="data-table" id="wargaTable">
|
||
<thead><tr>
|
||
<th><input type="checkbox" id="checkAll" onclick="toggleAll(this)"></th>
|
||
<th>Nama KK</th><th>NIK</th><th>Kelurahan</th><th>Penghasilan</th>
|
||
<th>Jiwa</th><th>Bantuan</th><th>Foto</th><th>Verifikasi</th><th>Aksi</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<?php foreach ($rows as $r): ?>
|
||
<tr data-verif="<?= $r['status_verifikasi'] ?>">
|
||
<td><input type="checkbox" class="rowCheck" value="<?= $r['id'] ?>"></td>
|
||
<td><strong><?= htmlspecialchars($r['nama_kk']) ?></strong></td>
|
||
<td style="font-size:0.82rem;color:var(--text-muted)"><?= htmlspecialchars($r['nik_kk'] ?: '-') ?></td>
|
||
<td style="font-size:0.85rem"><?= htmlspecialchars($r['wilayah_nama'] ?: '-') ?></td>
|
||
<td>Rp <?= number_format($r['penghasilan'],0,',','.') ?></td>
|
||
<td><?= $r['jumlah_jiwa'] ?: ($r['jumlah_tanggungan'] ?: '-') ?></td>
|
||
<td style="font-size:0.78rem"><?= $r['bantuan_aktif'] ? '<span class="badge badge-info">'.str_replace(',','</span> <span class="badge badge-info">',htmlspecialchars($r['bantuan_aktif'])).'</span>' : '<span style="color:#9CA3AF">—</span>' ?></td>
|
||
<td><?= $r['jumlah_foto'] > 0 ? '📷 '.$r['jumlah_foto'] : '<span style="color:#9CA3AF">—</span>' ?></td>
|
||
<td><?= verifBadge($r['status_verifikasi']) ?></td>
|
||
<td><div class="actions">
|
||
<button class="btn btn-ghost btn-sm btn-icon" title="Kelola (bantuan & foto)" onclick="openDetail(<?= $r['id'] ?>,'<?= addslashes($r['nama_kk']) ?>')"><i class="fas fa-folder-open"></i></button>
|
||
<button class="btn btn-ghost btn-sm btn-icon" title="Edit" onclick='editRow(<?= json_encode($r, JSON_HEX_APOS|JSON_HEX_QUOT) ?>)'><i class="fas fa-edit"></i></button>
|
||
<button class="btn btn-danger btn-sm btn-icon" title="Hapus" onclick="hapus(<?= $r['id'] ?>,'<?= addslashes($r['nama_kk']) ?>')"><i class="fas fa-trash"></i></button>
|
||
</div></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php if (!$rows): ?><tr><td colspan="10" style="text-align:center;padding:40px;color:var(--text-muted);">Belum ada data.</td></tr><?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- ===== Modal Tambah/Edit ===== -->
|
||
<div class="modal-overlay" id="modalForm"><div class="modal" style="max-width:680px;">
|
||
<div class="modal-header"><span class="modal-title" id="formTitle">Tambah Data</span><button class="modal-close" onclick="closeModal('modalForm')">×</button></div>
|
||
<div style="max-height:70vh;overflow-y:auto;padding-right:6px;">
|
||
<input type="hidden" id="fId">
|
||
<h4 style="margin:4px 0 8px;font-size:0.85rem;color:#2563EB;">Identitas</h4>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||
<div class="form-group"><label class="form-label">Nama KK *</label><input id="fNama" class="form-control" required></div>
|
||
<div class="form-group"><label class="form-label">NIK KK</label><input id="fNik" class="form-control" maxlength="16" placeholder="16 digit"></div>
|
||
</div>
|
||
<div class="form-group"><label class="form-label">Alamat</label><input id="fAlamat" class="form-control"></div>
|
||
<div style="display:grid;grid-template-columns:2fr 1fr 1fr;gap:10px;">
|
||
<div class="form-group"><label class="form-label">Kelurahan</label>
|
||
<select id="fWilayah" class="form-control"><option value="">- pilih -</option>
|
||
<?php foreach ($wilayahList as $w): ?><option value="<?= $w['id'] ?>"><?= htmlspecialchars($w['nama']) ?></option><?php endforeach; ?>
|
||
</select></div>
|
||
<div class="form-group"><label class="form-label">RT</label><input id="fRt" class="form-control"></div>
|
||
<div class="form-group"><label class="form-label">RW</label><input id="fRw" class="form-control"></div>
|
||
</div>
|
||
|
||
<h4 style="margin:10px 0 8px;font-size:0.85rem;color:#2563EB;">Demografi & Ekonomi</h4>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||
<div class="form-group"><label class="form-label">Jumlah Jiwa</label><input id="fJiwa" type="number" min="0" class="form-control"></div>
|
||
<div class="form-group"><label class="form-label">Jumlah Tanggungan</label><input id="fTanggungan" type="number" min="0" class="form-control"></div>
|
||
<div class="form-group"><label class="form-label">Pekerjaan KK</label><input id="fPekerjaan" class="form-control"></div>
|
||
<div class="form-group"><label class="form-label">Penghasilan/bln (Rp)</label><input id="fPenghasilan" type="number" min="0" class="form-control"></div>
|
||
</div>
|
||
|
||
<h4 style="margin:10px 0 8px;font-size:0.85rem;color:#2563EB;">Kondisi Rumah</h4>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||
<div class="form-group"><label class="form-label">Sumber Air</label>
|
||
<select id="fAir" class="form-control"><option value="">-</option><option>SUMUR_BOR</option><option>PDAM</option><option>SUNGAI</option><option>HUJAN</option><option>TIDAK_ADA</option></select></div>
|
||
<div class="form-group"><label class="form-label">Listrik</label>
|
||
<select id="fListrik" class="form-control"><option value="">-</option><option>PLN</option><option>NON_PLN</option><option>TIDAK_ADA</option></select></div>
|
||
<div class="form-group"><label class="form-label">Material Dinding</label>
|
||
<select id="fDinding" class="form-control"><option value="">-</option><option>PERMANEN</option><option>SEMI_PERMANEN</option><option>TIDAK_PERMANEN</option></select></div>
|
||
<div class="form-group"><label class="form-label">Status Kepemilikan</label>
|
||
<select id="fKepemilikan" class="form-control"><option value="">-</option><option>MILIK</option><option>SEWA</option><option>NUMPANG</option><option>LAINNYA</option></select></div>
|
||
</div>
|
||
<div class="form-group"><label class="form-label">Status Verifikasi</label>
|
||
<select id="fVerif" class="form-control"><option value="belum">Belum</option><option value="terverifikasi">Terverifikasi</option><option value="perlu_tinjauan">Perlu Tinjauan</option></select></div>
|
||
|
||
<h4 style="margin:10px 0 8px;font-size:0.85rem;color:#2563EB;">Lokasi *</h4>
|
||
<div id="miniMap" style="height:220px;border-radius:8px;border:1px solid #E5E7EB;margin-bottom:10px;z-index:1;"></div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||
<input id="fLat" class="form-control" placeholder="Latitude" readonly style="background:#F9FAFB;">
|
||
<input id="fLng" class="form-control" placeholder="Longitude" readonly style="background:#F9FAFB;">
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer"><button class="btn btn-ghost" onclick="closeModal('modalForm')">Batal</button><button class="btn btn-primary" onclick="simpan()"><i class="fas fa-save"></i> Simpan</button></div>
|
||
</div></div>
|
||
|
||
<!-- ===== Modal Detail: Bantuan & Foto ===== -->
|
||
<div class="modal-overlay" id="modalDetail"><div class="modal" style="max-width:640px;">
|
||
<div class="modal-header"><span class="modal-title" id="detailTitle">Kelola</span><button class="modal-close" onclick="closeModal('modalDetail')">×</button></div>
|
||
<div style="max-height:70vh;overflow-y:auto;">
|
||
<h4 style="font-size:0.9rem;margin-bottom:8px;">💳 Status Bantuan</h4>
|
||
<div id="bantuanList" style="margin-bottom:10px;"></div>
|
||
<div style="display:grid;grid-template-columns:2fr 1fr 1fr auto;gap:8px;align-items:end;background:#F9FAFB;padding:10px;border-radius:8px;">
|
||
<div><label class="form-label" style="font-size:0.75rem">Jenis</label>
|
||
<select id="bJenis" class="form-control">
|
||
<?php foreach ($jenisList as $j): ?><option value="<?= $j['id'] ?>"><?= htmlspecialchars($j['kode']) ?></option><?php endforeach; ?>
|
||
</select></div>
|
||
<div><label class="form-label" style="font-size:0.75rem">Mulai</label><input id="bMulai" type="date" class="form-control"></div>
|
||
<div><label class="form-label" style="font-size:0.75rem">Berakhir</label><input id="bBerakhir" type="date" class="form-control"></div>
|
||
<button class="btn btn-primary btn-sm" onclick="addBantuan()"><i class="fas fa-plus"></i></button>
|
||
</div>
|
||
|
||
<h4 style="font-size:0.9rem;margin:16px 0 8px;">📷 Foto Kondisi Rumah (maks 5)</h4>
|
||
<div id="fotoGallery" style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;"></div>
|
||
<input type="file" id="fotoInput" accept="image/*" multiple style="font-size:0.85rem;">
|
||
<button class="btn btn-primary btn-sm" onclick="uploadFoto()"><i class="fas fa-upload"></i> Unggah</button>
|
||
</div>
|
||
<div class="modal-footer"><button class="btn btn-ghost" onclick="closeModal('modalDetail')">Tutup</button></div>
|
||
</div></div>
|
||
|
||
<!-- ===== Modal Import ===== -->
|
||
<div class="modal-overlay" id="modalImport"><div class="modal">
|
||
<div class="modal-header"><span class="modal-title">📥 Import CSV</span><button class="modal-close" onclick="closeModal('modalImport')">×</button></div>
|
||
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:10px;">Format kolom (Lampiran A): <code>nik_kk, nama_kk, kode_kelurahan, latitude, longitude, jumlah_jiwa, sumber_air, status_listrik, material_dinding, pekerjaan_kk</code></p>
|
||
<a href="<?= app_url('api/warga_export.php?format=template') ?>" class="btn btn-ghost btn-sm" style="margin-bottom:12px;"><i class="fas fa-download"></i> Unduh Template CSV</a>
|
||
<div class="form-group"><input type="file" id="importFile" accept=".csv" class="form-control"></div>
|
||
<div id="importResult" style="font-size:0.85rem;"></div>
|
||
<div class="modal-footer"><button class="btn btn-ghost" onclick="closeModal('modalImport')">Batal</button><button class="btn btn-primary" onclick="doImport()"><i class="fas fa-file-import"></i> Proses</button></div>
|
||
</div></div>
|
||
|
||
<?php $extraScript = <<<'JS'
|
||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||
<script>
|
||
const API = APP_BASE + '/api/warga_miskin.php';
|
||
const API_BANTUAN = APP_BASE + '/api/bantuan.php';
|
||
const API_FOTO = APP_BASE + '/api/foto.php';
|
||
let miniMap=null, marker=null, currentDetailId=null;
|
||
|
||
function ensureMap(lat=-0.0500, lng=109.3450){
|
||
setTimeout(()=>{
|
||
if(!miniMap){
|
||
miniMap=L.map('miniMap').setView([lat,lng],13);
|
||
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',{subdomains:'abcd'}).addTo(miniMap);
|
||
miniMap.on('click',e=>setMarker(e.latlng.lat,e.latlng.lng));
|
||
} else { miniMap.invalidateSize(); miniMap.setView([lat,lng],13); }
|
||
},120);
|
||
}
|
||
function setMarker(lat,lng){
|
||
if(!marker) marker=L.marker([lat,lng]).addTo(miniMap); else marker.setLatLng([lat,lng]);
|
||
document.getElementById('fLat').value=lat.toFixed(6);
|
||
document.getElementById('fLng').value=lng.toFixed(6);
|
||
}
|
||
function clearForm(){
|
||
['fId','fNama','fNik','fAlamat','fRt','fRw','fJiwa','fTanggungan','fPekerjaan','fPenghasilan','fLat','fLng'].forEach(i=>document.getElementById(i).value='');
|
||
['fWilayah','fAir','fListrik','fDinding','fKepemilikan'].forEach(i=>document.getElementById(i).value='');
|
||
document.getElementById('fVerif').value='belum';
|
||
if(marker&&miniMap){miniMap.removeLayer(marker);marker=null;}
|
||
}
|
||
function openTambah(){ clearForm(); document.getElementById('formTitle').textContent='Tambah Data'; openModal('modalForm'); ensureMap(); }
|
||
function editRow(r){
|
||
clearForm();
|
||
document.getElementById('formTitle').textContent='Edit: '+r.nama_kk;
|
||
document.getElementById('fId').value=r.id;
|
||
document.getElementById('fNama').value=r.nama_kk||'';
|
||
document.getElementById('fNik').value=r.nik_kk||'';
|
||
document.getElementById('fAlamat').value=r.alamat||'';
|
||
document.getElementById('fWilayah').value=r.wilayah_id||'';
|
||
document.getElementById('fRt').value=r.rt||''; document.getElementById('fRw').value=r.rw||'';
|
||
document.getElementById('fJiwa').value=r.jumlah_jiwa||''; document.getElementById('fTanggungan').value=r.jumlah_tanggungan||'';
|
||
document.getElementById('fPekerjaan').value=r.pekerjaan_kk||''; document.getElementById('fPenghasilan').value=r.penghasilan||'';
|
||
document.getElementById('fAir').value=r.sumber_air||''; document.getElementById('fListrik').value=r.status_listrik||'';
|
||
document.getElementById('fDinding').value=r.material_dinding||''; document.getElementById('fKepemilikan').value=r.status_kepemilikan||'';
|
||
document.getElementById('fVerif').value=r.status_verifikasi||'belum';
|
||
openModal('modalForm');
|
||
ensureMap();
|
||
}
|
||
function payload(){
|
||
return {
|
||
nama_kk:document.getElementById('fNama').value.trim(),
|
||
nik_kk:document.getElementById('fNik').value.trim(),
|
||
alamat:document.getElementById('fAlamat').value.trim(),
|
||
wilayah_id:document.getElementById('fWilayah').value||null,
|
||
rt:document.getElementById('fRt').value.trim(), rw:document.getElementById('fRw').value.trim(),
|
||
jumlah_jiwa:document.getElementById('fJiwa').value||null,
|
||
jumlah_tanggungan:parseInt(document.getElementById('fTanggungan').value)||0,
|
||
pekerjaan_kk:document.getElementById('fPekerjaan').value.trim(),
|
||
penghasilan:parseFloat(document.getElementById('fPenghasilan').value)||0,
|
||
sumber_air:document.getElementById('fAir').value,
|
||
status_listrik:document.getElementById('fListrik').value,
|
||
material_dinding:document.getElementById('fDinding').value,
|
||
status_kepemilikan:document.getElementById('fKepemilikan').value,
|
||
status_verifikasi:document.getElementById('fVerif').value
|
||
};
|
||
}
|
||
async function simpan(){
|
||
const id=document.getElementById('fId').value;
|
||
const p=payload();
|
||
if(!p.nama_kk) return showToast('Nama KK wajib!','error');
|
||
const lat=parseFloat(document.getElementById('fLat').value), lng=parseFloat(document.getElementById('fLng').value);
|
||
if(id){
|
||
const res=await fetch(API+'?id='+id,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(p)});
|
||
const d=await res.json();
|
||
if(d.status==='success'){ showToast('Diperbarui!'); setTimeout(()=>location.reload(),800);} else showToast(d.message,'error');
|
||
} else {
|
||
if(isNaN(lat)||isNaN(lng)) return showToast('Pilih lokasi di peta!','error');
|
||
p.geometry={type:'Point',coordinates:[lng,lat]};
|
||
const res=await fetch(API,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(p)});
|
||
const d=await res.json();
|
||
if(d.status==='success'){ showToast('Data disimpan!'); setTimeout(()=>location.reload(),800);} else showToast(d.message,'error');
|
||
}
|
||
}
|
||
async function hapus(id,nama){ if(!confirm(`Hapus data "${nama}"?`))return;
|
||
const res=await fetch(API+'?id='+id,{method:'DELETE'}); const d=await res.json();
|
||
if(d.status==='success'){showToast('Dihapus!');setTimeout(()=>location.reload(),700);}else showToast(d.message,'error'); }
|
||
|
||
// ---- Verifikasi ----
|
||
function applyVerifFilter(){
|
||
const v=document.getElementById('filterVerif').value;
|
||
document.querySelectorAll('#wargaTable tbody tr').forEach(tr=>{
|
||
tr.style.display=(!v||tr.dataset.verif===v)?'':'none';
|
||
});
|
||
}
|
||
function toggleAll(c){ document.querySelectorAll('.rowCheck').forEach(x=>x.checked=c.checked); }
|
||
async function bulkVerify(){
|
||
const ids=[...document.querySelectorAll('.rowCheck:checked')].map(x=>x.value);
|
||
if(!ids.length) return showToast('Pilih minimal satu baris','error');
|
||
if(!confirm(`Tandai ${ids.length} data sebagai Terverifikasi?`))return;
|
||
for(const id of ids){ await fetch(API+'?id='+id,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({status_verifikasi:'terverifikasi'})}); }
|
||
showToast('Verifikasi massal selesai!'); setTimeout(()=>location.reload(),800);
|
||
}
|
||
|
||
// ---- Detail: bantuan & foto ----
|
||
async function openDetail(id,nama){
|
||
currentDetailId=id;
|
||
document.getElementById('detailTitle').textContent='Kelola: '+nama;
|
||
openModal('modalDetail');
|
||
loadBantuan(); loadFoto();
|
||
}
|
||
async function loadBantuan(){
|
||
const res=await fetch(API_BANTUAN+'?warga_id='+currentDetailId); const d=await res.json();
|
||
const box=document.getElementById('bantuanList');
|
||
if(!d.data||!d.data.length){ box.innerHTML='<p style="color:#9CA3AF;font-size:0.85rem">Belum ada bantuan.</p>'; return; }
|
||
box.innerHTML=d.data.map(b=>`<div style="display:flex;justify-content:space-between;align-items:center;padding:8px 10px;border:1px solid #E5E7EB;border-radius:8px;margin-bottom:6px;">
|
||
<div><strong>${b.kode}</strong> <span style="font-size:0.78rem;color:#6B7280">${b.nama}</span>
|
||
<div style="font-size:0.72rem;color:#9CA3AF">${b.tgl_mulai||'-'} s/d ${b.tgl_berakhir||'-'}</div></div>
|
||
<button class="btn btn-danger btn-sm btn-icon" onclick="delBantuan(${b.id})"><i class="fas fa-trash"></i></button></div>`).join('');
|
||
}
|
||
async function addBantuan(){
|
||
const body={warga_id:currentDetailId,jenis_bantuan_id:document.getElementById('bJenis').value,tgl_mulai:document.getElementById('bMulai').value,tgl_berakhir:document.getElementById('bBerakhir').value};
|
||
const res=await fetch(API_BANTUAN,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
|
||
const d=await res.json(); if(d.status==='success'){showToast('Bantuan ditambah');loadBantuan();}else showToast(d.message,'error');
|
||
}
|
||
async function delBantuan(id){ const res=await fetch(API_BANTUAN+'?id='+id,{method:'DELETE'}); const d=await res.json(); if(d.status==='success'){showToast('Dihapus');loadBantuan();} }
|
||
async function loadFoto(){
|
||
const res=await fetch(API_FOTO+'?warga_id='+currentDetailId); const d=await res.json();
|
||
const g=document.getElementById('fotoGallery');
|
||
if(!d.data||!d.data.length){ g.innerHTML='<p style="color:#9CA3AF;font-size:0.85rem">Belum ada foto.</p>'; return; }
|
||
g.innerHTML=d.data.map(f=>`<div style="position:relative;"><img src="${f.url}" style="width:90px;height:90px;object-fit:cover;border-radius:8px;border:1px solid #E5E7EB;">
|
||
<button onclick="delFoto(${f.id})" style="position:absolute;top:-6px;right:-6px;background:#EF4444;color:#fff;border:none;border-radius:50%;width:22px;height:22px;cursor:pointer;">×</button></div>`).join('');
|
||
}
|
||
async function uploadFoto(){
|
||
const inp=document.getElementById('fotoInput'); if(!inp.files.length) return showToast('Pilih foto dulu','error');
|
||
const fd=new FormData(); fd.append('warga_id',currentDetailId);
|
||
for(const f of inp.files) fd.append('foto[]',f);
|
||
const res=await fetch(API_FOTO,{method:'POST',body:fd}); const d=await res.json();
|
||
if(d.status==='success'){showToast(d.message);inp.value='';loadFoto();}else showToast(d.message,'error');
|
||
}
|
||
async function delFoto(id){ const res=await fetch(API_FOTO+'?id='+id,{method:'DELETE'}); const d=await res.json(); if(d.status==='success'){showToast('Foto dihapus');loadFoto();} }
|
||
|
||
// ---- Import ----
|
||
async function doImport(){
|
||
const inp=document.getElementById('importFile'); if(!inp.files.length) return showToast('Pilih file CSV','error');
|
||
const fd=new FormData(); fd.append('file',inp.files[0]);
|
||
document.getElementById('importResult').innerHTML='Memproses...';
|
||
const res=await fetch(APP_BASE+'/api/warga_import.php',{method:'POST',body:fd}); const d=await res.json();
|
||
if(d.status==='success'){
|
||
document.getElementById('importResult').innerHTML=`<div style="color:#059669">✔ ${d.data.berhasil} baris diimpor.</div>`+
|
||
(d.data.gagal?`<div style="color:#DC2626">${d.data.gagal} gagal:<br>${d.data.errors.join('<br>')}</div>`:'');
|
||
setTimeout(()=>location.reload(),1800);
|
||
} else { document.getElementById('importResult').innerHTML=`<div style="color:#DC2626">${d.message}</div>`; }
|
||
}
|
||
</script>
|
||
JS; ?>
|
||
<?php require_once __DIR__ . '/partials/footer.php'; ?>
|