Skip to main content

max / makenotwork

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