1<template> 2 <b-container fluid="xl"> 3 <page-title /> 4 <b-row class="align-items-end"> 5 <b-col sm="6" md="5" xl="4"> 6 <search 7 :placeholder="$t('pageSessions.table.searchSessions')" 8 data-test-id="sessions-input-searchSessions" 9 @change-search="onChangeSearchInput" 10 @clear-search="onClearSearchInput" 11 /> 12 </b-col> 13 <b-col sm="3" md="3" xl="2"> 14 <table-cell-count 15 :filtered-items-count="filteredRows" 16 :total-number-of-cells="allConnections.length" 17 ></table-cell-count> 18 </b-col> 19 </b-row> 20 <b-row> 21 <b-col> 22 <table-toolbar 23 ref="toolbar" 24 :selected-items-count=" 25 Array.isArray(selectedRows) ? selectedRows.length : 0 26 " 27 :actions="batchActions" 28 @clear-selected="clearSelectedRows($refs.table)" 29 @batch-action="onBatchAction" 30 > 31 </table-toolbar> 32 <b-table 33 id="table-session-logs" 34 ref="table" 35 responsive="md" 36 selectable 37 no-select-on-click 38 hover 39 show-empty 40 thead-class="table-light" 41 :sort-by="['sessionID']" 42 :busy="isBusy" 43 :fields="fields" 44 :items="allConnections" 45 :filter="searchFilter" 46 :empty-text="$t('global.table.emptyMessage')" 47 :per-page="perPage" 48 :current-page="currentPage" 49 @filtered="onFiltered" 50 @row-selected="onRowSelected" 51 > 52 <!-- Checkbox column --> 53 <template #head(checkbox)> 54 <b-form-checkbox 55 v-model="tableHeaderCheckboxModel" 56 data-test-id="sessions-checkbox-selectAll" 57 :indeterminate="tableHeaderCheckboxIndeterminate" 58 @change="onChangeHeaderCheckbox($refs.table, $event)" 59 > 60 <span class="visually-hidden-focusable"> 61 {{ $t('global.table.selectAll') }} 62 </span> 63 </b-form-checkbox> 64 </template> 65 <template #cell(checkbox)="row"> 66 <b-form-checkbox 67 v-model="row.rowSelected" 68 :data-test-id="`sessions-checkbox-selectRow-${row.index}`" 69 @change="toggleSelectRow($refs.table, row.index)" 70 > 71 <span class="visually-hidden-focusable"> 72 {{ $t('global.table.selectItem') }} 73 </span> 74 </b-form-checkbox> 75 </template> 76 77 <!-- Actions column --> 78 <template #cell(actions)="row"> 79 <table-row-action 80 v-for="(action, index) in row.item.actions" 81 :key="index" 82 :value="action.value" 83 :title="action.title" 84 :row-data="row.item" 85 :btn-icon-only="false" 86 :data-test-id="`sessions-button-disconnect-${row.index}`" 87 @click-table-action="onTableRowAction($event, row.item)" 88 ></table-row-action> 89 </template> 90 </b-table> 91 </b-col> 92 </b-row> 93 94 <!-- Table pagination --> 95 <b-row> 96 <b-col sm="6"> 97 <b-form-group 98 class="table-pagination-select" 99 :label="$t('global.table.itemsPerPage')" 100 label-for="pagination-items-per-page" 101 > 102 <b-form-select 103 id="pagination-items-per-page" 104 v-model="perPage" 105 :options="itemsPerPageOptions" 106 /> 107 </b-form-group> 108 </b-col> 109 <b-col sm="6"> 110 <b-pagination 111 v-model="currentPage" 112 first-number 113 last-number 114 :per-page="perPage" 115 :total-rows="getTotalRowCount(filteredRows)" 116 aria-controls="table-session-logs" 117 /> 118 </b-col> 119 </b-row> 120 </b-container> 121</template> 122 123<script> 124import PageTitle from '@/components/Global/PageTitle'; 125import Search from '@/components/Global/Search'; 126import TableCellCount from '@/components/Global/TableCellCount'; 127import TableRowAction from '@/components/Global/TableRowAction'; 128import TableToolbar from '@/components/Global/TableToolbar'; 129 130import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; 131import BVPaginationMixin, { 132 currentPage, 133 perPage, 134 itemsPerPageOptions, 135} from '@/components/Mixins/BVPaginationMixin'; 136import BVTableSelectableMixin from '@/components/Mixins/BVTableSelectableMixin'; 137import BVToastMixin from '@/components/Mixins/BVToastMixin'; 138import SearchFilterMixin, { 139 searchFilter, 140} from '@/components/Mixins/SearchFilterMixin'; 141import { useI18n } from 'vue-i18n'; 142import i18n from '@/i18n'; 143import { useModal } from 'bootstrap-vue-next'; 144 145export default { 146 components: { 147 PageTitle, 148 Search, 149 TableCellCount, 150 TableRowAction, 151 TableToolbar, 152 }, 153 mixins: [ 154 BVPaginationMixin, 155 BVTableSelectableMixin, 156 BVToastMixin, 157 LoadingBarMixin, 158 SearchFilterMixin, 159 ], 160 beforeRouteLeave(to, from, next) { 161 // Hide loader if the user navigates to another page 162 // before request is fulfilled. 163 this.hideLoader(); 164 next(); 165 }, 166 setup() { 167 const bvModal = useModal(); 168 return { bvModal }; 169 }, 170 data() { 171 return { 172 $t: useI18n().t, 173 isBusy: true, 174 fields: [ 175 { 176 key: 'checkbox', 177 class: 'text-center', 178 }, 179 { 180 key: 'sessionID', 181 label: i18n.global.t('pageSessions.table.sessionID'), 182 class: 'text-center', 183 }, 184 { 185 key: 'context', 186 label: i18n.global.t('pageSessions.table.context'), 187 class: 'text-center', 188 }, 189 { 190 key: 'username', 191 label: i18n.global.t('pageSessions.table.username'), 192 class: 'text-center', 193 }, 194 { 195 key: 'ipAddress', 196 label: i18n.global.t('pageSessions.table.ipAddress'), 197 class: 'text-center', 198 }, 199 { 200 key: 'actions', 201 label: '', 202 class: 'text-center', 203 }, 204 ], 205 batchActions: [ 206 { 207 value: 'disconnect', 208 label: i18n.global.t('pageSessions.action.disconnect'), 209 }, 210 ], 211 currentPage: currentPage, 212 itemsPerPageOptions: itemsPerPageOptions, 213 perPage: perPage, 214 searchTotalFilteredRows: 0, 215 searchFilter: searchFilter, 216 }; 217 }, 218 computed: { 219 filteredRows() { 220 return this.searchFilter 221 ? this.searchTotalFilteredRows 222 : this.allConnections.length; 223 }, 224 allConnections() { 225 return this.$store.getters['sessions/allConnections'].map((session) => { 226 return { 227 ...session, 228 actions: [ 229 { 230 value: 'disconnect', 231 title: i18n.global.t('pageSessions.action.disconnect'), 232 }, 233 ], 234 }; 235 }); 236 }, 237 }, 238 created() { 239 this.startLoader(); 240 this.$store.dispatch('sessions/getSessionsData').finally(() => { 241 this.endLoader(); 242 this.isBusy = false; 243 }); 244 }, 245 methods: { 246 onFiltered(filteredItems) { 247 this.searchTotalFilteredRows = filteredItems.length; 248 }, 249 onChangeSearchInput(event) { 250 this.searchFilter = event; 251 }, 252 disconnectSessions(uris) { 253 this.$store 254 .dispatch('sessions/disconnectSessions', uris) 255 .then((messages) => { 256 messages.forEach(({ type, message }) => { 257 if (type === 'success') { 258 this.successToast(message); 259 } else if (type === 'error') { 260 this.errorToast(message); 261 } 262 }); 263 }); 264 }, 265 onTableRowAction(action, { uri }) { 266 if (action === 'disconnect') { 267 this.confirmDialog( 268 i18n.global.t('pageSessions.modal.disconnectMessage'), 269 { 270 title: i18n.global.t('pageSessions.modal.disconnectTitle'), 271 okTitle: i18n.global.t('pageSessions.action.disconnect'), 272 cancelTitle: i18n.global.t('global.action.cancel'), 273 autoFocusButton: 'ok', 274 }, 275 ).then((deleteConfirmed) => { 276 if (deleteConfirmed) this.disconnectSessions([uri]); 277 }); 278 } 279 }, 280 onBatchAction(action) { 281 if (action === 'disconnect') { 282 const uris = this.selectedRows.map((row) => row.uri); 283 const count = this.selectedRows.length; 284 this.confirmDialog( 285 i18n.global.t('pageSessions.modal.disconnectMessage', count), 286 { 287 title: i18n.global.t('pageSessions.modal.disconnectTitle', count), 288 okTitle: i18n.global.t('pageSessions.action.disconnect'), 289 cancelTitle: i18n.global.t('global.action.cancel'), 290 autoFocusButton: 'ok', 291 }, 292 ).then((deleteConfirmed) => { 293 if (deleteConfirmed) { 294 this.disconnectSessions(uris); 295 } 296 }); 297 } 298 }, 299 confirmDialog(message, options = {}) { 300 return this.$confirm({ message, ...options }); 301 }, 302 }, 303}; 304</script> 305<style lang="scss"> 306#table-session-logs { 307 td .btn-link { 308 width: auto !important; 309 } 310} 311</style> 312