First commit / commit pertama
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../config/db.php';
|
||||
require_once __DIR__ . '/../config/auth_check.php';
|
||||
requireAnyRole(['enumerator']);
|
||||
$pdo = Database::getConnection();
|
||||
$uid = currentUser()['id'];
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT w.*, wl.nama AS wilayah_nama,
|
||||
(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
|
||||
WHERE w.created_by=? ORDER BY w.id DESC
|
||||
");
|
||||
$stmt->execute([$uid]);
|
||||
$rows = $stmt->fetchAll();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Data Saya — Enumerator</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box;font-family:'Inter',sans-serif;}
|
||||
body{background:#F3F4F6;color:#111827;padding-bottom:30px;}
|
||||
.topbar{position:sticky;top:0;z-index:500;background:linear-gradient(135deg,#2563EB,#1D4ED8);color:#fff;padding:14px 16px;display:flex;align-items:center;gap:12px;}
|
||||
.topbar a{color:#fff;text-decoration:none;font-size:1.2rem;}
|
||||
.topbar h1{font-size:1rem;font-weight:700;}
|
||||
.container{max-width:560px;margin:0 auto;padding:16px;}
|
||||
.card{background:#fff;border:1px solid #E5E7EB;border-radius:12px;padding:14px;margin-bottom:10px;}
|
||||
.card .nm{font-weight:700;font-size:0.95rem;}
|
||||
.card .meta{font-size:0.78rem;color:#6B7280;margin-top:2px;}
|
||||
.badge{font-size:0.7rem;padding:3px 9px;border-radius:20px;}
|
||||
.b-ok{background:#D1FAE5;color:#065F46;} .b-no{background:#F3F4F6;color:#6B7280;} .b-rev{background:#FEF3C7;color:#92400E;}
|
||||
.acts{display:flex;gap:8px;margin-top:10px;}
|
||||
.acts button{flex:1;padding:9px;border:1px solid #D1D5DB;background:#fff;border-radius:8px;font-size:0.8rem;cursor:pointer;}
|
||||
.acts button.pri{background:#2563EB;color:#fff;border-color:#2563EB;}
|
||||
.overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:1000;align-items:flex-end;}
|
||||
.overlay.show{display:flex;}
|
||||
.sheet{background:#fff;width:100%;max-width:560px;margin:0 auto;border-radius:16px 16px 0 0;padding:18px;max-height:88vh;overflow-y:auto;}
|
||||
.sheet h3{font-size:1rem;margin-bottom:12px;}
|
||||
label{display:block;font-size:0.78rem;font-weight:600;margin-bottom:4px;}
|
||||
input,select{width:100%;padding:11px;border:1px solid #D1D5DB;border-radius:9px;font-size:0.92rem;margin-bottom:10px;}
|
||||
.row2{display:grid;grid-template-columns:1fr 1fr;gap:8px;}
|
||||
.btn-save{width:100%;background:#2563EB;color:#fff;border:none;padding:13px;border-radius:10px;font-weight:700;cursor:pointer;}
|
||||
.photo-prev{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0;}
|
||||
.photo-prev .ph{position:relative;}
|
||||
.photo-prev img{width:70px;height:70px;object-fit:cover;border-radius:8px;border:1px solid #E5E7EB;}
|
||||
.photo-prev .del{position:absolute;top:-6px;right:-6px;background:#EF4444;color:#fff;border:none;border-radius:50%;width:20px;height:20px;cursor:pointer;}
|
||||
.toast{position:fixed;top:70px;left:50%;transform:translateX(-50%);background:#111827;color:#fff;padding:10px 18px;border-radius:10px;font-size:0.85rem;z-index:2000;opacity:0;transition:.3s;}
|
||||
.toast.show{opacity:1;} .toast.err{background:#DC2626;}
|
||||
.empty{text-align:center;color:#9CA3AF;padding:40px 0;font-size:0.9rem;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<a href="<?= app_url('enumerator/index.php') ?>"><i class="fas fa-arrow-left"></i></a>
|
||||
<h1>Data yang Saya Input (<?= count($rows) ?>)</h1>
|
||||
</div>
|
||||
<div class="container">
|
||||
<?php if (!$rows): ?><div class="empty">Belum ada data. <br>Tambahkan via "Input Data Warga Baru".</div><?php endif; ?>
|
||||
<?php foreach ($rows as $r):
|
||||
$vb = $r['status_verifikasi']==='terverifikasi' ? 'b-ok' : ($r['status_verifikasi']==='perlu_tinjauan' ? 'b-rev' : 'b-no'); ?>
|
||||
<div class="card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:start;">
|
||||
<div>
|
||||
<div class="nm"><?= htmlspecialchars($r['nama_kk']) ?></div>
|
||||
<div class="meta"><?= htmlspecialchars($r['wilayah_nama'] ?: 'Tanpa kelurahan') ?> · Rp <?= number_format($r['penghasilan'],0,',','.') ?> · 📷 <?= $r['jumlah_foto'] ?></div>
|
||||
</div>
|
||||
<span class="badge <?= $vb ?>"><?= htmlspecialchars($r['status_verifikasi']) ?></span>
|
||||
</div>
|
||||
<div class="acts">
|
||||
<button onclick='openEdit(<?= json_encode($r, JSON_HEX_APOS|JSON_HEX_QUOT) ?>)'><i class="fas fa-edit"></i> Edit</button>
|
||||
<button class="pri" onclick="openFoto(<?= $r['id'] ?>)"><i class="fas fa-camera"></i> Foto</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Sheet Edit -->
|
||||
<div class="overlay" id="editOverlay"><div class="sheet">
|
||||
<h3>✏️ Edit Data</h3>
|
||||
<input type="hidden" id="eId">
|
||||
<label>Nama KK *</label><input id="eNama">
|
||||
<label>NIK</label><input id="eNik" maxlength="16">
|
||||
<label>Alamat</label><input id="eAlamat">
|
||||
<div class="row2"><div><label>Jiwa</label><input id="eJiwa" type="number"></div><div><label>Tanggungan</label><input id="eTang" type="number"></div></div>
|
||||
<label>Pekerjaan</label><input id="ePek">
|
||||
<label>Penghasilan (Rp)</label><input id="ePeng" type="number">
|
||||
<label>Sumber Air</label><select id="eAir"><option value="">-</option><option>SUMUR_BOR</option><option>PDAM</option><option>SUNGAI</option><option>HUJAN</option><option>TIDAK_ADA</option></select>
|
||||
<label>Material Dinding</label><select id="eDind"><option value="">-</option><option>PERMANEN</option><option>SEMI_PERMANEN</option><option>TIDAK_PERMANEN</option></select>
|
||||
<button class="btn-save" onclick="saveEdit()">Simpan Perubahan</button>
|
||||
<button class="btn-save" style="background:#fff;color:#374151;border:1px solid #D1D5DB;margin-top:8px;" onclick="closeSheet('editOverlay')">Batal</button>
|
||||
</div></div>
|
||||
|
||||
<!-- Sheet Foto -->
|
||||
<div class="overlay" id="fotoOverlay"><div class="sheet">
|
||||
<h3>📷 Foto Kondisi Rumah</h3>
|
||||
<div class="photo-prev" id="fotoList"></div>
|
||||
<input type="file" id="fotoFile" accept="image/*" capture="environment" multiple style="margin-bottom:10px;">
|
||||
<button class="btn-save" onclick="uploadFoto()">Unggah Foto</button>
|
||||
<button class="btn-save" style="background:#fff;color:#374151;border:1px solid #D1D5DB;margin-top:8px;" onclick="closeSheet('fotoOverlay')">Tutup</button>
|
||||
</div></div>
|
||||
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<script>
|
||||
const APP_BASE='<?= app_url() ?>';
|
||||
let fotoWid=null;
|
||||
function toast(m,t){const e=document.getElementById('toast');e.textContent=m;e.className='toast show'+(t==='err'?' err':'');setTimeout(()=>e.className='toast',2800);}
|
||||
function closeSheet(id){document.getElementById(id).classList.remove('show');}
|
||||
function openEdit(r){
|
||||
document.getElementById('eId').value=r.id;
|
||||
document.getElementById('eNama').value=r.nama_kk||'';
|
||||
document.getElementById('eNik').value=r.nik_kk||'';
|
||||
document.getElementById('eAlamat').value=r.alamat||'';
|
||||
document.getElementById('eJiwa').value=r.jumlah_jiwa||'';
|
||||
document.getElementById('eTang').value=r.jumlah_tanggungan||'';
|
||||
document.getElementById('ePek').value=r.pekerjaan_kk||'';
|
||||
document.getElementById('ePeng').value=r.penghasilan||'';
|
||||
document.getElementById('eAir').value=r.sumber_air||'';
|
||||
document.getElementById('eDind').value=r.material_dinding||'';
|
||||
document.getElementById('editOverlay').classList.add('show');
|
||||
}
|
||||
async function saveEdit(){
|
||||
const id=document.getElementById('eId').value;
|
||||
const body={
|
||||
nama_kk:document.getElementById('eNama').value.trim(),
|
||||
nik_kk:document.getElementById('eNik').value.trim(),
|
||||
alamat:document.getElementById('eAlamat').value.trim(),
|
||||
jumlah_jiwa:document.getElementById('eJiwa').value||null,
|
||||
jumlah_tanggungan:parseInt(document.getElementById('eTang').value)||0,
|
||||
pekerjaan_kk:document.getElementById('ePek').value.trim(),
|
||||
penghasilan:parseFloat(document.getElementById('ePeng').value)||0,
|
||||
sumber_air:document.getElementById('eAir').value,
|
||||
material_dinding:document.getElementById('eDind').value
|
||||
};
|
||||
if(!body.nama_kk) return toast('Nama wajib','err');
|
||||
const res=await fetch(APP_BASE+'/api/warga_miskin.php?id='+id,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
|
||||
const d=await res.json();
|
||||
if(d.status==='success'){toast('Tersimpan ✓');setTimeout(()=>location.reload(),900);}else toast(d.message||'Gagal','err');
|
||||
}
|
||||
async function openFoto(wid){
|
||||
fotoWid=wid; document.getElementById('fotoOverlay').classList.add('show');
|
||||
const res=await fetch(APP_BASE+'/api/foto.php?warga_id='+wid); const d=await res.json();
|
||||
const g=document.getElementById('fotoList');
|
||||
g.innerHTML=(d.data&&d.data.length)?d.data.map(f=>`<div class="ph"><img src="${f.url}"><button class="del" onclick="delFoto(${f.id})">×</button></div>`).join(''):'<p style="color:#9CA3AF;font-size:0.85rem">Belum ada foto.</p>';
|
||||
}
|
||||
async function uploadFoto(){
|
||||
const inp=document.getElementById('fotoFile'); if(!inp.files.length) return toast('Pilih foto','err');
|
||||
const fd=new FormData(); fd.append('warga_id',fotoWid);
|
||||
for(const f of inp.files) fd.append('foto[]',f);
|
||||
const res=await fetch(APP_BASE+'/api/foto.php',{method:'POST',body:fd}); const d=await res.json();
|
||||
if(d.status==='success'){toast(d.message);inp.value='';openFoto(fotoWid);}else toast(d.message,'err');
|
||||
}
|
||||
async function delFoto(id){const res=await fetch(APP_BASE+'/api/foto.php?id='+id,{method:'DELETE'});const d=await res.json();if(d.status==='success'){toast('Foto dihapus');openFoto(fotoWid);}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../config/db.php';
|
||||
require_once __DIR__ . '/../config/auth_check.php';
|
||||
requireAnyRole(['enumerator']);
|
||||
|
||||
$pdo = Database::getConnection();
|
||||
$user = currentUser();
|
||||
$uid = $user['id'];
|
||||
$wilayah = $user['wilayah_tugas'] ?: 'Belum ditentukan';
|
||||
|
||||
$mine = (int)$pdo->query("SELECT COUNT(*) FROM warga_miskin WHERE created_by=" . (int)$uid)->fetchColumn();
|
||||
$verif = (int)$pdo->query("SELECT COUNT(*) FROM warga_miskin WHERE created_by=" . (int)$uid . " AND status_verifikasi='terverifikasi'")->fetchColumn();
|
||||
$belum = $mine - $verif;
|
||||
|
||||
$recent = $pdo->prepare("SELECT id, nama_kk, status_verifikasi, created_at FROM warga_miskin WHERE created_by=? ORDER BY id DESC LIMIT 5");
|
||||
$recent->execute([$uid]);
|
||||
$recentRows = $recent->fetchAll();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Enumerator — WebGIS Kemiskinan</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<style>
|
||||
* { margin:0; padding:0; box-sizing:border-box; font-family:'Inter',sans-serif; }
|
||||
body { background:#F3F4F6; color:#111827; padding-bottom:30px; }
|
||||
.topbar { background:linear-gradient(135deg,#2563EB,#1D4ED8); color:#fff; padding:20px 16px; }
|
||||
.topbar h1 { font-size:1.1rem; font-weight:700; }
|
||||
.topbar p { font-size:0.82rem; opacity:0.9; margin-top:2px; }
|
||||
.container { padding:16px; max-width:560px; margin:0 auto; }
|
||||
.stat-row { display:grid; grid-template-columns:1fr 1fr 1fr; gap:10px; margin-bottom:14px; }
|
||||
.stat { background:#fff; border:1px solid #E5E7EB; border-radius:14px; padding:14px 8px; text-align:center; }
|
||||
.stat .num { font-size:1.5rem; font-weight:800; color:#2563EB; }
|
||||
.stat .lbl { font-size:0.7rem; color:#6B7280; margin-top:4px; }
|
||||
.btn-big { display:flex; align-items:center; gap:14px; width:100%; background:#2563EB; color:#fff;
|
||||
border:none; border-radius:14px; padding:18px; font-size:1rem; font-weight:600; cursor:pointer;
|
||||
text-decoration:none; margin-bottom:12px; box-shadow:0 4px 12px rgba(37,99,235,0.25); }
|
||||
.btn-big.green { background:#059669; box-shadow:0 4px 12px rgba(5,150,105,0.25); }
|
||||
.btn-big.gray { background:#fff; color:#111827; border:1px solid #E5E7EB; box-shadow:none; }
|
||||
.btn-big i { font-size:1.4rem; }
|
||||
.card { background:#fff; border:1px solid #E5E7EB; border-radius:14px; padding:16px; margin-bottom:14px; }
|
||||
.card h3 { font-size:0.9rem; margin-bottom:10px; }
|
||||
.item { display:flex; justify-content:space-between; align-items:center; padding:9px 0; border-bottom:1px solid #F3F4F6; font-size:0.88rem; }
|
||||
.item:last-child { border-bottom:none; }
|
||||
.badge { font-size:0.7rem; padding:3px 9px; border-radius:20px; }
|
||||
.b-ok { background:#D1FAE5; color:#065F46; } .b-no { background:#F3F4F6; color:#6B7280; }
|
||||
.logout { display:block; text-align:center; color:#DC2626; font-size:0.85rem; margin-top:8px; text-decoration:none; font-weight:600; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<h1><i class="fas fa-clipboard-check"></i> Halo, <?= htmlspecialchars($user['nama_lengkap'] ?: $user['username']) ?></h1>
|
||||
<p>Petugas Lapangan · Wilayah: <?= htmlspecialchars($wilayah) ?></p>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="stat-row">
|
||||
<div class="stat"><div class="num"><?= $mine ?></div><div class="lbl">Input Saya</div></div>
|
||||
<div class="stat"><div class="num"><?= $verif ?></div><div class="lbl">Terverifikasi</div></div>
|
||||
<div class="stat"><div class="num"><?= $belum ?></div><div class="lbl">Menunggu</div></div>
|
||||
</div>
|
||||
|
||||
<a href="<?= app_url('enumerator/input.php') ?>" class="btn-big">
|
||||
<i class="fas fa-plus-circle"></i> <span>Input Data Warga Baru</span>
|
||||
</a>
|
||||
<a href="<?= app_url('enumerator/data.php') ?>" class="btn-big gray">
|
||||
<i class="fas fa-list"></i> <span>Data yang Saya Input</span>
|
||||
</a>
|
||||
<a href="<?= app_url('peta_publik.php') ?>" class="btn-big green">
|
||||
<i class="fas fa-map-marked-alt"></i> <span>Lihat Peta Sebaran</span>
|
||||
</a>
|
||||
|
||||
<div class="card">
|
||||
<h3>🕘 Input Terbaru</h3>
|
||||
<?php if ($recentRows): foreach ($recentRows as $r): ?>
|
||||
<div class="item">
|
||||
<div><?= htmlspecialchars($r['nama_kk']) ?><div style="font-size:0.72rem;color:#9CA3AF;"><?= date('d M Y H:i', strtotime($r['created_at'])) ?></div></div>
|
||||
<span class="badge <?= $r['status_verifikasi']==='terverifikasi'?'b-ok':'b-no' ?>"><?= $r['status_verifikasi']==='terverifikasi'?'Terverifikasi':'Menunggu' ?></span>
|
||||
</div>
|
||||
<?php endforeach; else: ?>
|
||||
<p style="color:#9CA3AF;font-size:0.85rem;">Belum ada data. Mulai dengan "Input Data Warga Baru".</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<a href="<?= app_url('logout.php') ?>" class="logout"><i class="fas fa-sign-out-alt"></i> Keluar</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../config/db.php';
|
||||
require_once __DIR__ . '/../config/auth_check.php';
|
||||
requireAnyRole(['enumerator']);
|
||||
$user = currentUser();
|
||||
$wilayah = $user['wilayah_tugas'] ?: 'Belum ditentukan';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Input Data Warga — Enumerator</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box;font-family:'Inter',sans-serif;}
|
||||
body{background:#F3F4F6;color:#111827;padding-bottom:90px;}
|
||||
.topbar{position:sticky;top:0;z-index:500;background:linear-gradient(135deg,#2563EB,#1D4ED8);color:#fff;padding:14px 16px;display:flex;align-items:center;gap:12px;box-shadow:0 2px 8px rgba(0,0,0,.15);}
|
||||
.topbar a{color:#fff;text-decoration:none;font-size:1.2rem;}
|
||||
.topbar h1{font-size:1rem;font-weight:700;}
|
||||
.topbar .sub{font-size:0.72rem;opacity:.9;}
|
||||
.container{max-width:560px;margin:0 auto;padding:16px;}
|
||||
.section{background:#fff;border:1px solid #E5E7EB;border-radius:14px;padding:16px;margin-bottom:14px;}
|
||||
.section h3{font-size:0.85rem;color:#2563EB;margin-bottom:12px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
label{display:block;font-size:0.8rem;font-weight:600;margin-bottom:5px;color:#374151;}
|
||||
input,select{width:100%;padding:12px;border:1px solid #D1D5DB;border-radius:10px;font-size:0.95rem;margin-bottom:12px;outline:none;}
|
||||
input:focus,select:focus{border-color:#2563EB;}
|
||||
.row2{display:grid;grid-template-columns:1fr 1fr;gap:10px;}
|
||||
.gps-btn{width:100%;background:#059669;color:#fff;border:none;padding:14px;border-radius:10px;font-size:0.95rem;font-weight:600;cursor:pointer;margin-bottom:10px;display:flex;align-items:center;justify-content:center;gap:8px;}
|
||||
#map{height:240px;border-radius:10px;border:1px solid #E5E7EB;margin-bottom:10px;}
|
||||
.coord{font-size:0.78rem;color:#6B7280;text-align:center;margin-bottom:8px;}
|
||||
.photo-prev{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px;}
|
||||
.photo-prev img{width:72px;height:72px;object-fit:cover;border-radius:8px;border:1px solid #E5E7EB;}
|
||||
.savebar{position:fixed;bottom:0;left:0;right:0;background:#fff;border-top:1px solid #E5E7EB;padding:12px 16px;display:flex;gap:10px;max-width:560px;margin:0 auto;}
|
||||
.btn-save{flex:1;background:#2563EB;color:#fff;border:none;padding:14px;border-radius:10px;font-size:1rem;font-weight:700;cursor:pointer;}
|
||||
.btn-save:disabled{opacity:.6;}
|
||||
.toast{position:fixed;top:70px;left:50%;transform:translateX(-50%);background:#111827;color:#fff;padding:12px 20px;border-radius:10px;font-size:0.88rem;z-index:1000;opacity:0;transition:.3s;}
|
||||
.toast.show{opacity:1;}
|
||||
.toast.err{background:#DC2626;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<a href="<?= app_url('enumerator/index.php') ?>"><i class="fas fa-arrow-left"></i></a>
|
||||
<div><h1>Input Data Warga</h1><div class="sub">Wilayah: <?= htmlspecialchars($wilayah) ?></div></div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="section">
|
||||
<h3>📍 Lokasi (Geotagging)</h3>
|
||||
<button type="button" class="gps-btn" onclick="getGPS()"><i class="fas fa-location-crosshairs"></i> <span id="gpsLabel">Gunakan GPS Otomatis</span></button>
|
||||
<div id="map"></div>
|
||||
<div class="coord" id="coordHint">Gunakan GPS, <b>ketuk peta</b>, <b>geser pin</b>, atau ketik koordinat manual di bawah.</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:8px;">
|
||||
<div><label>Latitude</label><input id="inLat" type="number" step="any" inputmode="decimal" placeholder="-0.0500" onchange="applyManual()"></div>
|
||||
<div><label>Longitude</label><input id="inLng" type="number" step="any" inputmode="decimal" placeholder="109.3450" onchange="applyManual()"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>👤 Identitas</h3>
|
||||
<label>Nama Kepala Keluarga *</label><input id="nama" placeholder="Nama lengkap KK">
|
||||
<label>NIK KK</label><input id="nik" maxlength="16" inputmode="numeric" placeholder="16 digit">
|
||||
<label>Alamat</label><input id="alamat" placeholder="Alamat tempat tinggal">
|
||||
<div class="row2"><div><label>RT</label><input id="rt"></div><div><label>RW</label><input id="rw"></div></div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>👨👩👧 Demografi & Ekonomi</h3>
|
||||
<div class="row2">
|
||||
<div><label>Jumlah Jiwa</label><input id="jiwa" type="number" inputmode="numeric"></div>
|
||||
<div><label>Tanggungan</label><input id="tanggungan" type="number" inputmode="numeric"></div>
|
||||
</div>
|
||||
<label>Pekerjaan KK</label><input id="pekerjaan" placeholder="mis. Buruh harian">
|
||||
<label>Penghasilan / bulan (Rp)</label><input id="penghasilan" type="number" inputmode="numeric">
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>🏠 Kondisi Rumah</h3>
|
||||
<label>Sumber Air</label>
|
||||
<select id="air"><option value="">-</option><option>SUMUR_BOR</option><option>PDAM</option><option>SUNGAI</option><option>HUJAN</option><option>TIDAK_ADA</option></select>
|
||||
<label>Listrik</label>
|
||||
<select id="listrik"><option value="">-</option><option>PLN</option><option>NON_PLN</option><option>TIDAK_ADA</option></select>
|
||||
<label>Material Dinding</label>
|
||||
<select id="dinding"><option value="">-</option><option>PERMANEN</option><option>SEMI_PERMANEN</option><option>TIDAK_PERMANEN</option></select>
|
||||
<label>Status Kepemilikan</label>
|
||||
<select id="kepemilikan"><option value="">-</option><option>MILIK</option><option>SEWA</option><option>NUMPANG</option><option>LAINNYA</option></select>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>📷 Foto Kondisi Rumah</h3>
|
||||
<input type="file" id="foto" accept="image/*" capture="environment" multiple>
|
||||
<div class="photo-prev" id="photoPrev"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="savebar">
|
||||
<button class="btn-save" id="btnSave" onclick="simpan()"><i class="fas fa-save"></i> Simpan Data</button>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script>
|
||||
const APP_BASE = '<?= app_url() ?>';
|
||||
let map, marker, lat=null, lng=null;
|
||||
|
||||
function initMap(){
|
||||
map = L.map('map').setView([-0.0500,109.3450],13);
|
||||
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',{subdomains:'abcd',maxZoom:19}).addTo(map);
|
||||
map.on('click',e=>setPoint(e.latlng.lat,e.latlng.lng,false));
|
||||
}
|
||||
function ensureMarker(la,ln){
|
||||
if(!marker){
|
||||
marker=L.marker([la,ln],{draggable:true}).addTo(map);
|
||||
marker.on('dragend',ev=>{const p=ev.target.getLatLng();lat=p.lat;lng=p.lng;syncInputs();});
|
||||
} else { marker.setLatLng([la,ln]); }
|
||||
}
|
||||
function setPoint(la,ln,zoom){
|
||||
lat=la; lng=ln;
|
||||
ensureMarker(la,ln);
|
||||
if(zoom!==false) map.setView([la,ln],16);
|
||||
syncInputs();
|
||||
}
|
||||
function syncInputs(){
|
||||
document.getElementById('inLat').value = lat.toFixed(6);
|
||||
document.getElementById('inLng').value = lng.toFixed(6);
|
||||
document.getElementById('coordHint').innerHTML = `📌 Titik terpasang: ${lat.toFixed(6)}, ${lng.toFixed(6)} — geser pin / ketik untuk koreksi.`;
|
||||
}
|
||||
function applyManual(){
|
||||
const la=parseFloat(document.getElementById('inLat').value), ln=parseFloat(document.getElementById('inLng').value);
|
||||
if(isNaN(la)||isNaN(ln)){ showToast('Koordinat tidak valid','err'); return; }
|
||||
setPoint(la,ln);
|
||||
}
|
||||
function getGPS(){
|
||||
const lbl=document.getElementById('gpsLabel');
|
||||
if(!navigator.geolocation){ toast('GPS tidak didukung perangkat','err'); return; }
|
||||
lbl.textContent='Mencari lokasi...';
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
pos=>{ setPoint(pos.coords.latitude,pos.coords.longitude); lbl.textContent='GPS Terdeteksi ✓'; toast('Lokasi GPS terisi'); },
|
||||
err=>{ lbl.textContent='Gunakan GPS Otomatis'; toast('Gagal ambil GPS: '+err.message+'. Ketuk peta manual.','err'); },
|
||||
{ enableHighAccuracy:true, timeout:10000 }
|
||||
);
|
||||
}
|
||||
// preview foto
|
||||
document.getElementById('foto').addEventListener('change',function(){
|
||||
const p=document.getElementById('photoPrev'); p.innerHTML='';
|
||||
[...this.files].slice(0,5).forEach(f=>{ const img=document.createElement('img'); img.src=URL.createObjectURL(f); p.appendChild(img); });
|
||||
});
|
||||
function toast(msg,type){ const t=document.getElementById('toast'); t.textContent=msg; t.className='toast show'+(type==='err'?' err':''); setTimeout(()=>t.className='toast',3000); }
|
||||
|
||||
async function simpan(){
|
||||
const nama=document.getElementById('nama').value.trim();
|
||||
if(!nama) return toast('Nama KK wajib diisi','err');
|
||||
if(lat===null||lng===null) return toast('Tentukan lokasi (GPS / ketuk peta)','err');
|
||||
const btn=document.getElementById('btnSave'); btn.disabled=true; btn.innerHTML='Menyimpan...';
|
||||
const body={
|
||||
nama_kk:nama, nik_kk:document.getElementById('nik').value.trim(),
|
||||
alamat:document.getElementById('alamat').value.trim(),
|
||||
rt:document.getElementById('rt').value.trim(), rw:document.getElementById('rw').value.trim(),
|
||||
jumlah_jiwa:document.getElementById('jiwa').value||null,
|
||||
jumlah_tanggungan:parseInt(document.getElementById('tanggungan').value)||0,
|
||||
pekerjaan_kk:document.getElementById('pekerjaan').value.trim(),
|
||||
penghasilan:parseFloat(document.getElementById('penghasilan').value)||0,
|
||||
sumber_air:document.getElementById('air').value, status_listrik:document.getElementById('listrik').value,
|
||||
material_dinding:document.getElementById('dinding').value, status_kepemilikan:document.getElementById('kepemilikan').value,
|
||||
geometry:{type:'Point',coordinates:[lng,lat]}
|
||||
};
|
||||
try{
|
||||
const res=await fetch(APP_BASE+'/api/warga_miskin.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
|
||||
const d=await res.json();
|
||||
if(d.status!=='success'){ toast(d.message||'Gagal','err'); btn.disabled=false; btn.innerHTML='<i class="fas fa-save"></i> Simpan Data'; return; }
|
||||
const wid=d.data.id;
|
||||
const files=document.getElementById('foto').files;
|
||||
if(files.length){
|
||||
const fd=new FormData(); fd.append('warga_id',wid);
|
||||
for(const f of files) fd.append('foto[]',f);
|
||||
btn.innerHTML='Mengunggah foto...';
|
||||
await fetch(APP_BASE+'/api/foto.php',{method:'POST',body:fd});
|
||||
}
|
||||
toast('Data tersimpan ✓');
|
||||
setTimeout(()=>{ window.location.href=APP_BASE+'/enumerator/index.php'; },1200);
|
||||
}catch(e){ toast('Error: '+e.message,'err'); btn.disabled=false; btn.innerHTML='<i class="fas fa-save"></i> Simpan Data'; }
|
||||
}
|
||||
initMap();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user