jobber 0.1.0-alpha

Minimalistic console work time tracker
use color_print::cformat;
use tabled::{
    Table, Tabled,
    settings::{Alignment, object::Columns},
};

use crate::{
    commands::{Config, current_client, current_job, current_worker, not_available},
    entities::{Company, format_job_id},
    jobber::{Deleted, JobberGet, JobberIter},
    views::ApplyTableStyle,
};

#[derive(Tabled)]
pub(crate) struct InfoView {
    #[tabled(rename = "Company")]
    pub(crate) company: String,
    #[tabled(rename = "Workers")]
    pub(crate) workers: String,
    #[tabled(rename = "Clients")]
    pub(crate) clients: String,
    #[tabled(rename = "Jobs")]
    pub(crate) jobs: String,
    #[tabled(rename = "Tags")]
    pub(crate) tags: String,
    #[tabled(rename = "Invoices")]
    pub(crate) invoices: String,
    #[tabled(rename = "Selected Job")]
    pub(crate) current: String,
}

pub(crate) struct Counts {
    workers: usize,
    clients: usize,
    jobs: usize,
    tags: usize,
    invoices: usize,
}

fn format_count(count: usize) -> String {
    if count == 0 {
        cformat!("<dim>-</>")
    } else {
        cformat!("<s>{}</>", count)
    }
}

impl InfoView {
    pub(crate) fn new(
        company: String,
        counts: Counts,
        current_job: Option<String>,
        current_worker: String,
        current_client: String,
        ansi: bool,
    ) -> Self {
        Self {
            company,
            workers: format_count(counts.workers),
            clients: format_count(counts.clients),
            jobs: format_count(counts.jobs),
            tags: format_count(counts.tags),
            invoices: format_count(counts.invoices),
            current: if let Some(current_job) = current_job {
                if ansi {
                    cformat!(
                        "<s>{current_job}</> (<s>{current_worker}</> for <s>{current_client}</>)"
                    )
                } else {
                    format!("{current_job} ({current_worker} for {current_client})")
                }
            } else {
                not_available(ansi)
            },
        }
    }
}

pub(crate) fn create_overview_table(
    config: &Config,
    database: &(impl JobberIter + JobberGet),
    ansi: bool,
) -> crate::commands::Result<Table> {
    let mut table = Table::new([InfoView::new(
        database
            .get_company()
            .map_or("-".to_string(), |c: &Company| c.address.name.clone()),
        Counts {
            workers: database.iter_workers(Deleted::No).count(),
            clients: database.iter_clients(Deleted::No).count(),
            jobs: database.iter_jobs(Deleted::No).count(),
            tags: database.iter_tags(Deleted::No).count(),
            invoices: database.iter_invoices(Deleted::No).count(),
        },
        current_job(config, database)
            .ok()
            .and_then(|job_id| format_job_id(job_id, database, config, ansi).ok()),
        current_worker(config, database, ansi)?,
        current_client(config, database, ansi)?,
        ansi,
    )]);
    table.modify(Columns::first(), Alignment::center());
    table.modify(Columns::one(1), Alignment::center());
    table.modify(Columns::one(2), Alignment::center());
    table.modify(Columns::one(3), Alignment::center());
    table.modify(Columns::one(4), Alignment::center());
    table.style(&config.table_style);
    Ok(table)
}