use ratatui::Frame; use ratatui::layout::{Constraint, Rect}; use ratatui::text::{Line, Span}; use ratatui::widgets::{Block, Borders, Cell, Paragraph, Row, Table}; use crate::state::AppState; use crate::theme::Theme; pub fn render(frame: &mut Frame, area: Rect, state: &AppState, theme: &Theme) { let block = Block::default() .borders(Borders::ALL) .border_style(theme.border) .title(Span::styled( format!(" Alerts ({}) ", state.alerts.len()), theme.title, )); if state.alerts.is_empty() { let paragraph = Paragraph::new(Line::styled(" No alerts", theme.dim)).block(block); frame.render_widget(paragraph, area); return; } let header = Row::new(vec!["Sev", "Time", "Event", "Table", "Metric", "Context"]) .style(theme.header) .bottom_margin(1); let rows: Vec = state .alerts .iter() .map(|alert| { let sev_style = theme.severity_style(&alert.severity); let icon = match alert.severity.as_str() { "critical" => "✗", "warning" => "⚠", _ => "●", }; Row::new(vec![ Cell::from(icon).style(sev_style), Cell::from(alert.timestamp.format("%H:%M:%S").to_string()).style(theme.dim), Cell::from(alert.event.as_str()), Cell::from(alert.table.as_str()).style(theme.dim), Cell::from(alert.metric.as_str()), Cell::from(alert.context.as_str()).style(theme.dim), ]) }) .collect(); let widths = [ Constraint::Length(3), // Sev icon Constraint::Length(10), // Time HH:MM:SS Constraint::Fill(2), // Event Constraint::Fill(3), // Table Constraint::Fill(2), // Metric Constraint::Fill(3), // Context ]; let table = Table::new(rows, widths).header(header).block(block); frame.render_widget(table, area); }