15 KiB
Admin rebuild (CI4 + TailAdmin + API)
Ini bukan migrasi modul admin HMVC CodeIgniter 3. Panel admin dibangun ulang di CodeIgniter 4 dengan UI Tailwind (aset TailAdmin di public/assets/tailadmin/) dan tanpa query database langsung dari controller web admin. Semua data modul Pegawai, Presensi, Laporan ringkasan, Dashboard beranda, dan Cuti diambil lewat API internal (/api/admin/* dan untuk profil/berita tetap /api/mobile/*).
Prinsip
- Controller di
app/Controllers/Admin/hanya memanggilApiClient→ HTTP ke aplikasi yang sama. - Tidak menyalin HMVC CI3, tidak memakai Grocery CRUD.
- Akses database untuk fitur admin terpusat di
app/Services/Admin/AdminApiService.php+ model CI4 (PegawaiModel, dll.). - Respons API diseragamkan:
{ "status": 0|1, "pesan": "...", "data": ... }(selaras API mobile).
RBAC (peran & fitur)
- Konfigurasi:
app/Config/AdminAccess.phpmemetakan nama fitur → daftar slug grup Ion yang boleh. Daftar kosong artinya semua admin yang sudah login (setelahauthadmin). - Helper:
app/Helpers/rbac_helper.php—hasRole(),hasAnyRole(),canAccess($feature). Membacaadmin_ion_groupsdari sesi (nama grup, perbandingan case-insensitive). - Kapan aturan Ion dipakai: jika
admin_auth_source === 'admin_users'. Login pegawai saja (tanpa grup Ion di sesi) mempertahankan perilaku lama:canAccessmengizinkan semua fitur yang punya daftar grup (kompatibel token proxy). Ini selaras kebutuhan token API pegawai untuk semua endpoint admin. - Jika
admin_userstetapi grup kosong: fitur yang membutuhkan grup tertentu → ditolak (menu disembunyikan,enforceAccessredirect dengan flash). - Controller web:
BaseAdminController::enforceAccess('nama_fitur')di awal aksi; gagal → redirect ke/admindengan flash error, atau JSON 403 untuk AJAX. - View: hanya memanggil
canAccess('...')— tanpa menuliskan nama peran di view; semua mapping ada diAdminAccess.
Pemetaan fitur ↔ grup (sama ci_bootstrap.php CI3)
Fitur (canAccess) |
Grup yang boleh |
|---|---|
dashboard |
(semua login admin) |
presensi |
(semua) |
perusahaan |
(semua) |
pegawai |
webmaster, hrd |
cuti |
webmaster, hrd |
laporan |
webmaster, hrd |
panel |
webmaster |
utilitas |
webmaster |
apk_link |
webmaster, penyelenggara, operator_cabang, operator_ranting, operator_sekolah, admin_soal, operator_soal |
references |
(semua login admin) — referensi form pegawai |
Autentikasi web
GET/POST /admin/login—Auth::attempturutannya:- Pegawai:
POST /api/mobile/login(MD5 password, tabelpegawai). - Jika gagal:
admin_users(Ion Auth CI3):password_verifyterhadap bcrypt/hash di kolompassword,active = 1. Jika sukses, sistem menerbitkan token API dengan memakai pegawai proxy (lihat bawah) dan menyimpan token yang sama di sesiadmin_mobile_token. Sesi juga menyimpanadmin_auth_source=pegawai|admin_users.
- Pegawai:
- Data Ion Auth di DB:
admin_users,admin_groups,admin_users_groups. Loginadmin_userswajib punya minimal satu baris diadmin_users_groupsbila tabel itu ada — tanpa grup, login ditolak. - Proxy token (login
admin_users): API hanya mengenali token dipegawai. Setelah Ion Auth sukses, token ditulis ke pegawai proxy:ADMIN_LOGIN_PROXY_PEGAWAI_ID(.env), lalu pegawai pertamasuper_akses = 'true', laluid_pegawaiterkecil. Untuk akun hanya HRD, bisa setADMIN_LOGIN_PROXY_PEGAWAI_ID_HRD. - Sesi menyimpan
admin_ion_user_iddanadmin_ion_groups(nama grup) bila sumberadmin_users— dipakai RBAC + header. GET /admin/logout— di luar filter.- Rute
admin/*lain memakai filterauthadmin: tanpa token sesi → redirect ke login (atau JSON 401 untuk AJAX).
API admin (/api/admin/*)
Parameter token (sama dengan admin_mobile_token di sesi panel) tetap dikirim lewat query GET, field POST, atau header X-Admin-Token — struktur token untuk klien mobile tidak diubah (/api/mobile/* tidak tersentuh).
Admin API Security
- Sesi panel wajib: setiap
/api/admin/*memverifikasi bahwa permintaan membawa cookie sesi yang valid dan berisiadmin_mobile_token, serta bahwatokendi URL/body/header sama persis dengan nilai di sesi. Tanpa itu, respons401dengan JSON{ "status": 0, "pesan": "Unauthorized" }— sehingga curl/Postman hanya dengan?token=pegawai (tanpa cookie login admin) ditolak. - RBAC di API: setelah sesi valid,
BaseAdminApiController::requireAdminApiAccess('fitur')memanggilcanAccess()(samaConfig\AdminAccess+admin_ion_groupsseperti UI). Gagal →403dengan{ "status": 0, "pesan": "Forbidden" }. - Penerusan cookie internal:
ApiClient::getAdmin/postAdminmeneruskan headerCookiedari permintaan browser ke hit HTTP internal, dan memanggilsession_write_close()sebelum CURL agar menghindari deadlock file session (sub-request dapat membuka sesi yang sama). - Logging teks: percobaan ditolak dicatat ke log aplikasi (
log_message('warning', ...)) dengan alasan singkat, alamat IP, path URI, dan timestamp ISO-8601; lihat juga Audit Logging System di bawah untuk rekaman DB. - Token admin terpisah: tidak wajib; tidak mengganti model sesi saat ini.
Audit Logging System
- Tabel:
admin_activity_logs(migrasiapp/Database/Migrations/2026-04-18-140000_CreateAdminActivityLogs.php). Kolom:id,admin_user,action,endpoint,payload(JSON teks),ip_address,user_agent,auth_source,roles_json(JSON grup Ion dari sesi),outcome(success|unauthorized|forbidden),created_at. - Layanan:
app/Services/AdminAuditService.php— metodelog(string $action, array $payload = []). Menyisipkan konteks HTTP (_http), panel (_session_panel: sumber auth, grup Ion, indikator token admin), ringkasan actor pegawai (dari token API), dan payload bisnis. Nilai sensitif (password,token, dll.) diganti[redacted]. - Integrasi API: setelah
requireAdminApiAccesssukses, setiap endpoint diapp/Controllers/Api/Admin/*memanggilauditAuthorized()diBaseAdminApiControllerdengan nama aksi konsisten (api.admin.cuti.approve,api.admin.pegawai.update, …). Mutasi penting (approve/reject cuti, create/update/delete/reset pegawai) menyertakan ID / field ringkas di payload. - Keamanan: percobaan Unauthorized / Forbidden juga ditulis lewat
log()dengan__outcome=unauthorized|forbiddendan aksiapi.admin.security.unauthorized/api.admin.security.forbidden. - Performa: insert sinkron ringan; payload dibatasi panjang; jika tabel belum ada, tulisan dilewati (debug log) agar tidak memutus deploy.
| Metode | Path | Keterangan |
|---|---|---|
| GET | /api/admin/references |
Dropdown: jabatan, unit_kerja, golongan, kantor, jadwal |
| GET | /api/admin/dashboard |
Statistik beranda + antrian cuti Waiting (20 baris + total) |
| GET | /api/admin/pegawai |
Daftar pegawai + pagination (page, per_page, q) |
| GET | /api/admin/pegawai/{id} |
Detail pegawai |
| POST | /api/admin/pegawai/create |
Tambah pegawai |
| POST | /api/admin/pegawai/update |
Body termasuk id_pegawai |
| POST | /api/admin/pegawai/delete |
Body id_pegawai |
| POST | /api/admin/pegawai/reset_password |
Body id_pegawai |
| GET | /api/admin/presensi |
Daftar presensi (tanggal_dari, tanggal_sampai, page, per_page, q nama/NIP) |
| GET | /api/admin/presensi/{id} |
Detail presensi |
| GET | /api/admin/laporan |
Ringkasan agregat (dari, sampai) |
| GET | /api/admin/cuti |
Daftar cuti (status kosong=semua, Waiting, Approve, Rejected, Cancelled, page, per_page) |
| GET | /api/admin/cuti/{id} |
Detail cuti + daftar file dokumen |
| POST | /api/admin/cuti/approve |
Body id_cuti |
| POST | /api/admin/cuti/reject |
Body id_cuti, alasan_tolak |
| GET | /api/admin/laporan/cuti |
Laporan cuti rentang (dari, sampai) |
| GET/POST | /api/admin/company/* |
Master perusahaan (kantor, unit, golongan, jabatan, berita) |
| GET/POST | `/api/admin/presensi/dilapangan | lembur |
| GET/POST | `/api/admin/panel/users | groups |
| GET/POST | /api/admin/backup* |
Daftar / jalankan / hapus file backup SQL |
Controller API: app/Controllers/Api/Admin/*. Rute di app/Config/Routes.php grup api/admin. Dasar kelas: BaseAdminApiController (sesi + ikatan token + RBAC per fitur).
Rute web admin
| Path | Fungsi |
|---|---|
/admin/login, POST login |
Login |
/admin/logout |
Keluar |
/admin |
Dashboard (API dashboard + profil/berita mobile) |
/admin/pegawai |
Daftar pegawai |
/admin/pegawai/create, POST store |
Tambah |
/admin/pegawai/edit/{id}, POST update/{id} |
Edit |
POST pegawai/delete/{id} |
Hapus |
POST pegawai/reset/{id} |
Reset password |
/admin/presensi |
Daftar + filter tanggal + cari nama/NIP |
/admin/presensi/detail/{id} |
Detail |
/admin/presensi/lapangan … aktivitas |
Master alat presensi (dilapangan, lembur, libur, jadwal, aktivitas) |
/admin/perusahaan/kantor … berita |
CRUD master perusahaan |
/admin/laporan |
Ringkasan + filter tanggal |
/admin/laporan/cuti |
Tabel cuti rentang tanggal |
/admin/panel/users, create, reset/{id} |
Pengguna admin Ion |
/admin/util/backup |
Backup DB (file di writable/admin_db_backup/) |
/admin/cuti |
Daftar cuti + filter status |
/admin/cuti/detail/{id} |
Detail + approve/reject |
POST /admin/cuti/approve/{id} |
Setujui |
POST /admin/cuti/reject/{id} |
Tolak + alasan |
ApiClient & BaseAdminController
getAdmin/postAdmin— ke/api/admin/*dengan token otomatis + penerusanCookieuntuk sesi panel.BaseAdminController::enforceAccess($feature)— RBAC fitur di UI web.
Konfigurasi
app.baseURL di .env harus benar agar panggilan internal CURLRequest berhasil.
Modul vs CI3 — parity & celah
Sudah mendekati CI3 (usable):
- Dashboard beranda: kartu pegawai/presensi/cuti hari ini, bar ringkasan, tabel permohonan cuti Waiting + aksi cepat (bila
canAccess('cuti')). - Cuti admin: daftar, detail, approve/reject (status
Approve/Rejected+alasan_tolak), dokumen diassets/uploads/dokcuti/. - Pegawai, presensi (dengan badge status baris), laporan ringkasan.
Masih berbeda / lanjutan:
- Lingkup data di
AdminApiServicetetap mengikuti baris pegawai yang memilikitoken(proxy); siapa boleh memanggil endpoint kini dijaga sesi + RBAC seperti UI. - Laporan keuangan CI3 (piutang, penjualan, laba, dll.) tidak di-port ke modul admin CI4 ini.
- Cetak khusus seperti PHPExcel di CI3 diganti dengan tabel web + cetak browser / integrasi via endpoint JSON bila diperlukan.
Peta halaman CI3 (discovery)
Daftar lengkap URL, controller, method, peran, dan pemetaan ke CI4 ada di docs/migration/admin-pages-map.md. File itu menjadi acuan agar tidak ada halaman CI3 yang “hilang” dari checklist tanpa sengaja.
UI shell TailAdmin (dashboard)
Tujuan: tampilan konsisten seperti demo TailAdmin (sidebar terang, border gray-200, kartu rounded-2xl, header sticky, tabel dengan header abu), tanpa memuat public/assets/tailadmin/css/style.css mentah di browser — file itu memakai @import "tailwindcss" / @apply dan membutuhkan build npm, bukan CSS siap pakai. Untuk kelas utilitas dari markup PHP, tetap memakai Tailwind CDN di layouts/main.php + layouts/auth.php; aset lokal yang dipakai: Font Awesome (fa-7.1.0-web/css/all.min.css) dan logo brand public/assets/images/bpr-logo.png (sidebar + halaman login).
Layout admin (layouts/main.php):
#admin-shell: flex penuh tinggi viewport, latarbg-gray-50.- Sidebar (
layouts/sidebar.php): lebar 290px, putih, border kanan; menu mengikutici_bootstrap.phpCI3; teks judul memakai kelassidebar-text/nav-section-labelagar bisa disembunyikan saat mode sempit desktop (sidebar-narrow+ localStorage). - Mobile (< lg): sidebar
transform: translateX(-100%); tombol hamburger di header membuka kelasmobile-sidebar-openpada#admin-shell; overlay#sidebar-overlaymenutup panel ketika diketuk. - Desktop: tombol kolom di header men-toggle
sidebar-narrow(lebar sidebar 90px, label disembunyikan). - Header (
layouts/header.php): sticky, flash message, info sesi singkat. - Footer (
layouts/footer.php): strip tipis di bawah konten scroll.
Struktur view modul (rapi):
| Modul | Path view |
|---|---|
| Beranda | app/Views/admin/dashboard/index.php |
| Login | app/Views/admin/auth/login.php |
| Pegawai | app/Views/admin/pegawai/index.php, form.php |
| Presensi | app/Views/admin/presensi/index.php, detail.php |
| Cuti | app/Views/admin/cuti/index.php, detail.php |
| Laporan | app/Views/admin/laporan/index.php, laporan/cuti.php |
| Perusahaan | app/Views/admin/perusahaan/*.php |
| Presensi (alat) | app/Views/admin/presensi/lapangan.php, lembur.php, libur.php, jadwal.php, aktivitas.php |
| Panel | app/Views/admin/panel/users.php, user_create.php, user_reset.php |
| Utilitas | app/Views/admin/util/backup.php |
Semua halaman admin di atas memakai <?= $this->extend('layouts/main') ?> (login memakai layouts/auth).
Halaman selesai vs sisa (web admin)
Selesai (parity operasional harian + UI TailAdmin):
- Beranda / dashboard (
/admin) - Data presensi + detail + pencarian (
/admin/presensi, detail) - Presensi: tugas luar/lapangan, lembur, management jadwal, hari libur, rekaman aktivitas
- Perusahaan: lokasi kerja (kantor), unit kerja, jabatan, golongan, berita/pengumuman
- Data pegawai + form tambah/edit + hapus + reset password (
/admin/pegawai, …) - Data cuti + detail approve/reject (
/admin/cuti, detail) - Ringkasan laporan (
/admin/laporan) + laporan cuti rentang (/admin/laporan/cuti, cetak via browser) - Panel: daftar pengguna admin Ion, tambah pengguna (bukan webmaster), reset password
- Utilitas: daftar backup SQL, jalankan backup, unduh, hapus file (
writable/admin_db_backup/) - Login (
/admin/login)
Belum di-port dari CI3 (sengaja / luar cakupan modul ini):
- Laporan modul keuangan (jatuhtempo, penjualan, laba, cetak terkait)
- Panel: kelola grup (
admin_user_group), halaman profil/ubah password akun admin terpisah (bisa memakai alur pegawai/API) - Utilitas: restore database dari file (hanya backup + unduh + hapus)
- Unggah foto berita lewat form file (CI4 saat ini: isi nama file manual jika file sudah di server)
Catatan parity UX
- Urutan dan label menu utama mengikuti sidemenu /
menuCI3; submenu yang sudah di CI4 memakai tautan aktif dilayouts/sidebar.php. - Tombol primer diseragamkan ke biru (
bg-blue-600) seperti aksen umum TailAdmin; sekunder tetap border gray + putih. - Tabel: header
bg-gray-50, border barisdivide-gray-100, footer pagination dengan stripbg-gray-50/80.