Skip to content

Commit 6dae6cc

Browse files
committed
feat: enhance ServerDetailChart with new chart tooltips and sync functionality
1 parent 65b32ec commit 6dae6cc

File tree

2 files changed

+247
-23
lines changed

2 files changed

+247
-23
lines changed

src/components/ServerDetailChart.tsx

Lines changed: 192 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,21 @@ import {
1111
YAxis,
1212
} from "recharts";
1313
import { Card, CardContent } from "@/components/ui/card";
14-
import { type ChartConfig, ChartContainer } from "@/components/ui/chart";
14+
import {
15+
type ChartConfig,
16+
ChartContainer,
17+
ChartTooltip,
18+
ChartTooltipContent,
19+
} from "@/components/ui/chart";
1520
import { useWebSocketContext } from "@/hooks/use-websocket-context";
1621
import { formatBytes } from "@/lib/format";
1722
import { fetchLoginUser, fetchServerMetrics } from "@/lib/nezha-api";
18-
import { cn, formatNezhaInfo, formatRelativeTime } from "@/lib/utils";
23+
import {
24+
cn,
25+
formatNezhaInfo,
26+
formatRelativeTime,
27+
formatTime,
28+
} from "@/lib/utils";
1929
import type {
2030
MetricPeriod,
2131
NezhaServer,
@@ -84,7 +94,7 @@ function PeriodSelector({
8494
];
8595

8696
return (
87-
<div className="flex gap-1 mb-3 flex-wrap">
97+
<div className="flex gap-1 mb-3 flex-wrap -mt-5">
8898
{periods.map((period) => {
8999
// Only realtime and 1d are available for non-logged-in users
90100
const isLocked =
@@ -427,6 +437,7 @@ function GpuChart({
427437
</div>
428438
) : (
429439
<AreaChart
440+
syncId="serverDetailCharts"
430441
accessibilityLayer
431442
data={displayData}
432443
margin={{
@@ -453,6 +464,27 @@ function GpuChart({
453464
domain={[0, 100]}
454465
tickFormatter={(value) => `${value}%`}
455466
/>
467+
<ChartTooltip
468+
isAnimationActive={false}
469+
content={
470+
<ChartTooltipContent
471+
indicator="dot"
472+
labelFormatter={(_, payload) => {
473+
return formatTime(
474+
Number(payload[0]?.payload?.timeStamp),
475+
);
476+
}}
477+
formatter={(value) => (
478+
<div className="flex flex-1 items-center justify-between leading-none">
479+
<span className="text-muted-foreground">GPU</span>
480+
<span className="ml-2 font-medium text-foreground tabular-nums">
481+
{Number(value).toFixed(1)}%
482+
</span>
483+
</div>
484+
)}
485+
/>
486+
}
487+
/>
456488
<Area
457489
isAnimationActive={false}
458490
dataKey="gpu"
@@ -607,6 +639,7 @@ function CpuChart({
607639
</div>
608640
) : (
609641
<AreaChart
642+
syncId="serverDetailCharts"
610643
accessibilityLayer
611644
data={displayData}
612645
margin={{
@@ -633,6 +666,27 @@ function CpuChart({
633666
domain={[0, 100]}
634667
tickFormatter={(value) => `${value}%`}
635668
/>
669+
<ChartTooltip
670+
isAnimationActive={false}
671+
content={
672+
<ChartTooltipContent
673+
indicator="dot"
674+
labelFormatter={(_, payload) => {
675+
return formatTime(
676+
Number(payload[0]?.payload?.timeStamp),
677+
);
678+
}}
679+
formatter={(value) => (
680+
<div className="flex flex-1 items-center justify-between leading-none">
681+
<span className="text-muted-foreground">CPU</span>
682+
<span className="ml-2 font-medium text-foreground tabular-nums">
683+
{Number(value).toFixed(1)}%
684+
</span>
685+
</div>
686+
)}
687+
/>
688+
}
689+
/>
636690
<Area
637691
isAnimationActive={false}
638692
dataKey="cpu"
@@ -783,6 +837,7 @@ function ProcessChart({
783837
</div>
784838
) : (
785839
<AreaChart
840+
syncId="serverDetailCharts"
786841
accessibilityLayer
787842
data={displayData}
788843
margin={{
@@ -807,6 +862,29 @@ function ProcessChart({
807862
mirror={true}
808863
tickMargin={-15}
809864
/>
865+
<ChartTooltip
866+
isAnimationActive={false}
867+
content={
868+
<ChartTooltipContent
869+
indicator={"dot"}
870+
labelFormatter={(_, payload) => {
871+
return formatTime(
872+
Number(payload[0]?.payload?.timeStamp),
873+
);
874+
}}
875+
formatter={(value) => (
876+
<div className="flex flex-1 items-center justify-between leading-none">
877+
<span className="text-muted-foreground">
878+
{t("serverDetailChart.process")}
879+
</span>
880+
<span className="ml-2 font-medium text-foreground tabular-nums">
881+
{Number(value).toFixed(0)}
882+
</span>
883+
</div>
884+
)}
885+
/>
886+
}
887+
/>
810888
<Area
811889
isAnimationActive={false}
812890
dataKey="process"
@@ -1043,6 +1121,7 @@ function MemChart({
10431121
</div>
10441122
) : (
10451123
<AreaChart
1124+
syncId="serverDetailCharts"
10461125
accessibilityLayer
10471126
data={displayData}
10481127
margin={{
@@ -1069,6 +1148,35 @@ function MemChart({
10691148
domain={[0, 100]}
10701149
tickFormatter={(value) => `${value}%`}
10711150
/>
1151+
<ChartTooltip
1152+
isAnimationActive={false}
1153+
content={
1154+
<ChartTooltipContent
1155+
indicator="dot"
1156+
labelFormatter={(_, payload) => {
1157+
return formatTime(
1158+
Number(payload[0]?.payload?.timeStamp),
1159+
);
1160+
}}
1161+
formatter={(value, name) => {
1162+
const label =
1163+
name === "mem"
1164+
? t("serverDetailChart.mem")
1165+
: t("serverDetailChart.swap");
1166+
return (
1167+
<div className="flex flex-1 items-center justify-between leading-none">
1168+
<span className="text-muted-foreground">
1169+
{label}
1170+
</span>
1171+
<span className="ml-2 font-medium text-foreground tabular-nums">
1172+
{Number(value).toFixed(1)}%
1173+
</span>
1174+
</div>
1175+
);
1176+
}}
1177+
/>
1178+
}
1179+
/>
10721180
<Area
10731181
isAnimationActive={false}
10741182
dataKey="mem"
@@ -1243,6 +1351,7 @@ function DiskChart({
12431351
</div>
12441352
) : (
12451353
<AreaChart
1354+
syncId="serverDetailCharts"
12461355
accessibilityLayer
12471356
data={displayData}
12481357
margin={{
@@ -1269,6 +1378,29 @@ function DiskChart({
12691378
domain={[0, 100]}
12701379
tickFormatter={(value) => `${value}%`}
12711380
/>
1381+
<ChartTooltip
1382+
isAnimationActive={false}
1383+
content={
1384+
<ChartTooltipContent
1385+
indicator="dot"
1386+
labelFormatter={(_, payload) => {
1387+
return formatTime(
1388+
Number(payload[0]?.payload?.timeStamp),
1389+
);
1390+
}}
1391+
formatter={(value) => (
1392+
<div className="flex flex-1 items-center justify-between leading-none">
1393+
<span className="text-muted-foreground">
1394+
{t("serverDetailChart.disk")}
1395+
</span>
1396+
<span className="ml-2 font-medium text-foreground tabular-nums">
1397+
{Number(value).toFixed(1)}%
1398+
</span>
1399+
</div>
1400+
)}
1401+
/>
1402+
}
1403+
/>
12721404
<Area
12731405
isAnimationActive={false}
12741406
dataKey="disk"
@@ -1490,6 +1622,7 @@ function NetworkChart({
14901622
</div>
14911623
) : (
14921624
<LineChart
1625+
syncId="serverDetailCharts"
14931626
accessibilityLayer
14941627
data={displayData}
14951628
margin={{
@@ -1519,6 +1652,35 @@ function NetworkChart({
15191652
domain={[1, maxDownload]}
15201653
tickFormatter={(value) => `${value.toFixed(0)}M/s`}
15211654
/>
1655+
<ChartTooltip
1656+
isAnimationActive={false}
1657+
content={
1658+
<ChartTooltipContent
1659+
indicator="dot"
1660+
labelFormatter={(_, payload) => {
1661+
return formatTime(
1662+
Number(payload[0]?.payload?.timeStamp),
1663+
);
1664+
}}
1665+
formatter={(value, name) => {
1666+
const label =
1667+
name === "upload"
1668+
? t("serverDetailChart.upload")
1669+
: t("serverDetailChart.download");
1670+
return (
1671+
<div className="flex flex-1 items-center justify-between leading-none">
1672+
<span className="text-muted-foreground">
1673+
{label}
1674+
</span>
1675+
<span className="ml-2 font-medium text-foreground tabular-nums">
1676+
{Number(value).toFixed(2)} MB/s
1677+
</span>
1678+
</div>
1679+
);
1680+
}}
1681+
/>
1682+
}
1683+
/>
15221684
<Line
15231685
isAnimationActive={false}
15241686
dataKey="upload"
@@ -1721,6 +1883,7 @@ function ConnectChart({
17211883
</div>
17221884
) : (
17231885
<LineChart
1886+
syncId="serverDetailCharts"
17241887
accessibilityLayer
17251888
data={displayData}
17261889
margin={{
@@ -1747,6 +1910,32 @@ function ConnectChart({
17471910
type="number"
17481911
interval="preserveStartEnd"
17491912
/>
1913+
<ChartTooltip
1914+
isAnimationActive={false}
1915+
content={
1916+
<ChartTooltipContent
1917+
indicator="dot"
1918+
labelFormatter={(_, payload) => {
1919+
return formatTime(
1920+
Number(payload[0]?.payload?.timeStamp),
1921+
);
1922+
}}
1923+
formatter={(value, name) => {
1924+
const label = name === "tcp" ? "TCP" : "UDP";
1925+
return (
1926+
<div className="flex flex-1 items-center justify-between leading-none">
1927+
<span className="text-muted-foreground">
1928+
{label}
1929+
</span>
1930+
<span className="ml-2 font-medium text-foreground tabular-nums">
1931+
{Number(value).toFixed(0)}
1932+
</span>
1933+
</div>
1934+
);
1935+
}}
1936+
/>
1937+
}
1938+
/>
17501939
<Line
17511940
isAnimationActive={false}
17521941
dataKey="tcp"

0 commit comments

Comments
 (0)