2023-08-04 13:34:18 +02:00
< script setup >
import PeerViewModal from "../components/PeerViewModal.vue" ;
2024-02-29 07:17:17 +03:00
import { onMounted , ref } from "vue" ;
import { profileStore } from "@/stores/profile" ;
2025-01-26 11:35:24 +01:00
import UserPeerEditModal from "@/components/UserPeerEditModal.vue" ;
2024-02-29 07:17:17 +03:00
import { settingsStore } from "@/stores/settings" ;
2024-09-23 21:44:43 +02:00
import { humanFileSize } from "@/helpers/utils" ;
2023-08-04 13:34:18 +02:00
const settings = settingsStore ( )
const profile = profileStore ( )
const viewedPeerId = ref ( "" )
const editPeerId = ref ( "" )
2025-02-26 22:24:37 +01:00
const sortKey = ref ( "" )
const sortOrder = ref ( 1 )
const selectAll = ref ( false )
2024-09-23 21:44:43 +02:00
function sortBy ( key ) {
if ( sortKey . value === key ) {
sortOrder . value = sortOrder . value * - 1 ; // Toggle sort order
} else {
sortKey . value = key ;
sortOrder . value = 1 ; // Default to ascending
}
profile . sortKey = sortKey . value ;
profile . sortOrder = sortOrder . value ;
}
2025-01-26 11:35:24 +01:00
function friendlyInterfaceName ( id , name ) {
if ( name ) {
return name
}
return id
}
2025-02-26 22:24:37 +01:00
function toggleSelectAll ( ) {
profile . FilteredAndPagedPeers . forEach ( peer => {
peer . IsSelected = selectAll . value ;
} ) ;
}
2023-08-04 13:34:18 +02:00
onMounted ( async ( ) => {
await profile . LoadUser ( )
await profile . LoadPeers ( )
await profile . LoadStats ( )
2025-01-26 11:35:24 +01:00
await profile . LoadInterfaces ( )
2024-09-23 21:44:43 +02:00
await profile . calculatePages ( ) ; // Forces to show initial page number
2023-08-04 13:34:18 +02:00
} )
2024-02-29 07:17:17 +03:00
2023-08-04 13:34:18 +02:00
< / script >
< template >
2024-02-29 07:17:17 +03:00
< PeerViewModal :peerId = "viewedPeerId" : visible = "viewedPeerId !== ''" @ close = "viewedPeerId = ''" > < / PeerViewModal >
2025-01-26 11:35:24 +01:00
< UserPeerEditModal :peerId = "editPeerId" : visible = "editPeerId !== ''" @ close = "editPeerId = ''; profile.LoadPeers()" > < / UserPeerEditModal >
2023-08-04 13:34:18 +02:00
<!-- Peer list -- >
< div class = "mt-4 row" >
< div class = "col-12 col-lg-5" >
< h2 class = "mt-2" > { { $t ( 'profile.headline' ) } } < / h2 >
< / div >
< div class = "col-12 col-lg-4 text-lg-end" >
< div class = "form-group d-inline" >
< div class = "input-group mb-3" >
2024-02-29 07:17:17 +03:00
< input v-model = "profile.filter" class="form-control" :placeholder="$t('general.search.placeholder')" type="text"
@ keyup = "profile.afterPageSizeChange" >
< button class = "input-group-text btn btn-primary" :title = "$t('general.search.button')" > < i
class = "fa-solid fa-search" > < / i > < / button >
2023-08-04 13:34:18 +02:00
< / div >
< / div >
< / div >
< div class = "col-12 col-lg-3 text-lg-end" >
2025-01-26 11:35:24 +01:00
< div class = "form-group" v-if = "settings.Setting('SelfProvisioning')" >
< div class = "input-group mb-3" >
< button class = "input-group-text btn btn-primary" :title = "$t('interfaces.button-add-peer')" @ click.prevent = " editPeerId = '#NEW#' " >
< i class = "fa fa-plus me-1" > < / i > < i class = "fa fa-user" > < / i >
< / button >
< select v-model = "profile.selectedInterfaceId" :disabled="profile.CountInterfaces===0" class="form-select" >
< option v-if = "profile.CountInterfaces===0" value="nothing" > {{ $ t ( ' interfaces.no -interface .default -selection ' ) }} < / option >
< option v-for = "iface in profile.interfaces" :key="iface.Identifier" :value="iface.Identifier" > {{ friendlyInterfaceName ( iface.Identifier , iface.DisplayName ) }} < / option >
< / select >
< / div >
< / div >
2023-08-04 13:34:18 +02:00
< / div >
< / div >
< div class = "mt-2 table-responsive" >
2024-02-29 07:17:17 +03:00
< div v-if = "profile.CountPeers === 0" >
< h4 > { { $t ( 'profile.no-peer.headline' ) } } < / h4 >
< p > { { $t ( 'profile.no-peer.abstract' ) } } < / p >
2023-08-04 13:34:18 +02:00
< / div >
2024-02-29 07:17:17 +03:00
< table v-if = "profile.CountPeers !== 0" id="peerTable" class="table table-sm" >
2023-08-04 13:34:18 +02:00
< thead >
2024-02-29 07:17:17 +03:00
< tr >
< th scope = "col" >
2025-02-26 22:24:37 +01:00
< input class = "form-check-input" :title = "$t('general.select-all')" type = "checkbox" v-model = "selectAll" @change="toggleSelectAll" >
2024-02-29 07:17:17 +03:00
< / th > <!-- select -- >
< th scope = "col" > < / th > <!-- status -- >
2024-09-23 21:44:43 +02:00
< th scope = "col" @click ="sortBy('DisplayName')" >
{ { $t ( "profile.table-heading.name" ) } }
< i v-if = "sortKey === 'DisplayName'" :class="sortOrder === 1 ? 'asc' : 'desc'" > < / i >
< / th >
< th scope = "col" @click ="sortBy('Addresses')" >
{ { $t ( "profile.table-heading.ip" ) } }
< i v-if = "sortKey === 'Addresses'" :class="sortOrder === 1 ? 'asc' : 'desc'" > < / i >
< / th >
< th v-if = "profile.hasStatistics" scope="col" @click="sortBy('IsConnected')" >
{ { $t ( "profile.table-heading.stats" ) } }
< i v-if = "sortKey === 'IsConnected'" :class="sortOrder === 1 ? 'asc' : 'desc'" > < / i >
< / th >
< th v-if = "profile.hasStatistics" scope="col" @click="sortBy('Traffic')" > RX / TX
< i v-if = "sortKey === 'Traffic'" :class="sortOrder === 1 ? 'asc' : 'desc'" > < / i >
< / th >
2024-02-29 07:17:17 +03:00
< th scope = "col" > { { $t ( 'profile.table-heading.interface' ) } } < / th >
< th scope = "col" > < / th > <!-- Actions -- >
< / tr >
2023-08-04 13:34:18 +02:00
< / thead >
< tbody >
< tr v-for = "peer in profile.FilteredAndPagedPeers" :key="peer.Identifier" >
< th scope = "row" >
2025-02-26 22:24:37 +01:00
< input class = "form-check-input" type = "checkbox" v-model = "peer.IsSelected" >
2023-08-04 13:34:18 +02:00
< / th >
< td class = "text-center" >
2024-02-29 07:17:17 +03:00
< span v-if = "peer.Disabled" class="text-danger"><i class="fa fa-circle-xmark"
: title = "peer.DisabledReason" > < / i > < / span >
< span v-if = "!peer.Disabled && peer.ExpiresAt" class="text-warning"><i class="fas fa-hourglass-end"
: title = "peer.ExpiresAt" > < / i > < / span >
2023-08-04 13:34:18 +02:00
< / td >
2024-02-29 07:17:17 +03:00
< td > < span v-if = "peer.DisplayName" :title="peer.Identifier" > {{ peer.DisplayName }} < / span > < span v -else
: title = "peer.Identifier" > { { $filters . truncate ( peer . Identifier , 10 ) } } < / span > < / td >
2023-08-04 13:34:18 +02:00
< td >
< span v-for = "ip in peer.Addresses" :key="ip" class="badge rounded-pill bg-light" > {{ ip }} < / span >
< / td >
< td v-if = "profile.hasStatistics" >
< div v-if = "profile.Statistics(peer.Identifier).IsConnected" >
2024-02-29 07:17:17 +03:00
< span class = "badge rounded-pill bg-success" > < i class = "fa-solid fa-link" > < / i > < / span >
< span :title = "profile.Statistics(peer.Identifier).LastHandshake" > { { $t ( 'profile.peer-connected' ) } } < / span >
2023-08-04 13:34:18 +02:00
< / div >
< div v-else >
< span class = "badge rounded-pill bg-light" > < i class = "fa-solid fa-link-slash" > < / i > < / span >
< / div >
< / td >
2024-09-23 21:44:43 +02:00
< td v-if = "profile.hasStatistics" >
< span class = "text-center" > { { humanFileSize ( profile . Statistics ( peer . Identifier ) . BytesReceived ) } } / { { humanFileSize ( profile . Statistics ( peer . Identifier ) . BytesTransmitted ) } } < / span >
< / td >
2024-02-29 07:17:17 +03:00
< td > { { peer . InterfaceIdentifier } } < / td >
2023-08-04 13:34:18 +02:00
< td class = "text-center" >
2024-02-29 07:17:17 +03:00
< a href = "#" :title = "$t('profile.button-show-peer')" @ click.prevent = " viewedPeerId = peer.Identifier " > < i
class = "fas fa-eye me-2" > < / i > < / a >
< a href = "#" :title = "$t('profile.button-edit-peer')" @ click.prevent = " editPeerId = peer.Identifier " > < i
class = "fas fa-cog" > < / i > < / a >
2023-08-04 13:34:18 +02:00
< / td >
< / tr >
< / tbody >
< / table >
< / div >
< hr >
< div class = "mt-3" >
< div class = "row" >
< div class = "col-6" >
< ul class = "pagination pagination-sm" >
2024-02-29 07:17:17 +03:00
< li : class = "{ disabled: profile.pageOffset === 0 }" class = "page-item" >
2023-08-04 13:34:18 +02:00
< a class = "page-link" @click ="profile.previousPage" > & laquo ; < / a >
< / li >
2024-02-29 07:17:17 +03:00
< li v-for = "page in profile.pages" :key="page" :class="{ active: profile.currentPage === page }" class="page-item" >
< a class = "page-link" @click ="profile.gotoPage(page)" > {{ page }} < / a >
2023-08-04 13:34:18 +02:00
< / li >
2024-02-29 07:17:17 +03:00
< li : class = "{ disabled: !profile.hasNextPage }" class = "page-item" >
2023-08-04 13:34:18 +02:00
< a class = "page-link" @click ="profile.nextPage" > & raquo ; < / a >
< / li >
< / ul >
< / div >
< div class = "col-6" >
< div class = "form-group row" >
2024-02-29 07:17:17 +03:00
< label class = "col-sm-6 col-form-label text-end" for = "paginationSelector" >
{ { $t ( 'general.pagination.size' ) } } :
< / label >
2023-08-04 13:34:18 +02:00
< div class = "col-sm-6" >
< select v -model .number = " profile.pageSize " class = "form-select" @click ="profile.afterPageSizeChange()" >
< option value = "10" > 10 < / option >
< option value = "25" > 25 < / option >
< option value = "50" > 50 < / option >
2024-02-29 07:17:17 +03:00
< option value = "100" > 100 < / option >
< option value = "999999999" > { { $t ( 'general.pagination.all' ) } } < / option >
< / select >
2023-08-04 13:34:18 +02:00
< / div >
< / div >
< / div >
< / div >
2024-02-29 07:17:17 +03:00
< / div > < / template >