Skip to content

Commit 0b55668

Browse files
committed
chore: update @remnawave/backend-contract to version 2.1.64 in package.json and package-lock.json; add hourly request statistics chart to SRH Inspector Metrics widget
1 parent d51e0d3 commit 0b55668

File tree

4 files changed

+168
-14
lines changed

4 files changed

+168
-14
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
"@mantine/spotlight": "^8.3.2",
5959
"@monaco-editor/react": "^4.7.0",
6060
"@paralleldrive/cuid2": "2.2.2",
61-
"@remnawave/backend-contract": "2.1.62",
61+
"@remnawave/backend-contract": "2.1.64",
6262
"@stablelib/base64": "^2.0.1",
6363
"@stablelib/x25519": "^2.0.1",
6464
"@tabler/icons-react": "^3.35.0",
@@ -185,4 +185,4 @@
185185
"inquirer": "9.3.5"
186186
}
187187
}
188-
}
188+
}

public/locales/en/remnawave.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,5 +1647,12 @@
16471647
"widget": {
16481648
"subscription-request-history": "Subscription Request History"
16491649
}
1650+
},
1651+
"srh-inspector-metrics": {
1652+
"widget": {
1653+
"requests": "Requests",
1654+
"hourly-request-statistics": "Hourly Request Statistics",
1655+
"no-hourly-data-available": "No hourly data available"
1656+
}
16501657
}
16511658
}

src/widgets/dashboard/users/srh-inspector-metrics/srh-inspector-metrics.widget.tsx

Lines changed: 155 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import { Box, Card, Center, Group, Loader, SimpleGrid, Stack, Text, ThemeIcon } from '@mantine/core'
2+
import { PiChartBar, PiChartPieDuotone } from 'react-icons/pi'
23
import HighchartsReact from 'highcharts-react-official'
3-
import { PiChartPieDuotone } from 'react-icons/pi'
44
import { useTranslation } from 'react-i18next'
55
import * as Highcharts from 'highcharts'
66
import { useMemo, useRef } from 'react'
7+
import dayjs from 'dayjs'
78

89
import { useGetSubscriptionRequestHistoryStats } from '@shared/api/hooks'
910

