618 lines
26 KiB
PHP
618 lines
26 KiB
PHP
<?php
|
|
// app/controllers/DinsosController.php
|
|
|
|
class DinsosController extends Controller {
|
|
|
|
public function __construct() {
|
|
parent::__construct();
|
|
Auth::require('dinsos');
|
|
}
|
|
|
|
public function dashboard(): void {
|
|
$stats = $this->db->fetchOne("
|
|
SELECT
|
|
(SELECT COUNT(*) FROM warga_miskin WHERE status='aktif') as total_warga,
|
|
(SELECT COUNT(*) FROM warga_miskin WHERE status='aktif' AND status_kemiskinan='Miskin Ekstrem') as ekstrem,
|
|
(SELECT COUNT(*) FROM warga_miskin WHERE status='aktif' AND status_kemiskinan='Miskin') as miskin,
|
|
(SELECT COUNT(*) FROM warga_miskin WHERE status='aktif' AND status_kemiskinan='Rentan Miskin') as rentan,
|
|
(SELECT COUNT(*) FROM warga_miskin WHERE is_blind_spot=1 AND status='aktif') as blind_spot,
|
|
(SELECT COUNT(*) FROM rumah_ibadah WHERE is_active=1) as total_ri,
|
|
(SELECT COUNT(*) FROM histori_bantuan WHERE status='disalurkan') as total_bantuan,
|
|
(SELECT COUNT(*) FROM histori_bantuan WHERE status='disalurkan' AND MONTH(tanggal_penyaluran)=MONTH(CURDATE())) as bantuan_bulan_ini,
|
|
(SELECT COUNT(*) FROM laporan_masyarakat WHERE status='menunggu') as laporan_pending
|
|
");
|
|
|
|
$perKecamatan = $this->db->fetchAll("
|
|
SELECT kec.id, kec.nama,
|
|
COUNT(w.id) as total,
|
|
SUM(w.status_kemiskinan='Miskin Ekstrem') as ekstrem,
|
|
SUM(w.status_kemiskinan='Miskin') as miskin,
|
|
SUM(w.status_kemiskinan='Rentan Miskin') as rentan,
|
|
SUM(w.is_blind_spot) as blind_spot
|
|
FROM kecamatan kec
|
|
LEFT JOIN kelurahan k ON k.id_kecamatan = kec.id
|
|
LEFT JOIN warga_miskin w ON w.id_kelurahan = k.id AND w.status='aktif'
|
|
GROUP BY kec.id, kec.nama
|
|
ORDER BY total DESC
|
|
");
|
|
|
|
$trenBantuan = $this->db->fetchAll("
|
|
SELECT DATE_FORMAT(tanggal_penyaluran,'%Y-%m') as bulan,
|
|
COUNT(*) as jumlah,
|
|
COUNT(DISTINCT id_warga) as warga_terbantu
|
|
FROM histori_bantuan WHERE status='disalurkan'
|
|
AND tanggal_penyaluran >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
|
|
GROUP BY bulan ORDER BY bulan
|
|
");
|
|
|
|
$topRi = $this->db->fetchAll("
|
|
SELECT ri.nama, ji.nama as jenis, COUNT(hb.id) as jml_bantuan,
|
|
COUNT(DISTINCT hb.id_warga) as jml_warga
|
|
FROM rumah_ibadah ri
|
|
JOIN jenis_rumah_ibadah ji ON ri.id_jenis = ji.id
|
|
LEFT JOIN histori_bantuan hb ON hb.id_rumah_ibadah = ri.id AND hb.status='disalurkan'
|
|
WHERE ri.is_active = 1
|
|
GROUP BY ri.id, ri.nama, ji.nama
|
|
ORDER BY jml_bantuan DESC LIMIT 5
|
|
");
|
|
|
|
$this->view('dinsos.dashboard', [
|
|
'title' => 'Dashboard Dinas Sosial',
|
|
'stats' => $stats,
|
|
'perKecamatan' => $perKecamatan,
|
|
'trenBantuan' => $trenBantuan,
|
|
'topRi' => $topRi,
|
|
'user' => Auth::user()
|
|
], 'dinsos');
|
|
}
|
|
|
|
public function kemiskinan(): void {
|
|
$filter_kec = $_GET['kecamatan'] ?? '';
|
|
$filter_kel = $_GET['kelurahan'] ?? '';
|
|
$filter_status = $_GET['status'] ?? '';
|
|
$filter_prioritas = $_GET['prioritas'] ?? '';
|
|
|
|
$sql = "SELECT w.*, k.nama as kelurahan, kec.nama as kecamatan, ri.nama as rumah_ibadah
|
|
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.status = 'aktif'";
|
|
$params = [];
|
|
|
|
if ($filter_kec) { $sql .= " AND kec.id = ?"; $params[] = $filter_kec; }
|
|
if ($filter_kel) { $sql .= " AND k.id = ?"; $params[] = $filter_kel; }
|
|
if ($filter_status) { $sql .= " AND w.status_kemiskinan = ?"; $params[] = $filter_status; }
|
|
if ($filter_prioritas) { $sql .= " AND w.level_prioritas = ?"; $params[] = $filter_prioritas; }
|
|
$sql .= " ORDER BY w.skor_prioritas DESC";
|
|
|
|
$data = $this->db->fetchAll($sql, $params);
|
|
$kecamatan = $this->db->fetchAll("SELECT * FROM kecamatan ORDER BY nama");
|
|
$kelurahan = !empty($filter_kec)
|
|
? $this->db->fetchAll("SELECT * FROM kelurahan WHERE id_kecamatan = ? ORDER BY nama", [$filter_kec])
|
|
: [];
|
|
|
|
$this->view('dinsos.kemiskinan', [
|
|
'title' => 'Monitoring Kemiskinan',
|
|
'data' => $data,
|
|
'kecamatan' => $kecamatan,
|
|
'kelurahan' => $kelurahan,
|
|
'filter_kec' => $filter_kec,
|
|
'filter_kel' => $filter_kel,
|
|
'filter_status' => $filter_status,
|
|
'filter_prioritas'=> $filter_prioritas,
|
|
'user' => Auth::user()
|
|
], 'dinsos');
|
|
}
|
|
|
|
public function bantuan(): void {
|
|
$bulan = $_GET['bulan'] ?? date('Y-m');
|
|
[$tahun, $bln] = explode('-', $bulan);
|
|
|
|
$data = $this->db->fetchAll("
|
|
SELECT hb.*, kb.nama as kategori, wm.nama as warga,
|
|
ri.nama as rumah_ibadah, k.nama as kelurahan, kec.nama as kecamatan
|
|
FROM histori_bantuan hb
|
|
JOIN kategori_bantuan kb ON hb.id_kategori_bantuan = kb.id
|
|
JOIN warga_miskin wm ON hb.id_warga = wm.id
|
|
JOIN rumah_ibadah ri ON hb.id_rumah_ibadah = ri.id
|
|
JOIN kelurahan k ON wm.id_kelurahan = k.id
|
|
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
|
WHERE hb.status = 'disalurkan'
|
|
AND YEAR(hb.tanggal_penyaluran) = ? AND MONTH(hb.tanggal_penyaluran) = ?
|
|
ORDER BY hb.tanggal_penyaluran DESC
|
|
", [$tahun, $bln]);
|
|
|
|
$perKategori = $this->db->fetchAll("
|
|
SELECT kb.nama, COUNT(hb.id) as jumlah
|
|
FROM histori_bantuan hb
|
|
JOIN kategori_bantuan kb ON hb.id_kategori_bantuan = kb.id
|
|
WHERE hb.status = 'disalurkan'
|
|
AND YEAR(hb.tanggal_penyaluran) = ? AND MONTH(hb.tanggal_penyaluran) = ?
|
|
GROUP BY kb.id, kb.nama ORDER BY jumlah DESC
|
|
", [$tahun, $bln]);
|
|
|
|
$this->view('dinsos.bantuan', [
|
|
'title' => 'Monitoring Bantuan',
|
|
'data' => $data,
|
|
'perKategori' => $perKategori,
|
|
'bulan' => $bulan,
|
|
'user' => Auth::user()
|
|
], 'dinsos');
|
|
}
|
|
|
|
public function akun(): void {
|
|
$users = $this->db->fetchAll("
|
|
SELECT u.*, r.nama as role_nama, r.slug as role_slug,
|
|
CONCAT(kec.nama, ' - ', k.nama) as kelurahan_nama,
|
|
ri.id as rumah_ibadah_id, ri.nama as rumah_ibadah_nama
|
|
FROM users u
|
|
JOIN roles r ON u.id_role = r.id
|
|
LEFT JOIN kelurahan k ON u.id_kelurahan = k.id
|
|
LEFT JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
|
LEFT JOIN rumah_ibadah ri ON ri.id_user = u.id
|
|
WHERE r.slug IN ('verifikator', 'rumah_ibadah')
|
|
ORDER BY r.id, u.nama
|
|
");
|
|
|
|
$roles = $this->db->fetchAll("
|
|
SELECT id, nama, slug
|
|
FROM roles
|
|
WHERE slug IN ('verifikator', 'rumah_ibadah')
|
|
ORDER BY id
|
|
");
|
|
|
|
$kelurahan = $this->db->fetchAll("
|
|
SELECT k.id, CONCAT(kec.nama, ' - ', k.nama) as nama
|
|
FROM kelurahan k
|
|
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
|
ORDER BY kec.nama, k.nama
|
|
");
|
|
|
|
$rumahIbadah = $this->db->fetchAll("
|
|
SELECT ri.id, ri.nama, ri.id_user, CONCAT(kec.nama, ' - ', k.nama) as wilayah,
|
|
ji.nama as jenis, u.nama as pengurus_nama
|
|
FROM rumah_ibadah ri
|
|
JOIN jenis_rumah_ibadah ji ON ri.id_jenis = ji.id
|
|
JOIN kelurahan k ON ri.id_kelurahan = k.id
|
|
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
|
LEFT JOIN users u ON ri.id_user = u.id
|
|
WHERE ri.is_active = 1
|
|
ORDER BY kec.nama, k.nama, ri.nama
|
|
");
|
|
|
|
$this->view('dinsos.akun', [
|
|
'title' => 'Manajemen Akun Pengguna',
|
|
'users' => $users,
|
|
'roles' => $roles,
|
|
'kelurahan' => $kelurahan,
|
|
'rumahIbadah' => $rumahIbadah,
|
|
'user' => Auth::user()
|
|
], 'dinsos');
|
|
}
|
|
|
|
public function storeAkun(): void {
|
|
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/dinsos/akun');
|
|
|
|
$roleSlug = $_POST['role_slug'] ?? '';
|
|
if (!in_array($roleSlug, ['verifikator', 'rumah_ibadah'], true)) {
|
|
$this->flash('error', 'Peran pengguna tidak valid.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
$role = $this->db->fetchOne("SELECT id, nama FROM roles WHERE slug = ?", [$roleSlug]);
|
|
if (!$role) {
|
|
$this->flash('error', 'Data peran tidak ditemukan.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
$idKelurahan = null;
|
|
$idRumahIbadah = null;
|
|
if ($roleSlug === 'verifikator') {
|
|
$idKelurahan = (int)($_POST['id_kelurahan'] ?? 0);
|
|
if ($idKelurahan <= 0) {
|
|
$this->flash('error', 'Kelurahan tanggung jawab verifikator wajib dipilih.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
} else {
|
|
$idRumahIbadah = (int)($_POST['id_rumah_ibadah'] ?? 0);
|
|
$ri = $this->db->fetchOne("SELECT id, id_kelurahan, id_user, nama FROM rumah_ibadah WHERE id = ? AND is_active = 1", [$idRumahIbadah]);
|
|
if (!$ri) {
|
|
$this->flash('error', 'Rumah ibadah tidak ditemukan atau tidak aktif.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
if (!empty($ri['id_user'])) {
|
|
$this->flash('error', 'Rumah ibadah tersebut sudah memiliki pengurus.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
$idKelurahan = (int)$ri['id_kelurahan'];
|
|
}
|
|
|
|
$password = generatePasswordAkun();
|
|
$data = [
|
|
'id_role' => (int)$role['id'],
|
|
'id_kelurahan' => $idKelurahan,
|
|
'nama' => trim($_POST['nama'] ?? ''),
|
|
'username' => trim($_POST['username'] ?? ''),
|
|
'email' => trim($_POST['email'] ?? ''),
|
|
'password' => password_hash($password, PASSWORD_BCRYPT),
|
|
'no_hp' => trim($_POST['no_hp'] ?? ''),
|
|
'is_active' => 1
|
|
];
|
|
|
|
if (!$data['nama'] || !$data['username'] || !$data['email']) {
|
|
$this->flash('error', 'Nama lengkap, username, dan email wajib diisi.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
try {
|
|
$idUser = $this->db->insert('users', $data);
|
|
if ($roleSlug === 'rumah_ibadah') {
|
|
$this->db->update('rumah_ibadah', ['id_user' => $idUser], 'id = ?', [$idRumahIbadah]);
|
|
}
|
|
|
|
$emailSent = kirimEmailAkun($data['email'], $data['nama'], $data['username'], $password, 'dibuat');
|
|
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'dinsos', 'Tambah Akun Pengguna', 'users', 'Menambah user: ' . $data['username']);
|
|
|
|
if ($emailSent) {
|
|
$this->flash('success', 'Akun berhasil disimpan dan kredensial dikirim ke email pengguna.');
|
|
} else {
|
|
$this->flash('warning', 'Akun berhasil disimpan, tetapi email belum terkirim. Password awal: <strong>' . $password . '</strong>');
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->flash('error', 'Gagal menyimpan akun. Username atau email mungkin sudah digunakan.');
|
|
}
|
|
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
public function updateAkun(string $id): void {
|
|
$id = (int)$id;
|
|
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/dinsos/akun');
|
|
|
|
$target = $this->managedUser($id);
|
|
if (!$target) {
|
|
$this->flash('error', 'Akun tidak ditemukan atau tidak dapat dikelola Dinsos.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
$roleSlug = $_POST['role_slug'] ?? '';
|
|
if (!in_array($roleSlug, ['verifikator', 'rumah_ibadah'], true)) {
|
|
$this->flash('error', 'Peran pengguna tidak valid.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
$role = $this->db->fetchOne("SELECT id FROM roles WHERE slug = ?", [$roleSlug]);
|
|
$idKelurahan = null;
|
|
$idRumahIbadah = null;
|
|
|
|
if ($roleSlug === 'verifikator') {
|
|
$idKelurahan = (int)($_POST['id_kelurahan'] ?? 0);
|
|
if ($idKelurahan <= 0) {
|
|
$this->flash('error', 'Kelurahan tanggung jawab verifikator wajib dipilih.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
} else {
|
|
$idRumahIbadah = (int)($_POST['id_rumah_ibadah'] ?? 0);
|
|
$ri = $this->db->fetchOne("SELECT id, id_kelurahan, id_user FROM rumah_ibadah WHERE id = ? AND is_active = 1", [$idRumahIbadah]);
|
|
if (!$ri) {
|
|
$this->flash('error', 'Rumah ibadah tidak ditemukan atau tidak aktif.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
if (!empty($ri['id_user']) && (int)$ri['id_user'] !== $id) {
|
|
$this->flash('error', 'Rumah ibadah tersebut sudah memiliki pengurus.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
$idKelurahan = (int)$ri['id_kelurahan'];
|
|
}
|
|
|
|
$data = [
|
|
'id_role' => (int)$role['id'],
|
|
'id_kelurahan' => $idKelurahan,
|
|
'nama' => trim($_POST['nama'] ?? ''),
|
|
'username' => trim($_POST['username'] ?? ''),
|
|
'email' => trim($_POST['email'] ?? ''),
|
|
'no_hp' => trim($_POST['no_hp'] ?? ''),
|
|
'is_active' => (int)($_POST['is_active'] ?? 1)
|
|
];
|
|
|
|
try {
|
|
$this->db->update('users', $data, 'id = ?', [$id]);
|
|
$this->db->update('rumah_ibadah', ['id_user' => null], 'id_user = ?', [$id]);
|
|
if ($roleSlug === 'rumah_ibadah' && $data['is_active']) {
|
|
$this->db->update('rumah_ibadah', ['id_user' => $id], 'id = ?', [$idRumahIbadah]);
|
|
}
|
|
|
|
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'dinsos', 'Update Akun Pengguna', 'users', "Update user ID $id");
|
|
$this->flash('success', 'Akun pengguna berhasil diperbarui.');
|
|
} catch (Exception $e) {
|
|
$this->flash('error', 'Gagal memperbarui akun. Username atau email mungkin sudah digunakan.');
|
|
}
|
|
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
public function resetAkunPassword(string $id): void {
|
|
$id = (int)$id;
|
|
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/dinsos/akun');
|
|
|
|
$target = $this->managedUser($id);
|
|
if (!$target) {
|
|
$this->flash('error', 'Akun tidak ditemukan atau tidak dapat dikelola Dinsos.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
$password = generatePasswordAkun();
|
|
$this->db->update('users', [
|
|
'password' => password_hash($password, PASSWORD_BCRYPT)
|
|
], 'id = ?', [$id]);
|
|
|
|
$emailSent = kirimEmailAkun($target['email'], $target['nama'], $target['username'], $password, 'direset passwordnya');
|
|
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'dinsos', 'Reset Password Akun', 'users', "Reset password user ID $id");
|
|
|
|
if ($emailSent) {
|
|
$this->flash('success', 'Password berhasil direset dan dikirim ke email pengguna.');
|
|
} else {
|
|
$this->flash('warning', 'Password berhasil direset, tetapi email belum terkirim. Password baru: <strong>' . $password . '</strong>');
|
|
}
|
|
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
public function deactivateAkun(string $id): void {
|
|
$id = (int)$id;
|
|
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/dinsos/akun');
|
|
|
|
$target = $this->managedUser($id);
|
|
if (!$target) {
|
|
$this->flash('error', 'Akun tidak ditemukan atau tidak dapat dikelola Dinsos.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
$this->db->update('users', ['is_active' => 0], 'id = ?', [$id]);
|
|
$this->db->update('rumah_ibadah', ['id_user' => null], 'id_user = ?', [$id]);
|
|
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'dinsos', 'Nonaktifkan Akun', 'users', "Nonaktifkan user ID $id");
|
|
$this->flash('success', 'Akun pengguna berhasil dinonaktifkan.');
|
|
$this->redirect(APP_URL . '/dinsos/akun');
|
|
}
|
|
|
|
private function managedUser(int $id): ?array {
|
|
return $this->db->fetchOne("
|
|
SELECT u.*, r.slug as role_slug
|
|
FROM users u
|
|
JOIN roles r ON u.id_role = r.id
|
|
WHERE u.id = ? AND r.slug IN ('verifikator', 'rumah_ibadah')
|
|
", [$id]);
|
|
}
|
|
|
|
public function blindSpot(): void {
|
|
$data = $this->db->fetchAll("
|
|
SELECT w.*, k.nama as kelurahan, kec.nama as kecamatan,
|
|
ri.id as assigned_ri_id, ri.nama as assigned_rumah_ibadah,
|
|
assigned_ji.nama as assigned_jenis
|
|
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
|
|
LEFT JOIN jenis_rumah_ibadah assigned_ji ON ri.id_jenis = assigned_ji.id
|
|
WHERE w.is_blind_spot = 1 AND w.status = 'aktif'
|
|
ORDER BY CASE WHEN w.id_rumah_ibadah IS NULL THEN 0 ELSE 1 END,
|
|
CASE w.level_prioritas WHEN 'Tinggi' THEN 1 WHEN 'Sedang' THEN 2 ELSE 3 END,
|
|
w.skor_prioritas DESC
|
|
");
|
|
|
|
$rumahIbadah = $this->db->fetchAll("
|
|
SELECT ri.id, ri.nama, ri.alamat, ri.lat, ri.lng, ri.radius_meter,
|
|
ji.nama as jenis, k.nama as kelurahan, kec.nama as kecamatan,
|
|
COUNT(wm.id) as jml_warga
|
|
FROM rumah_ibadah ri
|
|
JOIN jenis_rumah_ibadah ji ON ri.id_jenis = ji.id
|
|
JOIN kelurahan k ON ri.id_kelurahan = k.id
|
|
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
|
LEFT JOIN warga_miskin wm ON wm.id_rumah_ibadah = ri.id AND wm.status = 'aktif'
|
|
WHERE ri.is_active = 1
|
|
GROUP BY ri.id, ri.nama, ri.alamat, ri.lat, ri.lng, ri.radius_meter,
|
|
ji.nama, k.nama, kec.nama
|
|
ORDER BY kec.nama, k.nama, ri.nama
|
|
");
|
|
|
|
$rekomendasi = $this->buildRekomendasiRumahIbadah($data, $rumahIbadah);
|
|
$sudahDitugaskan = count(array_filter($data, fn($w) => !empty($w['id_rumah_ibadah'])));
|
|
|
|
$perKec = $this->db->fetchAll("
|
|
SELECT kec.nama, COUNT(w.id) as jumlah
|
|
FROM kecamatan kec
|
|
JOIN kelurahan k ON k.id_kecamatan = kec.id
|
|
JOIN warga_miskin w ON w.id_kelurahan = k.id
|
|
WHERE w.is_blind_spot = 1 AND w.status = 'aktif'
|
|
GROUP BY kec.id, kec.nama ORDER BY jumlah DESC
|
|
");
|
|
|
|
$this->view('dinsos.blind_spot', [
|
|
'title' => 'Monitoring Blind Spot',
|
|
'data' => $data,
|
|
'perKec' => $perKec,
|
|
'rumahIbadah' => $rumahIbadah,
|
|
'rekomendasi' => $rekomendasi,
|
|
'ringkasanPenugasan' => [
|
|
'sudah' => $sudahDitugaskan,
|
|
'belum' => count($data) - $sudahDitugaskan,
|
|
'total_ri' => count($rumahIbadah)
|
|
],
|
|
'user' => Auth::user()
|
|
], 'dinsos');
|
|
}
|
|
|
|
public function assignBlindSpot(string $id): void {
|
|
$id = (int) $id;
|
|
|
|
if (!$this->verifyCsrf()) {
|
|
$this->flash('error', 'Token tidak valid. Silakan coba lagi.');
|
|
$this->redirect(APP_URL . '/dinsos/blind-spot');
|
|
}
|
|
|
|
$idRumahIbadah = (int)($_POST['id_rumah_ibadah'] ?? 0);
|
|
if ($id <= 0 || $idRumahIbadah <= 0) {
|
|
$this->flash('error', 'Pilih rumah ibadah penanggung jawab terlebih dahulu.');
|
|
$this->redirect(APP_URL . '/dinsos/blind-spot');
|
|
}
|
|
|
|
$warga = $this->db->fetchOne("
|
|
SELECT id, nama
|
|
FROM warga_miskin
|
|
WHERE id = ? AND is_blind_spot = 1 AND status = 'aktif'
|
|
", [$id]);
|
|
|
|
if (!$warga) {
|
|
$this->flash('error', 'Data warga blind spot tidak ditemukan.');
|
|
$this->redirect(APP_URL . '/dinsos/blind-spot');
|
|
}
|
|
|
|
$rumahIbadah = $this->db->fetchOne("
|
|
SELECT id, nama
|
|
FROM rumah_ibadah
|
|
WHERE id = ? AND is_active = 1
|
|
", [$idRumahIbadah]);
|
|
|
|
if (!$rumahIbadah) {
|
|
$this->flash('error', 'Rumah ibadah tidak aktif atau tidak ditemukan.');
|
|
$this->redirect(APP_URL . '/dinsos/blind-spot');
|
|
}
|
|
|
|
$this->db->update('warga_miskin', [
|
|
'id_rumah_ibadah' => $idRumahIbadah
|
|
], 'id = ? AND is_blind_spot = 1 AND status = ?', [$id, 'aktif']);
|
|
|
|
Auth::logActivity(
|
|
Auth::id(),
|
|
Auth::user()['nama'],
|
|
'dinsos',
|
|
'Atur Penugasan Blind Spot',
|
|
'blind_spot',
|
|
'Menugaskan ' . $warga['nama'] . ' ke ' . $rumahIbadah['nama']
|
|
);
|
|
|
|
$this->flash('success', 'Penugasan warga blind spot berhasil disimpan.');
|
|
$this->redirect(APP_URL . '/dinsos/blind-spot');
|
|
}
|
|
|
|
private function buildRekomendasiRumahIbadah(array $wargaList, array $rumahIbadahList): array {
|
|
$hasil = [];
|
|
|
|
foreach ($wargaList as $warga) {
|
|
$lat = (float)($warga['lat'] ?? 0);
|
|
$lng = (float)($warga['lng'] ?? 0);
|
|
$idWarga = (int)($warga['id'] ?? 0);
|
|
$hasil[$idWarga] = [];
|
|
|
|
if (!$lat || !$lng) {
|
|
continue;
|
|
}
|
|
|
|
$kandidat = [];
|
|
foreach ($rumahIbadahList as $ri) {
|
|
if (empty($ri['lat']) || empty($ri['lng'])) {
|
|
continue;
|
|
}
|
|
|
|
$jarak = haversine($lat, $lng, (float)$ri['lat'], (float)$ri['lng']);
|
|
$item = $ri;
|
|
$item['jarak_meter'] = (int)round($jarak);
|
|
$item['di_dalam_radius'] = $jarak <= (float)$ri['radius_meter'];
|
|
$kandidat[] = $item;
|
|
}
|
|
|
|
usort($kandidat, fn($a, $b) => $a['jarak_meter'] <=> $b['jarak_meter']);
|
|
$hasil[$idWarga] = array_slice($kandidat, 0, 3);
|
|
}
|
|
|
|
return $hasil;
|
|
}
|
|
|
|
public function kinerja(): void {
|
|
$data = $this->db->fetchAll("
|
|
SELECT ri.id, ri.nama, ji.nama as jenis, k.nama as kelurahan, kec.nama as kecamatan,
|
|
ri.radius_meter,
|
|
COUNT(DISTINCT wm.id) as jml_warga,
|
|
COUNT(DISTINCT hb.id) as jml_bantuan,
|
|
COUNT(DISTINCT CASE WHEN wm.status_kemiskinan='Miskin Ekstrem' THEN wm.id END) as jml_ekstrem,
|
|
MAX(hb.tanggal_penyaluran) as bantuan_terakhir
|
|
FROM rumah_ibadah ri
|
|
JOIN jenis_rumah_ibadah ji ON ri.id_jenis = ji.id
|
|
JOIN kelurahan k ON ri.id_kelurahan = k.id
|
|
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
|
LEFT JOIN warga_miskin wm ON wm.id_rumah_ibadah = ri.id AND wm.status='aktif'
|
|
LEFT JOIN histori_bantuan hb ON hb.id_rumah_ibadah = ri.id AND hb.status='disalurkan'
|
|
WHERE ri.is_active = 1
|
|
GROUP BY ri.id, ri.nama, ji.nama, k.nama, kec.nama, ri.radius_meter
|
|
ORDER BY jml_bantuan DESC
|
|
");
|
|
|
|
$this->view('dinsos.kinerja', [
|
|
'title' => 'Kinerja Rumah Ibadah',
|
|
'data' => $data,
|
|
'user' => Auth::user()
|
|
], 'dinsos');
|
|
}
|
|
|
|
public function laporan(): void {
|
|
$tahun = (int)($_GET['tahun'] ?? date('Y'));
|
|
|
|
$perBulan = $this->db->fetchAll("
|
|
SELECT MONTH(tanggal_penyaluran) as bulan, COUNT(*) as bantuan,
|
|
COUNT(DISTINCT id_warga) as warga_terbantu
|
|
FROM histori_bantuan WHERE status='disalurkan' AND YEAR(tanggal_penyaluran) = ?
|
|
GROUP BY MONTH(tanggal_penyaluran) ORDER BY bulan
|
|
", [$tahun]);
|
|
|
|
$ringkasan = $this->db->fetchOne("
|
|
SELECT COUNT(*) as total_bantuan,
|
|
COUNT(DISTINCT id_warga) as total_warga_terbantu,
|
|
COUNT(DISTINCT id_rumah_ibadah) as total_ri_aktif
|
|
FROM histori_bantuan WHERE status='disalurkan' AND YEAR(tanggal_penyaluran) = ?
|
|
", [$tahun]);
|
|
|
|
$this->view('dinsos.laporan', [
|
|
'title' => 'Laporan Tahunan',
|
|
'perBulan' => $perBulan,
|
|
'ringkasan' => $ringkasan,
|
|
'tahun' => $tahun,
|
|
'user' => Auth::user()
|
|
], 'dinsos');
|
|
}
|
|
|
|
public function apiHeatmap(): void {
|
|
$data = $this->db->fetchAll("
|
|
SELECT lat, lng, status_kemiskinan,
|
|
CASE status_kemiskinan
|
|
WHEN 'Miskin Ekstrem' THEN 1.0
|
|
WHEN 'Miskin' THEN 0.7
|
|
WHEN 'Rentan Miskin' THEN 0.4
|
|
ELSE 0.3
|
|
END as intensity
|
|
FROM warga_miskin WHERE status = 'aktif' AND lat != 0
|
|
");
|
|
$this->json($data);
|
|
}
|
|
|
|
public function apiStats(): void {
|
|
$perKec = $this->db->fetchAll("
|
|
SELECT kec.nama,
|
|
COUNT(w.id) as total,
|
|
SUM(w.status_kemiskinan='Miskin Ekstrem') as ekstrem,
|
|
SUM(w.is_blind_spot) as blind_spot
|
|
FROM kecamatan kec
|
|
LEFT JOIN kelurahan k ON k.id_kecamatan = kec.id
|
|
LEFT JOIN warga_miskin w ON w.id_kelurahan = k.id AND w.status='aktif'
|
|
GROUP BY kec.id, kec.nama
|
|
");
|
|
$trenBantuan = $this->db->fetchAll("
|
|
SELECT DATE_FORMAT(tanggal_penyaluran,'%Y-%m') as bulan, COUNT(*) as jumlah
|
|
FROM histori_bantuan WHERE status='disalurkan'
|
|
AND tanggal_penyaluran >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
|
|
GROUP BY bulan ORDER BY bulan
|
|
");
|
|
$this->json(['perKecamatan' => $perKec, 'trenBantuan' => $trenBantuan]);
|
|
}
|
|
}
|