feat: display incident reason

pull/97/head
lyc8503 2025-03-25 16:39:05 +08:00
parent fe9e2ae2e0
commit dabccd83f6
2 changed files with 41 additions and 6 deletions

View File

@ -50,7 +50,7 @@ Please refer to [Wiki](https://github.com/lyc8503/UptimeFlare/wiki)
- [x] Notification grace period - [x] Notification grace period
- [ ] SSL certificate checks - [ ] SSL certificate checks
- [x] ~~Self-host Dockerfile~~ - [x] ~~Self-host Dockerfile~~
- [ ] Incident timeline - [x] Incident history
- [ ] Improve `checkLocationWorkerRoute` and fix possible `proxy failed` - [ ] Improve `checkLocationWorkerRoute` and fix possible `proxy failed`
- [ ] Groups - [ ] Groups
- [x] Remove old incidents - [x] Remove old incidents

View File

@ -1,8 +1,8 @@
import { MonitorState, MonitorTarget } from '@/uptime.types' import { MonitorState, MonitorTarget } from '@/uptime.types'
import { getColor } from '@/util/color' import { getColor } from '@/util/color'
import { Box, Tooltip } from '@mantine/core' import { Box, Tooltip, Modal } from '@mantine/core'
import { useResizeObserver } from '@mantine/hooks' import { useResizeObserver } from '@mantine/hooks'
import { useLayoutEffect, useRef, useState } from 'react' import { useState } from 'react'
const moment = require('moment') const moment = require('moment')
require('moment-precise-range-plugin') require('moment-precise-range-plugin')
@ -14,6 +14,9 @@ export default function DetailBar({
state: MonitorState state: MonitorState
}) { }) {
const [barRef, barRect] = useResizeObserver() const [barRef, barRect] = useResizeObserver()
const [modalOpened, setModalOpened] = useState(false)
const [modalTitle, setModalTitle] = useState('')
const [modelContent, setModelContent] = useState(<div />)
const overlapLen = (x1: number, x2: number, y1: number, y2: number) => { const overlapLen = (x1: number, x2: number, y1: number, y2: number) => {
return Math.max(0, Math.min(x2, y2) - Math.max(x1, y1)) return Math.max(0, Math.min(x2, y2) - Math.max(x1, y1))
@ -34,11 +37,30 @@ export default function DetailBar({
const dayMonitorTime = overlapLen(dayStart, dayEnd, montiorStartTime, currentTime) const dayMonitorTime = overlapLen(dayStart, dayEnd, montiorStartTime, currentTime)
let dayDownTime = 0 let dayDownTime = 0
let incidentReasons: string[] = []
for (let incident of state.incident[monitor.id]) { for (let incident of state.incident[monitor.id]) {
const incidentStart = incident.start[0] const incidentStart = incident.start[0]
const incidentEnd = incident.end ?? currentTime const incidentEnd = incident.end ?? currentTime
dayDownTime += overlapLen(dayStart, dayEnd, incidentStart, incidentEnd) const overlap = overlapLen(dayStart, dayEnd, incidentStart, incidentEnd)
dayDownTime += overlap
// Incident history for the day
if (overlap > 0) {
for (let i = 0; i < incident.error.length; i++) {
let partStart = incident.start[i]
let partEnd = i === incident.error.length - 1 ? (incident.end ?? currentTime) : incident.start[i + 1]
partStart = Math.max(partStart, dayStart)
partEnd = Math.min(partEnd, dayEnd)
if (overlapLen(dayStart, dayEnd, partStart, partEnd) > 0) {
const startStr = new Date(partStart * 1000).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})
const endStr = new Date(partEnd * 1000).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})
incidentReasons.push(`[${startStr}-${endStr}] ${incident.error[i]}`)
}
}
}
} }
const dayPercent = (((dayMonitorTime - dayDownTime) / dayMonitorTime) * 100).toPrecision(4) const dayPercent = (((dayMonitorTime - dayDownTime) / dayMonitorTime) * 100).toPrecision(4)
@ -55,9 +77,8 @@ export default function DetailBar({
<> <>
<div>{dayPercent + '% at ' + new Date(dayStart * 1000).toLocaleDateString()}</div> <div>{dayPercent + '% at ' + new Date(dayStart * 1000).toLocaleDateString()}</div>
{dayDownTime > 0 && ( {dayDownTime > 0 && (
<div>{`Down for ${moment.preciseDiff(moment(0), moment(dayDownTime * 1000))}`}</div> <div>{`Down for ${moment.preciseDiff(moment(0), moment(dayDownTime * 1000))} (click for detail)`}</div>
)} )}
{/* TODO: lantency detail for each bar */}
</> </>
) )
} }
@ -71,6 +92,17 @@ export default function DetailBar({
marginLeft: '1px', marginLeft: '1px',
marginRight: '1px', marginRight: '1px',
}} }}
onClick={() => {
if (dayDownTime > 0) {
setModalTitle(`🚨 ${monitor.name} incidents at ${new Date(dayStart * 1000).toLocaleDateString()}`)
setModelContent(
<>
{incidentReasons.map((reason) => (<div>{reason}</div>))}
</>
)
setModalOpened(true)
}
}}
/> />
</Tooltip> </Tooltip>
) )
@ -78,6 +110,9 @@ export default function DetailBar({
return ( return (
<> <>
<Modal opened={modalOpened} onClose={() => setModalOpened(false)} title={modalTitle} size={'40em'}>
{modelContent}
</Modal>
<Box <Box
style={{ style={{
display: 'flex', display: 'flex',