1use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
2use flow_core::{Feature, Theme};
3use ratatui::Frame;
4
5use crate::theme::TuiTheme;
6use crate::views;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum View {
10 Kanban,
11 Agents,
12 Logs,
13 Graph,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum LayoutMode {
18 Full,
19 Compact,
20 Mobile,
21}
22
23pub struct App {
24 pub current_view: View,
25 pub layout_mode: LayoutMode,
26 pub theme: Theme,
27 pub tui_theme: TuiTheme,
28 pub features: Vec<Feature>,
29 pub selected_index: usize,
30 pub show_help: bool,
31 #[allow(dead_code)]
32 pub should_quit: bool,
33 pub terminal_width: u16,
34 pub terminal_height: u16,
35 #[allow(dead_code)]
36 pub scroll_offset: usize,
37 pub log_messages: Vec<String>,
38}
39
40impl App {
41 pub fn new() -> Self {
42 let theme = Theme::default();
43 let tui_theme = TuiTheme::from(&theme.colors());
44
45 let mut app = Self {
46 current_view: View::Kanban,
47 layout_mode: LayoutMode::Full,
48 theme,
49 tui_theme,
50 features: Vec::new(),
51 selected_index: 0,
52 show_help: false,
53 should_quit: false,
54 terminal_width: 120,
55 terminal_height: 30,
56 scroll_offset: 0,
57 log_messages: Vec::new(),
58 };
59
60 app.load_demo_data();
61 app
62 }
63
64 pub fn render(&self, frame: &mut Frame) {
65 if self.show_help {
66 views::help::render(frame, self);
67 } else {
68 match self.current_view {
69 View::Kanban => views::kanban::render(frame, self),
70 View::Agents => views::agents::render(frame, self),
71 View::Logs => views::logs::render(frame, self),
72 View::Graph => views::graph::render(frame, self),
73 }
74 }
75 }
76
77 pub fn handle_key(&mut self, key: KeyEvent) -> bool {
78 if key.code == KeyCode::Char('q')
80 || (key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL))
81 {
82 return true;
83 }
84
85 if key.code == KeyCode::Char('?') {
87 self.show_help = !self.show_help;
88 return false;
89 }
90
91 if self.show_help {
93 self.show_help = false;
94 return false;
95 }
96
97 match key.code {
99 KeyCode::Char('1') => self.current_view = View::Kanban,
100 KeyCode::Char('2') => self.current_view = View::Agents,
101 KeyCode::Char('3') => self.current_view = View::Logs,
102 KeyCode::Char('4') => self.current_view = View::Graph,
103 KeyCode::Tab => self.next_view(),
104 _ => {}
105 }
106
107 match key.code {
109 KeyCode::Char('j') | KeyCode::Down => self.next_item(),
110 KeyCode::Char('k') | KeyCode::Up => self.prev_item(),
111 _ => {}
112 }
113
114 if key.code == KeyCode::Char('t') {
116 self.cycle_theme();
117 }
118
119 false
120 }
121
122 pub fn resize(&mut self, width: u16, height: u16) {
123 self.terminal_width = width;
124 self.terminal_height = height;
125
126 self.layout_mode = if width >= 120 {
128 LayoutMode::Full
129 } else if width >= 80 {
130 LayoutMode::Compact
131 } else {
132 LayoutMode::Mobile
133 };
134 }
135
136 fn next_view(&mut self) {
137 self.current_view = match self.current_view {
138 View::Kanban => View::Agents,
139 View::Agents => View::Logs,
140 View::Logs => View::Graph,
141 View::Graph => View::Kanban,
142 };
143 }
144
145 fn next_item(&mut self) {
146 if !self.features.is_empty() {
147 self.selected_index = (self.selected_index + 1) % self.features.len();
148 }
149 }
150
151 fn prev_item(&mut self) {
152 if !self.features.is_empty() {
153 self.selected_index = if self.selected_index == 0 {
154 self.features.len() - 1
155 } else {
156 self.selected_index - 1
157 };
158 }
159 }
160
161 fn cycle_theme(&mut self) {
162 self.theme = self.theme.next();
163 self.tui_theme = TuiTheme::from(&self.theme.colors());
164 self.add_log(format!("Theme changed to: {}", self.theme.name()));
165 }
166
167 pub fn add_log(&mut self, message: String) {
168 self.log_messages.push(message);
169 if self.log_messages.len() > 100 {
171 self.log_messages.remove(0);
172 }
173 }
174
175 pub fn load_demo_data(&mut self) {
176 self.features = vec![
178 Feature {
179 id: 1,
180 priority: 100,
181 category: "Backend".to_string(),
182 name: "User Authentication".to_string(),
183 description: "Implement JWT-based authentication system".to_string(),
184 steps: vec![
185 "Create user model".to_string(),
186 "Implement JWT tokens".to_string(),
187 "Add login/logout endpoints".to_string(),
188 ],
189 passes: false,
190 in_progress: false,
191 dependencies: vec![],
192 created_at: Some("2024-01-15T10:00:00Z".to_string()),
193 updated_at: Some("2024-01-15T10:00:00Z".to_string()),
194 },
195 Feature {
196 id: 2,
197 priority: 90,
198 category: "Frontend".to_string(),
199 name: "Login Page UI".to_string(),
200 description: "Create responsive login page".to_string(),
201 steps: vec![
202 "Design mockup".to_string(),
203 "Implement form".to_string(),
204 "Add validation".to_string(),
205 ],
206 passes: false,
207 in_progress: false,
208 dependencies: vec![],
209 created_at: Some("2024-01-15T11:00:00Z".to_string()),
210 updated_at: Some("2024-01-15T11:00:00Z".to_string()),
211 },
212 Feature {
213 id: 3,
214 priority: 80,
215 category: "Backend".to_string(),
216 name: "API Rate Limiting".to_string(),
217 description: "Add rate limiting middleware".to_string(),
218 steps: vec!["Research solutions".to_string(), "Implement middleware".to_string()],
219 passes: false,
220 in_progress: true,
221 dependencies: vec![1],
222 created_at: Some("2024-01-16T09:00:00Z".to_string()),
223 updated_at: Some("2024-01-16T14:30:00Z".to_string()),
224 },
225 Feature {
226 id: 4,
227 priority: 70,
228 category: "Testing".to_string(),
229 name: "Integration Tests".to_string(),
230 description: "Write comprehensive test suite".to_string(),
231 steps: vec![
232 "Setup test framework".to_string(),
233 "Write API tests".to_string(),
234 "Add CI/CD integration".to_string(),
235 ],
236 passes: false,
237 in_progress: true,
238 dependencies: vec![1, 3],
239 created_at: Some("2024-01-16T10:00:00Z".to_string()),
240 updated_at: Some("2024-01-16T15:00:00Z".to_string()),
241 },
242 Feature {
243 id: 5,
244 priority: 60,
245 category: "Frontend".to_string(),
246 name: "Dashboard".to_string(),
247 description: "User dashboard with stats".to_string(),
248 steps: vec![
249 "Create layout".to_string(),
250 "Add charts".to_string(),
251 "Implement data fetching".to_string(),
252 ],
253 passes: true,
254 in_progress: false,
255 dependencies: vec![1, 2],
256 created_at: Some("2024-01-14T08:00:00Z".to_string()),
257 updated_at: Some("2024-01-15T17:00:00Z".to_string()),
258 },
259 Feature {
260 id: 6,
261 priority: 50,
262 category: "Backend".to_string(),
263 name: "Email Notifications".to_string(),
264 description: "Send email notifications for events".to_string(),
265 steps: vec!["Setup email service".to_string(), "Create templates".to_string()],
266 passes: true,
267 in_progress: false,
268 dependencies: vec![1],
269 created_at: Some("2024-01-13T14:00:00Z".to_string()),
270 updated_at: Some("2024-01-14T16:00:00Z".to_string()),
271 },
272 Feature {
273 id: 7,
274 priority: 40,
275 category: "DevOps".to_string(),
276 name: "Docker Setup".to_string(),
277 description: "Containerize application".to_string(),
278 steps: vec!["Create Dockerfile".to_string(), "Setup docker-compose".to_string()],
279 passes: true,
280 in_progress: false,
281 dependencies: vec![],
282 created_at: Some("2024-01-12T09:00:00Z".to_string()),
283 updated_at: Some("2024-01-13T11:00:00Z".to_string()),
284 },
285 Feature {
286 id: 8,
287 priority: 30,
288 category: "Frontend".to_string(),
289 name: "Settings Page".to_string(),
290 description: "User settings and preferences".to_string(),
291 steps: vec!["Design UI".to_string(), "Implement form handling".to_string()],
292 passes: true,
293 in_progress: false,
294 dependencies: vec![1, 2],
295 created_at: Some("2024-01-11T10:00:00Z".to_string()),
296 updated_at: Some("2024-01-12T15:00:00Z".to_string()),
297 },
298 Feature {
299 id: 9,
300 priority: 20,
301 category: "Documentation".to_string(),
302 name: "API Documentation".to_string(),
303 description: "Complete OpenAPI specs".to_string(),
304 steps: vec!["Write specs".to_string(), "Generate docs".to_string()],
305 passes: true,
306 in_progress: false,
307 dependencies: vec![1, 3],
308 created_at: Some("2024-01-10T13:00:00Z".to_string()),
309 updated_at: Some("2024-01-11T16:00:00Z".to_string()),
310 },
311 Feature {
312 id: 10,
313 priority: 10,
314 category: "Security".to_string(),
315 name: "Security Audit".to_string(),
316 description: "Comprehensive security review".to_string(),
317 steps: vec![
318 "Run vulnerability scan".to_string(),
319 "Review dependencies".to_string(),
320 "Fix issues".to_string(),
321 ],
322 passes: true,
323 in_progress: false,
324 dependencies: vec![1, 3, 6],
325 created_at: Some("2024-01-09T08:00:00Z".to_string()),
326 updated_at: Some("2024-01-10T18:00:00Z".to_string()),
327 },
328 ];
329
330 self.add_log("Loaded 10 demo features".to_string());
331 self.add_log(format!("Current theme: {}", self.theme.name()));
332 self.add_log("Press ? for help".to_string());
333 }
334}