use ratatui::{
layout::Rect,
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, List, ListItem, ListState},
Frame,
};
use crate::tui::app::{App, FocusedPanel};
use crate::tui::ui::{border_style, severity_color, severity_symbol};
pub fn draw(frame: &mut Frame, app: &App, area: Rect) {
let focused = app.focused_panel == FocusedPanel::Issues;
let issues = app.watch_state.issues();
let items: Vec<ListItem> = issues
.iter()
.enumerate()
.map(|(i, issue)| {
let is_selected = i == app.issue_index;
let severity_style = Style::default()
.fg(severity_color(issue.severity))
.add_modifier(Modifier::BOLD);
let severity = Span::styled(
format!(" {} ", severity_symbol(issue.severity)),
severity_style,
);
let location = format!("{}:{}", app.display_path(&issue.file_path), issue.line);
let location_style = if is_selected && focused {
Style::default()
.fg(Color::White)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(Color::DarkGray)
};
let location_span = Span::styled(location, location_style);
let line1 = Line::from(vec![severity, location_span]);
let msg_style = if is_selected && focused {
Style::default().fg(Color::White)
} else {
Style::default().fg(Color::Gray)
};
let msg = if issue.message.len() > 60 {
format!("{}...", &issue.message[..57])
} else {
issue.message.clone()
};
let line2 = Line::from(vec![Span::raw(" "), Span::styled(msg, msg_style)]);
let lines = if let Some(ref code) = issue.code {
let code_style = Style::default().fg(Color::DarkGray);
let line3 = Line::from(vec![
Span::raw(" "),
Span::styled(format!("[{}]", code), code_style),
]);
vec![line1, line2, line3]
} else {
vec![line1, line2]
};
let item_style = if is_selected && focused {
Style::default().bg(Color::DarkGray)
} else {
Style::default()
};
ListItem::new(lines).style(item_style)
})
.collect();
let error_count = app.watch_state.error_count();
let warning_count = app.watch_state.warning_count();
let info_count = app.watch_state.info_count();
let title = if issues.is_empty() {
" Issues ".to_string()
} else {
format!(
" Issues ({} E, {} W, {} I) ",
error_count, warning_count, info_count
)
};
let block = Block::default()
.title(title)
.borders(Borders::ALL)
.border_style(border_style(focused));
let list = if issues.is_empty() {
List::new(vec![ListItem::new(Line::from(vec![Span::styled(
" ✓ No issues found",
Style::default().fg(Color::Green),
)]))])
.block(block)
} else {
List::new(items).block(block)
};
let mut state = ListState::default();
if !issues.is_empty() {
state.select(Some(app.issue_index));
}
frame.render_stateful_widget(list, area, &mut state);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_severity_display() {
assert_eq!(severity_symbol(crate::Severity::Error), "E");
assert_eq!(severity_symbol(crate::Severity::Warning), "W");
assert_eq!(severity_symbol(crate::Severity::Info), "I");
}
}