109 lines
3.9 KiB
TypeScript
109 lines
3.9 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { RefreshCw, TrendingUp, Cpu, HardDrive, Activity } from 'lucide-react';
|
|
import { api } from '@/lib/api';
|
|
import { MetricsChart } from '@/components/metrics/MetricsChart';
|
|
|
|
const METRIC_PRESETS = [
|
|
{ name: 'CPU Usage', metric: 'cpu.usage_percent', service: 'system', icon: Cpu },
|
|
{ name: 'Memory Usage', metric: 'memory.used_percent', service: 'system', icon: Activity },
|
|
{ name: 'Load Average', metric: 'cpu.load_avg_1', service: 'system', icon: TrendingUp },
|
|
{ name: 'Disk Usage', metric: 'disk.used_percent', service: 'system', icon: HardDrive },
|
|
{ name: 'Network Sent', metric: 'network.bytes_sent', service: 'system', icon: TrendingUp },
|
|
{ name: 'Network Recv', metric: 'network.bytes_recv', service: 'system', icon: TrendingUp },
|
|
{ name: 'Containers Running', metric: 'containers.running', service: 'docker', icon: Activity },
|
|
{ name: 'Container CPU', metric: 'container.cpu_percent', service: 'docker', icon: Cpu },
|
|
];
|
|
|
|
export default function MetricsPage() {
|
|
const [timeRange, setTimeRange] = useState('1h');
|
|
const [selectedMetrics, setSelectedMetrics] = useState<string[]>([
|
|
'cpu.usage_percent',
|
|
'memory.used_percent',
|
|
]);
|
|
|
|
const getTimeFrom = () => {
|
|
const ranges: Record<string, number> = {
|
|
'15m': 15 * 60 * 1000,
|
|
'1h': 60 * 60 * 1000,
|
|
'6h': 6 * 60 * 60 * 1000,
|
|
'24h': 24 * 60 * 60 * 1000,
|
|
};
|
|
return new Date(Date.now() - (ranges[timeRange] || ranges['1h'])).toISOString();
|
|
};
|
|
|
|
const toggleMetric = (metric: string) => {
|
|
setSelectedMetrics((prev) =>
|
|
prev.includes(metric)
|
|
? prev.filter((m) => m !== metric)
|
|
: [...prev, metric]
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="p-6 space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-2xl font-bold text-white">Metrics</h1>
|
|
<div className="flex items-center gap-4">
|
|
<select
|
|
value={timeRange}
|
|
onChange={(e) => setTimeRange(e.target.value)}
|
|
className="px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-white focus:outline-none focus:border-indigo-500"
|
|
>
|
|
<option value="15m">Last 15 minutes</option>
|
|
<option value="1h">Last hour</option>
|
|
<option value="6h">Last 6 hours</option>
|
|
<option value="24h">Last 24 hours</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Metric Selector */}
|
|
<div className="flex flex-wrap gap-2">
|
|
{METRIC_PRESETS.map((preset) => {
|
|
const Icon = preset.icon;
|
|
const isSelected = selectedMetrics.includes(preset.metric);
|
|
return (
|
|
<button
|
|
key={preset.metric}
|
|
onClick={() => toggleMetric(preset.metric)}
|
|
className={`flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors ${
|
|
isSelected
|
|
? 'bg-indigo-600 border-indigo-500 text-white'
|
|
: 'bg-gray-800 border-gray-700 text-gray-300 hover:border-gray-600'
|
|
}`}
|
|
>
|
|
<Icon className="h-4 w-4" />
|
|
{preset.name}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Charts Grid */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
{selectedMetrics.map((metric) => {
|
|
const preset = METRIC_PRESETS.find((p) => p.metric === metric);
|
|
return (
|
|
<MetricsChart
|
|
key={metric}
|
|
title={preset?.name || metric}
|
|
service={preset?.service || 'system'}
|
|
metric={metric}
|
|
from={getTimeFrom()}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{selectedMetrics.length === 0 && (
|
|
<div className="flex items-center justify-center h-64 bg-gray-900/50 rounded-lg border border-gray-800 text-gray-500">
|
|
Select metrics to display
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|