From cb90f69d1e4052ddc9cb01c50bb930b9723a7759 Mon Sep 17 00:00:00 2001 From: arifal Date: Tue, 11 Feb 2025 02:35:53 +0700 Subject: [PATCH] create user role and menu, create seeder for first user and create crud role, menu and user --- app/Console/Commands/ExecuteScraping.php | 12 +- app/Http/Controllers/Api/MenusController.php | 56 ++++++ app/Http/Controllers/Api/RolesController.php | 56 ++++++ .../Controllers/Master/UsersController.php | 68 +++++-- app/Http/Controllers/MenusController.php | 136 ++++++++++++++ app/Http/Controllers/RolesController.php | 103 +++++++++++ .../Settings/SyncronizeController.php | 2 +- app/Http/Resources/UserResource.php | 1 + app/Models/Menu.php | 25 +++ app/Models/Role.php | 22 +++ app/Models/User.php | 4 + app/Providers/AppServiceProvider.php | 20 ++ app/{ => Services}/ServiceClient.php | 2 +- app/{ => Services}/ServiceSIMBG.php | 5 +- .../2025_02_10_104053_create_roles_table.php | 29 +++ .../2025_02_10_104254_create_menus_table.php | 33 ++++ ...25_02_10_104342_create_user_role_table.php | 29 +++ ...25_02_10_104912_create_role_menu_table.php | 29 +++ database/seeders/UsersRoleMenuSeeder.php | 173 ++++++++++++++++++ resources/js/master/users/users.js | 19 +- resources/js/menus/index.js | 109 +++++++++++ resources/js/roles/index.js | 103 +++++++++++ resources/views/auth/signin.blade.php | 4 +- .../views/layouts/partials/sidebar.blade.php | 123 +++---------- .../views/layouts/partials/topbar.blade.php | 2 +- resources/views/master/users/create.blade.php | 11 +- resources/views/master/users/edit.blade.php | 31 ++-- resources/views/menus/create.blade.php | 56 ++++++ resources/views/menus/edit.blade.php | 57 ++++++ resources/views/menus/index.blade.php | 24 +++ resources/views/pages/404.blade.php | 6 +- resources/views/roles/create.blade.php | 37 ++++ resources/views/roles/edit.blade.php | 38 ++++ resources/views/roles/index.blade.php | 24 +++ routes/api.php | 8 + routes/web.php | 15 +- vite.config.js | 5 +- 37 files changed, 1326 insertions(+), 151 deletions(-) create mode 100644 app/Http/Controllers/Api/MenusController.php create mode 100644 app/Http/Controllers/Api/RolesController.php create mode 100644 app/Http/Controllers/MenusController.php create mode 100644 app/Http/Controllers/RolesController.php create mode 100644 app/Models/Menu.php create mode 100644 app/Models/Role.php rename app/{ => Services}/ServiceClient.php (98%) rename app/{ => Services}/ServiceSIMBG.php (99%) create mode 100644 database/migrations/2025_02_10_104053_create_roles_table.php create mode 100644 database/migrations/2025_02_10_104254_create_menus_table.php create mode 100644 database/migrations/2025_02_10_104342_create_user_role_table.php create mode 100644 database/migrations/2025_02_10_104912_create_role_menu_table.php create mode 100644 database/seeders/UsersRoleMenuSeeder.php create mode 100644 resources/js/menus/index.js create mode 100644 resources/js/roles/index.js create mode 100644 resources/views/menus/create.blade.php create mode 100644 resources/views/menus/edit.blade.php create mode 100644 resources/views/menus/index.blade.php create mode 100644 resources/views/roles/create.blade.php create mode 100644 resources/views/roles/edit.blade.php create mode 100644 resources/views/roles/index.blade.php diff --git a/app/Console/Commands/ExecuteScraping.php b/app/Console/Commands/ExecuteScraping.php index 77d4e21..9cee57b 100644 --- a/app/Console/Commands/ExecuteScraping.php +++ b/app/Console/Commands/ExecuteScraping.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\ServiceSIMBG; +use App\Services\ServiceSIMBG; use Illuminate\Console\Command; use \Illuminate\Support\Facades\Log; @@ -25,10 +25,16 @@ class ExecuteScraping extends Command /** * Execute the console command. */ + + private $service_simbg; + + public function __construct(ServiceSIMBG $service_simbg){ + $this->service_simbg = $service_simbg; + parent::__construct(); + } public function handle() { Log::info("running scheduler daily scraping"); - $service = new ServiceSIMBG(); - $service->syncTaskList(); + $this->service_simbg->syncTaskList(); } } diff --git a/app/Http/Controllers/Api/MenusController.php b/app/Http/Controllers/Api/MenusController.php new file mode 100644 index 0000000..95913c6 --- /dev/null +++ b/app/Http/Controllers/Api/MenusController.php @@ -0,0 +1,56 @@ +has("search") && !empty($request->get("search"))){ + $query = $query->where("name", "like", "%".$request->get("search")."%"); + } + + return response()->json($query->paginate()); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Http/Controllers/Api/RolesController.php b/app/Http/Controllers/Api/RolesController.php new file mode 100644 index 0000000..7a7774b --- /dev/null +++ b/app/Http/Controllers/Api/RolesController.php @@ -0,0 +1,56 @@ +has('search') && !empty($request->get('search'))){ + $query = $query->where('name', 'like', '%'. $request->get('search') . '%'); + } + + return response()->json($query->paginate()); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Http/Controllers/Master/UsersController.php b/app/Http/Controllers/Master/UsersController.php index 1f72c10..88d3388 100644 --- a/app/Http/Controllers/Master/UsersController.php +++ b/app/Http/Controllers/Master/UsersController.php @@ -3,7 +3,10 @@ namespace App\Http\Controllers\Master; use App\Http\Controllers\Controller; +use App\Models\Role; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; +use Illuminate\Validation\Rule; use Illuminate\Validation\Rules; use Illuminate\Support\Facades\Hash; use App\Models\User; @@ -22,7 +25,8 @@ class UsersController extends Controller return view('master.users.index', compact('users')); } public function create(){ - return view('master.users.create'); + $roles = Role::all(); + return view('master.users.create', compact('roles')); } public function store(Request $request){ $request->validate([ @@ -31,21 +35,29 @@ class UsersController extends Controller 'password' => ['required', 'confirmed', 'max:255'], 'firstname' => ['required', 'string', 'max:255'], 'lastname' => ['required', 'string', 'max:255'], - 'position' => ['required', 'string', 'max:255'] + 'position' => ['required', 'string', 'max:255'], + 'role_id' => 'required|exists:roles,id' ]); - // dd($request); + DB::beginTransaction(); + try{ + $user = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'password' => Hash::make($request->password), + 'firstname' => $request->firstname, + 'lastname' => $request->lastname, + 'position' => $request->position + ]); - $user = User::create([ - 'name' => $request->name, - 'email' => $request->email, - 'password' => Hash::make($request->password), - 'firstname' => $request->firstname, - 'lastname' => $request->lastname, - 'position' => $request->position - ]); + $user->roles()->attach($request->role_id); - return redirect()->route('users.index')->with('success','Successfully registered'); + DB::commit(); + return redirect()->route('users.index')->with('success','Successfully registered'); + }catch(\Exception $e){ + DB::rollBack(); + return redirect()->back()->with("error", $e->getMessage()); + }; } public function show($id){ $user = User::find($id); @@ -53,24 +65,40 @@ class UsersController extends Controller } public function edit($id){ $user = User::find($id); - return view('master.users.edit', compact('user')); + $roles = Role::all(); + return view('master.users.edit', compact('user', 'roles')); } public function update(Request $request, $id){ $user = User::find($id); - $validate = $request->validate([ + $validatedData = $request->validate([ 'name' => ['required', 'string', 'max:255'], - 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], - 'password' => ['required', 'confirmed', Rules\Password::defaults()], + 'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users')->ignore($id)], 'firstname' => ['required', 'string', 'max:255'], 'lastname' => ['required', 'string', 'max:255'], - 'position' => ['required', 'string', 'max:255'] + 'position' => ['required', 'string', 'max:255'], + 'role_id' => ['required', 'exists:roles,id'], ]); - $user->update($validate); - return redirect()->route('master.users')->with('success', 'Successfully'); + try{ + DB::beginTransaction(); + $updateData = [ + 'name' => $validatedData['name'], + 'email' => $validatedData['email'], + 'firstname' => $validatedData['firstname'], + 'lastname' => $validatedData['lastname'], + 'position' => $validatedData['position'], + ]; + $user->update($updateData); + $user->roles()->sync([$request->role_id]); + DB::commit(); + return redirect()->route('users.index')->with('success', 'Successfully'); + }catch(\Exception $e){ + DB::rollBack(); + return redirect()->back()->with("error", $e->getMessage()); + } } public function destroy($id){ $user = User::find($id); $user->delete(); - return redirect()->route('master.users')->with('success','Successfully deleted'); + return redirect()->route('users.index')->with('success','Successfully deleted'); } } diff --git a/app/Http/Controllers/MenusController.php b/app/Http/Controllers/MenusController.php new file mode 100644 index 0000000..53d59c8 --- /dev/null +++ b/app/Http/Controllers/MenusController.php @@ -0,0 +1,136 @@ +get(); + return view("menus.create", compact('parent_menus')); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + try{ + $request->validate([ + 'name' => 'required|string|max:255', + 'url' => 'nullable|string|max:255', + 'icon' => 'nullable|string|max:255', + 'parent_id' => 'nullable|exists:menus,id', // Ensures it's either null or a valid menu ID + 'sort_order' => 'required|integer', + ]); + DB::beginTransaction(); + Menu::create([ + 'name' => $request->name, + 'url' => $request->url, + 'icon' => $request->icon, + 'parent_id' => $request->parent_id ?? null, + 'sort_order' => $request->sort_order, + ]); + DB::commit(); + return redirect()->route('menus.index')->with('success','Success created menu'); + }catch(\Exception $e){ + DB::rollBack(); + \Log::error('Menu creation failed: ' . $e->getMessage()); // Log the error for debugging + return redirect()->back() + ->withInput() + ->withErrors('Something went wrong! Please try again.'); + } + + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + $menu = Menu::findOrFail($id); + $parent_menus = Menu::whereNull('parent_id')->where('id','!=',$id)->get(); + return view("menus.edit", compact('menu','parent_menus')); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + try{ + $validateData = $request->validate([ + 'name' => 'required', + 'url'=> 'required', + 'icon'=> 'nullable', + 'parent_id' => 'nullable', + 'sort_order' => 'required', + ]); + + $menu = Menu::findOrFail($id); + + DB::beginTransaction(); + $menu->update($validateData); + DB::commit(); + return redirect()->route('menus.index')->with('success','Successfully updated'); + }catch(\Exception $e){ + DB::rollBack(); + \Log::error('Menu update failed: '. $e->getMessage()); // Log the error for debugging + return redirect()->back() + ->withInput() + ->withErrors('Something went wrong! Please try again.'); + } + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + try{ + DB::beginTransaction(); + $menu = Menu::findOrFail($id); + $this->deleteChildren($menu); + $menu->roles()->detach(); + $menu->delete(); + DB::commit(); + return response()->json(['success' => true, 'message' => 'Successfully deleted']); + }catch(\Exception $e){ + DB::rollBack(); + \Log::error('failed delete menu'. $e->getMessage()); + return response()->json(['success' => false, 'message' => 'Something went wrong! Please try again.']); + } + } + + private function deleteChildren($menu) + { + foreach ($menu->children as $child) { + $this->deleteChildren($child); // Recursively delete its children + $child->roles()->detach(); // Detach roles before deleting + $child->delete(); + } + } +} diff --git a/app/Http/Controllers/RolesController.php b/app/Http/Controllers/RolesController.php new file mode 100644 index 0000000..9e8c549 --- /dev/null +++ b/app/Http/Controllers/RolesController.php @@ -0,0 +1,103 @@ +validate([ + "name" => "required", + "description" => "nullable", + ]); + + DB::beginTransaction(); + Role::create($request->all()); + DB::commit(); + return redirect()->route("roles.index")->with('success','Succesfully Created'); + } + catch(\Exception $e){ + DB::rollBack(); + return redirect()->back()->with("error", $e->getMessage()); + } + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + $role = Role::findOrFail($id); + return view("roles.edit", compact('role')); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + try{ + $role = Role::findOrFail($id); + // Validate request data + $validatedData = $request->validate([ + 'name' => 'required|string|max:255|unique:roles,name,' . $id, // Ensure name is unique except for the current role + 'description' => 'nullable|string|max:500', + ]); + + DB::beginTransaction(); + $role->update($validatedData); + DB::commit(); + return redirect()->route('roles.index')->with('success','Successfully updated'); + }catch(\Exception $e){ + DB::rollBack(); + return redirect()->back()->with("error", $e->getMessage()); + } + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + try{ + DB::beginTransaction(); + Role::findOrFail($id)->delete(); + DB::commit(); + }catch(\Exception $e){ + DB::rollBack(); + return redirect()->back()->with("error", $e->getMessage()); + } + } +} diff --git a/app/Http/Controllers/Settings/SyncronizeController.php b/app/Http/Controllers/Settings/SyncronizeController.php index 08644cc..65548df 100644 --- a/app/Http/Controllers/Settings/SyncronizeController.php +++ b/app/Http/Controllers/Settings/SyncronizeController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers\Settings; use App\Http\Controllers\Controller; -use App\ServiceSIMBG; +use App\Services\ServiceSIMBG; use Illuminate\Http\Request; use Exception; class SyncronizeController extends Controller diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index bbe7c69..3fc1acb 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -23,6 +23,7 @@ class UserResource extends JsonResource 'position' => $this->position, 'firstname' => $this->firstname, 'lastname' => $this->lastname, + 'roles' => $this->roles->pluck('name'), ]; } } diff --git a/app/Models/Menu.php b/app/Models/Menu.php new file mode 100644 index 0000000..7dcd69f --- /dev/null +++ b/app/Models/Menu.php @@ -0,0 +1,25 @@ +belongsToMany(Role::class, 'role_menu')->withTimestamps(); + } + + public function children(){ + return $this->hasMany(Menu::class,'parent_id'); + } +} diff --git a/app/Models/Role.php b/app/Models/Role.php new file mode 100644 index 0000000..460b7d8 --- /dev/null +++ b/app/Models/Role.php @@ -0,0 +1,22 @@ +belongsToMany(User::class,'user_role')->withTimestamps(); + } + + public function menus(){ + return $this->belongsToMany(Menu::class,'role_menu')->withTimestamps(); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 3c1f934..32df2cf 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -49,4 +49,8 @@ class User extends Authenticatable 'password' => 'hashed', ]; } + + public function roles(){ + return $this->belongsToMany(Role::class, 'user_role')->withTimestamps(); + } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3d20250..2d9929f 100755 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,8 +2,11 @@ namespace App\Providers; +use App\Models\Menu; use App\View\Components\Circle; +use Auth; use Illuminate\Support\Facades\Blade; +use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; use Carbon\Carbon; @@ -23,5 +26,22 @@ class AppServiceProvider extends ServiceProvider public function boot(): void { Blade::component('circle', Circle::class); + + View::composer('layouts.partials.sidebar', function ($view){ + $user = Auth::user(); + + if($user){ + $menus = Menu::whereHas('roles', function ($query) use ($user){ + $query->where('roles.id', $user->roles->pluck('id')); + }) + ->with('children') + ->orderBy('sort_order', 'asc') + ->get(); + }else{ + $menus = collect(); + } + + $view->with('menus', $menus); + }); } } diff --git a/app/ServiceClient.php b/app/Services/ServiceClient.php similarity index 98% rename from app/ServiceClient.php rename to app/Services/ServiceClient.php index be48fd5..5a0e199 100644 --- a/app/ServiceClient.php +++ b/app/Services/ServiceClient.php @@ -1,6 +1,6 @@ email = trim((string) GlobalSetting::where('key','SIMBG_EMAIL')->first()->value); $this->password = trim((string) GlobalSetting::where('key','SIMBG_PASSWORD')->first()->value); diff --git a/database/migrations/2025_02_10_104053_create_roles_table.php b/database/migrations/2025_02_10_104053_create_roles_table.php new file mode 100644 index 0000000..c29c4a9 --- /dev/null +++ b/database/migrations/2025_02_10_104053_create_roles_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('name')->unique(); + $table->text('description')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('roles'); + } +}; diff --git a/database/migrations/2025_02_10_104254_create_menus_table.php b/database/migrations/2025_02_10_104254_create_menus_table.php new file mode 100644 index 0000000..707aadd --- /dev/null +++ b/database/migrations/2025_02_10_104254_create_menus_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('name'); + $table->string('url'); + $table->string('icon')->nullable(); + $table->unsignedBigInteger('parent_id')->nullable(); + $table->integer('sort_order')->default(0); + $table->foreign('parent_id')->references('id')->on('menus')->onDelete('set null'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('menus'); + } +}; diff --git a/database/migrations/2025_02_10_104342_create_user_role_table.php b/database/migrations/2025_02_10_104342_create_user_role_table.php new file mode 100644 index 0000000..825f39e --- /dev/null +++ b/database/migrations/2025_02_10_104342_create_user_role_table.php @@ -0,0 +1,29 @@ +foreignId('user_id')->constrained()->onDelete('cascade'); + $table->foreignId('role_id')->constrained()->onDelete('cascade'); + $table->primary(['user_id', 'role_id']); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_role'); + } +}; diff --git a/database/migrations/2025_02_10_104912_create_role_menu_table.php b/database/migrations/2025_02_10_104912_create_role_menu_table.php new file mode 100644 index 0000000..5d522cd --- /dev/null +++ b/database/migrations/2025_02_10_104912_create_role_menu_table.php @@ -0,0 +1,29 @@ +foreignId('role_id')->constrained()->cascadeOnDelete(); + $table->foreignId('menu_id')->constrained()->cascadeOnDelete(); + $table->primary(['role_id', 'menu_id']); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('role_menu'); + } +}; diff --git a/database/seeders/UsersRoleMenuSeeder.php b/database/seeders/UsersRoleMenuSeeder.php new file mode 100644 index 0000000..21878a2 --- /dev/null +++ b/database/seeders/UsersRoleMenuSeeder.php @@ -0,0 +1,173 @@ + "superadmin", + "description" => "show all menus for super admins", + ], + [ + "name" => "admin", + "description" => "show only necessary menus for admins", + ], + [ + "name" => "operator", + "description" => "show only necessary menus for operators", + ] + ]; + + Role::upsert($roles, ['name']); + + $parent_menus = [ + [ + "name" => "Dashboard", + "url" => "/dashboard", + "icon" => "mingcute:home-3-line", + "parent_id" => null, + "sort_order" => 1, + ], + [ + "name" => "Master", + "url" => "/master", + "icon" => "mingcute:cylinder-line", + "parent_id" => null, + "sort_order" => 2, + ], + [ + "name" => "Settings", + "url" => "/settings", + "icon" => "mingcute:settings-6-line", + "parent_id" => null, + "sort_order" => 3, + ], + [ + "name" => "Data Settings", + "url" => "/data-settings", + "icon" => "mingcute:settings-1-line", + "parent_id" => null, + "sort_order" => 4, + ], + [ + "name" => "Data", + "url" => "/data", + "icon" => "mingcute:task-line", + "parent_id" => null, + "sort_order" => 5, + ] + ]; + + foreach ($parent_menus as $parent_menu) { + Menu::create($parent_menu); + } + + // Attach Menus to Roles + $superadmin = Role::where('name', 'superadmin')->first(); + $admin = Role::where('name', 'admin')->first(); + $operator = Role::where('name', 'operator')->first(); + + $dashboard = Menu::where('name', 'Dashboard')->first(); + $master = Menu::where('name', 'Master')->first(); + $settings = Menu::where('name', 'Settings')->first(); + $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", + "url" => "dashboard.home", + "icon" => null, + "parent_id" => $dashboard->id, + "sort_order" => 1, + ], + [ + "name" => "Dashboard PBG", + "url" => "dashboard.pbg", + "icon" => null, + "parent_id" => $dashboard->id, + "sort_order" => 2, + ], + [ + "name" => "Users", + "url" => "users.index", + "icon" => null, + "parent_id" => $master->id, + "sort_order" => 1, + ], + [ + "name" => "Syncronize", + "url" => "settings.syncronize", + "icon" => null, + "parent_id" => $settings->id, + "sort_order" => 1, + ], + [ + "name" => "Menu", + "url" => "menus.index", + "icon" => null, + "parent_id" => $settings->id, + "sort_order" => 2, + ], + [ + "name" => "Role", + "url" => "roles.index", + "icon" => null, + "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", + "icon" => null, + "parent_id" => $dataSettings->id, + "sort_order" => 1, + ], + [ + "name" => "PBG", + "url" => "pbg-task.index", + "icon" => null, + "parent_id" => $data->id, + "sort_order" => 1, + ], + ]; + + foreach ($children_menus as $child_menu) { + Menu::create($child_menu); + } + } +} diff --git a/resources/js/master/users/users.js b/resources/js/master/users/users.js index 90a3b13..ade7008 100644 --- a/resources/js/master/users/users.js +++ b/resources/js/master/users/users.js @@ -17,6 +17,15 @@ class UsersTable { "Firstname", "Lastname", "Position", + "Roles", + { + name: "Action", + formatter: (cell) => + gridjs.html(` +
+ Update + `), + }, ], pagination: { limit: 15, @@ -42,15 +51,19 @@ class UsersTable { .getAttribute("content")}`, "Content-Type": "application/json", }, - then: (data) => - data.data.map((item) => [ + then: (data) => { + console.log(data.data); + return data.data.map((item) => [ item.id, item.name, item.email, item.firstname, item.lastname, item.position, - ]), + item.roles, + item.id, + ]); + }, total: (data) => data.meta.total, }, }).render(document.getElementById("table-users")); diff --git a/resources/js/menus/index.js b/resources/js/menus/index.js new file mode 100644 index 0000000..ec6ca2b --- /dev/null +++ b/resources/js/menus/index.js @@ -0,0 +1,109 @@ +import { Grid } from "gridjs/dist/gridjs.umd.js"; +import gridjs from "gridjs/dist/gridjs.umd.js"; +import "gridjs/dist/gridjs.umd.js"; +import GlobalConfig from "../global-config"; + +class Menus { + init() { + this.initTableMenus(); + } + + initTableMenus() { + new Grid({ + columns: [ + "ID", + "Name", + "Url", + "Icon", + "ParentID", + "Sort Order", + { + name: "Action", + formatter: (cell) => + gridjs.html(` +
+ Update + + `), + }, + ], + pagination: { + limit: 15, + server: { + url: (prev, page) => + `${prev}${prev.includes("?") ? "&" : "?"}page=${ + page + 1 + }`, + }, + }, + sort: true, + search: { + server: { + url: (prev, keyword) => `${prev}?search=${keyword}`, + }, + }, + server: { + url: `${GlobalConfig.apiHost}/api/api-menus`, + credentials: "include", + headers: { + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, + "Content-Type": "application/json", + }, + then: (data) => + data.data.map((item) => [ + item.id, + item.name, + item.url, + item.icon, + item.parent_id, + item.sort_order, + item.id, + ]), + total: (data) => data.total, + }, + }).render(document.getElementById("table-menus")); + + document.addEventListener("click", this.handleDelete); + } + + handleDelete(event) { + if (event.target.classList.contains("btn-delete-menu")) { + event.preventDefault(); + const id = event.target.getAttribute("data-id"); + + if (confirm("Are you sure you want to delete this item?")) { + fetch(`/menus/${id}`, { + method: "DELETE", + headers: { + "X-CSRF-TOKEN": document + .querySelector('meta[name="csrf-token"]') + .getAttribute("content"), + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.ok) { + alert("Item deleted successfully!"); + window.location.reload(); + } else { + return response.json().then((error) => { + throw new Error( + error.message || "Failed to delete item." + ); + }); + } + }) + .catch((error) => { + console.error("Error deleting item:", error); + alert("Something went wrong. Please try again."); + }); + } + } + } +} + +document.addEventListener("DOMContentLoaded", function (e) { + new Menus().init(); +}); diff --git a/resources/js/roles/index.js b/resources/js/roles/index.js new file mode 100644 index 0000000..6bf72e1 --- /dev/null +++ b/resources/js/roles/index.js @@ -0,0 +1,103 @@ +import { Grid } from "gridjs/dist/gridjs.umd.js"; +import gridjs from "gridjs/dist/gridjs.umd.js"; +import "gridjs/dist/gridjs.umd.js"; +import GlobalConfig from "../global-config"; + +class Roles { + init() { + this.initTableRoles(); + } + + initTableRoles() { + new Grid({ + columns: [ + "ID", + "Name", + "Description", + { + name: "Action", + formatter: (cell) => + gridjs.html(` +
+ Update + + `), + }, + ], + pagination: { + limit: 15, + server: { + url: (prev, page) => + `${prev}${prev.includes("?") ? "&" : "?"}page=${ + page + 1 + }`, + }, + }, + sort: true, + search: { + server: { + url: (prev, keyword) => `${prev}?search=${keyword}`, + }, + }, + server: { + url: `${GlobalConfig.apiHost}/api/api-roles`, + credentials: "include", + headers: { + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, + "Content-Type": "application/json", + }, + then: (data) => + data.data.map((item) => [ + item.id, + item.name, + item.description, + item.id, + ]), + total: (data) => data.total, + }, + }).render(document.getElementById("table-roles")); + + document.addEventListener("click", this.handleDelete); + } + + handleDelete(event) { + if (event.target.classList.contains("btn-delete-role")) { + event.preventDefault(); + const id = event.target.getAttribute("data-id"); + + if (confirm("Are you sure you want to delete this item?")) { + fetch(`/roles/${id}`, { + method: "DELETE", + headers: { + "X-CSRF-TOKEN": document + .querySelector('meta[name="csrf-token"]') + .getAttribute("content"), + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.ok) { + alert("Item deleted successfully!"); + window.location.reload(); + } else { + return response.json().then((error) => { + throw new Error( + error.message || "Failed to delete item." + ); + }); + } + }) + .catch((error) => { + console.error("Error deleting item:", error); + alert("Something went wrong. Please try again."); + }); + } + } + } +} + +document.addEventListener("DOMContentLoaded", function (e) { + new Roles().init(); +}); diff --git a/resources/views/auth/signin.blade.php b/resources/views/auth/signin.blade.php index acabffd..1fd45cf 100755 --- a/resources/views/auth/signin.blade.php +++ b/resources/views/auth/signin.blade.php @@ -13,11 +13,11 @@ class="authentication-bg"
diff --git a/resources/views/layouts/partials/sidebar.blade.php b/resources/views/layouts/partials/sidebar.blade.php index 329763e..3433705 100644 --- a/resources/views/layouts/partials/sidebar.blade.php +++ b/resources/views/layouts/partials/sidebar.blade.php @@ -1,114 +1,47 @@
-
diff --git a/resources/views/layouts/partials/topbar.blade.php b/resources/views/layouts/partials/topbar.blade.php index 39795b6..547c711 100755 --- a/resources/views/layouts/partials/topbar.blade.php +++ b/resources/views/layouts/partials/topbar.blade.php @@ -172,7 +172,7 @@ class="align-middle me-2 fs-18">Help - + Lock screen diff --git a/resources/views/master/users/create.blade.php b/resources/views/master/users/create.blade.php index 755f8c4..ef3f6ac 100644 --- a/resources/views/master/users/create.blade.php +++ b/resources/views/master/users/create.blade.php @@ -26,7 +26,7 @@ placeholder="Enter your password" required>
- +
@@ -45,6 +45,15 @@
+
+ + +
diff --git a/resources/views/master/users/edit.blade.php b/resources/views/master/users/edit.blade.php index a4b37cb..53350d7 100644 --- a/resources/views/master/users/edit.blade.php +++ b/resources/views/master/users/edit.blade.php @@ -8,40 +8,47 @@
-
+ @csrf + @method("put")
+ class="form-control" placeholder="Enter your name" value="{{$user->name}}" required>
-
-
- - + class="form-control" placeholder="Enter your email" value="{{$user->email}}" required>
+ placeholder="Enter your firstname" value="{{$user->firstname}}" required>
+ placeholder="Enter your lastname" value="{{$user->lastname}}" required>
+ placeholder="Enter your position" value="{{$user->position}}" required> +
+
+ +
- +
diff --git a/resources/views/menus/create.blade.php b/resources/views/menus/create.blade.php new file mode 100644 index 0000000..8666454 --- /dev/null +++ b/resources/views/menus/create.blade.php @@ -0,0 +1,56 @@ +@extends('layouts.vertical', ['subtitle' => 'Menu']) + +@section('css') +@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) +@endsection + +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Menu']) + +
+
+
+
+
+ @csrf +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+ +@endsection + +@section('scripts') +@endsection \ No newline at end of file diff --git a/resources/views/menus/edit.blade.php b/resources/views/menus/edit.blade.php new file mode 100644 index 0000000..6be23d6 --- /dev/null +++ b/resources/views/menus/edit.blade.php @@ -0,0 +1,57 @@ +@extends('layouts.vertical', ['subtitle' => 'Menu']) + +@section('css') +@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) +@endsection + +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Menu']) + +
+
+
+
+
id)}}" method="post"> + @csrf + @method("put") +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+ +@endsection + +@section('scripts') +@endsection \ No newline at end of file diff --git a/resources/views/menus/index.blade.php b/resources/views/menus/index.blade.php new file mode 100644 index 0000000..481189b --- /dev/null +++ b/resources/views/menus/index.blade.php @@ -0,0 +1,24 @@ +@extends('layouts.vertical', ['subtitle' => 'Menu']) + +@section('css') +@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) +@endsection + +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Menu']) + +
+ +
+
+
+
+ +@endsection + +@section('scripts') +@vite(['resources/js/menus/index.js']) +@endsection \ No newline at end of file diff --git a/resources/views/pages/404.blade.php b/resources/views/pages/404.blade.php index b288254..4d31d01 100755 --- a/resources/views/pages/404.blade.php +++ b/resources/views/pages/404.blade.php @@ -17,11 +17,11 @@ class="authentication-bg"
@@ -32,7 +32,7 @@ class="authentication-bg"

