trackWork 0.13.0

A terminal-based time tracking application for managing work sessions
use ratatui::{
    layout::Rect,
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, Paragraph},
    Frame,
};

use crate::app::App;

pub fn draw_top_bar(f: &mut Frame, app: &App, area: Rect) {
    let date_str = app.current_date.format(&app.config.date_format).to_string();

    // Calculate stats using app-level methods to handle out-of-order entries.
    // "Total Time" follows the workday mechanic: span minus off-work time, so
    // lunch / personal entries don't count toward the day's tracked total.
    let (day_start, day_end_or_now, total_time) = if let (Some(start), Some(end)) =
        (app.get_earliest_start_time(), app.get_latest_end_time(Some(app.current_date))) {
        let span_seconds = end.signed_duration_since(start).num_seconds();
        let now = chrono::Local::now().naive_local();
        let off_work_seconds: i64 = app
            .entries
            .iter()
            .filter(|e| e.off_work)
            .map(|e| {
                e.end_time
                    .unwrap_or(now)
                    .signed_duration_since(e.start_time)
                    .num_seconds()
                    .max(0)
            })
            .sum();
        (
            start.format("%H:%M").to_string(),
            end.format("%H:%M").to_string(),
            format_duration_seconds((span_seconds - off_work_seconds).max(0)),
        )
    } else {
        ("--:--".to_string(), "--:--".to_string(), "0m".to_string())
    };

    let is_running = app.entries.iter().any(|e| e.is_running());
    let status = if is_running { "Running" } else { "Stopped" };
    let status_color = if is_running {
        Color::Green
    } else {
        Color::Gray
    };

    let header_text = vec![Line::from(vec![
        Span::styled(
            format!("trackWork v{}", env!("CARGO_PKG_VERSION")),
            Style::default()
                .fg(Color::Cyan)
                .add_modifier(Modifier::BOLD),
        ),
        Span::raw("  |  "),
        Span::styled(date_str, Style::default().fg(Color::Yellow)),
        Span::raw("  |  Day: "),
        Span::styled(
            format!("{} - {}", day_start, day_end_or_now),
            Style::default().fg(Color::Magenta),
        ),
        Span::raw("  |  Total Time: "),
        Span::styled(
            total_time,
            Style::default()
                .fg(Color::Green)
                .add_modifier(Modifier::BOLD),
        ),
        Span::raw("  |  "),
        Span::styled(
            status,
            Style::default()
                .fg(status_color)
                .add_modifier(Modifier::BOLD),
        ),
    ])];

    let header = Paragraph::new(header_text).block(Block::default().borders(Borders::ALL));
    f.render_widget(header, area);
}

fn format_duration_seconds(seconds: i64) -> String {
    let hours = seconds / 3600;
    let minutes = (seconds % 3600) / 60;
    let secs = seconds % 60;

    if hours > 0 {
        format!("{}h {:02}m {:02}s", hours, minutes, secs)
    } else if minutes > 0 {
        format!("{}m {:02}s", minutes, secs)
    } else {
        format!("{}s", secs)
    }
}