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(` +
The page you're trying to reach seems to have gone
missing in the digital wilderness.