main
vue 189 lines 5.96 KB
Raw
1 <template>
2 <div class="grid-auto-fit-200 grid gap-2 p-6 pt-3">
3 <CardKV v-for="(value, key) of asset" :key>
4 <template #key>
5 {{ key }}
6 </template>
7 <template #value>
8 <div v-if="key === 'id'">#{{ value }}</div>
9 <div v-else-if="key === 'customer_code'">
10 <code
11 class="text-primary cursor-pointer"
12 @click.stop="routeCustomer({ code: asset.customer_code }).navigate()"
13 >
14 #{{ asset.customer_code }}
15 <Icon :name="LinkIcon" :size="14" class="relative top-0.5" />
16 </code>
17 </div>
18 <div v-else-if="key === 'agent_id'">
19 <code class="text-primary cursor-pointer" @click.stop="routeAgent(asset.agent_id).navigate()">
20 {{ asset.agent_id }}
21 <Icon :name="LinkIcon" :size="14" class="relative top-0.5" />
22 </code>
23 </div>
24 <div v-else-if="key === 'index_name'">
25 <code class="text-primary cursor-pointer" @click.stop="routeIndex(asset.index_name).navigate()">
26 {{ asset.index_name }}
27 <Icon :name="LinkIcon" :size="14" class="relative top-0.5" />
28 </code>
29 </div>
30 <div v-else-if="key === 'index_id'">
31 <code class="text-primary cursor-pointer" @click.stop="openAlertDetails()">
32 {{ asset.index_id }}
33 <Icon :name="ViewIcon" :size="14" class="relative top-0.5" />
34 </code>
35 </div>
36 <div v-else-if="key === 'alert_linked'">
37 <div class="flex items-center gap-2">
38 <span>{{ value }}</span>
39 <code class="text-primary cursor-pointer" @click.stop="openEventSearch()">
40 View in Event Search
41 <Icon :name="LinkIcon" :size="14" class="relative top-0.5" />
42 </code>
43 </div>
44 </div>
45 <div v-else>
46 {{ value === "" ? "-" : (value ?? "-") }}
47 </div>
48 </template>
49 </CardKV>
50 </div>
51
52 <n-modal
53 v-model:show="showAlertDetails"
54 preset="card"
55 content-class="p-0!"
56 :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(550px, 90vh)', overflow: 'hidden' }"
57 :bordered="false"
58 title="Alert Details"
59 segmented
60 >
61 <n-spin :show="loading" class="min-h-40">
62 <n-tabs type="line" animated :tabs-padding="24">
63 <n-tab-pane name="Info" tab="Info" display-directive="show">
64 <div class="grid-auto-fit-200 grid gap-2 p-6 pt-3">
65 <CardKV v-for="(value, key) of alertDetailsInfo" :key>
66 <template #key>
67 {{ key }}
68 </template>
69 <template #value>
70 <div v-if="key === '_index'">
71 <code
72 class="text-primary cursor-pointer"
73 @click.stop="routeIndex(alertDetailsInfo._index).navigate()"
74 >
75 {{ alertDetailsInfo._index }}
76 <Icon :name="LinkIcon" :size="14" class="relative top-0.5" />
77 </code>
78 </div>
79 <div v-else>
80 {{ value === "" ? "-" : (value ?? "-") }}
81 </div>
82 </template>
83 </CardKV>
84 </div>
85 </n-tab-pane>
86 <n-tab-pane name="Source" tab="Source" display-directive="show">
87 <div v-if="alertDetailsSource" class="p-6 pt-3">
88 <CodeSource :code="alertDetailsSource" lang="json" />
89 </div>
90 </n-tab-pane>
91 </n-tabs>
92 </n-spin>
93 </n-modal>
94 </template>
95
96 <script setup lang="ts">
97 import type { AlertAsset, AlertDetails } from "@/types/incidentManagement/alerts.d"
98 import _omit from "lodash/omit"
99 import { NModal, NSpin, NTabPane, NTabs, useMessage } from "naive-ui"
100 import { computed, defineAsyncComponent, ref, toRefs, watch } from "vue"
101 import Api from "@/api"
102 import CardKV from "@/components/common/cards/CardKV.vue"
103 import Icon from "@/components/common/Icon.vue"
104 import { useNavigation } from "@/composables/useNavigation"
105
106 const props = defineProps<{ asset: AlertAsset }>()
107
108 const CodeSource = defineAsyncComponent(() => import("@/components/common/CodeSource.vue"))
109
110 const { asset } = toRefs(props)
111
112 const LinkIcon = "carbon:launch"
113 const ViewIcon = "iconoir:eye-solid"
114 const { routeAgent, routeIndex, routeCustomer, routeEventSearch } = useNavigation()
115 const message = useMessage()
116 const loading = ref(false)
117 const showAlertDetails = ref(false)
118 const alertDetails = ref<AlertDetails | null>(null)
119 const alertDetailsInfo = computed(() => _omit(alertDetails.value, ["_source"]))
120 const alertDetailsSource = computed(() => {
121 if (!alertDetails.value?._source) return undefined
122
123 const source = alertDetails.value._source
124 const priorityKeys = ["timestamp", "agent_name"] // Keys to show first
125 const endKeys = ["message"] // Keys to show last
126
127 // Separate priority keys, end keys, and other keys
128 const existingPriorityKeys = priorityKeys.filter(key => key in source)
129 const existingEndKeys = endKeys.filter(key => key in source)
130 const otherKeys = Object.keys(source)
131 .filter(key => !priorityKeys.includes(key) && !endKeys.includes(key))
132 .sort() // Sort alphabetically
133
134 // Combine: priority keys first, then sorted other keys, then end keys
135 const sortedKeys = [...existingPriorityKeys, ...otherKeys, ...existingEndKeys]
136
137 // Create new object with sorted keys
138 const sortedSource: Record<string, any> = {}
139 sortedKeys.forEach(key => {
140 sortedSource[key] = source[key]
141 })
142
143 return sortedSource
144 })
145
146 watch(showAlertDetails, val => {
147 if (val && !alertDetails.value) {
148 getAlertDetails(asset.value.index_id, asset.value.index_name)
149 }
150 })
151
152 function getAlertDetails(indexId: string, indexName: string) {
153 loading.value = true
154
155 Api.incidentManagement.alerts
156 .getAlertDetails(indexId, indexName)
157 .then(res => {
158 if (res.data.success) {
159 alertDetails.value = res.data?.alert_details || null
160 } else {
161 closeAlertDetails()
162 message.warning(res.data?.message || "An error occurred. Please try again later.")
163 }
164 })
165 .catch(err => {
166 closeAlertDetails()
167 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
168 })
169 .finally(() => {
170 loading.value = false
171 })
172 }
173
174 function openAlertDetails() {
175 showAlertDetails.value = true
176 }
177
178 function closeAlertDetails() {
179 showAlertDetails.value = false
180 }
181
182 function openEventSearch() {
183 const url = routeEventSearch({
184 customer_code: asset.value.customer_code,
185 query: `alert_id:"${asset.value.alert_linked}"`
186 }).fullUrl()
187 window.open(url, "_blank")
188 }
189 </script>