main
vue 191 lines 4.69 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 Enabled:
22 <code>{{ totalEnabled }}</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 :page-slot
31 :show-size-picker
32 :page-sizes
33 :item-count="total"
34 :simple="simpleMode"
35 />
36 <n-popover overlap placement="right" class="px-0!">
37 <template #trigger>
38 <div class="bg-default rounded-lg">
39 <n-button size="small">
40 <template #icon>
41 <Icon :name="FilterIcon" />
42 </template>
43 </n-button>
44 </div>
45 </template>
46 <div class="py-1">
47 <div class="px-3">
48 <div class="text-secondary mb-1 text-sm">Enabled:</div>
49 <n-select
50 v-model:value="enabledFilter"
51 size="small"
52 :options="enabledOptions"
53 clearable
54 placeholder="All"
55 class="w-36!"
56 />
57 </div>
58 <n-divider class="my-3!" />
59 <div class="px-3">
60 <div class="text-secondary mb-1 text-sm">Editable:</div>
61 <n-select
62 v-model:value="editableFilter"
63 size="small"
64 :options="editableOptions"
65 clearable
66 placeholder="All"
67 class="w-36!"
68 />
69 </div>
70 </div>
71 </n-popover>
72 </div>
73 <div class="my-3 flex min-h-52 flex-col gap-2">
74 <template v-if="itemsPaginated.length">
75 <StreamItem v-for="stream of itemsPaginated" :key="stream.id" :stream />
76 </template>
77 <template v-else>
78 <n-empty v-if="!loading" description="No items found" class="h-48 justify-center" />
79 </template>
80 </div>
81 <div class="footer flex justify-end">
82 <n-pagination
83 v-if="itemsPaginated.length > 3"
84 v-model:page="currentPage"
85 :page-size
86 :item-count="total"
87 :page-slot="6"
88 />
89 </div>
90 </n-spin>
91 </template>
92
93 <script setup lang="ts">
94 // TODO-FE: refactor
95 import type { Stream } from "@/types/graylog/stream.d"
96 import { useResizeObserver } from "@vueuse/core"
97 import { NButton, NDivider, NEmpty, NPagination, NPopover, NSelect, NSpin, useMessage } from "naive-ui"
98 import { computed, onBeforeMount, ref } from "vue"
99 import Api from "@/api"
100 import Icon from "@/components/common/Icon.vue"
101 import StreamItem from "./Item.vue"
102
103 const FilterIcon = "carbon:filter-edit"
104 const InfoIcon = "carbon:information"
105
106 const message = useMessage()
107 const loading = ref(false)
108 const streams = ref<Stream[]>([])
109 const total = ref(0)
110 const totalEnabled = computed(() => streams.value.filter(o => !o.disabled).length)
111 const pageSize = ref(25)
112 const currentPage = ref(1)
113 const simpleMode = ref(false)
114 const showSizePicker = ref(true)
115 const pageSizes = [10, 25, 50, 100]
116 const header = ref()
117 const pageSlot = ref(8)
118 const enabledFilter = ref<null | number>(null)
119 const editableFilter = ref<null | number>(null)
120 const enabledOptions = [
121 { label: "Enabled", value: 1 },
122 { label: "Not Enabled", value: 0 }
123 ]
124 const editableOptions = [
125 { label: "Editable", value: 1 },
126 { label: "Not Editable", value: 0 }
127 ]
128
129 const itemsPaginated = computed(() => {
130 const from = (currentPage.value - 1) * pageSize.value
131 const to = currentPage.value * pageSize.value
132
133 return streams.value
134 .filter(o => {
135 switch (enabledFilter.value) {
136 case 1:
137 return o.disabled === false
138 case 0:
139 return o.disabled === true
140 default:
141 return true
142 }
143 })
144 .filter(o => {
145 switch (editableFilter.value) {
146 case 1:
147 return o.is_editable === true
148 case 0:
149 return o.is_editable === false
150 default:
151 return true
152 }
153 })
154 .slice(from, to)
155 })
156
157 function getData() {
158 loading.value = true
159
160 Api.graylog
161 .getStreams()
162 .then(res => {
163 if (res.data.success) {
164 streams.value = res.data.streams || []
165 total.value = res.data.total || 0
166 } else {
167 message.warning(res.data?.message || "An error occurred. Please try again later.")
168 }
169 })
170 .catch(err => {
171 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
172 })
173 .finally(() => {
174 loading.value = false
175 })
176 }
177
178 useResizeObserver(header, entries => {
179 const entry = entries[0]
180 if (!entry) return
181
182 const { width } = entry.contentRect
183
184 pageSlot.value = width < 650 ? 5 : 8
185 simpleMode.value = width < 450
186 })
187
188 onBeforeMount(() => {
189 getData()
190 })
191 </script>