diff --git a/src/ui/components.rs b/src/ui/components.rs index 04a0128..4fc6e17 100644 --- a/src/ui/components.rs +++ b/src/ui/components.rs @@ -49,6 +49,24 @@ pub fn render_tabs_bar( frame.render_widget(tabs, area); } +fn relative_due_str(due: chrono::NaiveDateTime) -> (String, Color) { + let now = chrono::Local::now().naive_local(); + let diff = due - now; + + if diff < chrono::Duration::zero() { + (" Overdue ".to_string(), Color::Red) + } else if diff < chrono::Duration::hours(24) { + let hours = diff.num_hours(); + (format!(" {}h left ", hours), Color::Yellow) + } else { + let days = diff.num_days(); + ( + format!(" {} day{} left ", days, if days == 1 { "" } else { "s" }), + Color::DarkGray, + ) + } +} + pub fn render_task_list( frame: &mut Frame, area: Rect, @@ -61,6 +79,8 @@ pub fn render_task_list( let done = tasks.iter().filter(|t| t.status == TaskStatus::Completed).count(); let todo = total - done; + let content_width = (area.width as usize).saturating_sub(5); + let items: Vec = tasks .iter() .map(|task| { @@ -69,12 +89,12 @@ pub fn render_task_list( TaskStatus::NeedsAction => "[ ]", }; - let due_str = task + let (due_text, due_color) = task .due - .map(|d| d.format(" %d/%m/%Y %H:%M").to_string()) - .unwrap_or_default(); + .map(relative_due_str) + .unwrap_or((String::new(), Color::DarkGray)); - let content = Line::from(vec![ + let mut spans = vec![ Span::styled( format!("{} ", checkbox), Style::default().fg(if task.status == TaskStatus::Completed { @@ -93,12 +113,16 @@ pub fn render_task_list( }, ), ), - Span::styled( - due_str, - Style::default().fg(Color::DarkGray), - ), - ]); - ListItem::new(content) + ]; + + if !due_text.is_empty() { + let left_len: usize = spans.iter().map(|s| s.content.len()).sum(); + let pad = content_width.saturating_sub(left_len + due_text.len()); + spans.push(Span::raw(" ".repeat(pad))); + spans.push(Span::styled(due_text, Style::default().fg(due_color))); + } + + ListItem::new(Line::from(spans)) }) .collect();