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="clientID" 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 }, 167 { 168 key: 'clientID', 169 label: this.$t('pageSessions.table.clientID'), 170 }, 171 { 172 key: 'username', 173 label: this.$t('pageSessions.table.username'), 174 }, 175 { 176 key: 'ipAddress', 177 label: this.$t('pageSessions.table.ipAddress'), 178 }, 179 { 180 key: 'actions', 181 label: '', 182 }, 183 ], 184 batchActions: [ 185 { 186 value: 'disconnect', 187 label: this.$t('pageSessions.action.disconnect'), 188 }, 189 ], 190 currentPage: currentPage, 191 itemsPerPageOptions: itemsPerPageOptions, 192 perPage: perPage, 193 selectedRows: selectedRows, 194 searchTotalFilteredRows: 0, 195 tableHeaderCheckboxModel: tableHeaderCheckboxModel, 196 tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate, 197 searchFilter: searchFilter, 198 }; 199 }, 200 computed: { 201 filteredRows() { 202 return this.searchFilter 203 ? this.searchTotalFilteredRows 204 : this.allConnections.length; 205 }, 206 allConnections() { 207 return this.$store.getters['sessions/allConnections'].map((session) => { 208 return { 209 ...session, 210 actions: [ 211 { 212 value: 'disconnect', 213 title: this.$t('pageSessions.action.disconnect'), 214 }, 215 ], 216 }; 217 }); 218 }, 219 }, 220 created() { 221 this.startLoader(); 222 this.$store.dispatch('sessions/getSessionsData').finally(() => { 223 this.endLoader(); 224 this.isBusy = false; 225 }); 226 }, 227 methods: { 228 onFiltered(filteredItems) { 229 this.searchTotalFilteredRows = filteredItems.length; 230 }, 231 onChangeSearchInput(event) { 232 this.searchFilter = event; 233 }, 234 disconnectSessions(uris) { 235 this.$store 236 .dispatch('sessions/disconnectSessions', uris) 237 .then((messages) => { 238 messages.forEach(({ type, message }) => { 239 if (type === 'success') { 240 this.successToast(message); 241 } else if (type === 'error') { 242 this.errorToast(message); 243 } 244 }); 245 }); 246 }, 247 onTableRowAction(action, { uri }) { 248 if (action === 'disconnect') { 249 this.$bvModal 250 .msgBoxConfirm(this.$tc('pageSessions.modal.disconnectMessage'), { 251 title: this.$tc('pageSessions.modal.disconnectTitle'), 252 okTitle: this.$t('pageSessions.action.disconnect'), 253 cancelTitle: this.$t('global.action.cancel'), 254 }) 255 .then((deleteConfirmed) => { 256 if (deleteConfirmed) this.disconnectSessions([uri]); 257 }); 258 } 259 }, 260 onBatchAction(action) { 261 if (action === 'disconnect') { 262 const uris = this.selectedRows.map((row) => row.uri); 263 this.$bvModal 264 .msgBoxConfirm( 265 this.$tc( 266 'pageSessions.modal.disconnectMessage', 267 this.selectedRows.length 268 ), 269 { 270 title: this.$tc( 271 'pageSessions.modal.disconnectTitle', 272 this.selectedRows.length 273 ), 274 okTitle: this.$t('pageSessions.action.disconnect'), 275 cancelTitle: this.$t('global.action.cancel'), 276 } 277 ) 278 .then((deleteConfirmed) => { 279 if (deleteConfirmed) { 280 this.disconnectSessions(uris); 281 } 282 }); 283 } 284 }, 285 }, 286}; 287</script> 288<style lang="scss"> 289#table-session-logs { 290 td .btn-link { 291 width: auto !important; 292 } 293} 294</style> 295