profil
This commit is contained in:
115
app/Http/Controllers/Admin/UserController.php
Normal file
115
app/Http/Controllers/Admin/UserController.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\AuditLog;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
$users = User::with('roles')->orderBy('name')->paginate(25);
|
||||
$roles = User::ROLE_LABELS;
|
||||
|
||||
return view('admin.users.index', compact('users', 'roles'));
|
||||
}
|
||||
|
||||
public function create(): View
|
||||
{
|
||||
$roles = User::ROLE_LABELS;
|
||||
|
||||
return view('admin.users.create', compact('roles'));
|
||||
}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255', 'unique:users,email'],
|
||||
'role' => ['required', Rule::in(array_keys(User::ROLE_LABELS))],
|
||||
'password' => ['required', 'confirmed', Password::min(8)],
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'password' => Hash::make($data['password']),
|
||||
]);
|
||||
$user->syncRoles([$data['role']]);
|
||||
|
||||
AuditLog::record('user.create', "Cipta pengguna: {$user->name} ({$user->email})", $user, ['role' => $data['role']]);
|
||||
|
||||
return redirect()->route('admin.users.index')->with('success', "Pengguna '{$user->name}' dicipta.");
|
||||
}
|
||||
|
||||
public function edit(User $user): View
|
||||
{
|
||||
$roles = User::ROLE_LABELS;
|
||||
|
||||
return view('admin.users.edit', compact('user', 'roles'));
|
||||
}
|
||||
|
||||
public function update(Request $request, User $user): RedirectResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255', Rule::unique('users', 'email')->ignore($user->id)],
|
||||
'role' => ['required', Rule::in(array_keys(User::ROLE_LABELS))],
|
||||
'password' => ['nullable', 'confirmed', Password::min(8)],
|
||||
]);
|
||||
|
||||
// Jangan biar admin terakhir diturunkan peranannya (boleh kunci sistem)
|
||||
if ($this->isLastAdmin($user) && $data['role'] !== User::ROLE_ADMIN) {
|
||||
return back()->withInput()->with('error', 'Tidak boleh menukar peranan admin terakhir.');
|
||||
}
|
||||
|
||||
$user->update([
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
]);
|
||||
|
||||
if (! empty($data['password'])) {
|
||||
$user->update(['password' => Hash::make($data['password'])]);
|
||||
}
|
||||
|
||||
$user->syncRoles([$data['role']]);
|
||||
|
||||
AuditLog::record('user.update', "Kemaskini pengguna: {$user->name}", $user, [
|
||||
'role' => $data['role'],
|
||||
'password_changed' => ! empty($data['password']),
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.users.index')->with('success', 'Maklumat pengguna dikemaskini.');
|
||||
}
|
||||
|
||||
public function destroy(Request $request, User $user): RedirectResponse
|
||||
{
|
||||
if ($user->id === $request->user()->id) {
|
||||
return back()->with('error', 'Anda tidak boleh memadam akaun sendiri.');
|
||||
}
|
||||
|
||||
if ($this->isLastAdmin($user)) {
|
||||
return back()->with('error', 'Tidak boleh memadam admin terakhir.');
|
||||
}
|
||||
|
||||
$nama = $user->name;
|
||||
$user->delete();
|
||||
AuditLog::record('user.delete', "Padam pengguna: {$nama}");
|
||||
|
||||
return redirect()->route('admin.users.index')->with('success', "Pengguna '{$nama}' dipadam.");
|
||||
}
|
||||
|
||||
private function isLastAdmin(User $user): bool
|
||||
{
|
||||
return $user->hasRole(User::ROLE_ADMIN)
|
||||
&& User::role(User::ROLE_ADMIN)->count() <= 1;
|
||||
}
|
||||
}
|
||||
49
app/Http/Controllers/ProfileController.php
Normal file
49
app/Http/Controllers/ProfileController.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\AuditLog;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
public function edit(): View
|
||||
{
|
||||
return view('profile.edit', ['user' => auth()->user()]);
|
||||
}
|
||||
|
||||
public function update(Request $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$data = $request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255', Rule::unique('users', 'email')->ignore($user->id)],
|
||||
]);
|
||||
|
||||
$user->update($data);
|
||||
AuditLog::record('profile.update', 'Kemaskini profil sendiri', $user);
|
||||
|
||||
return redirect()->route('profile.edit')->with('success', 'Profil dikemaskini.');
|
||||
}
|
||||
|
||||
public function updatePassword(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'current_password' => ['required', 'current_password'],
|
||||
'password' => ['required', 'confirmed', Password::min(8)],
|
||||
]);
|
||||
|
||||
$request->user()->update([
|
||||
'password' => Hash::make($request->input('password')),
|
||||
]);
|
||||
AuditLog::record('profile.password', 'Tukar kata laluan sendiri', $request->user());
|
||||
|
||||
return redirect()->route('profile.edit')->with('success', 'Kata laluan ditukar.');
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,21 @@ class User extends Authenticatable
|
||||
public const ROLE_KAUNTER = 'petugas_kaunter';
|
||||
public const ROLE_CABUTAN = 'petugas_cabutan';
|
||||
|
||||
/** Peranan yang boleh ditugaskan kepada pengguna + label paparan ringkas. */
|
||||
public const ROLE_LABELS = [
|
||||
self::ROLE_ADMIN => 'Admin',
|
||||
self::ROLE_KAUNTER => 'Kaunter',
|
||||
self::ROLE_CABUTAN => 'Cabutan',
|
||||
];
|
||||
|
||||
/** Label ringkas peranan utama pengguna ini (cth "Admin"). */
|
||||
public function roleLabel(): string
|
||||
{
|
||||
$role = $this->getRoleNames()->first();
|
||||
|
||||
return self::ROLE_LABELS[$role] ?? ($role ? \Illuminate\Support\Str::headline($role) : '-');
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
|
||||
37
resources/views/admin/users/create.blade.php
Normal file
37
resources/views/admin/users/create.blade.php
Normal file
@@ -0,0 +1,37 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Pengguna Baru')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center"><div class="col-lg-7">
|
||||
<div class="card"><div class="card-body">
|
||||
<form method="POST" action="{{ route('admin.users.store') }}">
|
||||
@csrf
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6"><label class="form-label">Nama *</label>
|
||||
<input name="name" class="form-control @error('name') is-invalid @enderror" value="{{ old('name') }}" required>
|
||||
@error('name')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Emel *</label>
|
||||
<input type="email" name="email" class="form-control @error('email') is-invalid @enderror" value="{{ old('email') }}" required>
|
||||
@error('email')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Peranan *</label>
|
||||
<select name="role" class="form-select @error('role') is-invalid @enderror" required>
|
||||
<option value="">— Pilih peranan —</option>
|
||||
@foreach($roles as $value => $label)
|
||||
<option value="{{ $value }}" @selected(old('role') === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('role')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Kata Laluan *</label>
|
||||
<input type="password" name="password" class="form-control @error('password') is-invalid @enderror" required>
|
||||
@error('password')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Sahkan Kata Laluan *</label>
|
||||
<input type="password" name="password_confirmation" class="form-control" required></div>
|
||||
</div>
|
||||
<div class="mt-4 d-flex gap-2">
|
||||
<button class="btn btn-teal"><i class="bi bi-save"></i> Simpan</button>
|
||||
<a href="{{ route('admin.users.index') }}" class="btn btn-light">Kembali</a>
|
||||
</div>
|
||||
</form>
|
||||
</div></div>
|
||||
</div></div>
|
||||
@endsection
|
||||
42
resources/views/admin/users/edit.blade.php
Normal file
42
resources/views/admin/users/edit.blade.php
Normal file
@@ -0,0 +1,42 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Kemaskini Pengguna')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center"><div class="col-lg-7">
|
||||
<div class="card"><div class="card-body">
|
||||
<form method="POST" action="{{ route('admin.users.update', $user) }}">
|
||||
@csrf @method('PUT')
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6"><label class="form-label">Nama *</label>
|
||||
<input name="name" class="form-control @error('name') is-invalid @enderror" value="{{ old('name', $user->name) }}" required>
|
||||
@error('name')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Emel *</label>
|
||||
<input type="email" name="email" class="form-control @error('email') is-invalid @enderror" value="{{ old('email', $user->email) }}" required>
|
||||
@error('email')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Peranan *</label>
|
||||
<select name="role" class="form-select @error('role') is-invalid @enderror" required>
|
||||
@foreach($roles as $value => $label)
|
||||
<option value="{{ $value }}" @selected(old('role', $user->getRoleNames()->first()) === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('role')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
<p class="text-muted small mb-3"><i class="bi bi-key"></i> Biarkan kosong jika tidak mahu menukar kata laluan.</p>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6"><label class="form-label">Kata Laluan Baru</label>
|
||||
<input type="password" name="password" class="form-control @error('password') is-invalid @enderror">
|
||||
@error('password')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Sahkan Kata Laluan</label>
|
||||
<input type="password" name="password_confirmation" class="form-control"></div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 d-flex gap-2">
|
||||
<button class="btn btn-teal"><i class="bi bi-save"></i> Simpan</button>
|
||||
<a href="{{ route('admin.users.index') }}" class="btn btn-light">Kembali</a>
|
||||
</div>
|
||||
</form>
|
||||
</div></div>
|
||||
</div></div>
|
||||
@endsection
|
||||
37
resources/views/admin/users/index.blade.php
Normal file
37
resources/views/admin/users/index.blade.php
Normal file
@@ -0,0 +1,37 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Urus Pengguna')
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div class="text-muted">Pengguna sistem mengikut peranan: Admin, Kaunter, Cabutan.</div>
|
||||
<a href="{{ route('admin.users.create') }}" class="btn btn-teal"><i class="bi bi-person-plus"></i> Pengguna Baru</a>
|
||||
</div>
|
||||
|
||||
<div class="card"><div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-koipb table-hover align-middle">
|
||||
<thead><tr><th>Nama</th><th>Emel</th><th>Peranan</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse($users as $user)
|
||||
<tr>
|
||||
<td class="fw-semibold">{{ $user->name }}
|
||||
@if($user->id === auth()->id())<span class="badge bg-teal ms-1">Anda</span>@endif</td>
|
||||
<td>{{ $user->email }}</td>
|
||||
<td><span class="badge bg-dark2">{{ $user->roleLabel() }}</span></td>
|
||||
<td class="text-end">
|
||||
<a href="{{ route('admin.users.edit', $user) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-pencil"></i></a>
|
||||
<form action="{{ route('admin.users.destroy', $user) }}" method="POST" class="d-inline" onsubmit="return confirm('Padam pengguna ini?')">
|
||||
@csrf @method('DELETE')
|
||||
<button class="btn btn-sm btn-outline-danger" @disabled($user->id === auth()->id())><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="4" class="text-center text-muted py-4">Tiada pengguna.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $users->links() }}
|
||||
</div></div>
|
||||
@endsection
|
||||
@@ -59,6 +59,9 @@
|
||||
<a href="{{ route('admin.prizes.index') }}" class="{{ request()->routeIs('admin.prizes.*') ? 'active' : '' }}">
|
||||
<span class="ico"><i class="bi bi-gift"></i></span> Hadiah
|
||||
</a>
|
||||
<a href="{{ route('admin.users.index') }}" class="{{ request()->routeIs('admin.users.*') ? 'active' : '' }}">
|
||||
<span class="ico"><i class="bi bi-person-gear"></i></span> Pengguna
|
||||
</a>
|
||||
<a href="{{ route('admin.audit.index') }}" class="{{ request()->routeIs('admin.audit.*') ? 'active' : '' }}">
|
||||
<span class="ico"><i class="bi bi-shield-check"></i></span> Audit Trail
|
||||
</a>
|
||||
@@ -68,10 +71,10 @@
|
||||
@endif
|
||||
</nav>
|
||||
<div class="sb-foot">
|
||||
<div class="text-white-50 small">{{ $u?->name }}</div>
|
||||
<div class="small mb-2" style="color:var(--koipb-teal-light)">
|
||||
@foreach($u?->getRoleNames() ?? [] as $r){{ \Illuminate\Support\Str::headline($r) }}@endforeach
|
||||
</div>
|
||||
<a href="{{ route('profile.edit') }}" class="text-decoration-none d-block mb-2">
|
||||
<div class="text-white small"><i class="bi bi-person-circle"></i> {{ $u?->name }}</div>
|
||||
<div class="small" style="color:var(--koipb-teal-light)">{{ $u?->roleLabel() }}</div>
|
||||
</a>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button class="btn btn-sm btn-outline-light w-100"><i class="bi bi-box-arrow-right"></i> Log Keluar</button>
|
||||
|
||||
44
resources/views/profile/edit.blade.php
Normal file
44
resources/views/profile/edit.blade.php
Normal file
@@ -0,0 +1,44 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Profil Saya')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center g-3"><div class="col-lg-7">
|
||||
|
||||
<div class="card mb-3"><div class="card-body">
|
||||
<h5 class="fw-bold"><i class="bi bi-person-circle text-teal"></i> Maklumat Profil</h5>
|
||||
<form method="POST" action="{{ route('profile.update') }}">
|
||||
@csrf @method('PUT')
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6"><label class="form-label">Nama *</label>
|
||||
<input name="name" class="form-control @error('name') is-invalid @enderror" value="{{ old('name', $user->name) }}" required>
|
||||
@error('name')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Emel *</label>
|
||||
<input type="email" name="email" class="form-control @error('email') is-invalid @enderror" value="{{ old('email', $user->email) }}" required>
|
||||
@error('email')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Peranan</label>
|
||||
<input class="form-control" value="{{ $user->roleLabel() }}" disabled></div>
|
||||
</div>
|
||||
<div class="mt-4"><button class="btn btn-teal"><i class="bi bi-save"></i> Simpan Profil</button></div>
|
||||
</form>
|
||||
</div></div>
|
||||
|
||||
<div class="card"><div class="card-body">
|
||||
<h5 class="fw-bold"><i class="bi bi-key text-teal"></i> Tukar Kata Laluan</h5>
|
||||
<form method="POST" action="{{ route('profile.password') }}">
|
||||
@csrf @method('PUT')
|
||||
<div class="row g-3">
|
||||
<div class="col-12"><label class="form-label">Kata Laluan Semasa *</label>
|
||||
<input type="password" name="current_password" class="form-control @error('current_password') is-invalid @enderror" required>
|
||||
@error('current_password')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Kata Laluan Baru *</label>
|
||||
<input type="password" name="password" class="form-control @error('password') is-invalid @enderror" required>
|
||||
@error('password')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Sahkan Kata Laluan Baru *</label>
|
||||
<input type="password" name="password_confirmation" class="form-control" required></div>
|
||||
</div>
|
||||
<div class="mt-4"><button class="btn btn-teal"><i class="bi bi-shield-lock"></i> Tukar Kata Laluan</button></div>
|
||||
</form>
|
||||
</div></div>
|
||||
|
||||
</div></div>
|
||||
@endsection
|
||||
@@ -7,8 +7,10 @@ use App\Http\Controllers\Admin\SettingController;
|
||||
use App\Http\Controllers\AttendanceController;
|
||||
use App\Http\Controllers\Auth\LoginController;
|
||||
use App\Http\Controllers\CounterController;
|
||||
use App\Http\Controllers\Admin\UserController;
|
||||
use App\Http\Controllers\DashboardController;
|
||||
use App\Http\Controllers\DrawController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\ReportController;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
@@ -25,6 +27,11 @@ Route::get('/', fn () => redirect()->route('dashboard'));
|
||||
Route::middleware('auth')->group(function () {
|
||||
Route::get('dashboard', [DashboardController::class, 'index'])->name('dashboard');
|
||||
|
||||
// ---- Profil sendiri (semua pengguna) ----
|
||||
Route::get('profil', [ProfileController::class, 'edit'])->name('profile.edit');
|
||||
Route::put('profil', [ProfileController::class, 'update'])->name('profile.update');
|
||||
Route::put('profil/kata-laluan', [ProfileController::class, 'updatePassword'])->name('profile.password');
|
||||
|
||||
// ---- Kaunter Kehadiran (admin + petugas kaunter) ----
|
||||
Route::middleware('role:' . User::ROLE_ADMIN . '|' . User::ROLE_KAUNTER)->group(function () {
|
||||
Route::get('kaunter', [CounterController::class, 'index'])->name('counter.index');
|
||||
@@ -67,5 +74,13 @@ Route::middleware('auth')->group(function () {
|
||||
Route::post('tetapan/reset', [SettingController::class, 'reset'])->name('settings.reset');
|
||||
|
||||
Route::get('audit', [AuditController::class, 'index'])->name('audit.index');
|
||||
|
||||
// ---- Urus Pengguna ----
|
||||
Route::get('pengguna', [UserController::class, 'index'])->name('users.index');
|
||||
Route::get('pengguna/cipta', [UserController::class, 'create'])->name('users.create');
|
||||
Route::post('pengguna', [UserController::class, 'store'])->name('users.store');
|
||||
Route::get('pengguna/{user}/edit', [UserController::class, 'edit'])->name('users.edit');
|
||||
Route::put('pengguna/{user}', [UserController::class, 'update'])->name('users.update');
|
||||
Route::delete('pengguna/{user}', [UserController::class, 'destroy'])->name('users.destroy');
|
||||
});
|
||||
});
|
||||
|
||||
117
tests/Feature/UserManagementTest.php
Normal file
117
tests/Feature/UserManagementTest.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\User;
|
||||
use Database\Seeders\RoleUserSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UserManagementTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private function admin(): User
|
||||
{
|
||||
$this->seed(RoleUserSeeder::class);
|
||||
|
||||
return User::where('email', 'admin@koipb.test')->first();
|
||||
}
|
||||
|
||||
public function test_admin_can_create_user_with_role(): void
|
||||
{
|
||||
$this->actingAs($this->admin())->post(route('admin.users.store'), [
|
||||
'name' => 'Pegawai Baru',
|
||||
'email' => 'baru@koipb.test',
|
||||
'role' => User::ROLE_KAUNTER,
|
||||
'password' => 'rahsia123',
|
||||
'password_confirmation' => 'rahsia123',
|
||||
])->assertRedirect(route('admin.users.index'));
|
||||
|
||||
$user = User::where('email', 'baru@koipb.test')->first();
|
||||
$this->assertNotNull($user);
|
||||
$this->assertTrue($user->hasRole(User::ROLE_KAUNTER));
|
||||
}
|
||||
|
||||
public function test_admin_can_change_role_and_password(): void
|
||||
{
|
||||
$admin = $this->admin();
|
||||
$kaunter = User::where('email', 'kaunter@koipb.test')->first();
|
||||
|
||||
$this->actingAs($admin)->put(route('admin.users.update', $kaunter), [
|
||||
'name' => $kaunter->name,
|
||||
'email' => $kaunter->email,
|
||||
'role' => User::ROLE_CABUTAN,
|
||||
'password' => 'katalaluanbaru',
|
||||
'password_confirmation' => 'katalaluanbaru',
|
||||
])->assertRedirect(route('admin.users.index'));
|
||||
|
||||
$kaunter->refresh();
|
||||
$this->assertTrue($kaunter->hasRole(User::ROLE_CABUTAN));
|
||||
$this->assertFalse($kaunter->hasRole(User::ROLE_KAUNTER));
|
||||
$this->assertTrue(Hash::check('katalaluanbaru', $kaunter->password));
|
||||
}
|
||||
|
||||
public function test_cannot_delete_self(): void
|
||||
{
|
||||
$admin = $this->admin();
|
||||
|
||||
$this->actingAs($admin)->delete(route('admin.users.destroy', $admin))
|
||||
->assertSessionHas('error');
|
||||
|
||||
$this->assertDatabaseHas('users', ['id' => $admin->id]);
|
||||
}
|
||||
|
||||
public function test_cannot_demote_last_admin(): void
|
||||
{
|
||||
$admin = $this->admin();
|
||||
|
||||
$this->actingAs($admin)->put(route('admin.users.update', $admin), [
|
||||
'name' => $admin->name,
|
||||
'email' => $admin->email,
|
||||
'role' => User::ROLE_KAUNTER,
|
||||
])->assertSessionHas('error');
|
||||
|
||||
$admin->refresh();
|
||||
$this->assertTrue($admin->hasRole(User::ROLE_ADMIN));
|
||||
}
|
||||
|
||||
public function test_non_admin_cannot_access_user_management(): void
|
||||
{
|
||||
$this->seed(RoleUserSeeder::class);
|
||||
$kaunter = User::where('email', 'kaunter@koipb.test')->first();
|
||||
|
||||
$this->actingAs($kaunter)->get(route('admin.users.index'))->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_user_can_change_own_password(): void
|
||||
{
|
||||
$this->seed(RoleUserSeeder::class);
|
||||
$kaunter = User::where('email', 'kaunter@koipb.test')->first();
|
||||
|
||||
$this->actingAs($kaunter)->put(route('profile.password'), [
|
||||
'current_password' => 'password',
|
||||
'password' => 'katalaluanbaru',
|
||||
'password_confirmation' => 'katalaluanbaru',
|
||||
])->assertRedirect(route('profile.edit'));
|
||||
|
||||
$kaunter->refresh();
|
||||
$this->assertTrue(Hash::check('katalaluanbaru', $kaunter->password));
|
||||
}
|
||||
|
||||
public function test_wrong_current_password_rejected(): void
|
||||
{
|
||||
$this->seed(RoleUserSeeder::class);
|
||||
$kaunter = User::where('email', 'kaunter@koipb.test')->first();
|
||||
|
||||
$this->actingAs($kaunter)->put(route('profile.password'), [
|
||||
'current_password' => 'salah',
|
||||
'password' => 'katalaluanbaru',
|
||||
'password_confirmation' => 'katalaluanbaru',
|
||||
])->assertSessionHasErrors('current_password');
|
||||
|
||||
$kaunter->refresh();
|
||||
$this->assertTrue(Hash::check('password', $kaunter->password));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user