diff --git a/src/app/page.tsx b/src/app/page.tsx index e3bedc6..b424696 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,7 +9,7 @@ import NetworkChart from '@/components/charts/NetworkChart'; 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 AlertPanel from '@/components/report/AlertPanel'; import ErrorBoundary from '@/components/ErrorBoundary'; function getDefaultRange() { @@ -146,15 +146,30 @@ export default function Home() { /> - - - - - - - - - +
+ + + + + + +
+
+ + + + + + +
+
+ + + + + + +
{data.metrics.nas && data.metrics.nas.length > 0 && ( @@ -205,16 +220,6 @@ export default function Home() { - -
-

주의 구간

- -
-
); diff --git a/src/components/report/AlertPanel.tsx b/src/components/report/AlertPanel.tsx new file mode 100644 index 0000000..91648db --- /dev/null +++ b/src/components/report/AlertPanel.tsx @@ -0,0 +1,99 @@ +'use client'; + +import type { MetricInstance } from '@/types/metrics'; +import { formatTimestamp, formatPercent } from '@/lib/formatters'; + +interface AlertPanelProps { + instances: MetricInstance[]; + type: 'cpu' | 'memory' | 'disk'; + height?: number; +} + +interface Alert { + level: 'warning' | 'critical'; + instance: string; + message: string; +} + +function generateAlerts(instances: MetricInstance[], type: string): Alert[] { + const alerts: Alert[] = []; + + instances.forEach((inst) => { + if (type === 'cpu') { + if (inst.stats.max > 90) { + alerts.push({ + level: 'critical', + instance: inst.instance, + message: `최대 ${formatPercent(inst.stats.max)} 도달`, + }); + } + inst.peaks.peaks.forEach((peak) => { + alerts.push({ + level: 'warning', + instance: inst.instance, + message: `${formatTimestamp(peak.startTime)} ~ ${formatTimestamp(peak.endTime)} Peak (max: ${formatPercent(peak.maxValue)})`, + }); + }); + } else if (type === 'memory') { + if (inst.stats.max > 85) { + alerts.push({ + level: inst.stats.max > 95 ? 'critical' : 'warning', + instance: inst.instance, + message: `최대 ${formatPercent(inst.stats.max)} 도달`, + }); + } + inst.peaks.peaks.forEach((peak) => { + alerts.push({ + level: 'warning', + instance: inst.instance, + message: `${formatTimestamp(peak.startTime)} ~ ${formatTimestamp(peak.endTime)} Peak (max: ${formatPercent(peak.maxValue)})`, + }); + }); + } else if (type === 'disk') { + if (inst.stats.max > 80) { + alerts.push({ + level: inst.stats.max > 90 ? 'critical' : 'warning', + instance: inst.instance, + message: `사용률 ${formatPercent(inst.stats.max)} (증설 검토 필요)`, + }); + } + } + }); + + return alerts; +} + +export default function AlertPanel({ instances, type, height = 300 }: AlertPanelProps) { + const alerts = generateAlerts(instances, type); + + if (alerts.length === 0) { + return ( +
+

특이사항 없음

+
+ ); + } + + return ( +
+
+

주의 구간 ({alerts.length}건)

+
+ +
+ ); +}