From f1c3658166c82608da6733dd65930e6a99554391 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sat, 4 Jul 2026 12:35:18 +0700 Subject: [PATCH] feat: refactor EmployeeController and EmployeeService to utilize Role enum for role options; implement transaction management in EmployeeService methods for create, update, toggle status, and delete operations; enhance Employee model date formatting methods; update frontend components for improved UI and code clarity --- .../Admin/Hr/EmployeeController.php | 7 +- app/Models/Employee.php | 4 +- app/Services/Hr/EmployeeService.php | 109 ++++++++---------- .../js/pages/admin/hr/employees/Index.vue | 33 ++---- .../admin/hr/employees/form/EmployeeForm.vue | 9 +- .../pages/admin/hr/employees/table/columns.ts | 29 ++--- routes/web.php | 16 +-- tests/Feature/Admin/Hr/EmployeeTest.php | 89 +++++++++++++- 8 files changed, 177 insertions(+), 119 deletions(-) diff --git a/app/Http/Controllers/Admin/Hr/EmployeeController.php b/app/Http/Controllers/Admin/Hr/EmployeeController.php index 1e9fc01..73f4fe2 100644 --- a/app/Http/Controllers/Admin/Hr/EmployeeController.php +++ b/app/Http/Controllers/Admin/Hr/EmployeeController.php @@ -4,6 +4,7 @@ use App\Enums\EmploymentStatus; use App\Enums\Gender; +use App\Enums\Role; use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; @@ -46,7 +47,7 @@ public function index(Request $request): Response 'employment_status' => $employmentStatus, 'is_active' => $isActive, ]), - 'roles' => $this->employeeService->assignableRoleOptions(), + 'roles' => Role::assignableSelectOptions(), 'genders' => Gender::selectOptions(), 'employmentStatuses' => EmploymentStatus::selectOptions(), ]); @@ -57,7 +58,7 @@ public function create(): Response return Inertia::render('admin/hr/employees/Create', [ 'genders' => Gender::selectOptions(), 'employmentStatuses' => EmploymentStatus::selectOptions(), - 'roles' => $this->employeeService->assignableRoleOptions(), + 'roles' => Role::assignableSelectOptions(), ]); } @@ -77,7 +78,7 @@ public function edit(User $user): Response return Inertia::render('admin/hr/employees/Edit', [ 'genders' => Gender::selectOptions(), 'employmentStatuses' => EmploymentStatus::selectOptions(), - 'roles' => $this->employeeService->assignableRoleOptions(), + 'roles' => Role::assignableSelectOptions(), 'employee' => $editData['employee'], 'profilePhoto' => $editData['profilePhoto'], ]); diff --git a/app/Models/Employee.php b/app/Models/Employee.php index 9d97c20..854525f 100644 --- a/app/Models/Employee.php +++ b/app/Models/Employee.php @@ -78,7 +78,7 @@ public function employmentStatusLabel(): Attribute public function joinDateFormatted(): Attribute { return Attribute::make( - get: fn () => $this->join_date ? Carbon::parse($this->join_date)->translatedFormat('l, d F Y') : '-', + get: fn () => $this->join_date?->translatedFormat('l, d F Y') ?? '-', ); } @@ -92,7 +92,7 @@ public function joinDateInput(): Attribute public function resignDateFormatted(): Attribute { return Attribute::make( - get: fn () => $this->resign_date ? Carbon::parse($this->resign_date)->translatedFormat('l, d F Y') : null, + get: fn () => $this->resign_date?->translatedFormat('l, d F Y'), ); } diff --git a/app/Services/Hr/EmployeeService.php b/app/Services/Hr/EmployeeService.php index ccae702..9bc2ee5 100644 --- a/app/Services/Hr/EmployeeService.php +++ b/app/Services/Hr/EmployeeService.php @@ -6,6 +6,8 @@ use App\Models\Employee; use App\Models\User; use App\Models\UserProfile; +use App\Services\Concerns\RunsInTransaction; +use App\Services\Concerns\SyncsPhotos; use App\Services\Media\MediaService; use App\Support\Media\MediaPresenter; use Illuminate\Contracts\Pagination\LengthAwarePaginator; @@ -17,6 +19,8 @@ class EmployeeService { + use RunsInTransaction, SyncsPhotos; + public function __construct( private readonly MediaService $mediaService, ) {} @@ -75,8 +79,8 @@ public function findForEdit(User $user): array public function create(array $validated): void { - try { - DB::transaction(function () use ($validated): void { + $this->runInTransaction( + function () use ($validated): void { $user = User::create([ 'email' => $validated['email'], 'username' => $validated['username'], @@ -104,26 +108,17 @@ public function create(array $validated): void } $user->syncRoles([$validated['role']]); - }); - } catch (ValidationException $e) { - throw $e; - } catch (\Throwable $e) { - Log::error('Gagal membuat karyawan: '.$e->getMessage(), [ - 'trace' => $e->getTraceAsString(), - ]); - - throw ValidationException::withMessages([ - 'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.', - ]); - } + }, + 'Gagal membuat karyawan', + ); } public function update(User $user, array $validated): void { $employee = $user->employee; - try { - DB::transaction(function () use ($validated, $user, $employee): void { + $this->runInTransaction( + function () use ($validated, $user, $employee): void { $user->update([ 'email' => $validated['email'], 'username' => $validated['username'], @@ -166,73 +161,65 @@ public function update(User $user, array $validated): void } $user->syncRoles([$validated['role']]); - }); - } catch (ValidationException $e) { - throw $e; - } catch (\Throwable $e) { - Log::error('Gagal memperbarui karyawan: '.$e->getMessage(), [ - 'trace' => $e->getTraceAsString(), - ]); - - throw ValidationException::withMessages([ - 'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.', - ]); - } + }, + 'Gagal memperbarui karyawan', + ); } public function toggleStatus(User $user, array $validated): void { - $user->update([ - 'is_active' => $validated['is_active'], - ]); + $this->runInTransaction( + function () use ($user, $validated): void { + $user->update([ + 'is_active' => $validated['is_active'], + ]); - if (! $validated['is_active']) { - DB::table('sessions')->where('user_id', $user->id)->delete(); - } + if (! $validated['is_active']) { + DB::table('sessions')->where('user_id', $user->id)->delete(); + } + }, + 'Gagal memperbarui status karyawan', + ); } public function resetPassword(User $user): void { - $user->update([ - 'password' => config('auth.password_default'), - ]); + $this->runInTransaction( + function () use ($user): void { + $user->update([ + 'password' => config('auth.password_default'), + ]); - DB::table('sessions')->where('user_id', $user->id)->delete(); + DB::table('sessions')->where('user_id', $user->id)->delete(); + }, + 'Gagal mereset kata sandi karyawan', + ); } public function delete(User $user): void { - try { - DB::transaction(function () use ($user): void { + $this->runInTransaction( + function () use ($user): void { $user->employee?->delete(); $user->profile?->delete(); $user->delete(); - }); - } catch (ValidationException $e) { - throw $e; - } catch (\Throwable $e) { - Log::error('Gagal menghapus karyawan: '.$e->getMessage(), [ - 'trace' => $e->getTraceAsString(), - ]); - - throw ValidationException::withMessages([ - 'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.', - ]); - } + }, + 'Gagal menghapus karyawan', + ); } private function syncProfilePhoto(UserProfile $profile, array $validated): void { - $s3Keys = ! empty($validated['profile_s3_key']) ? [$validated['profile_s3_key']] : null; - $removeIds = $validated['remove_profile_photo_ids'] ?? null; - - $this->mediaService->syncCollection( + $this->syncPhotos( $profile, - 'profile_photo', - null, - $removeIds, - 1, - s3Keys: $s3Keys, + [ + 'photos' => $validated['profile_photo'] ?? null, + 'remove_media_ids' => $validated['remove_profile_photo_ids'] ?? null, + 's3_keys' => ! empty($validated['profile_s3_key']) ? [$validated['profile_s3_key']] : null, + ], + maxPhotos: 1, + required: false, + collection: 'profile_photo', ); } diff --git a/resources/js/pages/admin/hr/employees/Index.vue b/resources/js/pages/admin/hr/employees/Index.vue index 19e9039..94c60cd 100644 --- a/resources/js/pages/admin/hr/employees/Index.vue +++ b/resources/js/pages/admin/hr/employees/Index.vue @@ -1,4 +1,6 @@