feat: ability to complete a task (#35)
This commit is contained in:
commit
ac97deb996
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2907,6 +2907,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-wasm",
|
"tracing-wasm",
|
||||||
"validator",
|
"validator",
|
||||||
|
@ -23,6 +23,7 @@ tracing-wasm = "0.2.1"
|
|||||||
serde_with = { version = "3.9.0", features = ["chrono_0_4"] }
|
serde_with = { version = "3.9.0", features = ["chrono_0_4"] }
|
||||||
async-std = "1.12.0"
|
async-std = "1.12.0"
|
||||||
dioxus-query = "0.5.1"
|
dioxus-query = "0.5.1"
|
||||||
|
time = "0.3.36"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
@ -3,15 +3,21 @@ use crate::models::task::Task;
|
|||||||
use dioxus::core_macro::rsx;
|
use dioxus::core_macro::rsx;
|
||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_query::prelude::use_query_client;
|
||||||
|
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
||||||
|
use crate::server::tasks::complete_task;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub(crate) fn TaskList(tasks: Vec<Task>, class: Option<&'static str>) -> Element {
|
pub(crate) fn TaskList(tasks: Vec<Task>, class: Option<&'static str>) -> Element {
|
||||||
|
let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
|
||||||
let mut task_being_edited = use_context::<Signal<Option<Task>>>();
|
let mut task_being_edited = use_context::<Signal<Option<Task>>>();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: format!("flex flex-col {}", class.unwrap_or("")),
|
class: format!("flex flex-col {}", class.unwrap_or("")),
|
||||||
for task in tasks {
|
{tasks.iter().cloned().map(|task| {
|
||||||
|
let task_clone = task.clone();
|
||||||
|
rsx! {
|
||||||
div {
|
div {
|
||||||
key: "{task.id()}",
|
key: "{task.id()}",
|
||||||
class: format!(
|
class: format!(
|
||||||
@ -33,7 +39,28 @@ pub(crate) fn TaskList(tasks: Vec<Task>, class: Option<&'static str>) -> Element
|
|||||||
),
|
),
|
||||||
onclick: move |_| task_being_edited.set(Some(task.clone())),
|
onclick: move |_| task_being_edited.set(Some(task.clone())),
|
||||||
i {
|
i {
|
||||||
class: "fa-regular fa-square text-3xl text-zinc-600",
|
class: format!(
|
||||||
|
"{} text-3xl text-zinc-500",
|
||||||
|
if *(task_clone.category()) == Category::Done {
|
||||||
|
"fa solid fa-square-check"
|
||||||
|
} else {
|
||||||
|
"fa-regular fa-square"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
onclick: move |event| {
|
||||||
|
// To prevent editing the task.
|
||||||
|
event.stop_propagation();
|
||||||
|
let task = task_clone.clone();
|
||||||
|
async move {
|
||||||
|
let completed_task = complete_task(task.id()).await;
|
||||||
|
query_client.invalidate_queries(&[
|
||||||
|
QueryKey::Tasks,
|
||||||
|
QueryKey::TasksInCategory(
|
||||||
|
completed_task.unwrap().category().clone()
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
div {
|
div {
|
||||||
class: "flex flex-col",
|
class: "flex flex-col",
|
||||||
@ -67,6 +94,7 @@ pub(crate) fn TaskList(tasks: Vec<Task>, class: Option<&'static str>) -> Element
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ impl FromSql<Jsonb, Pg> for Category {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Hash, Clone, Debug)]
|
#[derive(Serialize, Deserialize, PartialEq, Hash, Clone, Debug)]
|
||||||
pub enum ReoccurrenceInterval {
|
pub enum ReoccurrenceInterval {
|
||||||
Day,
|
Day,
|
||||||
Month,
|
Month,
|
||||||
@ -102,6 +102,10 @@ impl Reoccurrence {
|
|||||||
Self { start_date, interval, length }
|
Self { start_date, interval, length }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn start_date(&self) -> NaiveDate {
|
||||||
|
self.start_date
|
||||||
|
}
|
||||||
|
|
||||||
pub fn interval(&self) -> &ReoccurrenceInterval {
|
pub fn interval(&self) -> &ReoccurrenceInterval {
|
||||||
&self.interval
|
&self.interval
|
||||||
}
|
}
|
||||||
|
@ -69,3 +69,9 @@ impl NewTask {
|
|||||||
Self { title, deadline, category, project_id }
|
Self { title, deadline, category, project_id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Task> for NewTask {
|
||||||
|
fn from(task: Task) -> Self {
|
||||||
|
Self::new(task.title, task.deadline, task.category, task.project_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
use chrono::{Datelike, Days, Months, NaiveDate};
|
||||||
use crate::errors::error::Error;
|
use crate::errors::error::Error;
|
||||||
use crate::errors::error_vec::ErrorVec;
|
use crate::errors::error_vec::ErrorVec;
|
||||||
use crate::models::task::{NewTask, Task};
|
use crate::models::task::{NewTask, Task};
|
||||||
use crate::server::database_connection::establish_database_connection;
|
use crate::server::database_connection::establish_database_connection;
|
||||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper};
|
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use time::util::days_in_year_month;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
use crate::errors::task_error::TaskError;
|
use crate::errors::task_error::TaskError;
|
||||||
use crate::models::category::Category;
|
use crate::models::category::{Category, ReoccurrenceInterval};
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub(crate) async fn create_task(new_task: NewTask)
|
pub(crate) async fn create_task(new_task: NewTask)
|
||||||
@ -30,6 +32,24 @@ pub(crate) async fn create_task(new_task: NewTask)
|
|||||||
Ok(new_task)
|
Ok(new_task)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
pub(crate) async fn get_task(task_id: i32) -> Result<Task, ServerFnError<ErrorVec<Error>>> {
|
||||||
|
use crate::schema::tasks::dsl::*;
|
||||||
|
|
||||||
|
let mut connection = establish_database_connection()
|
||||||
|
.map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?;
|
||||||
|
|
||||||
|
let task = tasks
|
||||||
|
.find(task_id)
|
||||||
|
.select(Task::as_select())
|
||||||
|
.first(&mut connection)
|
||||||
|
.optional()
|
||||||
|
.map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?;
|
||||||
|
|
||||||
|
// TODO: Handle not finding the task.
|
||||||
|
Ok(task.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub(crate) async fn get_tasks_in_category(filtered_category: Category)
|
pub(crate) async fn get_tasks_in_category(filtered_category: Category)
|
||||||
-> Result<Vec<Task>, ServerFnError<ErrorVec<Error>>> {
|
-> Result<Vec<Task>, ServerFnError<ErrorVec<Error>>> {
|
||||||
@ -78,3 +98,41 @@ pub(crate) async fn edit_task(task_id: i32, new_task: NewTask)
|
|||||||
|
|
||||||
Ok(updated_task)
|
Ok(updated_task)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<ErrorVec<Error>>> {
|
||||||
|
let task = get_task(task_id).await?;
|
||||||
|
let mut new_task = NewTask::from(task);
|
||||||
|
|
||||||
|
if let Category::Calendar {
|
||||||
|
reoccurrence: Some(reoccurrence),
|
||||||
|
date,
|
||||||
|
..
|
||||||
|
} = &mut new_task.category {
|
||||||
|
match reoccurrence.interval() {
|
||||||
|
ReoccurrenceInterval::Day => *date = *date + Days::new(reoccurrence.length() as u64),
|
||||||
|
ReoccurrenceInterval::Month | ReoccurrenceInterval::Year => {
|
||||||
|
*date = *date + Months::new(
|
||||||
|
reoccurrence.length() *
|
||||||
|
if *(reoccurrence.interval()) == ReoccurrenceInterval::Year
|
||||||
|
{ 12 } else { 1 }
|
||||||
|
);
|
||||||
|
*date = NaiveDate::from_ymd_opt(
|
||||||
|
date.year(),
|
||||||
|
date.month(),
|
||||||
|
reoccurrence.start_date().day().min(days_in_year_month(
|
||||||
|
date.year(),
|
||||||
|
(date.month() as u8).try_into().unwrap(),
|
||||||
|
) as u32),
|
||||||
|
).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_task.category = Category::Done;
|
||||||
|
}
|
||||||
|
|
||||||
|
let updated_task = edit_task(task_id, new_task).await
|
||||||
|
.map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?;
|
||||||
|
|
||||||
|
Ok(updated_task)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user