feat: add disk avg/min to node table, add NAS/Ingress/Istio detail tables
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:
Cloud User 2026-03-17 12:59:09 +09:00
parent ba52cb31c1
commit 57dcad38ff
4 changed files with 121 additions and 0 deletions

View File

@ -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>

View File

@ -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>
);
}

View File

@ -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>
);
}

View File

@ -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>
); );
})} })}