Implement comprehensive backup monitoring and fix timestamp issues
- Add BackupCollector for reading TOML status files with disk space metrics - Implement BackupWidget with disk usage display and service status details - Fix backup script disk space parsing by adding missing capture_output=True - Update backup widget to show actual disk usage instead of repository size - Fix timestamp parsing to use backup completion time instead of start time - Resolve timezone issues by using UTC timestamps in backup script - Add disk identification metrics (product name, serial number) to backup status - Enhance UI layout with proper backup monitoring integration
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
use cm_dashboard_shared::{Metric, MetricValue, Status};
|
||||
use cm_dashboard_shared::{Metric, Status};
|
||||
use ratatui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::{Color, Style},
|
||||
widgets::{Block, Borders, Gauge, Paragraph},
|
||||
text::{Line, Span},
|
||||
widgets::Paragraph,
|
||||
Frame,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use super::Widget;
|
||||
use crate::ui::theme::Theme;
|
||||
use crate::ui::theme::{Theme, Typography, Components, StatusIcons};
|
||||
|
||||
/// CPU widget displaying load, temperature, and frequency
|
||||
pub struct CpuWidget {
|
||||
@@ -40,11 +38,6 @@ impl CpuWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get status color for display (btop-style)
|
||||
fn get_status_color(&self) -> Color {
|
||||
Theme::status_color(self.status)
|
||||
}
|
||||
|
||||
/// Format load average for display
|
||||
fn format_load(&self) -> String {
|
||||
match (self.load_1min, self.load_5min, self.load_15min) {
|
||||
@@ -55,14 +48,6 @@ impl CpuWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Format temperature for display
|
||||
fn format_temperature(&self) -> String {
|
||||
match self.temperature {
|
||||
Some(temp) => format!("{:.1}°C", temp),
|
||||
None => "—°C".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Format frequency for display
|
||||
fn format_frequency(&self) -> String {
|
||||
match self.frequency {
|
||||
@@ -70,45 +55,7 @@ impl CpuWidget {
|
||||
None => "— MHz".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get load percentage for gauge (based on load_1min)
|
||||
fn get_load_percentage(&self) -> u16 {
|
||||
match self.load_1min {
|
||||
Some(load) => {
|
||||
// Assume 8-core system, so 100% = load of 8.0
|
||||
let percentage = (load / 8.0 * 100.0).min(100.0).max(0.0);
|
||||
percentage as u16
|
||||
}
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create btop-style dotted bar pattern (like real btop)
|
||||
fn create_btop_dotted_bar(&self, percentage: u16, width: usize) -> String {
|
||||
let filled = (width * percentage as usize) / 100;
|
||||
let empty = width.saturating_sub(filled);
|
||||
|
||||
// Real btop uses these patterns:
|
||||
// High usage: ████████ (solid blocks)
|
||||
// Medium usage: :::::::: (colons)
|
||||
// Low usage: ........ (dots)
|
||||
// Empty: (spaces)
|
||||
|
||||
let pattern = if percentage >= 75 {
|
||||
"█" // High usage - solid blocks
|
||||
} else if percentage >= 25 {
|
||||
":" // Medium usage - colons like btop
|
||||
} else if percentage > 0 {
|
||||
"." // Low usage - dots like btop
|
||||
} else {
|
||||
" " // No usage - spaces
|
||||
};
|
||||
|
||||
let filled_chars = pattern.repeat(filled);
|
||||
let empty_chars = " ".repeat(empty);
|
||||
|
||||
filled_chars + &empty_chars
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Widget for CpuWidget {
|
||||
@@ -168,27 +115,16 @@ impl Widget for CpuWidget {
|
||||
}
|
||||
|
||||
fn render(&mut self, frame: &mut Frame, area: Rect) {
|
||||
let content_chunks = Layout::default().direction(Direction::Vertical).constraints([Constraint::Length(1), Constraint::Length(1), Constraint::Length(1)]).split(area);
|
||||
let cpu_title = Paragraph::new("CPU:").style(Style::default().fg(Theme::primary_text()).bg(Theme::background()));
|
||||
let content_chunks = Layout::default().direction(Direction::Vertical).constraints([Constraint::Length(1), Constraint::Length(1)]).split(area);
|
||||
let cpu_title = Paragraph::new("CPU:").style(Typography::widget_title());
|
||||
frame.render_widget(cpu_title, content_chunks[0]);
|
||||
let overall_usage = self.get_load_percentage();
|
||||
let cpu_usage_text = format!("Usage: {} {:>3}%", self.create_btop_dotted_bar(overall_usage, 20), overall_usage);
|
||||
let cpu_usage_para = Paragraph::new(cpu_usage_text).style(Style::default().fg(Theme::cpu_color(overall_usage)).bg(Theme::background()));
|
||||
frame.render_widget(cpu_usage_para, content_chunks[1]);
|
||||
let load_freq_text = format!("Load: {} • {}", self.format_load(), self.format_frequency());
|
||||
let load_freq_para = Paragraph::new(load_freq_text).style(Style::default().fg(Theme::secondary_text()).bg(Theme::background()));
|
||||
frame.render_widget(load_freq_para, content_chunks[2]);
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &str {
|
||||
"CPU"
|
||||
}
|
||||
|
||||
fn has_data(&self) -> bool {
|
||||
self.has_data
|
||||
let load_freq_spans = StatusIcons::create_status_spans(self.status, &format!("Load: {} • {}", self.format_load(), self.format_frequency()));
|
||||
let load_freq_para = Paragraph::new(ratatui::text::Line::from(load_freq_spans));
|
||||
frame.render_widget(load_freq_para, content_chunks[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Default for CpuWidget {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
||||
Reference in New Issue
Block a user