The page you're trying to reach seems to have gone
missing in the digital wilderness.

diff --git a/resources/views/roles/create.blade.php b/resources/views/roles/create.blade.php new file mode 100644 index 0000000..f2ab4ab --- /dev/null +++ b/resources/views/roles/create.blade.php @@ -0,0 +1,37 @@ +@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']) + +
+
+
+
+
+ @csrf +
+ + +
+
+ + +
+ +
+
+
+
+
+ +@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 new file mode 100644 index 0000000..8667d01 --- /dev/null +++ b/resources/views/roles/edit.blade.php @@ -0,0 +1,38 @@ +@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']) + +
+
+
+
+
id)}}" method="post"> + @csrf + @method("put") +
+ + +
+
+ + +
+ +
+
+
+
+
+ +@endsection + +@section('scripts') +@endsection \ No newline at end of file diff --git a/resources/views/roles/index.blade.php b/resources/views/roles/index.blade.php new file mode 100644 index 0000000..b2bf211 --- /dev/null +++ b/resources/views/roles/index.blade.php @@ -0,0 +1,24 @@ +@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']) + +
+
+ Create +
+
+
+
+
+ +@endsection + +@section('scripts') +@vite(['resources/js/roles/index.js']) +@endsection \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index d19fe51..210733c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -5,8 +5,10 @@ use App\Http\Controllers\Api\DataSettingController; use App\Http\Controllers\Api\GlobalSettingsController; use App\Http\Controllers\Api\GoogleSheetController; use App\Http\Controllers\Api\ImportDatasourceController; +use App\Http\Controllers\Api\MenusController; use App\Http\Controllers\Api\PbgTaskController; use App\Http\Controllers\Api\RequestAssignmentController; +use App\Http\Controllers\Api\RolesController; use App\Http\Controllers\Api\ScrapingController; use App\Http\Controllers\Api\UsersController; use App\Http\Controllers\Settings\SyncronizeController; @@ -57,6 +59,12 @@ Route::group(['middleware' => 'auth:sanctum'], function (){ Route::get('/get-user-token', [SyncronizeController::class, 'getUserToken'])->name('api.task.token'); Route::get('/get-index-integration-retribution/{uuid}', [SyncronizeController::class, 'syncIndexIntegration'])->name('api.task.inntegration'); Route::get('/sync-task-submit/{uuid}', [SyncronizeController::class, 'syncTaskDetailSubmit'])->name('api.task.submit'); + + // menus api + Route::apiResource('api-menus', MenusController::class); + + // roles api + Route::apiResource('api-roles', RolesController::class); }); diff --git a/routes/web.php b/routes/web.php index 9bd7c29..5f7e9d7 100755 --- a/routes/web.php +++ b/routes/web.php @@ -4,7 +4,9 @@ use App\Http\Controllers\DataSettingController; use App\Http\Controllers\Dashboards\BigDataController; use App\Http\Controllers\Home\HomeController; use App\Http\Controllers\Master\UsersController; +use App\Http\Controllers\MenusController; use App\Http\Controllers\RequestAssignment\PbgTaskController; +use App\Http\Controllers\RolesController; use App\Http\Controllers\Settings\SettingsController; use App\Http\Controllers\Settings\SyncronizeController; use Illuminate\Support\Facades\Route; @@ -18,12 +20,9 @@ Route::group(['middleware' => 'auth'], function(){ //dashboards Route::group(['prefix' => '/dashboards'], function(){ - Route::get('/bigdata', [BigDataController::class, 'index'])->name('home'); + Route::get('/bigdata', [BigDataController::class, 'index'])->name('dashboard.home'); + Route::get('/dashboard-pbg', [BigDataController::class, 'pbg'])->name('dashboard.pbg'); }); - - Route::group(['prefix' => '/dashboards'], function(){ - Route::get('/dashboard-pbg', [BigDataController::class, 'pbg'])->name('dashboards.pbg'); - }); // settings Route::group(['prefix' => '/settings'], function(){ @@ -43,4 +42,10 @@ Route::group(['middleware' => 'auth'], function(){ // data settings Route::resource('/data-settings', DataSettingController::class); + + // menus + Route::resource('/menus', MenusController::class); + + // roles + Route::resource('/roles', RolesController::class); }); \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 7b9ce8e..5df2d80 100755 --- a/vite.config.js +++ b/vite.config.js @@ -6,7 +6,6 @@ export default defineConfig({ build: { outDir: "public/build", manifest: true, // Menghasilkan manifest.json untuk Laravel - }, resolve: { alias: { @@ -52,7 +51,9 @@ export default defineConfig({ "resources/js/data-settings/index.js", "resources/js/pbg-task/index.js", "resources/js/settings/general/general-settings.js", - "resources/js/tables/common-table.js" + "resources/js/tables/common-table.js", + "resources/js/menus/index.js", + "resources/js/roles/index.js", ], refresh: true, }),