dataTable($request); } $roles = Role::where('is_active', 1) ->whereNull('deleted_at') ->orderBy('name') ->get(); return view('pages.access_control.users', compact('roles')); } protected function dataTable(Request $request) { try { $authUser = $request->user(); $canManage = $authUser->can('manage user directory'); $query = User::query() ->withTrashed() ->with(['roles:id,name', 'creator:id,email', 'updater:id,email']); if (! $authUser->hasRole('Developer')) { $query->whereDoesntHave('roles', function ($roleQuery) { $roleQuery->where('name', 'Developer'); }); } // Fast count total $recordsTotal = User::withTrashed(); if (! $authUser->hasRole('Developer')) { $recordsTotal->whereDoesntHave('roles', function ($roleQuery) { $roleQuery->where('name', 'Developer'); }); } $recordsTotal = $recordsTotal->count(); $globalSearch = DataTable::globalSearch($request); if ($canManage) { $status = DataTable::columnSearch($request, 1); $name = DataTable::columnSearch($request, 2); $email = DataTable::columnSearch($request, 3); $role = DataTable::columnSearch($request, 4); $createdAt = DataTable::columnSearch($request, 5); $createdBy = DataTable::columnSearch($request, 6); $updatedAt = DataTable::columnSearch($request, 7); $updatedBy = DataTable::columnSearch($request, 8); } else { $status = null; $name = DataTable::columnSearch($request, 0); $email = DataTable::columnSearch($request, 1); $role = DataTable::columnSearch($request, 2); $createdAt = DataTable::columnSearch($request, 3); $createdBy = DataTable::columnSearch($request, 4); $updatedAt = DataTable::columnSearch($request, 5); $updatedBy = DataTable::columnSearch($request, 6); } $trashed = $request->input('trashed'); if ($trashed === 'archived') { $query->onlyTrashed(); } elseif ($trashed === 'active') { $query->withoutTrashed(); } if ($status) { $query->where('is_active', $status === 'active'); } if ($name) { $query->where('name', 'like', "%{$name}%"); } if ($email) { $query->where('email', 'like', "%{$email}%"); } if ($role) { $query->whereHas('roles', function ($roleQuery) use ($role) { $roleQuery->where('name', 'like', "%{$role}%"); }); } if ($createdAt) { $query->whereDate('created_at', $createdAt); } if ($createdBy) { $query->whereHas('creator', function ($creatorQuery) use ($createdBy) { $creatorQuery->where('email', 'like', "%{$createdBy}%"); }); } if ($updatedAt) { $query->whereDate('updated_at', $updatedAt); } if ($updatedBy) { $query->whereHas('updater', function ($updaterQuery) use ($updatedBy) { $updaterQuery->where('email', 'like', "%{$updatedBy}%"); }); } if ($globalSearch) { $query->where(function ($searchQuery) use ($globalSearch) { $searchQuery ->where('name', 'like', "%{$globalSearch}%") ->orWhere('email', 'like', "%{$globalSearch}%") ->orWhereHas('roles', function ($roleQuery) use ($globalSearch) { $roleQuery->where('name', 'like', "%{$globalSearch}%"); }) ->orWhereHas('creator', function ($creatorQuery) use ($globalSearch) { $creatorQuery->where('email', 'like', "%{$globalSearch}%"); }) ->orWhereHas('updater', function ($updaterQuery) use ($globalSearch) { $updaterQuery->where('email', 'like', "%{$globalSearch}%"); }); }); } // Perform filtered count WITHOUT eager loading or ordering $countQuery = clone $query; $countQuery->setEagerLoads([]); $countQuery->orders = null; $recordsFiltered = $countQuery->count(); [$orderIndex, $orderDirection] = DataTable::order($request, $canManage ? 5 : 3, 'desc'); $sortColumn = match (true) { $canManage && $orderIndex === 1 => 'is_active', ($canManage && $orderIndex === 2) || (! $canManage && $orderIndex === 0) => 'name', ($canManage && $orderIndex === 3) || (! $canManage && $orderIndex === 1) => 'email', ($canManage && $orderIndex === 5) || (! $canManage && $orderIndex === 3) => 'created_at', ($canManage && $orderIndex === 7) || (! $canManage && $orderIndex === 5) => 'updated_at', default => 'created_at', }; $users = $query ->orderBy($sortColumn, $orderDirection) ->skip(DataTable::start($request)) ->take(DataTable::length($request)) ->get(); $rows = $users->map(function (User $user) use ($authUser, $canManage) { $roles = $user->roles->pluck('name')->values(); $roleBadges = $roles->isNotEmpty() ? $roles->map(fn ($role) => ''.e($role).'')->implode(' ') : '-'; $row = []; if ($canManage) { $row[] = sprintf( '', $user->id ); $row[] = sprintf( '
%s
', $user->is_active ? 'checked' : '', $user->id, e($user->name), $user->is_active ? 'text-success' : 'text-danger', $user->is_active ? 'Active' : 'Inactive' ); } $row[] = e($user->name); $row[] = e($user->email); $row[] = $roleBadges; $row[] = e(format_datetime($user->created_at)); $row[] = e($user->creator->email ?? 'System'); $row[] = e(format_datetime($user->updated_at)); $row[] = e($user->updater->email ?? '-'); if ($canManage) { $impersonateButton = ''; if ($authUser->can('impersonate users')) { if ($authUser->id !== $user->id && ! $user->hasRole('Developer')) { $impersonateButton = '
' .csrf_field() .'' .'
'; } else { $impersonateButton = ''; } } if ($user->trashed()) { $row[] = '
' .'' .'' .'
'; } else { $row[] = '
' .$impersonateButton .'' .'' .'
'; } } return $row; })->all(); return DataTable::response($request, $recordsTotal, $recordsFiltered, $rows); } catch (\Exception $e) { Log::error('DataTable Error [UserManagement]: '.$e->getMessage()); return DataTable::response($request, 0, 0, []); } } /** * STORE — Tambah user + assign role */ public function store(Request $request) { $request->validate([ 'name' => [ 'required', 'string', 'min:3', 'max:100', 'regex:/^[a-zA-Z\s]+$/', ], 'email' => [ 'required', 'email', 'max:150', Rule::unique('users', 'email'), ], 'password' => [ 'required', 'string', 'min:12', 'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{12,}$/', ], 'roles' => [ 'required', 'array', 'min:1', ], 'roles.*' => [ 'required', Rule::exists('roles', 'id'), ], ], [ 'name.regex' => __('Name may only contain letters and spaces.'), 'email.unique' => __('This email is already registered.'), 'password.regex' => __('Password must contain uppercase, lowercase, number, and symbol.'), 'roles.required' => __('At least one role must be selected.'), ]); try { // create user $user = User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => bcrypt($request->password), 'is_active' => 1, 'created_by' => Auth::id(), ]); // 🔥 FIX: Convert role IDs → role names $roleNames = Role::whereIn('id', $request->roles)->pluck('name')->toArray(); // assign roles by name $user->syncRoles($roleNames); if ($request->expectsJson()) { return response()->json([ 'success' => true, 'message' => __('User created successfully.'), ]); } return back()->with('success', __('User created successfully.')); } catch (\Exception $e) { if ($request->expectsJson()) { return response()->json([ 'success' => false, 'message' => __('Failed to create user: ').$e->getMessage(), ], 500); } return back()->with('error', __('Failed to create user: ').$e->getMessage()); } } /** * UPDATE — Edit user data + (optional) password + roles */ public function update(Request $request, $id) { $request->validate([ 'name' => [ 'required', 'string', 'min:3', 'max:100', 'regex:/^[a-zA-Z\s]+$/', ], 'email' => [ 'required', 'email', 'max:150', Rule::unique('users', 'email')->ignore($id), ], 'password' => [ 'nullable', 'string', 'min:12', 'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{12,}$/', ], 'roles' => [ 'required', 'array', 'min:1', ], 'roles.*' => [ Rule::exists('roles', 'id'), ], ], [ 'name.regex' => __('Name may only contain letters and spaces.'), 'password.regex' => __('Password must contain uppercase, lowercase, number, and symbol.'), ]); try { $user = User::findOrFail($id); $data = [ 'name' => $request->name, 'email' => $request->email, 'updated_by' => Auth::id(), ]; if ($request->filled('password')) { $data['password'] = bcrypt($request->password); } $user->update($data); // 🔥 FIX: Convert ID role → Role Name $roleNames = Role::whereIn('id', $request->roles)->pluck('name')->toArray(); $user->syncRoles($roleNames); if ($request->expectsJson()) { return response()->json([ 'success' => true, 'message' => __('User updated successfully.'), ]); } return back()->with('success', __('User updated successfully.')); } catch (\Exception $e) { if ($request->expectsJson()) { return response()->json([ 'success' => false, 'message' => __('Failed to update user: ').$e->getMessage(), ], 500); } return back()->with('error', __('Failed to update user: ').$e->getMessage()); } } /** * TOGGLE — Aktivasi/Non-aktifkan user */ public function toggleStatus(Request $request) { $request->validate([ 'id' => 'required|exists:users,id', 'status' => 'required|in:activate,deactivate', ]); $user = User::findOrFail($request->id); $user->update([ 'is_active' => $request->status === 'activate' ? 1 : 0, 'updated_by' => Auth::id(), ]); return response()->json([ 'success' => true, 'message' => __('User status updated successfully.'), ]); } /** * DESTROY — Archive (Soft Delete) */ public function destroy($id) { $user = User::findOrFail($id); $user->delete(); return response()->json([ 'success' => true, 'message' => __('User has been archived.'), ]); } /** * RESTORE — Restore user (Soft Delete) */ public function restore($id) { $user = User::withTrashed()->findOrFail($id); $user->restore(); return response()->json([ 'success' => true, 'message' => __('User has been restored.'), ]); } /** * FORCE DELETE — Permanent removal */ public function forceDelete($id) { $user = User::withTrashed()->findOrFail($id); // Prevent accidental deletion of self if ($user->id === Auth::id()) { return response()->json([ 'success' => false, 'message' => __('You cannot permanently delete yourself.'), ], 403); } $user->forceDelete(); return response()->json([ 'success' => true, 'message' => __('User has been permanently removed.'), ]); } /** * BULK TOGGLE STATUS */ public function bulkToggleStatus(Request $request) { $request->validate([ 'ids' => 'required|array', 'ids.*' => 'exists:users,id', 'status' => 'required|in:activate,deactivate', ]); $status = $request->status === 'activate' ? 1 : 0; User::whereIn('id', $request->ids)->update([ 'is_active' => $status, 'updated_by' => Auth::id(), ]); return response()->json([ 'success' => true, 'message' => __('Selected users have been updated.'), ]); } /** * BULK DELETE (Archive) */ public function bulkDelete(Request $request) { $request->validate([ 'ids' => 'required|array', 'ids.*' => 'exists:users,id', ]); User::whereIn('id', $request->ids)->delete(); return response()->json([ 'success' => true, 'message' => __('Selected users have been archived.'), ]); } /** * BULK RESTORE */ public function bulkRestore(Request $request) { $request->validate([ 'ids' => 'required|array', 'ids.*' => 'exists:users,id', ]); User::withTrashed()->whereIn('id', $request->ids)->restore(); return response()->json([ 'success' => true, 'message' => __('Selected users have been restored.'), ]); } /** * BULK FORCE DELETE */ public function bulkForceDelete(Request $request) { $request->validate([ 'ids' => 'required|array', 'ids.*' => 'exists:users,id', ]); // Prevent self deletion $ids = array_filter($request->ids, fn ($id) => $id != Auth::id()); User::withTrashed()->whereIn('id', $ids)->forceDelete(); return response()->json([ 'success' => true, 'message' => __('Selected users have been permanently removed.'), ]); } }