Files

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.');
}
}