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 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 assignmentStore = DashboardClientAssignmentStore()
const available = computed(() => {
if (props.clientAssignedPeers){
if (Object.keys(props.clientAssignedPeers).includes(props.configuration)){
return props.peers.filter(
x => !props.clientAssignedPeers[props.configuration].map(
x => x.id
).includes(x.id)
x => {
return !props.clientAssignedPeers[props.configuration].map(
x => x.id
).includes(x.id) &&
(!props.availablePeerSearchString ||
(props.availablePeerSearchString &&
(x.id.includes(props.availablePeerSearchString) || x.name.includes(props.availablePeerSearchString))))
}
)
}
}
return props.peers
})
const confirmDelete = ref(false)
const collapse = ref(false)
</script>
@ -28,8 +32,8 @@ const collapse = ref(false)
<div
@click="collapse = !collapse"
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">
<samp>{{ configuration }}</samp>
class="card-header rounded-0 sticky-top z-5 bg-body-secondary border-0 border-bottom text-white d-flex">
<small><samp>{{ configuration }}</samp></small>
<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-up" v-else></i>

View File

@ -1,115 +1,103 @@
<script setup lang="ts">
<script setup lang="ts" async>
import {onMounted, ref, watch, watchEffect} from "vue";
import { fetchGet } from "@/utilities/fetch.js"
import {DashboardClientAssignmentStore} from "@/stores/DashboardClientAssignmentStore.js";
import AvailablePeersGroup from "@/components/clientComponents/availablePeersGroup.vue";
import LocaleText from "@/components/text/localeText.vue";
import configuration from "@/views/configuration.vue";
const props = defineProps(['client'])
const clientAssignedPeers = ref({})
const props = defineProps(['client', 'clientAssignedPeers'])
const loading = ref(false)
const assignmentStore = DashboardClientAssignmentStore()
const manage = ref(false)
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 emits = defineEmits(['refresh'])
const assign = async (ConfigurationName, Peer, ClientID) => {
await assignmentStore.assignClient(ConfigurationName, Peer, ClientID, false)
await getAssignedPeers()
emits('refresh')
}
const unassign = async (AssignmentID) => {
await assignmentStore.unassignClient(undefined, undefined, AssignmentID)
await getAssignedPeers()
emits('refresh')
}
const availablePeerSearchString = ref("")
</script>
<template>
<div class="d-flex rounded-3 flex-column gap-3">
<div style="height: 400px" class="d-flex flex-column">
<div class="d-flex align-items-center mb-2">
<h6 class="mb-0">
<LocaleText t="Assigned Peers"></LocaleText>
<span class="text-bg-primary badge ms-2">
{{ Object.keys(clientAssignedPeers).length }} <LocaleText :t="Object.keys(clientAssignedPeers).length > 1 ? 'Configurations' : 'Configuration'"></LocaleText>
</span>
<span class="text-bg-info badge ms-2">
{{ Object.values(clientAssignedPeers).flat().length }} <LocaleText :t="Object.values(clientAssignedPeers).flat().length > 1 ? 'Peers' : 'Peer'"></LocaleText>
</span>
</h6>
<button class="btn btn-sm bg-primary-subtle text-primary-emphasis rounded-3 ms-auto"
@click="manage = !manage">
<template v-if="!manage">
<i class="bi bi-list-check me-2"></i>Manage
</template>
<template v-else>
<i class="bi bi-check me-2"></i>Done
</template>
</button>
</div>
<div class="rounded-3 availablePeers border h-100 overflow-scroll flex-grow-1 d-flex flex-column">
<AvailablePeersGroup
:configuration="configuration"
:peers="peers"
@unassign="async (id) => await unassign(id)"
v-for="(peers, configuration) in clientAssignedPeers">
</AvailablePeersGroup>
<h6 class="text-muted m-auto" v-if="Object.keys(clientAssignedPeers).length === 0">
<LocaleText t="No peer assigned to this client"></LocaleText>
</h6>
</div>
</div>
<div style="height: 500px" class="d-flex flex-column" v-if="manage">
<div class="mt-3 availablePeers border h-100 card rounded-3">
<div class="card-header sticky-top p-3">
<h6 class="mb-0 d-flex align-items-center">
<LocaleText t="Available Peers"></LocaleText>
<div>
<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 flex-column p-3 gap-3">
<div class="d-flex align-items-center">
<h6 class="mb-0">
<LocaleText t="Assigned Peers"></LocaleText>
<span class="text-bg-primary badge ms-2">
{{ Object.keys(clientAssignedPeers).length }} <LocaleText :t="Object.keys(clientAssignedPeers).length > 1 ? 'Configurations' : 'Configuration'"></LocaleText>
</span>
<span class="text-bg-info badge ms-2">
{{ Object.values(clientAssignedPeers).flat().length }} <LocaleText :t="Object.values(clientAssignedPeers).flat().length > 1 ? 'Peers' : 'Peer'"></LocaleText>
</span>
</h6>
<button class="btn btn-sm bg-primary-subtle text-primary-emphasis rounded-3 ms-auto"
@click="manage = !manage">
<template v-if="!manage">
<i class="bi bi-list-check me-2"></i>Manage
</template>
<template v-else>
<i class="bi bi-check me-2"></i>Done
</template>
</button>
</div>
<div class="card-body p-0 overflow-scroll">
<div class="rounded-3 availablePeers border h-100 overflow-scroll flex-grow-1 d-flex flex-column">
<AvailablePeersGroup
:configuration="configuration"
:clientAssignedPeers="clientAssignedPeers"
:peers="peers"
@assign="async (id) => await assign(configuration, id, props.client.ClientID)"
v-for="(peers, configuration) in assignmentStore.allConfigurationsPeers">
@unassign="async (id) => await unassign(id)"
v-for="(peers, configuration) in clientAssignedPeers">
</AvailablePeersGroup>
<h6 class="text-muted m-auto" v-if="Object.keys(assignmentStore.allConfigurationsPeers).length === 0">
<LocaleText t="No peer is available to assign"></LocaleText>
<h6 class="text-muted m-auto p-3" v-if="Object.keys(clientAssignedPeers).length === 0">
<LocaleText t="No peer assigned to this client"></LocaleText>
</h6>
</div>
<div class="card-footer d-flex gap-2 p-3 align-items-center justify-content-end">
<label for="availablePeerSearchString">
<i class="bi bi-search me-2"></i>
</label>
<input
id="availablePeerSearchString"
class="form-control form-control-sm rounded-3 w-auto" type="text">
</div>
<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="card-header sticky-top p-3">
<h6 class="mb-0 d-flex align-items-center">
<LocaleText t="Available Peers"></LocaleText>
</h6>
</div>
<div class="card-body p-0 overflow-scroll">
<AvailablePeersGroup
:availablePeerSearchString="availablePeerSearchString"
:configuration="configuration"
:clientAssignedPeers="clientAssignedPeers"
:peers="peers"
@assign="async (id) => await assign(configuration, id, props.client.ClientID)"
v-for="(peers, configuration) in assignmentStore.allConfigurationsPeers">
</AvailablePeersGroup>
<h6 class="text-muted m-auto" v-if="Object.keys(assignmentStore.allConfigurationsPeers).length === 0">
<LocaleText t="No peer is available to assign"></LocaleText>
</h6>
</div>
<div class="card-footer d-flex gap-2 p-3 align-items-center justify-content-end">
<label for="availablePeerSearchString">
<i class="bi bi-search me-2"></i>
</label>
<input
id="availablePeerSearchString"
v-model="availablePeerSearchString"
class="form-control form-control-sm rounded-3 w-auto" type="text">
</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>
</template>

