Close to the ui final version

This commit is contained in:
Marco De Araujo 2025-12-24 14:47:57 -04:00
parent d614ec4d18
commit b130c4550d
9 changed files with 46 additions and 69 deletions

View file

@ -16,7 +16,7 @@ use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span, Text},
widgets::{Block, Borders, TableState, Padding, Paragraph},
widgets::{Block, Borders, Padding, Paragraph},
};
use std::{
@ -32,7 +32,6 @@ pub struct App {
update_interval: Duration,
endpoints: UptimeKumaEndpoints,
client: UptimeKumaClient,
list_state: TableState,
}
impl App {
@ -102,21 +101,7 @@ impl App {
frame.render_widget(no_data, area);
}
fn move_selection(&mut self, direction: isize) {
if self.state.monitors.is_empty() {
return;
}
let current = self.list_state.selected().unwrap_or(0);
let new_index = (current as isize + direction) as usize;
let new_index = new_index.clamp(0, self.state.monitors.len() - 1);
self.list_state.select(Some(new_index));
self.state.selected_index = new_index;
}
fn render(&mut self) {
let mut list_state = self.list_state.clone();
let _ = self.terminal.draw(|frame| {
let area = frame.area();
let chunks = Layout::default()
@ -134,7 +119,7 @@ impl App {
} else if self.state.monitors.is_empty() {
Self::render_no_data(frame, chunks[1]);
} else {
render_monitor_list(frame, chunks[1], &self.state, &mut list_state);
render_monitor_list(frame, chunks[1], &self.state);
}
});
}
@ -148,8 +133,6 @@ impl App {
match key.code {
KeyCode::Char('q') | KeyCode::Esc => self.should_quit = true,
KeyCode::Up => self.move_selection(-1),
KeyCode::Down => self.move_selection(1),
_ => {}
}
}
@ -185,8 +168,6 @@ impl App {
terminal.hide_cursor()?;
let state = DashboardViewState::new();
let mut list_state = TableState::default();
list_state.select(Some(0));
Ok(Self {
state,
@ -196,7 +177,6 @@ impl App {
update_interval: Duration::from_secs(30),
endpoints,
client: UptimeKumaClient::new(),
list_state: list_state,
})
}

View file

@ -3,21 +3,16 @@ use crate::ui::dashboard::model::DashboardViewState;
use crate::ui::dashboard::{MonitorStatus, MonitorViewState};
use ratatui::{
Frame,
layout::{Constraint, Direction, Layout, Rect},
layout::{Constraint, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, List, ListItem, Row, Table, TableState},
widgets::{Block, Borders, Row, Table},
};
const STATUS_LINE_LENGTH: usize = 100;
const MAX_NAME_LENGTH: usize = 30;
pub fn render_monitor_list(
frame: &mut Frame,
area: Rect,
state: &DashboardViewState,
list_state: &mut TableState,
) {
pub fn render_monitor_list(frame: &mut Frame, area: Rect, state: &DashboardViewState) {
let block = Block::default()
.title(t("monitors"))
.borders(Borders::ALL)
@ -45,8 +40,7 @@ pub fn render_monitor_list(
.monitors
.iter()
.take(visible_items)
.enumerate()
.map(|(i, m)| create_monitor_item(m, list_state.selected() == Some(i)))
.map(|m| create_monitor_item(m))
.collect();
let table = Table::new(rows, constraints)
@ -56,7 +50,7 @@ pub fn render_monitor_list(
.highlight_symbol(">> ")
.row_highlight_style(Style::default().add_modifier(Modifier::REVERSED));
frame.render_stateful_widget(table, area, list_state);
frame.render_widget(table, area);
}
pub fn get_status_char(status: &MonitorStatus) -> char {
@ -83,7 +77,7 @@ pub fn get_status_emoji(status: &MonitorStatus) -> &str {
}
}
fn create_monitor_item(monitor: &MonitorViewState, item_selected: bool) -> Row<'_> {
fn create_monitor_item(monitor: &MonitorViewState) -> Row<'_> {
let status_icon = get_status_emoji(&monitor.status);
let status_color = get_status_color(&monitor.status);
@ -95,35 +89,55 @@ fn create_monitor_item(monitor: &MonitorViewState, item_selected: bool) -> Row<'
let response_text = format!("{:>7}ms", monitor.response_time);
let uptime_text = format!("{:>7}%", monitor.uptime_24h);
let status_line = create_status_line_text(&monitor.status_history);
let style = if item_selected {
Style::default().add_modifier(Modifier::REVERSED)
} else {
Style::default()
};
let status_line_spans = create_status_line_spans(&monitor.status_history);
Row::new(vec![
format!("{} ", status_icon),
display_name,
response_text,
uptime_text,
format!("(1h) {} ({})", status_line, t("now")),
get_formated_line(format!("{} ", status_icon), status_color, Modifier::empty()),
get_formated_line(display_name, Color::White, Modifier::empty()),
get_formated_line(response_text, Color::Cyan, Modifier::empty()),
get_formated_line(uptime_text, Color::Magenta, Modifier::empty()),
status_line_spans,
])
.style(style)
.style(Style::default())
.height(1)
}
fn create_status_line_text(status_history: &[MonitorStatus]) -> String {
let mut line = String::new();
fn get_formated_line(text: String, color: Color, modifier: Modifier) -> Line<'static> {
Line::from(vec![Span::styled(
text,
Style::default().fg(color).add_modifier(modifier),
)])
}
fn create_status_line_spans(status_history: &[MonitorStatus]) -> Line<'_> {
let mut spans = Vec::new();
let recent_status: Vec<_> = status_history
.iter()
.rev()
.take(STATUS_LINE_LENGTH)
.collect();
spans.push(Span::styled(
"(1h)",
Style::default()
.fg(Color::Gray)
.add_modifier(Modifier::ITALIC),
));
spans.push(Span::raw(" "));
for status in recent_status.iter().rev() {
line.push(get_status_char(status));
let c = get_status_char(status);
let color = get_status_color(status);
spans.push(Span::styled(c.to_string(), Style::default().fg(color)));
}
line
spans.push(Span::raw(" "));
spans.push(Span::styled(
t("now"),
Style::default()
.fg(Color::Gray)
.add_modifier(Modifier::ITALIC),
));
Line::from(spans)
}

View file

@ -1,2 +1,2 @@
pub mod model;
pub use model::{DashboardViewState, MonitorStatus, MonitorViewState};
pub use model::{MonitorStatus, MonitorViewState};

View file

@ -1,4 +1,4 @@
use crate::core::models::{UnifiedData, UnifiedMonitorData};
use crate::core::models::UnifiedData;
use crate::data::heartbeat::model::HeartbeatEntry;
use crate::i18n::t;
@ -96,10 +96,6 @@ impl DashboardViewState {
error_message: None,
}
}
pub fn get_selected_monitor(&self) -> Option<&MonitorViewState> {
self.monitors.get(self.selected_index)
}
}
fn get_status_history(heartbeats: &[HeartbeatEntry]) -> Vec<MonitorStatus> {

View file

@ -1,6 +0,0 @@
#[derive(Debug)]
pub enum AppEvent {
KeyPress(char),
RefreshData,
Quit,
}

View file

@ -1,6 +1,4 @@
pub mod app;
pub mod components;
pub mod dashboard;
pub mod events;
pub use app::App;