From c16f4f70c2a9e9c56d382d7948c4cce310bd3120 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 30 Sep 2026 20:16:38 +0800 Subject: [PATCH] feat: resource view registries for IAM tables Extensions can add columns, row actions, bulk actions and toolbar buttons through iam:table::, and buttons and menu items to the edit dialogs through iam:details::. - groups, roles and policies move to Layout::Resource::Tabular with actionButtons/bulkActions getters (roles and policies keep their reload) - users keeps its TabularActions header; its columns merge in the controller - edit dialogs pass registered details buttons as footer actionButtons - stable ids on built-in items, query params include registered filters - fix the delete-group accept button reading the misspelt aim.* key Requires @fleetbase/ember-core 0.3.25 and @fleetbase/ember-ui 0.4.5. --- addon/controllers/groups/index.js | 55 +++++++++++++++++++++++++++-- addon/controllers/policies/index.js | 46 +++++++++++++++++++++++- addon/controllers/roles/index.js | 46 +++++++++++++++++++++++- addon/controllers/users/index.js | 50 +++++++++++++++++++++++--- addon/extension.js | 4 +++ addon/services/group-actions.js | 3 ++ addon/services/policy-actions.js | 3 ++ addon/services/role-actions.js | 3 ++ addon/services/user-actions.js | 3 ++ addon/templates/groups/index.hbs | 44 ++++++++--------------- addon/templates/policies/index.hbs | 54 +++++++++------------------- addon/templates/roles/index.hbs | 54 +++++++++------------------- addon/templates/users.hbs | 1 + 13 files changed, 253 insertions(+), 113 deletions(-) diff --git a/addon/controllers/groups/index.js b/addon/controllers/groups/index.js index a79b287..afae690 100644 --- a/addon/controllers/groups/index.js +++ b/addon/controllers/groups/index.js @@ -23,7 +23,7 @@ export default class GroupsIndexController extends Controller { * * @var {Array} */ - queryParams = ['view_group', 'page', 'limit', 'sort', 'query', 'type', 'created_by', 'updated_by', 'status']; + queryParams = this.groupActions.queryParamsFor(['view_group', 'page', 'limit', 'sort', 'query', 'type', 'created_by', 'updated_by', 'status']); /** * The current page of data being viewed @@ -54,6 +54,50 @@ export default class GroupsIndexController extends Controller { */ @tracked sort = '-created_at'; + /** + * Header buttons. Extensions add to them through `iam:table:group:actions`. + * + * @var {Array} + */ + get actionButtons() { + return [ + { + id: 'create', + text: this.intl.t('iam.common.new'), + type: 'primary', + icon: 'plus', + iconPrefix: 'fas', + onClick: this.createGroup, + permission: 'iam create group', + }, + { + id: 'export', + text: this.intl.t('iam.common.export'), + icon: 'long-arrow-up', + iconClass: 'rotate-icon-45', + onClick: this.exportGroups, + permission: 'iam export group', + }, + ]; + } + + /** + * Bulk actions for the selected rows. Extensions add to them through `iam:table:group:bulk-actions`. + * + * @var {Array} + */ + get bulkActions() { + return [ + { + id: 'bulk-delete', + label: this.intl.t('iam.groups.index.delete-groups'), + class: 'text-red-500', + fn: this.bulkDeleteGroups, + permission: 'iam delete group', + }, + ]; + } + /** * All columns applicable for groups * @@ -61,6 +105,7 @@ export default class GroupsIndexController extends Controller { */ @tracked columns = [ { + id: 'name', label: this.intl.t('iam.common.name'), valuePath: 'name', cellComponent: 'table/cell/anchor', @@ -70,12 +115,14 @@ export default class GroupsIndexController extends Controller { sortable: false, }, { + id: 'description', label: this.intl.t('iam.common.description'), valuePath: 'description', sortable: false, width: '25%', }, { + id: 'users', label: this.intl.t('iam.common.member'), valuePath: 'users', cellComponent: 'table/cell/group-members', @@ -86,6 +133,7 @@ export default class GroupsIndexController extends Controller { width: '35%', }, { + id: 'created-at', label: this.intl.t('iam.groups.index.created'), valuePath: 'createdAt', sortable: false, @@ -94,6 +142,7 @@ export default class GroupsIndexController extends Controller { cellClassNames: 'overflow-visible', }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -105,11 +154,13 @@ export default class GroupsIndexController extends Controller { width: '10%', actions: [ { + id: 'edit-group', label: this.intl.t('iam.groups.index.edit-group'), fn: this.editGroup, permission: 'iam view group', }, { + id: 'delete-group', label: this.intl.t('iam.groups.index.delete-group-label'), fn: this.deleteGroup, className: 'text-red-700 hover:text-red-800', @@ -154,7 +205,7 @@ export default class GroupsIndexController extends Controller { this.crud.bulkDelete(selected, { modelNamePath: `name`, - acceptButtonText: this.intl.t('aim.groups.index.delete-group'), + acceptButtonText: this.intl.t('iam.groups.index.delete-group'), onSuccess: () => { return this.hostRouter.refresh(); }, diff --git a/addon/controllers/policies/index.js b/addon/controllers/policies/index.js index 4c79c90..cb55e4e 100644 --- a/addon/controllers/policies/index.js +++ b/addon/controllers/policies/index.js @@ -24,7 +24,7 @@ export default class PoliciesIndexController extends Controller { * * @var {Array} */ - queryParams = ['view_policy', 'page', 'limit', 'sort', 'query', 'type', 'created_by', 'updated_by', 'status', 'service', 'type']; + queryParams = this.policyActions.queryParamsFor(['view_policy', 'page', 'limit', 'sort', 'query', 'type', 'created_by', 'updated_by', 'status', 'service', 'type']); /** * The current page of data being viewed @@ -69,6 +69,42 @@ export default class PoliciesIndexController extends Controller { */ @tracked types = this.iam.schemeTypes; + /** + * Header buttons. Extensions add to them through `iam:table:policy:actions`. + * + * @var {Array} + */ + get actionButtons() { + return [ + { + id: 'create', + text: this.intl.t('iam.common.new'), + type: 'primary', + icon: 'plus', + iconPrefix: 'fas', + onClick: this.createPolicy, + permission: 'iam create policy', + }, + ]; + } + + /** + * Bulk actions for the selected rows. Extensions add to them through `iam:table:policy:bulk-actions`. + * + * @var {Array} + */ + get bulkActions() { + return [ + { + id: 'bulk-delete', + label: this.intl.t('iam.policies.index.delete-policies'), + class: 'text-red-500', + fn: this.bulkDeletePolicies, + permission: 'iam delete policy', + }, + ]; + } + /** * All columns applicable for roles * @@ -76,6 +112,7 @@ export default class PoliciesIndexController extends Controller { */ @tracked columns = [ { + id: 'name', label: this.intl.t('iam.common.name'), valuePath: 'name', cellComponent: 'table/cell/anchor', @@ -85,12 +122,14 @@ export default class PoliciesIndexController extends Controller { sortable: false, }, { + id: 'description', label: this.intl.t('iam.common.description'), valuePath: 'description', sortable: false, width: '35%', }, { + id: 'service', label: this.intl.t('iam.common.service'), valuePath: 'service', sortable: false, @@ -100,6 +139,7 @@ export default class PoliciesIndexController extends Controller { filterOptions: this.services, }, { + id: 'type', label: this.intl.t('iam.common.type'), valuePath: 'type', sortable: false, @@ -111,6 +151,7 @@ export default class PoliciesIndexController extends Controller { filterOptions: this.types, }, { + id: 'created-at', label: this.intl.t('iam.common.create'), valuePath: 'createdAt', sortable: false, @@ -119,6 +160,7 @@ export default class PoliciesIndexController extends Controller { cellClassNames: 'overflow-visible', }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -130,11 +172,13 @@ export default class PoliciesIndexController extends Controller { width: '15%', actions: [ { + id: 'edit-policy', label: this.intl.t('iam.policies.index.edit-policy'), fn: this.editPolicy, permission: 'iam view policy', }, { + id: 'delete-policy', label: this.intl.t('iam.policies.index.delete-policy'), fn: this.deletePolicy, className: 'text-red-700 hover:text-red-800', diff --git a/addon/controllers/roles/index.js b/addon/controllers/roles/index.js index 9c8980a..b5191ef 100644 --- a/addon/controllers/roles/index.js +++ b/addon/controllers/roles/index.js @@ -24,7 +24,7 @@ export default class RolesIndexController extends Controller { * * @var {Array} */ - queryParams = ['view_role', 'page', 'limit', 'sort', 'query', 'service', 'type']; + queryParams = this.roleActions.queryParamsFor(['view_role', 'page', 'limit', 'sort', 'query', 'service', 'type']); /** * The current page of data being viewed @@ -69,6 +69,42 @@ export default class RolesIndexController extends Controller { */ @tracked types = this.iam.schemeTypes; + /** + * Header buttons. Extensions add to them through `iam:table:role:actions`. + * + * @var {Array} + */ + get actionButtons() { + return [ + { + id: 'create', + text: this.intl.t('iam.common.new'), + type: 'primary', + icon: 'plus', + iconPrefix: 'fas', + onClick: this.createRole, + permission: 'iam create role', + }, + ]; + } + + /** + * Bulk actions for the selected rows. Extensions add to them through `iam:table:role:bulk-actions`. + * + * @var {Array} + */ + get bulkActions() { + return [ + { + id: 'bulk-delete', + label: this.intl.t('iam.roles.index.delete-roles'), + class: 'text-red-500', + fn: this.bulkDeleteRoles, + permission: 'iam delete role', + }, + ]; + } + /** * All columns applicable for roles * @@ -76,6 +112,7 @@ export default class RolesIndexController extends Controller { */ @tracked columns = [ { + id: 'name', label: this.intl.t('iam.common.name'), valuePath: 'name', cellComponent: 'table/cell/anchor', @@ -85,12 +122,14 @@ export default class RolesIndexController extends Controller { sortable: false, }, { + id: 'description', label: this.intl.t('iam.common.description'), valuePath: 'description', sortable: false, width: '28%', }, { + id: 'service', label: this.intl.t('iam.common.service'), valuePath: 'service', sortable: false, @@ -100,6 +139,7 @@ export default class RolesIndexController extends Controller { filterOptions: this.services, }, { + id: 'type', label: this.intl.t('iam.common.type'), valuePath: 'type', sortable: false, @@ -111,6 +151,7 @@ export default class RolesIndexController extends Controller { filterOptions: this.types, }, { + id: 'created-at', label: this.intl.t('iam.common.create'), valuePath: 'createdAt', sortable: false, @@ -119,6 +160,7 @@ export default class RolesIndexController extends Controller { cellClassNames: 'overflow-visible', }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -130,11 +172,13 @@ export default class RolesIndexController extends Controller { width: '15%', actions: [ { + id: 'edit-role', label: this.intl.t('iam.roles.index.edit-role'), fn: this.editRole, permission: 'iam view role', }, { + id: 'delete-role', label: this.intl.t('iam.roles.index.delete-role'), fn: this.deleteRole, className: 'text-red-700 hover:text-red-800', diff --git a/addon/controllers/users/index.js b/addon/controllers/users/index.js index 00ad0b0..998b915 100644 --- a/addon/controllers/users/index.js +++ b/addon/controllers/users/index.js @@ -1,6 +1,6 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; -import { tracked } from '@glimmer/tracking'; +import { tracked, cached } from '@glimmer/tracking'; import { action } from '@ember/object'; import { isBlank } from '@ember/utils'; import { timeout, task } from 'ember-concurrency'; @@ -23,11 +23,13 @@ export default class UsersIndexController extends Controller { get actionButtons() { return [ { + id: 'refresh', icon: 'refresh', onClick: () => this.hostRouter.refresh(), helpText: this.intl.t('common.refresh'), }, { + id: 'invite-user', text: this.intl.t('iam.users.index.invite-user'), type: 'default', icon: 'paper-plane', @@ -35,6 +37,7 @@ export default class UsersIndexController extends Controller { onClick: this.inviteUser, }, { + id: 'create-user', text: this.intl.t('common.new'), type: 'primary', icon: 'plus', @@ -42,6 +45,7 @@ export default class UsersIndexController extends Controller { onClick: this.createUser, }, { + id: 'export-users', text: this.intl.t('common.export'), icon: 'long-arrow-up', iconClass: 'rotate-icon-45', @@ -58,6 +62,7 @@ export default class UsersIndexController extends Controller { return [ { + id: 'bulk-delete-users', label: this.intl.t('common.delete-selected-count', { count: selected.length }), class: 'text-red-500', fn: this.bulkDeleteUsers, @@ -65,7 +70,7 @@ export default class UsersIndexController extends Controller { ]; } - queryParams = [ + queryParams = this.userActions.queryParamsFor([ 'view_user', 'page', 'limit', @@ -83,7 +88,7 @@ export default class UsersIndexController extends Controller { 'phone_verified', 'country', 'timezone', - ]; + ]); @tracked page = 1; @tracked limit; @tracked query; @@ -104,8 +109,9 @@ export default class UsersIndexController extends Controller { * * @var {Array} */ - @tracked columns = [ + @tracked baseColumns = [ { + id: 'name', sticky: true, label: this.intl.t('iam.common.name'), valuePath: 'name', @@ -119,6 +125,7 @@ export default class UsersIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'email', sticky: true, label: this.intl.t('iam.common.email'), valuePath: 'email', @@ -129,6 +136,7 @@ export default class UsersIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'phone', label: this.intl.t('iam.common.phone'), valuePath: 'phone', cellComponent: 'click-to-copy', @@ -138,6 +146,7 @@ export default class UsersIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'role-name', label: this.intl.t('iam.common.role'), valuePath: 'role.name', sortable: false, @@ -148,6 +157,7 @@ export default class UsersIndexController extends Controller { model: 'role', }, { + id: 'session-status', label: this.intl.t('iam.common.status'), valuePath: 'session_status', sortable: false, @@ -158,6 +168,7 @@ export default class UsersIndexController extends Controller { filterOptions: ['pending', 'active', 'inactive'], }, { + id: 'email-verified-at', label: this.intl.t('iam.users.index.email-verified'), valuePath: 'email_verified_at', contactPath: 'email', @@ -169,6 +180,7 @@ export default class UsersIndexController extends Controller { filterOptions: this.verificationFilterOptions, }, { + id: 'phone-verified-at', label: this.intl.t('iam.users.index.phone-verified'), valuePath: 'phone_verified_at', contactPath: 'phone', @@ -180,6 +192,7 @@ export default class UsersIndexController extends Controller { filterOptions: this.verificationFilterOptions, }, { + id: 'country', label: this.intl.t('iam.common.country'), valuePath: 'country', cellComponent: 'table/cell/country', @@ -191,6 +204,7 @@ export default class UsersIndexController extends Controller { filterParam: 'country', }, { + id: 'timezone', label: this.intl.t('iam.users.index.timezone'), valuePath: 'timezone', hidden: true, @@ -201,6 +215,7 @@ export default class UsersIndexController extends Controller { filterParam: 'timezone', }, { + id: 'date-of-birth', label: this.intl.t('iam.users.index.date-of-birth'), valuePath: 'date_of_birth', hidden: true, @@ -209,6 +224,7 @@ export default class UsersIndexController extends Controller { filterable: false, }, { + id: 'ip-address', label: this.intl.t('iam.users.index.ip-address'), valuePath: 'ip_address', cellComponent: 'click-to-copy', @@ -218,6 +234,7 @@ export default class UsersIndexController extends Controller { filterable: false, }, { + id: 'last-login', label: this.intl.t('iam.users.index.last-login'), valuePath: 'lastLogin', resizable: true, @@ -226,6 +243,7 @@ export default class UsersIndexController extends Controller { filterComponent: 'filter/date', }, { + id: 'created-at', label: this.intl.t('iam.users.index.created-at'), valuePath: 'createdAt', sortParam: 'created_at', @@ -235,6 +253,7 @@ export default class UsersIndexController extends Controller { filterComponent: 'filter/date', }, { + id: 'updated-at', label: this.intl.t('iam.users.index.updated-at'), valuePath: 'updatedAt', sortParam: 'updated_at', @@ -245,6 +264,7 @@ export default class UsersIndexController extends Controller { filterComponent: 'filter/date', }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -257,22 +277,26 @@ export default class UsersIndexController extends Controller { width: 60, actions: [ { + id: 'edit-user', label: this.intl.t('iam.users.index.edit-user'), fn: this.editUser, permission: 'iam view user', }, { + id: 'view-user-permissions', label: this.intl.t('iam.users.index.view-user-permissions'), fn: this.viewUserPermissions, permission: 'iam view user', }, { + id: 'resend-invitation', label: this.intl.t('iam.users.index.re-send-invitation'), fn: this.resendInvitation, permission: 'iam update user', isVisible: (user) => user.get('session_status') === 'pending', }, { + id: 'deactivate-user', label: this.intl.t('iam.users.index.deactivate-user'), fn: this.deactivateUser, className: 'text-danger', @@ -280,6 +304,7 @@ export default class UsersIndexController extends Controller { isVisible: (user) => user.get('session_status') === 'active', }, { + id: 'activate-user', label: this.intl.t('iam.users.index.activate-user'), fn: this.activateUser, className: 'text-danger', @@ -287,18 +312,21 @@ export default class UsersIndexController extends Controller { isVisible: (user) => user.get('session_status') === 'inactive' || (this.currentUser.user.is_admin && user.get('session_status') === 'pending'), }, { + id: 'send-email-verification', label: this.intl.t('iam.users.index.send-email-verification'), fn: (user) => this.userActions.sendVerification(user, 'email'), permission: 'iam verify user', isVisible: (user) => this.canVerify(user, 'email'), }, { + id: 'send-phone-verification', label: this.intl.t('iam.users.index.send-phone-verification'), fn: (user) => this.userActions.sendVerification(user, 'phone'), permission: 'iam verify user', isVisible: (user) => this.canVerify(user, 'phone'), }, { + id: 'mark-email-verified', label: this.intl.t('iam.users.index.mark-email-verified'), fn: (user) => this.userActions.markVerified(user, 'email'), className: 'text-danger', @@ -306,6 +334,7 @@ export default class UsersIndexController extends Controller { isVisible: (user) => this.canVerify(user, 'email'), }, { + id: 'mark-phone-verified', label: this.intl.t('iam.users.index.mark-phone-verified'), fn: (user) => this.userActions.markVerified(user, 'phone'), className: 'text-danger', @@ -313,18 +342,21 @@ export default class UsersIndexController extends Controller { isVisible: (user) => this.canVerify(user, 'phone'), }, { + id: 'change-user-password', label: this.intl.t('iam.users.index.change-user-password'), fn: this.changeUserPassword, className: 'text-danger', isVisible: (user) => this.abilities.can('iam change-password-for user') || user.role_name === 'Administrator' || user.is_admin === true, }, { + id: 'change-user-email', label: this.intl.t('iam.users.index.change-user-email'), fn: this.changeUserEmail, className: 'text-danger', isVisible: () => this.abilities.can('iam change-email-for user') || this.currentUser.user.role_name === 'Administrator' || this.currentUser.user.is_admin === true, }, { + id: 'delete-user', label: this.intl.t('iam.users.index.delete-user'), fn: this.deleteUser, className: 'text-danger', @@ -338,6 +370,16 @@ export default class UsersIndexController extends Controller { }, ]; + /** + * The columns with what extensions registered under `iam:table:user` merged in. + * The table and its header (`users.hbs`) render separately, so both read this. + * + * @var {Array} + */ + @cached get columns() { + return this.userActions.mergeRegisteredColumns(this.baseColumns, { controller: this }); + } + /** * Options for the email/phone verified column filters. * diff --git a/addon/extension.js b/addon/extension.js index d89149b..ec20d2b 100644 --- a/addon/extension.js +++ b/addon/extension.js @@ -182,5 +182,9 @@ export default { // Register dashboard and widgets registerWidgets(widgetService); + + // Declare the registries extensions can add table columns, actions and + // buttons to, e.g. `iam:table:user:columns` or `iam:table:role:row-actions`. + universe.getService('universe/resource-view-service')?.declare('iam', ['group', 'policy', 'role', 'user']); }, }; diff --git a/addon/services/group-actions.js b/addon/services/group-actions.js index c2ce88e..cacd279 100644 --- a/addon/services/group-actions.js +++ b/addon/services/group-actions.js @@ -1,4 +1,5 @@ import ResourceActionService from '@fleetbase/ember-core/services/resource-action'; +import { mergeHeaderButtons } from '@fleetbase/ember-ui/utils/resource-view'; import { action } from '@ember/object'; import getWithDefault from '@fleetbase/ember-core/utils/get-with-default'; @@ -65,6 +66,8 @@ export default class GroupActionsService extends ResourceActionService { const formPermission = 'iam update group'; this.modalsManager.show('modals/group-form', { title: this.intl.t('iam.groups.index.edit-group-title'), + // What extensions registered under `iam:details:group` (buttons and "…" menu items). + actionButtons: mergeHeaderButtons(this.resourceView, this.detailsRegistry, [], { resource: group }, { withMenu: true }), acceptButtonText: this.intl.t('common.save-changes'), acceptButtonIcon: 'save', acceptButtonDisabled: this.abilities.cannot(formPermission), diff --git a/addon/services/policy-actions.js b/addon/services/policy-actions.js index 830a154..99ae9f2 100644 --- a/addon/services/policy-actions.js +++ b/addon/services/policy-actions.js @@ -1,4 +1,5 @@ import ResourceActionService from '@fleetbase/ember-core/services/resource-action'; +import { mergeHeaderButtons } from '@fleetbase/ember-ui/utils/resource-view'; import { action } from '@ember/object'; /** @@ -74,6 +75,8 @@ export default class PolicyActionsService extends ResourceActionService { const formPermission = 'iam update policy'; this.modalsManager.show('modals/policy-form', { title: this.intl.t('iam.policies.index.edit-policy-title'), + // What extensions registered under `iam:details:policy` (buttons and "…" menu items). + actionButtons: mergeHeaderButtons(this.resourceView, this.detailsRegistry, [], { resource: policy }, { withMenu: true }), acceptButtonText: this.intl.t('common.save-changes'), acceptButtonIcon: 'save', acceptButtonDisabled: this.abilities.cannot(formPermission), diff --git a/addon/services/role-actions.js b/addon/services/role-actions.js index ce234af..d47bd66 100644 --- a/addon/services/role-actions.js +++ b/addon/services/role-actions.js @@ -1,4 +1,5 @@ import ResourceActionService from '@fleetbase/ember-core/services/resource-action'; +import { mergeHeaderButtons } from '@fleetbase/ember-ui/utils/resource-view'; import { action } from '@ember/object'; /** @@ -83,6 +84,8 @@ export default class RoleActionsService extends ResourceActionService { const formPermission = 'iam update role'; this.modalsManager.show('modals/role-form', { title: this.intl.t('iam.roles.index.edit-role-title'), + // What extensions registered under `iam:details:role` (buttons and "…" menu items). + actionButtons: mergeHeaderButtons(this.resourceView, this.detailsRegistry, [], { resource: role }, { withMenu: true }), acceptButtonText: this.intl.t('common.save-changes'), acceptButtonIcon: 'save', acceptButtonDisabled: this.abilities.cannot(formPermission), diff --git a/addon/services/user-actions.js b/addon/services/user-actions.js index 0008eff..92c1f8f 100644 --- a/addon/services/user-actions.js +++ b/addon/services/user-actions.js @@ -1,4 +1,5 @@ import ResourceActionService from '@fleetbase/ember-core/services/resource-action'; +import { mergeHeaderButtons } from '@fleetbase/ember-ui/utils/resource-view'; import { action } from '@ember/object'; /** @@ -180,6 +181,8 @@ export default class UserActionsService extends ResourceActionService { const formPermission = 'iam update user'; this.modalsManager.show('modals/user-form', { title: this.intl.t('iam.users.index.edit-user-title'), + // What extensions registered under `iam:details:user` (buttons and "…" menu items). + actionButtons: mergeHeaderButtons(this.resourceView, this.detailsRegistry, [], { resource: user }, { withMenu: true }), modalClass: 'modal-lg', acceptButtonText: this.intl.t('common.save-changes'), acceptButtonIcon: 'save', diff --git a/addon/templates/groups/index.hbs b/addon/templates/groups/index.hbs index 2e747bc..d278b96 100644 --- a/addon/templates/groups/index.hbs +++ b/addon/templates/groups/index.hbs @@ -1,31 +1,17 @@ - - {{#if (safe-has this.table "selectedRows")}} - - - - {{/if}} -