Initial commit
This commit is contained in:
@@ -0,0 +1,418 @@
|
||||
<?php
|
||||
// app/controllers/AdminController.php
|
||||
|
||||
class AdminController extends Controller {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
Auth::require('superadmin');
|
||||
}
|
||||
|
||||
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 warga_ekstrem,
|
||||
(SELECT COUNT(*) FROM rumah_ibadah WHERE is_active=1) as total_ri,
|
||||
(SELECT COUNT(*) FROM laporan_masyarakat) as total_laporan,
|
||||
(SELECT COUNT(*) FROM laporan_masyarakat WHERE status='menunggu') as laporan_pending,
|
||||
(SELECT COUNT(*) FROM histori_bantuan WHERE status='disalurkan') as total_bantuan,
|
||||
(SELECT COUNT(*) FROM warga_miskin WHERE is_blind_spot=1 AND status='aktif') as blind_spot,
|
||||
(SELECT COUNT(*) FROM users WHERE is_active=1) as total_user
|
||||
");
|
||||
|
||||
// Data per kecamatan
|
||||
$perKecamatan = $this->db->fetchAll("
|
||||
SELECT 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=1) 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
|
||||
");
|
||||
|
||||
// Tren bantuan 6 bulan terakhir
|
||||
$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 6 MONTH)
|
||||
GROUP BY bulan ORDER BY bulan
|
||||
");
|
||||
|
||||
$aktivitas = $this->db->fetchAll("SELECT * FROM aktivitas_sistem ORDER BY created_at DESC LIMIT 15");
|
||||
|
||||
$this->view('admin.dashboard', [
|
||||
'title' => 'Dashboard Super Admin',
|
||||
'stats' => $stats,
|
||||
'perKecamatan'=> $perKecamatan,
|
||||
'trenBantuan' => $trenBantuan,
|
||||
'aktivitas' => $aktivitas,
|
||||
'user' => Auth::user()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
public function users(): void {
|
||||
$users = $this->db->fetchAll("
|
||||
SELECT u.*, r.nama as role_nama, r.slug as role_slug,
|
||||
k.nama as kelurahan_nama, 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 rumah_ibadah ri ON ri.id_user = u.id
|
||||
ORDER BY u.id_role, u.nama
|
||||
");
|
||||
$roles = $this->db->fetchAll("SELECT * FROM roles");
|
||||
$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");
|
||||
|
||||
$this->view('admin.users', [
|
||||
'title' => 'Manajemen User',
|
||||
'users' => $users,
|
||||
'roles' => $roles,
|
||||
'kelurahan' => $kelurahan,
|
||||
'user' => Auth::user()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
public function storeUser(): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/admin/users');
|
||||
|
||||
$data = [
|
||||
'id_role' => (int)$_POST['id_role'],
|
||||
'id_kelurahan' => !empty($_POST['id_kelurahan']) ? (int)$_POST['id_kelurahan'] : null,
|
||||
'nama' => trim($_POST['nama']),
|
||||
'username' => trim($_POST['username']),
|
||||
'email' => trim($_POST['email']),
|
||||
'password' => password_hash($_POST['password'], PASSWORD_BCRYPT),
|
||||
'no_hp' => trim($_POST['no_hp'] ?? ''),
|
||||
'is_active' => 1
|
||||
];
|
||||
|
||||
try {
|
||||
$this->db->insert('users', $data);
|
||||
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'superadmin', 'Tambah User', 'users', 'Menambah user: ' . $data['username']);
|
||||
$this->flash('success', 'User berhasil ditambahkan.');
|
||||
} catch (Exception $e) {
|
||||
$this->flash('error', 'Gagal menambah user: username/email sudah digunakan.');
|
||||
}
|
||||
$this->redirect(APP_URL . '/admin/users');
|
||||
}
|
||||
|
||||
public function updateUser(int $id): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/admin/users');
|
||||
|
||||
$data = [
|
||||
'id_role' => (int)$_POST['id_role'],
|
||||
'id_kelurahan' => !empty($_POST['id_kelurahan']) ? (int)$_POST['id_kelurahan'] : null,
|
||||
'nama' => trim($_POST['nama']),
|
||||
'username' => trim($_POST['username']),
|
||||
'email' => trim($_POST['email']),
|
||||
'no_hp' => trim($_POST['no_hp'] ?? ''),
|
||||
'is_active' => (int)$_POST['is_active']
|
||||
];
|
||||
if (!empty($_POST['password'])) {
|
||||
$data['password'] = password_hash($_POST['password'], PASSWORD_BCRYPT);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->db->update('users', $data, 'id = ?', [$id]);
|
||||
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'superadmin', 'Update User', 'users', "Update user ID $id");
|
||||
$this->flash('success', 'User berhasil diperbarui.');
|
||||
} catch (Exception $e) {
|
||||
$this->flash('error', 'Gagal memperbarui user: username atau email sudah digunakan.');
|
||||
}
|
||||
$this->redirect(APP_URL . '/admin/users');
|
||||
}
|
||||
|
||||
public function resetUserPassword(int $id): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/admin/users');
|
||||
|
||||
if ($id === Auth::id()) {
|
||||
$this->flash('error', 'Tidak bisa reset password akun sendiri dari halaman ini.');
|
||||
$this->redirect(APP_URL . '/admin/users');
|
||||
}
|
||||
|
||||
$target = $this->db->fetchOne("
|
||||
SELECT u.id, u.nama, u.username, u.email
|
||||
FROM users u
|
||||
WHERE u.id = ?
|
||||
", [$id]);
|
||||
|
||||
if (!$target) {
|
||||
$this->flash('error', 'User tidak ditemukan.');
|
||||
$this->redirect(APP_URL . '/admin/users');
|
||||
}
|
||||
|
||||
$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'], 'superadmin', 'Reset Password User', '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 . '/admin/users');
|
||||
}
|
||||
|
||||
public function deleteUser(int $id): void {
|
||||
if ($id === Auth::id()) {
|
||||
$this->flash('error', 'Tidak bisa menghapus akun sendiri.');
|
||||
$this->redirect(APP_URL . '/admin/users');
|
||||
}
|
||||
$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'], 'superadmin', 'Nonaktifkan User', 'users', "Nonaktifkan user ID $id");
|
||||
$this->flash('success', 'User berhasil dinonaktifkan.');
|
||||
$this->redirect(APP_URL . '/admin/users');
|
||||
}
|
||||
|
||||
public function rumahIbadah(): void {
|
||||
$data = $this->db->fetchAll("
|
||||
SELECT ri.*, ji.nama as jenis, k.nama as kelurahan, kec.nama as kecamatan,
|
||||
u.nama as pengurus_nama,
|
||||
(SELECT COUNT(*) FROM warga_miskin w WHERE w.id_rumah_ibadah=ri.id AND w.status='aktif') 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 users u ON ri.id_user = u.id
|
||||
ORDER BY kec.nama, k.nama, ri.nama
|
||||
");
|
||||
$jenis = $this->db->fetchAll("SELECT * FROM jenis_rumah_ibadah");
|
||||
$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");
|
||||
$pengurusAvail = $this->db->fetchAll("SELECT u.id, u.nama FROM users u LEFT JOIN rumah_ibadah ri ON ri.id_user=u.id WHERE u.id_role=3 AND ri.id IS NULL");
|
||||
|
||||
$this->view('admin.rumah_ibadah', [
|
||||
'title' => 'Manajemen Rumah Ibadah',
|
||||
'data' => $data,
|
||||
'jenis' => $jenis,
|
||||
'kelurahan' => $kelurahan,
|
||||
'pengurusAvail' => $pengurusAvail,
|
||||
'user' => Auth::user()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
public function storeRumahIbadah(): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/admin/rumah-ibadah');
|
||||
|
||||
$foto = null;
|
||||
if (!empty($_FILES['foto']['name'])) {
|
||||
$foto = $this->uploadFile($_FILES['foto'], 'rumah_ibadah');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id_user' => !empty($_POST['id_user']) ? (int)$_POST['id_user'] : null,
|
||||
'id_kelurahan' => (int)$_POST['id_kelurahan'],
|
||||
'id_jenis' => (int)$_POST['id_jenis'],
|
||||
'nama' => trim($_POST['nama']),
|
||||
'alamat' => trim($_POST['alamat']),
|
||||
'lat' => (float)$_POST['lat'],
|
||||
'lng' => (float)$_POST['lng'],
|
||||
'radius_meter' => (int)($_POST['radius_meter'] ?? 500),
|
||||
'nama_ketua' => trim($_POST['nama_ketua'] ?? ''),
|
||||
'no_hp_pengurus'=> trim($_POST['no_hp_pengurus'] ?? ''),
|
||||
'foto' => $foto,
|
||||
'is_active' => 1
|
||||
];
|
||||
|
||||
$this->db->insert('rumah_ibadah', $data);
|
||||
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'superadmin', 'Tambah Rumah Ibadah', 'rumah_ibadah', 'Menambah: ' . $data['nama']);
|
||||
$this->flash('success', 'Rumah ibadah berhasil ditambahkan.');
|
||||
$this->redirect(APP_URL . '/admin/rumah-ibadah');
|
||||
}
|
||||
|
||||
public function updateRumahIbadah(int $id): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/admin/rumah-ibadah');
|
||||
|
||||
$data = [
|
||||
'id_user' => !empty($_POST['id_user']) ? (int)$_POST['id_user'] : null,
|
||||
'id_kelurahan' => (int)$_POST['id_kelurahan'],
|
||||
'id_jenis' => (int)$_POST['id_jenis'],
|
||||
'nama' => trim($_POST['nama']),
|
||||
'alamat' => trim($_POST['alamat']),
|
||||
'lat' => (float)$_POST['lat'],
|
||||
'lng' => (float)$_POST['lng'],
|
||||
'radius_meter' => (int)($_POST['radius_meter'] ?? 500),
|
||||
'nama_ketua' => trim($_POST['nama_ketua'] ?? ''),
|
||||
'no_hp_pengurus'=> trim($_POST['no_hp_pengurus'] ?? ''),
|
||||
'is_active' => (int)$_POST['is_active']
|
||||
];
|
||||
if (!empty($_FILES['foto']['name'])) {
|
||||
$data['foto'] = $this->uploadFile($_FILES['foto'], 'rumah_ibadah');
|
||||
}
|
||||
|
||||
$this->db->update('rumah_ibadah', $data, 'id = ?', [$id]);
|
||||
$this->flash('success', 'Rumah ibadah berhasil diperbarui.');
|
||||
$this->redirect(APP_URL . '/admin/rumah-ibadah');
|
||||
}
|
||||
|
||||
public function deleteRumahIbadah(int $id): void {
|
||||
$this->db->update('rumah_ibadah', ['is_active' => 0], 'id = ?', [$id]);
|
||||
$this->flash('success', 'Rumah ibadah berhasil dinonaktifkan.');
|
||||
$this->redirect(APP_URL . '/admin/rumah-ibadah');
|
||||
}
|
||||
|
||||
public function wilayah(): void {
|
||||
$kecamatan = $this->db->fetchAll("SELECT k.*, COUNT(kel.id) as jml_kel FROM kecamatan k LEFT JOIN kelurahan kel ON kel.id_kecamatan=k.id GROUP BY k.id ORDER BY k.nama");
|
||||
$kelurahan = $this->db->fetchAll("SELECT kel.*, kec.nama as kecamatan FROM kelurahan kel JOIN kecamatan kec ON kel.id_kecamatan=kec.id ORDER BY kec.nama, kel.nama");
|
||||
$this->view('admin.wilayah', [
|
||||
'title' => 'Manajemen Wilayah',
|
||||
'kecamatan' => $kecamatan,
|
||||
'kelurahan' => $kelurahan,
|
||||
'user' => Auth::user()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
public function storeKecamatan(): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/admin/wilayah');
|
||||
$this->db->insert('kecamatan', [
|
||||
'nama' => trim($_POST['nama']),
|
||||
'kode' => trim($_POST['kode'] ?? ''),
|
||||
'lat' => (float)($_POST['lat'] ?? 0),
|
||||
'lng' => (float)($_POST['lng'] ?? 0)
|
||||
]);
|
||||
$this->flash('success', 'Kecamatan berhasil ditambahkan.');
|
||||
$this->redirect(APP_URL . '/admin/wilayah');
|
||||
}
|
||||
|
||||
public function storeKelurahan(): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/admin/wilayah');
|
||||
$this->db->insert('kelurahan', [
|
||||
'id_kecamatan' => (int)$_POST['id_kecamatan'],
|
||||
'nama' => trim($_POST['nama']),
|
||||
'kode' => trim($_POST['kode'] ?? ''),
|
||||
'lat' => (float)($_POST['lat'] ?? 0),
|
||||
'lng' => (float)($_POST['lng'] ?? 0)
|
||||
]);
|
||||
$this->flash('success', 'Kelurahan berhasil ditambahkan.');
|
||||
$this->redirect(APP_URL . '/admin/wilayah');
|
||||
}
|
||||
|
||||
public function kategoriBantuan(): void {
|
||||
$data = $this->db->fetchAll("SELECT * FROM kategori_bantuan ORDER BY nama");
|
||||
$this->view('admin.kategori_bantuan', [
|
||||
'title' => 'Manajemen Kategori Bantuan',
|
||||
'data' => $data,
|
||||
'user' => Auth::user()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
public function storeKategoriBantuan(): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/admin/kategori-bantuan');
|
||||
$this->db->insert('kategori_bantuan', [
|
||||
'nama' => trim($_POST['nama']),
|
||||
'deskripsi' => trim($_POST['deskripsi'] ?? ''),
|
||||
'satuan' => trim($_POST['satuan'] ?? ''),
|
||||
'is_active' => 1
|
||||
]);
|
||||
$this->flash('success', 'Kategori bantuan berhasil ditambahkan.');
|
||||
$this->redirect(APP_URL . '/admin/kategori-bantuan');
|
||||
}
|
||||
|
||||
public function updateKategoriBantuan(int $id): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/admin/kategori-bantuan');
|
||||
$this->db->update('kategori_bantuan', [
|
||||
'nama' => trim($_POST['nama']),
|
||||
'deskripsi' => trim($_POST['deskripsi'] ?? ''),
|
||||
'satuan' => trim($_POST['satuan'] ?? ''),
|
||||
'is_active' => (int)$_POST['is_active']
|
||||
], 'id = ?', [$id]);
|
||||
$this->flash('success', 'Kategori bantuan berhasil diperbarui.');
|
||||
$this->redirect(APP_URL . '/admin/kategori-bantuan');
|
||||
}
|
||||
|
||||
public function monitoring(): void {
|
||||
$warga = $this->db->fetchAll("
|
||||
SELECT w.*, k.nama as kelurahan, kec.nama as kecamatan,
|
||||
ri.nama as rumah_ibadah_nama
|
||||
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'
|
||||
ORDER BY w.level_prioritas DESC, w.skor_prioritas DESC
|
||||
");
|
||||
$this->view('admin.monitoring', [
|
||||
'title' => 'Monitoring Data Kemiskinan',
|
||||
'warga' => $warga,
|
||||
'user' => Auth::user()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
public function blindSpot(): void {
|
||||
$data = $this->db->fetchAll("
|
||||
SELECT w.*, k.nama as kelurahan, kec.nama as kecamatan
|
||||
FROM warga_miskin w
|
||||
JOIN kelurahan k ON w.id_kelurahan = k.id
|
||||
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
||||
WHERE w.is_blind_spot = 1 AND w.status = 'aktif'
|
||||
ORDER BY w.level_prioritas DESC
|
||||
");
|
||||
$this->view('admin.blind_spot', [
|
||||
'title' => 'Monitoring Blind Spot',
|
||||
'data' => $data,
|
||||
'user' => Auth::user()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
public function aktivitas(): void {
|
||||
$data = $this->db->fetchAll("
|
||||
SELECT * FROM aktivitas_sistem ORDER BY created_at DESC LIMIT 200
|
||||
");
|
||||
$this->view('admin.aktivitas', [
|
||||
'title' => 'Aktivitas Sistem',
|
||||
'data' => $data,
|
||||
'user' => Auth::user()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
// API: Stats untuk dashboard
|
||||
public function apiStats(): void {
|
||||
$data = $this->db->fetchOne("
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM warga_miskin WHERE status='aktif') as total_warga,
|
||||
(SELECT COUNT(*) FROM warga_miskin WHERE status='aktif' AND level_prioritas='Tinggi') as prioritas_tinggi,
|
||||
(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' AND MONTH(tanggal_penyaluran)=MONTH(CURDATE())) as bantuan_bulan_ini,
|
||||
(SELECT COUNT(*) FROM laporan_masyarakat WHERE status='menunggu') as laporan_pending
|
||||
");
|
||||
$this->json(['success' => true, 'data' => $data]);
|
||||
}
|
||||
|
||||
// API: Data peta
|
||||
public function apiPeta(): void {
|
||||
$warga = $this->db->fetchAll("
|
||||
SELECT w.id, w.nama, w.lat, w.lng, w.status_kemiskinan, w.level_prioritas,
|
||||
w.is_blind_spot, 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.status = 'aktif'
|
||||
");
|
||||
$ri = $this->db->fetchAll("
|
||||
SELECT ri.id, ri.nama, ri.lat, ri.lng, ri.radius_meter, ri.alamat,
|
||||
ji.nama as jenis, k.nama as kelurahan
|
||||
FROM rumah_ibadah ri
|
||||
JOIN jenis_rumah_ibadah ji ON ri.id_jenis = ji.id
|
||||
JOIN kelurahan k ON ri.id_kelurahan = k.id
|
||||
WHERE ri.is_active = 1
|
||||
");
|
||||
$laporan = $this->db->fetchAll("
|
||||
SELECT id, kode_laporan, nama_warga, lat, lng, status, created_at
|
||||
FROM laporan_masyarakat WHERE lat != 0 AND lng != 0
|
||||
AND status IN ('menunggu','diproses')
|
||||
");
|
||||
$this->json(['warga' => $warga, 'rumah_ibadah' => $ri, 'laporan' => $laporan]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
// app/controllers/AuthController.php
|
||||
|
||||
class AuthController extends Controller {
|
||||
|
||||
public function showLogin(): void {
|
||||
Auth::redirectIfLoggedIn();
|
||||
$this->view('auth.login', ['title' => 'Login - SINERGI'], null);
|
||||
}
|
||||
|
||||
public function doLogin(): void {
|
||||
if (!$this->verifyCsrf()) {
|
||||
$this->flash('error', 'Token tidak valid.');
|
||||
$this->redirect(APP_URL . '/login');
|
||||
}
|
||||
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
$this->flash('error', 'Username dan password wajib diisi.');
|
||||
$this->redirect(APP_URL . '/login');
|
||||
}
|
||||
|
||||
if (Auth::attempt($username, $password)) {
|
||||
$this->redirect(Auth::getDashboardUrl());
|
||||
} else {
|
||||
$this->flash('error', 'Username atau password salah.');
|
||||
$this->redirect(APP_URL . '/login');
|
||||
}
|
||||
}
|
||||
|
||||
public function logout(): void {
|
||||
Auth::logout();
|
||||
$this->redirect(APP_URL . '/login');
|
||||
}
|
||||
|
||||
public function forbidden(): void {
|
||||
http_response_code(403);
|
||||
$this->view('public.403', ['title' => 'Akses Ditolak'], null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,617 @@
|
||||
<?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]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
// app/controllers/PublicController.php
|
||||
|
||||
class PublicController extends Controller {
|
||||
|
||||
public function beranda(): void {
|
||||
$stats = $this->db->fetchOne("
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM warga_miskin WHERE status = 'aktif') as total_warga,
|
||||
(SELECT COUNT(*) FROM rumah_ibadah WHERE is_active = 1) as total_ri,
|
||||
(SELECT COUNT(*) FROM laporan_masyarakat) as total_laporan,
|
||||
(SELECT COUNT(*) FROM histori_bantuan WHERE status = 'disalurkan') as total_bantuan,
|
||||
(SELECT COUNT(*) FROM warga_miskin WHERE is_blind_spot = 1) as total_blind_spot
|
||||
");
|
||||
$this->view('public.beranda', ['title' => 'SINERGI - Sistem GIS Penanganan Kemiskinan', 'stats' => $stats], 'public');
|
||||
}
|
||||
|
||||
public function tentang(): void {
|
||||
$this->view('public.tentang', ['title' => 'Tentang Sistem - SINERGI'], 'public');
|
||||
}
|
||||
|
||||
public function kontak(): void {
|
||||
$this->view('public.kontak', ['title' => 'Kontak - SINERGI'], 'public');
|
||||
}
|
||||
|
||||
public function lapor(): void {
|
||||
$kecamatan = $this->db->fetchAll("SELECT * FROM kecamatan ORDER BY nama");
|
||||
$this->view('public.lapor', [
|
||||
'title' => 'Lapor Cepat - SINERGI',
|
||||
'kecamatan' => $kecamatan
|
||||
], 'public');
|
||||
}
|
||||
|
||||
public function kirimLaporan(): void {
|
||||
if (!$this->verifyCsrf()) {
|
||||
$this->flash('error', 'Token tidak valid.');
|
||||
$this->redirect(APP_URL . '/lapor');
|
||||
}
|
||||
|
||||
$namaWarga = trim($_POST['nama_warga'] ?? '');
|
||||
$namaPelapor = trim($_POST['nama_pelapor'] ?? '');
|
||||
$noHp = trim($_POST['no_hp_pelapor'] ?? '');
|
||||
$alamat = trim($_POST['alamat_warga'] ?? '');
|
||||
$idKelurahan = (int)($_POST['id_kelurahan'] ?? 0);
|
||||
$lat = (float)($_POST['lat'] ?? 0);
|
||||
$lng = (float)($_POST['lng'] ?? 0);
|
||||
$deskripsi = trim($_POST['deskripsi'] ?? '');
|
||||
|
||||
if (!$namaWarga || !$namaPelapor || !$alamat || !$idKelurahan || !$lat || !$lng) {
|
||||
$this->flash('error', 'Mohon lengkapi semua data yang wajib diisi.');
|
||||
$this->redirect(APP_URL . '/lapor');
|
||||
}
|
||||
|
||||
$foto = null;
|
||||
if (!empty($_FILES['foto']['name'])) {
|
||||
$foto = $this->uploadFile($_FILES['foto'], 'laporan');
|
||||
}
|
||||
|
||||
$kode = generateKodeLaporan();
|
||||
|
||||
$this->db->insert('laporan_masyarakat', [
|
||||
'kode_laporan' => $kode,
|
||||
'nama_pelapor' => $namaPelapor,
|
||||
'no_hp_pelapor' => $noHp,
|
||||
'nama_warga' => $namaWarga,
|
||||
'alamat_warga' => $alamat,
|
||||
'id_kelurahan' => $idKelurahan,
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'deskripsi' => $deskripsi,
|
||||
'foto' => $foto,
|
||||
'status' => 'menunggu'
|
||||
]);
|
||||
|
||||
$this->flash('success', 'Laporan berhasil dikirim! Simpan kode laporan Anda.', ['kode_laporan' => $kode]);
|
||||
$this->redirect(APP_URL . '/lapor');
|
||||
}
|
||||
|
||||
public function cekLaporan(): void {
|
||||
$this->view('public.cek_laporan', ['title' => 'Cek Status Laporan - SINERGI'], 'public');
|
||||
}
|
||||
|
||||
public function proseCekLaporan(): void {
|
||||
$kode = strtoupper(trim($_POST['kode_laporan'] ?? ''));
|
||||
$laporan = null;
|
||||
if ($kode) {
|
||||
$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.kode_laporan = ?",
|
||||
[$kode]
|
||||
);
|
||||
}
|
||||
$this->view('public.cek_laporan', [
|
||||
'title' => 'Cek Status Laporan - SINERGI',
|
||||
'laporan' => $laporan,
|
||||
'kode' => $kode
|
||||
], 'public');
|
||||
}
|
||||
|
||||
// API: Ambil kelurahan berdasarkan kecamatan
|
||||
public function apiKelurahan(int $id): void {
|
||||
$data = $this->db->fetchAll("SELECT id, nama FROM kelurahan WHERE id_kecamatan = ? ORDER BY nama", [$id]);
|
||||
$this->json($data);
|
||||
}
|
||||
|
||||
// API: Data rumah ibadah untuk peta publik
|
||||
public function apiRumahIbadah(): void {
|
||||
$data = $this->db->fetchAll(
|
||||
"SELECT ri.id, ri.nama, ri.lat, ri.lng, ri.radius_meter, ri.alamat,
|
||||
ji.nama as jenis, k.nama as kelurahan
|
||||
FROM rumah_ibadah ri
|
||||
JOIN jenis_rumah_ibadah ji ON ri.id_jenis = ji.id
|
||||
JOIN kelurahan k ON ri.id_kelurahan = k.id
|
||||
WHERE ri.is_active = 1"
|
||||
);
|
||||
$this->json($data);
|
||||
}
|
||||
|
||||
// API: Data warga untuk peta publik (tanpa data sensitif)
|
||||
public function apiWargaPeta(): void {
|
||||
$data = $this->db->fetchAll(
|
||||
"SELECT w.lat, w.lng, w.status_kemiskinan, w.level_prioritas, w.is_blind_spot,
|
||||
k.nama as kelurahan, kec.nama as kecamatan
|
||||
FROM warga_miskin w
|
||||
JOIN kelurahan k ON w.id_kelurahan = k.id
|
||||
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
||||
WHERE w.status = 'aktif'"
|
||||
);
|
||||
$this->json($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
// app/controllers/RumahIbadahController.php
|
||||
|
||||
class RumahIbadahController extends Controller {
|
||||
|
||||
private array $ri;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
Auth::require('rumah_ibadah');
|
||||
$user = Auth::user();
|
||||
if (!$user['rumah_ibadah_id']) {
|
||||
// Belum terhubung ke rumah ibadah
|
||||
die('<div style="padding:40px;font-family:sans-serif;"><h2>Akun belum terhubung ke Rumah Ibadah</h2><p>Hubungi Super Admin untuk menautkan akun Anda.</p></div>');
|
||||
}
|
||||
$this->ri = $this->db->fetchOne(
|
||||
"SELECT ri.*, ji.nama as jenis, k.nama as kelurahan, kec.nama as kecamatan
|
||||
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
|
||||
WHERE ri.id = ?",
|
||||
[$user['rumah_ibadah_id']]
|
||||
);
|
||||
}
|
||||
|
||||
public function dashboard(): void {
|
||||
$riId = $this->ri['id'];
|
||||
|
||||
$stats = $this->db->fetchOne("
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM warga_miskin WHERE id_rumah_ibadah=? AND status='aktif') as total_warga,
|
||||
(SELECT COUNT(*) FROM warga_miskin WHERE id_rumah_ibadah=? AND level_prioritas='Tinggi' AND status='aktif') as prioritas_tinggi,
|
||||
(SELECT COUNT(*) FROM warga_miskin WHERE id_rumah_ibadah=? AND level_prioritas='Sedang' AND status='aktif') as prioritas_sedang,
|
||||
(SELECT COUNT(*) FROM histori_bantuan WHERE id_rumah_ibadah=? AND status='disalurkan') as total_bantuan,
|
||||
(SELECT COUNT(*) FROM histori_bantuan WHERE id_rumah_ibadah=? AND status='disalurkan' AND MONTH(tanggal_penyaluran)=MONTH(CURDATE())) as bantuan_bulan_ini,
|
||||
(SELECT COUNT(*) FROM warga_miskin WHERE id_rumah_ibadah=? AND status_kemiskinan='Miskin Ekstrem' AND status='aktif') as ekstrem
|
||||
", array_fill(0, 6, $riId));
|
||||
|
||||
$wargaList = $this->db->fetchAll("
|
||||
SELECT w.id, w.nama, w.status_kemiskinan, w.level_prioritas, w.skor_prioritas,
|
||||
w.penghasilan_bulanan, w.jumlah_tanggungan, w.lat, w.lng,
|
||||
w.is_lansia, w.is_disabilitas, k.nama as kelurahan
|
||||
FROM warga_miskin w
|
||||
JOIN kelurahan k ON w.id_kelurahan = k.id
|
||||
WHERE w.id_rumah_ibadah = ? AND w.status = 'aktif'
|
||||
ORDER BY w.skor_prioritas DESC
|
||||
LIMIT 20
|
||||
", [$riId]);
|
||||
|
||||
$bantuanTerakhir = $this->db->fetchAll("
|
||||
SELECT hb.*, kb.nama as kategori, wm.nama as warga
|
||||
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
|
||||
WHERE hb.id_rumah_ibadah = ?
|
||||
ORDER BY hb.tanggal_penyaluran DESC LIMIT 10
|
||||
", [$riId]);
|
||||
|
||||
$this->view('rumah_ibadah.dashboard', [
|
||||
'title' => 'Dashboard Rumah Ibadah',
|
||||
'ri' => $this->ri,
|
||||
'stats' => $stats,
|
||||
'wargaList' => $wargaList,
|
||||
'bantuanTerakhir'=> $bantuanTerakhir,
|
||||
'user' => Auth::user()
|
||||
], 'rumah_ibadah');
|
||||
}
|
||||
|
||||
public function warga(): void {
|
||||
$riId = $this->ri['id'];
|
||||
$level = $_GET['level'] ?? 'semua';
|
||||
$sql = "SELECT w.*, k.nama as kelurahan FROM warga_miskin w
|
||||
JOIN kelurahan k ON w.id_kelurahan = k.id
|
||||
WHERE w.id_rumah_ibadah = ? AND w.status = 'aktif'";
|
||||
$params = [$riId];
|
||||
if ($level !== 'semua') {
|
||||
$sql .= " AND w.level_prioritas = ?";
|
||||
$params[] = $level;
|
||||
}
|
||||
$sql .= " ORDER BY w.skor_prioritas DESC";
|
||||
|
||||
$data = $this->db->fetchAll($sql, $params);
|
||||
$this->view('rumah_ibadah.warga', [
|
||||
'title' => 'Daftar Warga Binaan',
|
||||
'data' => $data,
|
||||
'ri' => $this->ri,
|
||||
'level' => $level,
|
||||
'user' => Auth::user()
|
||||
], 'rumah_ibadah');
|
||||
}
|
||||
|
||||
public function detailWarga(int $id): void {
|
||||
$warga = $this->db->fetchOne("
|
||||
SELECT w.*, k.nama as kelurahan, kec.nama as kecamatan
|
||||
FROM warga_miskin w
|
||||
JOIN kelurahan k ON w.id_kelurahan = k.id
|
||||
JOIN kecamatan kec ON k.id_kecamatan = kec.id
|
||||
WHERE w.id = ? AND w.id_rumah_ibadah = ?
|
||||
", [$id, $this->ri['id']]);
|
||||
|
||||
if (!$warga) {
|
||||
$this->flash('error', 'Warga tidak ditemukan dalam binaan Anda.');
|
||||
$this->redirect(APP_URL . '/rumah-ibadah/warga');
|
||||
}
|
||||
|
||||
$bantuan = $this->db->fetchAll("
|
||||
SELECT hb.*, kb.nama as kategori
|
||||
FROM histori_bantuan hb
|
||||
JOIN kategori_bantuan kb ON hb.id_kategori_bantuan = kb.id
|
||||
WHERE hb.id_warga = ?
|
||||
ORDER BY hb.tanggal_penyaluran DESC
|
||||
", [$id]);
|
||||
|
||||
$this->view('rumah_ibadah.detail_warga', [
|
||||
'title' => 'Detail Warga',
|
||||
'warga' => $warga,
|
||||
'ri' => $this->ri,
|
||||
'bantuan' => $bantuan,
|
||||
'user' => Auth::user()
|
||||
], 'rumah_ibadah');
|
||||
}
|
||||
|
||||
public function prioritas(): void {
|
||||
$data = $this->db->fetchAll("
|
||||
SELECT w.*, k.nama as kelurahan,
|
||||
(SELECT COUNT(*) FROM histori_bantuan WHERE id_warga=w.id AND status='disalurkan') as jml_bantuan,
|
||||
(SELECT MAX(tanggal_penyaluran) FROM histori_bantuan WHERE id_warga=w.id AND status='disalurkan') as bantuan_terakhir
|
||||
FROM warga_miskin w
|
||||
JOIN kelurahan k ON w.id_kelurahan = k.id
|
||||
WHERE w.id_rumah_ibadah = ? AND w.status = 'aktif'
|
||||
ORDER BY w.skor_prioritas DESC, w.status_kemiskinan
|
||||
", [$this->ri['id']]);
|
||||
|
||||
$this->view('rumah_ibadah.prioritas', [
|
||||
'title' => 'Prioritas Bantuan',
|
||||
'data' => $data,
|
||||
'ri' => $this->ri,
|
||||
'user' => Auth::user()
|
||||
], 'rumah_ibadah');
|
||||
}
|
||||
|
||||
public function bantuan(): void {
|
||||
$data = $this->db->fetchAll("
|
||||
SELECT hb.*, kb.nama as kategori, wm.nama as warga
|
||||
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
|
||||
WHERE hb.id_rumah_ibadah = ?
|
||||
ORDER BY hb.tanggal_penyaluran DESC
|
||||
", [$this->ri['id']]);
|
||||
|
||||
$this->view('rumah_ibadah.bantuan', [
|
||||
'title' => 'Histori Bantuan',
|
||||
'data' => $data,
|
||||
'ri' => $this->ri,
|
||||
'user' => Auth::user()
|
||||
], 'rumah_ibadah');
|
||||
}
|
||||
|
||||
public function formBantuan(): void {
|
||||
$wargaList = $this->db->fetchAll("
|
||||
SELECT id, nama, status_kemiskinan, level_prioritas, skor_prioritas
|
||||
FROM warga_miskin WHERE id_rumah_ibadah = ? AND status = 'aktif'
|
||||
ORDER BY skor_prioritas DESC
|
||||
", [$this->ri['id']]);
|
||||
|
||||
$kategori = $this->db->fetchAll("SELECT * FROM kategori_bantuan WHERE is_active = 1 ORDER BY nama");
|
||||
|
||||
$this->view('rumah_ibadah.form_bantuan', [
|
||||
'title' => 'Tambah Bantuan',
|
||||
'wargaList' => $wargaList,
|
||||
'kategori' => $kategori,
|
||||
'ri' => $this->ri,
|
||||
'user' => Auth::user()
|
||||
], 'rumah_ibadah');
|
||||
}
|
||||
|
||||
public function storeBantuan(): void {
|
||||
if (!$this->verifyCsrf()) $this->redirect(APP_URL . '/rumah-ibadah/bantuan');
|
||||
|
||||
// Pastikan warga memang binaan rumah ibadah ini
|
||||
$idWarga = (int)$_POST['id_warga'];
|
||||
$warga = $this->db->fetchOne("SELECT id FROM warga_miskin WHERE id = ? AND id_rumah_ibadah = ?", [$idWarga, $this->ri['id']]);
|
||||
if (!$warga) {
|
||||
$this->flash('error', 'Warga tidak valid.');
|
||||
$this->redirect(APP_URL . '/rumah-ibadah/bantuan/tambah');
|
||||
}
|
||||
|
||||
$foto = null;
|
||||
if (!empty($_FILES['foto_bukti']['name'])) {
|
||||
$foto = $this->uploadFile($_FILES['foto_bukti'], 'bantuan');
|
||||
}
|
||||
|
||||
$this->db->insert('histori_bantuan', [
|
||||
'id_warga' => $idWarga,
|
||||
'id_rumah_ibadah' => $this->ri['id'],
|
||||
'id_kategori_bantuan' => (int)$_POST['id_kategori_bantuan'],
|
||||
'id_user_pengurus' => Auth::id(),
|
||||
'kode_bantuan' => generateKodeBantuan(),
|
||||
'tanggal_penyaluran' => $_POST['tanggal_penyaluran'],
|
||||
'jumlah' => (float)($_POST['jumlah'] ?? 0),
|
||||
'satuan' => trim($_POST['satuan'] ?? ''),
|
||||
'deskripsi' => trim($_POST['deskripsi'] ?? ''),
|
||||
'foto_bukti' => $foto,
|
||||
'status' => 'disalurkan'
|
||||
]);
|
||||
|
||||
Auth::logActivity(Auth::id(), Auth::user()['nama'], 'rumah_ibadah', 'Salurkan Bantuan', 'bantuan', "Bantuan untuk warga ID $idWarga");
|
||||
$this->flash('success', 'Bantuan berhasil dicatat.');
|
||||
$this->redirect(APP_URL . '/rumah-ibadah/bantuan');
|
||||
}
|
||||
|
||||
public function apiPeta(): void {
|
||||
$riId = $this->ri['id'];
|
||||
$warga = $this->db->fetchAll("
|
||||
SELECT w.id, w.nama, w.lat, w.lng, w.status_kemiskinan, w.level_prioritas, w.skor_prioritas
|
||||
FROM warga_miskin w WHERE w.id_rumah_ibadah = ? AND w.status = 'aktif'
|
||||
", [$riId]);
|
||||
$this->json([
|
||||
'rumah_ibadah' => $this->ri,
|
||||
'warga' => $warga
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
<?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]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
// app/controllers/WalikotaController.php
|
||||
|
||||
class WalikotaController extends Controller {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
Auth::require('walikota');
|
||||
}
|
||||
|
||||
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 YEAR(tanggal_penyaluran)=YEAR(CURDATE())) as bantuan_tahun_ini,
|
||||
(SELECT COUNT(*) FROM laporan_masyarakat) as total_laporan,
|
||||
(SELECT COUNT(*) FROM laporan_masyarakat WHERE status='terverifikasi') as laporan_terverifikasi
|
||||
");
|
||||
|
||||
$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,
|
||||
COUNT(DISTINCT ri.id) as total_ri
|
||||
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'
|
||||
LEFT JOIN rumah_ibadah ri ON ri.id_kelurahan = k.id AND ri.is_active=1
|
||||
GROUP BY kec.id, kec.nama
|
||||
ORDER BY total DESC
|
||||
");
|
||||
|
||||
$topRi = $this->db->fetchAll("
|
||||
SELECT ri.nama, ji.nama as jenis, kec.nama as kecamatan,
|
||||
COUNT(DISTINCT wm.id) as jml_warga,
|
||||
COUNT(DISTINCT hb.id) as jml_bantuan
|
||||
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, kec.nama
|
||||
ORDER BY jml_bantuan DESC LIMIT 5
|
||||
");
|
||||
|
||||
$trenTahunan = $this->db->fetchAll("
|
||||
SELECT YEAR(tanggal_penyaluran) as tahun, COUNT(*) as bantuan,
|
||||
COUNT(DISTINCT id_warga) as warga_terbantu
|
||||
FROM histori_bantuan WHERE status='disalurkan'
|
||||
GROUP BY tahun ORDER BY tahun
|
||||
");
|
||||
|
||||
$trenBulanan = $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->view('walikota.dashboard', [
|
||||
'title' => 'Dashboard Eksekutif - Wali Kota',
|
||||
'stats' => $stats,
|
||||
'perKecamatan' => $perKecamatan,
|
||||
'topRi' => $topRi,
|
||||
'trenTahunan' => $trenTahunan,
|
||||
'trenBulanan' => $trenBulanan,
|
||||
'user' => Auth::user()
|
||||
], 'walikota');
|
||||
}
|
||||
|
||||
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
|
||||
");
|
||||
$this->json(['perKecamatan' => $perKec]);
|
||||
}
|
||||
|
||||
public function apiPeta(): void {
|
||||
$warga = $this->db->fetchAll("
|
||||
SELECT lat, lng, status_kemiskinan, is_blind_spot,
|
||||
CASE status_kemiskinan
|
||||
WHEN 'Miskin Ekstrem' THEN 1.0
|
||||
WHEN 'Miskin' THEN 0.7
|
||||
ELSE 0.4
|
||||
END as intensity
|
||||
FROM warga_miskin WHERE status='aktif' AND lat != 0
|
||||
");
|
||||
$ri = $this->db->fetchAll("
|
||||
SELECT ri.nama, ri.lat, ri.lng, ri.radius_meter, ji.nama as jenis
|
||||
FROM rumah_ibadah ri
|
||||
JOIN jenis_rumah_ibadah ji ON ri.id_jenis = ji.id
|
||||
WHERE ri.is_active = 1
|
||||
");
|
||||
$this->json(['warga' => $warga, 'rumah_ibadah' => $ri]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user