View File

@ -27,14 +27,12 @@ const getClients = computed(() => {
</span>
</div>
<div class="card-body p-0">
<div class="list-group list-group-flush" >
<RouterLink
:to="{ name: 'Client Viewer', params: { id: client.ClientID } }"
class="list-group-item d-flex flex-column border-bottom list-group-item-action"
v-for="client in getClients" >
<small class="text-body">
{{ client.Email }}
</small>
<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 {computed} from "vue";
import {computed, ref, watch} from "vue";
import LocaleText from "@/components/text/localeText.vue";
import ClientAssignedPeers from "@/components/clientComponents/clientAssignedPeers.vue";
import ClientResetPassword from "@/components/clientComponents/clientResetPassword.vue";
const assignmentStore = DashboardClientAssignmentStore()
const route = useRoute()
const client = computed(() => {
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>
<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">
<small class="text-muted">
<LocaleText t="Email"></LocaleText>
@ -33,10 +50,11 @@ const client = computed(() => {
</small>
</div>
</div>
<div class="px-4">
<ClientAssignedPeers :client="client"></ClientAssignedPeers>
</div>
<ClientAssignedPeers
@refresh="getAssignedPeers()"
:clientAssignedPeers="clientAssignedPeers"
:client="client"></ClientAssignedPeers>
<ClientResetPassword :client="client" v-if="client.ClientGroup === 'Local'"></ClientResetPassword>
</div>
<div v-else class="d-flex w-100 h-100 text-muted">
<div class="m-auto text-center">