From c10b0dc353e3f3c2cdefeb6c1322cd506bb61140 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 24 May 2026 22:43:46 +0100 Subject: [PATCH] Keep metric bars visually critical when alerts are disabled Refs #1319 --- .../components/Dashboard/EnhancedCPUBar.tsx | 11 ++------- .../components/Dashboard/MetricBar.test.tsx | 14 +++++++++++ .../src/components/Dashboard/MetricBar.tsx | 8 ++----- .../components/Dashboard/StackedDiskBar.tsx | 23 ++++--------------- .../components/Dashboard/StackedMemoryBar.tsx | 11 ++------- .../responsive/ResponsiveMetricCell.tsx | 8 ++----- .../utils/__tests__/alertThresholds.test.ts | 6 +++++ frontend-modern/src/utils/alertThresholds.ts | 9 ++++++++ 8 files changed, 42 insertions(+), 48 deletions(-) diff --git a/frontend-modern/src/components/Dashboard/EnhancedCPUBar.tsx b/frontend-modern/src/components/Dashboard/EnhancedCPUBar.tsx index 3d1d26383..d78742890 100644 --- a/frontend-modern/src/components/Dashboard/EnhancedCPUBar.tsx +++ b/frontend-modern/src/components/Dashboard/EnhancedCPUBar.tsx @@ -6,8 +6,7 @@ import { getMetricHistoryForRange, getMetricsVersion } from '@/stores/metricsHis import { Sparkline } from '@/components/shared/Sparkline'; import type { AnomalyReport } from '@/types/aiIntelligence'; import { - getDefaultMetricDisplayThresholds, - getMetricSeverity, + getMetricVisualSeverity, type MetricDisplayThresholds, } from '@/utils/alertThresholds'; @@ -36,13 +35,7 @@ export function EnhancedCPUBar(props: EnhancedCPUBarProps) { // Bar color based on usage const severity = createMemo(() => { - const thresholds = props.thresholds === undefined - ? getDefaultMetricDisplayThresholds('cpu') - : props.thresholds; - return getMetricSeverity( - props.usage, - thresholds, - ); + return getMetricVisualSeverity(props.usage, 'cpu', props.thresholds); }); const barColor = createMemo(() => { diff --git a/frontend-modern/src/components/Dashboard/MetricBar.test.tsx b/frontend-modern/src/components/Dashboard/MetricBar.test.tsx index add6e7d2c..8dbaf4a3b 100644 --- a/frontend-modern/src/components/Dashboard/MetricBar.test.tsx +++ b/frontend-modern/src/components/Dashboard/MetricBar.test.tsx @@ -154,6 +154,20 @@ describe('MetricBar', () => { expect(bar).toBeInTheDocument(); }); + it('uses default display colors when notification thresholds are disabled', () => { + const result = render(() => ( + + )); + + const bar = result.container.querySelector('.bg-red-500\\/60'); + expect(bar).toBeInTheDocument(); + }); + it('toggles sparkline view mode', () => { mockUseMetricsViewMode.mockReturnValue({ viewMode: () => 'sparklines', diff --git a/frontend-modern/src/components/Dashboard/MetricBar.tsx b/frontend-modern/src/components/Dashboard/MetricBar.tsx index 686f1bd00..9dcc6d575 100644 --- a/frontend-modern/src/components/Dashboard/MetricBar.tsx +++ b/frontend-modern/src/components/Dashboard/MetricBar.tsx @@ -3,8 +3,7 @@ import { Sparkline } from '@/components/shared/Sparkline'; import { useMetricsViewMode } from '@/stores/metricsViewMode'; import { getMetricHistoryForRange, getMetricsVersion } from '@/stores/metricsHistory'; import { - getDefaultMetricDisplayThresholds, - getMetricSeverity, + getMetricVisualSeverity, type MetricDisplayThresholds, } from '@/utils/alertThresholds'; @@ -60,10 +59,7 @@ export function MetricBar(props: MetricBarProps) { // Get color based on percentage and metric type (matching original) const severity = createMemo(() => { const metric = props.type || 'generic'; - const thresholds = props.thresholds === undefined - ? getDefaultMetricDisplayThresholds(metric) - : props.thresholds; - return getMetricSeverity(props.value, thresholds); + return getMetricVisualSeverity(props.value, metric, props.thresholds); }); // Map color to CSS classes diff --git a/frontend-modern/src/components/Dashboard/StackedDiskBar.tsx b/frontend-modern/src/components/Dashboard/StackedDiskBar.tsx index 9d8e0fec8..e73a61f78 100644 --- a/frontend-modern/src/components/Dashboard/StackedDiskBar.tsx +++ b/frontend-modern/src/components/Dashboard/StackedDiskBar.tsx @@ -4,8 +4,7 @@ import type { Disk } from '@/types/api'; import { formatBytes, formatPercent } from '@/utils/format'; import type { AnomalyReport } from '@/types/aiIntelligence'; import { - getDefaultMetricDisplayThresholds, - getMetricSeverity, + getMetricVisualSeverity, type MetricDisplayThresholds, } from '@/utils/alertThresholds'; @@ -45,13 +44,7 @@ function getUsageColor( percentage: number, thresholds?: MetricDisplayThresholds | null, ): string { - const resolvedThresholds = thresholds === undefined - ? getDefaultMetricDisplayThresholds('disk') - : thresholds; - const severity = getMetricSeverity( - percentage, - resolvedThresholds, - ); + const severity = getMetricVisualSeverity(percentage, 'disk', thresholds); if (severity === 'red') return 'rgba(239, 68, 68, 0.6)'; // red if (severity === 'yellow') return 'rgba(234, 179, 8, 0.6)'; // yellow return 'rgba(34, 197, 94, 0.6)'; // green @@ -138,10 +131,7 @@ export function StackedDiskBar(props: StackedDiskBarProps) { const usedPercent = (disk.used / total) * 100; const diskPercent = disk.total > 0 ? (disk.used / disk.total) * 100 : 0; // Use warning/critical colors for high usage, otherwise use the color palette - const color = getMetricSeverity( - diskPercent, - props.thresholds === undefined ? getDefaultMetricDisplayThresholds('disk') : props.thresholds, - ) !== 'green' + const color = getMetricVisualSeverity(diskPercent, 'disk', props.thresholds) !== 'green' ? getUsageColor(diskPercent, props.thresholds) : SEGMENT_COLORS[idx % SEGMENT_COLORS.length]; @@ -207,7 +197,7 @@ export function StackedDiskBar(props: StackedDiskBarProps) { const barColor = createMemo(() => { const info = maxDiskInfo(); if (aggregateMode() && hasMultipleDisks() && info) { - return getUsageColor(info.percent); + return getUsageColor(info.percent, props.thresholds); } return getUsageColor(overallPercent(), props.thresholds); }); @@ -227,10 +217,7 @@ export function StackedDiskBar(props: StackedDiskBarProps) { percent: measured ? formatPercent(percent) : '—', color: useUsageColors ? getUsageColor(percent, props.thresholds) - : getMetricSeverity( - percent, - props.thresholds === undefined ? getDefaultMetricDisplayThresholds('disk') : props.thresholds, - ) !== 'green' + : getMetricVisualSeverity(percent, 'disk', props.thresholds) !== 'green' ? getUsageColor(percent, props.thresholds) : SEGMENT_COLORS[idx % SEGMENT_COLORS.length], }; diff --git a/frontend-modern/src/components/Dashboard/StackedMemoryBar.tsx b/frontend-modern/src/components/Dashboard/StackedMemoryBar.tsx index 1a5e0270e..195648ef9 100644 --- a/frontend-modern/src/components/Dashboard/StackedMemoryBar.tsx +++ b/frontend-modern/src/components/Dashboard/StackedMemoryBar.tsx @@ -6,8 +6,7 @@ import { useMetricsViewMode } from '@/stores/metricsViewMode'; import { getMetricHistoryForRange, getMetricsVersion } from '@/stores/metricsHistory'; import type { AnomalyReport } from '@/types/aiIntelligence'; import { - getDefaultMetricDisplayThresholds, - getMetricSeverity, + getMetricVisualSeverity, type MetricDisplayThresholds, } from '@/utils/alertThresholds'; @@ -44,13 +43,7 @@ const getMemoryColor = ( percent: number, thresholds?: MetricDisplayThresholds | null, ): string => { - const resolvedThresholds = thresholds === undefined - ? getDefaultMetricDisplayThresholds('memory') - : thresholds; - const severity = getMetricSeverity( - percent, - resolvedThresholds, - ); + const severity = getMetricVisualSeverity(percent, 'memory', thresholds); if (severity === 'red') return 'rgba(239, 68, 68, 0.7)'; // red if (severity === 'yellow') return 'rgba(234, 179, 8, 0.7)'; // yellow/orange return 'rgba(34, 197, 94, 0.6)'; // green diff --git a/frontend-modern/src/components/shared/responsive/ResponsiveMetricCell.tsx b/frontend-modern/src/components/shared/responsive/ResponsiveMetricCell.tsx index 09b9bc55a..830cca9f8 100644 --- a/frontend-modern/src/components/shared/responsive/ResponsiveMetricCell.tsx +++ b/frontend-modern/src/components/shared/responsive/ResponsiveMetricCell.tsx @@ -2,8 +2,7 @@ import { Component, Show, createMemo, JSX } from 'solid-js'; import { MetricBar } from '@/components/Dashboard/MetricBar'; import { formatPercent } from '@/utils/format'; import { - getDefaultMetricDisplayThresholds, - getMetricSeverity, + getMetricVisualSeverity, type MetricDisplayThresholds, } from '@/utils/alertThresholds'; @@ -47,10 +46,7 @@ function getMetricColorClass( type: 'cpu' | 'memory' | 'disk', thresholds?: MetricDisplayThresholds | null, ): string { - const resolvedThresholds = thresholds === undefined - ? getDefaultMetricDisplayThresholds(type) - : thresholds; - const severity = getMetricSeverity(value, resolvedThresholds); + const severity = getMetricVisualSeverity(value, type, thresholds); if (severity === 'red') return 'text-red-600 dark:text-red-400 font-bold'; if (severity === 'yellow') return 'text-orange-600 dark:text-orange-400 font-medium'; return 'text-gray-600 dark:text-gray-400'; diff --git a/frontend-modern/src/utils/__tests__/alertThresholds.test.ts b/frontend-modern/src/utils/__tests__/alertThresholds.test.ts index 1788a1ccb..167ce1402 100644 --- a/frontend-modern/src/utils/__tests__/alertThresholds.test.ts +++ b/frontend-modern/src/utils/__tests__/alertThresholds.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest'; import { getDefaultMetricDisplayThresholds, getMetricSeverity, + getMetricVisualSeverity, resolveMetricDisplayThresholds, } from '../alertThresholds'; import type { AlertConfig } from '@/types/alerts'; @@ -75,4 +76,9 @@ describe('alertThresholds', () => { expect(getMetricSeverity(80, thresholds)).toBe('yellow'); expect(getMetricSeverity(85, thresholds)).toBe('red'); }); + + it('keeps visual severity when notification threshold is disabled', () => { + expect(getMetricSeverity(100, null)).toBe('green'); + expect(getMetricVisualSeverity(100, 'memory', null)).toBe('red'); + }); }); diff --git a/frontend-modern/src/utils/alertThresholds.ts b/frontend-modern/src/utils/alertThresholds.ts index 6a5e664f2..0e88bb7fb 100644 --- a/frontend-modern/src/utils/alertThresholds.ts +++ b/frontend-modern/src/utils/alertThresholds.ts @@ -221,3 +221,12 @@ export const getMetricSeverity = ( } return 'green'; }; + +export const getMetricVisualSeverity = ( + value: number, + metric: DisplayMetricBarType, + thresholds: MetricDisplayThresholds | null | undefined, +): MetricSeverity => { + const displayThresholds = thresholds ?? getDefaultMetricDisplayThresholds(metric); + return getMetricSeverity(value, displayThresholds); +};