Spaces:
Sleeping
Sleeping
| import { useState } from 'react'; | |
| import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; | |
| import { Metrics } from '../types'; | |
| interface MetricsChartProps { | |
| data: Metrics | null; | |
| metric: 'cpu' | 'memory'; | |
| loading?: boolean; | |
| } | |
| export function MetricsChart({ data, metric, loading }: MetricsChartProps) { | |
| const [visible, setVisible] = useState({ avg: true, max: true, min: true }); | |
| const chartData = data?.timestamps.map((ts, i) => ({ | |
| time: new Date(ts).toLocaleTimeString(), | |
| avg: data[metric].avg[i] ?? 0, | |
| max: data[metric].max[i] ?? 0, | |
| min: data[metric].min[i] ?? 0, | |
| })) || []; | |
| const colors = { | |
| avg: '#60a5fa', | |
| max: '#f87171', | |
| min: '#4ade80', | |
| }; | |
| const label = metric === 'cpu' ? 'CPU %' : 'Memory %'; | |
| if (loading) { | |
| return ( | |
| <div className="bg-gray-800 rounded-lg p-4 animate-pulse"> | |
| <div className="h-64 bg-gray-700 rounded"></div> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className="bg-gray-800 rounded-lg p-4 border border-gray-700"> | |
| <div className="flex items-center justify-between mb-4"> | |
| <h3 className="text-lg font-semibold text-white">{label}</h3> | |
| <div className="flex gap-2"> | |
| {(['avg', 'max', 'min'] as const).map((key) => ( | |
| <button | |
| key={key} | |
| onClick={() => setVisible(v => ({ ...v, [key]: !v[key] }))} | |
| className={`px-2 py-1 rounded text-xs transition ${ | |
| visible[key] ? 'bg-gray-600 text-white' : 'bg-gray-700 text-gray-400' | |
| }`} | |
| > | |
| {key.toUpperCase()} | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="h-64"> | |
| <ResponsiveContainer width="100%" height="100%"> | |
| <LineChart data={chartData}> | |
| <CartesianGrid strokeDasharray="3 3" stroke="#374151" /> | |
| <XAxis dataKey="time" stroke="#9ca3af" fontSize={10} /> | |
| <YAxis stroke="#9ca3af" fontSize={10} domain={[0, 100]} /> | |
| <Tooltip | |
| contentStyle={{ backgroundColor: '#1f2937', border: 'none', borderRadius: '8px' }} | |
| labelStyle={{ color: '#9ca3af' }} | |
| /> | |
| <Legend /> | |
| {visible.avg && ( | |
| <Line type="monotone" dataKey="avg" stroke={colors.avg} strokeWidth={2} dot={false} name="Avg" /> | |
| )} | |
| {visible.max && ( | |
| <Line type="monotone" dataKey="max" stroke={colors.max} strokeWidth={2} dot={false} name="Max" /> | |
| )} | |
| {visible.min && ( | |
| <Line type="monotone" dataKey="min" stroke={colors.min} strokeWidth={2} dot={false} name="Min" /> | |
| )} | |
| </LineChart> | |
| </ResponsiveContainer> | |
| </div> | |
| {chartData.length === 0 && ( | |
| <div className="absolute inset-0 flex items-center justify-center text-gray-500"> | |
| No data available | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } |