main
vue 125 lines 3.74 KB
Raw
1 <template>
2 <n-card title="Storage per Customer" segmented content-class="pr-1!">
3 <n-spin :show="loading">
4 <div v-if="customerSizes && customerSizes.length > 0">
5 <n-scrollbar style="max-height: 200px" trigger="none" content-class="flex flex-col gap-4 pr-5!">
6 <div v-for="customer in customerSizes" :key="customer.customer" class="flex flex-col gap-1">
7 <div class="flex items-center justify-between gap-2">
8 <div class="flex items-center gap-2">
9 <div class="font-mono font-bold">{{ customer.customer }}</div>
10 <n-popover trigger="click" placement="bottom" :width="300">
11 <template #trigger>
12 <n-tag size="small" class="cursor-pointer!">
13 {{ customer.index_count }}
14 {{ customer.index_count === 1 ? "index" : "indices" }}
15 </n-tag>
16 </template>
17 <div class="flex flex-col gap-2">
18 <span class="text-sm font-semibold">Indices for {{ customer.customer }}</span>
19 <n-scrollbar style="max-height: 200px" trigger="none">
20 <ul>
21 <li
22 v-for="index in customer.indices"
23 :key="index"
24 @click="selectIndex(index)"
25 >
26 <div
27 class="hover:text-primary flex cursor-pointer items-center gap-2 font-mono text-sm transition-colors"
28 >
29 {{ index }}
30
31 <Icon name="carbon:launch" />
32 </div>
33 </li>
34 </ul>
35 </n-scrollbar>
36 </div>
37 </n-popover>
38 </div>
39 <div class="text-secondary font-mono text-sm">{{ customer.total_size_human }}</div>
40 </div>
41 <n-progress
42 type="line"
43 :percentage="getPercentage(customer.total_size_bytes)"
44 :show-indicator="false"
45 :height="8"
46 :border-radius="4"
47 :color="getProgressColor(customer.total_size_bytes)"
48 />
49 </div>
50 </n-scrollbar>
51 </div>
52 <n-empty v-else-if="!loading" description="No customer data available" />
53 </n-spin>
54 </n-card>
55 </template>
56
57 <script lang="ts" setup>
58 import { NCard, NEmpty, NPopover, NProgress, NScrollbar, NSpin, NTag, useMessage, useThemeVars } from "naive-ui"
59 import { computed, onBeforeMount, ref } from "vue"
60 import Api from "@/api"
61 import Icon from "@/components/common/Icon.vue"
62
63 interface CustomerIndicesSize {
64 customer: string
65 total_size_bytes: number
66 total_size_human: string
67 index_count: number
68 indices: string[]
69 }
70
71 const emit = defineEmits<{
72 (e: "click", value: string): void
73 }>()
74
75 const message = useMessage()
76 const themeVars = useThemeVars()
77
78 const loading = ref(false)
79 const customerSizes = ref<CustomerIndicesSize[]>([])
80
81 const maxSize = computed(() => {
82 if (!customerSizes.value.length) return 0
83 return Math.max(...customerSizes.value.map(c => c.total_size_bytes))
84 })
85
86 function getPercentage(sizeBytes: number): number {
87 if (!maxSize.value) return 0
88 return Math.round((sizeBytes / maxSize.value) * 100)
89 }
90
91 function getProgressColor(sizeBytes: number): string {
92 const percentage = getPercentage(sizeBytes)
93 if (percentage >= 80) return themeVars.value.errorColor
94 if (percentage >= 60) return themeVars.value.warningColor
95 return themeVars.value.infoColor
96 }
97
98 function selectIndex(indexName: string) {
99 emit("click", indexName)
100 }
101
102 function getCustomerIndicesSize() {
103 loading.value = true
104
105 Api.wazuh.indices
106 .getIndicesSizePerCustomer()
107 .then(res => {
108 if (res.data.success) {
109 customerSizes.value = res.data.customer_sizes || []
110 } else {
111 message.error(res.data?.message || "An error occurred. Please try again later.")
112 }
113 })
114 .catch(err => {
115 message.error(err.response?.data?.message || "Failed to retrieve customer indices size.")
116 })
117 .finally(() => {
118 loading.value = false
119 })
120 }
121
122 onBeforeMount(() => {
123 getCustomerIndicesSize()
124 })
125 </script>