Skip to main content

max / makenotwork

3.4 KB · 103 lines History Blame Raw
1 //! Collections management screen — list collections.
2
3 use ratatui::Frame;
4 use ratatui::layout::{Constraint, Layout};
5 use ratatui::style::{Color, Modifier, Style};
6 use ratatui::text::{Line, Span};
7 use ratatui::widgets::{Block, Borders, Paragraph, Row};
8
9 use super::App;
10 use super::widgets;
11
12 pub fn render(frame: &mut Frame, app: &App) {
13 let area = frame.area();
14
15 let title = Line::from(vec![
16 Span::styled(" Makenot.work ", Style::default().add_modifier(Modifier::BOLD)),
17 Span::raw(" -- "),
18 Span::styled("Collections", Style::default().add_modifier(Modifier::BOLD)),
19 Span::raw(" "),
20 ]);
21
22 let block = Block::default()
23 .title(title)
24 .borders(Borders::ALL)
25 .border_style(Style::default().fg(Color::Gray));
26
27 let inner = block.inner(area);
28 frame.render_widget(block, area);
29
30 let chunks = Layout::vertical([
31 Constraint::Length(1), // spacer
32 Constraint::Length(1), // section header
33 Constraint::Min(3), // list
34 Constraint::Length(1), // status line
35 Constraint::Length(1), // keybindings
36 ])
37 .split(inner);
38
39 let count = app.collections.len();
40 let header = Paragraph::new(Line::from(vec![
41 Span::raw(" "),
42 Span::styled("Collections", Style::default().add_modifier(Modifier::BOLD)),
43 if count == 0 { Span::raw("") } else { Span::raw(format!(" ({})", count)) },
44 ]));
45 frame.render_widget(header, chunks[1]);
46
47 if app.loading {
48 let loading = Paragraph::new(" Loading...");
49 frame.render_widget(loading, chunks[2]);
50 } else if app.collections.is_empty() {
51 let empty = Paragraph::new(" No collections. Manage collections at makenot.work/dashboard");
52 frame.render_widget(empty, chunks[2]);
53 } else {
54 let rows: Vec<Row> = app
55 .collections
56 .iter()
57 .enumerate()
58 .map(|(i, c)| {
59 let status = if c.is_public { "public" } else { "draft" };
60 Row::new(vec![
61 format!(" {}", c.title),
62 c.slug.clone(),
63 status.to_string(),
64 c.item_count.to_string(),
65 ])
66 .style(widgets::selected_style(i, Some(app.selected_index)))
67 })
68 .collect();
69
70 let widths = [
71 Constraint::Min(20),
72 Constraint::Length(20),
73 Constraint::Length(8),
74 Constraint::Length(6),
75 ];
76
77 widgets::render_table(frame, chunks[2], &[" Title", "Slug", "Status", "Items"], &widths, rows);
78 }
79
80 if let Some(ref status) = app.collections_status {
81 let style = if status.starts_with("Error") {
82 Style::default().fg(Color::Red)
83 } else {
84 Style::default().fg(Color::Green)
85 };
86 let status_line = Paragraph::new(format!(" {}", status)).style(style);
87 frame.render_widget(status_line, chunks[3]);
88 }
89
90 let key_spans = vec![
91 Span::raw(" "),
92 Span::styled("[j/k]", Style::default().add_modifier(Modifier::BOLD)),
93 Span::raw(" Nav "),
94 Span::styled("[r]", Style::default().add_modifier(Modifier::BOLD)),
95 Span::raw(" Refresh "),
96 Span::styled("[Esc]", Style::default().add_modifier(Modifier::BOLD)),
97 Span::raw(" Back"),
98 ];
99
100 let keys = Paragraph::new(Line::from(key_spans)).style(Style::default().fg(Color::DarkGray));
101 frame.render_widget(keys, chunks[4]);
102 }
103