diff --git a/app/Http/Controllers/PermissionController.php b/app/Http/Controllers/PermissionController.php
new file mode 100644
index 0000000..bcdd231
--- /dev/null
+++ b/app/Http/Controllers/PermissionController.php
@@ -0,0 +1,35 @@
+get();
+
+ return view('rbac.permissions.index', compact('permissions'));
+ }
+
+ public function store(PermissionRequest $request): RedirectResponse
+ {
+ Permission::create([
+ 'name' => $request->input('name'),
+ 'guard_name' => 'web',
+ ]);
+
+ return back()->with('success', 'Permission berhasil ditambahkan.');
+ }
+
+ public function update(PermissionRequest $request, Permission $permission): RedirectResponse
+ {
+ $permission->update(['name' => $request->input('name')]);
+
+ return back()->with('success', 'Permission berhasil diperbarui.');
+ }
+}
diff --git a/app/Http/Controllers/RoleController.php b/app/Http/Controllers/RoleController.php
new file mode 100644
index 0000000..e5781ec
--- /dev/null
+++ b/app/Http/Controllers/RoleController.php
@@ -0,0 +1,40 @@
+orderBy('name')->get();
+ $permissions = Permission::orderBy('name')->get();
+
+ return view('rbac.roles.index', compact('roles', 'permissions'));
+ }
+
+ public function store(RoleRequest $request): RedirectResponse
+ {
+ $role = Role::create([
+ 'name' => $request->input('name'),
+ 'guard_name' => 'web',
+ ]);
+
+ $role->syncPermissions($request->input('permissions', []));
+
+ return back()->with('success', 'Role berhasil ditambahkan.');
+ }
+
+ public function update(RoleRequest $request, Role $role): RedirectResponse
+ {
+ $role->update(['name' => $request->input('name')]);
+ $role->syncPermissions($request->input('permissions', []));
+
+ return back()->with('success', 'Role berhasil diperbarui.');
+ }
+}
diff --git a/app/Http/Controllers/UserManagementController.php b/app/Http/Controllers/UserManagementController.php
new file mode 100644
index 0000000..d2b4d12
--- /dev/null
+++ b/app/Http/Controllers/UserManagementController.php
@@ -0,0 +1,43 @@
+orderBy('name')->get();
+ $roles = Role::orderBy('name')->get();
+
+ return view('rbac.users.index', compact('users', 'roles'));
+ }
+
+ public function store(UserRequest $request): RedirectResponse
+ {
+ $user = User::create($request->validated());
+
+ $user->syncRoles($request->input('roles', []));
+
+ return back()->with('success', 'User berhasil ditambahkan.');
+ }
+
+ public function update(UserRequest $request, User $user): RedirectResponse
+ {
+ $data = $request->validated();
+
+ if (empty($data['password'])) {
+ unset($data['password']);
+ }
+
+ $user->update($data);
+ $user->syncRoles($request->input('roles', []));
+
+ return back()->with('success', 'User berhasil diperbarui.');
+ }
+}
diff --git a/app/Http/Requests/PermissionRequest.php b/app/Http/Requests/PermissionRequest.php
new file mode 100644
index 0000000..3ed1bba
--- /dev/null
+++ b/app/Http/Requests/PermissionRequest.php
@@ -0,0 +1,28 @@
+route('permission')?->getKey();
+
+ return [
+ 'name' => [
+ 'required',
+ 'string',
+ 'max:100',
+ Rule::unique('permissions', 'name')->ignore($permissionId, 'uuid'),
+ ],
+ ];
+ }
+}
diff --git a/app/Http/Requests/RoleRequest.php b/app/Http/Requests/RoleRequest.php
new file mode 100644
index 0000000..3ebbbc1
--- /dev/null
+++ b/app/Http/Requests/RoleRequest.php
@@ -0,0 +1,30 @@
+route('role')?->getKey();
+
+ return [
+ 'name' => [
+ 'required',
+ 'string',
+ 'max:100',
+ Rule::unique('roles', 'name')->ignore($roleId, 'uuid'),
+ ],
+ 'permissions' => ['array'],
+ 'permissions.*' => ['uuid', 'exists:permissions,uuid'],
+ ];
+ }
+}
diff --git a/app/Http/Requests/UserRequest.php b/app/Http/Requests/UserRequest.php
new file mode 100644
index 0000000..accc1f7
--- /dev/null
+++ b/app/Http/Requests/UserRequest.php
@@ -0,0 +1,32 @@
+route('user')?->getKey();
+
+ return [
+ 'name' => ['required', 'string', 'max:255'],
+ 'email' => [
+ 'required',
+ 'email',
+ 'max:255',
+ Rule::unique('users', 'email')->ignore($userId),
+ ],
+ 'password' => [$this->isMethod('post') ? 'required' : 'nullable', 'string', 'min:8', 'confirmed'],
+ 'roles' => ['array'],
+ 'roles.*' => ['uuid', 'exists:roles,uuid'],
+ ];
+ }
+}
diff --git a/app/Models/Permission.php b/app/Models/Permission.php
new file mode 100644
index 0000000..af8365b
--- /dev/null
+++ b/app/Models/Permission.php
@@ -0,0 +1,15 @@
+ [
@@ -13,7 +16,7 @@ return [
* `Spatie\Permission\Contracts\Permission` contract.
*/
- 'permission' => Spatie\Permission\Models\Permission::class,
+ 'permission' => Permission::class,
/*
* When using the "HasRoles" trait from this package, we need to know which
@@ -24,7 +27,7 @@ return [
* `Spatie\Permission\Contracts\Role` contract.
*/
- 'role' => Spatie\Permission\Models\Role::class,
+ 'role' => Role::class,
],
diff --git a/resources/views/layouts/components/main-sidebar.blade.php b/resources/views/layouts/components/main-sidebar.blade.php
index 733bd0f..fc01f70 100644
--- a/resources/views/layouts/components/main-sidebar.blade.php
+++ b/resources/views/layouts/components/main-sidebar.blade.php
@@ -122,6 +122,56 @@
System
+
+
+
+
+
+
+
+
+
diff --git a/resources/views/rbac/permissions/index.blade.php b/resources/views/rbac/permissions/index.blade.php
new file mode 100644
index 0000000..36fc421
--- /dev/null
+++ b/resources/views/rbac/permissions/index.blade.php
@@ -0,0 +1,141 @@
+@extends('layouts.master')
+
+@section('content')
+
+
+
+
+
+
+
+ @if(session('success'))
+
+ {{ session('success') }}
+
+
+ @endif
+
+ @if($errors->any())
+
+
+ @foreach($errors->all() as $error)
+ - {{ $error }}
+ @endforeach
+
+
+ @endif
+
+
+
+
+
+ | Permission |
+ Aksi |
+
+
+
+ @forelse($permissions as $permission)
+
+ | {{ $permission->name }} |
+
+
+ |
+
+ @empty
+
+ | Belum ada permission. |
+
+ @endforelse
+
+
+
+
+
+
+
+
+ {{-- Modal create --}}
+
+
+ {{-- Modal edit --}}
+
+@endsection
+
+@section('scripts')
+
+@endsection
diff --git a/resources/views/rbac/roles/index.blade.php b/resources/views/rbac/roles/index.blade.php
new file mode 100644
index 0000000..a67a87d
--- /dev/null
+++ b/resources/views/rbac/roles/index.blade.php
@@ -0,0 +1,152 @@
+@extends('layouts.master')
+
+@section('content')
+
+
+
+
+
+
+
+ @if(session('success'))
+
+ {{ session('success') }}
+
+
+ @endif
+
+ @if($errors->any())
+
+
+ @foreach($errors->all() as $error)
+ - {{ $error }}
+ @endforeach
+
+
+ @endif
+
+
+
+
+
+ | Role |
+ Permissions |
+ Aksi |
+
+
+
+ @forelse($roles as $role)
+
+ | {{ $role->name }} |
+
+ @if($role->permissions->isEmpty())
+ Belum ada permission
+ @else
+ @foreach($role->permissions as $permission)
+ {{ $permission->name }}
+ @endforeach
+ @endif
+ |
+
+
+ |
+
+ @empty
+
+ | Belum ada role. |
+
+ @endforelse
+
+
+
+
+
+
+
+
+ {{-- Modal create --}}
+
+
+ {{-- Modal edit --}}
+
+@endsection
+
+@section('scripts')
+
+@endsection
diff --git a/resources/views/rbac/roles/partials/form.blade.php b/resources/views/rbac/roles/partials/form.blade.php
new file mode 100644
index 0000000..7d416c7
--- /dev/null
+++ b/resources/views/rbac/roles/partials/form.blade.php
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+ @foreach($permissions as $permission)
+
+
+
+
+
+
+ @endforeach
+
+
diff --git a/resources/views/rbac/users/index.blade.php b/resources/views/rbac/users/index.blade.php
new file mode 100644
index 0000000..fb2c54c
--- /dev/null
+++ b/resources/views/rbac/users/index.blade.php
@@ -0,0 +1,160 @@
+@extends('layouts.master')
+
+@section('content')
+
+
+
+
+
+
+
+ @if(session('success'))
+
+ {{ session('success') }}
+
+
+ @endif
+
+ @if($errors->any())
+
+
+ @foreach($errors->all() as $error)
+ - {{ $error }}
+ @endforeach
+
+
+ @endif
+
+
+
+
+
+ | Nama |
+ Email |
+ Roles |
+ Aksi |
+
+
+
+ @forelse($users as $user)
+
+ | {{ $user->name }} |
+ {{ $user->email }} |
+
+ @if($user->roles->isEmpty())
+ Belum ada role
+ @else
+ @foreach($user->roles as $role)
+ {{ $role->name }}
+ @endforeach
+ @endif
+ |
+
+
+ |
+
+ @empty
+
+ | Belum ada user. |
+
+ @endforelse
+
+
+
+
+
+
+
+
+ {{-- Modal create --}}
+
+
+ {{-- Modal edit --}}
+
+@endsection
+
+@section('scripts')
+
+@endsection
diff --git a/resources/views/rbac/users/partials/form.blade.php b/resources/views/rbac/users/partials/form.blade.php
new file mode 100644
index 0000000..00cfdbc
--- /dev/null
+++ b/resources/views/rbac/users/partials/form.blade.php
@@ -0,0 +1,35 @@
+
+
+
+
+
+ @forelse($roles as $role)
+
+
+
+
+
+
+ @empty
+
Belum ada role, buat dulu di menu Roles.
+ @endforelse
+
+
diff --git a/routes/web.php b/routes/web.php
index 55cddbe..9f64ff4 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -4,6 +4,9 @@ use Illuminate\Support\Facades\Route;
use App\Http\Controllers\DashboardsController;
use App\Http\Controllers\SystemSettingController;
+use App\Http\Controllers\RoleController;
+use App\Http\Controllers\PermissionController;
+use App\Http\Controllers\UserManagementController;
use App\Http\Controllers\Controller;
Route::get('/', function () {
@@ -19,3 +22,7 @@ Route::get('/', function () {
Route::get('index', [DashboardsController::class, 'index']);
Route::resource('settings', SystemSettingController::class)->only(['index', 'store', 'update', 'destroy']);
+
+Route::resource('roles', RoleController::class)->only(['index', 'store', 'update']);
+Route::resource('permissions', PermissionController::class)->only(['index', 'store', 'update']);
+Route::resource('users', UserManagementController::class)->only(['index', 'store', 'update']);
diff --git a/tests/Feature/RbacTest.php b/tests/Feature/RbacTest.php
new file mode 100644
index 0000000..ea30c05
--- /dev/null
+++ b/tests/Feature/RbacTest.php
@@ -0,0 +1,46 @@
+ 'asset.create', 'guard_name' => 'web']);
+
+ $response = $this->post(route('roles.store'), [
+ 'name' => 'admin',
+ 'permissions' => [$perm->getKey()],
+ ]);
+
+ $response->assertRedirect();
+ $this->assertDatabaseHas('roles', ['name' => 'admin']);
+ $role = Role::whereName('admin')->first();
+ $this->assertTrue($role->hasPermissionTo('asset.create'));
+ }
+
+ public function test_can_create_user_and_attach_role(): void
+ {
+ $role = Role::create(['name' => 'viewer', 'guard_name' => 'web']);
+
+ $response = $this->post(route('users.store'), [
+ 'name' => 'Jane',
+ 'email' => 'jane@example.com',
+ 'password' => 'password123',
+ 'password_confirmation' => 'password123',
+ 'roles' => [$role->getKey()],
+ ]);
+
+ $response->assertRedirect();
+ $user = User::whereEmail('jane@example.com')->first();
+ $this->assertTrue($user->hasRole('viewer'));
+ }
+}