with(['profile', 'employee', 'roles']) ->whereDoesntHave('roles', fn ($query) => $query->where('name', Role::DEVELOPER->value)) ->when( $authUser->hasRole(Role::ADMIN_BAHAN_BAKU->value), fn ($query) => $query->whereHas('roles', fn ($query) => $query->where('name', Role::ADMIN_BAHAN_BAKU->value)) ) ->when( $authUser->hasAnyRole([Role::DIREKTUR->value, Role::ADMIN_TOKO->value]), fn ($query) => $query ->whereDoesntHave('roles', fn ($query) => $query->where('name', Role::OWNER->value)) ->whereDoesntHave('roles', fn ($query) => $query->where('name', Role::ADMIN_BAHAN_BAKU->value)) ) ->when($tableQuery['search'] !== '', function ($query) use ($tableQuery): void { $search = $tableQuery['search']; $query->where(function ($query) use ($search): void { $query->where('email', 'like', "%{$search}%") ->orWhere('username', 'like', "%{$search}%") ->orWhereHas('profile', function ($query) use ($search): void { $query->where('full_name', 'like', "%{$search}%") ->orWhere('phone_number', 'like', "%{$search}%"); }); }); }) ->when($role !== '', fn ($query) => $query->whereHas('roles', fn ($query) => $query->where('name', $role))) ->when($gender !== '', fn ($query) => $query->whereHas('profile', fn ($query) => $query->where('gender', $gender))) ->when($employmentStatus !== '', function ($query) use ($employmentStatus): void { $query->whereHas('employee', fn ($query) => $query->where('employment_status', $employmentStatus)); }) ->when($isActive !== '', fn ($query) => $query->where('is_active', $isActive === '1')); $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query ->paginate(25) ->withQueryString(); } public function assignableRoleOptions(): array { return $this->cacheRemember('system:assignable_roles', 86400, function (): array { return Role::assignableSelectOptions(); }); } public function findForEdit(User $user): array { $user->load(['profile', 'employee', 'roles']); return [ 'employee' => $user, 'profilePhoto' => $user->profile ? MediaPresenter::first($user->profile, 'profile_photo') : null, ]; } public function create(array $validated, User $user): void { $this->runInTransaction( function () use ($validated): User { $user = User::create([ 'email' => $validated['email'], 'username' => $validated['username'], 'password' => Hash::make(config('auth.password_default')), ]); $profile = UserProfile::create([ 'user_id' => $user->id, 'full_name' => $validated['full_name'], 'phone_number' => $validated['phone_number'], 'gender' => $validated['gender'], 'birth_date' => $validated['birth_date'], 'address' => $validated['address'], ]); $this->syncProfilePhoto($profile, $validated); if ($validated['role'] !== Role::OWNER->value) { Employee::create([ 'user_id' => $user->id, 'join_date' => $validated['join_date'], 'employment_status' => $validated['employment_status'], 'base_salary' => $validated['base_salary'], ]); } $user->syncRoles([$validated['role']]); return $user; }, 'Gagal membuat karyawan', ); $this->cacheForget('system:assignable_roles'); $this->cacheForgetByPattern('hr:employees:*'); $this->notifyOwner( 'Tambah Pegawai', "Pegawai '{$validated['full_name']}' telah ditambahkan oleh {$user->profile?->full_name}.", route('admin.hr.employees.index'), ); } public function update(User $user, array $validated, User $authUser): void { $employee = $user->employee; $this->runInTransaction( function () use ($validated, $user, $employee): void { $user->update([ 'email' => $validated['email'], 'username' => $validated['username'], ]); $profile = $user->profile()->updateOrCreate( ['user_id' => $user->id], [ 'full_name' => $validated['full_name'], 'phone_number' => $validated['phone_number'], 'gender' => $validated['gender'], 'birth_date' => $validated['birth_date'], 'address' => $validated['address'], ], ); $this->syncProfilePhoto($profile, $validated); $hasEmployee = ! empty($validated['join_date']) && ! empty($validated['employment_status']) && ! empty($validated['base_salary']); if ($hasEmployee) { if ($employee) { $employee->update([ 'join_date' => $validated['join_date'], 'employment_status' => $validated['employment_status'], 'base_salary' => $validated['base_salary'], ]); } else { Employee::create([ 'user_id' => $user->id, 'join_date' => $validated['join_date'], 'employment_status' => $validated['employment_status'], 'base_salary' => $validated['base_salary'], ]); } } else { if ($employee) { $employee->delete(); } } $user->syncRoles([$validated['role']]); }, 'Gagal memperbarui karyawan', ); $this->cacheForget('system:assignable_roles'); $this->cacheForgetByPattern('hr:employees:*'); $this->notifyOwner( 'Ubah Pegawai', "Pegawai '{$user->profile?->full_name}' telah diperbarui oleh {$authUser->profile?->full_name}.", route('admin.hr.employees.index'), ); } public function toggleStatus(User $user, array $validated, User $authUser): void { $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(); } }, 'Gagal memperbarui status karyawan', ); $this->cacheForget('system:assignable_roles'); $this->cacheForgetByPattern('hr:employees:*'); $statusLabel = $validated['is_active'] ? 'aktif' : 'nonaktif'; $this->notifyOwner( 'Ubah Status Pegawai', "Status pegawai '{$user->profile?->full_name}' telah diubah menjadi {$statusLabel} oleh {$authUser->profile?->full_name}.", route('admin.hr.employees.index'), ); } public function resetPassword(User $user, User $authUser): void { $this->runInTransaction( function () use ($user): void { $user->update([ 'password' => config('auth.password_default'), ]); DB::table('sessions')->where('user_id', $user->id)->delete(); }, 'Gagal mereset kata sandi karyawan', ); $this->cacheForget('system:assignable_roles'); $this->cacheForgetByPattern('hr:employees:*'); $this->notifyOwner( 'Reset Kata Sandi Pegawai', "Kata sandi pegawai '{$user->profile?->full_name}' telah direset oleh {$authUser->profile?->full_name}.", route('admin.hr.employees.index'), ); } public function delete(User $user, User $authUser): void { $name = $user->profile?->full_name; $this->runInTransaction( function () use ($user): void { $user->employee?->delete(); $user->profile?->delete(); $user->delete(); }, 'Gagal menghapus karyawan', ); $this->cacheForget('system:assignable_roles'); $this->cacheForgetByPattern('hr:employees:*'); $this->notifyOwner( 'Hapus Pegawai', "Pegawai '{$name}' telah dihapus oleh {$authUser->profile?->full_name}.", route('admin.hr.employees.index'), ); } private function notifyOwner(string $typeLabel, string $body, string $url): void { $this->pushNotificationService->sendToRoles( "📦 {$typeLabel}", $body, ['owner', 'developer'], $url, ); } private function syncProfilePhoto(UserProfile $profile, array $validated): void { $this->syncPhotos( $profile, [ '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', ); } private function applySorting(Builder $query, string $sort, string $direction): void { $employeeSorts = [ 'join_date', 'base_salary', 'employment_status', ]; if (in_array($sort, $employeeSorts, true)) { $query->orderBy( Employee::select($sort) ->whereColumn('employees.user_id', 'users.id') ->limit(1), $direction ); return; } if ($sort === 'full_name') { $query->orderBy( UserProfile::select('full_name') ->whereColumn('user_profiles.user_id', 'users.id') ->limit(1), $direction ); return; } if ($sort === 'email') { $query->orderBy('email', $direction); return; } $query->latest(); } }