From 4d3eedf3c002bed2483a6369a08ee7f9b30506b0 Mon Sep 17 00:00:00 2001 From: myuqinurrohman Date: Fri, 28 Feb 2025 17:35:11 +0700 Subject: [PATCH] feat: user management and role --- app/Helpers/CustomHelper.php | 14 ++ .../Dashboard/DatatableController.php | 114 +++++++++++++ .../Controllers/Dashboard/RoleController.php | 15 ++ .../Controllers/Dashboard/UserController.php | 160 ++++++++++++++++++ app/Http/Requests/ResetPasswordRequest.php | 58 +++++++ app/Http/Requests/UserRequest.php | 69 ++++++++ app/Models/Company.php | 8 + app/Models/Journalist.php | 4 + app/Models/Media.php | 8 + app/Models/Role.php | 4 + app/Models/User.php | 6 + app/Rules/MatchCurrentPasswordRule.php | 25 +++ .../0001_01_01_000000_create_users_table.php | 2 +- .../views/dashboard/role/index.blade.php | 61 +++++++ .../views/dashboard/users/create.blade.php | 106 ++++++++++++ .../views/dashboard/users/edit.blade.php | 83 +++++++++ .../views/dashboard/users/index.blade.php | 63 +++++++ .../dashboard/users/reset-password.blade.php | 78 +++++++++ .../views/dashboard/users/show.blade.php | 68 ++++++++ .../views/partials/app/datatable.blade.php | 47 +++++ .../views/partials/app/sweetalert.blade.php | 47 +++++ .../partials/dashboard/sidebar.blade.php | 4 +- routes/web.php | 27 +++ 23 files changed, 1068 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controllers/Dashboard/RoleController.php create mode 100644 app/Http/Controllers/Dashboard/UserController.php create mode 100644 app/Http/Requests/ResetPasswordRequest.php create mode 100644 app/Http/Requests/UserRequest.php create mode 100644 app/Rules/MatchCurrentPasswordRule.php create mode 100644 resources/views/dashboard/role/index.blade.php create mode 100644 resources/views/dashboard/users/create.blade.php create mode 100644 resources/views/dashboard/users/edit.blade.php create mode 100644 resources/views/dashboard/users/index.blade.php create mode 100644 resources/views/dashboard/users/reset-password.blade.php create mode 100644 resources/views/dashboard/users/show.blade.php diff --git a/app/Helpers/CustomHelper.php b/app/Helpers/CustomHelper.php index fbd4994..8a8ba19 100644 --- a/app/Helpers/CustomHelper.php +++ b/app/Helpers/CustomHelper.php @@ -114,3 +114,17 @@ function idn_date($tanggal, $format = 'd F Y') return Carbon::parse($tanggal)->locale('id')->translatedFormat($format); } } + +if (!function_exists('get_user_status')) { + function get_user_status($status) + { + switch($status){ + case '0': + return 'Menunggu'; + case '1': + return 'Aktif'; + case '2': + return 'Nonaktif'; + } + } +} diff --git a/app/Http/Controllers/Dashboard/DatatableController.php b/app/Http/Controllers/Dashboard/DatatableController.php index 2c64009..b47822e 100644 --- a/app/Http/Controllers/Dashboard/DatatableController.php +++ b/app/Http/Controllers/Dashboard/DatatableController.php @@ -8,7 +8,9 @@ use App\Models\ContentRecap; use App\Models\GovernmentAgency; use App\Models\News; +use App\Models\Role; use App\Models\SubClassification; +use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use SebastianBergmann\Type\TrueType; @@ -348,4 +350,116 @@ public function contentRecap(Request $request){ return response()->json(['error' => 'Unauthorized'], 401); } + + public function user(Request $request){ + if ($request->ajax()) { + + $roleParam = $request->input('role', null); + + if($roleParam){ + $role = Role::findOrFail(decrypt_id($roleParam)); + $data = User::with(['role'])->where('role_id', '=', $role->id)->get(); + }else{ + $data = User::with(['role'])->get(); + } + + return DataTables::of($data) + ->addIndexColumn() + ->addColumn('role', function ($data){ + return $data->role->name; + }) + ->addColumn('status', function ($data){ + switch($data->status){ + case '0': + return 'Menunggu'; + case '1': + return 'Aktif'; + case '2': + return 'Nonaktif'; + case '3': + return 'Ditolak'; + default: + return 'Tidak Diketahui'; + + } + }) + ->addColumn('action', function ($data) { + $showUrl = route('dashboard.users.show', ['id' => encrypt_id($data->id)]); + + $acceptUrl = route('dashboard.users.status.action', ['id' => encrypt_id($data->id), 'action' => 'accept']); + + $rejectUrl = route('dashboard.users.status.action', ['id' => encrypt_id($data->id), 'action' => 'reject']); + + $resetPasswordUrl = route('dashboard.users.reset.password', ['id' => encrypt_id($data->id)]); + + $editUrl = route('dashboard.users.edit', ['id' => encrypt_id($data->id)]); + + $deleteUrl = route('dashboard.users.destroy', ['id' => encrypt_id($data->id)]); + + $dropdown = ''; + + return $dropdown; + }) + + ->rawColumns(['status', 'action']) + ->make(true); + } + + return response()->json(['error' => 'Unauthorized'], 401); + + } + + public function role(Request $request){ + if ($request->ajax()) { + + $data = Role::with(['user'])->get(); + + return DataTables::of($data) + ->addIndexColumn() + ->addColumn('account_count', function ($data) { + $accountCount = $data->user->count(); + + if($accountCount > 0){ + return $accountCount. ' [Lihat]'; + }else{ + return $accountCount; + } + }) + + ->rawColumns(['account_count']) + ->make(true); + } + + return response()->json(['error' => 'Unauthorized'], 401); + + } } diff --git a/app/Http/Controllers/Dashboard/RoleController.php b/app/Http/Controllers/Dashboard/RoleController.php new file mode 100644 index 0000000..ca47d06 --- /dev/null +++ b/app/Http/Controllers/Dashboard/RoleController.php @@ -0,0 +1,15 @@ + 'Role Pengguna' + ]); + } +} diff --git a/app/Http/Controllers/Dashboard/UserController.php b/app/Http/Controllers/Dashboard/UserController.php new file mode 100644 index 0000000..6573330 --- /dev/null +++ b/app/Http/Controllers/Dashboard/UserController.php @@ -0,0 +1,160 @@ + 'Pengguna', + 'role' => $request->input('role', null) + ]); + } + + public function create(){ + return view('dashboard.users.create', [ + 'title' => 'Tambah Pengguna', + 'roles' => Role::all() + ]); + } + + public function store(UserRequest $request){ + $validatedData = $request->validated(); + + $createdUser = User::create($validatedData); + + if($createdUser){ + return redirect()->back()->with('success-status', 'Berhasil menambahkan pengguna.'); + }else{ + return redirect()->back()->with('failed-status', 'Gagal menambahkan pengguna.'); + } + + + } + + public function edit($id){ + $user = User::with(['role'])->findOrFail(decrypt_id($id)); + + return view('dashboard.users.edit', [ + 'title' => 'Ubah Pengguna', + 'user' => $user, + 'roles' => Role::all() + ]); + } + + public function update(UserRequest $request, $id){ + $validatedData = $request->validated(); + + if (isset($validatedData['password']) && !empty($validatedData['password'])) { + $validatedData['password'] = $request->input('password'); + } else { + unset($validatedData['password']); + } + + $user = User::findOrFail(decrypt_id($id)); + $updatedUser = $user->update($validatedData); + + if($updatedUser){ + return redirect()->back()->with('success-status', 'Berhasil mengubah pengguna.'); + }else{ + return redirect()->back()->with('failed-status', 'Gagal mengubah pengguna.'); + } + } + + public function show($id){ + $user = User::with(['role', 'company.media.journalists'])->findOrFail(decrypt_id($id)); + + $viewData = [ + 'title' => 'Detail Pengguna', + 'user' => $user, + 'roles' => Role::all() + ]; + + if($user->role_id === '3'){ + $viewData['company'] = $user->company; + $viewData['media'] = $user->company->media; + $viewData['journalists'] = $user->company->media->journalists; + } + + + return view('dashboard.users.show', $viewData); + } + + public function resetPassword($id){ + $user = User::with(['role'])->findOrFail(decrypt_id($id)); + + return view('dashboard.users.reset-password',[ + 'title' => 'Reset Password', + 'user' => $user + ]); + } + + public function resetPasswordAction(ResetPasswordRequest $request, $id){ + $validatedData = $request->validated(); + + $updatedUser = User::findOrFail(decrypt_id($id))->update([ + 'password' => $validatedData['password'] + ]); + + if($updatedUser){ + return redirect()->back()->with('success-status', 'Berhasil mereset password pengguna.'); + }else{ + return redirect()->back()->with('failed-status', 'Gagal mereset password pengguna.'); + } + } + + public function statusAction($id, $action){ + $user = User::findOrFail(decrypt_id($id)); + + if($user->status === '0'){ + if($action == 'accept'){ + $user->update([ + 'status' => '1' + ]); + }else if($action == 'reject'){ + $user->update([ + 'status' => '3' + ]); + } + } + } + + public function destroy($id) + { + try { + $user = User::with(['company.media.journalists'])->findOrFail(decrypt_id($id)); + + if ($user->role_id == '3') { + if ($user->company) { + $company = $user->company; + + if ($company->media) { + $media = $company->media; + + if ($media->journalists) { + $media->journalists->delete(); + } + + $media->delete(); + } + + $company->delete(); + } + } + + $user->delete(); + + return response()->json(['message' => 'Berhasil menghapus user beserta data terkait.']); + } catch (\Exception $e) { + return response()->json(['message' => 'Gagal menghapus user atau data terkait.'], 500); + } + } + +} diff --git a/app/Http/Requests/ResetPasswordRequest.php b/app/Http/Requests/ResetPasswordRequest.php new file mode 100644 index 0000000..6fc7c7c --- /dev/null +++ b/app/Http/Requests/ResetPasswordRequest.php @@ -0,0 +1,58 @@ +|string> + */ + public function rules(): array + { + return [ + 'password' => ['required', 'confirmed', Password::min(8) + ->mixedCase() + ->numbers() + ->symbols()], + 'my_password' => ['required', Password::min(8) + ->mixedCase() + ->numbers() + ->symbols(), new MatchCurrentPasswordRule()], + ]; + } + + public function messages() + { + return [ + 'password.required' => 'Password baru wajib diisi.', + 'password.confirmed' => 'Konfirmasi password baru tidak cocok.', + 'password.min' => 'Password baru harus memiliki minimal 8 karakter.', + 'password.mixedCase' => 'Password baru harus mengandung huruf besar dan huruf kecil.', + 'password.numbers' => 'Password baru harus mengandung angka.', + 'password.symbols' => 'Password baru harus mengandung simbol.', + + 'my_password.required' => 'Password Anda wajib diisi.', + 'my_password.confirmed' => 'Konfirmasi password Anda tidak cocok.', + 'my_password.min' => 'Password Anda harus memiliki minimal 8 karakter.', + 'my_password.mixedCase' => 'Password Anda harus mengandung huruf besar dan huruf kecil.', + 'my_password.numbers' => 'Password Anda harus mengandung angka.', + 'my_password.symbols' => 'Password Anda harus mengandung simbol.', + ]; + } + +} diff --git a/app/Http/Requests/UserRequest.php b/app/Http/Requests/UserRequest.php new file mode 100644 index 0000000..902fe88 --- /dev/null +++ b/app/Http/Requests/UserRequest.php @@ -0,0 +1,69 @@ +|string> + */ + public function rules(): array + { + $rules = [ + 'name' => ['required', 'max:100'], + 'email' => ['required', 'email', 'max:100'], + 'role_id' => ['required', 'in:1,2,3,4'], + 'status' => ['required', 'in:1,2'], + ]; + + if($this->isMethod('post')){ + $rules['password'] = ['required', 'confirmed', Password::min(8) + ->mixedCase() + ->numbers() + ->symbols()]; + } + + return $rules; + } + + public function messages() + { + return [ + 'name.required' => 'Nama wajib diisi.', + 'name.max' => 'Nama tidak boleh lebih dari 100 karakter.', + + 'email.required' => 'Email wajib diisi.', + 'email.email' => 'Email harus menggunakan format email yang valid.', + 'email.max' => 'Email tidak boleh lebih dari 100 karakter.', + + 'role_id.required' => 'Role wajib dipilih.', + 'role_id.in' => 'Role harus salah satu dari: Admin, Admin Monitoring, atau Admin Konten.', + + 'status.required' => 'Status wajib dipilih.', + 'status.in' => 'Status harus salah satu dari: Aktif, atau Nonaktif.', + + 'password.required' => 'Password wajib diisi.', + 'password.confirmed' => 'Password dan konfirmasi password tidak cocok.', + 'password.min' => 'Password harus memiliki minimal 8 karakter.', + 'password.mixedCase' => 'Password harus mengandung huruf besar dan huruf kecil.', + 'password.numbers' => 'Password harus mengandung angka.', + 'password.symbols' => 'Password harus mengandung simbol.', + ]; + } + +} diff --git a/app/Models/Company.php b/app/Models/Company.php index 0ba4ab2..69f1a51 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -33,4 +33,12 @@ class Company extends Model 'cooperation_request_doc', 'user_id' ]; + + public function user(){ + return $this->belongsTo(User::class, 'user_id', 'id'); + } + + public function media(){ + return $this->hasOne(Media::class, 'company_id', 'id'); + } } diff --git a/app/Models/Journalist.php b/app/Models/Journalist.php index c59d960..b945732 100644 --- a/app/Models/Journalist.php +++ b/app/Models/Journalist.php @@ -18,4 +18,8 @@ class Journalist extends Model 'ukw_sertificate_doc', 'media_id', ]; + + public function media(){ + return $this->belongsTo(Media::class, 'media_id', 'id'); + } } diff --git a/app/Models/Media.php b/app/Models/Media.php index 62b8dea..3d0f8b1 100644 --- a/app/Models/Media.php +++ b/app/Models/Media.php @@ -20,4 +20,12 @@ class Media extends Model 'press_council_certificate_doc', 'company_id', ]; + + public function company(){ + return $this->belongsTo(Company::class, 'company_id', 'id'); + } + + public function journalists(){ + return $this->hasMany(Journalist::class, 'media_id', 'id'); + } } diff --git a/app/Models/Role.php b/app/Models/Role.php index 8142d2c..ae36b74 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -12,4 +12,8 @@ class Role extends Model 'name', 'description' ]; + + public function user(){ + return $this->hasMany(User::class, 'role_id', 'id'); + } } diff --git a/app/Models/User.php b/app/Models/User.php index 813a384..d5ec27f 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -4,6 +4,8 @@ // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -51,4 +53,8 @@ protected function casts(): array public function role(){ return $this->belongsTo(Role::class, 'role_id', 'id'); } + + public function company(){ + return $this->hasOne(Company::class, 'user_id', 'id'); + } } diff --git a/app/Rules/MatchCurrentPasswordRule.php b/app/Rules/MatchCurrentPasswordRule.php new file mode 100644 index 0000000..4ea9eee --- /dev/null +++ b/app/Rules/MatchCurrentPasswordRule.php @@ -0,0 +1,25 @@ +password)) { + $fail('Kata sandi saat ini tidak sesuai.'); + } + } +} diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 33a8cf7..404cf69 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -17,7 +17,7 @@ public function up(): void $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); - $table->enum('status', ['0', '1', '2'])->default('0'); + $table->enum('status', ['0', '1', '2', '3'])->default('0'); $table->unsignedBigInteger('role_id'); $table->foreign('role_id')->references('id')->on('user_roles'); $table->rememberToken(); diff --git a/resources/views/dashboard/role/index.blade.php b/resources/views/dashboard/role/index.blade.php new file mode 100644 index 0000000..c295e07 --- /dev/null +++ b/resources/views/dashboard/role/index.blade.php @@ -0,0 +1,61 @@ +@include('partials.dashboard.head') +@include('partials.dashboard.sidebar', ['page' => 'roles']) +@include('partials.dashboard.header') + +
+
+
+
+
+
+
+

