1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-07 23:05:26 +02:00

refactor(ui): replace ng selectors with react-select [EE-3608] (#7203)

Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
This commit is contained in:
Chaim Lev-Ari 2022-09-21 10:10:58 +03:00 committed by GitHub
parent 1e21961e6a
commit ceaee4e175
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 1188 additions and 625 deletions

View file

@ -72,8 +72,8 @@
class="form-group mt-4"
ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === $ctrl.RCO.RESTRICTED && ($ctrl.isAdmin || (!$ctrl.isAdmin && $ctrl.availableTeams.length > 1))"
>
<div class="col-sm-12 vertical-center">
<label for="group-access" class="control-label text-left col-sm-3 col-lg-2 !p-0">
<div class="w-full vertical-center">
<label for="group-access" class="control-label text-left col-sm-3 col-lg-2 !pt-0">
Authorized teams
<portainer-tooltip
ng-if="$ctrl.isAdmin && $ctrl.availableTeams.length > 0"
@ -84,54 +84,45 @@
message="'As you are a member of multiple teams, you can select which teams(s) will be able to manage this resource.'"
></portainer-tooltip>
</label>
<span ng-if="$ctrl.isAdmin && $ctrl.availableTeams.length === 0" class="small text-muted" style="margin-left: 20px">
You have not yet created any teams. Head over to the <a ui-sref="portainer.teams">Teams view</a> to manage teams.
</span>
<div
isteven-multi-select
ng-if="($ctrl.isAdmin && $ctrl.availableTeams.length > 0) || (!$ctrl.isAdmin && $ctrl.availableTeams.length > 1)"
class="col-sm-9 col-lg-10"
input-model="$ctrl.availableTeams"
output-model="$ctrl.formData.AuthorizedTeams"
button-label="Name"
item-label="Name"
tick-property="selected"
helper-elements="filter"
search-property="Name"
translation="{nothingSelected: 'Select one or more teams', search: 'Search...'}"
data-cy="portainer-selectTeamAccess"
>
<div class="col-sm-9 col-lg-10">
<span ng-if="$ctrl.isAdmin && $ctrl.availableTeams.length === 0" class="small text-muted">
You have not yet created any teams. Head over to the <a ui-sref="portainer.teams">Teams view</a> to manage teams.
</span>
<por-access-control-form-team-selector
ng-if="($ctrl.isAdmin && $ctrl.availableTeams.length > 0) || (!$ctrl.isAdmin && $ctrl.availableTeams.length > 1)"
options="$ctrl.availableTeams"
value="$ctrl.formData.AuthorizedTeams"
input-id="'teams-selector'"
on-change="($ctrl.onAuthorizedTeamsChange)"
></por-access-control-form-team-selector>
</div>
</div>
</div>
<!-- !authorized-teams -->
<!-- authorized-users -->
<div class="form-group" ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === $ctrl.RCO.RESTRICTED && $ctrl.isAdmin">
<div class="col-sm-12 vertical-center">
<label for="group-access" class="control-label text-left col-sm-3 col-lg-2 !p-0">
<div class="w-full vertical-center">
<label for="group-access" class="control-label text-left col-sm-3 col-lg-2 !pt-0">
Authorized users
<portainer-tooltip
ng-if="$ctrl.isAdmin && $ctrl.availableUsers.length > 0"
message="'You can select which user(s) will be able to manage this resource.'"
></portainer-tooltip>
</label>
<span ng-if="$ctrl.availableUsers.length === 0" class="small text-muted" style="margin-left: 20px">
You have not yet created any users. Head over to the <a ui-sref="portainer.users">Users view</a> to manage users.
</span>
<div
isteven-multi-select
ng-if="$ctrl.availableUsers.length > 0"
class="col-sm-9 col-lg-10"
input-model="$ctrl.availableUsers"
output-model="$ctrl.formData.AuthorizedUsers"
button-label="Username"
item-label="Username"
tick-property="selected"
helper-elements="filter"
search-property="Username"
translation="{nothingSelected: 'Select one or more users', search: 'Search...'}"
data-cy="portainer-selectUserAccess"
>
<div class="col-sm-9 col-lg-10">
<span ng-if="$ctrl.availableUsers.length === 0" class="small text-muted">
You have not yet created any users. Head over to the <a ui-sref="portainer.users">Users view</a> to manage users.
</span>
<por-access-control-form-user-selector
ng-if="$ctrl.availableUsers.length > 0"
options="$ctrl.availableUsers"
value="$ctrl.formData.AuthorizedUsers"
input-id="'users-selector'"
on-change="($ctrl.onAuthorizedUsersChange)"
></por-access-control-form-user-selector>
</div>
</div>
</div>

View file

@ -15,6 +15,9 @@ angular.module('portainer.app').controller('porAccessControlFormController', [
ctrl.RCO = RCO;
this.onAuthorizedTeamsChange = onAuthorizedTeamsChange.bind(this);
this.onAuthorizedUsersChange = onAuthorizedUsersChange.bind(this);
ctrl.availableTeams = [];
ctrl.availableUsers = [];
@ -31,18 +34,24 @@ angular.module('portainer.app').controller('porAccessControlFormController', [
}
function setAuthorizedUsersAndTeams(authorizedUsers, authorizedTeams) {
angular.forEach(ctrl.availableUsers, function (user) {
var found = _.find(authorizedUsers, { Id: user.Id });
if (found) {
user.selected = true;
}
});
ctrl.formData.AuthorizedTeams = authorizedTeams;
ctrl.formData.AuthorizedUsers = authorizedUsers;
}
angular.forEach(ctrl.availableTeams, function (team) {
var found = _.find(authorizedTeams, { Id: team.Id });
if (found) {
team.selected = true;
}
function onAuthorizedTeamsChange(AuthorizedTeams) {
onChange({ AuthorizedTeams });
}
function onAuthorizedUsersChange(AuthorizedUsers) {
onChange({ AuthorizedUsers });
}
function onChange(formData) {
$scope.$evalAsync(() => {
ctrl.formData = {
...ctrl.formData,
...formData,
};
});
}

View file

@ -1,9 +1,5 @@
import angular from 'angular';
import { porAccessManagement } from './por-access-management';
import { porAccessManagementUsersSelector } from './por-access-management-users-selector';
export default angular
.module('portainer.app.component.access-management', [])
.component('porAccessManagement', porAccessManagement)
.component('porAccessManagementUsersSelector', porAccessManagementUsersSelector).name;
export default angular.module('portainer.app.component.access-management', []).component('porAccessManagement', porAccessManagement).name;

View file

@ -1,7 +0,0 @@
export const porAccessManagementUsersSelector = {
templateUrl: './por-access-management-users-selector.html',
bindings: {
options: '<',
value: '=',
},
};

View file

@ -1,20 +0,0 @@
<div class="form-group">
<label class="col-sm-3 col-lg-2 control-label text-left vertical-center"> Select user(s) and/or team(s) </label>
<div class="col-sm-9 col-lg-4 vertical-center">
<span class="small text-muted" ng-if="$ctrl.options.length === 0"> No users or teams available. </span>
<span
isteven-multi-select
ng-if="$ctrl.options.length > 0"
input-model="$ctrl.options"
output-model="$ctrl.value"
button-label="icon Name"
item-label="icon Name"
tick-property="ticked"
helper-elements="filter"
search-property="Name"
translation="{nothingSelected: 'Select one or more users and/or teams', search: 'Search...'}"
data-cy="component-selectUser"
>
</span>
</div>
</div>

View file

@ -13,7 +13,11 @@
</span>
</div>
<por-access-management-users-selector options="ctrl.availableUsersAndTeams" value="ctrl.formValues.multiselectOutput"></por-access-management-users-selector>
<por-access-management-users-selector
options="ctrl.availableUsersAndTeams"
value="ctrl.formValues.multiselectOutput"
on-change="(ctrl.onChangeUsersAndTeams)"
></por-access-management-users-selector>
<div class="form-group" ng-if="ctrl.entityType !== 'registry'">
<label class="col-sm-3 col-lg-2 control-label text-left"> Role </label>
@ -38,7 +42,7 @@
<button
type="submit"
class="btn btn-primary btn-sm vertical-center"
ng-disabled="(ctrl.availableUsersAndTeams | filter:{ticked:true}).length === 0 || ctrl.actionInProgress"
ng-disabled="ctrl.formValues.multiselectOutput.length === 0 || ctrl.actionInProgress"
ng-click="ctrl.authorizeAccess()"
button-spinner="ctrl.actionInProgress"
data-cy="access-createAccess"

View file

@ -6,13 +6,20 @@ import { isLimitedToBE } from '@/portainer/feature-flags/feature-flags.service';
class PorAccessManagementController {
/* @ngInject */
constructor(Notifications, AccessService, RoleService) {
Object.assign(this, { Notifications, AccessService, RoleService });
constructor($scope, Notifications, AccessService, RoleService) {
Object.assign(this, { $scope, Notifications, AccessService, RoleService });
this.limitedToBE = false;
this.unauthorizeAccess = this.unauthorizeAccess.bind(this);
this.updateAction = this.updateAction.bind(this);
this.onChangeUsersAndTeams = this.onChangeUsersAndTeams.bind(this);
}
onChangeUsersAndTeams(value) {
this.$scope.$evalAsync(() => {
this.formValues.multiselectOutput = value;
});
}
updateAction() {

View file

@ -1,9 +0,0 @@
angular.module('portainer.app').component('endpointSelector', {
templateUrl: './endpointSelector.html',
controller: 'EndpointSelectorController',
bindings: {
model: '=',
endpoints: '<',
groups: '<',
},
});

View file

@ -1,8 +0,0 @@
<ui-select ng-model="$ctrl.model">
<ui-select-match placeholder="Select an environment">
<span>{{ $select.selected.Name }}</span>
</ui-select-match>
<ui-select-choices group-by="$ctrl.groupEndpoints" group-filter="$ctrl.sortGroups" repeat="endpoint in ($ctrl.endpoints | filter: $select.search) track by endpoint.Id">
<span>{{ endpoint.Name }}</span>
</ui-select-choices>
</ui-select>

View file

@ -1,35 +0,0 @@
import _ from 'lodash-es';
angular.module('portainer.app').controller('EndpointSelectorController', function () {
var ctrl = this;
this.sortGroups = function (groups) {
return _.sortBy(groups, ['name']);
};
this.groupEndpoints = function (endpoint) {
for (var i = 0; i < ctrl.availableGroups.length; i++) {
var group = ctrl.availableGroups[i];
if (endpoint.GroupId === group.Id) {
return group.Name;
}
}
};
this.$onInit = function () {
this.availableGroups = filterEmptyGroups(this.groups, this.endpoints);
};
function filterEmptyGroups(groups, endpoints) {
return groups.filter(function f(group) {
for (var i = 0; i < endpoints.length; i++) {
var endpoint = endpoints[i];
if (endpoint.GroupId === group.Id) {
return true;
}
}
return false;
});
}
});

View file

@ -2,16 +2,19 @@ import { STACK_NAME_VALIDATION_REGEX } from '@/constants';
angular.module('portainer.app').controller('StackDuplicationFormController', [
'Notifications',
function StackDuplicationFormController(Notifications) {
'$scope',
function StackDuplicationFormController(Notifications, $scope) {
var ctrl = this;
ctrl.environmentSelectorOptions = null;
ctrl.state = {
duplicationInProgress: false,
migrationInProgress: false,
};
ctrl.formValues = {
endpoint: null,
endpointId: null,
newName: '',
};
@ -23,15 +26,24 @@ angular.module('portainer.app').controller('StackDuplicationFormController', [
ctrl.migrateStack = migrateStack;
ctrl.isMigrationButtonDisabled = isMigrationButtonDisabled;
ctrl.isEndpointSelected = isEndpointSelected;
ctrl.onChangeEnvironment = onChangeEnvironment;
ctrl.$onChanges = $onChanges;
function isFormValidForMigration() {
return ctrl.formValues.endpoint && ctrl.formValues.endpoint.Id;
return ctrl.formValues.endpointId;
}
function isFormValidForDuplication() {
return isFormValidForMigration() && ctrl.formValues.newName && !ctrl.yamlError;
}
function onChangeEnvironment(endpointId) {
console.log({ endpointId });
return $scope.$evalAsync(() => {
ctrl.formValues.endpointId = endpointId;
});
}
function duplicateStack() {
if (!ctrl.formValues.newName) {
Notifications.error('Failure', null, 'Stack name is required for duplication');
@ -40,7 +52,7 @@ angular.module('portainer.app').controller('StackDuplicationFormController', [
ctrl.state.duplicationInProgress = true;
ctrl
.onDuplicate({
endpointId: ctrl.formValues.endpoint.Id,
endpointId: ctrl.formValues.endpointId,
name: ctrl.formValues.newName ? ctrl.formValues.newName : undefined,
})
.finally(function () {
@ -52,7 +64,7 @@ angular.module('portainer.app').controller('StackDuplicationFormController', [
ctrl.state.migrationInProgress = true;
ctrl
.onMigrate({
endpointId: ctrl.formValues.endpoint.Id,
endpointId: ctrl.formValues.endpointId,
name: ctrl.formValues.newName ? ctrl.formValues.newName : undefined,
})
.finally(function () {
@ -65,11 +77,42 @@ angular.module('portainer.app').controller('StackDuplicationFormController', [
}
function isTargetEndpointAndCurrentEquals() {
return ctrl.formValues.endpoint && ctrl.formValues.endpoint.Id === ctrl.currentEndpointId;
return ctrl.formValues.endpointId === ctrl.currentEndpointId;
}
function isEndpointSelected() {
return ctrl.formValues.endpoint && ctrl.formValues.endpoint.Id;
return ctrl.formValues.endpointId;
}
function $onChanges() {
ctrl.environmentSelectorOptions = getOptions(ctrl.groups, ctrl.endpoints);
}
},
]);
function getOptions(groups, environments) {
if (!groups || !environments) {
return [];
}
const groupSet = environments.reduce((groupSet, environment) => {
const groupEnvironments = groupSet[environment.GroupId] || [];
return {
...groupSet,
[environment.GroupId]: [...groupEnvironments, { label: environment.Name, value: environment.Id }],
};
}, {});
return Object.entries(groupSet).map(([groupId, environments]) => {
const group = groups.find((group) => group.Id === parseInt(groupId, 10));
if (!group) {
throw new Error('missing group');
}
return {
label: group.Name,
options: environments,
};
});
}

View file

@ -30,8 +30,8 @@
</div>
</div>
<div class="form-group">
<endpoint-selector ng-if="$ctrl.endpoints && $ctrl.groups" model="$ctrl.formValues.endpoint" endpoints="$ctrl.endpoints" groups="$ctrl.groups"></endpoint-selector>
<div class="form-group" ng-if="$ctrl.endpoints && $ctrl.groups">
<por-select value="$ctrl.formValues.endpointId" on-change="($ctrl.onChangeEnvironment)" options="$ctrl.environmentSelectorOptions"></por-select>
</div>
<div class="form-group">

View file

@ -17,6 +17,7 @@ type TeamAccessPolicies = Record<TeamId, AccessPolicy>;
export type RegistryId = number;
export interface Registry {
Id: RegistryId;
Name: string;
}
interface RegistryAccess {

View file

@ -6,7 +6,8 @@ import AccessViewerPolicyModel from '../../models/access';
export default class AccessViewerController {
/* @ngInject */
constructor(Notifications, RoleService, UserService, GroupService, TeamService, TeamMembershipService, Authentication) {
constructor($scope, Notifications, RoleService, UserService, GroupService, TeamService, TeamMembershipService, Authentication) {
this.$scope = $scope;
this.Notifications = Notifications;
this.RoleService = RoleService;
this.UserService = UserService;
@ -17,40 +18,51 @@ export default class AccessViewerController {
this.limitedFeature = 'rbac-roles';
this.users = [];
this.selectedUserId = null;
this.onUserSelect = this.onUserSelect.bind(this);
}
onUserSelect() {
this.userRoles = [];
const userRoles = {};
const user = this.selectedUser;
const userMemberships = _.filter(this.teamMemberships, { UserId: user.Id });
onUserSelect(selectedUserId) {
this.$scope.$evalAsync(() => {
this.userRoles = [];
this.selectedUserId = selectedUserId;
for (const [, endpoint] of _.entries(this.endpoints)) {
let role = this.getRoleFromUserEndpointPolicy(user, endpoint);
if (role) {
userRoles[endpoint.Id] = role;
continue;
const userRoles = {};
const user = this.allUsers.find((user) => user.Id === selectedUserId);
if (!user) {
throw new Error('User not found');
}
role = this.getRoleFromUserEndpointGroupPolicy(user, endpoint);
if (role) {
userRoles[endpoint.Id] = role;
continue;
const userMemberships = _.filter(this.teamMemberships, { UserId: user.value });
for (const [, endpoint] of _.entries(this.endpoints)) {
let role = this.getRoleFromUserEndpointPolicy(user, endpoint);
if (role) {
userRoles[endpoint.Id] = role;
continue;
}
role = this.getRoleFromUserEndpointGroupPolicy(user, endpoint);
if (role) {
userRoles[endpoint.Id] = role;
continue;
}
role = this.getRoleFromTeamEndpointPolicies(userMemberships, endpoint);
if (role) {
userRoles[endpoint.Id] = role;
continue;
}
role = this.getRoleFromTeamEndpointGroupPolicies(userMemberships, endpoint);
if (role) {
userRoles[endpoint.Id] = role;
}
}
role = this.getRoleFromTeamEndpointPolicies(userMemberships, endpoint);
if (role) {
userRoles[endpoint.Id] = role;
continue;
}
role = this.getRoleFromTeamEndpointGroupPolicies(userMemberships, endpoint);
if (role) {
userRoles[endpoint.Id] = role;
}
}
this.userRoles = _.values(userRoles);
this.userRoles = _.values(userRoles);
});
}
findLowestRole(policies) {
@ -150,7 +162,8 @@ export default class AccessViewerController {
this.roles = _.keyBy(await this.RoleService.roles(), 'Id');
this.teams = _.keyBy(await this.TeamService.teams(), 'Id');
this.teamMemberships = await this.TeamMembershipService.memberships();
this.users = await this.teamMemberUsers(this.allUsers, this.teamMemberships);
const teamUsers = await this.teamMemberUsers(this.allUsers, this.teamMemberships);
this.users = teamUsers.map((user) => ({ label: user.Username, value: user.Id }));
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve accesses');
}

View file

@ -12,14 +12,9 @@
<div class="form-group">
<div class="col-sm-12">
<span class="small text-muted" ng-if="$ctrl.users.length === 0"> No user available </span>
<ui-select ng-if="$ctrl.users.length > 0" ng-model="$ctrl.selectedUser" ng-change="$ctrl.onUserSelect()">
<ui-select-match placeholder="Select a user">
<span>{{ $select.selected.Username }}</span>
</ui-select-match>
<ui-select-choices repeat="item in ($ctrl.users | filter: $select.search)">
<span>{{ item.Username }}</span>
</ui-select-choices>
</ui-select>
<por-select ng-if="$ctrl.users.length > 0" value="$ctrl.selectedUserId" options="$ctrl.users" on-change="($ctrl.onUserSelect)" placeholder="'Select a user'">
</por-select>
</div>
</div>
<access-viewer-datatable table-key="access_viewer" dataset="$ctrl.userRoles" order-by="EndpointName" is-admin="$ctrl.isAdmin"> </access-viewer-datatable>

View file

@ -17,6 +17,9 @@ import { withI18nSuspense } from '@/react-tools/withI18nSuspense';
import { SettingsFDO } from '@/react/portainer/settings/EdgeComputeView/SettingsFDO';
import { SettingsOpenAMT } from '@/react/portainer/settings/EdgeComputeView/SettingsOpenAMT';
import { InternalAuth } from '@/react/portainer/settings/AuthenticationView/InternalAuth';
import { PorAccessControlFormTeamSelector } from '@/react/portainer/access-control/PorAccessControlForm/TeamsSelector';
import { PorAccessControlFormUserSelector } from '@/react/portainer/access-control/PorAccessControlForm/UsersSelector';
import { PorAccessManagementUsersSelector } from '@/react/portainer/access-control/AccessManagement/PorAccessManagementUsersSelector';
import { PageHeader } from '@@/PageHeader';
import { TagSelector } from '@@/TagSelector';
@ -29,6 +32,8 @@ import { DashboardItem } from '@@/DashboardItem';
import { SearchBar } from '@@/datatables/SearchBar';
import { FallbackImage } from '@@/FallbackImage';
import { BadgeIcon } from '@@/BoxSelector/BadgeIcon';
import { TeamsSelector } from '@@/TeamsSelector';
import { PortainerSelect } from '@@/form-components/PortainerSelect';
import { fileUploadField } from './file-upload-field';
import { switchField } from './switch-field';
@ -140,4 +145,54 @@ export const componentsModule = angular
.component(
'internalAuth',
r2a(InternalAuth, ['onSaveSettings', 'isLoading', 'value', 'onChange'])
)
.component(
'teamsSelector',
r2a(TeamsSelector, [
'onChange',
'value',
'dataCy',
'inputId',
'name',
'placeholder',
'teams',
])
)
.component(
'porAccessControlFormTeamSelector',
r2a(PorAccessControlFormTeamSelector, [
'inputId',
'onChange',
'options',
'value',
])
)
.component(
'porAccessControlFormUserSelector',
r2a(PorAccessControlFormUserSelector, [
'inputId',
'onChange',
'options',
'value',
])
)
.component(
'porSelect',
r2a(PortainerSelect, [
'name',
'inputId',
'placeholder',
'disabled',
'data-cy',
'bindToBody',
'value',
'onChange',
'options',
'isMulti',
'isClearable',
])
)
.component(
'porAccessManagementUsersSelector',
r2a(PorAccessManagementUsersSelector, ['onChange', 'options', 'value'])
).name;

View file

@ -1,7 +1,7 @@
export default class LdapCustomAdminGroupController {
/* @ngInject */
constructor($async, Notifications, LDAPService) {
Object.assign(this, { $async, Notifications, LDAPService });
constructor($async, $scope, Notifications, LDAPService) {
Object.assign(this, { $async, $scope, Notifications, LDAPService });
this.groups = null;
this.groupstest = null;
@ -10,6 +10,7 @@ export default class LdapCustomAdminGroupController {
this.onRemoveClick = this.onRemoveClick.bind(this);
this.onAddClick = this.onAddClick.bind(this);
this.search = this.search.bind(this);
this.onAdminGroupChange = this.onAdminGroupChange.bind(this);
}
onAddClick() {
@ -24,7 +25,8 @@ export default class LdapCustomAdminGroupController {
return this.$async(async () => {
try {
this.groups = null;
this.groups = await this.onSearchClick();
const groups = await this.onSearchClick();
this.groups = groups.map((group) => ({ label: group.name, value: group.name }));
this.enableAssignAdminGroup = this.groups && this.groups.length > 0;
} catch (error) {
this.Notifications.error('Failure', error, 'Failed to search groups');
@ -32,14 +34,15 @@ export default class LdapCustomAdminGroupController {
});
}
onAdminGroupChange(value) {
return this.$scope.$evalAsync(() => {
this.selectedAdminGroups = value;
});
}
async $onInit() {
if (this.settings.AdminAutoPopulate && this.settings.AdminGroups && this.settings.AdminGroups.length > 0) {
const settings = {
...this.settings,
AdminGroupSearchSettings: this.settings.AdminGroupSearchSettings.map((search) => ({ ...search, GroupFilter: search.GroupFilter || this.defaultAdminGroupSearchFilter })),
};
this.groups = await this.LDAPService.adminGroups(settings);
await this.search();
}
if (this.groups && this.groups.length > 0) {

View file

@ -114,22 +114,18 @@
</div>
</div>
<div class="form-group" ng-if="$ctrl.settings.AdminAutoPopulate">
<div class="col-sm-12">
<label for="group-access" class="control-label text-left"> Select Group(s) </label>
<span
class="ml-7"
isteven-multi-select
ng-if="$ctrl.enableAssignAdminGroup"
input-model="$ctrl.groups"
output-model="$ctrl.selectedAdminGroups"
button-label="name"
item-label="name"
tick-property="selected"
helper-elements="filter"
search-property="name"
translation="{nothingSelected: 'Select one or more groups', search: 'Search...'}"
<div class="form-group" ng-if="$ctrl.settings.AdminAutoPopulate && $ctrl.groups">
<label for="group-access" class="control-label text-left col-sm-2"> Select Group(s) </label>
<div class="col-sm-8">
<por-select
data-cy="'group-access-selector'"
input-id="'group-access'"
value="$ctrl.selectedAdminGroups"
on-change="($ctrl.onAdminGroupChange)"
options="$ctrl.groups"
placeholder="'Select one or more groups'"
is-multi="true"
>
</span>
</por-select>
</div>
</div>

View file

@ -105,25 +105,20 @@
<!-- !admin-checkbox -->
<!-- teams -->
<div class="form-group" ng-if="!formValues.Administrator">
<label class="col-sm-3 col-lg-2 control-label text-left">Add to team(s)</label>
<label class="col-sm-3 col-lg-2 control-label text-left" for="teams-selector">Add to team(s)</label>
<div class="col-sm-8">
<span class="small text-muted" ng-if="teams.length === 0">
You don't seem to have any teams to add users into. Head over to the <a ui-sref="portainer.teams">Teams view</a> to create some.
</span>
<span
isteven-multi-select
<teams-selector
ng-if="teams.length > 0"
input-model="teams"
output-model="formValues.Teams"
button-label="Name"
item-label="Name"
tick-property="ticked"
helper-elements="filter"
search-property="Name"
translation="{nothingSelected: 'Select one or more teams', search: 'Search...'}"
data-cy="user-teamSelect"
>
</span>
value="formValues.TeamIds"
teams="teams"
placeholder="'Select one or more teams'"
data-cy="'user-teamSelect'"
on-change="(onChangeTeamIds)"
input-id="'teams-selector'"
></teams-selector>
</div>
</div>
<!-- !teams -->

View file

@ -23,7 +23,7 @@ angular.module('portainer.app').controller('UsersController', [
Password: '',
ConfirmPassword: '',
Administrator: false,
Teams: [],
TeamIds: [],
};
$scope.handleAdministratorChange = function (checked) {
@ -32,6 +32,12 @@ angular.module('portainer.app').controller('UsersController', [
});
};
$scope.onChangeTeamIds = function (teamIds) {
return $scope.$evalAsync(() => {
$scope.formValues.TeamIds = teamIds;
});
};
$scope.checkUsernameValidity = function () {
var valid = true;
for (var i = 0; i < $scope.users.length; i++) {
@ -50,11 +56,7 @@ angular.module('portainer.app').controller('UsersController', [
var username = $scope.formValues.Username;
var password = $scope.formValues.Password;
var role = $scope.formValues.Administrator ? 1 : 2;
var teamIds = [];
angular.forEach($scope.formValues.Teams, function (team) {
teamIds.push(team.Id);
});
UserService.createUser(username, password, role, teamIds)
UserService.createUser(username, password, role, $scope.formValues.TeamIds)
.then(function success() {
Notifications.success('User successfully created', username);
$state.reload();