239 lines
7.8 KiB
PHP
239 lines
7.8 KiB
PHP
<?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()
|
|
->with('roleModel')
|
|
->when($request->search, function ($query, $search) {
|
|
$query->where(function ($q) use ($search) {
|
|
$q->where('name', 'like', "%{$search}%")
|
|
->orWhere('email', 'like', "%{$search}%");
|
|
});
|
|
})
|
|
->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.");
|
|
}
|
|
|
|
public function toggleStatus(User $user): RedirectResponse
|
|
{
|
|
Gate::authorize('check-access', ['users', 'update']);
|
|
|
|
if ($user->id === auth()->id() && $user->is_active) {
|
|
return back()->with('error', 'Akun yang sedang digunakan tidak dapat dinonaktifkan.');
|
|
}
|
|
|
|
$newStatus = !$user->is_active;
|
|
$user->forceFill([
|
|
'is_active' => $newStatus,
|
|
'deactivated_at' => $newStatus ? null : now(),
|
|
])->save();
|
|
|
|
$this->logAction(
|
|
$newStatus ? 'USER_ACTIVATED' : 'USER_DEACTIVATED',
|
|
$user,
|
|
($newStatus ? 'Mengaktifkan' : 'Menonaktifkan') . " akun user: {$user->name}",
|
|
'User'
|
|
);
|
|
|
|
return back()->with('success', "Status akun {$user->name} berhasil diperbarui.");
|
|
}
|
|
|
|
public function changePassword(Request $request, User $user): RedirectResponse
|
|
{
|
|
Gate::authorize('check-access', ['users', 'update']);
|
|
|
|
$validated = $request->validate([
|
|
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
|
]);
|
|
|
|
$user->forceFill(['password' => Hash::make($validated['password'])])->save();
|
|
$this->logAction('USER_PASSWORD_CHANGED', $user, "Mengganti kata sandi user: {$user->name}", 'User');
|
|
|
|
return back()->with('success', "Kata sandi {$user->name} berhasil diganti.");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* 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']);
|
|
return back()->with('error', 'Penghapusan massal audit trail dinonaktifkan untuk menjaga jejak perubahan data. Gunakan kebijakan retensi/arsip bila diperlukan.');
|
|
}
|
|
} |