main
vue 170 lines 4.4 KB
Raw
1 <template>
2 <div class="uncommitted-entries-wrap flex flex-col gap-3">
3 <div class="uncommitted-entries">
4 <div class="chart grow">
5 <div class="label flex items-center gap-3">
6 <Icon v-if="isWarning" :name="DangerIcon" />
7 <span>Uncommitted Journal Entries</span>
8 </div>
9 <VChart class="w-full" autoresize :option="chartOption" :style="{ height: '70px' }" />
10 </div>
11 <div class="value flex flex-col justify-center" :class="{ warning: isWarning }">
12 <span>{{ value }}</span>
13 </div>
14 </div>
15 <div class="footer">
16 <n-button
17 type="primary"
18 ghost
19 icon-placement="right"
20 @click="routeGraylogManagement('messages').navigate()"
21 >
22 <template #icon>
23 <Icon :name="LinkIcon" :size="14" />
24 </template>
25 See messages
26 </n-button>
27 </div>
28 </div>
29 </template>
30
31 <script setup lang="ts">
32 import type { LineSeriesOption } from "echarts/charts"
33 import type { GridComponentOption, TooltipComponentOption } from "echarts/components"
34 import type { ComposeOption } from "echarts/core"
35 import { LineChart } from "echarts/charts"
36 import { GridComponent, TooltipComponent } from "echarts/components"
37 import { use } from "echarts/core"
38 import { CanvasRenderer } from "echarts/renderers"
39 import { NButton } from "naive-ui"
40 import { computed, ref, toRefs, watch } from "vue"
41 import VChart from "vue-echarts"
42 import {
43 buildChartTooltipGlassBase,
44 chartTooltipThemeFromStyle,
45 formatChartTooltipTimeAxisFirst
46 } from "@/components/common/charts"
47 import Icon from "@/components/common/Icon.vue"
48 import { useNavigation } from "@/composables/useNavigation"
49 import { useHealthcheckStore } from "@/stores/healthcheck"
50 import { useThemeStore } from "@/stores/theme"
51 import dayjs from "@/utils/dayjs"
52
53 const props = defineProps<{
54 value: number
55 }>()
56
57 use([CanvasRenderer, LineChart, TooltipComponent, GridComponent])
58
59 type ChartOption = ComposeOption<TooltipComponentOption | GridComponentOption | LineSeriesOption>
60
61 const MAX_POINTS = 100
62 const CHART_ANIMATION_MAX_POINTS = 100
63
64 const UNCOMMITTED_JOURNAL_ENTRIES_THRESHOLD = useHealthcheckStore().uncommittedJournalEntriesThreshold
65
66 const { value } = toRefs(props)
67
68 const LinkIcon = "carbon:launch"
69 const DangerIcon = "majesticons:exclamation-line"
70
71 const style = computed(() => useThemeStore().style)
72 const { routeGraylogManagement } = useNavigation()
73
74 const dataPoints = ref<[number, number][]>([])
75
76 const isWarning = computed<boolean>(() => value.value > UNCOMMITTED_JOURNAL_ENTRIES_THRESHOLD)
77
78 const enableAnimation = computed(() => dataPoints.value.length < CHART_ANIMATION_MAX_POINTS)
79
80 const chartOption = computed((): ChartOption => {
81 const primary = style.value["primary-color"]
82
83 return {
84 backgroundColor: "transparent",
85 grid: { left: 0, right: 0, top: 2, bottom: 2 },
86 xAxis: {
87 type: "time",
88 show: false
89 },
90 yAxis: {
91 type: "value",
92 show: false,
93 scale: true
94 },
95 tooltip: {
96 ...buildChartTooltipGlassBase(
97 chartTooltipThemeFromStyle({
98 ...style.value,
99 "font-family": style.value["font-family-mono"]
100 }),
101 { trigger: "axis" }
102 ),
103 formatter: params =>
104 formatChartTooltipTimeAxisFirst(params, {
105 formatTime: ts => dayjs(ts).format("HH:mm:ss"),
106 resolveColor: () => primary
107 })
108 },
109 series: [
110 {
111 name: "Entries",
112 type: "line",
113 showSymbol: false,
114 smooth: false,
115 lineStyle: { width: 3, color: primary },
116 areaStyle: { color: primary, opacity: 0.12 },
117 animation: enableAnimation.value,
118 animationDurationUpdate: 200,
119 data: dataPoints.value
120 }
121 ]
122 }
123 })
124
125 watch(value, val => {
126 if (dataPoints.value.length >= MAX_POINTS) {
127 dataPoints.value.shift()
128 }
129 dataPoints.value.push([Date.now(), val])
130 })
131 </script>
132
133 <style lang="scss" scoped>
134 .uncommitted-entries-wrap {
135 width: 100%;
136
137 .uncommitted-entries {
138 background-color: var(--bg-default-color);
139 display: flex;
140 border-radius: var(--border-radius);
141 border: 1px solid var(--border-color);
142
143 .label {
144 padding: 18px 22px;
145 font-size: 18px;
146 flex-grow: 1;
147 font-weight: 700;
148
149 i {
150 color: var(--warning-color);
151 }
152 }
153 .value {
154 padding: 18px 22px;
155 background-color: var(--bg-secondary-color);
156 font-size: 20px;
157 font-family: var(--font-family-mono);
158 min-width: 80px;
159 text-align: center;
160 border-top-right-radius: var(--border-radius);
161 border-bottom-right-radius: var(--border-radius);
162
163 &.warning {
164 color: var(--warning-color);
165 background-color: rgba(var(--warning-color-rgb) / 0.05);
166 }
167 }
168 }
169 }
170 </style>