diff --git a/ui/core/app/components/form/role/add-principals/index.hbs b/ui/core/app/components/form/role/add-principals/index.hbs
index d15f49bc3..86cefd36a 100644
--- a/ui/core/app/components/form/role/add-principals/index.hbs
+++ b/ui/core/app/components/form/role/add-principals/index.hbs
@@ -21,31 +21,17 @@
- {{#each @groups as |group|}}
+ {{#each this.filteredPrincipals as |principal|}}
+ @label={{principal.id}}
+ @description={{principal.description}}
+ onChange={{fn this.togglePrincipal principal}} />
- {{group.name}}
+ {{principal.name}}
- {{t "resources.group"}}
-
-
- {{/each}}
- {{#each @users as |user|}}
-
-
-
-
- {{user.name}}
-
- {{t "resources.user"}}
+ {{t (concat 'resources.' principal.constructor.modelName)}}
{{/each}}
@@ -57,10 +43,14 @@
{{#unless this.hasAvailablePrincipals}}
-
+
- {{t "descriptions.no-principals"}}
+ {{t "descriptions.no-principals-available"}}
+
+
+ {{t "actions.back"}}
+
{{/unless}}
diff --git a/ui/core/app/components/form/role/add-principals/index.js b/ui/core/app/components/form/role/add-principals/index.js
index 34090a495..484f2ef1a 100644
--- a/ui/core/app/components/form/role/add-principals/index.js
+++ b/ui/core/app/components/form/role/add-principals/index.js
@@ -1,8 +1,9 @@
import Component from '@glimmer/component';
-import { action } from '@ember/object';
+import { computed, action } from '@ember/object';
import { A } from '@ember/array';
-export default class FormRoleAddPrincipalsComponent extends Component {
+export default class FormRoleAddPrincipalsIndexComponent extends Component {
+
// =properties
@@ -13,11 +14,29 @@ export default class FormRoleAddPrincipalsComponent extends Component {
selectedPrincipalIDs = A();
/**
+ * Checks for unassigned principals.
* True if there are users or groups not yet added to this role.
- * TODO: this must be computed on users, groups, and role principals.
+ * @param {[UserModel, GroupModel]} filteredPrincipals
* @type {boolean}
*/
- hasAvailablePrincipals = true;
+ @computed('filteredPrincipals')
+ get hasAvailablePrincipals() {
+ return this.filteredPrincipals.length > 0;
+ }
+
+ /**
+ * Principals not currently assigned to a role.
+ * @type {[UserModel, GroupModel]}
+ */
+ @computed('args.{model,users,groups}')
+ get filteredPrincipals() {
+ // Retrieve principal IDs assigned to current role
+ const currentPrincipalIDs = this.args.model.principals.map(principal => principal.principal_id);
+ // Retrieve principal IDs not assigned to current role
+ const unassignedUsers = this.args.users.filter(({ id }) => !currentPrincipalIDs.includes(id));
+ const unassignedGroups = this.args.groups.filter(({ id }) => !currentPrincipalIDs.includes(id));
+ return [...unassignedUsers, ...unassignedGroups];
+ }
// =actions
diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets/target/add-host-sets.js b/ui/core/app/routes/scopes/scope/projects/project/targets/target/add-host-sets.js
index f58b7ebb7..f1fdc55ec 100644
--- a/ui/core/app/routes/scopes/scope/projects/project/targets/target/add-host-sets.js
+++ b/ui/core/app/routes/scopes/scope/projects/project/targets/target/add-host-sets.js
@@ -13,7 +13,7 @@ export default class ScopesScopeProjectsProjectTargetsTargetAddHostSetsRoute ext
// =methods
/**
- * Emtpy out any previously loaded host catalogs and host sets.
+ * Empty out any previously loaded host catalogs and host sets.
*/
beforeModel() {
this.store.unloadAll('host-catalog');
diff --git a/ui/core/app/routes/scopes/scope/roles/role.js b/ui/core/app/routes/scopes/scope/roles/role.js
index 6269e7748..9a0012328 100644
--- a/ui/core/app/routes/scopes/scope/roles/role.js
+++ b/ui/core/app/routes/scopes/scope/roles/role.js
@@ -17,31 +17,26 @@ export default class ScopesScopeRolesRoleRoute extends Route {
}
/**
- * Renders the route-specific templates for header, navigation,
+ * Renders the role specific templates for header, navigation,
* and actions page sections.
* @override
- * @param {object} controller
- * @param {object} model
*/
- renderTemplate(controller, model) {
+ renderTemplate() {
super.renderTemplate(...arguments);
this.render('scopes/scope/roles/role/-header', {
into: 'scopes/scope/roles/role',
outlet: 'header',
- model
- });
-
- this.render('scopes/scope/roles/role/-actions', {
- into: 'scopes/scope/roles/role',
- outlet: 'actions',
- model
});
this.render('scopes/scope/roles/role/-navigation', {
into: 'scopes/scope/roles/role',
outlet: 'navigation',
- model
+ });
+
+ this.render('scopes/scope/roles/role/-actions', {
+ into: 'scopes/scope/roles/role',
+ outlet: 'actions',
});
}
}
diff --git a/ui/core/app/routes/scopes/scope/roles/role/add-principals.js b/ui/core/app/routes/scopes/scope/roles/role/add-principals.js
index b4854a825..860f3ba00 100644
--- a/ui/core/app/routes/scopes/scope/roles/role/add-principals.js
+++ b/ui/core/app/routes/scopes/scope/roles/role/add-principals.js
@@ -13,7 +13,7 @@ export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route {
// =methods
/**
- * Emtpy out any previously loaded users and groups
+ * Empty out any previously loaded users and groups.
* (collectively, principals).
*/
beforeModel() {
@@ -21,6 +21,10 @@ export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route {
this.store.unloadAll('group');
}
+ /**
+ * Returns the current role, all users, and all groups
+ * @return {{role: RoleModel, users: [UserModel], groups: [GroupModel]}}
+ */
model() {
const role = this.modelFor('scopes.scope.roles.role');
const { scopeID } = role;
@@ -33,7 +37,8 @@ export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route {
}
/**
- * Empties the actions and navigation outlets and renders a custom header.
+ * Renders the add-principals-specific header template.
+ * Empties the actions and navigation outlets and renders a custom empty header.
* @override
*/
renderTemplate() {
@@ -41,22 +46,24 @@ export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route {
this.render('scopes/scope/roles/role/add-principals/-header', {
into: 'scopes/scope/roles/role',
- outlet: 'header'
+ outlet: 'header',
});
this.render('-empty', {
into: 'scopes/scope/roles/role',
- outlet: 'actions'
+ outlet: 'navigation',
});
this.render('-empty', {
into: 'scopes/scope/roles/role',
- outlet: 'navigation'
+ outlet: 'actions',
});
}
+ // =actions
+
/**
- * Saves principal IDs into the role via the API.
+ * Save principal IDs to current role via the API.
* @param {RoleModel} role
* @param {[string]} principalIDs
*/
diff --git a/ui/core/app/templates/scopes/scope/projects/project/targets/target/add-host-sets.hbs b/ui/core/app/templates/scopes/scope/projects/project/targets/target/add-host-sets.hbs
index 206de2d68..e487f20f6 100644
--- a/ui/core/app/templates/scopes/scope/projects/project/targets/target/add-host-sets.hbs
+++ b/ui/core/app/templates/scopes/scope/projects/project/targets/target/add-host-sets.hbs
@@ -16,6 +16,10 @@
{{t "descriptions.no-host-sets-for-target"}}
+
+
+ {{t "actions.back"}}
+
{{/unless}}
diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml
index 7c354af99..20e0b4740 100644
--- a/ui/core/translations/en-us.yaml
+++ b/ui/core/translations/en-us.yaml
@@ -7,6 +7,7 @@ actions:
save: Save
manage: Manage
help: Need help?
+ back: Back
authenticate: Authenticate
deauthenticate: Deauthenticate
choose-org: Choose Scope
@@ -95,6 +96,7 @@ titles:
no-host-set-hosts: No Hosts Available
no-host-sets-for-target: No Host Sets Available
role-principals-welcome: No Principals Associated
+ no-principals-available: No Principals Available
members: Members
group-members-welcome: Welcome to Members
no-available-users: No Users Available
@@ -114,6 +116,7 @@ descriptions:
no-host-set-hosts: Before associating hosts with this host set, create hosts within the host catalog.
no-host-sets-for-target: No host sets are available to add to this target.
role-principals-welcome: No principals are available in this role.
+ no-principals-available: Principals are not available for assignment.
group-members-welcome: Users belonging to this group appear here.
no-available-users: No users are available to add to this group.
states: