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