This commit is contained in:
Donald Zou 2025-07-24 15:04:46 +08:00
parent b0bb320fb6
commit 2cf337a606
5 changed files with 146 additions and 96 deletions

View File

@ -3,23 +3,27 @@ import {computed, ref} from "vue";
import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js"; import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js";
import LocaleText from "@/components/text/localeText.vue"; import LocaleText from "@/components/text/localeText.vue";
const props = defineProps(['configuration', 'peers', 'clientAssignedPeers']) const props = defineProps(['configuration', 'peers', 'clientAssignedPeers', 'availablePeerSearchString'])
const emits = defineEmits(['assign', 'unassign']) const emits = defineEmits(['assign', 'unassign'])
const assignmentStore = DashboardClientAssignmentStore() const assignmentStore = DashboardClientAssignmentStore()
const available = computed(() => { const available = computed(() => {
if (props.clientAssignedPeers){ if (props.clientAssignedPeers){
if (Object.keys(props.clientAssignedPeers).includes(props.configuration)){ if (Object.keys(props.clientAssignedPeers).includes(props.configuration)){
return props.peers.filter( return props.peers.filter(
x => !props.clientAssignedPeers[props.configuration].map( x => {
return !props.clientAssignedPeers[props.configuration].map(
x => x.id x => x.id
).includes(x.id) ).includes(x.id) &&
(!props.availablePeerSearchString ||
(props.availablePeerSearchString &&
(x.id.includes(props.availablePeerSearchString) || x.name.includes(props.availablePeerSearchString))))
}
) )
} }
} }
return props.peers return props.peers
}) })
const confirmDelete = ref(false) const confirmDelete = ref(false)
const collapse = ref(false) const collapse = ref(false)
</script> </script>
@ -28,8 +32,8 @@ const collapse = ref(false)
<div <div
@click="collapse = !collapse" @click="collapse = !collapse"
role="button" role="button"
class="card-header rounded-0 sticky-top z-5 bg-body-secondary border-0 shadow border-bottom btn-brand text-white d-flex"> class="card-header rounded-0 sticky-top z-5 bg-body-secondary border-0 border-bottom text-white d-flex">
<samp>{{ configuration }}</samp> <small><samp>{{ configuration }}</samp></small>
<a role="button" class="ms-auto text-white" > <a role="button" class="ms-auto text-white" >
<i class="bi bi-chevron-compact-down" v-if="collapse"></i> <i class="bi bi-chevron-compact-down" v-if="collapse"></i>
<i class="bi bi-chevron-compact-up" v-else></i> <i class="bi bi-chevron-compact-up" v-else></i>

View File

