mirror of
https://github.com/hamster1963/nezha-dash.git
synced 2025-04-24 21:10:45 +08:00
feat: add detail page
This commit is contained in:
parent
78da53fbfa
commit
fe20952e7b
@ -28,9 +28,9 @@ import { useLocale } from "next-intl";
|
|||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { useCallback, useMemo } from "react";
|
||||||
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { useMemo, useCallback } from 'react';
|
|
||||||
|
|
||||||
interface ResultItem {
|
interface ResultItem {
|
||||||
created_at: number;
|
created_at: number;
|
||||||
@ -64,7 +64,6 @@ export function NetworkChartClient({ server_id }: { server_id: number }) {
|
|||||||
|
|
||||||
if (!data) return <NetworkChartLoading />;
|
if (!data) return <NetworkChartLoading />;
|
||||||
|
|
||||||
|
|
||||||
const transformedData = transformData(data);
|
const transformedData = transformData(data);
|
||||||
|
|
||||||
const formattedData = formatData(data);
|
const formattedData = formatData(data);
|
||||||
@ -109,32 +108,40 @@ export const NetworkChart = React.memo(function NetworkChart({
|
|||||||
|
|
||||||
const [activeChart, setActiveChart] = React.useState(defaultChart);
|
const [activeChart, setActiveChart] = React.useState(defaultChart);
|
||||||
|
|
||||||
const handleButtonClick = useCallback((chart: string) => {
|
const handleButtonClick = useCallback(
|
||||||
setActiveChart(prev => prev === chart ? defaultChart : chart);
|
(chart: string) => {
|
||||||
}, [defaultChart]);
|
setActiveChart((prev) => (prev === chart ? defaultChart : chart));
|
||||||
|
},
|
||||||
|
[defaultChart],
|
||||||
|
);
|
||||||
|
|
||||||
const getColorByIndex = useCallback((chart: string) => {
|
const getColorByIndex = useCallback(
|
||||||
const index = chartDataKey.indexOf(chart);
|
(chart: string) => {
|
||||||
return `hsl(var(--chart-${(index % 10) + 1}))`;
|
const index = chartDataKey.indexOf(chart);
|
||||||
}, [chartDataKey]);
|
return `hsl(var(--chart-${(index % 10) + 1}))`;
|
||||||
|
},
|
||||||
|
[chartDataKey],
|
||||||
|
);
|
||||||
|
|
||||||
const chartButtons = useMemo(() => (
|
const chartButtons = useMemo(
|
||||||
chartDataKey.map((key) => (
|
() =>
|
||||||
<button
|
chartDataKey.map((key) => (
|
||||||
key={key}
|
<button
|
||||||
data-active={activeChart === key}
|
key={key}
|
||||||
className={`relative z-30 flex flex-1 flex-col justify-center gap-1 border-b px-6 py-4 text-left data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-6`}
|
data-active={activeChart === key}
|
||||||
onClick={() => handleButtonClick(key)}
|
className={`relative z-30 flex flex-1 flex-col justify-center gap-1 border-b px-6 py-4 text-left data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-6`}
|
||||||
>
|
onClick={() => handleButtonClick(key)}
|
||||||
<span className="whitespace-nowrap text-xs text-muted-foreground">
|
>
|
||||||
{key}
|
<span className="whitespace-nowrap text-xs text-muted-foreground">
|
||||||
</span>
|
{key}
|
||||||
<span className="text-md font-bold leading-none sm:text-lg">
|
</span>
|
||||||
{chartData[key][chartData[key].length - 1].avg_delay.toFixed(2)}ms
|
<span className="text-md font-bold leading-none sm:text-lg">
|
||||||
</span>
|
{chartData[key][chartData[key].length - 1].avg_delay.toFixed(2)}ms
|
||||||
</button>
|
</span>
|
||||||
))
|
</button>
|
||||||
), [chartDataKey, activeChart, chartData, handleButtonClick]);
|
)),
|
||||||
|
[chartDataKey, activeChart, chartData, handleButtonClick],
|
||||||
|
);
|
||||||
|
|
||||||
const chartLines = useMemo(() => {
|
const chartLines = useMemo(() => {
|
||||||
if (activeChart !== defaultChart) {
|
if (activeChart !== defaultChart) {
|
||||||
@ -180,9 +187,7 @@ export const NetworkChart = React.memo(function NetworkChart({
|
|||||||
{chartDataKey.length} {t("ServerMonitorCount")}
|
{chartDataKey.length} {t("ServerMonitorCount")}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap">
|
<div className="flex flex-wrap">{chartButtons}</div>
|
||||||
{chartButtons}
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="px-2 sm:p-6">
|
<CardContent className="px-2 sm:p-6">
|
||||||
<ChartContainer
|
<ChartContainer
|
||||||
@ -191,7 +196,11 @@ export const NetworkChart = React.memo(function NetworkChart({
|
|||||||
>
|
>
|
||||||
<LineChart
|
<LineChart
|
||||||
accessibilityLayer
|
accessibilityLayer
|
||||||
data={activeChart === defaultChart ? formattedData : chartData[activeChart]}
|
data={
|
||||||
|
activeChart === defaultChart
|
||||||
|
? formattedData
|
||||||
|
: chartData[activeChart]
|
||||||
|
}
|
||||||
margin={{ left: 12, right: 12 }}
|
margin={{ left: 12, right: 12 }}
|
||||||
>
|
>
|
||||||
<CartesianGrid vertical={false} />
|
<CartesianGrid vertical={false} />
|
||||||
@ -235,7 +244,6 @@ export const NetworkChart = React.memo(function NetworkChart({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const transformData = (data: NezhaAPIMonitor[]) => {
|
const transformData = (data: NezhaAPIMonitor[]) => {
|
||||||
const monitorData: ServerMonitorChart = {};
|
const monitorData: ServerMonitorChart = {};
|
||||||
|
|
||||||
@ -255,7 +263,7 @@ const transformData = (data: NezhaAPIMonitor[]) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return monitorData;
|
return monitorData;
|
||||||
}
|
};
|
||||||
|
|
||||||
const formatData = (rawData: NezhaAPIMonitor[]) => {
|
const formatData = (rawData: NezhaAPIMonitor[]) => {
|
||||||
const result: { [time: number]: ResultItem } = {};
|
const result: { [time: number]: ResultItem } = {};
|
||||||
|
7
app/[locale]/(main)/detail/[id]/page.tsx
Normal file
7
app/[locale]/(main)/detail/[id]/page.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export default function Page({ params }: { params: { id: string } }) {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto grid w-full max-w-5xl gap-4 md:gap-6">
|
||||||
|
{params.id}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -36,35 +36,39 @@ export default function ServerCard({
|
|||||||
"flex flex-col items-center justify-start gap-3 p-3 md:px-5 lg:flex-row"
|
"flex flex-col items-center justify-start gap-3 p-3 md:px-5 lg:flex-row"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Popover>
|
{/* <Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<section
|
|
||||||
className="grid items-center gap-2 lg:w-28"
|
|
||||||
style={{ gridTemplateColumns: "auto 1fr auto" }}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"flex items-center justify-center",
|
|
||||||
showFlag ? "min-w-[17px]" : "min-w-0",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{showFlag ? <ServerFlag country_code={country_code} /> : null}
|
|
||||||
</div>
|
|
||||||
<p
|
|
||||||
className={cn(
|
|
||||||
"break-all font-bold tracking-tight",
|
|
||||||
showFlag ? "text-xs" : "text-sm",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</p>
|
|
||||||
<span className="h-2 w-2 shrink-0 rounded-full bg-green-500 self-center"></span>
|
|
||||||
</section>
|
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent side="top">
|
<PopoverContent side="top">
|
||||||
<ServerCardPopover status={props.status} host={props.host} />
|
<ServerCardPopover status={props.status} host={props.host} />
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover> */}
|
||||||
|
<section
|
||||||
|
className="grid items-center gap-2 lg:w-28 cursor-pointer"
|
||||||
|
style={{ gridTemplateColumns: "auto 1fr auto" }}
|
||||||
|
onClick={() => {
|
||||||
|
router.push(`/${locale}/detail/${id}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex items-center justify-center",
|
||||||
|
showFlag ? "min-w-[17px]" : "min-w-0",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{showFlag ? <ServerFlag country_code={country_code} /> : null}
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
className={cn(
|
||||||
|
"break-all font-bold tracking-tight",
|
||||||
|
showFlag ? "text-xs" : "text-sm",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</p>
|
||||||
|
<span className="h-2 w-2 shrink-0 rounded-full bg-green-500 self-center"></span>
|
||||||
|
</section>
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push(`/${locale}/${id}`);
|
router.push(`/${locale}/${id}`);
|
||||||
|
Loading…
Reference in New Issue
Block a user