feat: ability to edit a project #30
@ -2,6 +2,7 @@ use dioxus::prelude::*;
|
|||||||
use crate::components::navigation::Navigation;
|
use crate::components::navigation::Navigation;
|
||||||
use crate::components::project_form::ProjectForm;
|
use crate::components::project_form::ProjectForm;
|
||||||
use crate::components::task_form::TaskForm;
|
use crate::components::task_form::TaskForm;
|
||||||
|
use crate::models::project::Project;
|
||||||
use crate::route::Route;
|
use crate::route::Route;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@ -11,9 +12,11 @@ pub(crate) fn BottomPanel(display_form: Signal<bool>) -> Element {
|
|||||||
let mut expanded = use_signal(|| display_form());
|
let mut expanded = use_signal(|| display_form());
|
||||||
let navigation_expanded = use_signal(|| false);
|
let navigation_expanded = use_signal(|| false);
|
||||||
let current_route = use_route();
|
let current_route = use_route();
|
||||||
|
|
||||||
|
let mut project_being_edited = use_context::<Signal<Option<Project>>>();
|
||||||
|
|
||||||
use_effect(use_reactive(&display_form, move |creating_task| {
|
use_effect(use_reactive(&display_form, move |display_form| {
|
||||||
if creating_task() {
|
if display_form() {
|
||||||
expanded.set(true);
|
expanded.set(true);
|
||||||
} else {
|
} else {
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
@ -39,8 +42,10 @@ pub(crate) fn BottomPanel(display_form: Signal<bool>) -> Element {
|
|||||||
match current_route {
|
match current_route {
|
||||||
Route::ProjectsPage => rsx! {
|
Route::ProjectsPage => rsx! {
|
||||||
ProjectForm {
|
ProjectForm {
|
||||||
|
project: project_being_edited(),
|
||||||
on_successful_submit: move |_| {
|
on_successful_submit: move |_| {
|
||||||
display_form.set(false);
|
display_form.set(false);
|
||||||
|
project_being_edited.set(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
use dioxus::prelude::*;
|
|
||||||
|
|
||||||
#[component]
|
|
||||||
pub(crate) fn CreateButton(creating: Signal<bool>) -> Element {
|
|
||||||
rsx! {
|
|
||||||
button {
|
|
||||||
class: "m-4 py-3 px-5 self-end text-center bg-zinc-300/50 rounded-xl border-t-zinc-200 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] text-2xl text-zinc-200",
|
|
||||||
onclick: move |_| {
|
|
||||||
creating.set(!creating());
|
|
||||||
},
|
|
||||||
i {
|
|
||||||
class: format!("min-w-6 fa-solid {}", if creating() { "fa-xmark" } else { "fa-plus" }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
22
src/components/form_open_button.rs
Normal file
22
src/components/form_open_button.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use crate::models::project::Project;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub(crate) fn FormOpenButton(opened: Signal<bool>) -> Element {
|
||||||
|
let mut project_being_edited = use_context::<Signal<Option<Project>>>();
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
button {
|
||||||
|
class: "m-4 py-3 px-5 self-end text-center bg-zinc-300/50 rounded-xl border-t-zinc-200 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] text-2xl text-zinc-200",
|
||||||
|
onclick: move |_| {
|
||||||
|
if opened() {
|
||||||
|
project_being_edited.set(None);
|
||||||
|
}
|
||||||
|
opened.set(!opened());
|
||||||
|
},
|
||||||
|
i {
|
||||||
|
class: format!("min-w-6 fa-solid {}", if opened() { "fa-xmark" } else { "fa-plus" }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,18 +3,26 @@ use crate::route::Route;
|
|||||||
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 crate::components::create_task_button::CreateButton;
|
use crate::components::form_open_button::FormOpenButton;
|
||||||
use crate::components::sticky_bottom::StickyBottom;
|
use crate::components::sticky_bottom::StickyBottom;
|
||||||
|
use crate::models::project::Project;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub(crate) fn Layout() -> Element {
|
pub(crate) fn Layout() -> Element {
|
||||||
let display_form = use_signal(|| false);
|
let mut display_form = use_signal(|| false);
|
||||||
|
let project_being_edited = use_context_provider::<Signal<Option<Project>>>(
|
||||||
|
|| Signal::new(None)
|
||||||
|
);
|
||||||
|
|
||||||
|
use_effect(move || {
|
||||||
|
display_form.set(project_being_edited().is_some());
|
||||||
|
});
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
Outlet::<Route> {}
|
Outlet::<Route> {}
|
||||||
StickyBottom {
|
StickyBottom {
|
||||||
CreateButton {
|
FormOpenButton {
|
||||||
creating: display_form,
|
opened: display_form,
|
||||||
}
|
}
|
||||||
BottomPanel {
|
BottomPanel {
|
||||||
display_form: display_form,
|
display_form: display_form,
|
||||||
|
@ -5,7 +5,7 @@ pub(crate) mod task_form;
|
|||||||
pub(crate) mod task_list;
|
pub(crate) mod task_list;
|
||||||
pub(crate) mod pages;
|
pub(crate) mod pages;
|
||||||
pub(crate) mod navigation;
|
pub(crate) mod navigation;
|
||||||
pub(crate) mod create_task_button;
|
pub(crate) mod form_open_button;
|
||||||
pub(crate) mod bottom_panel;
|
pub(crate) mod bottom_panel;
|
||||||
pub(crate) mod sticky_bottom;
|
pub(crate) mod sticky_bottom;
|
||||||
pub(crate) mod category_input;
|
pub(crate) mod category_input;
|
||||||
|
@ -1,22 +1,32 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_query::prelude::QueryResult;
|
use dioxus_query::prelude::QueryResult;
|
||||||
|
use crate::models::project::Project;
|
||||||
use crate::query::projects::use_projects_query;
|
use crate::query::projects::use_projects_query;
|
||||||
use crate::query::QueryValue;
|
use crate::query::QueryValue;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub(crate) fn ProjectsPage() -> Element {
|
pub(crate) fn ProjectsPage() -> Element {
|
||||||
let projects_query = use_projects_query();
|
let projects_query = use_projects_query();
|
||||||
|
let mut project_being_edited = use_context::<Signal<Option<Project>>>();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
match projects_query.result().value() {
|
match projects_query.result().value() {
|
||||||
QueryResult::Ok(QueryValue::Projects(projects))
|
QueryResult::Ok(QueryValue::Projects(projects))
|
||||||
| QueryResult::Loading(Some(QueryValue::Projects(projects))) => rsx! {
|
| QueryResult::Loading(Some(QueryValue::Projects(projects))) => rsx! {
|
||||||
div {
|
div {
|
||||||
class: "flex flex-col",
|
class: "flex flex-col",
|
||||||
for project in projects {
|
for project in projects.clone() {
|
||||||
div {
|
div {
|
||||||
key: "{project.id()}",
|
key: "{project.id()}",
|
||||||
class: "px-8 py-4",
|
class: format!(
|
||||||
|
"px-8 py-4 select-none {}",
|
||||||
|
if project_being_edited().map(|p| p.id()) == Some(project.id()) {
|
||||||
|
"bg-zinc-700"
|
||||||
|
} else { "" }
|
||||||
|
),
|
||||||
|
onclick: move |_| {
|
||||||
|
project_being_edited.set(Some(project.clone()));
|
||||||
|
},
|
||||||
{project.title()}
|
{project.title()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::models::project::NewProject;
|
use crate::models::project::{NewProject, Project};
|
||||||
use crate::server::projects::create_project;
|
use crate::server::projects::{create_project, edit_project};
|
||||||
use dioxus::core_macro::{component, rsx};
|
use dioxus::core_macro::{component, rsx};
|
||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
@ -7,17 +7,28 @@ use dioxus_query::prelude::use_query_client;
|
|||||||
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub(crate) fn ProjectForm(on_successful_submit: EventHandler<()>) -> Element {
|
pub(crate) fn ProjectForm(project: Option<Project>, on_successful_submit: EventHandler<()>)
|
||||||
|
-> Element {
|
||||||
let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
|
let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
|
||||||
|
|
||||||
|
let project_for_submit = project.clone();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
form {
|
form {
|
||||||
onsubmit: move |event| {
|
onsubmit: move |event| {
|
||||||
|
let project_clone = project_for_submit.clone();
|
||||||
async move {
|
async move {
|
||||||
let new_project = NewProject::new(
|
let new_project = NewProject::new(
|
||||||
event.values().get("title").unwrap().as_value()
|
event.values().get("title").unwrap().as_value()
|
||||||
);
|
);
|
||||||
let _ = create_project(new_project).await;
|
match project_clone {
|
||||||
|
Some(project) => {
|
||||||
|
let _ = edit_project(project.id(), new_project).await;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let _ = create_project(new_project).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
query_client.invalidate_queries(&[
|
query_client.invalidate_queries(&[
|
||||||
QueryKey::Projects
|
QueryKey::Projects
|
||||||
]);
|
]);
|
||||||
@ -35,9 +46,10 @@ pub(crate) fn ProjectForm(on_successful_submit: EventHandler<()>) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
r#type: "text",
|
|
||||||
name: "title",
|
name: "title",
|
||||||
required: true,
|
required: true,
|
||||||
|
initial_value: project.map(|project| project.title().to_owned()),
|
||||||
|
r#type: "text",
|
||||||
class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg",
|
class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg",
|
||||||
id: "input_title"
|
id: "input_title"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user
Approve the implementation but suggest adding documentation.
The implementation of the
FormOpenButton
component is approved as it effectively uses context and signals to manage and toggle the state of a project being edited. However, consider adding documentation to explain the component's functionality and its role within the project management system, especially how it interacts with other components and the state.Would you like help with drafting the documentation for this component?