mirror of
https://github.com/Buriburizaem0n/nezha-dash-v1.git
synced 2026-05-06 05:48:41 +00:00
feat: enhance ServerDetailChart with new chart tooltips and sync functionality
This commit is contained in:
@@ -11,11 +11,21 @@ import {
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { type ChartConfig, ChartContainer } from "@/components/ui/chart";
|
||||
import {
|
||||
type ChartConfig,
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from "@/components/ui/chart";
|
||||
import { useWebSocketContext } from "@/hooks/use-websocket-context";
|
||||
import { formatBytes } from "@/lib/format";
|
||||
import { fetchLoginUser, fetchServerMetrics } from "@/lib/nezha-api";
|
||||
import { cn, formatNezhaInfo, formatRelativeTime } from "@/lib/utils";
|
||||
import {
|
||||
cn,
|
||||
formatNezhaInfo,
|
||||
formatRelativeTime,
|
||||
formatTime,
|
||||
} from "@/lib/utils";
|
||||
import type {
|
||||
MetricPeriod,
|
||||
NezhaServer,
|
||||
@@ -84,7 +94,7 @@ function PeriodSelector({
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex gap-1 mb-3 flex-wrap">
|
||||
<div className="flex gap-1 mb-3 flex-wrap -mt-5">
|
||||
{periods.map((period) => {
|
||||
// Only realtime and 1d are available for non-logged-in users
|
||||
const isLocked =
|
||||
@@ -427,6 +437,7 @@ function GpuChart({
|
||||
</div>
|
||||
) : (
|
||||
<AreaChart
|
||||
syncId="serverDetailCharts"
|
||||
accessibilityLayer
|
||||
data={displayData}
|
||||
margin={{
|
||||
@@ -453,6 +464,27 @@ function GpuChart({
|
||||
domain={[0, 100]}
|
||||
tickFormatter={(value) => `${value}%`}
|
||||
/>
|
||||
<ChartTooltip
|
||||
isAnimationActive={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
indicator="dot"
|
||||
labelFormatter={(_, payload) => {
|
||||
return formatTime(
|
||||
Number(payload[0]?.payload?.timeStamp),
|
||||
);
|
||||
}}
|
||||
formatter={(value) => (
|
||||
<div className="flex flex-1 items-center justify-between leading-none">
|
||||
<span className="text-muted-foreground">GPU</span>
|
||||
<span className="ml-2 font-medium text-foreground tabular-nums">
|
||||
{Number(value).toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Area
|
||||
isAnimationActive={false}
|
||||
dataKey="gpu"
|
||||
@@ -607,6 +639,7 @@ function CpuChart({
|
||||
</div>
|
||||
) : (
|
||||
<AreaChart
|
||||
syncId="serverDetailCharts"
|
||||
accessibilityLayer
|
||||
data={displayData}
|
||||
margin={{
|
||||
@@ -633,6 +666,27 @@ function CpuChart({
|
||||
domain={[0, 100]}
|
||||
tickFormatter={(value) => `${value}%`}
|
||||
/>
|
||||
<ChartTooltip
|
||||
isAnimationActive={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
indicator="dot"
|
||||
labelFormatter={(_, payload) => {
|
||||
return formatTime(
|
||||
Number(payload[0]?.payload?.timeStamp),
|
||||
);
|
||||
}}
|
||||
formatter={(value) => (
|
||||
<div className="flex flex-1 items-center justify-between leading-none">
|
||||
<span className="text-muted-foreground">CPU</span>
|
||||
<span className="ml-2 font-medium text-foreground tabular-nums">
|
||||
{Number(value).toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Area
|
||||
isAnimationActive={false}
|
||||
dataKey="cpu"
|
||||
@@ -783,6 +837,7 @@ function ProcessChart({
|
||||
</div>
|
||||
) : (
|
||||
<AreaChart
|
||||
syncId="serverDetailCharts"
|
||||
accessibilityLayer
|
||||
data={displayData}
|
||||
margin={{
|
||||
@@ -807,6 +862,29 @@ function ProcessChart({
|
||||
mirror={true}
|
||||
tickMargin={-15}
|
||||
/>
|
||||
<ChartTooltip
|
||||
isAnimationActive={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
indicator={"dot"}
|
||||
labelFormatter={(_, payload) => {
|
||||
return formatTime(
|
||||
Number(payload[0]?.payload?.timeStamp),
|
||||
);
|
||||
}}
|
||||
formatter={(value) => (
|
||||
<div className="flex flex-1 items-center justify-between leading-none">
|
||||
<span className="text-muted-foreground">
|
||||
{t("serverDetailChart.process")}
|
||||
</span>
|
||||
<span className="ml-2 font-medium text-foreground tabular-nums">
|
||||
{Number(value).toFixed(0)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Area
|
||||
isAnimationActive={false}
|
||||
dataKey="process"
|
||||
@@ -1043,6 +1121,7 @@ function MemChart({
|
||||
</div>
|
||||
) : (
|
||||
<AreaChart
|
||||
syncId="serverDetailCharts"
|
||||
accessibilityLayer
|
||||
data={displayData}
|
||||
margin={{
|
||||
@@ -1069,6 +1148,35 @@ function MemChart({
|
||||
domain={[0, 100]}
|
||||
tickFormatter={(value) => `${value}%`}
|
||||
/>
|
||||
<ChartTooltip
|
||||
isAnimationActive={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
indicator="dot"
|
||||
labelFormatter={(_, payload) => {
|
||||
return formatTime(
|
||||
Number(payload[0]?.payload?.timeStamp),
|
||||
);
|
||||
}}
|
||||
formatter={(value, name) => {
|
||||
const label =
|
||||
name === "mem"
|
||||
? t("serverDetailChart.mem")
|
||||
: t("serverDetailChart.swap");
|
||||
return (
|
||||
<div className="flex flex-1 items-center justify-between leading-none">
|
||||
<span className="text-muted-foreground">
|
||||
{label}
|
||||
</span>
|
||||
<span className="ml-2 font-medium text-foreground tabular-nums">
|
||||
{Number(value).toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Area
|
||||
isAnimationActive={false}
|
||||
dataKey="mem"
|
||||
@@ -1243,6 +1351,7 @@ function DiskChart({
|
||||
</div>
|
||||
) : (
|
||||
<AreaChart
|
||||
syncId="serverDetailCharts"
|
||||
accessibilityLayer
|
||||
data={displayData}
|
||||
margin={{
|
||||
@@ -1269,6 +1378,29 @@ function DiskChart({
|
||||
domain={[0, 100]}
|
||||
tickFormatter={(value) => `${value}%`}
|
||||
/>
|
||||
<ChartTooltip
|
||||
isAnimationActive={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
indicator="dot"
|
||||
labelFormatter={(_, payload) => {
|
||||
return formatTime(
|
||||
Number(payload[0]?.payload?.timeStamp),
|
||||
);
|
||||
}}
|
||||
formatter={(value) => (
|
||||
<div className="flex flex-1 items-center justify-between leading-none">
|
||||
<span className="text-muted-foreground">
|
||||
{t("serverDetailChart.disk")}
|
||||
</span>
|
||||
<span className="ml-2 font-medium text-foreground tabular-nums">
|
||||
{Number(value).toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Area
|
||||
isAnimationActive={false}
|
||||
dataKey="disk"
|
||||
@@ -1490,6 +1622,7 @@ function NetworkChart({
|
||||
</div>
|
||||
) : (
|
||||
<LineChart
|
||||
syncId="serverDetailCharts"
|
||||
accessibilityLayer
|
||||
data={displayData}
|
||||
margin={{
|
||||
@@ -1519,6 +1652,35 @@ function NetworkChart({
|
||||
domain={[1, maxDownload]}
|
||||
tickFormatter={(value) => `${value.toFixed(0)}M/s`}
|
||||
/>
|
||||
<ChartTooltip
|
||||
isAnimationActive={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
indicator="dot"
|
||||
labelFormatter={(_, payload) => {
|
||||
return formatTime(
|
||||
Number(payload[0]?.payload?.timeStamp),
|
||||
);
|
||||
}}
|
||||
formatter={(value, name) => {
|
||||
const label =
|
||||
name === "upload"
|
||||
? t("serverDetailChart.upload")
|
||||
: t("serverDetailChart.download");
|
||||
return (
|
||||
<div className="flex flex-1 items-center justify-between leading-none">
|
||||
<span className="text-muted-foreground">
|
||||
{label}
|
||||
</span>
|
||||
<span className="ml-2 font-medium text-foreground tabular-nums">
|
||||
{Number(value).toFixed(2)} MB/s
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Line
|
||||
isAnimationActive={false}
|
||||
dataKey="upload"
|
||||
@@ -1721,6 +1883,7 @@ function ConnectChart({
|
||||
</div>
|
||||
) : (
|
||||
<LineChart
|
||||
syncId="serverDetailCharts"
|
||||
accessibilityLayer
|
||||
data={displayData}
|
||||
margin={{
|
||||
@@ -1747,6 +1910,32 @@ function ConnectChart({
|
||||
type="number"
|
||||
interval="preserveStartEnd"
|
||||
/>
|
||||
<ChartTooltip
|
||||
isAnimationActive={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
indicator="dot"
|
||||
labelFormatter={(_, payload) => {
|
||||
return formatTime(
|
||||
Number(payload[0]?.payload?.timeStamp),
|
||||
);
|
||||
}}
|
||||
formatter={(value, name) => {
|
||||
const label = name === "tcp" ? "TCP" : "UDP";
|
||||
return (
|
||||
<div className="flex flex-1 items-center justify-between leading-none">
|
||||
<span className="text-muted-foreground">
|
||||
{label}
|
||||
</span>
|
||||
<span className="ml-2 font-medium text-foreground tabular-nums">
|
||||
{Number(value).toFixed(0)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Line
|
||||
isAnimationActive={false}
|
||||
dataKey="tcp"
|
||||
|
||||
Reference in New Issue
Block a user