From 44f3abe8d238937198e9d05ebeb9052d378daba5 Mon Sep 17 00:00:00 2001 From: Juan Denis Date: Mon, 19 Jan 2026 13:30:50 -0500 Subject: [PATCH] Enhance metrics graph visuals and tooltip UI Switches from LineChart to AreaChart for main metrics graph, adds animated gradients and glow effects, and updates legend styling. Tooltip UI is improved with clearer layout, colored dots, and value formatting. Chart colors now use explicit hex values for better SVG compatibility, and LESS styles are updated for improved appearance and interactivity. --- frontend/dist/index.html | 4 +- frontend/src/components/MetricsGraph.jsx | 112 +++++++++++----- .../src/styles/components/_metrics-graph.less | 124 ++++++++++++++++-- 3 files changed, 190 insertions(+), 50 deletions(-) diff --git a/frontend/dist/index.html b/frontend/dist/index.html index d91f330d..25c4a44b 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -6,8 +6,8 @@ React Dashboard Template - - + + diff --git a/frontend/src/components/MetricsGraph.jsx b/frontend/src/components/MetricsGraph.jsx index 18791671..f98d48b6 100644 --- a/frontend/src/components/MetricsGraph.jsx +++ b/frontend/src/components/MetricsGraph.jsx @@ -1,11 +1,18 @@ import React, { useState, useEffect } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, - Tooltip, ResponsiveContainer, Area, AreaChart + Tooltip, ResponsiveContainer, Area, AreaChart, Legend } from 'recharts'; import { Cpu, MemoryStick, HardDrive, Clock, TrendingUp } from 'lucide-react'; import api from '../services/api'; +// Chart colors - using actual hex values for SVG compatibility +const CHART_COLORS = { + cpu: '#10b981', // Green + memory: '#6366f1', // Purple/Indigo + disk: '#f59e0b' // Amber/Orange +}; + const MetricsGraph = ({ compact = false }) => { const [data, setData] = useState(null); const [period, setPeriod] = useState('1h'); @@ -46,13 +53,13 @@ const MetricsGraph = ({ compact = false }) => { function getColor(value, type) { if (type === 'cpu') { - if (value > 80) return 'var(--danger)'; - if (value > 60) return 'var(--warning)'; - return 'var(--success)'; + if (value > 80) return '#ef4444'; // danger + if (value > 60) return '#f59e0b'; // warning + return '#10b981'; // success } - if (value > 90) return 'var(--danger)'; - if (value > 80) return 'var(--warning)'; - return 'var(--accent-primary)'; + if (value > 90) return '#ef4444'; // danger + if (value > 80) return '#f59e0b'; // warning + return '#6366f1'; // accent } if (loading && !data) { @@ -94,9 +101,10 @@ const MetricsGraph = ({ compact = false }) => {
{label}
{payload.map((entry, index) => ( -
- {entry.name}: - {entry.value}% +
+ + {entry.name}: + {entry.value?.toFixed(1)}%
))}
@@ -129,9 +137,9 @@ const MetricsGraph = ({ compact = false }) => { - - - + + + @@ -140,8 +148,8 @@ const MetricsGraph = ({ compact = false }) => { @@ -224,63 +232,97 @@ const MetricsGraph = ({ compact = false }) => {
- - - + + + + {/* CPU Gradient - Green */} + + + + + {/* Memory Gradient - Purple */} + + + + + {/* Disk Gradient - Amber */} + + + + + {/* Glow filters for lines */} + + + + + + + + + `${value}%`} /> } /> - - - - +
- + CPU
- + Memory
- + Disk
diff --git a/frontend/src/styles/components/_metrics-graph.less b/frontend/src/styles/components/_metrics-graph.less index 891f9e3a..b0b1153f 100644 --- a/frontend/src/styles/components/_metrics-graph.less +++ b/frontend/src/styles/components/_metrics-graph.less @@ -85,19 +85,51 @@ // Chart Container .metrics-chart-container { margin-bottom: @space-4; + background: linear-gradient(180deg, rgba(39, 39, 42, 0.3) 0%, transparent 100%); + border-radius: @radius-lg; + padding: @space-2 0; &.compact { margin-bottom: @space-3; + background: transparent; + padding: 0; + } + + // Recharts path animation + .recharts-area-area { + animation: fadeIn 0.6s ease-out; + } + + .recharts-area-curve { + animation: drawLine 1s ease-out; + } +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes drawLine { + from { + stroke-dasharray: 1000; + stroke-dashoffset: 1000; + } + to { + stroke-dasharray: 1000; + stroke-dashoffset: 0; } } // Tooltip .metrics-tooltip { - background: @bg-elevated; + background: rgba(24, 24, 27, 0.95); + backdrop-filter: blur(8px); border: 1px solid @border-subtle; - border-radius: @radius-md; - padding: @space-3; - box-shadow: @shadow-lg; + border-radius: @radius-lg; + padding: @space-3 @space-4; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); + min-width: 140px; .tooltip-time { font-size: @font-size-xs; @@ -105,19 +137,34 @@ margin-bottom: @space-2; padding-bottom: @space-2; border-bottom: 1px solid @border-subtle; + text-align: center; } .tooltip-row { display: flex; - justify-content: space-between; - gap: @space-4; + align-items: center; + gap: @space-2; font-size: @font-size-sm; padding: @space-1 0; + } - span:last-child { - font-family: @font-mono; - font-weight: @font-weight-semibold; - } + .tooltip-dot { + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; + } + + .tooltip-label { + color: @text-secondary; + flex: 1; + } + + .tooltip-value { + font-family: @font-mono; + font-weight: @font-weight-bold; + min-width: 48px; + text-align: right; } } @@ -126,23 +173,60 @@ display: flex; align-items: center; justify-content: center; - gap: @space-6; + gap: @space-8; padding-top: @space-4; + margin-top: @space-2; border-top: 1px solid @border-subtle; } .legend-item { display: flex; align-items: center; - gap: @space-2; + gap: @space-3; font-size: @font-size-sm; color: @text-secondary; + padding: @space-1 @space-3; + border-radius: @radius-full; + transition: @transition-fast; + + &:hover { + background: @bg-hover; + color: @text-primary; + } } .legend-line { - width: 20px; + width: 24px; height: 3px; border-radius: 2px; + position: relative; + + &::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 6px; + height: 6px; + border-radius: 50%; + background: inherit; + } + + &.cpu { + background: #10b981; + box-shadow: 0 0 8px rgba(16, 185, 129, 0.5); + } + + &.memory { + background: #6366f1; + box-shadow: 0 0 8px rgba(99, 102, 241, 0.5); + } + + &.disk { + background: #f59e0b; + box-shadow: 0 0 8px rgba(245, 158, 11, 0.5); + } } // Compact Version @@ -192,12 +276,26 @@ .recharts-cartesian-grid-horizontal line, .recharts-cartesian-grid-vertical line { stroke: @border-subtle; + opacity: 0.5; } .recharts-text { fill: @text-secondary; } +.recharts-area-curve { + filter: drop-shadow(0 0 4px currentColor); +} + +.recharts-active-dot { + filter: drop-shadow(0 0 6px currentColor); +} + +.recharts-tooltip-cursor { + stroke: @text-tertiary; + stroke-dasharray: 4 4; +} + // Responsive @media (max-width: @breakpoint-md) { .metrics-summary-row {