2026-02-08 15:16:35 +07:00
< ? php
namespace App\Http\Controllers ;
use App\Models\User ;
use App\Models\Role ;
use App\Traits\LogsActivity ;
use Illuminate\Http\Request ;
use Illuminate\View\View ;
use Illuminate\Http\RedirectResponse ;
use Illuminate\Support\Facades\Hash ;
use Illuminate\Support\Facades\Gate ; // WAJIB DIIMPORT
use Illuminate\Validation\Rules ;
use App\Models\ActivityLog ;
use Maatwebsite\Excel\Facades\Excel ;
use App\Exports\ActivityLogExport ;
class UserController extends Controller
{
use LogsActivity ;
/**
* Menampilkan daftar user.
* Izin: users (read)
*/
public function index ( Request $request ) : View
{
// GANTI: Otorisasi berdasarkan Matriks Permission
Gate :: authorize ( 'check-access' , [ 'users' , 'read' ]);
$users = User :: query ()
2026-09-23 12:45:02 +07:00
-> with ( 'roleModel' )
2026-02-08 15:16:35 +07:00
-> when ( $request -> search , function ( $query , $search ) {
2026-09-23 12:45:02 +07:00
$query -> where ( function ( $q ) use ( $search ) {
$q -> where ( 'name' , 'like' , " % { $search } % " )
2026-02-08 15:16:35 +07:00
-> orWhere ( 'email' , 'like' , " % { $search } % " );
2026-09-23 12:45:02 +07:00
});
2026-02-08 15:16:35 +07:00
})
-> latest ()
-> paginate ( 10 );
return view ( 'pages.users.index' , compact ( 'users' ));
}
/**
* Form tambah user baru.
* Izin: users (create)
*/
public function create () : View
{
Gate :: authorize ( 'check-access' , [ 'users' , 'create' ]);
$roles = Role :: all ();
return view ( 'pages.users.create' , compact ( 'roles' ));
}
/**
* Simpan user baru.
* Izin: users (create)
*/
public function store ( Request $request ) : RedirectResponse
{
Gate :: authorize ( 'check-access' , [ 'users' , 'create' ]);
$validated = $request -> validate ([
'name' => 'required|string|max:255' ,
'email' => 'required|string|lowercase|email|max:255|unique:users' ,
'phone' => 'nullable|string|max:15' ,
'role_id' => 'required|exists:roles,id' ,
'password' => [ 'required' , 'confirmed' , Rules\Password :: defaults ()],
]);
$role = Role :: find ( $validated [ 'role_id' ]);
$user = User :: create ([
'name' => $validated [ 'name' ],
'email' => $validated [ 'email' ],
'phone' => $validated [ 'phone' ],
'role_id' => $validated [ 'role_id' ],
'role' => $role -> slug ,
'password' => Hash :: make ( $validated [ 'password' ]),
]);
// Catat aktivitas (Gunakan parameter model untuk log)
$this -> logAction ( 'CREATED' , $user , " Mendaftarkan user baru: { $user -> name } sebagai { $role -> name } " , 'User' );
return redirect () -> route ( 'admin.users.index' ) -> with ( 'success' , " User { $user -> name } berhasil ditambahkan. " );
}
/**
* Halaman Edit User.
* Izin: users (update)
*/
public function edit ( User $user ) : View
{
Gate :: authorize ( 'check-access' , [ 'users' , 'update' ]);
$roles = Role :: all ();
return view ( 'pages.users.edit' , compact ( 'user' , 'roles' ));
}
/**
* Update Data User.
* Izin: users (update)
*/
public function update ( Request $request , User $user ) : RedirectResponse
{
Gate :: authorize ( 'check-access' , [ 'users' , 'update' ]);
$validated = $request -> validate ([
'name' => 'required|string|max:255' ,
'email' => 'required|email|unique:users,email,' . $user -> id ,
'role_id' => 'required|exists:roles,id' ,
'phone' => 'nullable|string|max:15' ,
]);
$newRole = Role :: find ( $validated [ 'role_id' ]);
// Keamanan Logika: Tetap pertahankan agar user tidak merusak akun sendiri
if ( $user -> id === auth () -> id () && $newRole -> slug !== 'admin' && auth () -> user () -> role === 'admin' ) {
return back () -> with ( 'error' , 'Akses Ditolak! Anda tidak bisa menurunkan otoritas Admin Anda sendiri.' );
}
$user -> update ( array_merge ( $validated , [
'role' => $newRole -> slug
]));
$this -> logAction ( 'UPDATED' , $user , " Memperbarui data profil user: { $user -> name } " , 'User' );
return redirect () -> route ( 'admin.users.index' ) -> with ( 'success' , " Data { $user -> name } berhasil diperbarui. " );
}
/**
* Hapus User.
* Izin: users (delete)
*/
public function destroy ( User $user ) : RedirectResponse
{
Gate :: authorize ( 'check-access' , [ 'users' , 'delete' ]);
if ( $user -> id === auth () -> id ()) {
return back () -> with ( 'error' , 'Akses Ditolak! Anda tidak dapat menghapus akun Anda sendiri.' );
}
$userName = $user -> name ;
$this -> logAction ( 'DELETED' , $user , " Menghapus user: { $userName } " , 'User' );
$user -> delete ();
return redirect () -> route ( 'admin.users.index' ) -> with ( 'success' , " User { $userName } telah dihapus. " );
}
/* -------------------------------------------------------------------------- */
/* AUDIT TRAIL / LOGS SECTION */
/* -------------------------------------------------------------------------- */
/**
* Daftar Aktivitas.
* Izin: logs (read)
*/
public function activityLogs ( Request $request )
{
Gate :: authorize ( 'check-access' , [ 'logs' , 'read' ]);
$logs = ActivityLog :: with ( 'user' )
-> when ( $request -> search , function ( $query , $search ) {
$query -> where ( function ( $q ) use ( $search ) {
$q -> where ( 'description' , 'like' , " % { $search } % " )
-> orWhereHas ( 'user' , function ( $qu ) use ( $search ) {
$qu -> where ( 'name' , 'like' , " % { $search } % " );
});
});
})
-> when ( $request -> action , function ( $query , $action ) {
$query -> where ( 'action' , 'like' , " % { $action } % " );
})
-> latest ()
-> paginate ( 20 );
return view ( 'pages.admin.logs' , compact ( 'logs' ));
}
/**
* Export Logs ke Excel.
* Izin: logs (read)
*/
public function exportLogs ()
{
Gate :: authorize ( 'check-access' , [ 'logs' , 'read' ]);
return Excel :: download ( new ActivityLogExport , 'audit_trail_' . now () -> format ( 'Ymd' ) . '.xlsx' );
}
/**
* Bersihkan Logs.
* Izin: logs (delete)
*/
public function clearLogs ()
{
Gate :: authorize ( 'check-access' , [ 'logs' , 'delete' ]);
2026-09-23 12:45:02 +07:00
return back () -> with ( 'error' , 'Penghapusan massal audit trail dinonaktifkan untuk menjaga jejak perubahan data. Gunakan kebijakan retensi/arsip bila diperlukan.' );
2026-02-08 15:16:35 +07:00
}
}