use crate::app::App;
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout},
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Gauge, Paragraph},
Frame,
};
#[allow(clippy::too_many_lines)]
pub fn render(frame: &mut Frame, app: &App) {
let area = frame.area();
let theme = &app.tui_theme;
let chunks_outer = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Min(0), Constraint::Length(1), ])
.split(area);
let dashboard_area = chunks_outer[0];
let footer_area = chunks_outer[1];
let block = Block::default()
.title("Agent Dashboard")
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border))
.title_style(
Style::default()
.fg(theme.primary)
.add_modifier(Modifier::BOLD),
);
let inner = block.inner(dashboard_area);
frame.render_widget(block, dashboard_area);
let (total, passing, in_progress, _pending) = if let Some(ref stats) = app.feature_stats {
(stats.total, stats.passing, stats.in_progress, stats.failing)
} else {
let total = app.features.len();
let passing = app.features.iter().filter(|f| f.passes).count();
let in_progress = app.features.iter().filter(|f| f.in_progress).count();
let pending = app
.features
.iter()
.filter(|f| !f.passes && !f.in_progress)
.count();
(total, passing, in_progress, pending)
};
#[allow(clippy::cast_precision_loss)]
let progress_ratio = if total > 0 {
passing as f64 / total as f64
} else {
0.0
};
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints([
Constraint::Length(3), Constraint::Length(2), Constraint::Length(3), Constraint::Length(2), Constraint::Min(5), ])
.split(inner);
let gauge_label = format!("{}/{} ({:.0}%)", passing, total, progress_ratio * 100.0);
let gauge = Gauge::default()
.block(Block::default())
.gauge_style(Style::default().fg(theme.done))
.label(gauge_label)
.ratio(progress_ratio);
frame.render_widget(gauge, chunks[0]);
let agent_info = Paragraph::new(vec![Line::from(vec![
Span::styled(
format!("Coding Agents: {in_progress}/5 "),
Style::default().fg(theme.in_progress),
),
Span::styled(
"Testing Agents: 1/5".to_string(),
Style::default().fg(theme.secondary),
),
])])
.alignment(Alignment::Left);
frame.render_widget(agent_info, chunks[2]);
let activity = Paragraph::new(vec![
Line::from(Span::styled(
"Recent Activity:",
Style::default()
.fg(theme.foreground)
.add_modifier(Modifier::BOLD),
)),
Line::from(""),
Line::from(vec![
Span::styled(" • ", Style::default().fg(theme.done)),
Span::styled(
format!(
"{} features completed",
app.features.iter().filter(|f| f.passes).count()
),
Style::default().fg(theme.foreground),
),
]),
Line::from(vec![
Span::styled(" • ", Style::default().fg(theme.in_progress)),
Span::styled(
format!(
"{} features in progress",
app.features.iter().filter(|f| f.in_progress).count()
),
Style::default().fg(theme.foreground),
),
]),
Line::from(vec![
Span::styled(" • ", Style::default().fg(theme.pending)),
Span::styled(
format!(
"{} features pending",
app.features
.iter()
.filter(|f| !f.passes && !f.in_progress)
.count()
),
Style::default().fg(theme.foreground),
),
]),
])
.alignment(Alignment::Left);
frame.render_widget(activity, chunks[4]);
let footer_text = if let Some(status) = app.get_status_message() {
status.to_string()
} else {
" 1-4:views r:refresh t:theme ?:help q:quit".to_string()
};
let footer = Paragraph::new(footer_text).style(Style::default().fg(theme.muted));
frame.render_widget(footer, footer_area);
}