feat: add disk avg/min to node table, add NAS/Ingress/Istio detail tables
CI/CD / build-and-push (push) Successful in 1m27s
Details
CI/CD / build-and-push (push) Successful in 1m27s
Details
- NodeTable: disk now shows avg, max, min (was max only) - NasTable: mountpoint-based avg/max/min - NetworkTable: RX/TX avg/max/min for Ingress and Istio Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ba52cb31c1
commit
57dcad38ff
|
|
@ -7,6 +7,8 @@ import SummaryCards from '@/components/report/SummaryCards';
|
||||||
import TimeSeriesChart from '@/components/charts/TimeSeriesChart';
|
import TimeSeriesChart from '@/components/charts/TimeSeriesChart';
|
||||||
import NetworkChart from '@/components/charts/NetworkChart';
|
import NetworkChart from '@/components/charts/NetworkChart';
|
||||||
import NodeTable from '@/components/report/NodeTable';
|
import NodeTable from '@/components/report/NodeTable';
|
||||||
|
import NasTable from '@/components/report/NasTable';
|
||||||
|
import NetworkTable from '@/components/report/NetworkTable';
|
||||||
import AlertSection from '@/components/report/AlertSection';
|
import AlertSection from '@/components/report/AlertSection';
|
||||||
import ErrorBoundary from '@/components/ErrorBoundary';
|
import ErrorBoundary from '@/components/ErrorBoundary';
|
||||||
|
|
||||||
|
|
@ -180,6 +182,29 @@ export default function Home() {
|
||||||
</div>
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|
||||||
|
{data.metrics.nas && data.metrics.nas.length > 0 && (
|
||||||
|
<ErrorBoundary fallback="NAS 상세 오류">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-lg font-semibold text-gray-800 mb-3">NAS 사용률 상세</h2>
|
||||||
|
<NasTable nas={data.metrics.nas} />
|
||||||
|
</div>
|
||||||
|
</ErrorBoundary>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ErrorBoundary fallback="Ingress 상세 오류">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-lg font-semibold text-gray-800 mb-3">Ingress Network 상세</h2>
|
||||||
|
<NetworkTable title="Ingress" data={data.metrics.ingress} />
|
||||||
|
</div>
|
||||||
|
</ErrorBoundary>
|
||||||
|
|
||||||
|
<ErrorBoundary fallback="Istio 상세 오류">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-lg font-semibold text-gray-800 mb-3">Istio Network 상세</h2>
|
||||||
|
<NetworkTable title="Istio" data={data.metrics.istio} />
|
||||||
|
</div>
|
||||||
|
</ErrorBoundary>
|
||||||
|
|
||||||
<ErrorBoundary fallback="AlertSection 오류">
|
<ErrorBoundary fallback="AlertSection 오류">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold text-gray-800 mb-3">주의 구간</h2>
|
<h2 className="text-lg font-semibold text-gray-800 mb-3">주의 구간</h2>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import type { MetricInstance } from '@/types/metrics';
|
||||||
|
import { formatPercent } from '@/lib/formatters';
|
||||||
|
|
||||||
|
interface NasTableProps {
|
||||||
|
nas: MetricInstance[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NasTable({ nas }: NasTableProps) {
|
||||||
|
if (!nas || nas.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="min-w-full bg-white rounded-lg shadow">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-gray-50 border-b">
|
||||||
|
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Mountpoint</th>
|
||||||
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">Avg</th>
|
||||||
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">Max</th>
|
||||||
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">Min</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-gray-200">
|
||||||
|
{nas.map((inst) => (
|
||||||
|
<tr key={inst.instance} className="hover:bg-gray-50">
|
||||||
|
<td className="px-4 py-3 text-sm font-medium">{inst.instance || '-'}</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right">{formatPercent(inst.stats.avg)}</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right font-medium">{formatPercent(inst.stats.max)}</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right text-gray-400">{formatPercent(inst.stats.min)}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import type { NetworkMetrics } from '@/types/metrics';
|
||||||
|
import { formatBytesPerSec } from '@/lib/formatters';
|
||||||
|
|
||||||
|
interface NetworkTableProps {
|
||||||
|
title: string;
|
||||||
|
data: NetworkMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NetworkTable({ title, data }: NetworkTableProps) {
|
||||||
|
const hasData = data.receive.length > 0 || data.transmit.length > 0;
|
||||||
|
if (!hasData) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="min-w-full bg-white rounded-lg shadow">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-gray-50 border-b">
|
||||||
|
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">{title}</th>
|
||||||
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">Avg</th>
|
||||||
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">Max</th>
|
||||||
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">Min</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-gray-200">
|
||||||
|
{data.receive.map((inst, idx) => (
|
||||||
|
<tr key={`rx-${idx}`} className="hover:bg-gray-50">
|
||||||
|
<td className="px-4 py-3 text-sm font-medium">
|
||||||
|
<span className="inline-block px-2 py-0.5 rounded text-xs bg-blue-100 text-blue-700 mr-2">RX</span>
|
||||||
|
{inst.instance || 'total'}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right">{formatBytesPerSec(inst.stats.avg)}</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right font-medium">{formatBytesPerSec(inst.stats.max)}</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right text-gray-400">{formatBytesPerSec(inst.stats.min)}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
{data.transmit.map((inst, idx) => (
|
||||||
|
<tr key={`tx-${idx}`} className="hover:bg-gray-50">
|
||||||
|
<td className="px-4 py-3 text-sm font-medium">
|
||||||
|
<span className="inline-block px-2 py-0.5 rounded text-xs bg-green-100 text-green-700 mr-2">TX</span>
|
||||||
|
{inst.instance || 'total'}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right">{formatBytesPerSec(inst.stats.avg)}</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right font-medium">{formatBytesPerSec(inst.stats.max)}</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right text-gray-400">{formatBytesPerSec(inst.stats.min)}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -31,7 +31,9 @@ export default function NodeTable({ nodes, cpu, memory, disk }: NodeTableProps)
|
||||||
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">MEM avg</th>
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">MEM avg</th>
|
||||||
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">MEM max</th>
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">MEM max</th>
|
||||||
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">MEM min</th>
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">MEM min</th>
|
||||||
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">DISK avg</th>
|
||||||
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">DISK max</th>
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">DISK max</th>
|
||||||
|
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">DISK min</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-200">
|
<tbody className="divide-y divide-gray-200">
|
||||||
|
|
@ -70,9 +72,13 @@ export default function NodeTable({ nodes, cpu, memory, disk }: NodeTableProps)
|
||||||
<td className="px-4 py-3 text-sm text-right text-gray-400">
|
<td className="px-4 py-3 text-sm text-right text-gray-400">
|
||||||
{memStats ? formatPercent(memStats.min) : '-'}
|
{memStats ? formatPercent(memStats.min) : '-'}
|
||||||
</td>
|
</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right">{diskStats ? formatPercent(diskStats.avg) : '-'}</td>
|
||||||
<td className="px-4 py-3 text-sm text-right font-medium">
|
<td className="px-4 py-3 text-sm text-right font-medium">
|
||||||
{diskStats ? formatPercent(diskStats.max) : '-'}
|
{diskStats ? formatPercent(diskStats.max) : '-'}
|
||||||
</td>
|
</td>
|
||||||
|
<td className="px-4 py-3 text-sm text-right text-gray-400">
|
||||||
|
{diskStats ? formatPercent(diskStats.min) : '-'}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue