infra-report/src/components/report/SummaryCards.tsx

93 lines
3.1 KiB
TypeScript

'use client';
import { formatPercent, formatBytesPerSec } from '@/lib/formatters';
import type { MetricInstance, NetworkMetrics } from '@/types/metrics';
interface SummaryCardsProps {
cpu: MetricInstance[];
memory: MetricInstance[];
disk: MetricInstance[];
ingress: NetworkMetrics;
istio: NetworkMetrics;
}
interface CardData {
label: string;
avg: string;
max: string;
min: string;
color: string;
}
function getClusterStats(instances: MetricInstance[]): { avg: number; max: number; min: number } {
if (instances.length === 0) return { avg: 0, max: 0, min: 0 };
const avg = instances.reduce((sum, i) => sum + i.stats.avg, 0) / instances.length;
const max = Math.max(...instances.map((i) => i.stats.max));
const min = Math.min(...instances.map((i) => i.stats.min));
return { avg, max, min };
}
export default function SummaryCards({ cpu, memory, disk, ingress, istio }: SummaryCardsProps) {
const cpuStats = getClusterStats(cpu);
const memStats = getClusterStats(memory);
const diskStats = getClusterStats(disk);
const ingressRxAvg = ingress.receive.reduce((sum, i) => sum + i.stats.avg, 0);
const ingressTxAvg = ingress.transmit.reduce((sum, i) => sum + i.stats.avg, 0);
const istioRxAvg = istio.receive.reduce((sum, i) => sum + i.stats.avg, 0);
const istioTxAvg = istio.transmit.reduce((sum, i) => sum + i.stats.avg, 0);
const cards: CardData[] = [
{
label: 'CPU',
avg: formatPercent(cpuStats.avg),
max: formatPercent(cpuStats.max),
min: formatPercent(cpuStats.min),
color: cpuStats.max > 90 ? 'border-red-500' : cpuStats.max > 70 ? 'border-yellow-500' : 'border-green-500',
},
{
label: 'Memory',
avg: formatPercent(memStats.avg),
max: formatPercent(memStats.max),
min: formatPercent(memStats.min),
color: memStats.max > 90 ? 'border-red-500' : memStats.max > 70 ? 'border-yellow-500' : 'border-green-500',
},
{
label: 'Disk',
avg: formatPercent(diskStats.avg),
max: formatPercent(diskStats.max),
min: formatPercent(diskStats.min),
color: diskStats.max > 90 ? 'border-red-500' : diskStats.max > 70 ? 'border-yellow-500' : 'border-green-500',
},
{
label: 'Ingress',
avg: `RX ${formatBytesPerSec(ingressRxAvg)}`,
max: `TX ${formatBytesPerSec(ingressTxAvg)}`,
min: '',
color: 'border-blue-500',
},
{
label: 'Istio',
avg: `RX ${formatBytesPerSec(istioRxAvg)}`,
max: `TX ${formatBytesPerSec(istioTxAvg)}`,
min: '',
color: 'border-purple-500',
},
];
return (
<div className="grid grid-cols-2 lg:grid-cols-5 gap-4">
{cards.map((card) => (
<div key={card.label} className={`bg-white rounded-lg shadow p-4 border-l-4 ${card.color}`}>
<h3 className="text-sm font-medium text-gray-500">{card.label}</h3>
<div className="mt-2 space-y-1">
<p className="text-lg font-semibold">avg: {card.avg}</p>
<p className="text-sm text-gray-600">max: {card.max}</p>
{card.min && <p className="text-sm text-gray-600">min: {card.min}</p>}
</div>
</div>
))}
</div>
);
}