From 671f22c1d38c988759ed78aec0e81fda4088fc77 Mon Sep 17 00:00:00 2001 From: Tola Leng Date: Thu, 3 Jul 2025 21:05:56 +0700 Subject: [PATCH 1/8] Add FUNDING.yml (#63) * Merge pull request #27 from operacle/develop feat: Implement uptime monitoring data retention * Create FUNDING.yml * add codeowners and pull request workflow --- .github/CODEOWNERS | 2 ++ .github/FUNDING.yml | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/FUNDING.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..78454b3 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# These owners will be the default owners for everything in +* @tolaleng diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..cca37f4 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# These are supported funding model platforms + +github: [tolaleng] +patreon: # Replace with a single Patreon username +open_collective: checkcle +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: # Replace with a single Buy Me a Coffee username +thanks_dev: # Replace with a single thanks.dev username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From f0eec90e75ba3bc455a19ac67c6eda10220bceef Mon Sep 17 00:00:00 2001 From: samang-dauth Date: Sat, 5 Jul 2025 17:00:21 +0700 Subject: [PATCH 2/8] feat: Add system info cards to server detail header Adds small cards in the server detail page header to display server system information. --- .../servers/ServerMetricsOverview.tsx | 6 +- .../servers/ServerSystemInfoCard.tsx | 112 ++++++++++++++++++ application/src/pages/ServerDetail.tsx | 11 +- 3 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 application/src/components/servers/ServerSystemInfoCard.tsx diff --git a/application/src/components/servers/ServerMetricsOverview.tsx b/application/src/components/servers/ServerMetricsOverview.tsx index 34ec57a..e706f5e 100644 --- a/application/src/components/servers/ServerMetricsOverview.tsx +++ b/application/src/components/servers/ServerMetricsOverview.tsx @@ -54,8 +54,8 @@ export const ServerMetricsOverview = ({ server }: ServerMetricsOverviewProps) =>
@@ -64,7 +64,7 @@ export const ServerMetricsOverview = ({ server }: ServerMetricsOverviewProps) =>
{percentage.toFixed(1)}% diff --git a/application/src/components/servers/ServerSystemInfoCard.tsx b/application/src/components/servers/ServerSystemInfoCard.tsx new file mode 100644 index 0000000..893b60d --- /dev/null +++ b/application/src/components/servers/ServerSystemInfoCard.tsx @@ -0,0 +1,112 @@ +import { Card, CardContent } from "@/components/ui/card"; +import { Server, Monitor, Cpu, HardDrive, DatabaseIcon, InfoIcon } from "lucide-react"; +import { Server as ServerType } from "@/types/server.types"; + +interface ServerSystemInfoCardProps { + server: ServerType; +} + +export function ServerSystemInfoCard({ server }: ServerSystemInfoCardProps) { + // Parse system_info if it's a string + let systemInfo: any = {}; + if (server.system_info) { + try { + systemInfo = typeof server.system_info === 'string' + ? JSON.parse(server.system_info) + : server.system_info; + } catch (error) { + console.log('Error parsing system_info:', error); + } + } + + const formatBytes = (bytes: number) => { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + + const formatUptime = (uptimeSeconds: string | number) => { + const seconds = typeof uptimeSeconds === 'string' ? parseInt(uptimeSeconds) : uptimeSeconds; + const days = Math.floor(seconds / 86400); + const hours = Math.floor((seconds % 86400) / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + + if (days > 0) return `${days}d ${hours}h`; + if (hours > 0) return `${hours}h ${minutes}m`; + return `${minutes}m`; + }; + + // Format the detailed system info string + const getDetailedSystemInfo = () => { + if (typeof server.system_info === 'string' && server.system_info.includes('|')) { + // If system_info is already in the detailed format, return it + return server.system_info; + } + + // Otherwise, build the detailed string from available data + const parts = []; + + // Basic server info + parts.push(server.hostname || 'Unknown'); + parts.push(server.ip_address || 'Unknown IP'); + parts.push(server.os_type || 'Unknown OS'); + + // Parse additional info from system_info if available + if (systemInfo.OSName) { + parts.push(systemInfo.OSName + (systemInfo.OSVersion ? ` ${systemInfo.OSVersion}` : '')); + } + + if (systemInfo.Architecture) { + parts.push(`| ${systemInfo.Architecture}`); + } + + if (systemInfo.KernelVersion) { + parts.push(`| Kernel: ${systemInfo.KernelVersion}`); + } + + if (systemInfo.CPUModel) { + parts.push(`| CPU: ${systemInfo.CPUModel} (${server.cpu_cores || 0} cores)`); + } else if (server.cpu_cores) { + parts.push(`| CPU: ${server.cpu_cores} cores`); + } + + if (server.ram_total) { + parts.push(`| RAM: ${formatBytes(server.ram_total)}`); + } + + if (systemInfo.GoVersion) { + parts.push(`| Go ${systemInfo.GoVersion}`); + } + + if (systemInfo.IPAddress && systemInfo.IPAddress !== server.ip_address) { + parts.push(`| IP: ${systemInfo.IPAddress}`); + } + + // Check for Docker info + const dockerInfo = server.docker || systemInfo.Docker; + if (dockerInfo !== undefined) { + parts.push(`| Docker: ${dockerInfo}`); + } + + return parts.join(' • ').replace(/• \|/g, '|'); + }; + + return ( + + +
+
+ +
+

System Information

+
+ +
+ {getDetailedSystemInfo()} +
+
+
+ ); +} \ No newline at end of file diff --git a/application/src/pages/ServerDetail.tsx b/application/src/pages/ServerDetail.tsx index 4e7fdd1..6522f3a 100644 --- a/application/src/pages/ServerDetail.tsx +++ b/application/src/pages/ServerDetail.tsx @@ -14,6 +14,7 @@ import { ArrowLeft, Server, Database } from "lucide-react"; import { ServerMetricsCharts } from "@/components/servers/ServerMetricsCharts"; import { ServerMetricsOverview } from "@/components/servers/ServerMetricsOverview"; import { ServerHistoryCharts } from "@/components/servers/ServerHistoryCharts"; +import { ServerSystemInfoCard } from "@/components/servers/ServerSystemInfoCard"; const ServerDetail = () => { const { serverId } = useParams(); @@ -136,14 +137,18 @@ const ServerDetail = () => { Monitor server performance metrics and system health {server && ( - {server.hostname} • {server.ip_address} • {server.os_type} • {server.system_info} + {server.hostname} • {server.ip_address} • {server.os_type} )}

- -
+ {/* System Info Card */} + {server && ( +
+ +
+ )} From 5d2b8b2ed20ce0fa6ed56437f0bd1a387cd5283a Mon Sep 17 00:00:00 2001 From: Tola Leng Date: Sun, 6 Jul 2025 15:12:03 +0700 Subject: [PATCH 3/8] Refactor: Split ServerHistory and Charts into smaller components --- .../servers/ServerHistoryCharts.tsx | 494 ++---------------- .../servers/ServerMetricsCharts.tsx | 19 +- .../components/servers/charts/CPUChart.tsx | 83 +++ .../components/servers/charts/CPUCharts.tsx | 141 ----- .../components/servers/charts/DiskChart.tsx | 83 +++ .../components/servers/charts/DiskCharts.tsx | 138 ----- .../components/servers/charts/MemoryChart.tsx | 83 +++ .../servers/charts/MemoryCharts.tsx | 138 ----- .../servers/charts/NetworkChart.tsx | 102 ++++ .../servers/charts/NetworkCharts.tsx | 140 ----- .../servers/charts/TimeRangeSelector.tsx | 11 +- .../components/servers/charts/chartConfig.ts | 34 -- .../components/servers/charts/dataUtils.ts | 115 ++-- .../charts/hooks/useServerHistoryData.ts | 45 ++ .../tooltips/DetailedTooltipContent.tsx | 65 +++ .../charts/tooltips/NetworkTooltipContent.tsx | 48 ++ 16 files changed, 662 insertions(+), 1077 deletions(-) create mode 100644 application/src/components/servers/charts/CPUChart.tsx delete mode 100644 application/src/components/servers/charts/CPUCharts.tsx create mode 100644 application/src/components/servers/charts/DiskChart.tsx delete mode 100644 application/src/components/servers/charts/DiskCharts.tsx create mode 100644 application/src/components/servers/charts/MemoryChart.tsx delete mode 100644 application/src/components/servers/charts/MemoryCharts.tsx create mode 100644 application/src/components/servers/charts/NetworkChart.tsx delete mode 100644 application/src/components/servers/charts/NetworkCharts.tsx delete mode 100644 application/src/components/servers/charts/chartConfig.ts create mode 100644 application/src/components/servers/charts/hooks/useServerHistoryData.ts create mode 100644 application/src/components/servers/charts/tooltips/DetailedTooltipContent.tsx create mode 100644 application/src/components/servers/charts/tooltips/NetworkTooltipContent.tsx diff --git a/application/src/components/servers/ServerHistoryCharts.tsx b/application/src/components/servers/ServerHistoryCharts.tsx index 870b0ee..22dcfc9 100644 --- a/application/src/components/servers/ServerHistoryCharts.tsx +++ b/application/src/components/servers/ServerHistoryCharts.tsx @@ -1,163 +1,37 @@ -import { useQuery } from "@tanstack/react-query"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"; -import { LineChart, Line, AreaChart, Area, XAxis, YAxis, CartesianGrid, ResponsiveContainer } from "recharts"; -import { serverService } from "@/services/serverService"; -import { Loader2, TrendingUp, Cpu, HardDrive, Wifi, MemoryStick } from "lucide-react"; -import { useTheme } from "@/contexts/ThemeContext"; + +import { useMemo } from "react"; +import { Card, CardContent } from "@/components/ui/card"; +import { Loader2, TrendingUp } from "lucide-react"; import { TimeRangeSelector } from "./charts/TimeRangeSelector"; -import { useState } from "react"; -import { formatChartData, filterMetricsByTimeRange, formatBytes } from "./charts/dataUtils"; +import { useServerHistoryData } from "./charts/hooks/useServerHistoryData"; +import { CPUChart } from "./charts/CPUChart"; +import { MemoryChart } from "./charts/MemoryChart"; +import { DiskChart } from "./charts/DiskChart"; +import { NetworkChart } from "./charts/NetworkChart"; interface ServerHistoryChartsProps { serverId: string; } -type TimeRange = '60m' | '1d' | '7d' | '1m' | '3m'; - export const ServerHistoryCharts = ({ serverId }: ServerHistoryChartsProps) => { - const { theme } = useTheme(); - const [timeRange, setTimeRange] = useState("1d"); - - console.log('ServerHistoryCharts: Rendering with serverId:', serverId); - const { - data: metrics = [], + timeRange, + setTimeRange, + metrics, + chartData, isLoading, - error - } = useQuery({ - queryKey: ['server-metrics-history', serverId, timeRange], - queryFn: async () => { - console.log('ServerHistoryCharts: Fetching metrics for serverId:', serverId, 'timeRange:', timeRange); - const result = await serverService.getServerMetrics(serverId, timeRange); - console.log('ServerHistoryCharts: Raw metrics result for timeRange', timeRange, ':', result?.length || 0, 'records'); - console.log('ServerHistoryCharts: First 3 records:', result?.slice(0, 3)); - return result; - }, - enabled: !!serverId, - refetchInterval: 60000, - retry: 1 - }); - - console.log('ServerHistoryCharts: Query state:', { - metricsCount: metrics.length, - isLoading, - error: error?.message, - firstMetric: metrics[0], - serverId, - timeRange - }); + error, + isFetching + } = useServerHistoryData(serverId); - console.log('ServerHistoryCharts: About to format chart data with', metrics?.length || 0, 'metrics for timeRange:', timeRange); - const chartData = formatChartData(metrics, timeRange); - console.log('ServerHistoryCharts: After formatting, got', chartData?.length || 0, 'chart data points'); - - const getGridColor = () => theme === 'dark' ? '#374151' : '#e5e7eb'; - const getAxisColor = () => theme === 'dark' ? '#9ca3af' : '#6b7280'; - - // Custom tooltip content with detailed information and full date/time - const DetailedTooltipContent = ({ active, payload, label }: any) => { - if (active && payload && payload.length) { - const data = payload[0]?.payload; - return ( -
-

{label}

- {data?.fullTimestamp && ( -

- {new Date(data.fullTimestamp).toLocaleString('en-US', { - weekday: 'short', - year: 'numeric', - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - })} -

- )} - {payload.map((entry: any, index: number) => { - const data = entry.payload; - return ( -
-
-
- {entry.name}: {entry.value}% -
- {entry.dataKey === 'cpuUsage' && ( -
-
CPU Cores: {data.cpuCores}
-
Free: {data.cpuFree}%
-
- )} - {entry.dataKey === 'ramUsagePercent' && ( -
-
Used: {data.ramUsed}
-
Total: {data.ramTotal}
-
Free: {data.ramFree}
-
- )} - {entry.dataKey === 'diskUsagePercent' && ( -
-
Used: {data.diskUsed}
-
Total: {data.diskTotal}
-
Free: {data.diskFree}
-
- )} -
- ); - })} -
- ); - } - return null; - }; + console.log('ServerHistoryCharts: Rendering with serverId:', serverId, 'timeRange:', timeRange); - // Network tooltip with detailed info and full date/time - const NetworkTooltipContent = ({ active, payload, label }: any) => { - if (active && payload && payload.length) { - const data = payload[0]?.payload; - return ( -
-

