1use crate::app::App;
2use ratatui::{
3 style::{Modifier, Style},
4 text::{Line, Span},
5 widgets::{Block, Borders, List, ListItem},
6 Frame,
7};
8
9pub fn render(frame: &mut Frame, app: &App) {
10 let area = frame.area();
11 let theme = &app.tui_theme;
12
13 let block = Block::default()
14 .title("Dependency Graph")
15 .borders(Borders::ALL)
16 .border_style(Style::default().fg(theme.border))
17 .title_style(Style::default().fg(theme.primary).add_modifier(Modifier::BOLD));
18
19 let inner = block.inner(area);
20 frame.render_widget(block, area);
21
22 if app.features.is_empty() {
23 return;
24 }
25
26 let mut items = Vec::new();
28
29 for feature in &app.features {
30 let status_color = if feature.passes {
32 theme.done
33 } else if feature.in_progress {
34 theme.in_progress
35 } else {
36 theme.pending
37 };
38
39 let icon = if feature.passes {
41 "✓"
42 } else if feature.in_progress {
43 "◉"
44 } else {
45 "○"
46 };
47
48 let is_blocked = !feature.dependencies.is_empty()
50 && feature.dependencies.iter().any(|dep_id| {
51 app.features
52 .iter()
53 .find(|f| f.id == *dep_id)
54 .map(|f| !f.passes)
55 .unwrap_or(true)
56 });
57
58 let border_color = if is_blocked {
59 theme.blocked
60 } else {
61 status_color
62 };
63
64 items.push(ListItem::new(vec![
66 Line::from(vec![
67 Span::styled("┌─", Style::default().fg(border_color)),
68 Span::styled(
69 format!(" {} {} ", icon, feature.name),
70 Style::default().fg(status_color).add_modifier(Modifier::BOLD),
71 ),
72 Span::styled("─┐", Style::default().fg(border_color)),
73 ]),
74 Line::from(vec![
75 Span::styled("│ ", Style::default().fg(border_color)),
76 Span::styled(&feature.category, Style::default().fg(theme.secondary)),
77 Span::styled(" │", Style::default().fg(border_color)),
78 ]),
79 Line::from(vec![Span::styled(
80 "└──────────────────┘",
81 Style::default().fg(border_color),
82 )]),
83 ]));
84
85 if !feature.dependencies.is_empty() {
87 for dep_id in &feature.dependencies {
88 if let Some(dep) = app.features.iter().find(|f| f.id == *dep_id) {
89 let dep_status = if dep.passes {
90 "✓"
91 } else if dep.in_progress {
92 "◉"
93 } else {
94 "○"
95 };
96
97 items.push(ListItem::new(Line::from(vec![
98 Span::styled(" ↓ ", Style::default().fg(theme.accent)),
99 Span::styled(
100 format!("{} depends on: ", dep_status),
101 Style::default().fg(theme.muted),
102 ),
103 Span::styled(&dep.name, Style::default().fg(theme.foreground)),
104 ])));
105 }
106 }
107 }
108
109 items.push(ListItem::new(Line::from("")));
110 }
111
112 let list = List::new(items);
113 frame.render_widget(list, inner);
114}