418 lines
18 KiB
PHP
418 lines
18 KiB
PHP
<?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]);
|
|
}
|
|
} |