Month -> 31 trailing days

This commit is contained in:
Ian Keane 2025-12-01 15:13:40 -05:00
parent e62832fde6
commit 360af2f0cb
2 changed files with 45 additions and 33 deletions

View file

@ -28,7 +28,7 @@ pub enum Screen {
pub enum LogViewPeriod { pub enum LogViewPeriod {
Day, Day,
Week, Week,
Month, Month, // Represents the last 31 days
} }
pub enum LogViewDayOrder { pub enum LogViewDayOrder {
@ -1587,17 +1587,29 @@ impl App {
} }
fn load_log_content(&mut self) -> anyhow::Result<()> { fn load_log_content(&mut self) -> anyhow::Result<()> {
let flag = match self.log_view_period { let mut command = Command::new("watson");
LogViewPeriod::Day => "--day", command.arg("log").arg("--json");
LogViewPeriod::Week => "--week",
LogViewPeriod::Month => "--month", // Handle different period options
match self.log_view_period {
LogViewPeriod::Day => {
command.arg("--day");
},
LogViewPeriod::Week => {
command.arg("--week");
},
LogViewPeriod::Month => {
// Use --from with date 31 days ago for month view
let thirty_one_days_ago = chrono::Local::now()
.checked_sub_signed(chrono::Duration::days(31))
.expect("Failed to calculate date from 31 days ago");
command.arg("--from");
command.arg(thirty_one_days_ago.format("%Y-%m-%d").to_string());
},
}; };
let output = Command::new("watson") let output = command.output()?;
.arg("log")
.arg(flag)
.arg("--json")
.output()?;
if output.status.success() { if output.status.success() {
let json_str = String::from_utf8_lossy(&output.stdout); let json_str = String::from_utf8_lossy(&output.stdout);

View file

@ -33,10 +33,10 @@ fn render_main(frame: &mut Frame, app: &App) {
let has_active_timer = app.state.active_timer.is_some(); let has_active_timer = app.state.active_timer.is_some();
let has_status = app.status_message.is_some(); let has_status = app.status_message.is_some();
let show_help_hint = app.config.show_help_hint; let show_help_hint = app.config.show_help_hint;
// Show bottom bar if we have any of: timer, status, or help hint // Show bottom bar if we have any of: timer, status, or help hint
let show_bottom_bar = has_active_timer || has_status || show_help_hint; let show_bottom_bar = has_active_timer || has_status || show_help_hint;
let bottom_height = if show_bottom_bar { let bottom_height = if show_bottom_bar {
if has_status { if has_status {
2 // Need extra line for status 2 // Need extra line for status
@ -63,41 +63,41 @@ fn render_main(frame: &mut Frame, app: &App) {
.borders(Borders::ALL) .borders(Borders::ALL)
.title("WAT") .title("WAT")
.style(Style::default().fg(ACTIVE_COLOR)); .style(Style::default().fg(ACTIVE_COLOR));
let text = Paragraph::new("No sections enabled. Edit config (press 'c') to enable sections.") let text = Paragraph::new("No sections enabled. Edit config (press 'c') to enable sections.")
.block(block) .block(block)
.style(Style::default().fg(Color::Yellow)) .style(Style::default().fg(Color::Yellow))
.alignment(Alignment::Center); .alignment(Alignment::Center);
frame.render_widget(text, frame.size()); frame.render_widget(text, frame.size());
return; return;
} }
// Build constraints for enabled sections // Build constraints for enabled sections
let mut constraints = Vec::new(); let mut constraints = Vec::new();
if bottom_height > 0 { if bottom_height > 0 {
// Reserve space for bottom bar first, then split remainder among sections // Reserve space for bottom bar first, then split remainder among sections
constraints.push(Constraint::Min(0)); // Sections get remaining space constraints.push(Constraint::Min(0)); // Sections get remaining space
constraints.push(Constraint::Length(bottom_height)); // Bottom bar gets fixed height constraints.push(Constraint::Length(bottom_height)); // Bottom bar gets fixed height
let layout = Layout::default() let layout = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(constraints) .constraints(constraints)
.split(frame.size()); .split(frame.size());
// Split the top area among enabled sections // Split the top area among enabled sections
let section_percentage = 100 / enabled_sections as u16; let section_percentage = 100 / enabled_sections as u16;
let section_constraints = vec![Constraint::Percentage(section_percentage); enabled_sections]; let section_constraints = vec![Constraint::Percentage(section_percentage); enabled_sections];
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(section_constraints) .constraints(section_constraints)
.split(layout[0]); .split(layout[0]);
// Render enabled sections // Render enabled sections
let mut chunk_idx = 0; let mut chunk_idx = 0;
if app.config.show_permanent { if app.config.show_permanent {
render_section( render_section(
frame, frame,
@ -145,14 +145,14 @@ fn render_main(frame: &mut Frame, app: &App) {
// No bottom bar - just render sections // No bottom bar - just render sections
let section_percentage = 100 / enabled_sections as u16; let section_percentage = 100 / enabled_sections as u16;
let constraints = vec![Constraint::Percentage(section_percentage); enabled_sections]; let constraints = vec![Constraint::Percentage(section_percentage); enabled_sections];
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(constraints) .constraints(constraints)
.split(frame.size()); .split(frame.size());
let mut chunk_idx = 0; let mut chunk_idx = 0;
if app.config.show_permanent { if app.config.show_permanent {
render_section( render_section(
frame, frame,
@ -679,7 +679,7 @@ fn render_log_view(frame: &mut Frame, app: &App) {
let period_str = match app.log_view_period { let period_str = match app.log_view_period {
LogViewPeriod::Day => "Day", LogViewPeriod::Day => "Day",
LogViewPeriod::Week => "Week", LogViewPeriod::Week => "Week",
LogViewPeriod::Month => "Month", LogViewPeriod::Month => "31 Days",
}; };
let grouping_str = match app.log_view_grouping { let grouping_str = match app.log_view_grouping {
@ -812,14 +812,14 @@ fn render_log_view(frame: &mut Frame, app: &App) {
// Third pass: render with highlighting and overlap detection // Third pass: render with highlighting and overlap detection
let is_by_date = matches!(app.log_view_grouping, LogViewGrouping::ByDate); let is_by_date = matches!(app.log_view_grouping, LogViewGrouping::ByDate);
// Pre-calculate overlaps for efficiency - check consecutive entries within same day // Pre-calculate overlaps for efficiency - check consecutive entries within same day
let mut overlap_entry_indices = std::collections::HashSet::new(); let mut overlap_entry_indices = std::collections::HashSet::new();
if is_by_date && app.log_view_frame_indices.len() > 1 { if is_by_date && app.log_view_frame_indices.len() > 1 {
// Track which day we're in to avoid comparing across days // Track which day we're in to avoid comparing across days
let mut current_day_start_entry = 0; let mut current_day_start_entry = 0;
let mut in_day = false; let mut in_day = false;
for (line_idx, line) in app.log_view_content.iter().enumerate() { for (line_idx, line) in app.log_view_content.iter().enumerate() {
// New day header // New day header
if !line.starts_with(" ") && !line.is_empty() && !line.starts_with(" ") { if !line.starts_with(" ") && !line.is_empty() && !line.starts_with(" ") {
@ -835,12 +835,12 @@ fn render_log_view(frame: &mut Frame, app: &App) {
.filter(|l| l.starts_with(" ") && !l.is_empty() && l.trim() != "---") .filter(|l| l.starts_with(" ") && !l.is_empty() && l.trim() != "---")
.count() .count()
.saturating_sub(1); .saturating_sub(1);
// Only check overlap if not the first entry of the day // Only check overlap if not the first entry of the day
if entry_idx > current_day_start_entry { if entry_idx > current_day_start_entry {
let current_frame_idx = app.log_view_frame_indices[entry_idx]; let current_frame_idx = app.log_view_frame_indices[entry_idx];
let prev_frame_idx = app.log_view_frame_indices[entry_idx - 1]; let prev_frame_idx = app.log_view_frame_indices[entry_idx - 1];
if let (Some(current), Some(prev)) = ( if let (Some(current), Some(prev)) = (
app.log_view_frames.get(current_frame_idx), app.log_view_frames.get(current_frame_idx),
app.log_view_frames.get(prev_frame_idx), app.log_view_frames.get(prev_frame_idx),
@ -859,16 +859,16 @@ fn render_log_view(frame: &mut Frame, app: &App) {
} }
} }
} }
app.log_view_content app.log_view_content
.iter() .iter()
.enumerate() .enumerate()
.map(|(idx, line)| { .map(|(idx, line)| {
let is_selected = idx >= selected_line_start && idx <= selected_line_end; let is_selected = idx >= selected_line_start && idx <= selected_line_end;
// Skip styling for separator lines // Skip styling for separator lines
let is_separator = line.trim() == "---"; let is_separator = line.trim() == "---";
// Check if this line corresponds to an overlapping entry // Check if this line corresponds to an overlapping entry
let has_overlap = if !is_separator && is_by_date && line.starts_with(" ") && !line.is_empty() { let has_overlap = if !is_separator && is_by_date && line.starts_with(" ") && !line.is_empty() {
// Count which entry this is (0-based in display order) // Count which entry this is (0-based in display order)
@ -878,7 +878,7 @@ fn render_log_view(frame: &mut Frame, app: &App) {
.filter(|l| l.starts_with(" ") && !l.is_empty() && l.trim() != "---") .filter(|l| l.starts_with(" ") && !l.is_empty() && l.trim() != "---")
.count() .count()
.saturating_sub(1); .saturating_sub(1);
overlap_entry_indices.contains(&entry_idx) overlap_entry_indices.contains(&entry_idx)
} else { } else {
false false
@ -904,7 +904,7 @@ fn render_log_view(frame: &mut Frame, app: &App) {
let paragraph = Paragraph::new(text_lines) let paragraph = Paragraph::new(text_lines)
.block(block) .block(block)
.wrap(ratatui::widgets::Wrap { trim: false }); .wrap(ratatui::widgets::Wrap { trim: false });
frame.render_widget(paragraph, chunks[0]); frame.render_widget(paragraph, chunks[0]);
// Render help hint at bottom if enabled // Render help hint at bottom if enabled
@ -928,7 +928,7 @@ fn render_log_view_help(frame: &mut Frame, app: &App) {
"Time Periods:", "Time Periods:",
"- d: Switch to Day view (current day)", "- d: Switch to Day view (current day)",
"- w: Switch to Week view (current week)", "- w: Switch to Week view (current week)",
"- m: Switch to Month view (current month)", "- m: Switch to Month view (last 31 days)",
"", "",
"Grouping:", "Grouping:",
"- g: Toggle between grouping by Date or by Project", "- g: Toggle between grouping by Date or by Project",