{{$title}}

+
+ {{--
+
+ +
+ +
+
+
--}} +
+
+
+
+
+
+ + + + + + + + + +
NoNamaJumlah Akun
+
+
+
+
+
+
+
+
+ +@include('partials.dashboard.footer' ,['datatable' => 'role']) diff --git a/resources/views/dashboard/users/create.blade.php b/resources/views/dashboard/users/create.blade.php new file mode 100644 index 0000000..e9f9535 --- /dev/null +++ b/resources/views/dashboard/users/create.blade.php @@ -0,0 +1,106 @@ +@include('partials.dashboard.head') +@include('partials.dashboard.sidebar', ['page' => 'users']) +@include('partials.dashboard.header') + +
+
+
+
+
+
+
+
+
{{$title}}
+
+
+ @csrf +
+
+
+ +
+ +
+ @error('name') + {{$message}} + @enderror +
+
+
+
+ +
+ +
+ @error('email') + {{$message}} + @enderror +
+
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+ +
+ @error('password') + {{$message}} + @enderror +
+
+
+
+ +
+ +
+ @error('password_confirmation') + {{$message}} + @enderror +
+
+
+
+
+ Kembali + +
+
+
+
+
+
+
+
+
+
+
+ +@include('partials.dashboard.footer', ['rich_editor' => false]) diff --git a/resources/views/dashboard/users/edit.blade.php b/resources/views/dashboard/users/edit.blade.php new file mode 100644 index 0000000..17da47f --- /dev/null +++ b/resources/views/dashboard/users/edit.blade.php @@ -0,0 +1,83 @@ +@include('partials.dashboard.head') +@include('partials.dashboard.sidebar', ['page' => 'users']) +@include('partials.dashboard.header') + +
+
+
+
+
+
+
+
+
{{$title}}
+
+
+ @csrf + @method('put') +
+
+
+ +
+ +
+ @error('name') + {{$message}} + @enderror +
+
+
+
+ +
+ +
+ @error('email') + {{$message}} + @enderror +
+
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+
+ Kembali + +
+
+
+
+
+
+
+
+
+
+
+ +@include('partials.dashboard.footer', ['rich_editor' => false]) diff --git a/resources/views/dashboard/users/index.blade.php b/resources/views/dashboard/users/index.blade.php new file mode 100644 index 0000000..9d05a3f --- /dev/null +++ b/resources/views/dashboard/users/index.blade.php @@ -0,0 +1,63 @@ +@include('partials.dashboard.head') +@include('partials.dashboard.sidebar', ['page' => 'users']) +@include('partials.dashboard.header') + +
+
+
+
+
+
+
+

