main
vue 218 lines 5.12 KB
Raw
1 <template>
2 <n-spin :show="loading">
3 <div ref="header" 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 class="box">
21 Indicies:
22 <code>{{ usedIndicies }}</code>
23 </div>
24 </div>
25 </n-popover>
26 </div>
27 <n-pagination
28 v-model:page="currentPage"
29 v-model:page-size="pageSize"
30 :item-count="total"
31 :page-slot
32 :show-size-picker
33 :page-sizes
34 :simple="simpleMode"
35 />
36 <n-select v-if="!compactMode" v-model:value="timerange" size="small" :options="timeOptions" class="w-32!" />
37 <n-popover v-if="compactMode" overlap placement="right">
38 <template #trigger>
39 <div class="bg-default rounded-lg">
40 <n-button size="small">
41 <template #icon>
42 <Icon :name="FilterIcon" />
43 </template>
44 </n-button>
45 </div>
46 </template>
47 <div class="mb-2">
48 <div class="text-secondary my-1 text-sm">Time range:</div>
49 <n-select v-model:value="timerange" size="small" :options="timeOptions" class="mb-1 w-32!" />
50 </div>
51 </n-popover>
52 </div>
53 <div class="my-3 flex min-h-28 flex-col gap-2">
54 <template v-if="alertsEvents.length">
55 <AlertsEventItem
56 v-for="alertsEvent of alertsEvents"
57 :key="alertsEvent.event.id"
58 :alerts-event
59 @click-event="gotoEventsPage($event)"
60 />
61 </template>
62 <template v-else>
63 <n-empty v-if="!loading" description="No items found" class="h-48 justify-center" />
64 </template>
65 </div>
66 <div class="footer flex justify-end">
67 <n-pagination
68 v-if="alertsEvents.length > 3"
69 v-model:page="currentPage"
70 :page-size
71 :item-count="total"
72 :page-slot="6"
73 />
74 </div>
75 </n-spin>
76 </template>
77
78 <script setup lang="ts">
79 // TODO-FE: refactor
80 import type { AlertsEventElement, AlertsQuery } from "@/types/graylog/alerts.d"
81 import { useResizeObserver } from "@vueuse/core"
82 import { NButton, NEmpty, NPagination, NPopover, NSelect, NSpin, useMessage } from "naive-ui"
83 import { computed, onBeforeMount, ref, watch } from "vue"
84 import Api from "@/api"
85 import Icon from "@/components/common/Icon.vue"
86 import dayjs from "@/utils/dayjs"
87 import AlertsEventItem from "./Item.vue"
88
89 const emit = defineEmits<{
90 (e: "clickEvent", value: string): void
91 }>()
92
93 const message = useMessage()
94 const loading = ref(false)
95 const alertsEvents = ref<AlertsEventElement[]>([])
96 const total = ref(0)
97 const pageSize = ref(50)
98 const currentPage = ref(1)
99 const header = ref()
100 const compactMode = ref(false)
101 const simpleMode = ref(false)
102 const showSizePicker = computed(() => !compactMode.value)
103 const pageSizes = [25, 50, 100, 150, 200]
104 const pageSlot = ref(8)
105 const usedIndicies = ref("")
106
107 const FilterIcon = "carbon:filter-edit"
108 const InfoIcon = "carbon:information"
109
110 const hour = 60 * 60
111 const day = hour * 24
112 const week = day * 7
113 const month = week * 4
114 const year = month * 12
115
116 const timerange = ref(year)
117
118 const timeOptions = [
119 {
120 label: "24 Hours",
121 value: day
122 },
123 {
124 label: "This week",
125 value: dayjs().startOf("week").unix()
126 },
127 {
128 label: "Last week",
129 value: week
130 },
131 {
132 label: "This month",
133 value: dayjs().startOf("month").unix()
134 },
135 {
136 label: "Last month",
137 value: month
138 },
139 {
140 label: "This year",
141 value: dayjs().startOf("year").unix()
142 },
143 {
144 label: "Last year",
145 value: year
146 }
147 ]
148
149 function gotoEventsPage(event_definition_id: string) {
150 emit("clickEvent", event_definition_id)
151 }
152
153 function getData(page: number, pageSize: number, timerange: number) {
154 loading.value = true
155
156 const query: AlertsQuery = {
157 query: "",
158 page,
159 per_page: pageSize,
160 filter: {
161 alerts: "only",
162 event_definitions: []
163 },
164 timerange: {
165 range: timerange,
166 type: "relative"
167 }
168 }
169
170 Api.graylog
171 .getAlerts(query)
172 .then(res => {
173 if (res.data.success) {
174 alertsEvents.value = res.data?.alerts?.events || []
175 total.value = res.data?.alerts?.total_events || 0
176 usedIndicies.value = res.data?.alerts?.used_indices?.join(", ")
177 } else {
178 message.warning(res.data?.message || "An error occurred. Please try again later.")
179 }
180 })
181 .catch(err => {
182 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
183 })
184 .finally(() => {
185 loading.value = false
186 })
187 }
188
189 useResizeObserver(header, entries => {
190 const entry = entries[0]
191 if (!entry) return
192
193 const { width } = entry.contentRect
194
195 if (width < 650) {
196 compactMode.value = true
197 pageSize.value = pageSizes[0] ?? 25
198 pageSlot.value = 5
199 } else {
200 compactMode.value = false
201 pageSlot.value = 8
202 }
203
204 simpleMode.value = width < 450
205 })
206
207 watch([currentPage, pageSize, timerange], ([page, pageSize, timerange]) => {
208 getData(page, pageSize, timerange)
209 })
210
211 onBeforeMount(() => {
212 getData(currentPage.value, pageSize.value, timerange.value)
213 // MOCK
214 /*
215 alertsEvents.value = alerts_event_element
216 */
217 })
218 </script>