mirror of
https://github.com/hamster1963/nezha-dash.git
synced 2025-04-24 21:10:45 +08:00
feat: detail i18n
This commit is contained in:
parent
e293d04993
commit
f6ef1877bf
@ -2,19 +2,10 @@
|
||||
|
||||
import AnimatedCircularProgressBar from "@/components/ui/animated-circular-progress-bar";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import {
|
||||
ChartConfig,
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from "@/components/ui/chart";
|
||||
import { ChartConfig, ChartContainer } from "@/components/ui/chart";
|
||||
import getEnv from "@/lib/env-entry";
|
||||
import {
|
||||
formatNezhaInfo,
|
||||
formatRelativeTime,
|
||||
formatTime,
|
||||
nezhaFetcher,
|
||||
} from "@/lib/utils";
|
||||
import { formatNezhaInfo, formatRelativeTime, nezhaFetcher } from "@/lib/utils";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Area,
|
||||
@ -67,6 +58,8 @@ export default function ServerDetailChartClient({
|
||||
}: {
|
||||
server_id: number;
|
||||
}) {
|
||||
const t = useTranslations("ServerDetailChartClient");
|
||||
|
||||
const { data, error } = useSWR<NezhaAPISafe>(
|
||||
`/api/detail?server_id=${server_id}`,
|
||||
nezhaFetcher,
|
||||
@ -81,8 +74,7 @@ export default function ServerDetailChartClient({
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<p className="text-sm font-medium opacity-40">{error.message}</p>
|
||||
<p className="text-sm font-medium opacity-40">
|
||||
{/* {t("chart_fetch_error_message")} */}
|
||||
fetch_error_message
|
||||
{t("chart_fetch_error_message")}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
@ -191,6 +183,8 @@ function CpuChart({ data }: { data: NezhaAPISafe }) {
|
||||
}
|
||||
|
||||
function ProcessChart({ data }: { data: NezhaAPISafe }) {
|
||||
const t = useTranslations("ServerDetailChartClient");
|
||||
|
||||
const [processChartData, setProcessChartData] = useState(
|
||||
[] as processChartData[],
|
||||
);
|
||||
@ -222,7 +216,7 @@ function ProcessChart({ data }: { data: NezhaAPISafe }) {
|
||||
<CardContent className="px-6 py-3">
|
||||
<section className="flex flex-col gap-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-md font-medium">Process</p>
|
||||
<p className="text-md font-medium">{t("Process")}</p>
|
||||
<section className="flex items-center gap-2">
|
||||
<p className="text-xs text-end w-10 font-medium">{process}</p>
|
||||
</section>
|
||||
@ -273,6 +267,8 @@ function ProcessChart({ data }: { data: NezhaAPISafe }) {
|
||||
}
|
||||
|
||||
function MemChart({ data }: { data: NezhaAPISafe }) {
|
||||
const t = useTranslations("ServerDetailChartClient");
|
||||
|
||||
const [memChartData, setMemChartData] = useState([] as memChartData[]);
|
||||
|
||||
const { mem, swap } = formatNezhaInfo(data);
|
||||
@ -307,7 +303,7 @@ function MemChart({ data }: { data: NezhaAPISafe }) {
|
||||
<div className="flex items-center">
|
||||
<section className="flex items-center gap-4">
|
||||
<div className="flex flex-col">
|
||||
<p className=" text-xs text-muted-foreground">Mem</p>
|
||||
<p className=" text-xs text-muted-foreground">{t("Mem")}</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<AnimatedCircularProgressBar
|
||||
className="size-3 text-[0px]"
|
||||
@ -320,7 +316,7 @@ function MemChart({ data }: { data: NezhaAPISafe }) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<p className=" text-xs text-muted-foreground">Swap</p>
|
||||
<p className=" text-xs text-muted-foreground">{t("Swap")}</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<AnimatedCircularProgressBar
|
||||
className="size-3 text-[0px]"
|
||||
@ -390,6 +386,8 @@ function MemChart({ data }: { data: NezhaAPISafe }) {
|
||||
}
|
||||
|
||||
function DiskChart({ data }: { data: NezhaAPISafe }) {
|
||||
const t = useTranslations("ServerDetailChartClient");
|
||||
|
||||
const [diskChartData, setDiskChartData] = useState([] as diskChartData[]);
|
||||
|
||||
const { disk } = formatNezhaInfo(data);
|
||||
@ -412,11 +410,11 @@ function DiskChart({ data }: { data: NezhaAPISafe }) {
|
||||
} satisfies ChartConfig;
|
||||
|
||||
return (
|
||||
<Card className=" rounded-sm">
|
||||
<Card className="rounded-sm">
|
||||
<CardContent className="px-6 py-3">
|
||||
<section className="flex flex-col gap-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-md font-medium">Disk</p>
|
||||
<p className="text-md font-medium">{t("Disk")}</p>
|
||||
<section className="flex items-center gap-2">
|
||||
<p className="text-xs text-end w-10 font-medium">
|
||||
{disk.toFixed(0)}%
|
||||
@ -478,6 +476,8 @@ function DiskChart({ data }: { data: NezhaAPISafe }) {
|
||||
}
|
||||
|
||||
function NetworkChart({ data }: { data: NezhaAPISafe }) {
|
||||
const t = useTranslations("ServerDetailChartClient");
|
||||
|
||||
const [networkChartData, setNetworkChartData] = useState(
|
||||
[] as networkChartData[],
|
||||
);
|
||||
@ -520,14 +520,16 @@ function NetworkChart({ data }: { data: NezhaAPISafe }) {
|
||||
<div className="flex items-center">
|
||||
<section className="flex items-center gap-4">
|
||||
<div className="flex flex-col w-20">
|
||||
<p className="text-xs text-muted-foreground">Upload</p>
|
||||
<p className="text-xs text-muted-foreground">{t("Upload")}</p>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-1))]"></span>
|
||||
<p className="text-xs font-medium">{up.toFixed(2)} M/s</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-20">
|
||||
<p className=" text-xs text-muted-foreground">Download</p>
|
||||
<p className=" text-xs text-muted-foreground">
|
||||
{t("Download")}
|
||||
</p>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="relative inline-flex size-1.5 rounded-full bg-[hsl(var(--chart-4))]"></span>
|
||||
<p className="text-xs font-medium">{down.toFixed(2)} M/s</p>
|
||||
@ -624,7 +626,7 @@ function ConnectChart({ data }: { data: NezhaAPISafe }) {
|
||||
} satisfies ChartConfig;
|
||||
|
||||
return (
|
||||
<Card className=" rounded-sm">
|
||||
<Card className="rounded-sm">
|
||||
<CardContent className="px-6 py-3">
|
||||
<section className="flex flex-col gap-1">
|
||||
<div className="flex items-center">
|
||||
|
@ -6,15 +6,18 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import getEnv from "@/lib/env-entry";
|
||||
import { cn, formatBytes, nezhaFetcher } from "@/lib/utils";
|
||||
import { useLocale } from "next-intl";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
|
||||
import ServerDetailLoading from "./ServerDetailLoading";
|
||||
|
||||
export default function ServerDetailClient({
|
||||
server_id,
|
||||
}: {
|
||||
server_id: number;
|
||||
}) {
|
||||
const t = useTranslations("ServerDetailClient");
|
||||
const router = useRouter();
|
||||
const locale = useLocale();
|
||||
const { data, error } = useSWR<NezhaAPISafe>(
|
||||
@ -24,20 +27,22 @@ export default function ServerDetailClient({
|
||||
refreshInterval: Number(getEnv("NEXT_PUBLIC_NezhaFetchInterval")) || 5000,
|
||||
},
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<p className="text-sm font-medium opacity-40">{error.message}</p>
|
||||
<p className="text-sm font-medium opacity-40">
|
||||
{/* {t("chart_fetch_error_message")} */}
|
||||
fetch_error_message
|
||||
{t("detail_fetch_error_message")}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (!data) return null;
|
||||
|
||||
if (!data) return <ServerDetailLoading />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
@ -53,7 +58,7 @@ export default function ServerDetailClient({
|
||||
<Card className="rounded-[10px] bg-transparent border-none shadow-none">
|
||||
<CardContent className="px-1.5 py-1">
|
||||
<section className="flex flex-col items-start gap-0.5">
|
||||
<p className="text-xs text-muted-foreground">Status</p>
|
||||
<p className="text-xs text-muted-foreground">{t("status")}</p>
|
||||
<Badge
|
||||
className={cn(
|
||||
"text-[10px] rounded-[6px] w-fit px-1 py-0 dark:text-white",
|
||||
@ -63,7 +68,7 @@ export default function ServerDetailClient({
|
||||
},
|
||||
)}
|
||||
>
|
||||
{data?.online_status ? "Online" : "Offline"}
|
||||
{data?.online_status ? t("Online") : t("Offline")}
|
||||
</Badge>
|
||||
</section>
|
||||
</CardContent>
|
||||
@ -71,10 +76,10 @@ export default function ServerDetailClient({
|
||||
<Card className="rounded-[10px] bg-transparent border-none shadow-none">
|
||||
<CardContent className="px-1.5 py-1">
|
||||
<section className="flex flex-col items-start gap-0.5">
|
||||
<p className="text-xs text-muted-foreground">Uptime</p>
|
||||
<p className="text-xs text-muted-foreground">{t("Uptime")}</p>
|
||||
<div className="text-xs">
|
||||
{" "}
|
||||
{(data?.status.Uptime / 86400).toFixed(0)} Days{" "}
|
||||
{(data?.status.Uptime / 86400).toFixed(0)} {t("Days")}{" "}
|
||||
</div>
|
||||
</section>
|
||||
</CardContent>
|
||||
@ -82,7 +87,7 @@ export default function ServerDetailClient({
|
||||
<Card className="rounded-[10px] bg-transparent border-none shadow-none">
|
||||
<CardContent className="px-1.5 py-1">
|
||||
<section className="flex flex-col items-start gap-0.5">
|
||||
<p className="text-xs text-muted-foreground">Version</p>
|
||||
<p className="text-xs text-muted-foreground">{t("Version")}</p>
|
||||
<div className="text-xs">{data?.host.Version || "Unknown"} </div>
|
||||
</section>
|
||||
</CardContent>
|
||||
@ -90,7 +95,7 @@ export default function ServerDetailClient({
|
||||
<Card className="rounded-[10px] bg-transparent border-none shadow-none">
|
||||
<CardContent className="px-1.5 py-1">
|
||||
<section className="flex flex-col items-start gap-0.5">
|
||||
<p className="text-xs text-muted-foreground">Arch</p>
|
||||
<p className="text-xs text-muted-foreground">{t("Arch")}</p>
|
||||
<div className="text-xs">{data?.host.Arch || "Unknown"} </div>
|
||||
</section>
|
||||
</CardContent>
|
||||
@ -98,7 +103,7 @@ export default function ServerDetailClient({
|
||||
<Card className="rounded-[10px] bg-transparent border-none shadow-none">
|
||||
<CardContent className="px-1.5 py-1">
|
||||
<section className="flex flex-col items-start gap-0.5">
|
||||
<p className="text-xs text-muted-foreground">Mem</p>
|
||||
<p className="text-xs text-muted-foreground">{t("Mem")}</p>
|
||||
<div className="text-xs">{formatBytes(data?.host.MemTotal)}</div>
|
||||
</section>
|
||||
</CardContent>
|
||||
@ -106,7 +111,7 @@ export default function ServerDetailClient({
|
||||
<Card className="rounded-[10px] bg-transparent border-none shadow-none">
|
||||
<CardContent className="px-1.5 py-1">
|
||||
<section className="flex flex-col items-start gap-0.5">
|
||||
<p className="text-xs text-muted-foreground">Disk</p>
|
||||
<p className="text-xs text-muted-foreground">{t("Disk")}</p>
|
||||
<div className="text-xs">{formatBytes(data?.host.DiskTotal)}</div>
|
||||
</section>
|
||||
</CardContent>
|
||||
@ -116,7 +121,7 @@ export default function ServerDetailClient({
|
||||
<Card className="rounded-[10px] bg-transparent border-none shadow-none">
|
||||
<CardContent className="px-1.5 py-1">
|
||||
<section className="flex flex-col items-start gap-0.5">
|
||||
<p className="text-xs text-muted-foreground">System</p>
|
||||
<p className="text-xs text-muted-foreground">{t("System")}</p>
|
||||
{data?.host.Platform ? (
|
||||
<div className="text-xs">
|
||||
{" "}
|
||||
@ -132,7 +137,7 @@ export default function ServerDetailClient({
|
||||
<Card className="rounded-[10px] bg-transparent border-none shadow-none">
|
||||
<CardContent className="px-1.5 py-1">
|
||||
<section className="flex flex-col items-start gap-0.5">
|
||||
<p className="text-xs text-muted-foreground">CPU</p>
|
||||
<p className="text-xs text-muted-foreground">{t("CPU")}</p>
|
||||
{data?.host.CPU ? (
|
||||
<div className="text-xs"> {data?.host.CPU}</div>
|
||||
) : (
|
||||
|
33
app/[locale]/(main)/ClientComponents/ServerDetailLoading.tsx
Normal file
33
app/[locale]/(main)/ClientComponents/ServerDetailLoading.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { BackIcon } from "@/components/Icon";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useLocale } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export default function ServerDetailLoading() {
|
||||
const router = useRouter();
|
||||
const locale = useLocale();
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
onClick={() => {
|
||||
router.push(`/${locale}/`);
|
||||
}}
|
||||
className="flex flex-none cursor-pointer font-semibold leading-none items-center break-all tracking-tight gap-0.5 text-xl"
|
||||
>
|
||||
<BackIcon />
|
||||
<Skeleton className="h-[20px] w-24 rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
||||
</div>
|
||||
<Skeleton className="flex flex-wrap gap-2 h-[100px] w-1/2 mt-3 rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
||||
<Separator className="my-4" />
|
||||
<section className="grid md:grid-cols-2 lg:grid-cols-3 grid-cols-1 gap-3">
|
||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
||||
<Skeleton className="h-[182px] w-full rounded-[5px] bg-muted-foreground/10 animate-none"></Skeleton>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -60,7 +60,6 @@ export default function ServerCard({
|
||||
>
|
||||
{name}
|
||||
</p>
|
||||
|
||||
</section>
|
||||
<div
|
||||
onClick={() => {
|
||||
|
@ -35,6 +35,29 @@
|
||||
"NetworkChart": {
|
||||
"ServerMonitorCount": "Services"
|
||||
},
|
||||
"ServerDetailClient": {
|
||||
"detail_fetch_error_message": "Please check your environment variables and review the server console",
|
||||
"status": "Status",
|
||||
"Online": "Online",
|
||||
"Offline": "Offline",
|
||||
"Uptime": "Uptime",
|
||||
"Days": "Days",
|
||||
"Version": "Version",
|
||||
"Arch": "Arch",
|
||||
"Mem": "Mem",
|
||||
"Disk": "Disk",
|
||||
"System": "System",
|
||||
"CPU": "CPU"
|
||||
},
|
||||
"ServerDetailChartClient": {
|
||||
"chart_fetch_error_message": "Please check your environment variables and review the server console",
|
||||
"Process": "Process",
|
||||
"Disk": "Disk",
|
||||
"Mem": "Mem",
|
||||
"Swap": "Swap",
|
||||
"Upload": "Upload",
|
||||
"Download": "Download"
|
||||
},
|
||||
"ThemeSwitcher": {
|
||||
"Light": "Light",
|
||||
"Dark": "Dark",
|
||||
|
@ -35,6 +35,29 @@
|
||||
"NetworkChart": {
|
||||
"ServerMonitorCount": "サービス"
|
||||
},
|
||||
"ServerDetailClient": {
|
||||
"detail_fetch_error_message": "環境変数を確認し、サーバーコンソールを確認してください",
|
||||
"status": "ステータス",
|
||||
"Online": "オンライン",
|
||||
"Offline": "オフライン",
|
||||
"Uptime": "稼働時間",
|
||||
"Days": "日",
|
||||
"Version": "バージョン",
|
||||
"Arch": "アーキテクチャ",
|
||||
"Mem": "メモリ",
|
||||
"Disk": "ディスク",
|
||||
"System": "システム",
|
||||
"CPU": "CPU"
|
||||
},
|
||||
"ServerDetailChartClient": {
|
||||
"chart_fetch_error_message": "環境変数を確認し、サーバーコンソールを確認してください",
|
||||
"Process": "進捗状況",
|
||||
"Disk": "ディスク",
|
||||
"Mem": "メモリ",
|
||||
"Swap": "スワップ",
|
||||
"Upload": "アップロード",
|
||||
"Download": "ダウンロード"
|
||||
},
|
||||
"ThemeSwitcher": {
|
||||
"Light": "ライト",
|
||||
"Dark": "ダーク",
|
||||
|
@ -35,6 +35,29 @@
|
||||
"NetworkChart": {
|
||||
"ServerMonitorCount": "個監測服務"
|
||||
},
|
||||
"ServerDetailClient": {
|
||||
"detail_fetch_error_message": "獲取伺服器詳情失敗,請檢查您的環境變數並檢查伺服器控制台",
|
||||
"status": "狀態",
|
||||
"Online": "在線",
|
||||
"Offline": "離線",
|
||||
"Uptime": "稼働時間",
|
||||
"Days": "天",
|
||||
"Version": "版本",
|
||||
"Arch": "架構",
|
||||
"Mem": "記憶體",
|
||||
"Disk": "磁碟",
|
||||
"System": "系統",
|
||||
"CPU": "CPU"
|
||||
},
|
||||
"ServerDetailChartClient": {
|
||||
"chart_fetch_error_message": "獲取伺服器詳情失敗,請檢查您的環境變數並檢查伺服器控制台",
|
||||
"Process": "進程",
|
||||
"Disk": "磁碟",
|
||||
"Mem": "記憶體",
|
||||
"Swap": "虛擬記憶體",
|
||||
"Upload": "上傳",
|
||||
"Download": "下載"
|
||||
},
|
||||
"ThemeSwitcher": {
|
||||
"Light": "亮色",
|
||||
"Dark": "暗色",
|
||||
|
@ -35,6 +35,29 @@
|
||||
"NetworkChart": {
|
||||
"ServerMonitorCount": "个监控服务"
|
||||
},
|
||||
"ServerDetailClient": {
|
||||
"detail_fetch_error_message": "获取服务器详情失败,请检查您的环境变量并检查服务器控制台",
|
||||
"status": "状态",
|
||||
"Online": "在线",
|
||||
"Offline": "离线",
|
||||
"Uptime": "运行时间",
|
||||
"Days": "天",
|
||||
"Version": "版本",
|
||||
"Arch": "架构",
|
||||
"Mem": "内存",
|
||||
"Disk": "磁盘",
|
||||
"System": "系统",
|
||||
"CPU": "CPU"
|
||||
},
|
||||
"ServerDetailChartClient": {
|
||||
"chart_fetch_error_message": "获取服务器详情失败,请检查您的环境变量并检查服务器控制台",
|
||||
"Process": "进程",
|
||||
"Disk": "磁盘",
|
||||
"Mem": "内存",
|
||||
"Swap": "虚拟内存",
|
||||
"Upload": "上传",
|
||||
"Download": "下载"
|
||||
},
|
||||
"ThemeSwitcher": {
|
||||
"Light": "亮色",
|
||||
"Dark": "暗色",
|
||||
|
Loading…
Reference in New Issue
Block a user