{{$title}}

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+ + + + + + + + + + + +
NoNamaEmailRoleStatus
+
+
+
+
+
+
+
+
+ +@include('partials.dashboard.footer' ,['datatable' => 'user']) diff --git a/resources/views/dashboard/users/reset-password.blade.php b/resources/views/dashboard/users/reset-password.blade.php new file mode 100644 index 0000000..880b76f --- /dev/null +++ b/resources/views/dashboard/users/reset-password.blade.php @@ -0,0 +1,78 @@ +@include('partials.dashboard.head') +@include('partials.dashboard.sidebar', ['page' => 'users']) +@include('partials.dashboard.header') + +
+
+
+
+
+
+
+
+
+
Reset Kata Sandi
+
+

Ubah kata sandi akun dan login menggunakan kata sandi baru.

+
+
+
+
+ @csrf +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+ @error('password') + {{$message}} + @enderror +
+
+
+ +
+
+ +
+ @error('password_confirmation') + {{$message}} + @enderror +
+
+
+ +
+
+ +
+ @error('my_password') + {{$message}} + @enderror +
+
+ +
+
+
+ Kembali +
+
+
+
+
+
+
+
+ +@include('partials.dashboard.footer', ['rich_editor' => false]) diff --git a/resources/views/dashboard/users/show.blade.php b/resources/views/dashboard/users/show.blade.php new file mode 100644 index 0000000..a17c176 --- /dev/null +++ b/resources/views/dashboard/users/show.blade.php @@ -0,0 +1,68 @@ +@include('partials.dashboard.head') +@include('partials.dashboard.sidebar', ['page' => 'users']) +@include('partials.dashboard.header') + +
+
+
+
+
+
+
+
+
Detail Akun Pengguna
+
+
+ @csrf + @method('put') +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+
+ Kembali + Ubah +
+
+
+
+
+
+
+
+
+
+
+ +@include('partials.dashboard.footer', ['rich_editor' => false]) diff --git a/resources/views/partials/app/datatable.blade.php b/resources/views/partials/app/datatable.blade.php index 8c31290..99334e9 100644 --- a/resources/views/partials/app/datatable.blade.php +++ b/resources/views/partials/app/datatable.blade.php @@ -163,3 +163,50 @@ }); @endif + +@if ($datatable === 'user') + +@endif + +@if ($datatable === 'role') + +@endif diff --git a/resources/views/partials/app/sweetalert.blade.php b/resources/views/partials/app/sweetalert.blade.php index fc77df4..26f8123 100644 --- a/resources/views/partials/app/sweetalert.blade.php +++ b/resources/views/partials/app/sweetalert.blade.php @@ -45,6 +45,53 @@ function deleteItem(url, message) { }); } + @if (session('success-status'))