Initial commit

This commit is contained in:
cw
2026-06-11 12:30:23 +07:00
commit 2676f3a61c
70 changed files with 9867 additions and 0 deletions
+141
View File
@@ -0,0 +1,141 @@
<?php ob_start(); ?>
<script>
document.addEventListener('DOMContentLoaded', async () => {
const map = GISMap.init('verif-map', <?= MAP_DEFAULT_LAT ?>, <?= MAP_DEFAULT_LNG ?>, 14);
const laporan = <?= json_encode(array_map(fn($l) => [
'lat' => (float)$l['lat'], 'lng' => (float)$l['lng'],
'nama_warga' => $l['nama_warga'], 'kode_laporan' => $l['kode_laporan'],
'status' => $l['status']
], array_filter($laporanTerbaru, fn($l) => $l['lat'] && $l['lng']))) ?>;
laporan.forEach(l => {
const color = l.status === 'terverifikasi' ? '#16a34a' : l.status === 'ditolak' ? '#dc2626' : '#0284c7';
L.circleMarker([l.lat, l.lng], {radius:9,fillColor:color,color:'#fff',weight:2,fillOpacity:0.9})
.bindPopup(`<strong>${l.nama_warga}</strong><br><code>${l.kode_laporan}</code>`)
.addTo(map);
GISMap._allMarkerBounds.push([l.lat, l.lng]);
});
GISMap.autoFitBounds(laporan.length === 0);
const statsData = await App.fetchJSON('<?= APP_URL ?>/verifikator/api/stats');
if (statsData?.data) {
const d = statsData.data;
Charts.donut('chart-status',
['Menunggu', 'Diproses', 'Terverifikasi', 'Ditolak'],
[d.menunggu, d.diproses, d.terverifikasi, d.ditolak],
['#d97706','#0284c7','#16a34a','#dc2626']
);
}
});
</script>
<?php $extraJs = ob_get_clean(); ?>
<!-- Stats -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-card-icon orange">📋</div>
<div class="stat-card-body">
<div class="stat-card-value"><?= number_format($stats['laporan_menunggu'] ?? 0) ?></div>
<div class="stat-card-label">Laporan Menunggu</div>
<div class="stat-card-sub"><a href="<?= APP_URL ?>/verifikator/laporan?status=menunggu" style="color:var(--clr-primary-l)">Lihat semua →</a></div>
</div>
</div>
<div class="stat-card">
<div class="stat-card-icon blue">🔄</div>
<div class="stat-card-body">
<div class="stat-card-value"><?= number_format($stats['laporan_diproses'] ?? 0) ?></div>
<div class="stat-card-label">Sedang Diproses</div>
</div>
</div>
<div class="stat-card">
<div class="stat-card-icon green">✅</div>
<div class="stat-card-body">
<div class="stat-card-value"><?= number_format($stats['total_warga'] ?? 0) ?></div>
<div class="stat-card-label">Warga Terverifikasi</div>
</div>
</div>
<div class="stat-card">
<div class="stat-card-icon red">🔥</div>
<div class="stat-card-body">
<div class="stat-card-value"><?= number_format($stats['prioritas_tinggi'] ?? 0) ?></div>
<div class="stat-card-label">Prioritas Tinggi</div>
</div>
</div>
<div class="stat-card">
<div class="stat-card-icon purple">⚠️</div>
<div class="stat-card-body">
<div class="stat-card-value"><?= number_format($stats['blind_spot'] ?? 0) ?></div>
<div class="stat-card-label">Blind Spot</div>
</div>
</div>
</div>
<!-- Grid: Peta + Chart -->
<div class="dashboard-grid" style="margin-bottom:20px">
<!-- PETA -->
<div class="card">
<div class="card-header"><span class="card-icon">🗺</span><h3>Peta Laporan Kelurahan Saya</h3></div>
<div class="card-body no-pad">
<div id="verif-map" style="height:380px"></div>
</div>
</div>
<!-- SIDEBAR: Donut + Laporan List -->
<div style="display:flex;flex-direction:column;gap:16px">
<div class="card">
<div class="card-header"><span class="card-icon">📊</span><h3>Status Laporan</h3></div>
<div class="card-body"><div class="chart-wrap" style="height:180px"><canvas id="chart-status"></canvas></div></div>
</div>
<div class="card" style="flex:1">
<div class="card-header">
<span class="card-icon">📋</span><h3>Laporan Terbaru</h3>
<div class="card-actions"><a href="<?= APP_URL ?>/verifikator/laporan" class="btn btn-ghost btn-sm">Semua</a></div>
</div>
<div class="card-body no-pad" style="max-height:300px;overflow-y:auto">
<?php foreach ($laporanTerbaru as $l): ?>
<a href="<?= APP_URL ?>/verifikator/laporan/<?= $l['id'] ?>" style="display:block;text-decoration:none;color:inherit">
<div class="sidebar-panel-item">
<div style="display:flex;justify-content:space-between;align-items:center">
<h4><?= e($l['nama_warga']) ?></h4>
<?php
$badge = match($l['status']) {
'menunggu' => 'badge-yellow', 'diproses' => 'badge-blue',
'terverifikasi' => 'badge-green', 'ditolak' => 'badge-red', default => 'badge-gray'
};
$label = match($l['status']) {
'menunggu'=>'Menunggu','diproses'=>'Diproses',
'terverifikasi'=>'Terverifikasi','ditolak'=>'Ditolak', default=>$l['status']
};
?>
<span class="badge <?= $badge ?>"><?= $label ?></span>
</div>
<p><?= e($l['kode_laporan']) ?> · <?= formatTanggal($l['created_at']) ?></p>
</div>
</a>
<?php endforeach; ?>
<?php if (empty($laporanTerbaru)): ?>
<div class="empty-state" style="padding:24px">
<div class="empty-state-icon">📭</div>
<p>Belum ada laporan masuk</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="card">
<div class="card-header"><span class="card-icon">⚡</span><h3>Aksi Cepat</h3></div>
<div class="card-body">
<div style="display:flex;gap:12px;flex-wrap:wrap">
<a href="<?= APP_URL ?>/verifikator/laporan?status=menunggu" class="btn btn-accent">
📋 Verifikasi Laporan (<?= $stats['laporan_menunggu'] ?? 0 ?>)
</a>
<a href="<?= APP_URL ?>/verifikator/warga/tambah" class="btn btn-primary"> Tambah Data Warga</a>
<a href="<?= APP_URL ?>/verifikator/warga?level=Tinggi" class="btn btn-danger">🔥 Warga Prioritas Tinggi</a>
</div>
</div>
</div>
+179
View File
@@ -0,0 +1,179 @@
<?php ob_start(); ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
const lat = <?= (float)$laporan['lat'] ?>;
const lng = <?= (float)$laporan['lng'] ?>;
if (lat && lng) {
const map = L.map('laporan-map').setView([lat, lng], 16);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{maxZoom:19}).addTo(map);
L.marker([lat, lng]).bindPopup('<strong><?= addslashes($laporan['nama_warga']) ?></strong>').addTo(map).openPopup();
}
});
</script>
<?php $extraJs = ob_get_clean(); ?>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:20px">
<a href="<?= APP_URL ?>/verifikator/laporan" class="btn btn-ghost btn-sm">← Kembali</a>
<h2 style="font-size:1.1rem;font-weight:700">Detail Laporan: <?= e($laporan['kode_laporan']) ?></h2>
<?php $badge = ['menunggu'=>'badge-yellow','diproses'=>'badge-blue','terverifikasi'=>'badge-green','ditolak'=>'badge-red']; ?>
<span class="badge <?= $badge[$laporan['status']] ?? 'badge-gray' ?>" style="font-size:0.8rem"><?= ucfirst($laporan['status']) ?></span>
</div>
<div style="display:grid;grid-template-columns:1fr 380px;gap:20px">
<!-- KIRI: Info Detail -->
<div style="display:flex;flex-direction:column;gap:16px">
<!-- Info Warga Dilaporkan -->
<div class="card">
<div class="card-header"><span class="card-icon">👤</span><h3>Data Warga Dilaporkan</h3></div>
<div class="card-body">
<div class="form-row cols-2">
<div>
<div class="text-xs text-muted" style="margin-bottom:2px">Nama Warga</div>
<div style="font-weight:700;font-size:1.05rem"><?= e($laporan['nama_warga']) ?></div>
</div>
<div>
<div class="text-xs text-muted" style="margin-bottom:2px">Wilayah</div>
<div style="font-weight:500"><?= e($laporan['kelurahan'] ?? '') ?>, <?= e($laporan['kecamatan'] ?? '') ?></div>
</div>
</div>
<hr class="divider">
<div class="form-group">
<div class="text-xs text-muted">Alamat Lengkap</div>
<div style="font-weight:500"><?= e($laporan['alamat_warga']) ?></div>
</div>
<div class="form-row cols-2">
<div>
<div class="text-xs text-muted">Koordinat</div>
<div class="text-sm" style="font-family:monospace"><?= $laporan['lat'] ?>, <?= $laporan['lng'] ?></div>
</div>
<div>
<div class="text-xs text-muted">Tanggal Laporan</div>
<div class="text-sm"><?= formatTanggal($laporan['created_at'], true) ?></div>
</div>
</div>
<?php if ($laporan['deskripsi']): ?>
<hr class="divider">
<div class="form-group">
<div class="text-xs text-muted">Deskripsi Kondisi</div>
<div style="font-size:0.875rem;line-height:1.6;background:#f8fafc;padding:12px;border-radius:6px;margin-top:4px"><?= nl2br(e($laporan['deskripsi'])) ?></div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Info Pelapor -->
<div class="card">
<div class="card-header"><span class="card-icon">📝</span><h3>Data Pelapor</h3></div>
<div class="card-body">
<div class="form-row cols-2">
<div>
<div class="text-xs text-muted">Nama Pelapor</div>
<div style="font-weight:600"><?= e($laporan['nama_pelapor']) ?></div>
</div>
<div>
<div class="text-xs text-muted">No HP</div>
<div><?= e($laporan['no_hp_pelapor'] ?? '—') ?></div>
</div>
</div>
</div>
</div>
<!-- Foto -->
<?php if ($laporan['foto']): ?>
<div class="card">
<div class="card-header"><span class="card-icon">📷</span><h3>Foto Bukti</h3></div>
<div class="card-body">
<img src="<?= uploadUrl($laporan['foto']) ?>" style="max-width:100%;border-radius:8px;border:1px solid var(--clr-border)">
</div>
</div>
<?php endif; ?>
<!-- Catatan Verifikator -->
<?php if ($laporan['catatan_verifikator']): ?>
<div class="card">
<div class="card-header"><span class="card-icon">💬</span><h3>Catatan Verifikator</h3></div>
<div class="card-body">
<div style="background:#f8fafc;padding:12px;border-radius:6px;font-size:0.875rem"><?= nl2br(e($laporan['catatan_verifikator'])) ?></div>
<?php if ($laporan['tanggal_verifikasi']): ?>
<div class="text-xs text-muted" style="margin-top:8px">Diverifikasi: <?= formatTanggal($laporan['tanggal_verifikasi'], true) ?></div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
<!-- KANAN: Peta + Aksi -->
<div style="display:flex;flex-direction:column;gap:16px">
<!-- PETA LOKASI -->
<div class="card">
<div class="card-header"><span class="card-icon">📍</span><h3>Lokasi Warga</h3></div>
<div class="card-body no-pad">
<div id="laporan-map" style="height:240px"></div>
</div>
</div>
<!-- PANEL AKSI VERIFIKASI -->
<?php if (in_array($laporan['status'], ['menunggu','diproses'])): ?>
<div class="card" style="border:2px solid var(--clr-primary-l)">
<div class="card-header" style="background:var(--clr-primary);color:#fff">
<span class="card-icon">⚡</span>
<h3 style="color:#fff">Tindakan Verifikasi</h3>
</div>
<div class="card-body">
<!-- Tandai Diproses -->
<?php if ($laporan['status'] === 'menunggu'): ?>
<form method="POST" action="<?= APP_URL ?>/verifikator/laporan/<?= $laporan['id'] ?>/proses" style="margin-bottom:12px">
<?= csrfField() ?>
<input type="hidden" name="aksi" value="proses">
<div class="form-group">
<label class="form-label">Catatan (opsional)</label>
<textarea name="catatan" class="form-control" rows="2" placeholder="Catatan survei awal..."></textarea>
</div>
<button type="submit" class="btn btn-primary w-full">🔄 Tandai Sedang Diproses</button>
</form>
<hr class="divider">
<?php endif; ?>
<!-- VERIFIKASI - Buat data warga -->
<form method="POST" action="<?= APP_URL ?>/verifikator/laporan/<?= $laporan['id'] ?>/proses" style="margin-bottom:12px">
<?= csrfField() ?>
<input type="hidden" name="aksi" value="verifikasi">
<div class="form-group">
<label class="form-label">Catatan Verifikasi</label>
<textarea name="catatan" class="form-control" rows="2" placeholder="Hasil survei lapangan..."></textarea>
</div>
<button type="submit" class="btn btn-success w-full">✅ Verifikasi & Buat Data Warga</button>
<div class="form-hint" style="margin-top:4px">Akan otomatis membuat data warga miskin dan menetapkan rumah ibadah penanggung jawab</div>
</form>
<!-- TOLAK -->
<form method="POST" action="<?= APP_URL ?>/verifikator/laporan/<?= $laporan['id'] ?>/proses">
<?= csrfField() ?>
<input type="hidden" name="aksi" value="tolak">
<div class="form-group">
<label class="form-label">Alasan Penolakan <span class="required">*</span></label>
<textarea name="catatan" class="form-control" rows="2" placeholder="Jelaskan alasan penolakan..." required></textarea>
</div>
<button type="submit" class="btn btn-danger w-full" onclick="return confirm('Tolak laporan ini?')">❌ Tolak Laporan</button>
</form>
</div>
</div>
<?php else: ?>
<div class="card">
<div class="card-body" style="text-align:center;padding:28px">
<?php if ($laporan['status'] === 'terverifikasi'): ?>
<div style="font-size:2.5rem">✅</div>
<div style="font-weight:700;color:var(--clr-success);margin-top:8px">Laporan Terverifikasi</div>
<div class="text-sm text-muted" style="margin-top:4px">Data warga telah ditambahkan ke sistem</div>
<a href="<?= APP_URL ?>/verifikator/warga" class="btn btn-success" style="margin-top:12px">Lihat Data Warga</a>
<?php else: ?>
<div style="font-size:2.5rem">❌</div>
<div style="font-weight:700;color:var(--clr-danger);margin-top:8px">Laporan Ditolak</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
</div>
+277
View File
@@ -0,0 +1,277 @@
<?php $umur = $warga['tanggal_lahir'] ? hitungUmur($warga['tanggal_lahir']) : 0; ?>
<?php ob_start(); ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
const lat = <?= (float)$warga['lat'] ?>;
const lng = <?= (float)$warga['lng'] ?>;
if (lat && lng) {
const map = L.map('warga-detail-map').setView([lat,lng],16);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{maxZoom:19}).addTo(map);
const color = '<?= warnaMiskin($warga['status_kemiskinan']) ?>';
L.circleMarker([lat,lng],{radius:12,fillColor:color,color:'#fff',weight:3,fillOpacity:0.9})
.bindPopup('<strong><?= addslashes($warga['nama']) ?></strong>').addTo(map).openPopup();
<?php if($warga['ri_lat'] && $warga['ri_lng']): ?>
GISMap.map = map;
GISMap.createRIMarker({lat:<?= (float)$warga['ri_lat'] ?>,lng:<?= (float)$warga['ri_lng'] ?>})
.bindPopup('<?= addslashes($warga['rumah_ibadah_nama']??'Rumah Ibadah') ?>').addTo(map);
<?php endif; ?>
}
});
</script>
<?php $extraJs = ob_get_clean(); ?>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:20px">
<a href="<?= APP_URL ?>/verifikator/warga" class="btn btn-ghost btn-sm">← Kembali</a>
<h2 style="font-size:1.1rem;font-weight:700"><?= e($warga['nama']) ?></h2>
<?php $sc=['Miskin Ekstrem'=>'badge-red','Miskin'=>'badge-orange','Rentan Miskin'=>'badge-yellow']; ?>
<span class="badge <?= $sc[$warga['status_kemiskinan']]??'badge-gray' ?>"><?= e($warga['status_kemiskinan']) ?></span>
<?php if($warga['is_blind_spot']): ?><span class="badge badge-purple">⚠ Blind Spot</span><?php endif; ?>
<div style="margin-left:auto;display:flex;gap:8px">
<button class="btn btn-primary btn-sm" onclick="App.showModal('modal-edit')">✏️ Edit Data</button>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 340px;gap:20px">
<!-- KIRI -->
<div style="display:flex;flex-direction:column;gap:16px">
<!-- Skor Prioritas Card -->
<div class="card" style="border-left:4px solid <?= ['Tinggi'=>'#dc2626','Sedang'=>'#d97706','Rendah'=>'#16a34a'][$warga['level_prioritas']]??'#6b7280' ?>">
<div class="card-body">
<div style="display:flex;align-items:center;gap:20px">
<div style="text-align:center;min-width:90px">
<div style="font-size:2.5rem;font-weight:900;color:<?= ['Tinggi'=>'#dc2626','Sedang'=>'#d97706','Rendah'=>'#16a34a'][$warga['level_prioritas']]??'#6b7280' ?>"><?= $warga['skor_prioritas'] ?></div>
<div class="text-xs text-muted">Skor / 100</div>
</div>
<div style="flex:1">
<div class="priority-bar" style="height:10px;margin-bottom:8px">
<div class="priority-bar-fill <?= strtolower($warga['level_prioritas'])==='tinggi'?'high':(strtolower($warga['level_prioritas'])==='sedang'?'medium':'low') ?>"
style="width:<?= min(100,$warga['skor_prioritas']) ?>%"></div>
</div>
<div style="font-size:1rem;font-weight:700">Prioritas <?= e($warga['level_prioritas']) ?></div>
<div class="text-sm text-muted">Diperbarui otomatis berdasarkan data sosial ekonomi</div>
</div>
</div>
</div>
</div>
<!-- Data Pribadi -->
<div class="card">
<div class="card-header"><span class="card-icon">👤</span><h3>Data Pribadi</h3></div>
<div class="card-body">
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px">
<?php $items = [
['NIK', $warga['nik']??'—'],
['Tempat Lahir', $warga['tempat_lahir']??'—'],
['Tanggal Lahir', $warga['tanggal_lahir'] ? formatTanggal($warga['tanggal_lahir'])." ($umur thn)" : '—'],
['Jenis Kelamin', $warga['jenis_kelamin']==='L'?'Laki-laki':'Perempuan'],
['Agama', $warga['agama']??'—'],
['No HP', $warga['no_hp']??'—'],
]; foreach($items as [$l,$v]): ?>
<div>
<div class="text-xs text-muted"><?= $l ?></div>
<div style="font-weight:500;font-size:0.875rem;margin-top:2px"><?= e($v) ?></div>
</div>
<?php endforeach; ?>
</div>
<hr class="divider">
<div>
<div class="text-xs text-muted">Alamat</div>
<div style="font-weight:500;margin-top:2px"><?= e($warga['alamat']) ?></div>
<div class="text-xs text-muted" style="margin-top:2px"><?= e($warga['kelurahan']) ?>, <?= e($warga['kecamatan']) ?></div>
</div>
<?php if($warga['foto']): ?>
<hr class="divider">
<img src="<?= uploadUrl($warga['foto']) ?>" style="width:100px;height:100px;object-fit:cover;border-radius:8px;border:1px solid var(--clr-border)">
<?php endif; ?>
</div>
</div>
<!-- Sosial Ekonomi -->
<div class="card">
<div class="card-header"><span class="card-icon">💼</span><h3>Data Sosial Ekonomi</h3></div>
<div class="card-body">
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px">
<?php $items2 = [
['Pendidikan', $warga['pendidikan_terakhir']??'—'],
['Pekerjaan', $warga['pekerjaan']??'—'],
['Penghasilan', formatRupiah($warga['penghasilan_bulanan']??0).'/bln'],
['Tanggungan', ($warga['jumlah_tanggungan']??0).' orang'],
['Kondisi Rumah', $warga['kondisi_rumah']??'—'],
['Riwayat Penyakit', $warga['riwayat_penyakit']??'—'],
]; foreach($items2 as [$l,$v]): ?>
<div>
<div class="text-xs text-muted"><?= $l ?></div>
<div style="font-weight:500;font-size:0.875rem;margin-top:2px"><?= e($v) ?></div>
</div>
<?php endforeach; ?>
</div>
<hr class="divider">
<div style="display:flex;gap:12px;flex-wrap:wrap">
<?php if($warga['is_lansia']): ?>
<span class="badge badge-orange" style="font-size:0.8rem;padding:4px 10px">👴 Lansia</span>
<?php endif; ?>
<?php if($warga['is_disabilitas']): ?>
<span class="badge badge-blue" style="font-size:0.8rem;padding:4px 10px">♿ Disabilitas: <?= e($warga['disabilitas_jenis']??'') ?></span>
<?php endif; ?>
<?php if($warga['is_blind_spot']): ?>
<span class="badge badge-purple" style="font-size:0.8rem;padding:4px 10px">⚠ Blind Spot</span>
<?php endif; ?>
</div>
</div>
</div>
<!-- Histori Bantuan -->
<div class="card">
<div class="card-header"><span class="card-icon">📦</span><h3>Histori Bantuan (<?= count($bantuan) ?>)</h3></div>
<div class="card-body no-pad">
<?php if($bantuan): ?>
<div class="table-wrap">
<table>
<thead><tr><th>Tanggal</th><th>Kategori</th><th>Jumlah</th><th>Rumah Ibadah</th></tr></thead>
<tbody>
<?php foreach($bantuan as $b): ?>
<tr>
<td class="text-sm"><?= formatTanggal($b['tanggal_penyaluran']) ?></td>
<td><span class="badge badge-green"><?= e($b['kategori']) ?></span></td>
<td class="text-sm"><?= $b['jumlah'] ? ($b['satuan']==='rupiah'?formatRupiah($b['jumlah']):(number_format($b['jumlah']).' '.$b['satuan'])) : '—' ?></td>
<td class="text-sm"><?= e($b['rumah_ibadah']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="empty-state" style="padding:24px"><div class="empty-state-icon">📭</div><p>Belum ada bantuan</p></div>
<?php endif; ?>
</div>
</div>
</div>
<!-- KANAN: Peta + RI Info -->
<div style="display:flex;flex-direction:column;gap:16px">
<div class="card">
<div class="card-header"><span class="card-icon">📍</span><h3>Lokasi</h3></div>
<div class="card-body no-pad">
<div id="warga-detail-map" style="height:260px"></div>
</div>
<div class="card-body">
<div class="text-xs text-muted">Koordinat</div>
<div style="font-family:monospace;font-size:0.8rem"><?= $warga['lat'] ?>, <?= $warga['lng'] ?></div>
</div>
</div>
<?php if($warga['rumah_ibadah_nama']): ?>
<div class="card" style="border:1px solid #bbf7d0">
<div class="card-header" style="background:#f0fdf4">
<span class="card-icon">⛪</span><h3>Rumah Ibadah Penanggung Jawab</h3>
</div>
<div class="card-body">
<div style="font-weight:700;font-size:1rem;color:var(--clr-success)"><?= e($warga['rumah_ibadah_nama']) ?></div>
<div class="text-sm text-muted" style="margin-top:4px">Ditetapkan otomatis berdasarkan Haversine Formula</div>
</div>
</div>
<?php endif; ?>
<div class="card">
<div class="card-header"><span class="card-icon">️</span><h3>Info Verifikasi</h3></div>
<div class="card-body">
<div class="text-xs text-muted">Tanggal Verifikasi</div>
<div class="text-sm"><?= $warga['tanggal_verifikasi'] ? formatTanggal($warga['tanggal_verifikasi']) : '—' ?></div>
</div>
</div>
</div>
</div>
<!-- MODAL EDIT (sederhana) -->
<div class="modal-overlay" id="modal-edit">
<div class="modal" style="max-width:680px">
<div class="modal-header">
<h3>✏️ Edit Data Warga</h3>
<button class="modal-close" onclick="App.hideModal('modal-edit')">✕</button>
</div>
<form method="POST" action="<?= APP_URL ?>/verifikator/warga/<?= $warga['id'] ?>/update" enctype="multipart/form-data">
<?= csrfField() ?>
<div class="modal-body">
<div class="form-row cols-2">
<div class="form-group"><label class="form-label">Nama <span class="required">*</span></label>
<input type="text" name="nama" class="form-control" value="<?= e($warga['nama']) ?>" required></div>
<div class="form-group"><label class="form-label">NIK</label>
<input type="text" name="nik" class="form-control" value="<?= e($warga['nik']??'') ?>" maxlength="16"></div>
</div>
<div class="form-row cols-2">
<div class="form-group"><label class="form-label">Tempat Lahir</label>
<input type="text" name="tempat_lahir" class="form-control" value="<?= e($warga['tempat_lahir']??'') ?>"></div>
<div class="form-group"><label class="form-label">Tgl Lahir</label>
<input type="date" name="tanggal_lahir" class="form-control" value="<?= $warga['tanggal_lahir']??'' ?>"></div>
</div>
<div class="form-row cols-3">
<div class="form-group"><label class="form-label">JK</label>
<select name="jenis_kelamin" class="form-control">
<option value="L" <?= $warga['jenis_kelamin']==='L'?'selected':'' ?>>Laki-laki</option>
<option value="P" <?= $warga['jenis_kelamin']==='P'?'selected':'' ?>>Perempuan</option>
</select></div>
<div class="form-group"><label class="form-label">Agama</label>
<select name="agama" class="form-control">
<?php foreach(['Islam','Kristen','Katolik','Hindu','Buddha','Konghucu','Lainnya'] as $ag): ?>
<option value="<?= $ag ?>" <?= ($warga['agama']??'')===$ag?'selected':'' ?>><?= $ag ?></option>
<?php endforeach; ?>
</select></div>
<div class="form-group"><label class="form-label">No HP</label>
<input type="text" name="no_hp" class="form-control" value="<?= e($warga['no_hp']??'') ?>"></div>
</div>
<div class="form-group"><label class="form-label">Alamat</label>
<input type="text" name="alamat" class="form-control" value="<?= e($warga['alamat']) ?>"></div>
<div class="form-row cols-2">
<div class="form-group"><label class="form-label">Penghasilan/Bln</label>
<input type="number" name="penghasilan_bulanan" class="form-control" value="<?= $warga['penghasilan_bulanan']??0 ?>"></div>
<div class="form-group"><label class="form-label">Tanggungan</label>
<input type="number" name="jumlah_tanggungan" class="form-control" value="<?= $warga['jumlah_tanggungan']??0 ?>"></div>
</div>
<div class="form-row cols-2">
<div class="form-group"><label class="form-label">Pendidikan</label>
<select name="pendidikan_terakhir" class="form-control">
<?php foreach(['Tidak Sekolah','SD','SMP','SMA','D3','S1','S2','S3'] as $p): ?>
<option value="<?= $p ?>" <?= $warga['pendidikan_terakhir']===$p?'selected':'' ?>><?= $p ?></option>
<?php endforeach; ?>
</select></div>
<div class="form-group"><label class="form-label">Pekerjaan</label>
<input type="text" name="pekerjaan" class="form-control" value="<?= e($warga['pekerjaan']??'') ?>" placeholder="Buruh, Petani, dll"></div>
</div>
<div class="form-row cols-2">
<div class="form-group"><label class="form-label">Kondisi Rumah</label>
<select name="kondisi_rumah" class="form-control">
<?php foreach(['Sangat Tidak Layak','Tidak Layak','Cukup Layak','Layak'] as $k): ?>
<option value="<?= $k ?>" <?= $warga['kondisi_rumah']===$k?'selected':'' ?>><?= $k ?></option>
<?php endforeach; ?>
</select></div>
</div>
<div class="form-group"><label class="form-label">Riwayat Penyakit</label>
<input type="text" name="riwayat_penyakit" class="form-control" value="<?= e($warga['riwayat_penyakit']??'') ?>"></div>
<div style="display:flex;gap:20px">
<label style="display:flex;align-items:center;gap:6px;cursor:pointer">
<input type="checkbox" name="is_lansia" value="1" <?= $warga['is_lansia']?'checked':'' ?>> Lansia</label>
<label style="display:flex;align-items:center;gap:6px;cursor:pointer">
<input type="checkbox" name="is_disabilitas" id="edit-is-dis" value="1" <?= $warga['is_disabilitas']?'checked':'' ?>
onchange="document.getElementById('edit-jenis-dis').style.display=this.checked?'block':'none'"> Disabilitas</label>
</div>
<div id="edit-jenis-dis" style="margin-top:10px;<?= $warga['is_disabilitas']?'':'display:none' ?>">
<div class="form-group"><label class="form-label">Jenis Disabilitas</label>
<input type="text" name="disabilitas_jenis" class="form-control" value="<?= e($warga['disabilitas_jenis']??'') ?>" placeholder="Tuna netra, tuna daksa, dll"></div>
</div>
<div class="form-row cols-2" style="margin-top:12px">
<div class="form-group"><label class="form-label">Latitude</label>
<input type="number" name="lat" class="form-control" value="<?= $warga['lat'] ?>" step="0.0000001"></div>
<div class="form-group"><label class="form-label">Longitude</label>
<input type="number" name="lng" class="form-control" value="<?= $warga['lng'] ?>" step="0.0000001"></div>
</div>
<div class="form-group"><label class="form-label">Foto Baru</label>
<input type="file" name="foto" class="form-control" accept="image/*"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-ghost" onclick="App.hideModal('modal-edit')">Batal</button>
<button type="submit" class="btn btn-primary">Simpan Perubahan</button>
</div>
</form>
</div>
</div>
+208
View File
@@ -0,0 +1,208 @@
<?php
$_mapLat = MAP_DEFAULT_LAT;
$_mapLng = MAP_DEFAULT_LNG;
$extraJs = <<<JS
<script>
document.addEventListener('DOMContentLoaded', () => {
LocationPicker.init('picker-map','lat','lng', {$_mapLat}, {$_mapLng});
// Auto-detect lansia
document.getElementById('tanggal_lahir').addEventListener('change', function() {
const d = new Date(this.value);
const age = Math.floor((Date.now() - d.getTime()) / (365.25*24*60*60*1000));
document.getElementById('is_lansia').checked = age >= 60;
});
// Auto hitung skor
const calcBtn = document.getElementById('calc-skor');
if (calcBtn) {
calcBtn.addEventListener('click', function() {
const p = parseInt(document.getElementById('penghasilan').value) || 0;
const t = parseInt(document.getElementById('tanggungan').value) || 0;
const kapita = t > 0 ? p/t : p;
const st = kapita <= 350000 ? 'Miskin Ekstrem' : kapita <= 550000 ? 'Miskin' : 'Rentan Miskin';
document.getElementById('status_kem_preview').textContent = st;
});
}
});
</script>
JS; ?>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:20px">
<a href="<?= APP_URL ?>/verifikator/warga" class="btn btn-ghost btn-sm">← Kembali</a>
<h2 style="font-size:1.1rem;font-weight:700">Input Data Warga Baru</h2>
</div>
<form method="POST" action="<?= APP_URL ?>/verifikator/warga/store" enctype="multipart/form-data">
<?= csrfField() ?>
<div style="display:grid;grid-template-columns:1fr 360px;gap:20px">
<!-- KIRI: Form Data -->
<div style="display:flex;flex-direction:column;gap:16px">
<!-- Data Pribadi -->
<div class="card">
<div class="card-header"><span class="card-icon">👤</span><h3>Data Pribadi</h3></div>
<div class="card-body">
<div class="form-row cols-2">
<div class="form-group">
<label class="form-label">Nama Lengkap <span class="required">*</span></label>
<input type="text" name="nama" class="form-control" required>
</div>
<div class="form-group">
<label class="form-label">NIK (KTP)</label>
<input type="text" name="nik" class="form-control" maxlength="16" placeholder="16 digit NIK">
</div>
</div>
<div class="form-row cols-2">
<div class="form-group">
<label class="form-label">Tempat Lahir</label>
<input type="text" name="tempat_lahir" class="form-control">
</div>
<div class="form-group">
<label class="form-label">Tanggal Lahir</label>
<input type="date" name="tanggal_lahir" id="tanggal_lahir" class="form-control">
</div>
</div>
<div class="form-row cols-3">
<div class="form-group">
<label class="form-label">Jenis Kelamin</label>
<select name="jenis_kelamin" class="form-control">
<option value="L">Laki-laki</option>
<option value="P">Perempuan</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Agama</label>
<select name="agama" class="form-control">
<?php foreach (['Islam','Kristen','Katolik','Hindu','Buddha','Konghucu','Lainnya'] as $ag): ?>
<option value="<?= $ag ?>"><?= $ag ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">No HP</label>
<input type="text" name="no_hp" class="form-control">
</div>
</div>
<div class="form-group">
<label class="form-label">Alamat Lengkap <span class="required">*</span></label>
<textarea name="alamat" class="form-control" rows="2" required></textarea>
</div>
<div class="form-group">
<label class="form-label">Foto</label>
<input type="file" name="foto" class="form-control" accept="image/*">
</div>
</div>
</div>
<!-- Sosial Ekonomi -->
<div class="card">
<div class="card-header"><span class="card-icon">💼</span><h3>Data Sosial Ekonomi</h3></div>
<div class="card-body">
<div class="form-row cols-2">
<div class="form-group">
<label class="form-label">Pendidikan Terakhir</label>
<select name="pendidikan_terakhir" class="form-control">
<?php foreach (['Tidak Sekolah','SD','SMP','SMA','D3','S1','S2','S3'] as $p): ?>
<option value="<?= $p ?>"><?= $p ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">Pekerjaan</label>
<input type="text" name="pekerjaan" class="form-control" placeholder="Buruh, Petani, dll">
</div>
</div>
<div class="form-row cols-2">
<div class="form-group">
<label class="form-label">Penghasilan/Bulan (Rp) <span class="required">*</span></label>
<input type="number" name="penghasilan_bulanan" id="penghasilan" class="form-control" value="0" min="0" required>
</div>
<div class="form-group">
<label class="form-label">Jumlah Tanggungan <span class="required">*</span></label>
<input type="number" name="jumlah_tanggungan" id="tanggungan" class="form-control" value="0" min="0" max="20" required>
</div>
</div>
<div style="display:flex;gap:12px;align-items:center;margin-bottom:12px">
<button type="button" id="calc-skor" class="btn btn-ghost btn-sm">⚡ Hitung Otomatis</button>
<span class="text-sm">Status Kemiskinan: <strong id="status_kem_preview" style="color:var(--clr-danger)">—</strong></span>
</div>
<div class="form-row cols-2">
<div class="form-group">
<label class="form-label">Kondisi Rumah <span class="required">*</span></label>
<select name="kondisi_rumah" class="form-control" required>
<?php foreach (['Sangat Tidak Layak','Tidak Layak','Cukup Layak','Layak'] as $k): ?>
<option value="<?= $k ?>"><?= $k ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">Riwayat Penyakit</label>
<input type="text" name="riwayat_penyakit" class="form-control" placeholder="Diabetes, Hipertensi, dll">
</div>
</div>
<!-- Kondisi Khusus -->
<div style="display:flex;gap:24px;flex-wrap:wrap">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
<input type="checkbox" name="is_lansia" id="is_lansia" value="1"> Lansia (≥60 tahun)
</label>
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
<input type="checkbox" name="is_disabilitas" id="is_disabilitas" value="1"
onchange="document.getElementById('jenis-dis').style.display=this.checked?'block':'none'">
Penyandang Disabilitas
</label>
</div>
<div id="jenis-dis" style="display:none;margin-top:10px">
<div class="form-group">
<label class="form-label">Jenis Disabilitas</label>
<input type="text" name="disabilitas_jenis" class="form-control" placeholder="Tuna netra, tuna daksa, dll">
</div>
</div>
</div>
</div>
</div>
<!-- KANAN: Peta Lokasi -->
<div style="display:flex;flex-direction:column;gap:16px">
<div class="card" style="position:sticky;top:80px">
<div class="card-header"><span class="card-icon">📍</span><h3>Lokasi Rumah</h3></div>
<div class="card-body">
<div id="picker-map" style="height:340px;border-radius:8px;border:1px solid var(--clr-border);margin-bottom:12px"></div>
<div class="form-row cols-2">
<div class="form-group">
<label class="form-label">Latitude <span class="required">*</span></label>
<input type="number" name="lat" id="lat" class="form-control" step="0.0000001" required placeholder="-0.0263">
</div>
<div class="form-group">
<label class="form-label">Longitude <span class="required">*</span></label>
<input type="number" name="lng" id="lng" class="form-control" step="0.0000001" required placeholder="109.3425">
</div>
</div>
<div class="form-hint">Klik peta atau drag marker untuk menentukan lokasi rumah</div>
<!-- Wilayah -->
<div class="form-group" style="margin-top:12px">
<label class="form-label">Kelurahan</label>
<input type="text" class="form-control" value="<?= e($kelurahan['nama'] ?? '') ?> — <?= e($kelurahan['kecamatan'] ?? '') ?>" readonly style="background:#f8fafc">
</div>
<div style="margin-top:16px;padding:12px;background:#f0fdf4;border-radius:8px;border:1px solid #bbf7d0">
<div style="font-size:0.78rem;color:var(--clr-success);font-weight:600;margin-bottom:4px">️ Assignment Otomatis</div>
<div style="font-size:0.75rem;color:#166534">Setelah disimpan, sistem akan otomatis:<br>
✓ Menghitung rumah ibadah terdekat (Haversine)<br>
✓ Mendeteksi blind spot<br>
✓ Menghitung skor prioritas bantuan
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ACTION BUTTONS -->
<div style="display:flex;justify-content:flex-end;gap:12px;margin-top:20px">
<a href="<?= APP_URL ?>/verifikator/warga" class="btn btn-ghost">Batal</a>
<button type="submit" class="btn btn-primary" style="padding:11px 28px">💾 Simpan Data Warga</button>
</div>
</form>
+64
View File
@@ -0,0 +1,64 @@
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">
<div>
<h2 style="font-size:1.2rem;font-weight:700">Daftar Laporan Masyarakat</h2>
<p class="text-muted text-sm">Kelurahan: <strong><?= e(Auth::user()['kelurahan_nama'] ?? '-') ?></strong></p>
</div>
<a href="<?= APP_URL ?>/verifikator/warga/tambah" class="btn btn-primary"> Input Manual Warga</a>
</div>
<!-- Filter Status -->
<div class="tabs">
<?php $statuses = ['semua'=>'Semua','menunggu'=>'⏳ Menunggu','diproses'=>'🔄 Diproses','terverifikasi'=>'✅ Terverifikasi','ditolak'=>'❌ Ditolak']; ?>
<?php foreach ($statuses as $s => $l): ?>
<a href="?status=<?= $s ?>" class="tab-btn <?= ($status === $s) ? 'active' : '' ?>"><?= $l ?></a>
<?php endforeach; ?>
</div>
<!-- SEARCH -->
<div class="filters-bar">
<input type="text" id="search-lap" class="form-control" placeholder="🔍 Cari nama warga, kode laporan..." style="max-width:320px">
</div>
<!-- TABLE -->
<div class="card">
<div class="card-body no-pad">
<div class="table-wrap">
<table id="table-laporan">
<thead>
<tr><th>Kode</th><th>Nama Warga</th><th>Pelapor</th><th>Alamat</th><th>Status</th><th>Tanggal</th><th>Aksi</th></tr>
</thead>
<tbody>
<?php foreach ($data as $l): ?>
<tr>
<td><code style="font-size:0.78rem;background:#f1f5f9;padding:2px 6px;border-radius:4px"><?= e($l['kode_laporan']) ?></code></td>
<td>
<div style="font-weight:600"><?= e($l['nama_warga']) ?></div>
<div class="text-xs text-muted"><?= e($l['alamat_warga']) ?></div>
</td>
<td class="text-sm">
<?= e($l['nama_pelapor']) ?>
<?php if ($l['no_hp_pelapor']): ?>
<div class="text-xs text-muted"><?= e($l['no_hp_pelapor']) ?></div>
<?php endif; ?>
</td>
<td class="text-sm"><?= e($l['kelurahan'] ?? '') ?></td>
<td>
<?php $badgeMap = ['menunggu'=>'badge-yellow','diproses'=>'badge-blue','terverifikasi'=>'badge-green','ditolak'=>'badge-red']; ?>
<span class="badge <?= $badgeMap[$l['status']] ?? 'badge-gray' ?>"><?= ucfirst($l['status']) ?></span>
</td>
<td class="text-xs text-muted"><?= formatTanggal($l['created_at']) ?></td>
<td>
<a href="<?= APP_URL ?>/verifikator/laporan/<?= $l['id'] ?>" class="btn btn-primary btn-sm">Detail</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if (empty($data)): ?>
<div class="empty-state"><div class="empty-state-icon">📭</div><h3>Tidak ada laporan</h3><p>Belum ada laporan dengan status ini</p></div>
<?php endif; ?>
</div>
</div>
<script>document.addEventListener('DOMContentLoaded', () => App.initTableSearch('search-lap', 'table-laporan'));</script>
+111
View File
@@ -0,0 +1,111 @@
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">
<div>
<h2 style="font-size:1.2rem;font-weight:700">Data Warga Miskin</h2>
<p class="text-muted text-sm">Kelurahan: <strong><?= e(Auth::user()['kelurahan_nama'] ?? '-') ?></strong> · <?= count($data) ?> warga terdata</p>
</div>
<a href="<?= APP_URL ?>/verifikator/warga/tambah" class="btn btn-primary"> Tambah Warga</a>
</div>
<!-- Filter -->
<div class="tabs">
<?php $levels = ['semua'=>'Semua','Tinggi'=>'🔥 Prioritas Tinggi','Sedang'=>'⚡ Sedang','Rendah'=>'✅ Rendah']; ?>
<?php foreach ($levels as $l => $label): ?>
<a href="?level=<?= $l ?>" class="tab-btn <?= $level===$l?'active':'' ?>"><?= $label ?></a>
<?php endforeach; ?>
</div>
<div class="filters-bar">
<input type="text" id="search-warga" class="form-control" placeholder="🔍 Cari nama, NIK..." style="max-width:300px">
</div>
<!-- PETA -->
<div class="card" style="margin-bottom:20px">
<div class="card-header"><span class="card-icon">🗺</span><h3>Peta Warga Kelurahan</h3></div>
<div class="card-body no-pad">
<div id="warga-map" style="height:280px"></div>
</div>
</div>
<!-- TABLE -->
<div class="card">
<div class="card-body no-pad">
<div class="table-wrap">
<table id="table-warga">
<thead>
<tr>
<th>#</th><th>Nama / NIK</th><th>Status Kemiskinan</th><th>Prioritas</th>
<th>Penghasilan</th><th>Tanggungan</th><th>Rumah Ibadah</th><th>Ket.</th><th>Aksi</th>
</tr>
</thead>
<tbody>
<?php foreach ($data as $i => $w): ?>
<tr>
<td class="text-xs text-muted"><?= $i+1 ?></td>
<td>
<div style="font-weight:600"><?= e($w['nama']) ?></div>
<div class="text-xs text-muted"><?= $w['nik'] ? 'NIK: '.e($w['nik']) : '<em>NIK belum diisi</em>' ?></div>
</td>
<td>
<?php $sc = ['Miskin Ekstrem'=>'badge-red','Miskin'=>'badge-orange','Rentan Miskin'=>'badge-yellow']; ?>
<span class="badge <?= $sc[$w['status_kemiskinan']] ?? 'badge-gray' ?>"><?= e($w['status_kemiskinan']) ?></span>
</td>
<td>
<?php $pc = ['Tinggi'=>'badge-red','Sedang'=>'badge-yellow','Rendah'=>'badge-green']; ?>
<span class="badge <?= $pc[$w['level_prioritas']] ?? 'badge-gray' ?>"><?= e($w['level_prioritas']) ?></span>
<div style="margin-top:4px">
<div class="priority-bar" style="width:80px">
<div class="priority-bar-fill <?= strtolower($w['level_prioritas'])==='tinggi'?'high':(strtolower($w['level_prioritas'])==='sedang'?'medium':'low') ?>"
style="width:<?= min(100,$w['skor_prioritas']) ?>%"></div>
</div>
</div>
<div class="text-xs text-muted"><?= $w['skor_prioritas'] ?>/100</div>
</td>
<td class="text-sm"><?= formatRupiah($w['penghasilan_bulanan'] ?? 0) ?></td>
<td class="text-sm text-center"><?= $w['jumlah_tanggungan'] ?> orang</td>
<td class="text-sm"><?= e($w['rumah_ibadah'] ?? '—') ?></td>
<td>
<?php if ($w['is_blind_spot']): ?>
<span class="badge badge-purple" title="Tidak dalam radius rumah ibadah">⚠ Blind</span>
<?php endif; ?>
<?php if ($w['is_lansia']): ?>
<span class="badge badge-orange" title="Lansia">👴</span>
<?php endif; ?>
<?php if ($w['is_disabilitas']): ?>
<span class="badge badge-blue" title="Disabilitas">♿</span>
<?php endif; ?>
</td>
<td>
<a href="<?= APP_URL ?>/verifikator/warga/<?= $w['id'] ?>" class="btn btn-ghost btn-sm">Detail</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if (empty($data)): ?>
<div class="empty-state"><div class="empty-state-icon">👥</div><h3>Belum ada data warga</h3></div>
<?php endif; ?>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
App.initTableSearch('search-warga','table-warga');
const map = GISMap.init('warga-map', <?= MAP_DEFAULT_LAT ?>, <?= MAP_DEFAULT_LNG ?>, 14);
const warga = <?= json_encode(array_map(fn($w) => [
'id'=>$w['id'],'nama'=>$w['nama'],'lat'=>(float)$w['lat'],'lng'=>(float)$w['lng'],
'status_kemiskinan'=>$w['status_kemiskinan'],'level_prioritas'=>$w['level_prioritas'],
'is_blind_spot'=>$w['is_blind_spot'],'kelurahan'=>$w['kelurahan']??''
], array_filter($data, fn($w) => $w['lat'] && $w['lng']))) ?>;
if (warga.length) {
GISMap.addWargaLayer(warga, w => {
window.location.href = '<?= APP_URL ?>/verifikator/warga/' + w.id;
});
GISMap.autoFitBounds();
} else {
GISMap.autoFitBounds(true);
}
});
</script>