feat: enhance ServiceTracker with detailed uptime and delay visualization

This commit is contained in:
hamster1963 2025-02-01 19:10:04 +08:00
parent 94eb966d65
commit 0f81ef2f25
5 changed files with 93 additions and 22 deletions

View File

@ -19,10 +19,16 @@ export function ServiceTracker({ serverList }: { serverList: NezhaServer[] }) {
}) })
const processServiceData = (serviceData: ServiceData) => { const processServiceData = (serviceData: ServiceData) => {
const days = serviceData.up.map((up, index) => ({ const days = serviceData.up.map((up, index) => {
completed: up > serviceData.down[index], const totalChecks = up + serviceData.down[index]
date: new Date(Date.now() - (29 - index) * 24 * 60 * 60 * 1000), const dailyUptime = totalChecks > 0 ? (up / totalChecks) * 100 : 0
})) return {
completed: up > serviceData.down[index],
date: new Date(Date.now() - (29 - index) * 24 * 60 * 60 * 1000),
uptime: dailyUptime,
delay: serviceData.delay[index] || 0
}
})
const totalUp = serviceData.up.reduce((a, b) => a + b, 0) const totalUp = serviceData.up.reduce((a, b) => a + b, 0)
const totalChecks = serviceData.up.reduce((a, b) => a + b, 0) + serviceData.down.reduce((a, b) => a + b, 0) const totalChecks = serviceData.up.reduce((a, b) => a + b, 0) + serviceData.down.reduce((a, b) => a + b, 0)

View File

@ -1,13 +1,20 @@
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import React from "react" import React from "react"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { Separator } from "./ui/separator" import { Separator } from "./ui/separator"
interface ServiceTrackerProps { interface ServiceTrackerProps {
days: Array<{ days: Array<{
completed: boolean completed: boolean
date?: Date date?: Date
uptime: number
delay: number
}> }>
className?: string className?: string
title?: string title?: string
@ -18,6 +25,25 @@ interface ServiceTrackerProps {
export const ServiceTrackerClient: React.FC<ServiceTrackerProps> = ({ days, className, title, uptime = 100, avgDelay = 0 }) => { export const ServiceTrackerClient: React.FC<ServiceTrackerProps> = ({ days, className, title, uptime = 100, avgDelay = 0 }) => {
const { t } = useTranslation() const { t } = useTranslation()
const customBackgroundImage = (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined const customBackgroundImage = (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined
const getUptimeColor = (uptime: number) => {
if (uptime >= 99) return "text-emerald-500"
if (uptime >= 95) return "text-amber-500"
return "text-rose-500"
}
const getDelayColor = (delay: number) => {
if (delay < 100) return "text-emerald-500"
if (delay < 300) return "text-amber-500"
return "text-rose-500"
}
const getStatusColor = (uptime: number) => {
if (uptime >= 99) return "bg-emerald-500"
if (uptime >= 95) return "bg-amber-500"
return "bg-rose-500"
}
return ( return (
<div <div
className={cn( className={cn(
@ -30,27 +56,63 @@ export const ServiceTrackerClient: React.FC<ServiceTrackerProps> = ({ days, clas
> >
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-5 h-5 rounded-full bg-green-600 flex items-center justify-center"> <div className={cn("w-2.5 h-2.5 rounded-full transition-colors", getStatusColor(uptime))} />
<div className="w-3 h-3 rounded-full bg-white dark:bg-black" />
</div>
<span className="font-medium text-sm">{title}</span> <span className="font-medium text-sm">{title}</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-3">
<span className="text-stone-600 dark:text-stone-400 font-medium text-sm">{avgDelay.toFixed(0)}ms</span> <span className={cn(
<Separator className="h-4 mx-0" orientation="vertical" /> "font-medium text-sm transition-colors",
<span className="text-green-600 font-medium text-sm"> getDelayColor(avgDelay)
)}>
{avgDelay.toFixed(0)}ms
</span>
<Separator className="h-4" orientation="vertical" />
<span className={cn(
"font-medium text-sm transition-colors",
getUptimeColor(uptime)
)}>
{uptime.toFixed(1)}% {t("serviceTracker.uptime")} {uptime.toFixed(1)}% {t("serviceTracker.uptime")}
</span> </span>
</div> </div>
</div> </div>
<div className="flex gap-[2px]"> <div className="flex gap-[3px] bg-muted/30 p-1 rounded-lg">
{days.map((day, index) => ( {days.map((day, index) => (
<div <TooltipProvider delayDuration={50} key={index}>
key={index} <Tooltip>
className={cn("flex-1 h-6 rounded-[5px] transition-colors", day.completed ? "bg-green-600" : "bg-red-500/60")} <TooltipTrigger asChild>
title={day.date ? day.date.toLocaleDateString() : `Day ${index + 1}`} <div
/> className={cn(
"relative flex-1 h-7 rounded-[4px] transition-all duration-200 cursor-help",
"before:absolute before:inset-0 before:rounded-[4px] before:opacity-0 hover:before:opacity-100 before:bg-white/10 before:transition-opacity",
"after:absolute after:inset-0 after:rounded-[4px] after:shadow-[inset_0_1px_theme(colors.white/10%)]",
day.completed
? "bg-gradient-to-b from-green-500/90 to-green-600 shadow-[0_1px_2px_theme(colors.green.600/30%)]"
: "bg-gradient-to-b from-red-500/80 to-red-600/90 shadow-[0_1px_2px_theme(colors.red.600/30%)]"
)}
/>
</TooltipTrigger>
<TooltipContent className="p-0 overflow-hidden">
<div className="px-3 py-2 bg-popover">
<p className="font-medium text-sm mb-2">{day.date?.toLocaleDateString()}</p>
<div className="space-y-1.5">
<div className="flex items-center justify-between gap-3">
<span className="text-xs text-muted-foreground">{t("serviceTracker.uptime")}:</span>
<span className={cn("text-xs font-medium", day.uptime > 95 ? "text-green-500" : "text-red-500")}>
{day.uptime.toFixed(1)}%
</span>
</div>
<div className="flex items-center justify-between gap-3">
<span className="text-xs text-muted-foreground">{t("serviceTracker.delay")}:</span>
<span className={cn("text-xs font-medium", day.delay < 100 ? "text-green-500" : day.delay < 300 ? "text-yellow-500" : "text-red-500")}>
{day.delay.toFixed(0)}ms
</span>
</div>
</div>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
))} ))}
</div> </div>

View File

@ -46,6 +46,7 @@
"serviceTracker": { "serviceTracker": {
"noService": "No service data", "noService": "No service data",
"uptime": "Uptime", "uptime": "Uptime",
"delay": "Delay",
"daysAgo": "days ago", "daysAgo": "days ago",
"today": "Today", "today": "Today",
"loading": "Loading..." "loading": "Loading..."

View File

@ -44,8 +44,9 @@
"nextUpdate": "下次更新" "nextUpdate": "下次更新"
}, },
"serviceTracker": { "serviceTracker": {
"noService": "没有服务监测数据", "noService": "无服务数据",
"uptime": "可用率", "uptime": "在线率",
"delay": "延迟",
"daysAgo": "天前", "daysAgo": "天前",
"today": "今天", "today": "今天",
"loading": "加载中..." "loading": "加载中..."

View File

@ -44,8 +44,9 @@
"nextUpdate": "下次更新" "nextUpdate": "下次更新"
}, },
"serviceTracker": { "serviceTracker": {
"noService": "沒有服務監控數據", "noService": "無服務數據",
"uptime": "可用率", "uptime": "在線率",
"delay": "延遲",
"daysAgo": "天前", "daysAgo": "天前",
"today": "今天", "today": "今天",
"loading": "載入中..." "loading": "載入中..."