{label}

- {data?.fullTimestamp && ( -

- {new Date(data.fullTimestamp).toLocaleString('en-US', { - weekday: 'short', - year: 'numeric', - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - })} -

- )} - {payload.map((entry: any, index: number) => ( -
-
- - {entry.name}: {entry.dataKey.includes('Speed') ? `${entry.value} KB/s` : formatBytes(entry.value)} - -
- ))} - {data && ( -
-
Total RX: {data.networkRx}
-
Total TX: {data.networkTx}
-
- )} -
- ); - } - return null; - }; + // Memoize latest data calculation to prevent unnecessary recalculations + const latestData = useMemo(() => { + return chartData.length > 0 ? chartData[chartData.length - 1] : null; + }, [chartData]); + // Show skeleton loading state for better UX if (isLoading) { return (
@@ -165,22 +39,28 @@ export const ServerHistoryCharts = ({ serverId }: ServerHistoryChartsProps) => {

Historical Performance

- +
+ + Loading... +
+ + {/* Skeleton loading cards */}
{[1, 2, 3, 4].map((index) => ( - - - -
-
- - - -
-
+ + +
+
+
+
+
+
+
+
+
@@ -198,6 +78,7 @@ export const ServerHistoryCharts = ({ serverId }: ServerHistoryChartsProps) => {

Historical Performance

+ {isFetching && }
@@ -206,7 +87,7 @@ export const ServerHistoryCharts = ({ serverId }: ServerHistoryChartsProps) => {

Error loading chart data

{error?.message}

-

Server ID: {serverId}

+

Server ID: {serverId} • Time Range: {timeRange}

@@ -221,6 +102,7 @@ export const ServerHistoryCharts = ({ serverId }: ServerHistoryChartsProps) => {

Historical Performance

+ {isFetching && }
@@ -229,9 +111,9 @@ export const ServerHistoryCharts = ({ serverId }: ServerHistoryChartsProps) => {

No historical data available for {timeRange}

Raw metrics count: {metrics.length}

-

Server ID: {serverId}

+

Server ID: {serverId} • Time Range: {timeRange}

- {metrics.length > 0 ? 'Data exists but filtered out by time range' : 'No metrics data found'} + {metrics.length > 0 ? 'Data exists but may be outside selected time range' : 'No metrics data found'}

@@ -242,291 +124,31 @@ export const ServerHistoryCharts = ({ serverId }: ServerHistoryChartsProps) => { console.log('ServerHistoryCharts: Rendering charts with', chartData.length, 'data points for time range:', timeRange); - // Calculate summary stats from latest data point - const latestData = chartData[chartData.length - 1]; - return (

Historical Performance

- ({chartData.length} data points • {timeRange}) + + ({chartData.length} data points • {timeRange}) + {isFetching && ( + + + Updating... + + )} +
+ {/* Use CSS Grid for better performance than flexbox */}
- {/* CPU Usage Chart - Smooth Area Chart with Blue Gradient */} - - - -
-
- -
- CPU Usage -
- {latestData && ( -
-
{latestData.cpuUsage}%
-
{latestData.cpuCores} cores
-
- )} -
-
- - - - - - - - - - - - - } - cursor={{ stroke: getGridColor() }} - /> - - - - -
- - {/* Memory Usage Chart - Basis Area Chart with Green Gradient */} - - - -
-
- -
- Memory Usage -
- {latestData && ( -
-
{latestData.ramUsagePercent}%
-
{latestData.ramUsed} / {latestData.ramTotal}
-
- )} -
-
- - - - - - - - - - - - - } - cursor={{ stroke: getGridColor() }} - /> - - - - -
- - {/* Disk Usage Chart - Stepped Area Chart with Orange Gradient */} - - - -
-
- -
- Disk Usage -
- {latestData && ( -
-
{latestData.diskUsagePercent}%
-
{latestData.diskUsed} / {latestData.diskTotal}
-
- )} -
-
- - - - - - - - - - - - - } - cursor={{ stroke: getGridColor() }} - /> - - - - -
- - {/* Network Traffic Chart - Dual Line Chart with Purple/Red Theme */} - - - -
-
- -
- Network Traffic -
- {latestData && ( -
-
{latestData.networkRxSpeed} KB/s ↓
-
{latestData.networkTxSpeed} KB/s ↑
-
- )} -
-
- - - - - - - - - - - - - - - - - - - - } - cursor={{ stroke: getGridColor() }} - /> - - - - - -
+ + + +
); diff --git a/application/src/components/servers/ServerMetricsCharts.tsx b/application/src/components/servers/ServerMetricsCharts.tsx index 31fc7e0..a3fa10c 100644 --- a/application/src/components/servers/ServerMetricsCharts.tsx +++ b/application/src/components/servers/ServerMetricsCharts.tsx @@ -6,10 +6,10 @@ import { serverService } from "@/services/serverService"; import { Loader2, Cpu, HardDrive, Network, MemoryStick } from "lucide-react"; import { formatChartData } from "./charts/dataUtils"; import { TimeRangeSelector } from "./charts/TimeRangeSelector"; -import { CPUCharts } from "./charts/CPUCharts"; -import { MemoryCharts } from "./charts/MemoryCharts"; -import { DiskCharts } from "./charts/DiskCharts"; -import { NetworkCharts } from "./charts/NetworkCharts"; +import { CPUChart } from "./charts/CPUChart"; +import { MemoryChart } from "./charts/MemoryChart"; +import { DiskChart } from "./charts/DiskChart"; +import { NetworkChart } from "./charts/NetworkChart"; interface ServerMetricsChartsProps { serverId: string; @@ -58,6 +58,9 @@ export const ServerMetricsCharts = ({ serverId }: ServerMetricsChartsProps) => { ); } + // Calculate latest data for each chart + const latestData = chartData[chartData.length - 1]; + return (
@@ -89,19 +92,19 @@ export const ServerMetricsCharts = ({ serverId }: ServerMetricsChartsProps) => { - + - + - + - +
diff --git a/application/src/components/servers/charts/CPUChart.tsx b/application/src/components/servers/charts/CPUChart.tsx new file mode 100644 index 0000000..c5fb013 --- /dev/null +++ b/application/src/components/servers/charts/CPUChart.tsx @@ -0,0 +1,83 @@ + +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { ChartContainer, ChartTooltip } from "@/components/ui/chart"; +import { AreaChart, Area, XAxis, YAxis, CartesianGrid } from "recharts"; +import { Cpu } from "lucide-react"; +import { useTheme } from "@/contexts/ThemeContext"; +import { DetailedTooltipContent } from "./tooltips/DetailedTooltipContent"; + +interface CPUChartProps { + data: any[]; + latestData?: any; +} + +export const CPUChart = ({ data, latestData }: CPUChartProps) => { + const { theme } = useTheme(); + + const getGridColor = () => theme === 'dark' ? '#374151' : '#e5e7eb'; + const getAxisColor = () => theme === 'dark' ? '#9ca3af' : '#6b7280'; + + return ( + + + +
+
+ +
+ CPU Usage +
+ {latestData && ( +
+
{latestData.cpuUsage}%
+
{latestData.cpuCores} cores
+
+ )} +
+
+ + + + + + + + + + + + + } + cursor={{ stroke: getGridColor() }} + /> + + + + +
+ ); +}; \ No newline at end of file diff --git a/application/src/components/servers/charts/CPUCharts.tsx b/application/src/components/servers/charts/CPUCharts.tsx deleted file mode 100644 index db9f3f4..0000000 --- a/application/src/components/servers/charts/CPUCharts.tsx +++ /dev/null @@ -1,141 +0,0 @@ - -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"; -import { AreaChart, Area, XAxis, YAxis, CartesianGrid } from "recharts"; -import { Cpu } from "lucide-react"; -import { useChartConfig } from "./chartConfig"; -import { formatBytes } from "./dataUtils"; - -interface CPUChartsProps { - data: any[]; -} - -export const CPUCharts = ({ data }: CPUChartsProps) => { - const { chartConfig, getGridColor, getAxisColor } = useChartConfig(); - - return ( -
- -
- - -
- -
- CPU Usage -
-
- - - - - - - - - - - - - - - - - - - - - } - cursor={{ stroke: '#3b82f6', strokeWidth: 2, strokeOpacity: 0.5 }} - /> - - - - - - - -
- - -
- -
- CPU Usage Distribution -
-
- - - - - - - - - - - - - - - - - } - cursor={{ stroke: '#3b82f6', strokeWidth: 2, strokeOpacity: 0.5 }} - /> - - - - - - -
- ); -}; \ No newline at end of file diff --git a/application/src/components/servers/charts/DiskChart.tsx b/application/src/components/servers/charts/DiskChart.tsx new file mode 100644 index 0000000..da82c15 --- /dev/null +++ b/application/src/components/servers/charts/DiskChart.tsx @@ -0,0 +1,83 @@ + +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { ChartContainer, ChartTooltip } from "@/components/ui/chart"; +import { AreaChart, Area, XAxis, YAxis, CartesianGrid } from "recharts"; +import { HardDrive } from "lucide-react"; +import { useTheme } from "@/contexts/ThemeContext"; +import { DetailedTooltipContent } from "./tooltips/DetailedTooltipContent"; + +interface DiskChartProps { + data: any[]; + latestData?: any; +} + +export const DiskChart = ({ data, latestData }: DiskChartProps) => { + const { theme } = useTheme(); + + const getGridColor = () => theme === 'dark' ? '#374151' : '#e5e7eb'; + const getAxisColor = () => theme === 'dark' ? '#9ca3af' : '#6b7280'; + + return ( + + + +
+
+ +
+ Disk Usage +
+ {latestData && ( +
+
{latestData.diskUsagePercent}%
+
{latestData.diskUsed} / {latestData.diskTotal}
+
+ )} +
+
+ + + + + + + + + + + + + } + cursor={{ stroke: getGridColor() }} + /> + + + + +
+ ); +}; \ No newline at end of file diff --git a/application/src/components/servers/charts/DiskCharts.tsx b/application/src/components/servers/charts/DiskCharts.tsx deleted file mode 100644 index 1b6bf07..0000000 --- a/application/src/components/servers/charts/DiskCharts.tsx +++ /dev/null @@ -1,138 +0,0 @@ - -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"; -import { AreaChart, Area, XAxis, YAxis, CartesianGrid } from "recharts"; -import { HardDrive } from "lucide-react"; -import { useChartConfig } from "./chartConfig"; -import { formatBytes } from "./dataUtils"; - -interface DiskChartsProps { - data: any[]; -} - -export const DiskCharts = ({ data }: DiskChartsProps) => { - const { chartConfig, getGridColor, getAxisColor } = useChartConfig(); - - return ( -
- -
- - -
- -
- Disk Usage -
-
- - - - - - - - - - - - - - } - cursor={{ stroke: '#f59e0b', strokeWidth: 2, strokeOpacity: 0.5 }} - /> - - - - - - - -
- - -
- -
- Disk Usage (Bytes) -
-
- - - - - - - - - - - - - - - - formatBytes(value)} - /> - } - cursor={{ stroke: '#f59e0b', strokeWidth: 2, strokeOpacity: 0.5 }} - formatter={(value, name) => [ - name === 'Used Disk' ? formatBytes(Number(value)) : - name === 'Total Disk' ? formatBytes(Number(value)) : value, - name - ]} - /> - - - - - - -
- ); -}; \ No newline at end of file diff --git a/application/src/components/servers/charts/MemoryChart.tsx b/application/src/components/servers/charts/MemoryChart.tsx new file mode 100644 index 0000000..a0abd7b --- /dev/null +++ b/application/src/components/servers/charts/MemoryChart.tsx @@ -0,0 +1,83 @@ + +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { ChartContainer, ChartTooltip } from "@/components/ui/chart"; +import { AreaChart, Area, XAxis, YAxis, CartesianGrid } from "recharts"; +import { MemoryStick } from "lucide-react"; +import { useTheme } from "@/contexts/ThemeContext"; +import { DetailedTooltipContent } from "./tooltips/DetailedTooltipContent"; + +interface MemoryChartProps { + data: any[]; + latestData?: any; +} + +export const MemoryChart = ({ data, latestData }: MemoryChartProps) => { + const { theme } = useTheme(); + + const getGridColor = () => theme === 'dark' ? '#374151' : '#e5e7eb'; + const getAxisColor = () => theme === 'dark' ? '#9ca3af' : '#6b7280'; + + return ( + + + +
+
+ +
+ Memory Usage +
+ {latestData && ( +
+
{latestData.ramUsagePercent}%
+
{latestData.ramUsed} / {latestData.ramTotal}
+
+ )} +
+
+ + + + + + + + + + + + + } + cursor={{ stroke: getGridColor() }} + /> + + + + +
+ ); +}; \ No newline at end of file diff --git a/application/src/components/servers/charts/MemoryCharts.tsx b/application/src/components/servers/charts/MemoryCharts.tsx deleted file mode 100644 index 4c748ce..0000000 --- a/application/src/components/servers/charts/MemoryCharts.tsx +++ /dev/null @@ -1,138 +0,0 @@ - -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"; -import { AreaChart, Area, XAxis, YAxis, CartesianGrid } from "recharts"; -import { MemoryStick } from "lucide-react"; -import { useChartConfig } from "./chartConfig"; -import { formatBytes } from "./dataUtils"; - -interface MemoryChartsProps { - data: any[]; -} - -export const MemoryCharts = ({ data }: MemoryChartsProps) => { - const { chartConfig, getGridColor, getAxisColor } = useChartConfig(); - - return ( -
- -
- - -
- -
- Memory Usage -
-
- - - - - - - - - - - - - - } - cursor={{ stroke: '#10b981', strokeWidth: 2, strokeOpacity: 0.5 }} - /> - - - - - - - -
- - -
- -
- Memory Usage (Bytes) -
-
- - - - - - - - - - - - - - - - formatBytes(value)} - /> - } - cursor={{ stroke: '#10b981', strokeWidth: 2, strokeOpacity: 0.5 }} - formatter={(value, name) => [ - name === 'Used Memory' ? formatBytes(Number(value)) : - name === 'Total Memory' ? formatBytes(Number(value)) : value, - name - ]} - /> - - - - - - -
- ); -}; \ No newline at end of file diff --git a/application/src/components/servers/charts/NetworkChart.tsx b/application/src/components/servers/charts/NetworkChart.tsx new file mode 100644 index 0000000..cbb25b5 --- /dev/null +++ b/application/src/components/servers/charts/NetworkChart.tsx @@ -0,0 +1,102 @@ + +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { ChartContainer, ChartTooltip } from "@/components/ui/chart"; +import { LineChart, Line, XAxis, YAxis, CartesianGrid } from "recharts"; +import { Wifi } from "lucide-react"; +import { useTheme } from "@/contexts/ThemeContext"; +import { NetworkTooltipContent } from "./tooltips/NetworkTooltipContent"; + +interface NetworkChartProps { + data: any[]; + latestData?: any; +} + +export const NetworkChart = ({ data, latestData }: NetworkChartProps) => { + const { theme } = useTheme(); + + const getGridColor = () => theme === 'dark' ? '#374151' : '#e5e7eb'; + const getAxisColor = () => theme === 'dark' ? '#9ca3af' : '#6b7280'; + + return ( + + + +
+
+ +
+ Network Traffic +
+ {latestData && ( +
+
{latestData.networkRxSpeed} KB/s ↓
+
{latestData.networkTxSpeed} KB/s ↑
+
+ )} +
+
+ + + + + + + + + + + + + + + + + + + + } + cursor={{ stroke: getGridColor() }} + /> + + + + + +
+ ); +}; \ No newline at end of file diff --git a/application/src/components/servers/charts/NetworkCharts.tsx b/application/src/components/servers/charts/NetworkCharts.tsx deleted file mode 100644 index 2ad4c16..0000000 --- a/application/src/components/servers/charts/NetworkCharts.tsx +++ /dev/null @@ -1,140 +0,0 @@ - -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"; -import { LineChart, Line, AreaChart, Area, XAxis, YAxis, CartesianGrid } from "recharts"; -import { Network } from "lucide-react"; -import { useChartConfig } from "./chartConfig"; - -interface NetworkChartsProps { - data: any[]; -} - -export const NetworkCharts = ({ data }: NetworkChartsProps) => { - const { chartConfig, getGridColor, getAxisColor } = useChartConfig(); - - return ( -
- -
- - -
- -
- Network Traffic -
-
- - - - - - - - - - - - - - - - - } - cursor={{ stroke: '#8b5cf6', strokeWidth: 2, strokeOpacity: 0.5 }} - /> - - - - - - - - -
- - -
- -
- Network Speed (KB/s) -
-
- - - - - - - - - - - - - - - - - } - cursor={{ stroke: '#8b5cf6', strokeWidth: 2, strokeOpacity: 0.5 }} - /> - - - - - - -
- ); -}; \ No newline at end of file diff --git a/application/src/components/servers/charts/TimeRangeSelector.tsx b/application/src/components/servers/charts/TimeRangeSelector.tsx index 6847b1c..7a9f5f2 100644 --- a/application/src/components/servers/charts/TimeRangeSelector.tsx +++ b/application/src/components/servers/charts/TimeRangeSelector.tsx @@ -1,6 +1,5 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { timeRangeOptions } from "./dataUtils"; type TimeRange = '60m' | '1d' | '7d' | '1m' | '3m'; @@ -9,10 +8,18 @@ interface TimeRangeSelectorProps { onChange: (value: TimeRange) => void; } +const timeRangeOptions = [ + { value: '60m' as TimeRange, label: 'Last 60 minutes' }, + { value: '1d' as TimeRange, label: 'Last 24 hours' }, + { value: '7d' as TimeRange, label: 'Last 7 days' }, + { value: '1m' as TimeRange, label: 'Last 30 days' }, + { value: '3m' as TimeRange, label: 'Last 90 days' }, +]; + export const TimeRangeSelector = ({ value, onChange }: TimeRangeSelectorProps) => { return (