@ -1,52 +1,34 @@
<script setup lang="ts"> <script setup lang="ts" async>
import {onMounted, ref, watch, watchEffect} from "vue"; import {onMounted, ref, watch, watchEffect} from "vue";
import { fetchGet } from "@/utilities/fetch.js" import { fetchGet } from "@/utilities/fetch.js"
import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js"; import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js";
import AvailablePeersGroup from "@/components/clientComponents/availablePeersGroup.vue"; import AvailablePeersGroup from "@/components/clientComponents/availablePeersGroup.vue";
import LocaleText from "@/components/text/localeText.vue"; import LocaleText from "@/components/text/localeText.vue";
import configuration from "@/views/configuration.vue"; const props = defineProps(['client', 'clientAssignedPeers'])
const props = defineProps(['client'])
const clientAssignedPeers = ref({})
const loading = ref(false) const loading = ref(false)
const assignmentStore = DashboardClientAssignmentStore() const assignmentStore = DashboardClientAssignmentStore()
const manage = ref(false) const manage = ref(false)
const emits = defineEmits(['refresh'])
const selectedAssign = ref(undefined)
const selectedUnassign = ref(undefined)
const getAssignedPeers = async () => {
await fetchGet('/api/clients/assignedPeers', {
ClientID: props.client.ClientID
}, (res) => {
clientAssignedPeers.value = res.data;
loading.value = false;
})
}
watchEffect(async () => {
loading.value = true
await getAssignedPeers()
})
const assign = async (ConfigurationName, Peer, ClientID) => { const assign = async (ConfigurationName, Peer, ClientID) => {
await assignmentStore.assignClient(ConfigurationName, Peer, ClientID, false) await assignmentStore.assignClient(ConfigurationName, Peer, ClientID, false)
await getAssignedPeers() emits('refresh')
} }
const unassign = async (AssignmentID) => { const unassign = async (AssignmentID) => {
await assignmentStore.unassignClient(undefined, undefined, AssignmentID) await assignmentStore.unassignClient(undefined, undefined, AssignmentID)
await getAssignedPeers() emits('refresh')
} }
const availablePeerSearchString = ref("")
</script> </script>
<template> <template>
<div class="d-flex rounded-3 flex-column gap-3"> <div>
<div style="height: 400px" class="d-flex flex-column"> <div class="d-flex rounded-0 border-0 flex-column d-flex flex-column border-bottom pb-1" v-if="!loading">
<div class="d-flex align-items-center mb-2"> <div class="d-flex flex-column p-3 gap-3">
<div class="d-flex align-items-center">
<h6 class="mb-0"> <h6 class="mb-0">
<LocaleText t="Assigned Peers"></LocaleText> <LocaleText t="Assigned Peers"></LocaleText>
<span class="text-bg-primary badge ms-2"> <span class="text-bg-primary badge ms-2">
@ -73,14 +55,13 @@ const unassign = async (AssignmentID) => {
@unassign="async (id) => await unassign(id)" @unassign="async (id) => await unassign(id)"
v-for="(peers, configuration) in clientAssignedPeers"> v-for="(peers, configuration) in clientAssignedPeers">
</AvailablePeersGroup> </AvailablePeersGroup>
<h6 class="text-muted m-auto" v-if="Object.keys(clientAssignedPeers).length === 0"> <h6 class="text-muted m-auto p-3" v-if="Object.keys(clientAssignedPeers).length === 0">
<LocaleText t="No peer assigned to this client"></LocaleText> <LocaleText t="No peer assigned to this client"></LocaleText>
</h6> </h6>
</div> </div>
</div> </div>
<div style="height: 500px" class="d-flex flex-column" v-if="manage"> <div style="height: 500px" class="d-flex flex-column p-3" v-if="manage">
<div class="availablePeers border h-100 card rounded-3">
<div class="mt-3 availablePeers border h-100 card rounded-3">
<div class="card-header sticky-top p-3"> <div class="card-header sticky-top p-3">
<h6 class="mb-0 d-flex align-items-center"> <h6 class="mb-0 d-flex align-items-center">
<LocaleText t="Available Peers"></LocaleText> <LocaleText t="Available Peers"></LocaleText>
@ -88,6 +69,7 @@ const unassign = async (AssignmentID) => {
</div> </div>
<div class="card-body p-0 overflow-scroll"> <div class="card-body p-0 overflow-scroll">
<AvailablePeersGroup <AvailablePeersGroup
:availablePeerSearchString="availablePeerSearchString"
:configuration="configuration" :configuration="configuration"
:clientAssignedPeers="clientAssignedPeers" :clientAssignedPeers="clientAssignedPeers"
:peers="peers" :peers="peers"
@ -104,12 +86,18 @@ const unassign = async (AssignmentID) => {
</label> </label>
<input <input
id="availablePeerSearchString" id="availablePeerSearchString"
v-model="availablePeerSearchString"
class="form-control form-control-sm rounded-3 w-auto" type="text"> class="form-control form-control-sm rounded-3 w-auto" type="text">
</div> </div>
</div> </div>
</div> </div>
</div>
<div v-else>
<div class="p-3 placeholder-glow border-bottom">
<h6 class="placeholder w-100 rounded-3"></h6>
<div class="placeholder w-100 rounded-3" style="height: 400px"></div>
</div>
</div>
</div> </div>
</template> </template>

View File

@ -27,14 +27,12 @@ const getClients = computed(() => {
</span> </span>
</div> </div>
<div class="card-body p-0"> <div class="card-body p-0">
<div class="list-group list-group-flush" > <div class="list-group list-group-flush" >
<RouterLink <RouterLink
:to="{ name: 'Client Viewer', params: { id: client.ClientID } }" :to="{ name: 'Client Viewer', params: { id: client.ClientID } }"
class="list-group-item d-flex flex-column border-bottom list-group-item-action" class="list-group-item d-flex flex-column border-bottom list-group-item-action"
v-for="client in getClients" > v-for="client in getClients" >
<small class="text-body"> <small class="text-body">
{{ client.Email }} {{ client.Email }}
</small> </small>
<small class="text-muted"> <small class="text-muted">

View File

@ -0,0 +1,42 @@
<script setup lang="ts">
import LocaleText from "@/components/text/localeText.vue";
</script>
<template>
<div class="p-3">
<h6>
<LocaleText t="Reset Password"></LocaleText>
</h6>
<div class="row g-2">
<div class="col-sm-4">
<label class="mb-1">
<small class="fw-bold text-muted">
<LocaleText t="Current Password"></LocaleText>
</small>
</label>
<input type="password" class="form-control form-control-sm rounded-3">
</div>
<div class="col-sm-4">
<label class="mb-1">
<small class="fw-bold text-muted">
<LocaleText t="New Password"></LocaleText>
</small>
</label>
<input type="password" class="form-control form-control-sm rounded-3">
</div>
<div class="col-sm-4">
<label class="mb-1">
<small class="fw-bold text-muted">
<LocaleText t="Confirm New Password"></LocaleText>
</small>
</label>
<input type="password" class="form-control form-control-sm rounded-3">
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -4,19 +4,36 @@ import { fetchGet } from "@/utilities/fetch.js"
import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js"; import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js";
import {computed} from "vue"; import {computed, ref, watch} from "vue";
import LocaleText from "@/components/text/localeText.vue"; import LocaleText from "@/components/text/localeText.vue";
import ClientAssignedPeers from "@/components/clientComponents/clientAssignedPeers.vue"; import ClientAssignedPeers from "@/components/clientComponents/clientAssignedPeers.vue";
import ClientResetPassword from "@/components/clientComponents/clientResetPassword.vue";
const assignmentStore = DashboardClientAssignmentStore() const assignmentStore = DashboardClientAssignmentStore()
const route = useRoute() const route = useRoute()
const client = computed(() => { const client = computed(() => {
return assignmentStore.getClientById(route.params.id) return assignmentStore.getClientById(route.params.id)
}) })
const clientAssignedPeers = ref({})
const getAssignedPeers = async () => {
await fetchGet('/api/clients/assignedPeers', {
ClientID: client.value.ClientID
}, (res) => {
clientAssignedPeers.value = res.data;
})
}
if (client.value){
watch(() => client.value.ClientID, async () => {
await getAssignedPeers()
})
await getAssignedPeers()
}
</script> </script>
<template> <template>
<div class="text-body d-flex flex-column gap-3" v-if="client"> <div class="text-body d-flex flex-column" v-if="client">
<div class="p-4 border-bottom bg-body-tertiary"> <div class="p-4 border-bottom bg-body-tertiary">
<small class="text-muted"> <small class="text-muted">
<LocaleText t="Email"></LocaleText> <LocaleText t="Email"></LocaleText>
@ -33,10 +50,11 @@ const client = computed(() => {
</small> </small>
</div> </div>
</div> </div>
<div class="px-4"> <ClientAssignedPeers
@refresh="getAssignedPeers()"
<ClientAssignedPeers :client="client"></ClientAssignedPeers> :clientAssignedPeers="clientAssignedPeers"
</div> :client="client"></ClientAssignedPeers>
<ClientResetPassword :client="client" v-if="client.ClientGroup === 'Local'"></ClientResetPassword>
</div> </div>
<div v-else class="d-flex w-100 h-100 text-muted"> <div v-else class="d-flex w-100 h-100 text-muted">
<div class="m-auto text-center"> <div class="m-auto text-center">