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"> 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'; 138import { useI18n } from 'vue-i18n'; 139import i18n from '@/i18n'; 140 141export default { 142 components: { 143 PageTitle, 144 Search, 145 TableCellCount, 146 TableRowAction, 147 TableToolbar, 148 }, 149 mixins: [ 150 BVPaginationMixin, 151 BVTableSelectableMixin, 152 BVToastMixin, 153 LoadingBarMixin, 154 SearchFilterMixin, 155 ], 156 beforeRouteLeave(to, from, next) { 157 // Hide loader if the user navigates to another page 158 // before request is fulfilled. 159 this.hideLoader(); 160 next(); 161 }, 162 data() { 163 return { 164 $t: useI18n().t, 165 isBusy: true, 166 fields: [ 167 { 168 key: 'checkbox', 169 class: 'text-center', 170 }, 171 { 172 key: 'sessionID', 173 label: i18n.global.t('pageSessions.table.sessionID'), 174 class: 'text-center', 175 }, 176 { 177 key: 'context', 178 label: i18n.global.t('pageSessions.table.context'), 179 class: 'text-center', 180 }, 181 { 182 key: 'username', 183 label: i18n.global.t('pageSessions.table.username'), 184 class: 'text-center', 185 }, 186 { 187 key: 'ipAddress', 188 label: i18n.global.t('pageSessions.table.ipAddress'), 189 class: 'text-center', 190 }, 191 { 192 key: 'actions', 193 label: '', 194 class: 'text-center', 195 }, 196 ], 197 batchActions: [ 198 { 199 value: 'disconnect', 200 label: i18n.global.t('pageSessions.action.disconnect'), 201 }, 202 ], 203 currentPage: currentPage, 204 itemsPerPageOptions: itemsPerPageOptions, 205 perPage: perPage, 206 selectedRows: selectedRows, 207 searchTotalFilteredRows: 0, 208 tableHeaderCheckboxModel: tableHeaderCheckboxModel, 209 tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate, 210 searchFilter: searchFilter, 211 }; 212 }, 213 computed: { 214 filteredRows() { 215 return this.searchFilter 216 ? this.searchTotalFilteredRows 217 : this.allConnections.length; 218 }, 219 allConnections() { 220 return this.$store.getters['sessions/allConnections'].map((session) => { 221 return { 222 ...session, 223 actions: [ 224 { 225 value: 'disconnect', 226 title: i18n.global.t('pageSessions.action.disconnect'), 227 }, 228 ], 229 }; 230 }); 231 }, 232 }, 233 created() { 234 this.startLoader(); 235 this.$store.dispatch('sessions/getSessionsData').finally(() => { 236 this.endLoader(); 237 this.isBusy = false; 238 }); 239 }, 240 methods: { 241 onFiltered(filteredItems) { 242 this.searchTotalFilteredRows = filteredItems.length; 243 }, 244 onChangeSearchInput(event) { 245 this.searchFilter = event; 246 }, 247 disconnectSessions(uris) { 248 this.$store 249 .dispatch('sessions/disconnectSessions', uris) 250 .then((messages) => { 251 messages.forEach(({ type, message }) => { 252 if (type === 'success') { 253 this.successToast(message); 254 } else if (type === 'error') { 255 this.errorToast(message); 256 } 257 }); 258 }); 259 }, 260 onTableRowAction(action, { uri }) { 261 if (action === 'disconnect') { 262 this.$bvModal 263 .msgBoxConfirm( 264 i18n.global.t('pageSessions.modal.disconnectMessage'), 265 { 266 title: i18n.global.t('pageSessions.modal.disconnectTitle'), 267 okTitle: i18n.global.t('pageSessions.action.disconnect'), 268 cancelTitle: i18n.global.t('global.action.cancel'), 269 autoFocusButton: 'ok', 270 }, 271 ) 272 .then((deleteConfirmed) => { 273 if (deleteConfirmed) this.disconnectSessions([uri]); 274 }); 275 } 276 }, 277 onBatchAction(action) { 278 if (action === 'disconnect') { 279 const uris = this.selectedRows.map((row) => row.uri); 280 this.$bvModal 281 .msgBoxConfirm( 282 i18n.global.t( 283 'pageSessions.modal.disconnectMessage', 284 this.selectedRows.length, 285 ), 286 { 287 title: i18n.global.t( 288 'pageSessions.modal.disconnectTitle', 289 this.selectedRows.length, 290 ), 291 okTitle: i18n.global.t('pageSessions.action.disconnect'), 292 cancelTitle: i18n.global.t('global.action.cancel'), 293 autoFocusButton: 'ok', 294 }, 295 ) 296 .then((deleteConfirmed) => { 297 if (deleteConfirmed) { 298 this.disconnectSessions(uris); 299 } 300 }); 301 } 302 }, 303 }, 304}; 305</script> 306<style lang="scss"> 307#table-session-logs { 308 td .btn-link { 309 width: auto !important; 310 } 311} 312</style> 313