SpaceProbe1 / frontend /src /components /MetricsChart.tsx
a9's picture
Upload 27 files
9b2dc95 verified
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>
);
}