From 9e2226f2baf25a3ce48f7c33269419ac2975cec9 Mon Sep 17 00:00:00 2001 From: hamster1963 <1410514192@qq.com> Date: Fri, 18 Oct 2024 16:46:29 +0800 Subject: [PATCH] feat: add detail card --- .../ClientComponents/ServerDetailClient.tsx | 287 ++++++++++++++---- app/[locale]/(main)/detail/[id]/page.tsx | 4 +- app/api/detail/route.ts | 33 +- .../ui/animated-circular-progress-bar.tsx | 102 +++++++ components/ui/badge.tsx | 2 +- lib/serverFetch.tsx | 26 +- prettier.config.js | 1 + 7 files changed, 369 insertions(+), 86 deletions(-) create mode 100644 components/ui/animated-circular-progress-bar.tsx diff --git a/app/[locale]/(main)/ClientComponents/ServerDetailClient.tsx b/app/[locale]/(main)/ClientComponents/ServerDetailClient.tsx index 357b96d..84f8c8b 100644 --- a/app/[locale]/(main)/ClientComponents/ServerDetailClient.tsx +++ b/app/[locale]/(main)/ClientComponents/ServerDetailClient.tsx @@ -1,62 +1,243 @@ "use client"; -import useSWR from "swr"; import { NezhaAPISafe } from "@/app/[locale]/types/nezha-api"; -import { nezhaFetcher } from "@/lib/utils"; -import getEnv from "@/lib/env-entry"; -import { useRouter } from "next/navigation"; -import { useLocale } from "next-intl"; import { BackIcon } from "@/components/Icon"; -import { Card, CardContent } from "@/components/ui/card"; +import AnimatedCircularProgressBar from "@/components/ui/animated-circular-progress-bar"; import { Badge } from "@/components/ui/badge"; +import { Card, CardContent } from "@/components/ui/card"; +import getEnv from "@/lib/env-entry"; +import { cn, nezhaFetcher } from "@/lib/utils"; +import { useLocale } from "next-intl"; +import { useRouter } from "next/navigation"; +import useSWR from "swr"; -export default function ServerDetailClient({ server_id }: { server_id: number }) { - const router = useRouter(); - const locale = useLocale(); - const { data, error } = useSWR( - `/api/detail?server_id=${server_id}`, - nezhaFetcher, - { - refreshInterval: - Number(getEnv("NEXT_PUBLIC_NezhaFetchInterval")) || 5000, - }, - ); +export default function ServerDetailClient({ + server_id, +}: { + server_id: number; +}) { + const router = useRouter(); + const locale = useLocale(); + const { data, error } = useSWR( + `/api/detail?server_id=${server_id}`, + nezhaFetcher, + { + refreshInterval: Number(getEnv("NEXT_PUBLIC_NezhaFetchInterval")) || 5000, + }, + ); + if (error) { return ( -
-
{ - router.push(`/${locale}/`); - }} - className="flex flex-none cursor-pointer font-semibold leading-none items-center break-all tracking-tight gap-0.5 text-xl" - > - - HomeDash -
-
- - -
-

- ID -

-
{server_id}
-
-
-
- - -
-

- Tag -

- {data?.tag} -
-
-
-
+ <> +
+

{error.message}

+

+ {/* {t("chart_fetch_error_message")} */} + fetch_error_message +

- ) + + ); + } + if (!data) return null; + return ( +
+
{ + router.push(`/${locale}/`); + }} + className="flex flex-none cursor-pointer font-semibold leading-none items-center break-all tracking-tight gap-0.5 text-xl" + > + + {data?.name} +
+
+ + +
+

Status

+ + {data?.online_status ? "Online" : "Offline"} + +
+
+
+ + +
+

Uptime

+ + {" "} + {(data?.status.Uptime / 86400).toFixed(0)} Days{" "} + +
+
+
- - -} \ No newline at end of file + + +
+

Tag

+ + {" "} + {data?.tag || "Unknown"}{" "} + +
+
+
+ + +
+

Arch

+ + {" "} + {data?.host.Arch || "Unknown"}{" "} + +
+
+
+ + +
+

Version

+ + {" "} + {data?.host.Version || "Unknown"}{" "} + +
+
+
+
+
+ + +
+

System

+ {data?.host.Platform ? ( +
+ {" "} + {data?.host.Platform || "Unknown"} -{" "} + {data?.host.PlatformVersion}{" "} +
+ ) : ( +
Unknown
+ )} +
+
+
+ + +
+

CPU

+ {data?.host.CPU ? ( +
+ {" "} + {data?.host.CPU || "Unknown"} +
+ ) : ( +
Unknown
+ )} +
+
+
+
+
+ + +
+

CPU

+

+ {data?.status.CPU.toFixed(0)}% +

+ +
+
+
+ + +
+

Mem

+

+ {((data?.status.MemUsed / data?.host.MemTotal) * 100).toFixed( + 0, + )} + % +

+ +
+
+
+ + +
+

Swap

+

+ {data?.status.SwapUsed + ? ( + (data?.status.SwapUsed / data?.host.SwapTotal) * + 100 + ).toFixed(0) + : 0} + % +

+ +
+
+
+ + +
+

Disk

+

+ {((data?.status.DiskUsed / data?.host.DiskTotal) * 100).toFixed( + 0, + )} + % +

+ +
+
+
+
+
+ ); +} diff --git a/app/[locale]/(main)/detail/[id]/page.tsx b/app/[locale]/(main)/detail/[id]/page.tsx index a1f10cd..ad85bc9 100644 --- a/app/[locale]/(main)/detail/[id]/page.tsx +++ b/app/[locale]/(main)/detail/[id]/page.tsx @@ -1,7 +1,5 @@ import ServerDetailClient from "@/app/[locale]/(main)/ClientComponents/ServerDetailClient"; export default function Page({ params }: { params: { id: string } }) { - return ( - - ); + return ; } diff --git a/app/api/detail/route.ts b/app/api/detail/route.ts index 0d169a8..b0f8bc1 100644 --- a/app/api/detail/route.ts +++ b/app/api/detail/route.ts @@ -10,21 +10,20 @@ interface NezhaDataResponse { } export async function GET(req: Request) { - const { searchParams } = new URL(req.url); - const server_id = searchParams.get("server_id"); - if (!server_id) { - return NextResponse.json( - { error: "server_id is required" }, - { status: 400 }, - ); - } - const response = (await GetServerDetail({ - server_id: parseInt(server_id), - })) as NezhaDataResponse; - if (response.error) { - console.log(response.error); - return NextResponse.json({ error: response.error }, { status: 400 }); - } - return NextResponse.json(response, { status: 200 }); + const { searchParams } = new URL(req.url); + const server_id = searchParams.get("server_id"); + if (!server_id) { + return NextResponse.json( + { error: "server_id is required" }, + { status: 400 }, + ); } - \ No newline at end of file + const response = (await GetServerDetail({ + server_id: parseInt(server_id), + })) as NezhaDataResponse; + if (response.error) { + console.log(response.error); + return NextResponse.json({ error: response.error }, { status: 400 }); + } + return NextResponse.json(response, { status: 200 }); +} diff --git a/components/ui/animated-circular-progress-bar.tsx b/components/ui/animated-circular-progress-bar.tsx new file mode 100644 index 0000000..bdb7b4a --- /dev/null +++ b/components/ui/animated-circular-progress-bar.tsx @@ -0,0 +1,102 @@ +import { cn } from "@/lib/utils"; + +interface Props { + max: number; + value: number; + min: number; + className?: string; +} + +export default function AnimatedCircularProgressBar({ + max = 100, + min = 0, + value = 0, + className, +}: Props) { + const circumference = 2 * Math.PI * 45; + const percentPx = circumference / 100; + const currentPercent = ((value - min) / (max - min)) * 100; + + return ( +
+ + {currentPercent <= 90 && currentPercent >= 0 && ( + + )} + + + + {currentPercent} + +
+ ); +} diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index 2cba296..7c304ec 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -3,7 +3,7 @@ import { type VariantProps, cva } from "class-variance-authority"; import * as React from "react"; const badgeVariants = cva( - "inline-flex items-center text-nowarp rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + "inline-flex items-center text-nowarp rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors pointer-events-none focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", { variants: { variant: { diff --git a/lib/serverFetch.tsx b/lib/serverFetch.tsx index 16e61a1..c41622c 100644 --- a/lib/serverFetch.tsx +++ b/lib/serverFetch.tsx @@ -145,20 +145,22 @@ export async function GetServerDetail({ server_id }: { server_id: number }) { } const timestamp = Date.now() / 1000; - const detailData = detailDataList.map((element: MakeOptional) => { - if (timestamp - element.last_active > 300) { - element.online_status = false; - } else { - element.online_status = true; - } - delete element.ipv4; - delete element.ipv6; - delete element.valid_ip; - return element; - })[0]; + const detailData = detailDataList.map( + (element: MakeOptional) => { + if (timestamp - element.last_active > 300) { + element.online_status = false; + } else { + element.online_status = true; + } + delete element.ipv4; + delete element.ipv6; + delete element.valid_ip; + return element; + }, + )[0]; return detailData; } catch (error) { return error; } -} \ No newline at end of file +} diff --git a/prettier.config.js b/prettier.config.js index d9c7057..7d1ee54 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -3,6 +3,7 @@ module.exports = { importOrder: ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"], importOrderSeparation: true, importOrderSortSpecifiers: true, + endOfLine: "auto", plugins: [ "prettier-plugin-tailwindcss", "@trivago/prettier-plugin-sort-imports",