feat: create a from for subtasks
This commit is contained in:
parent
11bab284d4
commit
a05b4f9f66
@ -12,3 +12,4 @@ pub(crate) mod category_input;
|
||||
pub(crate) mod reoccurrence_input;
|
||||
pub(crate) mod layout;
|
||||
pub(crate) mod navigation_item;
|
||||
pub(crate) mod subtasks_form;
|
||||
|
158
src/components/subtasks_form.rs
Normal file
158
src/components/subtasks_form.rs
Normal file
@ -0,0 +1,158 @@
|
||||
use crate::models::subtask::NewSubtask;
|
||||
use crate::query::subtasks::use_subtasks_of_task_query;
|
||||
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
||||
use crate::server::subtasks::{create_subtask, delete_subtask, edit_subtask};
|
||||
use dioxus::core_macro::{component, rsx};
|
||||
use dioxus::dioxus_core::Element;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_query::prelude::{use_query_client, QueryResult};
|
||||
|
||||
#[component]
|
||||
pub(crate) fn SubtasksForm(task_id: i32) -> Element {
|
||||
let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
|
||||
let subtasks_query = use_subtasks_of_task_query(task_id);
|
||||
|
||||
let mut new_title = use_signal(String::new);
|
||||
|
||||
rsx! {
|
||||
form {
|
||||
class: "flex flex-row items-center gap-3",
|
||||
onsubmit: move |event| async move {
|
||||
let new_subtask = NewSubtask::new(
|
||||
task_id,
|
||||
event.values().get("title").unwrap().as_value(),
|
||||
false
|
||||
);
|
||||
let _ = create_subtask(new_subtask).await;
|
||||
query_client.invalidate_queries(&[QueryKey::SubtasksOfTaskId(task_id)]);
|
||||
new_title.set(String::new());
|
||||
},
|
||||
label {
|
||||
r#for: "input_new_title",
|
||||
class: "min-w-6 text-center",
|
||||
i {
|
||||
class: "fa-solid fa-list-check text-zinc-400/50"
|
||||
}
|
||||
}
|
||||
div {
|
||||
class: "grow grid grid-cols-6 gap-2",
|
||||
input {
|
||||
name: "title",
|
||||
required: true,
|
||||
value: new_title,
|
||||
r#type: "text",
|
||||
class: "grow py-2 px-3 col-span-5 bg-zinc-800/50 rounded-lg",
|
||||
id: "input_new_title",
|
||||
onchange: move |event| new_title.set(event.value())
|
||||
}
|
||||
button {
|
||||
r#type: "submit",
|
||||
class: "py-2 col-span-1 bg-zinc-800/50 rounded-lg",
|
||||
i {
|
||||
class: "fa-solid fa-plus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match subtasks_query.result().value() {
|
||||
QueryResult::Ok(QueryValue::Subtasks(subtasks))
|
||||
| QueryResult::Loading(Some(QueryValue::Subtasks(subtasks))) => {
|
||||
rsx! {
|
||||
for subtask in subtasks.clone() {
|
||||
div {
|
||||
key: "{subtask.id()}",
|
||||
class: "flex flex-row items-center gap-3",
|
||||
i {
|
||||
class: format!(
|
||||
"{} min-w-6 text-center text-2xl text-zinc-400/50",
|
||||
if subtask.is_completed() {
|
||||
"fa solid fa-square-check"
|
||||
} else {
|
||||
"fa-regular fa-square"
|
||||
}
|
||||
),
|
||||
onclick: {
|
||||
let subtask = subtask.clone();
|
||||
move |_| {
|
||||
let subtask = subtask.clone();
|
||||
async move {
|
||||
let new_subtask = NewSubtask::new(
|
||||
subtask.task_id(),
|
||||
subtask.title().to_owned(),
|
||||
!subtask.is_completed()
|
||||
);
|
||||
let _ = edit_subtask(
|
||||
subtask.id(),
|
||||
new_subtask
|
||||
).await;
|
||||
query_client.invalidate_queries(&[
|
||||
QueryKey::SubtasksOfTaskId(task_id)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
class: "grow grid grid-cols-6 gap-2",
|
||||
input {
|
||||
r#type: "text",
|
||||
class: "grow py-2 px-3 col-span-5 bg-zinc-800/50 rounded-lg",
|
||||
id: "input_new_title",
|
||||
initial_value: subtask.title(),
|
||||
onchange: {
|
||||
let subtask = subtask.clone();
|
||||
move |event| {
|
||||
let subtask = subtask.clone();
|
||||
async move {
|
||||
let new_subtask = NewSubtask::new(
|
||||
subtask.task_id(),
|
||||
event.value(),
|
||||
subtask.is_completed()
|
||||
);
|
||||
let _ = edit_subtask(
|
||||
subtask.id(),
|
||||
new_subtask
|
||||
).await;
|
||||
query_client.invalidate_queries(&[
|
||||
QueryKey::SubtasksOfTaskId(task_id)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
button {
|
||||
r#type: "button",
|
||||
class: "py-2 col-span-1 bg-zinc-800/50 rounded-lg",
|
||||
onclick: {
|
||||
let subtask = subtask.clone();
|
||||
move |_| {
|
||||
let subtask = subtask.clone();
|
||||
async move {
|
||||
let _ = delete_subtask(subtask.id()).await;
|
||||
query_client.invalidate_queries(&[
|
||||
QueryKey::SubtasksOfTaskId(task_id)
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
i {
|
||||
class: "fa-solid fa-trash-can"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
QueryResult::Loading(None) => rsx! {
|
||||
// TODO: Add a loading indicator.
|
||||
},
|
||||
QueryResult::Err(errors) => rsx! {
|
||||
div {
|
||||
"Errors occurred: {errors:?}"
|
||||
}
|
||||
},
|
||||
value => panic!("Unexpected query result: {value:?}")
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ use dioxus::core_macro::{component, rsx};
|
||||
use dioxus::dioxus_core::Element;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_query::prelude::use_query_client;
|
||||
use crate::components::subtasks_form::SubtasksForm;
|
||||
|
||||
const REMINDER_OFFSETS: [Option<Duration>; 17] = [
|
||||
None,
|
||||
@ -79,7 +80,11 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
||||
let task_for_submit = task.clone();
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
class: "p-4 flex flex-col gap-4",
|
||||
form {
|
||||
class: "flex flex-col gap-4",
|
||||
id: "form_task",
|
||||
onsubmit: move |event| {
|
||||
let task = task_for_submit.clone();
|
||||
async move {
|
||||
@ -99,7 +104,8 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
||||
event.values().get("category_calendar_date").unwrap()
|
||||
.as_value().parse().unwrap(),
|
||||
reoccurrence_interval,
|
||||
event.values().get("category_calendar_reoccurrence_length")
|
||||
event.values()
|
||||
.get("category_calendar_reoccurrence_length")
|
||||
.unwrap().as_value().parse().unwrap()
|
||||
)
|
||||
),
|
||||
@ -109,8 +115,8 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
||||
time,
|
||||
REMINDER_OFFSETS[
|
||||
event.values()
|
||||
.get("category_calendar_reminder_offset_index").unwrap()
|
||||
.as_value().parse::<usize>().unwrap()
|
||||
.get("category_calendar_reminder_offset_index")
|
||||
.unwrap().as_value().parse::<usize>().unwrap()
|
||||
]
|
||||
)
|
||||
)
|
||||
@ -132,7 +138,6 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
||||
on_successful_submit.call(());
|
||||
}
|
||||
},
|
||||
class: "p-4 flex flex-col gap-4",
|
||||
div {
|
||||
class: "flex flex-row items-center gap-3",
|
||||
label {
|
||||
@ -286,9 +291,11 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
||||
disabled: category_calendar_reoccurrence_interval().is_none(),
|
||||
required: true,
|
||||
min: 1,
|
||||
initial_value: category_calendar_reoccurrence_interval()
|
||||
.map_or(String::new(), |_| reoccurrence.map_or(1, |reoccurrence|
|
||||
reoccurrence.length()).to_string()),
|
||||
initial_value: category_calendar_reoccurrence_interval().map_or(
|
||||
String::new(),
|
||||
|_| reoccurrence.map_or(1, |reoccurrence|
|
||||
reoccurrence.length()).to_string()
|
||||
),
|
||||
class: "py-2 px-3 bg-zinc-800/50 rounded-lg col-span-2 text-right",
|
||||
id: "category_calendar_reoccurrence_length"
|
||||
}
|
||||
@ -334,7 +341,13 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
||||
}
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
},
|
||||
if let Some(task) = task.as_ref() {
|
||||
SubtasksForm {
|
||||
task_id: task.id()
|
||||
}
|
||||
}
|
||||
div {
|
||||
class: "flex flex-row justify-between mt-auto",
|
||||
button {
|
||||
@ -370,6 +383,7 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
||||
}
|
||||
}
|
||||
button {
|
||||
form: "form_task",
|
||||
r#type: "submit",
|
||||
class: "py-2 px-4 bg-zinc-300/50 rounded-lg",
|
||||
i {
|
||||
|
@ -15,9 +15,7 @@ pub(crate) fn TaskList(tasks: Vec<Task>, class: Option<&'static str>) -> Element
|
||||
rsx! {
|
||||
div {
|
||||
class: format!("flex flex-col {}", class.unwrap_or("")),
|
||||
{tasks.iter().cloned().map(|task| {
|
||||
let task_clone = task.clone();
|
||||
rsx! {
|
||||
for task in tasks.clone() {
|
||||
div {
|
||||
key: "{task.id()}",
|
||||
class: format!(
|
||||
@ -37,28 +35,39 @@ pub(crate) fn TaskList(tasks: Vec<Task>, class: Option<&'static str>) -> Element
|
||||
"bg-zinc-700"
|
||||
} else { "" }
|
||||
),
|
||||
onclick: move |_| task_being_edited.set(Some(task.clone())),
|
||||
onclick: {
|
||||
let task = task.clone();
|
||||
move |_| task_being_edited.set(Some(task.clone()))
|
||||
},
|
||||
i {
|
||||
class: format!(
|
||||
"{} text-3xl text-zinc-500",
|
||||
if *(task_clone.category()) == Category::Done {
|
||||
if *(task.category()) == Category::Done {
|
||||
"fa solid fa-square-check"
|
||||
} else {
|
||||
"fa-regular fa-square"
|
||||
}
|
||||
),
|
||||
onclick: move |event| {
|
||||
onclick: {
|
||||
let task = task.clone();
|
||||
move |event| {
|
||||
// To prevent editing the task.
|
||||
event.stop_propagation();
|
||||
let task = task_clone.clone();
|
||||
let task = task.clone();
|
||||
async move {
|
||||
let completed_task = complete_task(task.id()).await;
|
||||
query_client.invalidate_queries(&[
|
||||
let mut query_keys = vec![
|
||||
QueryKey::Tasks,
|
||||
QueryKey::TasksInCategory(
|
||||
completed_task.unwrap().category().clone()
|
||||
),
|
||||
]);
|
||||
)
|
||||
];
|
||||
if let Category::Calendar { reoccurrence: Some(_), .. }
|
||||
= task.category() {
|
||||
query_keys.push(QueryKey::SubtasksOfTaskId(task.id()));
|
||||
}
|
||||
query_client.invalidate_queries(&query_keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -94,7 +103,6 @@ pub(crate) fn TaskList(tasks: Vec<Task>, class: Option<&'static str>) -> Element
|
||||
}
|
||||
}
|
||||
}
|
||||
})}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user