main
vue 138 lines 3.51 KB
Raw
1 <template>
2 <n-spin :show="loading">
3 <div class="header flex items-center justify-end gap-2">
4 <div class="info flex grow gap-5">
5 <n-popover overlap placement="bottom-start">
6 <template #trigger>
7 <div class="bg-default rounded-lg">
8 <n-button size="small" class="cursor-help!">
9 <template #icon>
10 <Icon :name="InfoIcon" />
11 </template>
12 </n-button>
13 </div>
14 </template>
15 <div class="flex flex-col gap-2">
16 <div class="box">
17 Total:
18 <code>{{ total }}</code>
19 </div>
20 </div>
21 </n-popover>
22 </div>
23 <n-select
24 v-model:value="prioritySelected"
25 :options="priorities"
26 clearable
27 placeholder="Priority..."
28 size="small"
29 style="width: 110px"
30 />
31 </div>
32 <div class="my-3 flex min-h-52 flex-col gap-2">
33 <template v-if="itemsPaginated.length">
34 <EventItem v-for="event of itemsPaginated" :key="event.id" :event :highlight="event.id === highlight" />
35 </template>
36 <template v-else>
37 <n-empty v-if="!loading" description="No items found" class="h-48 justify-center" />
38 </template>
39 </div>
40 </n-spin>
41 </template>
42
43 <script setup lang="ts">
44 import type { SelectMixedOption } from "naive-ui/es/select/src/interface"
45 import type { EventDefinition } from "@/types/graylog/event-definition.d"
46 import { NButton, NEmpty, NPopover, NSelect, NSpin, useMessage } from "naive-ui"
47 import { computed, nextTick, onBeforeMount, ref, toRefs, watch } from "vue"
48 import Api from "@/api"
49 import Icon from "@/components/common/Icon.vue"
50 import EventItem from "./Item.vue"
51
52 const props = defineProps<{ highlight: string | null | undefined }>()
53
54 const emit = defineEmits<{
55 (e: "loaded", value: EventDefinition[]): void
56 }>()
57
58 const { highlight } = toRefs(props)
59
60 const InfoIcon = "carbon:information"
61
62 const message = useMessage()
63 const total = ref(0)
64 const loading = ref(false)
65 const events = ref<EventDefinition[]>([])
66 const priorities = computed<SelectMixedOption[]>(() =>
67 Array.from(new Set(events.value.map(o => o.priority)), o => ({
68 label: `Priority ${o.toString()}`,
69 value: o
70 }))
71 )
72 const prioritySelected = ref<null | number>(null)
73
74 const itemsPaginated = computed(() => {
75 return events.value.filter(o => {
76 if (!prioritySelected.value) {
77 return true
78 } else {
79 return o.priority === prioritySelected.value
80 }
81 })
82 })
83
84 function scrollToItem(id: string) {
85 const element = document.getElementById(`event-${id}`)
86 const scrollContent = document.querySelector("#main > .n-scrollbar > .n-scrollbar-container") as HTMLElement
87
88 if (element && scrollContent) {
89 const wrap: HTMLElement = scrollContent
90 const middle = element.offsetTop - wrap.offsetHeight / 2
91 scrollContent?.scrollTo({ top: middle, behavior: "smooth" })
92 }
93 }
94
95 function getData() {
96 loading.value = true
97
98 Api.graylog
99 .getEventDefinitions()
100 .then(res => {
101 if (res.data.success) {
102 events.value = res.data.event_definitions || []
103 total.value = events.value.length || 0
104 emit("loaded", events.value)
105
106 nextTick(() => {
107 setTimeout(() => {
108 if (highlight.value) {
109 scrollToItem(highlight.value)
110 }
111 }, 300)
112 })
113 } else {
114 message.warning(res.data?.message || "An error occurred. Please try again later.")
115 }
116 })
117 .catch(err => {
118 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
119 })
120 .finally(() => {
121 loading.value = false
122 })
123 }
124
125 watch(highlight, val => {
126 if (val) {
127 nextTick(() => {
128 setTimeout(() => {
129 scrollToItem(val)
130 })
131 })
132 }
133 })
134
135 onBeforeMount(() => {
136 getData()
137 })
138 </script>