diff --git a/plans/046-monitor-nezha-redesign.md b/plans/046-monitor-nezha-redesign.md
new file mode 100644
index 0000000..9f7ad4a
--- /dev/null
+++ b/plans/046-monitor-nezha-redesign.md
@@ -0,0 +1,15 @@
+# Monitor Nezha Redesign
+## Summary
+Redesign the monitor overview to resemble Nezha dashboard by introducing a Server Matrix (Grid) containing cards with metrics visualisations.
+
+## Objectives
+- [x] Analyze `monitor-view.tsx` layout boundaries.
+- [x] Create `ServerCard` utilizing `Progress` component.
+- [x] Render metrics (CPU, RAM, Disk, System Load, Connections, Network speeeds).
+- [x] Implement robust real-time updates and seamless state linkage via existing hooks.
+- [x] Adjust layout positioning for impact at top-of-page.
+
+## Technical Details
+Injected a `ServerCard` inline component mapping over unified node metric states. Used the tailwind and hero UI components matching visual designs like progress bars and small monospace typograhy. Extracted utility color scales.
+
+## Status: Complete
diff --git a/vite-frontend/src/pages/node/monitor-view.tsx b/vite-frontend/src/pages/node/monitor-view.tsx
index 78ae0ed..cd9d4c6 100644
--- a/vite-frontend/src/pages/node/monitor-view.tsx
+++ b/vite-frontend/src/pages/node/monitor-view.tsx
@@ -26,6 +26,12 @@ import {
Edit,
Activity,
Play,
+ Server,
+ Zap,
+ HardDrive,
+ Cpu,
+ Clock,
+ Globe,
} from "lucide-react";
import toast from "react-hot-toast";
@@ -69,6 +75,7 @@ import {
DropdownItem,
} from "@/shadcn-bridge/heroui/dropdown";
import { Card, CardBody, CardHeader } from "@/shadcn-bridge/heroui/card";
+import { Progress } from "@/shadcn-bridge/heroui/progress";
import { useNodeRealtime } from "@/pages/node/use-node-realtime";
interface MonitorViewProps {
@@ -145,6 +152,117 @@ const formatBytesPerSecond = (bytesPerSecond: number): string => {
return `${parseFloat((bytesPerSecond / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
};
+const formatUptime = (seconds: number) => {
+ if (!seconds) return "-";
+ const days = Math.floor(seconds / 86400);
+ const hours = Math.floor((seconds % 86400) / 3600);
+ if (days > 0) return `${days} 天 ${hours} 小时`;
+ return `${hours} 小时`;
+};
+
+const getColorByUsage = (usage?: number) => {
+ if (usage === undefined || usage === null) return "default";
+ if (usage >= 90) return "danger";
+ if (usage >= 75) return "warning";
+ if (usage >= 50) return "primary";
+ return "success";
+};
+
+function ServerCard({ node, metric }: { node: any; metric: RealtimeNodeMetric | null }) {
+ const isOnline = node.connectionStatus === "online";
+
+ return (
+