388 lines
17 KiB
PHP
388 lines
17 KiB
PHP
<?php
|
|
// app/controllers/VerifikatorController.php
|
|
|
|
class VerifikatorController extends Controller {
|
|
|
|
private int $idKelurahan;
|
|
|
|
public function __construct() {
|
|
parent::__construct();
|
|
Auth::require('verifikator');
|
|
$this->idKelurahan = (int)($_SESSION['id_kelurahan'] ?? 0);
|
|
}
|
|
|
|
public function dashboard(): void {
|
|
$stats = $this->db->fetchOne("
|
|
SELECT
|
|
(SELECT COUNT(*) FROM laporan_masyarakat WHERE id_kelurahan=?) as total_laporan,
|
|
(SELECT COUNT(*) FROM laporan_masyarakat WHERE id_kelurahan=? AND status='menunggu') as laporan_menunggu,
|
|
(SELECT COUNT(*) FROM laporan_masyarakat WHERE id_kelurahan=? AND status='diproses') as laporan_diproses,
|
|
(SELECT COUNT(*) FROM warga_miskin WHERE id_kelurahan=? AND status='aktif') as total_warga,
|
|
(SELECT COUNT(*) FROM warga_miskin WHERE id_kelurahan=? AND level_prioritas='Tinggi' AND status='aktif') as prioritas_tinggi,
|
|
(SELECT COUNT(*) FROM warga_miskin WHERE id_kelurahan=? AND is_blind_spot=1 AND status='aktif') as blind_spot
|
|
", array_fill(0, 6, $this->idKelurahan));
|
|
|
|
$laporanTerbaru = $this->db->fetchAll("
|
|
SELECT l.*, k.nama as kelurahan FROM laporan_masyarakat l
|
|
JOIN kelurahan k ON l.id_kelurahan = k.id
|
|
WHERE l.id_kelurahan = ?
|
|
ORDER BY l.created_at DESC LIMIT 10
|
|
", [$this->idKelurahan]);
|
|
|
|
$this->view('verifikator.dashboard', [
|
|
'title' => 'Dashboard Verifikator',
|
|
'stats' => $stats,
|
|
'laporanTerbaru'=> $laporanTerbaru,
|
|
'user' => Auth::user()
|
|
], 'verifikator');
|
|
}
|
|
|
|
public function laporan(): void {
|
|
$status = $_GET['status'] ?? 'semua';
|
|
$sql = "SELECT l.*, k.nama as kelurahan, kec.nama as kecamatan
|
|
FROM laporan_masyarakat l
|
|
JOIN kelurahan k ON l.id_kelurahan = k.id
|
|
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
|
WHERE l.id_kelurahan = ?";
|
|
$params = [$this->idKelurahan];
|
|
if ($status !== 'semua') {
|
|
$sql .= " AND l.status = ?";
|
|
$params[] = $status;
|
|
}
|
|
$sql .= " ORDER BY l.created_at DESC";
|
|
|
|
$data = $this->db->fetchAll($sql, $params);
|
|
$this->view('verifikator.laporan', [
|
|
'title' => 'Daftar Laporan',
|
|
'data' => $data,
|
|
'status' => $status,
|
|
'user' => Auth::user()
|
|
], 'verifikator');
|
|
}
|
|
|
|
// FIX: type hint string, cast to int internally (router always passes strings)
|
|
public function detailLaporan(string $id): void {
|
|
$id = (int) $id;
|
|
|
|
$laporan = $this->db->fetchOne("
|
|
SELECT l.*, k.nama as kelurahan, kec.nama as kecamatan
|
|
FROM laporan_masyarakat l
|
|
JOIN kelurahan k ON l.id_kelurahan = k.id
|
|
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
|
WHERE l.id = ? AND l.id_kelurahan = ?
|
|
", [$id, $this->idKelurahan]);
|
|
|
|
if (!$laporan) {
|
|
$this->flash('error', 'Laporan tidak ditemukan.');
|
|
$this->redirect(APP_URL . '/verifikator/laporan');
|
|
}
|
|
|
|
$this->view('verifikator.detail_laporan', [
|
|
'title' => 'Detail Laporan',
|
|
'laporan' => $laporan,
|
|
'user' => Auth::user()
|
|
], 'verifikator');
|
|
}
|
|
|
|
// FIX: type hint string, cast to int internally
|
|
public function prosesLaporan(string $id): void {
|
|
$id = (int) $id;
|
|
|
|
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/verifikator/laporan');
|
|
|
|
$aksi = $_POST['aksi'] ?? '';
|
|
$catatan = trim($_POST['catatan'] ?? '');
|
|
|
|
if ($aksi === 'proses') {
|
|
$this->db->update('laporan_masyarakat', [
|
|
'status' => 'diproses',
|
|
'id_verifikator' => Auth::id(),
|
|
'catatan_verifikator' => $catatan
|
|
], 'id = ? AND id_kelurahan = ?', [$id, $this->idKelurahan]);
|
|
$this->flash('success', 'Laporan ditandai sebagai diproses.');
|
|
|
|
} elseif ($aksi === 'tolak') {
|
|
$this->db->update('laporan_masyarakat', [
|
|
'status' => 'ditolak',
|
|
'id_verifikator' => Auth::id(),
|
|
'catatan_verifikator'=> $catatan,
|
|
'tanggal_verifikasi' => date('Y-m-d H:i:s')
|
|
], 'id = ? AND id_kelurahan = ?', [$id, $this->idKelurahan]);
|
|
$this->flash('info', 'Laporan ditolak.');
|
|
|
|
} elseif ($aksi === 'verifikasi') {
|
|
// Verifikasi = buat warga miskin baru dari laporan
|
|
$laporan = $this->db->fetchOne("SELECT * FROM laporan_masyarakat WHERE id=?", [$id]);
|
|
if ($laporan) {
|
|
$this->db->update('laporan_masyarakat', [
|
|
'status' => 'terverifikasi',
|
|
'id_verifikator' => Auth::id(),
|
|
'catatan_verifikator'=> $catatan,
|
|
'tanggal_verifikasi' => date('Y-m-d H:i:s')
|
|
], 'id = ?', [$id]);
|
|
|
|
// Cek apakah warga sudah ada
|
|
$existing = $this->db->fetchOne("SELECT id FROM warga_miskin WHERE id_laporan = ?", [$id]);
|
|
if (!$existing) {
|
|
// Buat entry warga miskin baru
|
|
$isBlindSpot = isInBlindSpot((float)$laporan['lat'], (float)$laporan['lng']) ? 1 : 0;
|
|
$ri = findRumahIbadahTerdekat((float)$laporan['lat'], (float)$laporan['lng']);
|
|
$idRi = ($ri && !$isBlindSpot) ? $ri['id'] : null;
|
|
|
|
// Jika dalam radius, gunakan yang terdekat dan dalam radius
|
|
if ($ri && !$isBlindSpot) {
|
|
$dist = haversine((float)$laporan['lat'], (float)$laporan['lng'], (float)$ri['lat'], (float)$ri['lng']);
|
|
if ($dist > $ri['radius_meter']) {
|
|
$idRi = null;
|
|
$isBlindSpot = 1;
|
|
}
|
|
}
|
|
|
|
$wargaData = [
|
|
'id_laporan' => $id,
|
|
'id_kelurahan' => $laporan['id_kelurahan'],
|
|
'id_rumah_ibadah' => $idRi,
|
|
'id_verifikator' => Auth::id(),
|
|
'nama' => $laporan['nama_warga'],
|
|
'alamat' => $laporan['alamat_warga'],
|
|
'lat' => $laporan['lat'],
|
|
'lng' => $laporan['lng'],
|
|
'is_blind_spot' => $isBlindSpot,
|
|
'status' => 'aktif',
|
|
'tanggal_verifikasi' => date('Y-m-d')
|
|
];
|
|
|
|
// Hitung skor prioritas awal
|
|
$skor = hitungSkorPrioritas($wargaData);
|
|
$wargaData['skor_prioritas'] = $skor['skor'];
|
|
$wargaData['level_prioritas'] = $skor['level'];
|
|
|
|
$this->db->insert('warga_miskin', $wargaData);
|
|
}
|
|
}
|
|
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'verifikator', 'Verifikasi Laporan', 'verifikasi', "Verifikasi laporan ID $id");
|
|
$this->flash('success', 'Laporan berhasil diverifikasi. Data warga telah ditambahkan.');
|
|
}
|
|
|
|
$this->redirect(APP_URL . '/verifikator/laporan/' . $id);
|
|
}
|
|
|
|
public function warga(): void {
|
|
$level = $_GET['level'] ?? 'semua';
|
|
$sql = "SELECT w.*, k.nama as kelurahan, ri.nama as rumah_ibadah
|
|
FROM warga_miskin w
|
|
JOIN kelurahan k ON w.id_kelurahan = k.id
|
|
LEFT JOIN rumah_ibadah ri ON w.id_rumah_ibadah = ri.id
|
|
WHERE w.id_kelurahan = ? AND w.status = 'aktif'";
|
|
$params = [$this->idKelurahan];
|
|
if ($level !== 'semua') {
|
|
$sql .= " AND w.level_prioritas = ?";
|
|
$params[] = $level;
|
|
}
|
|
$sql .= " ORDER BY w.skor_prioritas DESC, w.nama";
|
|
|
|
$data = $this->db->fetchAll($sql, $params);
|
|
$this->view('verifikator.warga', [
|
|
'title' => 'Data Warga Miskin',
|
|
'data' => $data,
|
|
'level' => $level,
|
|
'user' => Auth::user()
|
|
], 'verifikator');
|
|
}
|
|
|
|
// FIX: type hint string, cast to int internally
|
|
public function detailWarga(string $id): void {
|
|
$id = (int) $id;
|
|
|
|
$warga = $this->db->fetchOne("
|
|
SELECT w.*, k.nama as kelurahan, kec.nama as kecamatan,
|
|
ri.nama as rumah_ibadah_nama, ri.lat as ri_lat, ri.lng as ri_lng
|
|
FROM warga_miskin w
|
|
JOIN kelurahan k ON w.id_kelurahan = k.id
|
|
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
|
LEFT JOIN rumah_ibadah ri ON w.id_rumah_ibadah = ri.id
|
|
WHERE w.id = ? AND w.id_kelurahan = ?
|
|
", [$id, $this->idKelurahan]);
|
|
|
|
if (!$warga) {
|
|
$this->flash('error', 'Data tidak ditemukan.');
|
|
$this->redirect(APP_URL . '/verifikator/warga');
|
|
}
|
|
|
|
$bantuan = $this->db->fetchAll("
|
|
SELECT hb.*, kb.nama as kategori, ri.nama as rumah_ibadah
|
|
FROM histori_bantuan hb
|
|
JOIN kategori_bantuan kb ON hb.id_kategori_bantuan = kb.id
|
|
JOIN rumah_ibadah ri ON hb.id_rumah_ibadah = ri.id
|
|
WHERE hb.id_warga = ?
|
|
ORDER BY hb.tanggal_penyaluran DESC
|
|
", [$id]);
|
|
|
|
$this->view('verifikator.detail_warga', [
|
|
'title' => 'Detail Warga',
|
|
'warga' => $warga,
|
|
'bantuan' => $bantuan,
|
|
'user' => Auth::user()
|
|
], 'verifikator');
|
|
}
|
|
|
|
public function formTambahWarga(): void {
|
|
$kelurahan = $this->db->fetchOne("SELECT k.id, k.nama, kec.nama as kecamatan FROM kelurahan k JOIN kecamatan kec ON k.id_kecamatan=kec.id WHERE k.id=?", [$this->idKelurahan]);
|
|
$this->view('verifikator.form_warga', [
|
|
'title' => 'Tambah Data Warga',
|
|
'kelurahan' => $kelurahan,
|
|
'user' => Auth::user()
|
|
], 'verifikator');
|
|
}
|
|
|
|
public function storeWarga(): void {
|
|
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/verifikator/warga');
|
|
|
|
$lat = (float)$_POST['lat'];
|
|
$lng = (float)$_POST['lng'];
|
|
|
|
// Blind spot check
|
|
$isBlindSpot = isInBlindSpot($lat, $lng) ? 1 : 0;
|
|
|
|
// Assignment rumah ibadah
|
|
$ri = findRumahIbadahTerdekat($lat, $lng);
|
|
$idRi = null;
|
|
if ($ri && !$isBlindSpot) {
|
|
$dist = haversine($lat, $lng, (float)$ri['lat'], (float)$ri['lng']);
|
|
$idRi = ($dist <= $ri['radius_meter']) ? $ri['id'] : null;
|
|
if (!$idRi) $isBlindSpot = 1;
|
|
}
|
|
|
|
$penghasilan = (int)$_POST['penghasilan_bulanan'];
|
|
$tanggungan = (int)$_POST['jumlah_tanggungan'];
|
|
$statusKemiskinan = tentukanStatusKemiskinan($penghasilan, $tanggungan);
|
|
|
|
$tanggalLahir = $_POST['tanggal_lahir'] ?? '';
|
|
$isLansia = 0;
|
|
if ($tanggalLahir) {
|
|
$umur = hitungUmur($tanggalLahir);
|
|
if ($umur >= 60) $isLansia = 1;
|
|
}
|
|
|
|
$data = [
|
|
'id_kelurahan' => $this->idKelurahan,
|
|
'id_rumah_ibadah' => $idRi,
|
|
'id_verifikator' => Auth::id(),
|
|
'nik' => trim($_POST['nik'] ?? '') ?: null,
|
|
'nama' => trim($_POST['nama']),
|
|
'tempat_lahir' => trim($_POST['tempat_lahir'] ?? ''),
|
|
'tanggal_lahir' => $tanggalLahir ?: null,
|
|
'jenis_kelamin' => $_POST['jenis_kelamin'] ?? 'L',
|
|
'agama' => $_POST['agama'] ?? 'Islam',
|
|
'alamat' => trim($_POST['alamat']),
|
|
'lat' => $lat,
|
|
'lng' => $lng,
|
|
'no_hp' => trim($_POST['no_hp'] ?? ''),
|
|
'pendidikan_terakhir'=> $_POST['pendidikan_terakhir'] ?? 'SD',
|
|
'pekerjaan' => trim($_POST['pekerjaan'] ?? ''),
|
|
'penghasilan_bulanan'=> $penghasilan,
|
|
'jumlah_tanggungan' => $tanggungan,
|
|
'kondisi_rumah' => $_POST['kondisi_rumah'] ?? 'Tidak Layak',
|
|
'riwayat_penyakit' => trim($_POST['riwayat_penyakit'] ?? ''),
|
|
'is_lansia' => $isLansia,
|
|
'is_disabilitas' => (int)($_POST['is_disabilitas'] ?? 0),
|
|
'disabilitas_jenis' => trim($_POST['disabilitas_jenis'] ?? ''),
|
|
'status_kemiskinan' => $statusKemiskinan,
|
|
'is_blind_spot' => $isBlindSpot,
|
|
'status' => 'aktif',
|
|
'tanggal_verifikasi' => date('Y-m-d')
|
|
];
|
|
|
|
$foto = null;
|
|
if (!empty($_FILES['foto']['name'])) {
|
|
$foto = $this->uploadFile($_FILES['foto'], 'warga');
|
|
}
|
|
$data['foto'] = $foto;
|
|
|
|
$skor = hitungSkorPrioritas($data);
|
|
$data['skor_prioritas'] = $skor['skor'];
|
|
$data['level_prioritas'] = $skor['level'];
|
|
|
|
try {
|
|
$this->db->insert('warga_miskin', $data);
|
|
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'verifikator', 'Tambah Warga', 'warga_miskin', 'Tambah: ' . $data['nama']);
|
|
$this->flash('success', 'Data warga berhasil ditambahkan.' . ($isBlindSpot ? ' <strong>PERHATIAN: Warga ini terdeteksi sebagai Blind Spot.</strong>' : ''));
|
|
} catch (Exception $e) {
|
|
$this->flash('error', 'Gagal menyimpan. NIK mungkin sudah terdaftar.');
|
|
}
|
|
|
|
$this->redirect(APP_URL . '/verifikator/warga');
|
|
}
|
|
|
|
// FIX: type hint string, cast to int internally
|
|
public function updateWarga(string $id): void {
|
|
$id = (int) $id;
|
|
|
|
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/verifikator/warga');
|
|
|
|
$lat = (float)$_POST['lat'];
|
|
$lng = (float)$_POST['lng'];
|
|
$isBlindSpot = isInBlindSpot($lat, $lng) ? 1 : 0;
|
|
$ri = findRumahIbadahTerdekat($lat, $lng);
|
|
$idRi = null;
|
|
if ($ri && !$isBlindSpot) {
|
|
$dist = haversine($lat, $lng, (float)$ri['lat'], (float)$ri['lng']);
|
|
$idRi = ($dist <= $ri['radius_meter']) ? $ri['id'] : null;
|
|
if (!$idRi) $isBlindSpot = 1;
|
|
}
|
|
|
|
$penghasilan = (int)$_POST['penghasilan_bulanan'];
|
|
$tanggungan = (int)$_POST['jumlah_tanggungan'];
|
|
$tanggalLahir = $_POST['tanggal_lahir'] ?? '';
|
|
$isLansia = ($tanggalLahir && hitungUmur($tanggalLahir) >= 60) ? 1 : 0;
|
|
|
|
$data = [
|
|
'id_rumah_ibadah' => $idRi,
|
|
'nik' => trim($_POST['nik'] ?? '') ?: null,
|
|
'nama' => trim($_POST['nama']),
|
|
'tempat_lahir' => trim($_POST['tempat_lahir'] ?? ''),
|
|
'tanggal_lahir' => $tanggalLahir ?: null,
|
|
'jenis_kelamin' => $_POST['jenis_kelamin'] ?? 'L',
|
|
'agama' => $_POST['agama'] ?? 'Islam',
|
|
'alamat' => trim($_POST['alamat']),
|
|
'lat' => $lat,
|
|
'lng' => $lng,
|
|
'pendidikan_terakhir'=> $_POST['pendidikan_terakhir'] ?? 'SD',
|
|
'pekerjaan' => trim($_POST['pekerjaan'] ?? ''),
|
|
'penghasilan_bulanan'=> $penghasilan,
|
|
'jumlah_tanggungan' => $tanggungan,
|
|
'kondisi_rumah' => $_POST['kondisi_rumah'] ?? 'Tidak Layak',
|
|
'riwayat_penyakit' => trim($_POST['riwayat_penyakit'] ?? ''),
|
|
'is_lansia' => $isLansia,
|
|
'is_disabilitas' => (int)($_POST['is_disabilitas'] ?? 0),
|
|
'disabilitas_jenis' => trim($_POST['disabilitas_jenis'] ?? ''),
|
|
'status_kemiskinan' => tentukanStatusKemiskinan($penghasilan, $tanggungan),
|
|
'is_blind_spot' => $isBlindSpot
|
|
];
|
|
|
|
$skor = hitungSkorPrioritas($data);
|
|
$data['skor_prioritas'] = $skor['skor'];
|
|
$data['level_prioritas'] = $skor['level'];
|
|
|
|
if (!empty($_FILES['foto']['name'])) {
|
|
$data['foto'] = $this->uploadFile($_FILES['foto'], 'warga');
|
|
}
|
|
|
|
$this->db->update('warga_miskin', $data, 'id = ? AND id_kelurahan = ?', [$id, $this->idKelurahan]);
|
|
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'verifikator', 'Update Warga', 'warga_miskin', "Update warga ID $id");
|
|
$this->flash('success', 'Data warga berhasil diperbarui.');
|
|
$this->redirect(APP_URL . '/verifikator/warga/' . $id);
|
|
}
|
|
|
|
public function apiStats(): void {
|
|
$data = $this->db->fetchOne("
|
|
SELECT
|
|
COUNT(CASE WHEN status='menunggu' THEN 1 END) as menunggu,
|
|
COUNT(CASE WHEN status='diproses' THEN 1 END) as diproses,
|
|
COUNT(CASE WHEN status='terverifikasi' THEN 1 END) as terverifikasi,
|
|
COUNT(CASE WHEN status='ditolak' THEN 1 END) as ditolak
|
|
FROM laporan_masyarakat WHERE id_kelurahan = ?
|
|
", [$this->idKelurahan]);
|
|
$this->json(['success' => true, 'data' => $data]);
|
|
}
|
|
} |