"use client" import { useEffect, useState } from "react" import { useParams } from "next/navigation" import axios from "axios" import Chart from 'chart.js/auto' import { AppSidebar } from "@/components/app-sidebar" import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb" import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar" import { Separator } from "@/components/ui/separator" import { Link, Cpu, MicroscopeIcon as Microchip, MemoryStick, HardDrive, MonitorIcon as MonitorCog, FileDigit, History } from "lucide-react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { StatusIndicator } from "@/components/status-indicator" import { DynamicIcon } from "lucide-react/dynamic" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { ScrollArea } from "@/components/ui/scroll-area" import { Button } from "@/components/ui/button" import NextLink from "next/link" interface ServerHistory { labels: string[]; datasets: { cpu: (number | null)[]; ram: (number | null)[]; disk: (number | null)[]; online: (boolean | null)[]; } } interface Server { id: number; name: string; icon: string; host: boolean; hostServer: number | null; os?: string; ip?: string; url?: string; cpu?: string; gpu?: string; ram?: string; disk?: string; hostedVMs?: Server[]; isVM?: boolean; monitoring?: boolean; monitoringURL?: string; online?: boolean; cpuUsage: number; ramUsage: number; diskUsage: number; history?: ServerHistory; port: number; } interface GetServersResponse { servers: Server[]; maxPage: number; } export default function ServerDetail() { const params = useParams() const serverId = params.server_id as string const [server, setServer] = useState(null) const [timeRange, setTimeRange] = useState<'1h' | '1d' | '7d' | '30d'>('1h') const [loading, setLoading] = useState(true) // Chart references const cpuChartRef = { current: null as Chart | null } const ramChartRef = { current: null as Chart | null } const diskChartRef = { current: null as Chart | null } const fetchServerDetails = async () => { try { setLoading(true) const response = await axios.post("/api/servers/get", { serverId: parseInt(serverId), timeRange: timeRange }) if (response.data.servers && response.data.servers.length > 0) { setServer(response.data.servers[0]) } setLoading(false) } catch (error) { console.error("Failed to fetch server details:", error) setLoading(false) } } useEffect(() => { fetchServerDetails() }, [serverId, timeRange]) useEffect(() => { if (!server || !server.history) return; // Clean up existing charts if (cpuChartRef.current) cpuChartRef.current.destroy(); if (ramChartRef.current) ramChartRef.current.destroy(); if (diskChartRef.current) diskChartRef.current.destroy(); // Wait for DOM to be ready const initTimer = setTimeout(() => { const history = server.history as ServerHistory; // Format time labels based on the selected time range const timeLabels = history.labels.map((date: string) => { const d = new Date(date) if (timeRange === '1h') { return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } else if (timeRange === '1d') { // For 1 day, show hours and minutes return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } else if (timeRange === '7d') { // For 7 days, show day and time return d.toLocaleDateString([], { weekday: 'short', month: 'numeric', day: 'numeric' }) + ' ' + d.toLocaleTimeString([], { hour: '2-digit' }) } else { // For 30 days return d.toLocaleDateString([], { month: 'numeric', day: 'numeric' }) } }) // Create a time range title for the chart const getRangeTitle = () => { const now = new Date() const startDate = new Date(history.labels[0]) if (timeRange === '1h') { return `Last Hour (${startDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - ${now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })})` } else if (timeRange === '1d') { return `Last 24 Hours (${startDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - ${now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })})` } else if (timeRange === '7d') { return `Last 7 Days (${startDate.toLocaleDateString([], { month: 'short', day: 'numeric' })} - ${now.toLocaleDateString([], { month: 'short', day: 'numeric' })})` } else { return `Last 30 Days (${startDate.toLocaleDateString([], { month: 'short', day: 'numeric' })} - ${now.toLocaleDateString([], { month: 'short', day: 'numeric' })})` } } // Directly hardcode the y-axis maximum in each chart option const commonOptions = { responsive: true, maintainAspectRatio: false, interaction: { mode: 'nearest' as const, axis: 'x' as const, intersect: false }, scales: { y: { min: 0, max: 100, beginAtZero: true, ticks: { stepSize: 25, autoSkip: false, callback: function(value: any) { return value + '%'; } }, title: { display: true, text: 'Usage %' } }, x: { grid: { display: false } } }, elements: { point: { radius: 0 }, line: { tension: 0.4, spanGaps: true } } }; // Create charts with very explicit y-axis max values const cpuCanvas = document.getElementById(`cpu-chart`) as HTMLCanvasElement if (cpuCanvas) { cpuChartRef.current = new Chart(cpuCanvas, { type: 'line', data: { labels: timeLabels, datasets: [{ label: 'CPU Usage', data: history.datasets.cpu, borderColor: 'rgb(75, 192, 192)', backgroundColor: 'rgba(75, 192, 192, 0.1)', fill: true, spanGaps: false }] }, options: { ...commonOptions, plugins: { title: { display: true, text: 'CPU Usage History', font: { size: 14 } }, tooltip: { callbacks: { title: function(tooltipItems: any) { return timeLabels[tooltipItems[0].dataIndex]; } } }, legend: { display: false } }, scales: { ...commonOptions.scales, y: { ...commonOptions.scales.y, max: 100 // Force this to ensure it's applied } } } }) } const ramCanvas = document.getElementById(`ram-chart`) as HTMLCanvasElement if (ramCanvas) { ramChartRef.current = new Chart(ramCanvas, { type: 'line', data: { labels: timeLabels, datasets: [{ label: 'RAM Usage', data: history.datasets.ram, borderColor: 'rgb(153, 102, 255)', backgroundColor: 'rgba(153, 102, 255, 0.1)', fill: true, spanGaps: false }] }, options: { ...commonOptions, plugins: { title: { display: true, text: 'RAM Usage History', font: { size: 14 } }, tooltip: { callbacks: { title: function(tooltipItems: any) { return timeLabels[tooltipItems[0].dataIndex]; } } }, legend: { display: false } }, scales: { ...commonOptions.scales, y: { ...commonOptions.scales.y, max: 100 // Force this to ensure it's applied } } } }) } const diskCanvas = document.getElementById(`disk-chart`) as HTMLCanvasElement if (diskCanvas) { diskChartRef.current = new Chart(diskCanvas, { type: 'line', data: { labels: timeLabels, datasets: [{ label: 'Disk Usage', data: history.datasets.disk, borderColor: 'rgb(255, 159, 64)', backgroundColor: 'rgba(255, 159, 64, 0.1)', fill: true, spanGaps: false }] }, options: { ...commonOptions, plugins: { title: { display: true, text: 'Disk Usage History', font: { size: 14 } }, tooltip: { callbacks: { title: function(tooltipItems: any) { return timeLabels[tooltipItems[0].dataIndex]; } } }, legend: { display: false } }, scales: { ...commonOptions.scales, y: { ...commonOptions.scales.y, max: 100 // Force this to ensure it's applied } } } }) } }, 100); return () => { clearTimeout(initTimer); if (cpuChartRef.current) cpuChartRef.current.destroy(); if (ramChartRef.current) ramChartRef.current.destroy(); if (diskChartRef.current) diskChartRef.current.destroy(); }; }, [server, timeRange]); // Function to refresh data const refreshData = () => { fetchServerDetails() } return (
/ My Infrastructure Servers {server && ( <> {server.name} )}
{loading ? (
Loading...
) : server ? (
{/* Server header card */}
{server.icon && }
{server.name} {server.os || "No OS specified"} • {server.isVM ? "Virtual Machine" : "Physical Server"} {server.isVM && server.hostServer && ( <> • Hosted on {server.hostedVMs?.[0]?.name} )}
{server.monitoring && (
)}

Hardware

CPU:
{server.cpu || "-"}
GPU:
{server.gpu || "-"}
RAM:
{server.ram || "-"}
Disk:
{server.disk || "-"}

Network

IP Address:
{server.ip || "-"}
Management URL:
{server.url ? ( {server.url} ) : ( "-" )}
{server.monitoring && (

Current Usage

CPU Usage:
80 ? "bg-destructive" : server.cpuUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${server.cpuUsage}%` }} />
{server.cpuUsage}%
RAM Usage:
80 ? "bg-destructive" : server.ramUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${server.ramUsage}%` }} />
{server.ramUsage}%
Disk Usage:
80 ? "bg-destructive" : server.diskUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${server.diskUsage}%` }} />
{server.diskUsage}%
)}
{/* Charts */} {server.monitoring && server.history && (
Resource Usage History {timeRange === '1h' ? 'Last hour, per minute' : timeRange === '1d' ? 'Last 24 hours, 15-minute intervals' : timeRange === '7d' ? 'Last 7 days, hourly intervals' : 'Last 30 days, 4-hour intervals'}
)} {/* Virtual Machines */} {server.hostedVMs && server.hostedVMs.length > 0 && ( Virtual Machines Virtual machines hosted on this server
{server.hostedVMs.map((hostedVM) => (
{hostedVM.icon && ( )}
{hostedVM.icon && "・ "} {hostedVM.name}
{hostedVM.monitoring && ( )}
OS: {hostedVM.os || "-"}
IP: {hostedVM.ip || "Not set"}

Hardware Information

CPU: {hostedVM.cpu || "-"}
GPU: {hostedVM.gpu || "-"}
RAM: {hostedVM.ram || "-"}
Disk: {hostedVM.disk || "-"}
{hostedVM.monitoring && ( <>
CPU
{hostedVM.cpuUsage || 0}%
80 ? "bg-destructive" : hostedVM.cpuUsage && hostedVM.cpuUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${hostedVM.cpuUsage || 0}%` }} />
RAM
{hostedVM.ramUsage || 0}%
80 ? "bg-destructive" : hostedVM.ramUsage && hostedVM.ramUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${hostedVM.ramUsage || 0}%` }} />
Disk
{hostedVM.diskUsage || 0}%
80 ? "bg-destructive" : hostedVM.diskUsage && hostedVM.diskUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${hostedVM.diskUsage || 0}%` }} />
)}
))}
)}
) : (

Server not found

The requested server could not be found or you don't have permission to view it.

)}
) }