1011
export function SrhInspectorMetrics() {
1112
const { t } = useTranslation()
1213
const appChartRef = useRef<HighchartsReact.RefObject>(null)
14+
const hourlyChartRef = useRef<HighchartsReact.RefObject>(null)
1315

1416
const { data: stats, isLoading } = useGetSubscriptionRequestHistoryStats()
1517

16-
const getHighchartsConfig = (chartFor: 'apps' | 'platforms') => {
18+
const getPieChartConfig = (chartFor: 'apps' | 'platforms') => {
1719
return {
1820
chart: {
1921
type: 'pie',
@@ -74,6 +76,94 @@ export function SrhInspectorMetrics() {
7476
}
7577
}
7678

79+
const getBarChartConfig = () => {
80+
return {
81+
chart: {
82+
type: 'column',
83+
backgroundColor: 'transparent',
84+
height: 400,
85+
style: {
86+
fontFamily: 'inherit'
87+
}
88+
},
89+
time: {
90+
useUTC: false
91+
},
92+
accessibility: {
93+
enabled: false
94+
},
95+
title: {
96+
text: undefined
97+
},
98+
xAxis: {
99+
type: 'datetime',
100+
dateTimeLabelFormats: {
101+
hour: '%H:00'
102+
},
103+
104+
units: [['hour', [1, 2, 3, 4, 6, 8, 12]]],
105+
labels: {
106+
style: {
107+
color: 'var(--mantine-color-text)'
108+
}
109+
},
110+
gridLineColor: 'var(--mantine-color-gray-6)',
111+
lineColor: 'var(--mantine-color-gray-6)'
112+
},
113+
yAxis: {
114+
title: {
115+
text: t('srh-inspector-metrics.widget.requests'),
116+
style: {
117+
color: 'var(--mantine-color-text)'
118+
}
119+
},
120+
labels: {
121+
style: {
122+
color: 'var(--mantine-color-text)'
123+
}
124+
},
125+
gridLineColor: 'var(--mantine-color-gray-6)',
126+
lineColor: 'var(--mantine-color-gray-6)'
127+
},
128+
129+
tooltip: {
130+
shared: true,
131+
backgroundColor: 'var(--mantine-color-body)',
132+
borderColor: 'var(--mantine-color-gray-4)',
133+
headerFormat: '',
134+
style: {
135+
color: 'var(--mantine-color-text)'
136+
},
137+
pointFormatter(this: { x: number; y: number }): string {
138+
return `<b>${dayjs(this.x).format('DD.MM.YYYY, HH:mm')}</b> </br> Requests: <b>${this.y}<b>`
139+
}
140+
},
141+
plotOptions: {
142+
column: {
143+
borderWidth: 0,
144+
borderRadius: 4,
145+
pointPadding: 0.1,
146+
groupPadding: 0.1,
147+
color: 'var(--mantine-color-indigo-6)',
148+
states: {
149+
hover: {
150+
color: 'var(--mantine-color-indigo-3)'
151+
}
152+
},
153+
dataLabels: {
154+
enabled: false
155+
}
156+
}
157+
},
158+
legend: {
159+
enabled: false
160+
},
161+
credits: {
162+
enabled: false
163+
}
164+
}
165+
}
166+
77167
const platformChartOptions: Highcharts.Options = useMemo(() => {
78168
if (!stats?.byParsedApp || stats.byParsedApp.length === 0) return {}
79169

@@ -83,18 +173,38 @@ export function SrhInspectorMetrics() {
83173
}))
84174

85175
return {
86-
...getHighchartsConfig('platforms'),
87-
series: [{ ...getHighchartsConfig('platforms').series[0], data }]
176+
...getPieChartConfig('platforms'),
177+
series: [{ ...getPieChartConfig('platforms').series[0], data }]
88178
}
89179
}, [stats?.byParsedApp])
90180

181+
const hourlyChartOptions: Highcharts.Options = useMemo(() => {
182+
if (!stats?.hourlyRequestStats || stats.hourlyRequestStats.length === 0) return {}
183+
184+
const data = stats.hourlyRequestStats.map((item) => {
185+
const utcDate = new Date(item.dateTime)
186+
return [utcDate.getTime(), item.requestCount]
187+
})
188+
189+
return {
190+
...getBarChartConfig(),
191+
series: [
192+
{
193+
type: 'column',
194+
name: t('srh-inspector-metrics.widget.requests'),
195+
data
196+
}
197+
]
198+
}
199+
}, [stats?.hourlyRequestStats, t])
200+
91201
if (isLoading) {
92202
return (
93203
<Stack gap="md">
94-
<SimpleGrid cols={{ base: 1, lg: 1 }} spacing="md">
95-
{[1].map((i) => (
204+
<SimpleGrid cols={{ base: 1, lg: 2 }} spacing="md">
205+
{[1, 2].map((i) => (
96206
<Card key={i} p="lg" radius="md" withBorder>
97-
<Center h={300}>
207+
<Center h={i === 1 ? 300 : 400}>
98208
<Loader size="lg" />
99209
</Center>
100210
</Card>
@@ -106,7 +216,7 @@ export function SrhInspectorMetrics() {
106216

107217
return (
108218
<Stack gap="xl">
109-
<SimpleGrid cols={{ base: 1 }} spacing="lg">
219+
<SimpleGrid cols={{ base: 1, lg: 2 }} spacing="lg">
110220
<Card
111221
p="lg"
112222
radius="md"
@@ -146,6 +256,43 @@ export function SrhInspectorMetrics() {
146256
</Center>
147257
)}
148258
</Card>
259+
260+
<Card
261+
p="lg"
262+
radius="md"
263+
style={{
264+
background:
265+
'linear-gradient(135deg, var(--mantine-color-dark-6) 0%, var(--mantine-color-dark-7) 100%)'
266+
}}
267+
>
268+
<Group align="center" gap="sm" mb="lg">
269+
<ThemeIcon color="blue" size="lg" variant="outline">
270+
<PiChartBar size="20px" />
271+
</ThemeIcon>
272+
273+
<Text fw={600} size="lg">
274+
{t('srh-inspector-metrics.widget.hourly-request-statistics')}
275+
</Text>
276+
</Group>
277+
{stats?.hourlyRequestStats && stats.hourlyRequestStats.length > 0 ? (
278+
<Box h={400}>
279+
<HighchartsReact
280+
highcharts={Highcharts}
281+
options={hourlyChartOptions}
282+
ref={hourlyChartRef}
283+
/>
284+
</Box>
285+
) : (
286+
<Center h={400}>
287+
<Stack align="center" gap="sm">
288+
<PiChartBar color="var(--mantine-color-gray-4)" size="48px" />
289+
<Text c="dimmed" size="sm">
290+
{t('srh-inspector-metrics.widget.no-hourly-data-available')}
291+
</Text>
292+
</Stack>
293+
</Center>
294+
)}
295+
</Card>
149296
</SimpleGrid>
150297
</Stack>
151298
)

0 commit comments

Comments
 (0)