Add batch actions to local user table
Add ability to remove, enable, disable local users in bulk.
- Updates to table-actions component to fix flickering issue
by including track by $index in ng-repeat
Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I67039e9b9d9cf7debe9f6ef87e71210bd4b64251
diff --git a/app/common/components/table/table-actions.js b/app/common/components/table/table-actions.js
index e1e47ec..d76d5e5 100644
--- a/app/common/components/table/table-actions.js
+++ b/app/common/components/table/table-actions.js
@@ -58,9 +58,9 @@
};
/**
- * onInit Component lifecycle hook
+ * onChanges Component lifecycle hook
*/
- this.$onInit = () => {
+ this.$onChanges = () => {
this.actions = setActions(this.actions);
};
};
@@ -73,7 +73,7 @@
class="btn btn-tertiary"
type="button"
aria-label="{{action.type}}"
- ng-repeat="action in $ctrl.actions"
+ ng-repeat="action in $ctrl.actions track by $index"
ng-disabled="!action.enabled"
ng-click="$ctrl.onClick(action.type)">
<icon ng-if="action.file !== null" ng-file="{{action.file}}"></icon>
diff --git a/app/common/components/table/table.html b/app/common/components/table/table.html
index 7d906a1..387b18d 100644
--- a/app/common/components/table/table.html
+++ b/app/common/components/table/table.html
@@ -99,6 +99,7 @@
<td ng-if="$ctrl.rowActionsEnabled"
class="bmc-table__cell bmc-table__row-actions">
<table-actions
+ ng-if="row.actions"
actions="row.actions"
emit-action="$ctrl.onEmitRowAction(action, row)">
</table-actions>
diff --git a/app/common/components/table/table.js b/app/common/components/table/table.js
index 2063555..a382429 100644
--- a/app/common/components/table/table.js
+++ b/app/common/components/table/table.js
@@ -288,6 +288,7 @@
const dataChange = onChangesObj.data;
if (dataChange) {
prepData();
+ deselectAllRows();
}
};
};
diff --git a/app/users/controllers/user-accounts-controller.html b/app/users/controllers/user-accounts-controller.html
index 9577196..31ba62d 100644
--- a/app/users/controllers/user-accounts-controller.html
+++ b/app/users/controllers/user-accounts-controller.html
@@ -26,7 +26,10 @@
data="tableData"
header="tableHeader"
row-actions-enabled="true"
+ selectable="true"
+ batch-actions="tableBatchActions"
emit-row-action="onEmitRowAction(value)"
+ emit-batch-action="onEmitBatchAction(value)"
class="local-users__table">
</bmc-table>
</div>
diff --git a/app/users/controllers/user-accounts-controller.js b/app/users/controllers/user-accounts-controller.js
index 9f31beb..2e7605c 100644
--- a/app/users/controllers/user-accounts-controller.js
+++ b/app/users/controllers/user-accounts-controller.js
@@ -10,8 +10,8 @@
'use strict';
angular.module('app.users').controller('userAccountsController', [
- '$scope', 'APIUtils', 'toastService', '$uibModal',
- function($scope, APIUtils, toastService, $uibModal) {
+ '$scope', 'APIUtils', 'toastService', '$uibModal', '$q',
+ function($scope, APIUtils, toastService, $uibModal, $q) {
$scope.loading;
$scope.accountSettings;
$scope.userRoles;
@@ -21,6 +21,19 @@
$scope.tableHeader = [
{label: 'Username'}, {label: 'Privilege'}, {label: 'Account status'}
];
+ $scope.tableBatchActions = [
+ {type: 'delete', label: 'Remove'},
+ {type: 'enable', label: 'Enable'},
+ {type: 'disable', label: 'Disable'},
+ ];
+
+ /**
+ * Returns true if username is 'root'
+ * @param {*} user
+ */
+ function checkIfRoot(user) {
+ return user.UserName === 'root' ? true : false;
+ }
/**
* Data table mapper
@@ -33,10 +46,10 @@
const editAction = {type: 'Edit', enabled: true, file: 'icon-edit.svg'};
const deleteAction = {
type: 'Delete',
- enabled: user.UserName === 'root' ? false : true,
+ enabled: checkIfRoot(user) ? false : true,
file: 'icon-trashcan.svg'
};
-
+ user.selectable = checkIfRoot(user) ? false : true;
user.actions = [editAction, deleteAction];
user.uiData = [user.UserName, user.RoleId, accountStatus];
return user;
@@ -144,21 +157,74 @@
}
/**
- * API call to delete user
- * @param {*} username
+ * API call to delete users
+ * @param {*} users : Array of users to delete
*/
- function deleteUser(username) {
+ function deleteUsers(users = []) {
$scope.loading = true;
- APIUtils.deleteUser(username)
+ const promises =
+ users.map((user) => APIUtils.deleteUser(user.UserName));
+ $q.all(promises)
.then(() => {
- getLocalUsers();
- toastService.success(`User '${username}' has been deleted.`);
+ let message;
+ if (users.length > 1) {
+ message = 'Users have been removed.'
+ } else {
+ message = `User '${users[0].UserName}' has been removed.`
+ }
+ toastService.success(message);
})
.catch((error) => {
console.log(JSON.stringify(error));
- toastService.error(`Failed to delete user '${username}'.`);
+ let message;
+ if (users.length > 1) {
+ message = 'Failed to remove users.'
+ } else {
+ message = `Failed to remove user '${users[0].UserName}'.`
+ }
+ toastService.error(message);
})
.finally(() => {
+ getLocalUsers();
+ $scope.loading = false;
+ });
+ }
+
+ /**
+ * API call to update user status enabled/disabled
+ * @param {*} users : Array of users to update
+ * @param {boolean} enabled : status
+ */
+ function updateUserStatus(users = [], enabled = true) {
+ $scope.loading = true;
+ const promises = users.map(
+ (user) => APIUtils.updateUser(
+ user.UserName, null, null, null, enabled, null));
+ $q.all(promises)
+ .then(() => {
+ let message;
+ let statusLabel = enabled ? 'enabled' : 'disabled';
+ if (users.length > 1) {
+ message = `Users ${statusLabel}.`
+ } else {
+ message = `User '${users[0].UserName}' ${statusLabel}.`;
+ }
+ toastService.success(message);
+ })
+ .catch((error) => {
+ console.log(JSON.stringify(error));
+ let message;
+ let statusLabel = enabled ? 'enable' : 'disable';
+ if (users.length > 1) {
+ message = `Failed to ${statusLabel} users.`
+ } else {
+ message =
+ `Failed to ${statusLabel} user '${users[0].UserName}'.`
+ }
+ toastService.error(message);
+ })
+ .finally(() => {
+ getLocalUsers();
$scope.loading = false;
});
}
@@ -257,7 +323,7 @@
// Some form controls will be disabled for root users:
// edit enabled status, edit username, edit role
const isRoot =
- newUser ? false : user.UserName === 'root' ? true : false;
+ newUser ? false : checkIfRoot(user) ? true : false;
// Array of existing usernames (excluding current user instance)
const existingUsernames =
$scope.localUsers.reduce((acc, val) => {
@@ -328,10 +394,10 @@
}
/**
- * Intiate remove user modal
- * @param {*} user
+ * Intiate remove users modal
+ * @param {*} users
*/
- function initRemoveModal(user) {
+ function initRemoveModal(users) {
const template = require('./user-accounts-modal-remove.html');
$uibModal
.open({
@@ -340,17 +406,12 @@
ariaLabelledBy: 'dialog_label',
controllerAs: 'modalCtrl',
controller: function() {
- this.user = user.UserName;
+ this.users = users;
}
})
.result
.then(() => {
- const isRoot = user.UserName === 'root' ? true : false;
- if (isRoot) {
- toastService.error(`Cannot delete 'root' user.`)
- return;
- }
- deleteUser(user.UserName);
+ deleteUsers(users);
})
.catch(
() => {
@@ -368,13 +429,32 @@
initUserModal(value.row);
break;
case 'Delete':
- initRemoveModal(value.row);
+ initRemoveModal([value.row]);
break;
default:
}
};
/**
+ * Callback when batch action emitted from table
+ */
+ $scope.onEmitBatchAction = (value) => {
+ switch (value.action) {
+ case 'delete':
+ initRemoveModal(value.filteredRows);
+ break;
+ case 'enable':
+ updateUserStatus(value.filteredRows, true)
+ break;
+ case 'disable':
+ updateUserStatus(value.filteredRows, false)
+ break;
+ default:
+ break;
+ }
+ };
+
+ /**
* Callback when 'Account settings policy' button clicked
*/
$scope.onClickAccountSettingsPolicy = () => {
diff --git a/app/users/controllers/user-accounts-modal-remove.html b/app/users/controllers/user-accounts-modal-remove.html
index e615251..4a3efce 100644
--- a/app/users/controllers/user-accounts-modal-remove.html
+++ b/app/users/controllers/user-accounts-modal-remove.html
@@ -8,7 +8,8 @@
</button>
</div>
<div class="modal-body">
- <p>Are you sure you want to remove user '{{modalCtrl.user}}'? This action cannot be undone.</p>
+ <p ng-if="modalCtrl.users.length > 1">Are you sure you want to remove {{modalCtrl.users.length}} users? This action cannot be undone.</p>
+ <p ng-if="modalCtrl.users.length === 1">Are you sure you want to remove user '{{modalCtrl.users[0].UserName}}'? This action cannot be undone.</p>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" ng-click="$dismiss()" type="button">