feat: display a project form upon clicking the create button on the projects page
This commit is contained in:
parent
270e06de46
commit
fe1837d6a1
@ -1,18 +1,17 @@
|
||||
use std::thread::sleep;
|
||||
use dioxus::prelude::*;
|
||||
use crate::components::navigation::Navigation;
|
||||
use crate::components::project_form::ProjectForm;
|
||||
use crate::components::task_form::TaskForm;
|
||||
use crate::components::task_list::TaskList;
|
||||
use crate::models::category::Category;
|
||||
use crate::route::Route;
|
||||
|
||||
#[component]
|
||||
pub(crate) fn BottomPanel(creating_task: bool) -> Element {
|
||||
let mut expanded = use_signal(|| creating_task);
|
||||
pub(crate) fn BottomPanel(display_form: Signal<bool>) -> Element {
|
||||
let mut expanded = use_signal(|| display_form());
|
||||
let navigation_expanded = use_signal(|| false);
|
||||
let current_route = use_route();
|
||||
|
||||
use_effect(use_reactive(&creating_task, move |creating_task| {
|
||||
if creating_task {
|
||||
use_effect(use_reactive(&display_form, move |creating_task| {
|
||||
if creating_task() {
|
||||
expanded.set(true);
|
||||
} else {
|
||||
spawn(async move {
|
||||
@ -26,14 +25,30 @@ pub(crate) fn BottomPanel(creating_task: bool) -> Element {
|
||||
div {
|
||||
class: format!(
|
||||
"bg-zinc-700/50 rounded-t-xl border-t-zinc-600 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] transition-[height] duration-[500ms] ease-[cubic-bezier(0.79,0.14,0.15,0.86)] {}",
|
||||
match (creating_task, navigation_expanded()) {
|
||||
(false, false) => "h-[64px]",
|
||||
(false, true) => "h-[128px]",
|
||||
(true, _) => "h-[448px]",
|
||||
match (display_form(), current_route, navigation_expanded()) {
|
||||
(false, _, false) => "h-[64px]",
|
||||
(false, _, true) => "h-[128px]",
|
||||
(true, Route::ProjectsPage, _) => "h-[128px]",
|
||||
(true, _, _) => "h-[448px]",
|
||||
}
|
||||
),
|
||||
if expanded() {
|
||||
TaskForm {}
|
||||
match current_route {
|
||||
Route::ProjectsPage => rsx! {
|
||||
ProjectForm {
|
||||
on_successful_submit: move |_| {
|
||||
display_form.set(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => rsx! {
|
||||
TaskForm {
|
||||
on_successful_submit: move |_| {
|
||||
display_form.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Navigation {
|
||||
expanded: navigation_expanded,
|
||||
|
@ -4,7 +4,7 @@ use crate::models::category::Category;
|
||||
use crate::route::Route;
|
||||
|
||||
#[component]
|
||||
pub(crate) fn CreateTaskButton(creating: Signal<bool>) -> Element {
|
||||
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",
|
||||
|
@ -1,29 +1,23 @@
|
||||
use crate::components::bottom_panel::BottomPanel;
|
||||
use crate::components::navigation::Navigation;
|
||||
use crate::components::task_list::TaskList;
|
||||
use crate::models::category::Category;
|
||||
use crate::route::Route;
|
||||
use chrono::NaiveDate;
|
||||
use dioxus::core_macro::rsx;
|
||||
use dioxus::dioxus_core::Element;
|
||||
use dioxus::prelude::*;
|
||||
use crate::components::create_task_button::CreateTaskButton;
|
||||
use crate::components::create_task_button::CreateButton;
|
||||
use crate::components::sticky_bottom::StickyBottom;
|
||||
use crate::components::task_form::TaskForm;
|
||||
use crate::server::tasks::get_tasks_in_category;
|
||||
|
||||
#[component]
|
||||
pub(crate) fn Layout() -> Element {
|
||||
let creating_task = use_signal(|| false);
|
||||
let display_form = use_signal(|| false);
|
||||
|
||||
rsx! {
|
||||
Outlet::<Route> {}
|
||||
StickyBottom {
|
||||
CreateTaskButton {
|
||||
creating: creating_task,
|
||||
CreateButton {
|
||||
creating: display_form,
|
||||
}
|
||||
BottomPanel {
|
||||
creating_task: creating_task(),
|
||||
display_form: display_form,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,8 @@ pub(crate) fn CategoryCalendarPage() -> Element {
|
||||
div {
|
||||
"Errors occurred: {errors:?}"
|
||||
}
|
||||
}
|
||||
},
|
||||
value => panic!("Unexpected query result: {value:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ pub(crate) fn CategoryPage(category: Category) -> Element {
|
||||
div {
|
||||
"Errors occurred: {errors:?}"
|
||||
}
|
||||
}
|
||||
},
|
||||
value => panic!("Unexpected query result: {value:?}")
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,8 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
||||
div {
|
||||
"Errors occurred: {errors:?}"
|
||||
}
|
||||
}
|
||||
},
|
||||
value => panic!("Unexpected query result: {value:?}")
|
||||
}
|
||||
match calendar_tasks_query_result.value() {
|
||||
QueryResult::Ok(QueryValue::Tasks(tasks))
|
||||
@ -151,7 +152,8 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
||||
div {
|
||||
"Errors occurred: {errors:?}"
|
||||
}
|
||||
}
|
||||
},
|
||||
value => panic!("Unexpected query result: {value:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,36 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_query::prelude::QueryResult;
|
||||
use crate::query::projects::use_projects_query;
|
||||
use crate::query::QueryValue;
|
||||
|
||||
#[component]
|
||||
pub(crate) fn ProjectsPage() -> Element {
|
||||
let projects_query = use_projects_query();
|
||||
|
||||
rsx! {
|
||||
match projects_query.result().value() {
|
||||
QueryResult::Ok(QueryValue::Projects(projects))
|
||||
| QueryResult::Loading(Some(QueryValue::Projects(projects))) => rsx! {
|
||||
div {
|
||||
class: "flex flex-col",
|
||||
for project in projects {
|
||||
div {
|
||||
key: "{project.id()}",
|
||||
class: "px-8 py-4",
|
||||
{project.title()}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
QueryResult::Loading(None) => rsx! {
|
||||
// TODO: Add a loading indicator.
|
||||
},
|
||||
QueryResult::Err(errors) => rsx! {
|
||||
div {
|
||||
"Errors occurred: {errors:?}"
|
||||
}
|
||||
},
|
||||
value => panic!("Unexpected query result: {value:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,13 @@ use crate::server::projects::create_project;
|
||||
use dioxus::core_macro::{component, rsx};
|
||||
use dioxus::dioxus_core::Element;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_query::prelude::use_query_client;
|
||||
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
||||
|
||||
#[component]
|
||||
pub(crate) fn ProjectForm() -> Element {
|
||||
pub(crate) fn ProjectForm(on_successful_submit: EventHandler<()>) -> Element {
|
||||
let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
|
||||
|
||||
rsx! {
|
||||
form {
|
||||
onsubmit: move |event| {
|
||||
@ -14,17 +18,39 @@ pub(crate) fn ProjectForm() -> Element {
|
||||
event.values().get("title").unwrap().as_value()
|
||||
);
|
||||
let _ = create_project(new_project).await;
|
||||
query_client.invalidate_queries(&[
|
||||
QueryKey::Projects
|
||||
]);
|
||||
on_successful_submit.call(());
|
||||
}
|
||||
},
|
||||
input {
|
||||
r#type: "text",
|
||||
name: "title",
|
||||
required: true,
|
||||
placeholder: "title"
|
||||
class: "p-4 flex flex-col gap-4",
|
||||
div {
|
||||
class: "flex flex-row items-center gap-3",
|
||||
label {
|
||||
r#for: "input_title",
|
||||
class: "min-w-6 text-center",
|
||||
i {
|
||||
class: "fa-solid fa-pen-clip text-zinc-400/50"
|
||||
}
|
||||
}
|
||||
input {
|
||||
r#type: "text",
|
||||
name: "title",
|
||||
required: true,
|
||||
class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg",
|
||||
id: "input_title"
|
||||
}
|
||||
}
|
||||
button {
|
||||
r#type: "submit",
|
||||
"create"
|
||||
div {
|
||||
class: "flex flex-row justify-end mt-auto",
|
||||
button {
|
||||
r#type: "submit",
|
||||
class: "py-2 px-4 bg-zinc-300/50 rounded-lg",
|
||||
i {
|
||||
class: "fa-solid fa-floppy-disk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::components::category_input::CategoryInput;
|
||||
use crate::components::reoccurrence_input::ReoccurrenceIntervalInput;
|
||||
use crate::models::category::{CalendarTime, Category, Reoccurrence, ReoccurrenceInterval};
|
||||
use crate::models::category::{CalendarTime, Category, Reoccurrence};
|
||||
use crate::models::task::NewTask;
|
||||
use crate::server::projects::get_projects;
|
||||
use crate::server::tasks::create_task;
|
||||
@ -33,11 +33,11 @@ const REMINDER_OFFSETS: [Option<Duration>; 17] = [
|
||||
];
|
||||
|
||||
#[component]
|
||||
pub(crate) fn TaskForm() -> Element {
|
||||
pub(crate) fn TaskForm(on_successful_submit: EventHandler<()>) -> Element {
|
||||
let projects = use_server_future(get_projects)?.unwrap().unwrap();
|
||||
|
||||
let route = use_route::<Route>();
|
||||
let mut selected_category = use_signal(|| match route {
|
||||
let selected_category = use_signal(|| match route {
|
||||
Route::CategorySomedayMaybePage => Category::SomedayMaybe,
|
||||
Route::CategoryWaitingForPage => Category::WaitingFor(String::new()),
|
||||
Route::CategoryNextStepsPage => Category::NextSteps,
|
||||
@ -49,7 +49,7 @@ pub(crate) fn TaskForm() -> Element {
|
||||
Route::CategoryLongTermPage => Category::LongTerm,
|
||||
_ => Category::Inbox,
|
||||
});
|
||||
let mut category_calendar_reoccurrence_interval = use_signal(|| None);
|
||||
let category_calendar_reoccurrence_interval = use_signal(|| None);
|
||||
let mut category_calendar_has_time = use_signal(|| false);
|
||||
let mut category_calendar_reminder_offset_index = use_signal(|| REMINDER_OFFSETS.len() - 1);
|
||||
|
||||
@ -101,6 +101,7 @@ pub(crate) fn TaskForm() -> Element {
|
||||
QueryKey::Tasks,
|
||||
QueryKey::TasksInCategory(selected_category())
|
||||
]);
|
||||
on_successful_submit.call(());
|
||||
}
|
||||
},
|
||||
class: "p-4 flex flex-col gap-4",
|
||||
@ -171,7 +172,7 @@ pub(crate) fn TaskForm() -> Element {
|
||||
}
|
||||
},
|
||||
CategoryInput {
|
||||
selected_category: selected_category.clone(),
|
||||
selected_category: selected_category,
|
||||
class: "grow"
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use validator::Validate;
|
||||
const TITLE_LENGTH_MIN: u64 = 1;
|
||||
const TITLE_LENGTH_MAX: u64 = 255;
|
||||
|
||||
#[derive(Queryable, Selectable, Serialize, Deserialize, Clone, Debug)]
|
||||
#[derive(Queryable, Selectable, Serialize, Deserialize, PartialEq, Clone, Debug)]
|
||||
#[diesel(table_name = crate::schema::projects)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Project {
|
||||
|
@ -1,9 +1,8 @@
|
||||
use std::cmp::Ordering;
|
||||
use crate::models::category::Category;
|
||||
use crate::schema::tasks;
|
||||
use diesel::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
use crate::models::category::Category;
|
||||
use crate::schema::tasks;
|
||||
|
||||
const TITLE_LENGTH_MIN: u64 = 1;
|
||||
const TITLE_LENGTH_MAX: u64 = 255;
|
||||
|
@ -1,13 +1,16 @@
|
||||
use crate::errors::error::Error;
|
||||
use crate::errors::error_vec::ErrorVec;
|
||||
use crate::models::category::Category;
|
||||
use crate::models::project::Project;
|
||||
use crate::models::task::Task;
|
||||
|
||||
pub(crate) mod tasks;
|
||||
pub(crate) mod projects;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub(crate) enum QueryValue {
|
||||
Tasks(Vec<Task>),
|
||||
Projects(Vec<Project>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -19,4 +22,5 @@ pub(crate) enum QueryErrors {
|
||||
pub(crate) enum QueryKey {
|
||||
Tasks,
|
||||
TasksInCategory(Category),
|
||||
Projects,
|
||||
}
|
||||
|
20
src/query/projects.rs
Normal file
20
src/query/projects.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
||||
use crate::server::projects::get_projects;
|
||||
use dioxus::prelude::ServerFnError;
|
||||
use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery};
|
||||
|
||||
pub(crate) fn use_projects_query() -> UseQuery<QueryValue, QueryErrors, QueryKey> {
|
||||
use_get_query([QueryKey::Projects, QueryKey::Tasks], fetch_projects)
|
||||
}
|
||||
|
||||
async fn fetch_projects(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryErrors> {
|
||||
if let Some(QueryKey::Projects) = keys.first() {
|
||||
match get_projects().await {
|
||||
Ok(projects) => Ok(QueryValue::Projects(projects)),
|
||||
Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)),
|
||||
Err(error) => panic!("Unexpected error: {:?}", error)
|
||||
}.into()
|
||||
} else {
|
||||
panic!("Unexpected query keys: {:?}", keys);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user