dataTable($request); } $permissions = Permission::where('is_active', 1) ->whereNull('deleted_at') ->orderBy('name') ->get(); // Categorize permissions for UI Matrix $groupedPermissions = $this->groupPermissions($permissions); return view('pages.access_control.roles', [ 'permissions' => $permissions, 'groupedPermissions' => $groupedPermissions, ]); } /** * Group permissions into a hierarchical tree: category → menu → tabs. * * Each entry in $groups[$category][$baseName] has: * 'view' => Permission|null (menu-level view) * 'manage' => Permission|null (menu-level manage) * 'tabs' => [ * $tabSlug => ['view' => Permission|null, 'manage' => Permission|null] * ] */ protected function groupPermissions($permissions) { $groups = []; foreach ($permissions as $permission) { $name = $permission->name; // Determine category $category = match (true) { str_contains($name, 'ai ') => 'AI Intelligence', str_contains($name, 'dashboard') => 'Dashboard', str_contains($name, 'user directory') => 'User Directory', str_contains($name, 'access rights') => 'Access Rights', str_contains($name, 'health and logs') => 'Health & Logs', str_contains($name, 'action history') => 'Action History', str_contains($name, 'global settings') => 'Global Settings', str_contains($name, 'mobile settings') => 'Mobile Settings', str_contains($name, 'active sessions') => 'Active Sessions', str_contains($name, 'notification center') => 'Notification Center', str_contains($name, 'impersonate') => 'Impersonation', str_contains($name, 'backup') || str_contains($name, 'maintenance') => 'System Config', default => 'Other', }; // Strip action prefix $type = 'other'; $resource = $name; if (str_starts_with($name, 'manage ')) { $type = 'manage'; $resource = substr($name, 7); } elseif (str_starts_with($name, 'view ')) { $type = 'view'; $resource = substr($name, 5); } // Detect scoped (tab-level) permission: "resource:tab-slug" if (str_contains($resource, ':')) { [$menu, $tab] = explode(':', $resource, 2); $menu = trim($menu); $tab = trim($tab); if (! isset($groups[$category][$menu])) { $groups[$category][$menu] = ['view' => null, 'manage' => null, 'tabs' => []]; } if (! isset($groups[$category][$menu]['tabs'][$tab])) { $groups[$category][$menu]['tabs'][$tab] = ['view' => null, 'manage' => null]; } $groups[$category][$menu]['tabs'][$tab][$type] = $permission; } else { $menu = trim($resource); if (! isset($groups[$category][$menu])) { $groups[$category][$menu] = ['view' => null, 'manage' => null, 'tabs' => []]; } $groups[$category][$menu][$type] = $permission; } } // Sort categories $priority = ['Dashboard', 'User Directory', 'Access Rights', 'AI Intelligence', 'Health & Logs', 'Action History', 'Global Settings', 'Mobile Settings', 'Notification Center', 'Active Sessions', 'Impersonation', 'System Config']; uksort($groups, function ($a, $b) use ($priority) { $posA = array_search($a, $priority); $posB = array_search($b, $priority); if ($posA === false && $posB === false) { return strcmp($a, $b); } return ($posA === false) ? 1 : (($posB === false) ? -1 : $posA - $posB); }); return $groups; } protected function dataTable(Request $request) { try { $authUser = $request->user(); $canManage = $authUser->can('manage access rights'); $query = Role::query()->withTrashed()->with(['permissions:id,name', 'creator:id,email', 'updater:id,email']); $globalSearch = DataTable::globalSearch($request); $trashed = $request->input('trashed'); if ($trashed === 'archived') { $query->onlyTrashed(); } elseif ($trashed === 'active') { $query->withoutTrashed(); } if ($canManage) { $status = DataTable::columnSearch($request, 1); $name = DataTable::columnSearch($request, 2); $guard = DataTable::columnSearch($request, 3); $permission = DataTable::columnSearch($request, 4); } else { $status = null; $name = DataTable::columnSearch($request, 0); $guard = DataTable::columnSearch($request, 1); $permission = DataTable::columnSearch($request, 2); } if ($status) { $query->where('is_active', $status === 'active'); } if ($name) { $query->where('name', 'like', "%{$name}%"); } if ($guard) { $query->where('guard_name', 'like', "%{$guard}%"); } if ($permission) { $query->whereHas('permissions', function ($permissionQuery) use ($permission) { $permissionQuery->where('name', 'like', "%{$permission}%"); }); } // Audit columns (fixed indices from the end or just using canManage logic) if ($canManage) { $createdAt = DataTable::columnSearch($request, 5); $createdBy = DataTable::columnSearch($request, 6); $updatedAt = DataTable::columnSearch($request, 7); $updatedBy = DataTable::columnSearch($request, 8); } else { $createdAt = DataTable::columnSearch($request, 3); $createdBy = DataTable::columnSearch($request, 4); $updatedAt = DataTable::columnSearch($request, 5); $updatedBy = DataTable::columnSearch($request, 6); } 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('guard_name', 'like', "%{$globalSearch}%") ->orWhereHas('permissions', function ($permissionQuery) use ($globalSearch) { $permissionQuery->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(); $recordsTotal = Role::withTrashed()->count(); // Total across all states without eager loads [$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) => 'guard_name', ($canManage && $orderIndex === 5) || (! $canManage && $orderIndex === 3) => 'created_at', ($canManage && $orderIndex === 7) || (! $canManage && $orderIndex === 5) => 'updated_at', default => 'created_at', }; $roles = $query ->orderBy($sortColumn, $orderDirection) ->skip(DataTable::start($request)) ->take(DataTable::length($request)) ->get(); $rows = $roles->map(function (Role $role) use ($canManage) { $permissionNames = $role->permissions->pluck('name')->values(); $permissionBadges = $permissionNames->isNotEmpty() ? $permissionNames->map(fn ($permission) => ''.e($permission).'')->implode(' ') : '-'; $row = []; if ($canManage) { $row[] = sprintf( '', $role->id ); $row[] = sprintf( '