1<template> 2 <b-container fluid="xl"> 3 <page-title /> 4 <b-row> 5 <b-col xl="9" class="text-end"> 6 <b-button variant="link" @click="initModalSettings"> 7 <icon-settings /> 8 {{ $t('pageUserManagement.accountPolicySettings') }} 9 </b-button> 10 <b-button 11 variant="primary" 12 data-test-id="userManagement-button-addUser" 13 @click="initModalUser(null)" 14 > 15 <icon-add /> 16 {{ $t('pageUserManagement.addUser') }} 17 </b-button> 18 </b-col> 19 </b-row> 20 <b-row> 21 <b-col xl="9"> 22 <table-toolbar 23 ref="toolbar" 24 :selected-items-count=" 25 Array.isArray(selectedRows) ? selectedRows.length : 0 26 " 27 :actions="tableToolbarActions" 28 @clear-selected="clearSelectedRows($refs.table)" 29 @batch-action="onBatchAction" 30 /> 31 <b-table 32 ref="table" 33 responsive="md" 34 selectable 35 show-empty 36 no-select-on-click 37 hover 38 thead-class="table-light" 39 :busy="isBusy" 40 :fields="fields" 41 :items="tableItems" 42 :empty-text="$t('global.table.emptyMessage')" 43 @row-selected="onRowSelected($event, tableItems.length)" 44 > 45 <!-- Checkbox column --> 46 <template #head(checkbox)> 47 <b-form-checkbox 48 v-model="tableHeaderCheckboxModel" 49 data-test-id="userManagement-checkbox-tableHeaderCheckbox" 50 :indeterminate="tableHeaderCheckboxIndeterminate" 51 @change="onChangeHeaderCheckbox($refs.table, $event)" 52 > 53 <span class="visually-hidden-focusable"> 54 {{ $t('global.table.selectAll') }} 55 </span> 56 </b-form-checkbox> 57 </template> 58 <template #cell(checkbox)="row"> 59 <b-form-checkbox 60 v-model="row.rowSelected" 61 data-test-id="userManagement-checkbox-toggleSelectRow" 62 @change="toggleSelectRow($refs.table, row.index)" 63 > 64 <span class="visually-hidden-focusable"> 65 {{ $t('global.table.selectItem') }} 66 </span> 67 </b-form-checkbox> 68 </template> 69 70 <!-- table actions column --> 71 <template #cell(actions)="{ item }"> 72 <table-row-action 73 v-for="(action, index) in item.actions" 74 :key="index" 75 :value="action.value" 76 :enabled="action.enabled" 77 :title="action.title" 78 @click-table-action="onTableRowAction($event, item)" 79 > 80 <template #icon> 81 <icon-edit 82 v-if="action.value === 'edit'" 83 :data-test-id="`userManagement-tableRowAction-edit-${index}`" 84 /> 85 <icon-trashcan 86 v-if="action.value === 'delete'" 87 :data-test-id="`userManagement-tableRowAction-delete-${index}`" 88 /> 89 </template> 90 </table-row-action> 91 </template> 92 </b-table> 93 </b-col> 94 </b-row> 95 <b-row> 96 <b-col xl="8"> 97 <b-button 98 data-test-id="userManagement-button-viewPrivilegeRoleDescriptions" 99 variant="link" 100 class="mt-3" 101 @click="showRoles = !showRoles" 102 > 103 <icon-chevron /> 104 {{ $t('pageUserManagement.viewPrivilegeRoleDescriptions') }} 105 </b-button> 106 <b-collapse id="collapse-role-table" :visible="showRoles" class="mt-3"> 107 <table-roles /> 108 </b-collapse> 109 </b-col> 110 </b-row> 111 <!-- Modals --> 112 <modal-settings 113 v-model="showSettingsModal" 114 :settings="setting" 115 @ok="saveAccountSettings" 116 /> 117 <modal-user 118 v-model="showUserModal" 119 :user="activeUser" 120 :password-requirements="passwordRequirements" 121 @ok="saveUser" 122 @hidden="activeUser = null" 123 /> 124 </b-container> 125</template> 126 127<script> 128import IconTrashcan from '@carbon/icons-vue/es/trash-can/20'; 129import IconEdit from '@carbon/icons-vue/es/edit/20'; 130import IconAdd from '@carbon/icons-vue/es/add--alt/20'; 131import IconSettings from '@carbon/icons-vue/es/settings/20'; 132import IconChevron from '@carbon/icons-vue/es/chevron--up/20'; 133 134import ModalUser from './ModalUser'; 135import ModalSettings from './ModalSettings'; 136import PageTitle from '@/components/Global/PageTitle'; 137import TableRoles from './TableRoles'; 138import TableToolbar from '@/components/Global/TableToolbar'; 139import TableRowAction from '@/components/Global/TableRowAction'; 140 141import BVTableSelectableMixin, { 142 selectedRows, 143 tableHeaderCheckboxModel, 144 tableHeaderCheckboxIndeterminate, 145} from '@/components/Mixins/BVTableSelectableMixin'; 146import BVToastMixin from '@/components/Mixins/BVToastMixin'; 147import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; 148import { useI18n } from 'vue-i18n'; 149import i18n from '@/i18n'; 150import { useModal } from 'bootstrap-vue-next'; 151 152export default { 153 name: 'UserManagement', 154 components: { 155 IconAdd, 156 IconChevron, 157 IconEdit, 158 IconSettings, 159 IconTrashcan, 160 ModalSettings, 161 ModalUser, 162 PageTitle, 163 TableRoles, 164 TableRowAction, 165 TableToolbar, 166 }, 167 mixins: [BVTableSelectableMixin, BVToastMixin, LoadingBarMixin], 168 beforeRouteLeave(to, from, next) { 169 this.hideLoader(); 170 next(); 171 }, 172 setup() { 173 const bvModal = useModal(); 174 return { bvModal }; 175 }, 176 data() { 177 return { 178 $t: useI18n().t, 179 isBusy: true, 180 activeUser: null, 181 setting: {}, 182 fields: [ 183 { 184 key: 'checkbox', 185 }, 186 { 187 key: 'username', 188 label: i18n.global.t('pageUserManagement.table.username'), 189 }, 190 { 191 key: 'privilege', 192 label: i18n.global.t('pageUserManagement.table.privilege'), 193 }, 194 { 195 key: 'status', 196 label: i18n.global.t('pageUserManagement.table.status'), 197 }, 198 { 199 key: 'actions', 200 label: '', 201 tdClass: 'text-end text-nowrap', 202 }, 203 ], 204 tableToolbarActions: [ 205 { 206 value: 'delete', 207 label: i18n.global.t('global.action.delete'), 208 }, 209 { 210 value: 'enable', 211 label: i18n.global.t('global.action.enable'), 212 }, 213 { 214 value: 'disable', 215 label: i18n.global.t('global.action.disable'), 216 }, 217 ], 218 selectedRows: selectedRows, 219 tableHeaderCheckboxModel: tableHeaderCheckboxModel, 220 tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate, 221 showUserModal: false, 222 showSettingsModal: false, 223 showRoles: false, 224 }; 225 }, 226 computed: { 227 allUsers() { 228 return this.$store.getters['userManagement/allUsers']; 229 }, 230 tableItems() { 231 // transform user data to table data 232 return this.allUsers.map((user) => { 233 return { 234 username: user.UserName, 235 privilege: user.RoleId, 236 status: user.Locked 237 ? 'Locked' 238 : user.Enabled 239 ? 'Enabled' 240 : 'Disabled', 241 actions: [ 242 { 243 value: 'edit', 244 enabled: this.editEnable(user), 245 title: i18n.global.t('pageUserManagement.editUser'), 246 }, 247 { 248 value: 'delete', 249 enabled: 250 user.UserName === this.$store.getters['global/username'] 251 ? false 252 : true && user.UserName === 'root' 253 ? false 254 : true, 255 title: i18n.global.t('pageUserManagement.deleteUser'), 256 }, 257 ], 258 ...user, 259 }; 260 }); 261 }, 262 settings() { 263 return this.$store.getters['userManagement/accountSettings']; 264 }, 265 passwordRequirements() { 266 return this.$store.getters['userManagement/accountPasswordRequirements']; 267 }, 268 }, 269 created() { 270 this.startLoader(); 271 this.$store.dispatch('userManagement/getUsers').finally(() => { 272 this.endLoader(); 273 this.isBusy = false; 274 }); 275 this.$store.dispatch('userManagement/getAccountSettings'); 276 this.$store.dispatch('userManagement/getAccountRoles'); 277 }, 278 methods: { 279 editEnable(user) { 280 if ('root' === this.$store.getters['global/username']) { 281 return true; 282 } else { 283 return user.UserName === 'root' ? false : true; 284 } 285 }, 286 initModalUser(user) { 287 this.activeUser = user; 288 this.showUserModal = true; 289 }, 290 initModalDelete(user) { 291 this.confirmDialog( 292 i18n.global.t('pageUserManagement.modal.deleteConfirmMessage', { 293 user: user.username, 294 }), 295 { 296 title: i18n.global.t('pageUserManagement.deleteUser'), 297 okTitle: i18n.global.t('pageUserManagement.deleteUser'), 298 cancelTitle: i18n.global.t('global.action.cancel'), 299 autoFocusButton: 'ok', 300 }, 301 ).then((deleteConfirmed) => { 302 if (deleteConfirmed) { 303 this.deleteUser(user); 304 } 305 }); 306 }, 307 initModalSettings() { 308 this.setting = this.settings; 309 this.showSettingsModal = true; 310 }, 311 saveUser({ isNewUser, userData }) { 312 this.startLoader(); 313 if (isNewUser) { 314 this.$store 315 .dispatch('userManagement/createUser', userData) 316 .then((success) => this.successToast(success)) 317 .catch(({ message }) => this.errorToast(message)) 318 .finally(() => this.endLoader()); 319 } else { 320 this.$store 321 .dispatch('userManagement/updateUser', userData) 322 .then((success) => this.successToast(success)) 323 .catch(({ message }) => this.errorToast(message)) 324 .finally(() => this.endLoader()); 325 } 326 }, 327 deleteUser({ username }) { 328 this.startLoader(); 329 this.$store 330 .dispatch('userManagement/deleteUser', username) 331 .then((success) => this.successToast(success)) 332 .catch(({ message }) => this.errorToast(message)) 333 .finally(() => this.endLoader()); 334 }, 335 onBatchAction(action) { 336 const count = this.selectedRows.length; 337 switch (action) { 338 case 'delete': 339 this.confirmDialog( 340 i18n.global.t( 341 'pageUserManagement.modal.batchDeleteConfirmMessage', 342 count, 343 ), 344 { 345 title: i18n.global.t('pageUserManagement.deleteUser', count), 346 okTitle: i18n.global.t('pageUserManagement.deleteUser', count), 347 cancelTitle: i18n.global.t('global.action.cancel'), 348 autoFocusButton: 'ok', 349 }, 350 ).then((deleteConfirmed) => { 351 if (deleteConfirmed) { 352 this.startLoader(); 353 this.$store 354 .dispatch('userManagement/deleteUsers', this.selectedRows) 355 .then((messages) => { 356 messages.forEach(({ type, message }) => { 357 if (type === 'success') this.successToast(message); 358 if (type === 'error') this.errorToast(message); 359 }); 360 }) 361 .finally(() => this.endLoader()); 362 } 363 }); 364 break; 365 case 'enable': 366 this.startLoader(); 367 this.$store 368 .dispatch('userManagement/enableUsers', this.selectedRows) 369 .then((messages) => { 370 messages.forEach(({ type, message }) => { 371 if (type === 'success') this.successToast(message); 372 if (type === 'error') this.errorToast(message); 373 }); 374 }) 375 .finally(() => this.endLoader()); 376 break; 377 case 'disable': 378 this.confirmDialog( 379 i18n.global.t( 380 'pageUserManagement.modal.batchDisableConfirmMessage', 381 count, 382 ), 383 { 384 title: i18n.global.t('pageUserManagement.disableUser', count), 385 okTitle: i18n.global.t('pageUserManagement.disableUser', count), 386 cancelTitle: i18n.global.t('global.action.cancel'), 387 autoFocusButton: 'ok', 388 }, 389 ).then((disableConfirmed) => { 390 if (disableConfirmed) { 391 this.startLoader(); 392 this.$store 393 .dispatch('userManagement/disableUsers', this.selectedRows) 394 .then((messages) => { 395 messages.forEach(({ type, message }) => { 396 if (type === 'success') this.successToast(message); 397 if (type === 'error') this.errorToast(message); 398 }); 399 }) 400 .finally(() => this.endLoader()); 401 } 402 }); 403 break; 404 } 405 }, 406 confirmDialog(message, options = {}) { 407 return this.$confirm({ message, ...options }); 408 }, 409 onTableRowAction(action, row) { 410 switch (action) { 411 case 'edit': 412 this.initModalUser(row); 413 break; 414 case 'delete': 415 this.initModalDelete(row); 416 break; 417 default: 418 break; 419 } 420 }, 421 saveAccountSettings(settings) { 422 this.startLoader(); 423 this.$store 424 .dispatch('userManagement/saveAccountSettings', settings) 425 .then((message) => this.successToast(message)) 426 .catch(({ message }) => this.errorToast(message)) 427 .finally(() => this.endLoader()); 428 }, 429 }, 430}; 431</script> 432 433<style lang="scss" scoped> 434.btn.collapsed { 435 svg { 436 transform: rotate(180deg); 437 } 438} 439</style> 440