diff --git a/app/Http/Controllers/RolesController.php b/app/Http/Controllers/RolesController.php index 9e8c549..ef78ba6 100644 --- a/app/Http/Controllers/RolesController.php +++ b/app/Http/Controllers/RolesController.php @@ -2,9 +2,13 @@ namespace App\Http\Controllers; +use App\Models\Menu; use App\Models\Role; +use App\Models\RoleMenu; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Schema; class RolesController extends Controller { @@ -100,4 +104,48 @@ class RolesController extends Controller return redirect()->back()->with("error", $e->getMessage()); } } + + public function menu_permission(string $role_id){ + try{ + $role = Role::findOrFail($role_id); + $menus = Menu::all(); + $role_menus = RoleMenu::where('role_id', $role_id)->get() ?? collect(); + return view('roles.role_menu', compact('role', 'menus', 'role_menus')); + }catch(\Exception $e){ + return redirect()->back()->with("error", $e->getMessage()); + } + } + + public function update_menu_permission(Request $request, string $role_id){ + try{ + $validateData = $request->validate([ + "permissions" => "array", + "permissions.*.allow_show" => "nullable|boolean", + "permissions.*.allow_create" => "nullable|boolean", + "permissions.*.allow_update" => "nullable|boolean", + "permissions.*.allow_destroy" => "nullable|boolean" + ]); + + $role = Role::find($role_id); + + $permissionsArray = []; + foreach ($validateData['permissions'] as $menu_id => $permission) { + $permissionsArray[$menu_id] = [ + "allow_show" => (int) ($permission["allow_show"] ?? 0), + "allow_create" => (int) ($permission["allow_create"] ?? 0), + "allow_update" => (int) ($permission["allow_update"] ?? 0), + "allow_destroy" => (int) ($permission["allow_destroy"] ?? 0), + "updated_at" => now(), + ]; + } + + // Sync will update existing records and insert new ones + $role->menus()->sync($permissionsArray); + + return redirect()->route("role-menu.permission", $role_id)->with('success','Menu Permission updated successfully'); + }catch(\Exception $e){ + Log::error("Error updating role_menu:", ["error" => $e->getMessage()]); + return redirect()->route("role-menu.permission", $role_id)->with("error", $e->getMessage()); + } + } } diff --git a/app/Models/RoleMenu.php b/app/Models/RoleMenu.php new file mode 100644 index 0000000..4208269 --- /dev/null +++ b/app/Models/RoleMenu.php @@ -0,0 +1,30 @@ +belongsTo(Role::class, 'role_id'); + } + + public function menu(){ + return $this->belongsTo(Menu::class,'menu_id'); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2d9929f..714c4b6 100755 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -34,7 +34,11 @@ class AppServiceProvider extends ServiceProvider $menus = Menu::whereHas('roles', function ($query) use ($user){ $query->where('roles.id', $user->roles->pluck('id')); }) - ->with('children') + ->with(['children' => function ($query) { + $query->whereHas('roles', function ($subQuery) { + $subQuery->where('role_menu.allow_show', 1); + }); + }]) ->orderBy('sort_order', 'asc') ->get(); }else{ diff --git a/database/migrations/2025_02_11_134558_add_permision_to_role_menu.php b/database/migrations/2025_02_11_134558_add_permision_to_role_menu.php new file mode 100644 index 0000000..67339b9 --- /dev/null +++ b/database/migrations/2025_02_11_134558_add_permision_to_role_menu.php @@ -0,0 +1,31 @@ +boolean('allow_show')->default(true)->after('menu_id'); + $table->boolean('allow_create')->default(true)->after('allow_show'); + $table->boolean('allow_update')->default(true)->after('allow_create'); + $table->boolean('allow_destroy')->default(true)->after('allow_update'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('role_menu', function (Blueprint $table) { + $table->dropColumn(['allow_show','allow_create', 'allow_update', 'allow_destroy']); + }); + } +}; diff --git a/database/seeders/UsersRoleMenuSeeder.php b/database/seeders/UsersRoleMenuSeeder.php index 21878a2..0e43148 100644 --- a/database/seeders/UsersRoleMenuSeeder.php +++ b/database/seeders/UsersRoleMenuSeeder.php @@ -71,7 +71,7 @@ class UsersRoleMenuSeeder extends Seeder ]; foreach ($parent_menus as $parent_menu) { - Menu::create($parent_menu); + Menu::firstOrCreate(['name' => $parent_menu['name']], $parent_menu); } // Attach Menus to Roles @@ -85,21 +85,7 @@ class UsersRoleMenuSeeder extends Seeder $dataSettings = Menu::where('name', 'Data Settings')->first(); $data = Menu::where('name', 'Data')->first(); - // Superadmin gets all menus - $superadmin->menus()->attach([$dashboard->id, $master->id, $settings->id, $dataSettings->id, $data->id]); - - // Admin gets limited menus - $admin->menus()->attach([$dashboard->id, $master->id, $settings->id]); - - // Operator gets only basic menus - $operator->menus()->attach([$dashboard->id, $data->id]); - - // Attach User to role super admin - User::findOrFail(1)->roles()->attach($superadmin->id); - // create children menu - - // dashboard children $children_menus = [ [ "name" => "Dashboard Pimpinan", @@ -143,13 +129,6 @@ class UsersRoleMenuSeeder extends Seeder "parent_id" => $settings->id, "sort_order" => 3, ], - [ - "name" => "Assign Role Menu", - "url" => "roles.index", - "icon" => null, - "parent_id" => $settings->id, - "sort_order" => 4, - ], [ "name" => "Setting Dashboard", "url" => "data-settings.index", @@ -167,7 +146,48 @@ class UsersRoleMenuSeeder extends Seeder ]; foreach ($children_menus as $child_menu) { - Menu::create($child_menu); + Menu::firstOrCreate(['name' => $child_menu['name']], $child_menu); } + + $dashboard_pimpinan = Menu::where('name', 'Dashboard Pimpinan')->first(); + $dashboard_pbg = Menu::where('name', 'Dashboard PBG')->first(); + $users = Menu::where('name', 'Users')->first(); + $syncronize = Menu::where('name', 'Syncronize')->first(); + $setting_menu = Menu::where('name', 'Menu')->first(); + $setting_role = Menu::where('name', 'Role')->first(); + $setting_dashboard = Menu::where('name', 'Setting Dashboard')->first(); + $setting_pbg = Menu::where('name', 'PBG')->first(); + + // Superadmin gets all menus + $superadmin->menus()->sync([ + $dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $master->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $settings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $dataSettings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $data->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $dashboard_pimpinan->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $dashboard_pbg->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $users->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $syncronize->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $setting_menu->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $setting_role->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $setting_dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $setting_pbg->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + ]); + + // Admin gets limited menus + $admin->menus()->sync([ + $dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $master->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $settings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + ]); + + // Operator gets only basic menus with full permissions + $operator->menus()->sync([ + $dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + $data->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + ]); + // Attach User to role super admin + User::findOrFail(1)->roles()->sync([$superadmin->id]); } } diff --git a/resources/js/roles/index.js b/resources/js/roles/index.js index 6bf72e1..617d8ba 100644 --- a/resources/js/roles/index.js +++ b/resources/js/roles/index.js @@ -18,10 +18,12 @@ class Roles { name: "Action", formatter: (cell) => gridjs.html(` -
- Update - - `), +
+ Update + Role Menu + +
+ `), }, ], pagination: { diff --git a/resources/js/roles/role_menu.js b/resources/js/roles/role_menu.js new file mode 100644 index 0000000..955103e --- /dev/null +++ b/resources/js/roles/role_menu.js @@ -0,0 +1,36 @@ +class RoleMenus { + init() { + this.initCheckboxRoles(); + } + + initCheckboxRoles() { + const childPermissions = + document.querySelectorAll(".child-permissions"); + + childPermissions.forEach((child) => { + child.addEventListener("change", function () { + const parentId = this.dataset.parentId; + const parentShow = document.querySelector( + `input[name='permissions[${parentId}][allow_show]']` + ); + + if (parentShow) { + // If any child permission is checked, check parent "Show" + if ( + document.querySelectorAll( + `.child-permission[data-parent-id="${parentId}"]:checked` + ).length > 0 + ) { + parentShow.checked = true; + } else { + parentShow.checked = false; + } + } + }); + }); + } +} + +document.addEventListener("DOMContentLoaded", function (event) { + new RoleMenus().init(); +}); diff --git a/resources/views/layouts/partials/sidebar.blade.php b/resources/views/layouts/partials/sidebar.blade.php index 3433705..8c75d44 100644 --- a/resources/views/layouts/partials/sidebar.blade.php +++ b/resources/views/layouts/partials/sidebar.blade.php @@ -19,13 +19,15 @@ @foreach ($menus as $menu) @endforeach diff --git a/resources/views/roles/create.blade.php b/resources/views/roles/create.blade.php index f2ab4ab..7b9627f 100644 --- a/resources/views/roles/create.blade.php +++ b/resources/views/roles/create.blade.php @@ -1,9 +1,5 @@ @extends('layouts.vertical', ['subtitle' => 'Role']) -@section('css') -@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) -@endsection - @section('content') @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Role']) @@ -32,6 +28,3 @@
@endsection - -@section('scripts') -@endsection \ No newline at end of file diff --git a/resources/views/roles/edit.blade.php b/resources/views/roles/edit.blade.php index 8667d01..2c4c073 100644 --- a/resources/views/roles/edit.blade.php +++ b/resources/views/roles/edit.blade.php @@ -1,9 +1,5 @@ @extends('layouts.vertical', ['subtitle' => 'Role']) -@section('css') -@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) -@endsection - @section('content') @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Role']) @@ -33,6 +29,3 @@
@endsection - -@section('scripts') -@endsection \ No newline at end of file diff --git a/resources/views/roles/role_menu.blade.php b/resources/views/roles/role_menu.blade.php new file mode 100644 index 0000000..f5dce4e --- /dev/null +++ b/resources/views/roles/role_menu.blade.php @@ -0,0 +1,81 @@ +@extends('layouts.vertical', ['subtitle' => 'Role']) + +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Role']) + +
+
+
+
+
Manage Permissions for Role: {{ $role->name }}
+
id)}}" method="post"> + @csrf + @method("put") + + + + + + + + + + + + @foreach($menus->where('parent_id', null) as $parent) + @php + $role_menu = optional($role_menus->firstWhere('menu_id', $parent->id)); + @endphp + + + + + + + @foreach($menus->where('parent_id', $parent->id) as $child) + @php + $child_role_menu = optional($role_menus->firstWhere('menu_id', $child->id)); + @endphp + + + + + + + + @endforeach + @endforeach + +
MenuShowCreateUpdateDestroy
{{ $parent->name }} + allow_show ? 'checked' : '' }} readonly> +
— {{ $child->name }} + allow_show ? 'checked' : '' }}> + + allow_create ? 'checked' : '' }}> + + allow_update ? 'checked' : '' }}> + + allow_destroy ? 'checked' : '' }}> +
+ +
+
+
+
+
+ +@endsection + +@section('scripts') +@vite(['resources/roles/role_menu.js']) +@endsection \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 5f7e9d7..adcc95b 100755 --- a/routes/web.php +++ b/routes/web.php @@ -48,4 +48,8 @@ Route::group(['middleware' => 'auth'], function(){ // roles Route::resource('/roles', RolesController::class); + Route::group(['prefix' => '/roles'], function (){ + Route::get('/role-menu/{role_id}', [RolesController::class, 'menu_permission'])->name('role-menu.permission'); + Route::put('/role-menu/{role_id}', [RolesController::class, 'update_menu_permission'])->name('role-menu.permission.update'); + }); }); \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 5df2d80..5d3fcab 100755 --- a/vite.config.js +++ b/vite.config.js @@ -54,6 +54,7 @@ export default defineConfig({ "resources/js/tables/common-table.js", "resources/js/menus/index.js", "resources/js/roles/index.js", + "resources/roles/role_menu.js", ], refresh: true, }),