Skip to main content

max / makenotwork

4.4 KB · 145 lines History Blame Raw
1 //! License key management screen — list, generate, revoke keys.
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 item_title = app
16 .keys_item_title
17 .as_deref()
18 .unwrap_or("License Keys");
19
20 let title = Line::from(vec![
21 Span::styled(" Makenot.work ", Style::default().add_modifier(Modifier::BOLD)),
22 Span::raw(" -- "),
23 Span::styled(item_title, Style::default().add_modifier(Modifier::BOLD)),
24 Span::raw(" -- Keys "),
25 ]);
26
27 let block = Block::default()
28 .title(title)
29 .borders(Borders::ALL)
30 .border_style(Style::default().fg(Color::Gray));
31
32 let inner = block.inner(area);
33 frame.render_widget(block, area);
34
35 let chunks = Layout::vertical([
36 Constraint::Length(1), // spacer
37 Constraint::Length(1), // section header
38 Constraint::Min(3), // key list
39 Constraint::Length(1), // status line
40 Constraint::Length(1), // keybindings
41 ])
42 .split(inner);
43
44 // Section header
45 let count = app.license_keys.len();
46 let header = Paragraph::new(Line::from(vec![
47 Span::raw(" "),
48 Span::styled("Keys", Style::default().add_modifier(Modifier::BOLD)),
49 if count == 0 {
50 Span::raw("")
51 } else {
52 Span::raw(format!(" ({})", count))
53 },
54 ]));
55 frame.render_widget(header, chunks[1]);
56
57 // Key list
58 if app.loading {
59 let loading = Paragraph::new(" Loading...");
60 frame.render_widget(loading, chunks[2]);
61 } else if app.license_keys.is_empty() {
62 let empty = Paragraph::new(" No license keys. Press [g] to generate one.");
63 frame.render_widget(empty, chunks[2]);
64 } else {
65 render_key_table(frame, app, chunks[2]);
66 }
67
68 // Status line
69 if let Some(ref status) = app.keys_status {
70 let style = if status.starts_with("Error") {
71 Style::default().fg(Color::Red)
72 } else {
73 Style::default().fg(Color::Green)
74 };
75 let status_line = Paragraph::new(Line::from(vec![
76 Span::raw(" "),
77 Span::styled(status.as_str(), style),
78 ]));
79 frame.render_widget(status_line, chunks[3]);
80 }
81
82 // Keybindings
83 let mut key_spans = vec![
84 Span::raw(" "),
85 Span::styled("[j/k]", Style::default().add_modifier(Modifier::BOLD)),
86 Span::raw(" Nav "),
87 Span::styled("[g]", Style::default().add_modifier(Modifier::BOLD)),
88 Span::raw(" Generate "),
89 ];
90
91 if !app.license_keys.is_empty() {
92 key_spans.extend([
93 Span::styled("[x]", Style::default().add_modifier(Modifier::BOLD)),
94 Span::raw(" Revoke "),
95 ]);
96 }
97
98 key_spans.extend([
99 Span::styled("[r]", Style::default().add_modifier(Modifier::BOLD)),
100 Span::raw(" Refresh "),
101 Span::styled("[Esc]", Style::default().add_modifier(Modifier::BOLD)),
102 Span::raw(" Back"),
103 ]);
104
105 let keys = Paragraph::new(Line::from(key_spans)).style(Style::default().fg(Color::DarkGray));
106 frame.render_widget(keys, chunks[4]);
107 }
108
109 fn render_key_table(frame: &mut Frame, app: &App, area: ratatui::layout::Rect) {
110 let rows: Vec<Row> = app
111 .license_keys
112 .iter()
113 .enumerate()
114 .map(|(i, key)| {
115 let status = if key.is_revoked {
116 "Revoked"
117 } else {
118 "Active"
119 };
120 let activations = match key.max_activations {
121 Some(max) => format!("{}/{}", key.activation_count, max),
122 None => key.activation_count.to_string(),
123 };
124 let date = key.created_at.get(..10).unwrap_or(&key.created_at);
125
126 Row::new(vec![
127 format!(" {}", key.key_code),
128 status.to_string(),
129 activations,
130 date.to_string(),
131 ])
132 .style(widgets::selected_style(i, Some(app.selected_index)))
133 })
134 .collect();
135
136 let widths = [
137 Constraint::Min(24),
138 Constraint::Length(10),
139 Constraint::Length(12),
140 Constraint::Length(12),
141 ];
142
143 widgets::render_table(frame, area, &[" Key", "Status", "Activations", "Created"], &widths, rows);
144 }
145