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: