mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-07-24 14:07:02 +00:00
Finished client assigning
This commit is contained in:
parent
68e757aafc
commit
a9d74e834d
2915
src/static/app/package-lock.json
generated
2915
src/static/app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,8 @@
|
||||
<script setup async>
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
import {onMounted, ref} from "vue";
|
||||
import {GetLocale} from "@/utilities/locale.js";
|
||||
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||
import SearchClients from "@/components/configurationComponents/peerAssignModalComponents/searchClients.vue";
|
||||
import AssignedClients from "@/components/configurationComponents/peerAssignModalComponents/assignedClients.vue";
|
||||
import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js";
|
||||
|
||||
const props = defineProps({
|
||||
selectedPeer: Object
|
||||
@ -12,44 +10,17 @@ const props = defineProps({
|
||||
const emits = defineEmits([
|
||||
'close'
|
||||
])
|
||||
const assignments = ref([])
|
||||
const clients = ref([])
|
||||
await fetchGet('/api/clients/allClients', {},(res) => {
|
||||
clients.value = res.data;
|
||||
console.log(clients.value)
|
||||
})
|
||||
const assignmentStore = DashboardClientAssignmentStore()
|
||||
|
||||
const getAssignedClients = async () => {
|
||||
await fetchGet('/api/clients/assignedClients', {
|
||||
ConfigurationName: props.selectedPeer.configuration.Name,
|
||||
Peer: props.selectedPeer.id
|
||||
}, (res) => {
|
||||
assignments.value = res.data
|
||||
})
|
||||
if (assignmentStore.clients.length > 0){
|
||||
assignmentStore.getClients()
|
||||
}else{
|
||||
await assignmentStore.getClients()
|
||||
}
|
||||
|
||||
await getAssignedClients()
|
||||
|
||||
await assignmentStore.getAssignedClients(props.selectedPeer.configuration.Name, props.selectedPeer.id)
|
||||
const assignClient = async (clientID) => {
|
||||
await fetchPost('/api/clients/assignClient', {
|
||||
ConfigurationName: props.selectedPeer.configuration.Name,
|
||||
Peer: props.selectedPeer.id,
|
||||
ClientID: clientID
|
||||
}, async (res) => {
|
||||
if (res.status){
|
||||
await getAssignedClients()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const unassignClient = async (assignmentID) => {
|
||||
await fetchPost('/api/clients/unassignClient', {
|
||||
AssignmentID: assignmentID
|
||||
}, async (res) => {
|
||||
if (res.status){
|
||||
await getAssignedClients()
|
||||
}
|
||||
})
|
||||
await assignmentStore.assignClient(props.selectedPeer.configuration.Name, props.selectedPeer.id, clientID)
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -65,13 +36,12 @@ const unassignClient = async (assignmentID) => {
|
||||
<button type="button" class="btn-close ms-auto" @click="emits('close')"></button>
|
||||
</div>
|
||||
<div class="card-body px-4 pb-4 d-flex gap-2 flex-column">
|
||||
<AssignedClients
|
||||
@unassign="args => unassignClient(args)"
|
||||
:assignments="assignments"></AssignedClients>
|
||||
<AssignedClients
|
||||
:configuration-name="props.selectedPeer.configuration.Name"
|
||||
:peer="props.selectedPeer.id"
|
||||
></AssignedClients>
|
||||
<SearchClients
|
||||
:assignments="assignments"
|
||||
@assign="args => assignClient(args)"
|
||||
:clients="clients"></SearchClients>
|
||||
@assign="args => assignClient(args)"></SearchClients>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,11 @@
|
||||
<script setup>
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
const props = defineProps(['assignments'])
|
||||
import {ref} from "vue";
|
||||
import Assignment from "@/components/configurationComponents/peerAssignModalComponents/assignment.vue";
|
||||
import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js";
|
||||
const emits = defineEmits(['unassign'])
|
||||
const props = defineProps(['configurationName', 'peer'])
|
||||
const assignmentStore = DashboardClientAssignmentStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -10,26 +14,11 @@ const emits = defineEmits(['unassign'])
|
||||
<LocaleText t="Assigned Clients"></LocaleText>
|
||||
</h6>
|
||||
<TransitionGroup name="list" tag="div" class="position-relative">
|
||||
<div class="bg-body-secondary rounded-3 text-start p-2 d-flex mb-2 assignment"
|
||||
:key="a.AssignmentID"
|
||||
v-for="a in assignments">
|
||||
<div class="d-flex flex-column">
|
||||
<small>
|
||||
{{ a.Client.Email }}
|
||||
</small>
|
||||
<small class="text-muted">
|
||||
{{ a.Client.Name ? a.Client.Name + ' | ' : '' }}{{ a.Client.ClientGroup ? a.Client.ClientGroup : 'Local' }}
|
||||
</small>
|
||||
</div>
|
||||
<button
|
||||
@click="emits('unassign', a.AssignmentID)"
|
||||
aria-label="Delete Assignment"
|
||||
class="btn bg-danger-subtle text-danger-emphasis ms-auto">
|
||||
<i class="bi bi-trash-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
<Assignment :assignment="a" :key="a.AssignmentID"
|
||||
@unassign="assignmentStore.unassignClient(configurationName, peer, a.AssignmentID)"
|
||||
v-for="a in assignmentStore.assignments"></Assignment>
|
||||
</TransitionGroup>
|
||||
<div class="text-center" v-if="assignments.length === 0">
|
||||
<div class="text-center" v-if="assignmentStore.assignments.length === 0">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="No client assigned to this peer yet"></LocaleText>
|
||||
</small>
|
||||
|
@ -0,0 +1,64 @@
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js";
|
||||
const props = defineProps(['assignment'])
|
||||
const emits = defineEmits(['unassign'])
|
||||
const confirmDelete = ref(false)
|
||||
const assignmentStore = DashboardClientAssignmentStore()
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-body-secondary rounded-3 text-start p-2 mb-2 assignment">
|
||||
<div class="d-flex" v-if="!confirmDelete">
|
||||
<div class="d-flex flex-column">
|
||||
<small>
|
||||
{{ assignment.Client.Email }}
|
||||
</small>
|
||||
<small class="text-muted">
|
||||
{{ assignment.Client.Name ? assignment.Client.Name + ' | ' : '' }}{{ assignment.Client.ClientGroup ? assignment.Client.ClientGroup : 'Local' }}
|
||||
</small>
|
||||
</div>
|
||||
<button
|
||||
v-if="!confirmDelete"
|
||||
@click="confirmDelete = !confirmDelete"
|
||||
:class="{disabled: assignmentStore.unassigning}"
|
||||
aria-label="Delete Assignment"
|
||||
class="btn bg-danger-subtle text-danger-emphasis ms-auto">
|
||||
<i class="bi bi-trash-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="d-flex gap-2" v-else>
|
||||
<div class="d-flex flex-column">
|
||||
<small>
|
||||
<LocaleText t="Are you sure to delete assignment for"></LocaleText>
|
||||
</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText :t="assignment.Client.Email + ' in group ' + (assignment.Client.ClientGroup ? assignment.Client.ClientGroup : 'Local') + '?'"></LocaleText>
|
||||
</small>
|
||||
</div>
|
||||
<button
|
||||
@click="emits('unassign')"
|
||||
aria-label="Delete Assignment"
|
||||
:class="{disabled: assignmentStore.unassigning}"
|
||||
class="btn bg-danger-subtle text-danger-emphasis ms-auto">
|
||||
<span class="spinner-border spinner-border-sm" v-if="assignmentStore.unassigning"></span>
|
||||
<i class="bi bi-check-lg" v-else></i>
|
||||
</button>
|
||||
<button
|
||||
:class="{disabled: assignmentStore.unassigning}"
|
||||
@click="confirmDelete = !confirmDelete"
|
||||
aria-label="Cancel Delete Assignment"
|
||||
class="btn bg-secondary-subtle text-secondary-emphasis ">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -4,22 +4,25 @@ import LocaleText from "@/components/text/localeText.vue";
|
||||
import {computed, reactive, ref} from "vue";
|
||||
import SearchClientsGroup from "@/components/configurationComponents/peerAssignModalComponents/searchClientsGroup.vue";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js";
|
||||
|
||||
const props = defineProps(['clients', 'newAssignClients', 'assignments'])
|
||||
|
||||
const assignmentStore = DashboardClientAssignmentStore()
|
||||
|
||||
const selectedGroup = ref("")
|
||||
const searchString = ref("")
|
||||
const getSelectedGroup = computed(() => {
|
||||
if (selectedGroup.value){
|
||||
return {
|
||||
[selectedGroup.value] : props.clients[selectedGroup.value]
|
||||
[selectedGroup.value] : assignmentStore.clients[selectedGroup.value]
|
||||
}
|
||||
}
|
||||
return props.clients
|
||||
return assignmentStore.clients
|
||||
})
|
||||
const groupCount = reactive({})
|
||||
Object.keys(props.clients).forEach(
|
||||
x => groupCount[x] = props.clients[x].length
|
||||
Object.keys(assignmentStore.clients).forEach(
|
||||
x => groupCount[x] = assignmentStore.clients[x].length
|
||||
)
|
||||
|
||||
const emits = defineEmits(['assign'])
|
||||
@ -52,7 +55,7 @@ const emits = defineEmits(['assign'])
|
||||
@click="selectedGroup = groupName"
|
||||
:class="{'active': selectedGroup === groupName}"
|
||||
class="btn bg-primary-subtle text-primary-emphasis btn-sm me-2 rounded-3"
|
||||
v-for="(_, groupName) in clients">
|
||||
v-for="(_, groupName) in assignmentStore.clients">
|
||||
{{ groupName }}
|
||||
<span class="ms-1 badge" :class="[ groupCount[groupName] > 0 ? 'bg-primary' : 'bg-secondary' ]">
|
||||
{{ groupCount[groupName] }}
|
||||
@ -61,12 +64,12 @@ const emits = defineEmits(['assign'])
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-3 border rounded-3 d-flex flex-column gap-2 overflow-y-scroll" style="height: 400px">
|
||||
<SearchClientsGroup
|
||||
:assignments="assignments"
|
||||
<SearchClientsGroup
|
||||
@assign="(args) => emits('assign', args)"
|
||||
@count="(args) => groupCount[groupName] = args"
|
||||
:searchString="searchString"
|
||||
:group="group" :groupName="groupName" v-for="(group, groupName) in getSelectedGroup"></SearchClientsGroup>
|
||||
:group="group" :groupName="groupName"
|
||||
v-for="(group, groupName) in getSelectedGroup"></SearchClientsGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,14 +1,15 @@
|
||||
<script setup>
|
||||
import {computed} from "vue";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js";
|
||||
|
||||
const props = defineProps(['group', 'groupName', 'searchString', 'assignments'])
|
||||
const props = defineProps(['group', 'groupName', 'searchString'])
|
||||
const emits = defineEmits(['count', 'assign'])
|
||||
const assignmentStore = DashboardClientAssignmentStore()
|
||||
|
||||
const filterGroup = computed(() => {
|
||||
let g = props.group.filter(x =>
|
||||
!props.assignments.map(a => a.Client.ClientID).includes(x.ClientID))
|
||||
|
||||
!assignmentStore.assignments.map(a => a.Client.ClientID).includes(x.ClientID))
|
||||
if (props.searchString){
|
||||
let v = g.filter(
|
||||
x => (x.Name && x.Name.includes(props.searchString)) || (x.Email && x.Email.includes(props.searchString))
|
||||
@ -27,13 +28,22 @@ const filterGroup = computed(() => {
|
||||
<small>{{groupName}}</small>
|
||||
</h6>
|
||||
<div v-if="filterGroup.length > 0" class="d-flex flex-column gap-2">
|
||||
<div class="bg-body-secondary rounded-3 text-start p-2 d-flex p-1" role="button"
|
||||
@click="emits('assign', client.ClientID)"
|
||||
<div class="bg-body-secondary rounded-3 text-start p-2 d-flex"
|
||||
|
||||
v-for="client in filterGroup">
|
||||
<small class="mb-0">
|
||||
{{ client.Email }}
|
||||
</small>
|
||||
<small class="text-muted ms-auto">{{ client.Name }}</small>
|
||||
<div class="d-flex flex-column">
|
||||
<small class="mb-0">
|
||||
{{ client.Email }}
|
||||
</small>
|
||||
<small class="text-muted">{{ client.Name ? client.Name : 'No Name' }}</small>
|
||||
</div>
|
||||
<button
|
||||
@click="emits('assign', client.ClientID)"
|
||||
:class="{disabled: assignmentStore.assigning}"
|
||||
class="btn bg-success-subtle text-success-emphasis ms-auto">
|
||||
<span class="spinner-border spinner-border-sm" v-if="assignmentStore.assigning === client.ClientID"></span>
|
||||
<i class="bi bi-plus-circle-fill" v-else></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
|
70
src/static/app/src/stores/DashboardClientAssignmentStore.js
Normal file
70
src/static/app/src/stores/DashboardClientAssignmentStore.js
Normal file
@ -0,0 +1,70 @@
|
||||
import {defineStore} from "pinia";
|
||||
import {ref} from "vue";
|
||||
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||
|
||||
export const DashboardClientAssignmentStore =
|
||||
defineStore('DashboardClientAssignmentStore', () => {
|
||||
const assignments = ref([])
|
||||
const clients = ref({})
|
||||
const unassigning = ref(false)
|
||||
const assigning = ref("")
|
||||
|
||||
const getClients = async () => {
|
||||
await fetchGet('/api/clients/allClients', {},(res) => {
|
||||
clients.value = res.data;
|
||||
})
|
||||
}
|
||||
|
||||
const getClientById = (ClientID) => {
|
||||
return Object.values(clients.value).flat().find(x => x.ClientID === ClientID)
|
||||
}
|
||||
|
||||
const getAssignedClients = async (ConfigurationName, Peer) => {
|
||||
await fetchGet('/api/clients/assignedClients', {
|
||||
ConfigurationName: ConfigurationName,
|
||||
Peer: Peer
|
||||
}, (res) => {
|
||||
assignments.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
const assignClient = async (ConfigurationName, Peer, ClientID) => {
|
||||
assigning.value = ClientID;
|
||||
await fetchPost('/api/clients/assignClient', {
|
||||
ConfigurationName: ConfigurationName,
|
||||
Peer: Peer,
|
||||
ClientID: ClientID
|
||||
}, async (res) => {
|
||||
if (res.status){
|
||||
await getAssignedClients(ConfigurationName, Peer)
|
||||
assigning.value = "";
|
||||
}else{
|
||||
assigning.value = "";
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const unassignClient = async (ConfigurationName, Peer, AssignmentID) => {
|
||||
unassigning.value = true;
|
||||
await fetchPost('/api/clients/unassignClient', {
|
||||
AssignmentID: AssignmentID
|
||||
}, async (res) => {
|
||||
if (res.status){
|
||||
await getAssignedClients(ConfigurationName, Peer)
|
||||
}
|
||||
unassigning.value = false;
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
assignments,
|
||||
getAssignedClients,
|
||||
getClients,
|
||||
clients,
|
||||
unassignClient,
|
||||
assignClient,
|
||||
getClientById,
|
||||
unassigning,
|
||||
assigning
|
||||
}
|
||||
})
|
@ -48,12 +48,12 @@ window.dayjs = dayjs
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card rounded-3 border shadow">
|
||||
<div class="card-header border-0 align-items-center d-flex p-3 flex-column flex-sm-row gap-2">
|
||||
<div class="card rounded-3 border-0 shadow">
|
||||
<div class="card-header rounded-top-3 border-0 align-items-center d-flex p-3 flex-column flex-sm-row gap-2">
|
||||
<small class="fw-bold">
|
||||
{{ props.config.name }}
|
||||
</small>
|
||||
<span class="badge rounded-3 shadow ms-sm-auto"
|
||||
<span class="badge rounded-3 ms-sm-auto"
|
||||
:class="[props.config.protocol === 'wg' ? 'wireguardBg' : 'amneziawgBg' ]"
|
||||
v-if="props.config.protocol === 'wg'">
|
||||
{{ props.config.protocol === 'wg' ? 'WireGuard': 'AmneziaWG' }}
|
||||
|
@ -72,8 +72,10 @@ const signOut = async () => {
|
||||
<small>No configuration available</small>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="d-flex py-4">
|
||||
<div class="spinner-border m-auto"></div>
|
||||
<div v-else class="d-flex p-3">
|
||||
<div class="bg-body rounded-3 d-flex" style="width: 100%; height: 211px;">
|
||||
<div class="spinner-border m-auto"></div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user