Initial commit: TUI Watson time tracker implementation

This commit is contained in:
Ian Keane 2025-11-16 09:57:01 -05:00
commit 2f0cc79631
7 changed files with 1389 additions and 0 deletions

102
src/ui.rs Normal file
View file

@ -0,0 +1,102 @@
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, List, ListItem, Paragraph},
Frame,
};
use crate::state::{AppState, TimeItem};
const ACTIVE_COLOR: Color = Color::Green;
const INACTIVE_COLOR: Color = Color::Yellow;
pub fn render(frame: &mut Frame, state: &AppState) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
])
.split(frame.size());
render_section(
frame,
chunks[0],
"Permanent Items",
&state.permanent_items,
state.current_pane == 0,
state.selected_indices[0],
state,
);
render_section(
frame,
chunks[1],
"Recurring Items",
&state.recurring_items,
state.current_pane == 1,
state.selected_indices[1],
state,
);
render_section(
frame,
chunks[2],
"Recent Items",
&state.recent_items,
state.current_pane == 2,
state.selected_indices[2],
state,
);
}
fn render_section(
frame: &mut Frame,
area: Rect,
title: &str,
items: &[TimeItem],
is_active: bool,
selected: usize,
state: &AppState,
) {
let border_color = if is_active { ACTIVE_COLOR } else { INACTIVE_COLOR };
let block = Block::default()
.borders(Borders::ALL)
.title(title)
.style(Style::default().fg(border_color));
let items: Vec<ListItem> = items
.iter()
.enumerate()
.map(|(i, item)| {
let is_running = state
.active_timer
.as_ref()
.map(|(active, _)| active.name == item.name)
.unwrap_or(false);
let style = if is_running {
Style::default().fg(ACTIVE_COLOR).add_modifier(Modifier::BOLD)
} else if i == selected && is_active {
Style::default().fg(border_color).add_modifier(Modifier::REVERSED)
} else {
Style::default()
};
let mut line = item.name.clone();
if !item.tags.is_empty() {
line.push_str(" [");
line.push_str(&item.tags.join(", "));
line.push(']');
}
ListItem::new(Line::from(vec![Span::styled(line, style)]))
})
.collect();
let list = List::new(items).block(block);
frame.render_widget(list, area);
}