feat: ability to view tasks in different categories
This commit is contained in:
		| @@ -7,7 +7,7 @@ use dioxus::prelude::*; | |||||||
| pub(crate) fn App() -> Element { | pub(crate) fn App() -> Element { | ||||||
|     rsx! { |     rsx! { | ||||||
|         div { |         div { | ||||||
|             class: "min-h-screen text-white bg-neutral-800", |             class: "min-h-screen text-zinc-200 bg-zinc-800", | ||||||
|             Router::<Route> {} |             Router::<Route> {} | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								src/components/bottom_panel.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/components/bottom_panel.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | use std::thread::sleep; | ||||||
|  | use dioxus::prelude::*; | ||||||
|  | use crate::components::navigation::Navigation; | ||||||
|  | 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); | ||||||
|  |     let navigation_expanded = use_signal(|| false); | ||||||
|  |  | ||||||
|  |     use_effect(use_reactive(&creating_task, move |creating_task| { | ||||||
|  |         if creating_task { | ||||||
|  |             expanded.set(true); | ||||||
|  |         } else { | ||||||
|  |             spawn(async move { | ||||||
|  |                 async_std::task::sleep(std::time::Duration::from_millis(500)).await; | ||||||
|  |                 expanded.set(false); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     })); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         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]", | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             if expanded() { | ||||||
|  |                 TaskForm {} | ||||||
|  |             } else { | ||||||
|  |                 Navigation { | ||||||
|  |                     expanded: navigation_expanded, | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										104
									
								
								src/components/category_input.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/components/category_input.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | |||||||
|  | use crate::models::category::Category; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  | use chrono::NaiveDate; | ||||||
|  | use dioxus::core_macro::rsx; | ||||||
|  | use dioxus::dioxus_core::Element; | ||||||
|  | use dioxus::prelude::*; | ||||||
|  | use std::fmt::format; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategoryInput(selected_category: Signal<Category>, class: Option<&'static str>) -> Element { | ||||||
|  |     rsx! { | ||||||
|  |         div { | ||||||
|  |             class: format!("flex flex-row gap-2 {}", class.unwrap_or("")), | ||||||
|  |             button { | ||||||
|  |                 r#type: "button", | ||||||
|  |                 class: format!( | ||||||
|  |                     "py-2 rounded-lg grow basis-0 {}", | ||||||
|  |                     if selected_category() == Category::SomedayMaybe { "bg-zinc-500/50" } | ||||||
|  |                     else { "bg-zinc-800/50" } | ||||||
|  |                 ), | ||||||
|  |                 onclick: move |_| { | ||||||
|  |                     selected_category.set(Category::SomedayMaybe); | ||||||
|  |                 }, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-question" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             button { | ||||||
|  |                 r#type: "button", | ||||||
|  |                 class: format!( | ||||||
|  |                     "py-2 rounded-lg grow basis-0 {}", | ||||||
|  |                     if selected_category() == Category::LongTerm { "bg-zinc-500/50" } | ||||||
|  |                     else { "bg-zinc-800/50" } | ||||||
|  |                 ), | ||||||
|  |                 onclick: move |_| { | ||||||
|  |                     selected_category.set(Category::LongTerm); | ||||||
|  |                 }, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-water" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             button { | ||||||
|  |                 r#type: "button", | ||||||
|  |                 class: format!( | ||||||
|  |                     "py-2 rounded-lg grow basis-0 {}", | ||||||
|  |                     if let Category::WaitingFor(_) = selected_category() { "bg-zinc-500/50" } | ||||||
|  |                     else { "bg-zinc-800/50" } | ||||||
|  |                 ), | ||||||
|  |                 onclick: move |_| { | ||||||
|  |                     selected_category.set(Category::WaitingFor(String::new())); | ||||||
|  |                 }, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-hourglass-half" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             button { | ||||||
|  |                 r#type: "button", | ||||||
|  |                 class: format!( | ||||||
|  |                     "py-2 rounded-lg grow basis-0 {}", | ||||||
|  |                     if selected_category() == Category::NextSteps { "bg-zinc-500/50" } | ||||||
|  |                     else { "bg-zinc-800/50" } | ||||||
|  |                 ), | ||||||
|  |                 onclick: move |_| { | ||||||
|  |                     selected_category.set(Category::NextSteps); | ||||||
|  |                 }, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-forward" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             button { | ||||||
|  |                 r#type: "button", | ||||||
|  |                 class: format!( | ||||||
|  |                     "py-2 rounded-lg grow basis-0 {}", | ||||||
|  |                     if let Category::Calendar { .. } = selected_category() { "bg-zinc-500/50" } | ||||||
|  |                     else { "bg-zinc-800/50" } | ||||||
|  |                 ), | ||||||
|  |                 onclick: move |_| { | ||||||
|  |                     selected_category.set(Category::Calendar { | ||||||
|  |                         date: NaiveDate::default(), | ||||||
|  |                         reoccurrence: None, | ||||||
|  |                         time: None, | ||||||
|  |                     }); | ||||||
|  |                 }, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-calendar-days" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             button { | ||||||
|  |                 r#type: "button", | ||||||
|  |                 class: format!( | ||||||
|  |                     "py-2 rounded-lg grow basis-0 {}", | ||||||
|  |                     if selected_category() == Category::Inbox { "bg-zinc-500/50" } | ||||||
|  |                     else { "bg-zinc-800/50" } | ||||||
|  |                 ), | ||||||
|  |                 onclick: move |_| { | ||||||
|  |                     selected_category.set(Category::Inbox); | ||||||
|  |                 }, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-inbox" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								src/components/create_task_button.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/components/create_task_button.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | use dioxus::prelude::*; | ||||||
|  | use crate::components::task_list::TaskList; | ||||||
|  | use crate::models::category::Category; | ||||||
|  | use crate::route::Route; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CreateTaskButton(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" }), | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,13 +1,9 @@ | |||||||
| use crate::components::project_form::ProjectForm; |  | ||||||
| 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::task_form::TaskForm; |  | ||||||
|  |  | ||||||
| #[component] | #[component] | ||||||
| pub(crate) fn Home() -> Element { | pub(crate) fn Home() -> Element { | ||||||
|     rsx! { |     rsx! { | ||||||
|         ProjectForm {} |  | ||||||
|         TaskForm {} |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								src/components/layout.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/components/layout.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | 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::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); | ||||||
|  |      | ||||||
|  |     rsx! { | ||||||
|  |         Outlet::<Route> {} | ||||||
|  |         StickyBottom { | ||||||
|  |             CreateTaskButton { | ||||||
|  |                 creating: creating_task, | ||||||
|  |             } | ||||||
|  |             BottomPanel { | ||||||
|  |                 creating_task: creating_task(), | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -2,3 +2,13 @@ pub(crate) mod app; | |||||||
| pub(crate) mod home; | pub(crate) mod home; | ||||||
| pub(crate) mod project_form; | pub(crate) mod project_form; | ||||||
| pub(crate) mod task_form; | pub(crate) mod task_form; | ||||||
|  | pub(crate) mod task_list; | ||||||
|  | pub(crate) mod pages; | ||||||
|  | pub(crate) mod navigation; | ||||||
|  | pub(crate) mod create_task_button; | ||||||
|  | pub(crate) mod bottom_panel; | ||||||
|  | pub(crate) mod sticky_bottom; | ||||||
|  | pub(crate) mod category_input; | ||||||
|  | pub(crate) mod reoccurrence_input; | ||||||
|  | pub(crate) mod layout; | ||||||
|  | mod navigation_item; | ||||||
|   | |||||||
							
								
								
									
										83
									
								
								src/components/navigation.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/components/navigation.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | use crate::components::navigation_item::NavigationItem; | ||||||
|  | use crate::components::task_list::TaskList; | ||||||
|  | use crate::models::category::Category; | ||||||
|  | use crate::route::Route; | ||||||
|  | use dioxus::prelude::*; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn Navigation(expanded: Signal<bool>) -> Element { | ||||||
|  |     rsx! { | ||||||
|  |         div { | ||||||
|  |             class: "grid grid-cols-5 justify-stretch", | ||||||
|  |             button { | ||||||
|  |                 class: format!( | ||||||
|  |                     "py-4 text-center text-2xl {}", | ||||||
|  |                     if expanded() { "text-zinc-200" } | ||||||
|  |                     else { "text-zinc-500" } | ||||||
|  |                 ), | ||||||
|  |                 onclick: move |_| expanded.set(!expanded()), | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-bars" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             NavigationItem { | ||||||
|  |                 route: Route::CategoryNextStepsPage, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-forward" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             NavigationItem { | ||||||
|  |                 route: Route::CategoryCalendarPage, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-calendar-days" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             NavigationItem { | ||||||
|  |                 route: Route::CategoryTodayPage, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-calendar-day" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             NavigationItem { | ||||||
|  |                 route: Route::CategoryInboxPage, | ||||||
|  |                 i { | ||||||
|  |                     class: "fa-solid fa-inbox" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             {if expanded() { | ||||||
|  |                 rsx! { | ||||||
|  |                     NavigationItem { | ||||||
|  |                         route: Route::ProjectsPage, | ||||||
|  |                         i { | ||||||
|  |                             class: "fa-solid fa-list" | ||||||
|  |                         } | ||||||
|  |                     }, | ||||||
|  |                     NavigationItem { | ||||||
|  |                         route: Route::CategoryTrashPage, | ||||||
|  |                         i { | ||||||
|  |                             class: "fa-solid fa-trash-can" | ||||||
|  |                         } | ||||||
|  |                     }, | ||||||
|  |                     NavigationItem { | ||||||
|  |                         route: Route::CategoryDonePage, | ||||||
|  |                         i { | ||||||
|  |                             class: "fa-solid fa-check" | ||||||
|  |                         } | ||||||
|  |                     }, | ||||||
|  |                     NavigationItem { | ||||||
|  |                         route: Route::CategoryLongTermPage, | ||||||
|  |                         i { | ||||||
|  |                             class: "fa-solid fa-water" | ||||||
|  |                         } | ||||||
|  |                     }, | ||||||
|  |                     NavigationItem { | ||||||
|  |                         route: Route::CategoryWaitingForPage, | ||||||
|  |                         i { | ||||||
|  |                             class: "fa-solid fa-hourglass-half" | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { None }} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								src/components/navigation_item.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/components/navigation_item.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | use dioxus::prelude::*; | ||||||
|  | use crate::components::task_list::TaskList; | ||||||
|  | use crate::models::category::Category; | ||||||
|  | use crate::route::Route; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn NavigationItem(route: Route, children: Element) -> Element { | ||||||
|  |     let current_route = use_route::<Route>(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         Link { | ||||||
|  |             to: route.clone(), | ||||||
|  |             class: format!( | ||||||
|  |                 "py-4 text-center text-2xl {}", | ||||||
|  |                 if current_route == route { "text-zinc-200" } | ||||||
|  |                 else { "text-zinc-500" } | ||||||
|  |             ), | ||||||
|  |             children | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								src/components/pages/category_calendar_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/components/pages/category_calendar_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | 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::sticky_bottom::StickyBottom; | ||||||
|  | use crate::components::task_form::TaskForm; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategoryCalendarPage() -> Element { | ||||||
|  |     let tasks = use_server_future( | ||||||
|  |         move || get_tasks_in_category(Category::Calendar { | ||||||
|  |             date: NaiveDate::default(), | ||||||
|  |             reoccurrence: None, | ||||||
|  |             time: None, | ||||||
|  |         }) | ||||||
|  |     )?.unwrap().unwrap(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         TaskList { | ||||||
|  |             tasks: tasks, | ||||||
|  |             class: "pb-36" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/components/pages/category_done_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/components/pages/category_done_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | 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::sticky_bottom::StickyBottom; | ||||||
|  | use crate::components::task_form::TaskForm; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategoryDonePage() -> Element { | ||||||
|  |     let tasks = use_server_future( | ||||||
|  |         move || get_tasks_in_category(Category::Done) | ||||||
|  |     )?.unwrap().unwrap(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         TaskList { | ||||||
|  |             tasks: tasks, | ||||||
|  |             class: "pb-36" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/components/pages/category_inbox_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/components/pages/category_inbox_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | 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::sticky_bottom::StickyBottom; | ||||||
|  | use crate::components::task_form::TaskForm; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategoryInboxPage() -> Element { | ||||||
|  |     let tasks = use_server_future( | ||||||
|  |         move || get_tasks_in_category(Category::Inbox) | ||||||
|  |     )?.unwrap().unwrap(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         TaskList { | ||||||
|  |             tasks: tasks, | ||||||
|  |             class: "pb-36" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/components/pages/category_long_term_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/components/pages/category_long_term_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | 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::sticky_bottom::StickyBottom; | ||||||
|  | use crate::components::task_form::TaskForm; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategoryLongTermPage() -> Element { | ||||||
|  |     let tasks = use_server_future( | ||||||
|  |         move || get_tasks_in_category(Category::LongTerm) | ||||||
|  |     )?.unwrap().unwrap(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         TaskList { | ||||||
|  |             tasks: tasks, | ||||||
|  |             class: "pb-36" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/components/pages/category_next_steps_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/components/pages/category_next_steps_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | 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::sticky_bottom::StickyBottom; | ||||||
|  | use crate::components::task_form::TaskForm; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategoryNextStepsPage() -> Element { | ||||||
|  |     let tasks = use_server_future( | ||||||
|  |         move || get_tasks_in_category(Category::NextSteps) | ||||||
|  |     )?.unwrap().unwrap(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         TaskList { | ||||||
|  |             tasks: tasks, | ||||||
|  |             class: "pb-36" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/components/pages/category_someday_maybe_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/components/pages/category_someday_maybe_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | 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::sticky_bottom::StickyBottom; | ||||||
|  | use crate::components::task_form::TaskForm; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategorySomedayMaybePage() -> Element { | ||||||
|  |     let tasks = use_server_future( | ||||||
|  |         move || get_tasks_in_category(Category::SomedayMaybe) | ||||||
|  |     )?.unwrap().unwrap(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         TaskList { | ||||||
|  |             tasks: tasks, | ||||||
|  |             class: "pb-36" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								src/components/pages/category_today_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/components/pages/category_today_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | use crate::components::bottom_panel::BottomPanel; | ||||||
|  | use crate::components::create_task_button::CreateTaskButton; | ||||||
|  | use crate::components::navigation::Navigation; | ||||||
|  | use crate::components::sticky_bottom::StickyBottom; | ||||||
|  | use crate::components::task_form::TaskForm; | ||||||
|  | use crate::components::task_list::TaskList; | ||||||
|  | use crate::models::category::Category; | ||||||
|  | use crate::models::task::Task; | ||||||
|  | use crate::route::Route; | ||||||
|  | use crate::schema::tasks::category; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  | use chrono::{Local, NaiveDate}; | ||||||
|  | use dioxus::core_macro::rsx; | ||||||
|  | use dioxus::dioxus_core::Element; | ||||||
|  | use dioxus::prelude::*; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategoryTodayPage() -> Element { | ||||||
|  |     let tasks = use_server_future( | ||||||
|  |         move || get_tasks_in_category(Category::Calendar { | ||||||
|  |             date: NaiveDate::default(), | ||||||
|  |             reoccurrence: None, | ||||||
|  |             time: None, | ||||||
|  |         }) | ||||||
|  |     )?.unwrap().unwrap().iter().filter(|task| { | ||||||
|  |         if let Category::Calendar { date, .. } = task.category() { | ||||||
|  |             *date == Local::now().date_naive() | ||||||
|  |         } else { | ||||||
|  |             panic!("Unexpected category."); | ||||||
|  |         } | ||||||
|  |     }).cloned().collect::<Vec<Task>>(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         TaskList { | ||||||
|  |             tasks: tasks, | ||||||
|  |             class: "pb-36" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/components/pages/category_trash_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/components/pages/category_trash_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | 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::sticky_bottom::StickyBottom; | ||||||
|  | use crate::components::task_form::TaskForm; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategoryTrashPage() -> Element { | ||||||
|  |     let tasks = use_server_future( | ||||||
|  |         move || get_tasks_in_category(Category::Trash) | ||||||
|  |     )?.unwrap().unwrap(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         TaskList { | ||||||
|  |             tasks: tasks, | ||||||
|  |             class: "pb-36" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/components/pages/category_waiting_for_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/components/pages/category_waiting_for_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | 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::sticky_bottom::StickyBottom; | ||||||
|  | use crate::components::task_form::TaskForm; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn CategoryWaitingForPage() -> Element { | ||||||
|  |     let tasks = use_server_future( | ||||||
|  |         move || get_tasks_in_category(Category::WaitingFor(String::new())) | ||||||
|  |     )?.unwrap().unwrap(); | ||||||
|  |  | ||||||
|  |     rsx! { | ||||||
|  |         TaskList { | ||||||
|  |             tasks: tasks, | ||||||
|  |             class: "pb-36" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								src/components/pages/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/components/pages/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | pub(crate) mod category_inbox_page; | ||||||
|  | pub(crate) mod category_calendar_page; | ||||||
|  | pub(crate) mod category_today_page; | ||||||
|  | pub(crate) mod category_waiting_for_page; | ||||||
|  | pub(crate) mod category_long_term_page; | ||||||
|  | pub(crate) mod category_next_steps_page; | ||||||
|  | pub(crate) mod category_someday_maybe_page; | ||||||
|  | pub(crate) mod category_done_page; | ||||||
|  | pub(crate) mod category_trash_page; | ||||||
|  | pub(crate) mod not_found_page; | ||||||
|  | pub(crate) mod projects_page; | ||||||
							
								
								
									
										11
									
								
								src/components/pages/not_found_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/components/pages/not_found_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | use dioxus::prelude::*; | ||||||
|  | use crate::components::task_list::TaskList; | ||||||
|  | use crate::models::category::Category; | ||||||
|  | use crate::route::Route; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn NotFoundPage(route: Vec<String>) -> Element { | ||||||
|  |     rsx! { | ||||||
|  |         {"404"} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								src/components/pages/projects_page.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/components/pages/projects_page.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | use dioxus::prelude::*; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn ProjectsPage() -> Element { | ||||||
|  |     rsx! { | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								src/components/reoccurrence_input.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/components/reoccurrence_input.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | use crate::models::category::{Category, Reoccurrence, ReoccurrenceInterval}; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  | use chrono::NaiveDate; | ||||||
|  | use dioxus::core_macro::rsx; | ||||||
|  | use dioxus::dioxus_core::Element; | ||||||
|  | use dioxus::prelude::*; | ||||||
|  | use std::fmt::format; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn ReoccurrenceIntervalInput( | ||||||
|  |     reoccurrence_interval: Signal<Option<ReoccurrenceInterval>>, | ||||||
|  |     class_buttons: Option<&'static str> | ||||||
|  | ) -> Element { | ||||||
|  |     rsx! { | ||||||
|  |         button { | ||||||
|  |             r#type: "button", | ||||||
|  |             class: format!( | ||||||
|  |                 "py-2 rounded-lg {} {}", | ||||||
|  |                 class_buttons.unwrap_or(""), | ||||||
|  |                 if reoccurrence_interval().is_none() { "bg-zinc-500/50" } | ||||||
|  |                 else { "bg-zinc-800/50" } | ||||||
|  |             ), | ||||||
|  |             onclick: move |_| { | ||||||
|  |                 reoccurrence_interval.set(None); | ||||||
|  |             }, | ||||||
|  |             i { | ||||||
|  |                 class: "fa-solid fa-ban" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         button { | ||||||
|  |             r#type: "button", | ||||||
|  |             class: format!( | ||||||
|  |                 "py-2 rounded-lg {} {}", | ||||||
|  |                 class_buttons.unwrap_or(""), | ||||||
|  |                 if let Some(ReoccurrenceInterval::Day) = reoccurrence_interval() | ||||||
|  |                 { "bg-zinc-500/50" } | ||||||
|  |                 else { "bg-zinc-800/50" } | ||||||
|  |             ), | ||||||
|  |             onclick: move |_| { | ||||||
|  |                 reoccurrence_interval.set(Some(ReoccurrenceInterval::Day)) | ||||||
|  |             }, | ||||||
|  |             i { | ||||||
|  |                 class: "fa-solid fa-sun" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         button { | ||||||
|  |             r#type: "button", | ||||||
|  |             class: format!( | ||||||
|  |                 "py-2 rounded-lg {} {}", | ||||||
|  |                 class_buttons.unwrap_or(""), | ||||||
|  |                 if let Some(ReoccurrenceInterval::Month) = reoccurrence_interval() | ||||||
|  |                 { "bg-zinc-500/50" } | ||||||
|  |                 else { "bg-zinc-800/50" } | ||||||
|  |             ), | ||||||
|  |             onclick: move |_| { | ||||||
|  |                 reoccurrence_interval.set(Some(ReoccurrenceInterval::Month)) | ||||||
|  |             }, | ||||||
|  |             i { | ||||||
|  |                 class: "fa-solid fa-moon" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         button { | ||||||
|  |             r#type: "button", | ||||||
|  |             class: format!( | ||||||
|  |                 "py-2 rounded-lg {} {}", | ||||||
|  |                 class_buttons.unwrap_or(""), | ||||||
|  |                 if let Some(ReoccurrenceInterval::Year) = reoccurrence_interval() | ||||||
|  |                 { "bg-zinc-500/50" } | ||||||
|  |                 else { "bg-zinc-800/50" } | ||||||
|  |             ), | ||||||
|  |             onclick: move |_| { | ||||||
|  |                 reoccurrence_interval.set(Some(ReoccurrenceInterval::Year)) | ||||||
|  |             }, | ||||||
|  |             i { | ||||||
|  |                 class: "fa-solid fa-earth-europe" | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								src/components/sticky_bottom.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/components/sticky_bottom.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | use dioxus::prelude::*; | ||||||
|  | use crate::components::task_list::TaskList; | ||||||
|  | use crate::models::category::Category; | ||||||
|  | use crate::route::Route; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn StickyBottom(children: Element) -> Element { | ||||||
|  |     rsx! { | ||||||
|  |         div { | ||||||
|  |             class: "fixed bottom-0 left-0 right-0 flex flex-col", | ||||||
|  |             {children} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,45 +1,65 @@ | |||||||
| use chrono::Duration; | use std::fmt::Display; | ||||||
| use crate::models::category::{CalendarTime, Category}; | use crate::components::category_input::CategoryInput; | ||||||
|  | use crate::components::reoccurrence_input::ReoccurrenceIntervalInput; | ||||||
|  | use crate::models::category::{CalendarTime, Category, Reoccurrence, ReoccurrenceInterval}; | ||||||
| use crate::models::task::NewTask; | use crate::models::task::NewTask; | ||||||
| use crate::server::projects::get_projects; | use crate::server::projects::get_projects; | ||||||
| use crate::server::tasks::create_task; | use crate::server::tasks::create_task; | ||||||
|  | use chrono::{Duration, NaiveDate}; | ||||||
| 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::*; | ||||||
|  | use crate::route::Route; | ||||||
|  |  | ||||||
|  | const REMINDER_OFFSETS: [Option<Duration>; 17] = [ | ||||||
|  |     None, | ||||||
|  |     Some(Duration::days(1)), | ||||||
|  |     Some(Duration::hours(12)), | ||||||
|  |     Some(Duration::hours(11)), | ||||||
|  |     Some(Duration::hours(10)), | ||||||
|  |     Some(Duration::hours(9)), | ||||||
|  |     Some(Duration::hours(8)), | ||||||
|  |     Some(Duration::hours(7)), | ||||||
|  |     Some(Duration::hours(6)), | ||||||
|  |     Some(Duration::hours(5)), | ||||||
|  |     Some(Duration::hours(4)), | ||||||
|  |     Some(Duration::hours(3)), | ||||||
|  |     Some(Duration::hours(2)), | ||||||
|  |     Some(Duration::hours(1)), | ||||||
|  |     Some(Duration::minutes(30)), | ||||||
|  |     Some(Duration::minutes(10)), | ||||||
|  |     Some(Duration::zero()), | ||||||
|  | ]; | ||||||
|  |  | ||||||
| #[component] | #[component] | ||||||
| pub(crate) fn TaskForm() -> Element { | pub(crate) fn TaskForm() -> Element { | ||||||
|     let categories = vec![ |  | ||||||
|         Category::Inbox, |  | ||||||
|         Category::SomedayMaybe, |  | ||||||
|         Category::WaitingFor(String::new()), |  | ||||||
|         Category::NextSteps, |  | ||||||
|         Category::Calendar { |  | ||||||
|             date: chrono::Local::now().date_naive(), |  | ||||||
|             reoccurance_interval: None, |  | ||||||
|             time: None, |  | ||||||
|         }, |  | ||||||
|         Category::LongTerm, |  | ||||||
|     ]; |  | ||||||
|     let projects = use_server_future(get_projects)?.unwrap().unwrap(); |     let projects = use_server_future(get_projects)?.unwrap().unwrap(); | ||||||
|  |  | ||||||
|     let mut selected_category_index = use_signal::<usize>(|| 0); |     let route = use_route::<Route>(); | ||||||
|     let mut category_calendar_is_reoccurring = use_signal::<bool>(|| false); |     let mut selected_category = use_signal(|| match route {  | ||||||
|     let mut category_calendar_has_time = use_signal::<bool>(|| false); |         Route::CategorySomedayMaybePage => Category::SomedayMaybe, | ||||||
|     let mut category_calendar_has_reminder = use_signal::<bool>(|| false); |         Route::CategoryWaitingForPage => Category::WaitingFor(String::new()), | ||||||
|  |         Route::CategoryNextStepsPage => Category::NextSteps, | ||||||
|  |         Route::CategoryCalendarPage | Route::CategoryTodayPage => Category::Calendar { | ||||||
|  |             date: NaiveDate::default(), | ||||||
|  |             reoccurrence: None, | ||||||
|  |             time: None, | ||||||
|  |         }, | ||||||
|  |         Route::CategoryLongTermPage => Category::LongTerm, | ||||||
|  |         _ => Category::Inbox, | ||||||
|  |     }); | ||||||
|  |     let mut 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); | ||||||
|  |  | ||||||
|     rsx! { |     rsx! { | ||||||
|         form { |         form { | ||||||
|             onsubmit: move |event| { |             onsubmit: move |event| { | ||||||
|                 let categories = categories.clone(); |  | ||||||
|                 async move { |                 async move { | ||||||
|                     let new_task = NewTask::new( |                     let new_task = NewTask::new( | ||||||
|                         event.values().get("title").unwrap().as_value(), |                         event.values().get("title").unwrap().as_value(), | ||||||
|                         event.values().get("deadline").unwrap().as_value().parse().ok(), |                         event.values().get("deadline").unwrap().as_value().parse().ok(), | ||||||
|                         match &categories[ |                         match &selected_category() { | ||||||
|                             event.values().get("category_index").unwrap() |  | ||||||
|                             .as_value().parse::<usize>().unwrap() |  | ||||||
|                         ] { |  | ||||||
|                             Category::WaitingFor(_) => Category::WaitingFor( |                             Category::WaitingFor(_) => Category::WaitingFor( | ||||||
|                                 event.values().get("category_waiting_for").unwrap() |                                 event.values().get("category_waiting_for").unwrap() | ||||||
|                                 .as_value() |                                 .as_value() | ||||||
| @@ -47,24 +67,24 @@ pub(crate) fn TaskForm() -> Element { | |||||||
|                             Category::Calendar { .. } => Category::Calendar { |                             Category::Calendar { .. } => Category::Calendar { | ||||||
|                                 date: event.values().get("category_calendar_date").unwrap() |                                 date: event.values().get("category_calendar_date").unwrap() | ||||||
|                                 .as_value().parse().unwrap(), |                                 .as_value().parse().unwrap(), | ||||||
|                                 reoccurance_interval: |                                 reoccurrence: category_calendar_reoccurrence_interval().map( | ||||||
|                                 event.values().get("category_calendar_is_reoccurring").map( |                                     |reoccurrence_interval| Reoccurrence::new( | ||||||
|                                     |_| Duration::days( |                                         event.values().get("category_calendar_date").unwrap() | ||||||
|                                         event.values().get("category_calendar_reoccurance_interval") |                                         .as_value().parse().unwrap(), | ||||||
|  |                                         reoccurrence_interval, | ||||||
|  |                                         event.values().get("category_calendar_reoccurrence_length") | ||||||
|                                         .unwrap().as_value().parse().unwrap() |                                         .unwrap().as_value().parse().unwrap() | ||||||
|                                     ) |                                     ) | ||||||
|                                 ), |                                 ), | ||||||
|                                 time: event.values().get("category_calendar_time").unwrap() |                                 time: event.values().get("category_calendar_time").unwrap() | ||||||
|                                     .as_value().parse().ok().map(|time| |                                 .as_value().parse().ok().map(|time| | ||||||
|                                     CalendarTime::new( |                                     CalendarTime::new( | ||||||
|                                         time, |                                         time, | ||||||
|                                         event.values().get("category_calendar_has_reminder").map( |                                         REMINDER_OFFSETS[ | ||||||
|                                             |_| Duration::minutes( |                                             event.values() | ||||||
|                                                 event.values() |                                             .get("category_calendar_reminder_offset_index").unwrap() | ||||||
|                                                 .get("category_calendar_reminder_offset").unwrap() |                                             .as_value().parse::<usize>().unwrap() | ||||||
|                                                 .as_value().parse().unwrap() |                                         ] | ||||||
|                                             ) |  | ||||||
|                                         ) |  | ||||||
|                                     ) |                                     ) | ||||||
|                                 ) |                                 ) | ||||||
|                             }, |                             }, | ||||||
| @@ -77,145 +97,207 @@ pub(crate) fn TaskForm() -> Element { | |||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             class: "p-4 flex flex-col gap-4", |             class: "p-4 flex flex-col gap-4", | ||||||
|             input { |             div { | ||||||
|                 r#type: "text", |                 class: "flex flex-row items-center gap-3", | ||||||
|                 name: "title", |                 label { | ||||||
|                 required: true, |                     r#for: "input_title", | ||||||
|                 placeholder: "title", |                     class: "min-w-6 text-center", | ||||||
|                 class: "p-2 bg-neutral-700 rounded", |                     i { | ||||||
|             }, |                         class: "fa-solid fa-pen-clip text-zinc-400/50" | ||||||
|             select { |  | ||||||
|                 name: "category_index", |  | ||||||
|                 oninput: move |event| { |  | ||||||
|                     selected_category_index.set(event.value().parse().unwrap()); |  | ||||||
|                 }, |  | ||||||
|                 class: "p-2 bg-neutral-700 rounded", |  | ||||||
|                 option { |  | ||||||
|                     value: 0, |  | ||||||
|                     "inbox" |  | ||||||
|                 }, |  | ||||||
|                 option { |  | ||||||
|                     value: 1, |  | ||||||
|                     "someday maybe" |  | ||||||
|                 }, |  | ||||||
|                 option { |  | ||||||
|                     value: 2, |  | ||||||
|                     "waiting for" |  | ||||||
|                 }, |  | ||||||
|                 option { |  | ||||||
|                     value: 3, |  | ||||||
|                     "next steps" |  | ||||||
|                 }, |  | ||||||
|                 option { |  | ||||||
|                     value: 4, |  | ||||||
|                     "calendar" |  | ||||||
|                 }, |  | ||||||
|                 option { |  | ||||||
|                     value: 5, |  | ||||||
|                     "long term" |  | ||||||
|                 }, |  | ||||||
|             }, |  | ||||||
|             match categories[selected_category_index()] { |  | ||||||
|                 Category::WaitingFor(_) => rsx !{ |  | ||||||
|                     input { |  | ||||||
|                         r#type: "text", |  | ||||||
|                         name: "category_waiting_for", |  | ||||||
|                         required: true, |  | ||||||
|                         class: "p-2 bg-neutral-700 rounded", |  | ||||||
|                     }, |                     }, | ||||||
|                 }, |                 }, | ||||||
|                 Category::Calendar { .. } => rsx !{ |                 input { | ||||||
|                     input { |                     r#type: "text", | ||||||
|                         r#type: "date", |                     name: "title", | ||||||
|                         name: "category_calendar_date", |                     required: true, | ||||||
|                         required: true, |                     class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg", | ||||||
|                         class: "p-2 bg-neutral-700 rounded", |                     id: "input_title" | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|  |             div { | ||||||
|  |                 class: "flex flex-row items-center gap-3", | ||||||
|  |                 label { | ||||||
|  |                     r#for: "input_project", | ||||||
|  |                     class: "min-w-6 text-center", | ||||||
|  |                     i { | ||||||
|  |                         class: "fa-solid fa-list text-zinc-400/50" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 select { | ||||||
|  |                     name: "project_id", | ||||||
|  |                     class: "px-3.5 py-2.5 bg-zinc-800/50 rounded-lg grow", | ||||||
|  |                     id: "input_project", | ||||||
|  |                     option { | ||||||
|  |                         value: 0, | ||||||
|  |                         "None" | ||||||
|                     }, |                     }, | ||||||
|  |                     for project in projects { | ||||||
|  |                         option { | ||||||
|  |                             value: project.id().to_string(), | ||||||
|  |                             {project.title()} | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|  |             div { | ||||||
|  |                 class: "flex flex-row items-center gap-3", | ||||||
|  |                 label { | ||||||
|  |                     r#for: "input_deadline", | ||||||
|  |                     class: "min-w-6 text-center", | ||||||
|  |                     i { | ||||||
|  |                         class: "fa-solid fa-bomb text-zinc-400/50" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 input { | ||||||
|  |                     r#type: "date", | ||||||
|  |                     name: "deadline", | ||||||
|  |                     class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow basis-0", | ||||||
|  |                     id: "input_deadline" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             div { | ||||||
|  |                 class: "flex flex-row items-center gap-3", | ||||||
|  |                 label { | ||||||
|  |                     class: "min-w-6 text-center", | ||||||
|  |                     i { | ||||||
|  |                         class: "fa-solid fa-layer-group text-zinc-400/50" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 CategoryInput { | ||||||
|  |                     selected_category: selected_category.clone(), | ||||||
|  |                     class: "grow" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             match selected_category() { | ||||||
|  |                 Category::WaitingFor(_) => rsx! { | ||||||
|                     div { |                     div { | ||||||
|                         input { |                         class: "flex flex-row items-center gap-3", | ||||||
|                             r#type: "checkbox", |                         label { | ||||||
|                             name: "category_calendar_is_reoccurring", |                             r#for: "input_deadline", | ||||||
|                             id: "category_calendar_is_reoccurring", |                             class: "min-w-6 text-center", | ||||||
|                             onchange: move |event| { |                             i { | ||||||
|                                 category_calendar_is_reoccurring.set(event.checked()); |                                 class: "fa-solid fa-hourglass-end text-zinc-400/50" | ||||||
|                             } |                             } | ||||||
|                         }, |                         }, | ||||||
|                         label { |  | ||||||
|                             r#for: "category_calendar_is_reoccurring", |  | ||||||
|                             " is reoccurring" |  | ||||||
|                         } |  | ||||||
|                     }, |  | ||||||
|                     if category_calendar_is_reoccurring() { |  | ||||||
|                         input { |                         input { | ||||||
|                             r#type: "number", |                             r#type: "text", | ||||||
|                             name: "category_calendar_reoccurance_interval", |                             name: "category_waiting_for", | ||||||
|                             required: true, |                             required: true, | ||||||
|                             min: 1, |                             class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow", | ||||||
|                             placeholder: "reoccurance interval (days)", |                             id: "input_category_waiting_for" | ||||||
|                             class: "p-2 bg-neutral-700 rounded", |                         }, | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 Category::Calendar { .. } => rsx! { | ||||||
|  |                     div { | ||||||
|  |                         class: "flex flex-row items-center gap-3", | ||||||
|  |                         label { | ||||||
|  |                             r#for: "input_category_calendar_date", | ||||||
|  |                             class: "min-w-6 text-center", | ||||||
|  |                             i { | ||||||
|  |                                 class: "fa-solid fa-clock text-zinc-400/50" | ||||||
|  |                             } | ||||||
|  |                         }, | ||||||
|  |                         div { | ||||||
|  |                             class: "grow flex flex-row gap-2", | ||||||
|  |                             input { | ||||||
|  |                                 r#type: "date", | ||||||
|  |                                 name: "category_calendar_date", | ||||||
|  |                                 required: true, | ||||||
|  |                                 initial_value: chrono::Local::now().format("%Y-%m-%d").to_string(), | ||||||
|  |                                 class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow", | ||||||
|  |                                 id: "input_category_calendar_date" | ||||||
|  |                             }, | ||||||
|  |                             input { | ||||||
|  |                                 r#type: "time", | ||||||
|  |                                 name: "category_calendar_time", | ||||||
|  |                                 class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow", | ||||||
|  |                                 id: "input_category_calendar_time", | ||||||
|  |                                 oninput: move |event| { | ||||||
|  |                                     category_calendar_has_time.set(!event.value().is_empty()); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
|                     }, |                     }, | ||||||
|                     input { |                     div { | ||||||
|                         r#type: "time", |                         class: "flex flex-row items-center gap-3", | ||||||
|                         name: "category_calendar_time", |                         label { | ||||||
|                         class: "p-2 bg-neutral-700 rounded", |                             r#for: "category_calendar_reoccurrence_length", | ||||||
|                         oninput: move |event| { |                             class: "min-w-6 text-center", | ||||||
|                             category_calendar_has_time.set(!event.value().is_empty()); |                             i { | ||||||
|  |                                 class: "fa-solid fa-repeat text-zinc-400/50" | ||||||
|  |                             } | ||||||
|  |                         }, | ||||||
|  |                         div { | ||||||
|  |                             class: "grow grid grid-cols-6 gap-2", | ||||||
|  |                             ReoccurrenceIntervalInput { | ||||||
|  |                                 reoccurrence_interval: category_calendar_reoccurrence_interval | ||||||
|  |                             }, | ||||||
|  |                             input { | ||||||
|  |                                 r#type: "number", | ||||||
|  |                                 inputmode: "numeric", | ||||||
|  |                                 name: "category_calendar_reoccurrence_length", | ||||||
|  |                                 disabled: category_calendar_reoccurrence_interval().is_none(), | ||||||
|  |                                 required: true, | ||||||
|  |                                 min: 1, | ||||||
|  |                                 initial_value: | ||||||
|  |                                 if category_calendar_reoccurrence_interval().is_none() { "" } | ||||||
|  |                                 else { "1" }, | ||||||
|  |                                 class: "py-2 px-3 bg-zinc-800/50 rounded-lg col-span-2 text-right", | ||||||
|  |                                 id: "category_calendar_reoccurrence_length" | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
|                     }, |                     }, | ||||||
|                     if category_calendar_has_time() { |                     if category_calendar_has_time() { | ||||||
|                         div { |                         div { | ||||||
|  |                             class: "flex flex-row items-center gap-3", | ||||||
|  |                             label { | ||||||
|  |                                 r#for: "category_calendar_reminder_offset_index", | ||||||
|  |                                 class: "min-w-6 text-center", | ||||||
|  |                                 i { | ||||||
|  |                                     class: "fa-solid fa-bell text-zinc-400/50" | ||||||
|  |                                 } | ||||||
|  |                             }, | ||||||
|                             input { |                             input { | ||||||
|                                 r#type: "checkbox", |                                 r#type: "range", | ||||||
|                                 name: "category_calendar_has_reminder", |                                 name: "category_calendar_reminder_offset_index", | ||||||
|                                 value: 0, |                                 min: 0, | ||||||
|  |                                 max: REMINDER_OFFSETS.len() as i64 - 1, | ||||||
|  |                                 initial_value: REMINDER_OFFSETS.len() as i64 - 1, | ||||||
|  |                                 class: "grow input-range-reverse", | ||||||
|                                 id: "category_calendar_has_reminder", |                                 id: "category_calendar_has_reminder", | ||||||
|                                 onchange: move |event| { |                                 oninput: move |event| { | ||||||
|                                     category_calendar_has_reminder.set(event.checked()); |                                     category_calendar_reminder_offset_index.set( | ||||||
|  |                                         event.value().parse().unwrap() | ||||||
|  |                                     ); | ||||||
|                                 } |                                 } | ||||||
|                             }, |                             }, | ||||||
|                             label { |                             label { | ||||||
|                                 r#for: "category_calendar_has_reminder", |                                 r#for: "category_calendar_reminder_offset_index", | ||||||
|                                 " set a reminder" |                                 class: "pr-3 min-w-16 text-right", | ||||||
|  |                                 {REMINDER_OFFSETS[category_calendar_reminder_offset_index()].map( | ||||||
|  |                                     |offset| if offset.num_hours() < 1 { | ||||||
|  |                                         format!("{} min", offset.num_minutes()) | ||||||
|  |                                     } else { | ||||||
|  |                                         format!("{} h", offset.num_hours()) | ||||||
|  |                                     } | ||||||
|  |                                 ).unwrap_or_else(|| "none".to_string())} | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     if category_calendar_has_reminder() { |  | ||||||
|                         input { |  | ||||||
|                             r#type: "number", |  | ||||||
|                             name: "category_calendar_reminder_offset", |  | ||||||
|                             required: true, |  | ||||||
|                             min: 0, |  | ||||||
|                             placeholder: "reminder offset (minutes)", |  | ||||||
|                             class: "p-2 bg-neutral-700 rounded", |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 }, |                 }, | ||||||
|                 _ => None |                 _ => None | ||||||
|             }, |             }, | ||||||
|             input { |             div { | ||||||
|                 r#type: "date", |                 class: "flex flex-row justify-end mt-auto", | ||||||
|                 name: "deadline", |                 button { | ||||||
|                 class: "p-2 bg-neutral-700 rounded", |                     r#type: "submit", | ||||||
|             }, |                     class: "py-2 px-4 bg-zinc-300/50 rounded-lg", | ||||||
|             select { |                     i { | ||||||
|                 name: "project_id", |                         class: "fa-solid fa-floppy-disk" | ||||||
|                 class: "p-2 bg-neutral-700 rounded", |  | ||||||
|                 option { |  | ||||||
|                     value: 0, |  | ||||||
|                     "none" |  | ||||||
|                 }, |  | ||||||
|                 for project in projects { |  | ||||||
|                     option { |  | ||||||
|                         value: project.id().to_string(), |  | ||||||
|                         {project.title()} |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }, |  | ||||||
|             button { |  | ||||||
|                 r#type: "submit", |  | ||||||
|                 "create" |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										66
									
								
								src/components/task_list.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/components/task_list.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | use crate::models::category::Category; | ||||||
|  | use crate::models::task::Task; | ||||||
|  | use crate::server::tasks::get_tasks_in_category; | ||||||
|  | use dioxus::core_macro::rsx; | ||||||
|  | use dioxus::dioxus_core::Element; | ||||||
|  | use dioxus::prelude::*; | ||||||
|  |  | ||||||
|  | #[component] | ||||||
|  | pub(crate) fn TaskList(tasks: Vec<Task>, class: Option<&'static str>) -> Element { | ||||||
|  |     rsx! { | ||||||
|  |         div { | ||||||
|  |             class: format!("pt-3 px-8 flex flex-col {}", class.unwrap_or("")), | ||||||
|  |             for task in tasks { | ||||||
|  |                 div { | ||||||
|  |                     class: format!( | ||||||
|  |                         "pt-5 {} flex flex-row gap-4", | ||||||
|  |                         if task.deadline().is_some() { | ||||||
|  |                             "pb-0.5" | ||||||
|  |                         } else if let Category::Calendar { time, .. } = task.category() { | ||||||
|  |                             if time.is_some() { | ||||||
|  |                                 "pb-0.5" | ||||||
|  |                             } else { | ||||||
|  |                                 "pb-5" | ||||||
|  |                             } | ||||||
|  |                         } else { | ||||||
|  |                             "pb-5" | ||||||
|  |                         } | ||||||
|  |                     ), | ||||||
|  |                     i { | ||||||
|  |                         class: "fa-regular fa-square text-3xl text-zinc-600", | ||||||
|  |                     }, | ||||||
|  |                     div { | ||||||
|  |                         class: "flex flex-col", | ||||||
|  |                         div { | ||||||
|  |                             class: "mt-1 grow", | ||||||
|  |                             {task.title()} | ||||||
|  |                         }, | ||||||
|  |                         div { | ||||||
|  |                             class: "flex flex-row gap-3", | ||||||
|  |                             if let Some(deadline) = task.deadline() { | ||||||
|  |                                 div { | ||||||
|  |                                     class: "text-sm text-zinc-400", | ||||||
|  |                                     i { | ||||||
|  |                                         class: "fa-solid fa-bomb" | ||||||
|  |                                     }, | ||||||
|  |                                     {deadline.format(" %m. %d.").to_string()} | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                             if let Category::Calendar { time, .. } = task.category() { | ||||||
|  |                                 if let Some(calendar_time) = time { | ||||||
|  |                                     div { | ||||||
|  |                                         class: "text-sm text-zinc-400", | ||||||
|  |                                         i { | ||||||
|  |                                             class: "fa-solid fa-clock" | ||||||
|  |                                         }, | ||||||
|  |                                         {calendar_time.time().format(" %k:%M").to_string()} | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,10 +1,12 @@ | |||||||
|  | use crate::schema::tasks; | ||||||
| use chrono::{Duration, NaiveDate, NaiveTime}; | use chrono::{Duration, NaiveDate, NaiveTime}; | ||||||
| use diesel::deserialize::FromSql; | use diesel::deserialize::FromSql; | ||||||
| use diesel::pg::{Pg, PgValue}; | use diesel::pg::{Pg, PgValue}; | ||||||
| use diesel::serialize::{Output, ToSql}; | use diesel::serialize::{Output, ToSql}; | ||||||
| use diesel::sql_types::Jsonb; | use diesel::sql_types::{Bool, Jsonb}; | ||||||
| use diesel::{AsExpression, FromSqlRow}; | use diesel::{AsExpression, BoxableExpression, FromSqlRow, PgJsonbExpressionMethods}; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
|  | use serde_json::json; | ||||||
| use serde_with::DurationSeconds; | use serde_with::DurationSeconds; | ||||||
| use std::io::Write; | use std::io::Write; | ||||||
|  |  | ||||||
| @@ -18,8 +20,7 @@ pub enum Category { | |||||||
|     NextSteps, |     NextSteps, | ||||||
|     Calendar { |     Calendar { | ||||||
|         date: NaiveDate, |         date: NaiveDate, | ||||||
|         #[serde_as(as = "Option<DurationSeconds<i64>>")] |         reoccurrence: Option<Reoccurrence>, | ||||||
|         reoccurance_interval: Option<Duration>, |  | ||||||
|         time: Option<CalendarTime>, |         time: Option<CalendarTime>, | ||||||
|     }, |     }, | ||||||
|     LongTerm, |     LongTerm, | ||||||
| @@ -27,17 +28,26 @@ pub enum Category { | |||||||
|     Trash, |     Trash, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[serde_with::serde_as] | impl Category { | ||||||
| #[derive(Serialize, Deserialize, Clone, Debug)] |     pub fn eq_sql_predicate(&self) -> Box<dyn BoxableExpression<tasks::table, Pg, SqlType=Bool>> { | ||||||
| pub struct CalendarTime { |         use crate::schema::tasks::dsl::*; | ||||||
|     time: NaiveTime, |  | ||||||
|     #[serde_as(as = "Option<DurationSeconds<i64>>")] |         match self { | ||||||
|     reminder_offset: Option<Duration>, |             Category::Inbox => Box::new(category.contains(json!("Inbox"))), | ||||||
|  |             Category::SomedayMaybe => Box::new(category.contains(json!("SomedayMaybe"))), | ||||||
|  |             Category::WaitingFor(_) => Box::new(category.has_key("WaitingFor")), | ||||||
|  |             Category::NextSteps => Box::new(category.contains(json!("NextSteps"))), | ||||||
|  |             Category::Calendar { .. } => Box::new(category.has_key("Calendar")), | ||||||
|  |             Category::LongTerm => Box::new(category.contains(json!("LongTerm"))), | ||||||
|  |             Category::Done => Box::new(category.contains(json!("Done"))), | ||||||
|  |             Category::Trash => Box::new(category.contains(json!("Trash"))), | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl CalendarTime { | impl PartialEq for Category { | ||||||
|     pub fn new(time: NaiveTime, reminder_offset: Option<Duration>) -> Self { |     fn eq(&self, other: &Self) -> bool { | ||||||
|         Self { time, reminder_offset } |         std::mem::discriminant(self) == std::mem::discriminant(other) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -63,3 +73,53 @@ impl FromSql<Jsonb, Pg> for Category { | |||||||
|         serde_json::from_str(str).map_err(Into::into) |         serde_json::from_str(str).map_err(Into::into) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Serialize, Deserialize, Clone, Debug)] | ||||||
|  | pub enum ReoccurrenceInterval { | ||||||
|  |     Day, | ||||||
|  |     Month, | ||||||
|  |     Year, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Serialize, Deserialize, Clone, Debug)] | ||||||
|  | pub struct Reoccurrence { | ||||||
|  |     start_date: NaiveDate, | ||||||
|  |     interval: ReoccurrenceInterval, | ||||||
|  |     length: u32, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Reoccurrence { | ||||||
|  |     pub fn new(start_date: NaiveDate, interval: ReoccurrenceInterval, length: u32) -> Self { | ||||||
|  |         Self { start_date, interval, length } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     pub fn interval(&self) -> &ReoccurrenceInterval { | ||||||
|  |         &self.interval | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn length(&self) -> u32 { | ||||||
|  |         self.length | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[serde_with::serde_as] | ||||||
|  | #[derive(Serialize, Deserialize, Clone, Debug)] | ||||||
|  | pub struct CalendarTime { | ||||||
|  |     time: NaiveTime, | ||||||
|  |     #[serde_as(as = "Option<DurationSeconds<i64>>")] | ||||||
|  |     reminder_offset: Option<Duration>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl CalendarTime { | ||||||
|  |     pub fn new(time: NaiveTime, reminder_offset: Option<Duration>) -> Self { | ||||||
|  |         Self { time, reminder_offset } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     pub fn time(&self) -> NaiveTime { | ||||||
|  |         self.time | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn reminder_offset(&self) -> Option<Duration> { | ||||||
|  |         self.reminder_offset | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ use crate::schema::tasks; | |||||||
| const TITLE_LENGTH_MIN: u64 = 1; | const TITLE_LENGTH_MIN: u64 = 1; | ||||||
| const TITLE_LENGTH_MAX: u64 = 255; | 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::tasks)] | #[diesel(table_name = crate::schema::tasks)] | ||||||
| #[diesel(check_for_backend(diesel::pg::Pg))] | #[diesel(check_for_backend(diesel::pg::Pg))] | ||||||
| pub struct Task { | pub struct Task { | ||||||
|   | |||||||
| @@ -1,8 +1,48 @@ | |||||||
| use crate::components::home::Home; | use crate::components::home::Home; | ||||||
|  | use crate::components::pages::category_inbox_page::CategoryInboxPage; | ||||||
|  | use crate::components::pages::category_next_steps_page::CategoryNextStepsPage; | ||||||
|  | use crate::components::pages::category_today_page::CategoryTodayPage; | ||||||
|  | use crate::components::pages::category_trash_page::CategoryTrashPage; | ||||||
|  | use crate::components::pages::category_waiting_for_page::CategoryWaitingForPage; | ||||||
|  | use crate::components::pages::category_someday_maybe_page::CategorySomedayMaybePage; | ||||||
|  | use crate::components::pages::category_done_page::CategoryDonePage; | ||||||
|  | use crate::components::pages::category_calendar_page::CategoryCalendarPage; | ||||||
|  | use crate::components::pages::category_long_term_page::CategoryLongTermPage; | ||||||
|  | use crate::components::pages::projects_page::ProjectsPage; | ||||||
|  | use crate::components::pages::not_found_page::NotFoundPage; | ||||||
|  | use crate::components::layout::Layout; | ||||||
| use dioxus::prelude::*; | use dioxus::prelude::*; | ||||||
|  | use crate::models::category::Category; | ||||||
|  |  | ||||||
| #[derive(Clone, Routable, Debug, PartialEq)] | #[derive(Clone, Routable, Debug, PartialEq)] | ||||||
|  | #[rustfmt::skip] | ||||||
| pub(crate) enum Route { | pub(crate) enum Route { | ||||||
|     #[route("/")] |     #[layout(Layout)] | ||||||
|     Home {}, |         #[redirect("/", || Route::CategoryTodayPage {})] | ||||||
|  |         #[route("/today")] | ||||||
|  |         CategoryTodayPage, | ||||||
|  |         #[route("/inbox")] | ||||||
|  |         CategoryInboxPage, | ||||||
|  |         #[route("/someday-maybe")] | ||||||
|  |         CategorySomedayMaybePage, | ||||||
|  |         #[route("/waiting-for")] | ||||||
|  |         CategoryWaitingForPage, | ||||||
|  |         #[route("/next-steps")] | ||||||
|  |         CategoryNextStepsPage, | ||||||
|  |         #[route("/calendar")] | ||||||
|  |         CategoryCalendarPage, | ||||||
|  |         #[route("/long-term")] | ||||||
|  |         CategoryLongTermPage, | ||||||
|  |         #[route("/done")] | ||||||
|  |         CategoryDonePage, | ||||||
|  |         #[route("/trash")] | ||||||
|  |         CategoryTrashPage, | ||||||
|  |         #[route("/projects")] | ||||||
|  |         ProjectsPage, | ||||||
|  |     #[end_layout] | ||||||
|  |     #[redirect("/", || Route::CategoryTodayPage)] | ||||||
|  |     #[route("/:..route")] | ||||||
|  |     NotFoundPage { | ||||||
|  |         route: Vec<String>, | ||||||
|  |     }, | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,10 +2,11 @@ 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::{RunQueryDsl, SelectableHelper}; | use diesel::{QueryDsl, RunQueryDsl, SelectableHelper}; | ||||||
| use dioxus::prelude::*; | use dioxus::prelude::*; | ||||||
| use validator::Validate; | use validator::Validate; | ||||||
| use crate::errors::task_create_error::TaskCreateError; | use crate::errors::task_create_error::TaskCreateError; | ||||||
|  | use crate::models::category::Category; | ||||||
|  |  | ||||||
| #[server] | #[server] | ||||||
| pub(crate) async fn create_task(new_task: NewTask) | pub(crate) async fn create_task(new_task: NewTask) | ||||||
| @@ -43,3 +44,24 @@ pub(crate) async fn create_task(new_task: NewTask) | |||||||
|  |  | ||||||
|     Ok(new_task) |     Ok(new_task) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[server] | ||||||
|  | pub(crate) async fn get_tasks_in_category(filtered_category: Category) | ||||||
|  |     -> Result<Vec<Task>, ServerFnError<ErrorVec<Error>>> { | ||||||
|  |     use crate::schema::tasks::dsl::*; | ||||||
|  |  | ||||||
|  |     let mut connection = establish_database_connection() | ||||||
|  |         .map_err::<ErrorVec<Error>, _>( | ||||||
|  |             |_| vec![Error::ServerInternal].into() | ||||||
|  |         )?; | ||||||
|  |  | ||||||
|  |     let results = tasks | ||||||
|  |         .select(Task::as_select()) | ||||||
|  |         .filter(filtered_category.eq_sql_predicate()) | ||||||
|  |         .load::<Task>(&mut connection) | ||||||
|  |         .map_err::<ErrorVec<Error>, _>( | ||||||
|  |             |_| vec![Error::ServerInternal].into() | ||||||
|  |         )?; | ||||||
|  |  | ||||||
|  |     Ok(results) | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Matouš Volf
					Matouš Volf