diff --git a/Cargo.toml b/Cargo.toml index e5cd73d..4b4ada8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ chrono = "0.4.38" clap = { version = "4.5.4", features = ["cargo"] } crossterm = "0.27.0" moka = { version = "0.12.7", features = ["sync"] } -reqwest = { version = "0.12.4", features = ["blocking"] } +reqwest = { version = "0.12.4", features = ["blocking", "json"] } serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" toml = "0.8.14" diff --git a/src/api/mod.rs b/src/api/mod.rs index 8dc4673..156fc0f 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -98,6 +98,32 @@ impl VikunjaAPI { } } + fn put_request(&self, path: &str, data: serde_json::Value) -> String { + let client = reqwest::blocking::Client::new(); + + client + .put(&format!("{}/api/v1{}", self.host, path)) + .header("Authorization", format!("Bearer {}", self.token)) + .json(&data) + .send() + .unwrap() + .text() + .unwrap() + } + + fn post_request(&self, path: &str, data: serde_json::Value) -> String { + let client = reqwest::blocking::Client::new(); + + client + .post(&format!("{}/api/v1{}", self.host, path)) + .header("Authorization", format!("Bearer {}", self.token)) + .json(&data) + .send() + .unwrap() + .text() + .unwrap() + } + // projects pub fn get_project_name_from_id(&self, id: isize) -> String { @@ -127,4 +153,32 @@ impl VikunjaAPI { let resp = self.get_request(&format!("/tasks/{id}")); serde_json::from_str(&resp).unwrap() } + + pub fn new_task(&self, title: &str, project: ProjectID) -> Task { + let id = project.0; + + let data = serde_json::json!({ + "title": title + }); + + // description + // due_date + // end_date + // is_favorite + // labels + // priority + + let resp = self.put_request(&format!("/projects/{id}/tasks"), data); + serde_json::from_str(&resp).unwrap() + } + + pub fn done_task(&self, task_id: isize) -> Task { + let resp = self.post_request( + &format!("/tasks/{task_id}"), + serde_json::json!({ + "done": true + }), + ); + serde_json::from_str(&resp).unwrap() + } } diff --git a/src/api/task.rs b/src/api/task.rs index 1f6d4dc..7ad99af 100644 --- a/src/api/task.rs +++ b/src/api/task.rs @@ -4,7 +4,7 @@ use super::{Label, User}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Task { - pub id: usize, + pub id: isize, pub title: String, pub description: String, pub done: bool, diff --git a/src/args.rs b/src/args.rs index 8c1720f..2fa8aad 100644 --- a/src/args.rs +++ b/src/args.rs @@ -18,5 +18,22 @@ pub fn get_args() -> clap::ArgMatches { .about("Commands about projects") .subcommand(command!().name("ls").about("List projects")), ) + .subcommand( + command!() + .name("new") + .about("Create a new task") + .arg(arg!([title] "Task title").required(true)) + .arg( + arg!(-p --project "Project to add task to") + .required(false) + .default_value("Inbox"), + ), + ) + .subcommand( + command!() + .name("done") + .about("Mark task as done") + .arg(arg!([task_id] "Task ID").required(true)), + ) .get_matches() } diff --git a/src/main.rs b/src/main.rs index 90ccde0..940ca90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ mod args; mod config; mod ui; -use api::VikunjaAPI; +use api::{ProjectID, VikunjaAPI}; fn main() { let config: config::Config = @@ -24,6 +24,17 @@ fn main() { ui::project::list_projects(&api); } }, + Some(("new", new_task_arg)) => { + let title: &String = new_task_arg.get_one("title").unwrap(); + let project: &String = new_task_arg.get_one("project").unwrap(); + let project = ProjectID::parse(&api, project).unwrap(); + let task = api.new_task(title.as_str(), project); + ui::task::print_task_info(task.id, &api); + } + Some(("done", done_args)) => { + let task_id: &String = done_args.get_one("task_id").unwrap(); + api.done_task(task_id.parse().unwrap()); + } _ => { let done = arg.get_flag("done"); let fav = arg.get_flag("favorite"); diff --git a/src/ui/task.rs b/src/ui/task.rs index 00c7c6a..b87fba6 100644 --- a/src/ui/task.rs +++ b/src/ui/task.rs @@ -57,24 +57,29 @@ pub fn print_current_tasks(api: &VikunjaAPI, done: bool, fav: bool, project: Opt pub fn print_task_info(task_id: isize, api: &VikunjaAPI) { let task = api.get_task(task_id); - let done_indicator = if task.done { - format!("{} ✓ ", parse_datetime(&task.done_at).unwrap()) - } else { - String::new() - }; - let fav_indicator = if task.is_favorite { " ⭐ " } else { "" }; - println!( - "{}{}'{}' [{}] [{}]", - done_indicator, - fav_indicator, - task.title, - task.id, - api.get_project_name_from_id(task.project_id) + if task.done { + print_color( + crossterm::style::Color::Green, + &format!("{} ✓ ", time_since(parse_datetime(&task.done_at).unwrap())), + ); + } + + if task.is_favorite { + print!("⭐ "); + } + + print_color(crossterm::style::Color::Blue, &task.title); + print_color(crossterm::style::Color::Yellow, &format!(" ({})", task.id)); + print_color( + crossterm::style::Color::DarkRed, + &format!(" [{}]\n", api.get_project_name_from_id(task.project_id)), ); + println!("Created by {}", task.created_by.username); if let Some(due_date) = parse_datetime(&task.due_date) { + // todo : color if overdue println!("Due at {due_date}"); } @@ -90,6 +95,7 @@ pub fn print_task_info(task_id: isize, api: &VikunjaAPI) { } if let Some(labels) = task.labels { + // todo : labels and color println!("Labels: {}", labels.first().unwrap().title); } @@ -99,7 +105,7 @@ pub fn print_task_info(task_id: isize, api: &VikunjaAPI) { time_since(parse_datetime(&task.updated).unwrap()) ); - if task.description != "

" { + if task.description != "

" && !task.description.is_empty() { println!("---\n{}", task.description); }