mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-22 19:40:14 -04:00
Use nested routing and expand the recipe page into two
This commit is contained in:
parent
efbd5140a8
commit
2058e047eb
@ -53,6 +53,10 @@ impl Mealplan {
|
||||
pub struct RecipeEntry(pub String, pub String);
|
||||
|
||||
impl RecipeEntry {
|
||||
pub fn new<IS: Into<String>, TS: Into<String>>(recipe_id: IS, text: TS) -> Self {
|
||||
Self(recipe_id.into(), text.into())
|
||||
}
|
||||
|
||||
pub fn set_recipe_id<S: Into<String>>(&mut self, id: S) {
|
||||
self.0 = id.into();
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ pub fn Header<G: Html>(cx: Scope) -> View<G> {
|
||||
nav(class="no-print") {
|
||||
h1(class="title") { "Kitchen" }
|
||||
ul {
|
||||
li { a(href="/ui/plan") { "MealPlan" } }
|
||||
li { a(href="/ui/categories") { "Manage" } }
|
||||
li { a(href="/ui/planning/plan") { "MealPlan" } }
|
||||
li { a(href="/ui/manage/categories") { "Manage" } }
|
||||
li { a(href="/ui/login") { "Login" } }
|
||||
li { a(href="https://github.com/zaphar/kitchen") { "Github" } }
|
||||
}
|
||||
|
@ -40,9 +40,28 @@ fn check_recipe_parses(text: &str, error_text: &Signal<String>) -> bool {
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Editor<G: Html>(cx: Scope, recipe: RecipeEntry) -> View<G> {
|
||||
let id = create_signal(cx, recipe.recipe_id().to_owned());
|
||||
let text = create_signal(cx, recipe.recipe_text().to_owned());
|
||||
pub fn Editor<G: Html>(cx: Scope, recipe_id: String) -> View<G> {
|
||||
let store = crate::api::HttpStore::get_from_context(cx);
|
||||
let recipe: &Signal<RecipeEntry> =
|
||||
create_signal(cx, RecipeEntry::new(&recipe_id, String::new()));
|
||||
let text = create_signal(cx, String::new());
|
||||
spawn_local_scoped(cx, {
|
||||
let store = store.clone();
|
||||
async move {
|
||||
let entry = store
|
||||
.get_recipe_text(recipe_id.as_str())
|
||||
.await
|
||||
.expect("Failure getting recipe");
|
||||
if let Some(entry) = entry {
|
||||
text.set(entry.recipe_text().to_owned());
|
||||
recipe.set(entry);
|
||||
} else {
|
||||
// FIXME(jwall): Show error message for missing recipe
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let id = create_memo(cx, || recipe.get().recipe_id().to_owned());
|
||||
let error_text = create_signal(cx, String::new());
|
||||
let save_signal = create_signal(cx, ());
|
||||
let dirty = create_signal(cx, false);
|
||||
@ -57,6 +76,7 @@ pub fn Editor<G: Html>(cx: Scope, recipe: RecipeEntry) -> View<G> {
|
||||
debug!("Recipe text is changed");
|
||||
spawn_local_scoped(cx, {
|
||||
let store = crate::api::HttpStore::get_from_context(cx);
|
||||
let state = app_state::State::get_from_context(cx);
|
||||
async move {
|
||||
debug!("Attempting to save recipe");
|
||||
if let Err(e) = store
|
||||
@ -69,7 +89,14 @@ pub fn Editor<G: Html>(cx: Scope, recipe: RecipeEntry) -> View<G> {
|
||||
error!(?e, "Failed to save recipe");
|
||||
error_text.set(format!("{:?}", e));
|
||||
} else {
|
||||
// We also need to set recipe in our state
|
||||
dirty.set(false);
|
||||
if let Ok(recipe) = recipes::parse::as_recipe(text.get_untracked().as_ref()) {
|
||||
state
|
||||
.recipes
|
||||
.modify()
|
||||
.insert(id.get_untracked().as_ref().to_owned(), recipe);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -114,91 +141,114 @@ pub fn Editor<G: Html>(cx: Scope, recipe: RecipeEntry) -> View<G> {
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Steps<'ctx, G: Html>(cx: Scope<'ctx>, steps: &'ctx ReadSignal<Vec<recipes::Step>>) -> View<G> {
|
||||
fn Steps<G: Html>(cx: Scope, steps: Vec<recipes::Step>) -> View<G> {
|
||||
let step_fragments = View::new_fragment(steps.iter().map(|step| {
|
||||
let mut step = step.clone();
|
||||
let ingredient_fragments = View::new_fragment(step.ingredients.drain(0..).map(|i| {
|
||||
view! {cx,
|
||||
h2 { "Steps: " }
|
||||
div(class="recipe_steps") {
|
||||
Indexed(
|
||||
iterable=steps,
|
||||
view = |cx, step: recipes::Step| { view! {cx,
|
||||
div {
|
||||
h3 { "Instructions" }
|
||||
ul(class="ingredients") {
|
||||
Indexed(
|
||||
iterable = create_signal(cx, step.ingredients),
|
||||
view = |cx, i| { view! {cx,
|
||||
li {
|
||||
(i.amt) " " (i.name) " " (i.form.as_ref().map(|f| format!("({})", f)).unwrap_or(String::new()))
|
||||
}
|
||||
}}
|
||||
)
|
||||
}
|
||||
}).collect());
|
||||
view! {cx,
|
||||
div {
|
||||
h3 { "Instructions" }
|
||||
ul(class="ingredients") {
|
||||
(ingredient_fragments)
|
||||
}
|
||||
div(class="instructions") {
|
||||
(step.instructions)
|
||||
}
|
||||
}}
|
||||
}
|
||||
)
|
||||
}
|
||||
}).collect());
|
||||
view! {cx,
|
||||
h2 { "Steps: " }
|
||||
div(class="recipe_steps") {
|
||||
(step_fragments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Recipe<'ctx, G: Html>(cx: Scope<'ctx>, recipe_id: String) -> View<G> {
|
||||
pub fn Viewer<G: Html>(cx: Scope, recipe_id: String) -> View<G> {
|
||||
let state = app_state::State::get_from_context(cx);
|
||||
let store = crate::api::HttpStore::get_from_context(cx);
|
||||
let view = create_signal(cx, View::empty());
|
||||
let show_edit = create_signal(cx, false);
|
||||
if let Some(recipe) = state.recipes.get_untracked().get(&recipe_id) {
|
||||
// FIXME(jwall): This should be create_effect rather than create_signal
|
||||
let recipe_text: &Signal<Option<RecipeEntry>> = create_signal(cx, None);
|
||||
spawn_local_scoped(cx, {
|
||||
let store = store.clone();
|
||||
async move {
|
||||
let entry = store
|
||||
.get_recipe_text(recipe_id.as_str())
|
||||
.await
|
||||
.expect("Failure getting recipe");
|
||||
recipe_text.set(entry);
|
||||
}
|
||||
});
|
||||
let recipe = create_signal(cx, recipe.clone());
|
||||
let title = create_memo(cx, move || recipe.get().title.clone());
|
||||
let desc = create_memo(cx, move || {
|
||||
recipe
|
||||
.clone()
|
||||
.get()
|
||||
.desc
|
||||
.clone()
|
||||
.unwrap_or_else(|| String::new())
|
||||
});
|
||||
let steps = create_memo(cx, move || recipe.get().steps.clone());
|
||||
create_effect(cx, move || {
|
||||
debug!("Choosing edit or view for recipe.");
|
||||
if *show_edit.get() {
|
||||
{
|
||||
debug!("Showing editor for recipe.");
|
||||
view.set(view! {cx,
|
||||
Editor(recipe_text.get().as_ref().clone().unwrap())
|
||||
});
|
||||
}
|
||||
} else {
|
||||
debug!("Showing text for recipe.");
|
||||
let title = recipe.title.clone();
|
||||
let desc = recipe.desc.clone().unwrap_or_else(|| String::new());
|
||||
let steps = recipe.steps.clone();
|
||||
debug!("Viewing recipe.");
|
||||
view.set(view! {cx,
|
||||
div(class="recipe") {
|
||||
h1(class="recipe_title") { (title.get()) }
|
||||
h1(class="recipe_title") { (title) }
|
||||
div(class="recipe_description") {
|
||||
(desc.get())
|
||||
(desc)
|
||||
}
|
||||
Steps(steps)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
view! {cx,
|
||||
span(role="button", on:click=move |_| { show_edit.set(true); }) { "Edit" } " "
|
||||
span(role="button", on:click=move |_| { show_edit.set(false); }) { "View" }
|
||||
(view.get().as_ref())
|
||||
}
|
||||
view! {cx, (view.get().as_ref()) }
|
||||
}
|
||||
|
||||
//#[component]
|
||||
//pub fn Recipe<'ctx, G: Html>(cx: Scope<'ctx>, recipe_id: String) -> View<G> {
|
||||
// let state = app_state::State::get_from_context(cx);
|
||||
// let store = crate::api::HttpStore::get_from_context(cx);
|
||||
// let view = create_signal(cx, View::empty());
|
||||
// let show_edit = create_signal(cx, false);
|
||||
// if let Some(recipe) = state.recipes.get_untracked().get(&recipe_id) {
|
||||
// // FIXME(jwall): This should be create_effect rather than create_signal
|
||||
// let recipe_text: &Signal<Option<RecipeEntry>> = create_signal(cx, None);
|
||||
// spawn_local_scoped(cx, {
|
||||
// let store = store.clone();
|
||||
// async move {
|
||||
// let entry = store
|
||||
// .get_recipe_text(recipe_id.as_str())
|
||||
// .await
|
||||
// .expect("Failure getting recipe");
|
||||
// recipe_text.set(entry);
|
||||
// }
|
||||
// });
|
||||
// let recipe = create_signal(cx, recipe.clone());
|
||||
// let title = create_memo(cx, move || recipe.get().title.clone());
|
||||
// let desc = create_memo(cx, move || {
|
||||
// recipe
|
||||
// .clone()
|
||||
// .get()
|
||||
// .desc
|
||||
// .clone()
|
||||
// .unwrap_or_else(|| String::new())
|
||||
// });
|
||||
// let steps = create_memo(cx, move || recipe.get().steps.clone());
|
||||
// create_effect(cx, move || {
|
||||
// debug!("Choosing edit or view for recipe.");
|
||||
// if *show_edit.get() {
|
||||
// {
|
||||
// debug!("Showing editor for recipe.");
|
||||
// view.set(view! {cx,
|
||||
// Editor(recipe_text.get().as_ref().clone().unwrap())
|
||||
// });
|
||||
// }
|
||||
// } else {
|
||||
// debug!("Showing text for recipe.");
|
||||
// view.set(view! {cx,
|
||||
// div(class="recipe") {
|
||||
// h1(class="recipe_title") { (title.get()) }
|
||||
// div(class="recipe_description") {
|
||||
// (desc.get())
|
||||
// }
|
||||
// Steps(steps)
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// view! {cx,
|
||||
// span(role="button", on:click=move |_| { show_edit.set(true); }) { "Edit" } " "
|
||||
// span(role="button", on:click=move |_| { show_edit.set(false); }) { "View" }
|
||||
// (view.get().as_ref())
|
||||
// }
|
||||
//}
|
||||
|
@ -11,7 +11,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use crate::{app_state, components::Recipe};
|
||||
use crate::{app_state, components::recipe::Viewer};
|
||||
|
||||
use sycamore::prelude::*;
|
||||
use tracing::{debug, instrument};
|
||||
@ -26,10 +26,10 @@ pub fn RecipeList<G: Html>(cx: Scope) -> View<G> {
|
||||
div() {
|
||||
Indexed(
|
||||
iterable=menu_list,
|
||||
view= |cx, (idx, _count)| {
|
||||
debug!(idx=%idx, "Rendering recipe");
|
||||
view= |cx, (id, _count)| {
|
||||
debug!(id=%id, "Rendering recipe");
|
||||
view ! {cx,
|
||||
Recipe(idx)
|
||||
Viewer(id)
|
||||
hr()
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ pub fn RecipeSelection<G: Html>(cx: Scope, props: RecipeCheckBoxProps) -> View<G
|
||||
);
|
||||
let title = props.title.get().clone();
|
||||
let for_id = id.clone();
|
||||
let href = format!("/ui/recipe/{}", id);
|
||||
let href = format!("/ui/recipe/view/{}", id);
|
||||
let name = format!("recipe_id:{}", id);
|
||||
view! {cx,
|
||||
div() {
|
||||
|
@ -18,7 +18,7 @@ use tracing::debug;
|
||||
pub struct TabState<'a, G: Html> {
|
||||
pub children: Children<'a, G>,
|
||||
pub selected: Option<String>,
|
||||
tablist: Vec<(&'static str, &'static str)>,
|
||||
tablist: Vec<(String, &'static str)>,
|
||||
}
|
||||
|
||||
#[component]
|
||||
@ -32,7 +32,8 @@ pub fn TabbedView<'a, G: Html>(cx: Scope<'a>, state: TabState<'a, G>) -> View<G>
|
||||
let menu = View::new_fragment(
|
||||
tablist
|
||||
.iter()
|
||||
.map(|&(href, show)| {
|
||||
.map(|&(ref href, show)| {
|
||||
let href = href.clone();
|
||||
debug!(?selected, show, "identifying tab");
|
||||
let class = if selected.as_ref().map_or(false, |selected| selected == show) {
|
||||
"no-print selected"
|
||||
|
@ -27,9 +27,9 @@ pub struct PageState<'a, G: Html> {
|
||||
pub fn ManagePage<'a, G: Html>(cx: Scope<'a>, state: PageState<'a, G>) -> View<G> {
|
||||
let PageState { children, selected } = state;
|
||||
let children = children.call(cx);
|
||||
let manage_tabs: Vec<(&'static str, &'static str)> = vec![
|
||||
("/ui/categories", "Categories"),
|
||||
("/ui/new_recipe", "New Recipe"),
|
||||
let manage_tabs: Vec<(String, &'static str)> = vec![
|
||||
("/ui/manage/categories".to_owned(), "Categories"),
|
||||
("/ui/manage/new_recipe".to_owned(), "New Recipe"),
|
||||
];
|
||||
|
||||
view! {cx,
|
||||
|
@ -28,10 +28,10 @@ pub struct PageState<'a, G: Html> {
|
||||
pub fn PlanningPage<'a, G: Html>(cx: Scope<'a>, state: PageState<'a, G>) -> View<G> {
|
||||
let PageState { children, selected } = state;
|
||||
let children = children.call(cx);
|
||||
let planning_tabs: Vec<(&'static str, &'static str)> = vec![
|
||||
("/ui/plan", "Plan"),
|
||||
("/ui/inventory", "Inventory"),
|
||||
("/ui/cook", "Cook"),
|
||||
let planning_tabs: Vec<(String, &'static str)> = vec![
|
||||
("/ui/planning/plan".to_owned(), "Plan"),
|
||||
("/ui/planning/inventory".to_owned(), "Inventory"),
|
||||
("/ui/planning/cook".to_owned(), "Cook"),
|
||||
];
|
||||
|
||||
view! {cx,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Jeremy Wall (jeremy@marzhillstudios.com)
|
||||
// Copyright 2022 Jeremy Wall (Jeremy@marzhilsltudios.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -11,20 +11,20 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use crate::components::recipe::Recipe;
|
||||
use crate::components::recipe::Editor;
|
||||
|
||||
use sycamore::prelude::*;
|
||||
use tracing::instrument;
|
||||
|
||||
#[derive(Debug, Props)]
|
||||
pub struct RecipePageProps {
|
||||
pub recipe: String,
|
||||
}
|
||||
use super::{RecipePage, RecipePageProps};
|
||||
|
||||
#[instrument]
|
||||
#[component()]
|
||||
pub fn RecipePage<G: Html>(cx: Scope, props: RecipePageProps) -> View<G> {
|
||||
pub fn RecipeEditPage<G: Html>(cx: Scope, props: RecipePageProps) -> View<G> {
|
||||
view! {cx,
|
||||
Recipe(props.recipe)
|
||||
RecipePage(
|
||||
selected=Some("Edit".to_owned()),
|
||||
recipe=props.recipe.clone(),
|
||||
) { Editor(props.recipe) }
|
||||
}
|
||||
}
|
53
web/src/pages/recipe/mod.rs
Normal file
53
web/src/pages/recipe/mod.rs
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2022 Jeremy Wall (jeremy@marzhillstudios.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use sycamore::prelude::*;
|
||||
|
||||
use crate::components::tabs::*;
|
||||
|
||||
mod edit;
|
||||
mod view;
|
||||
pub use edit::*;
|
||||
pub use view::*;
|
||||
|
||||
#[derive(Debug, Props)]
|
||||
pub struct RecipePageProps {
|
||||
pub recipe: String,
|
||||
}
|
||||
|
||||
#[derive(Props)]
|
||||
pub struct PageState<'a, G: Html> {
|
||||
pub recipe: String,
|
||||
pub children: Children<'a, G>,
|
||||
pub selected: Option<String>,
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn RecipePage<'ctx, G: Html>(cx: Scope<'ctx>, state: PageState<'ctx, G>) -> View<G> {
|
||||
let PageState {
|
||||
children,
|
||||
selected,
|
||||
recipe,
|
||||
} = state;
|
||||
let children = children.call(cx);
|
||||
let recipe_tabs: Vec<(String, &'static str)> = vec![
|
||||
(format!("/ui/recipe/view/{}", recipe), "View"),
|
||||
(format!("/ui/recipe/edit/{}", recipe), "Edit"),
|
||||
];
|
||||
view! {cx,
|
||||
TabbedView(
|
||||
selected= selected,
|
||||
tablist=recipe_tabs,
|
||||
) { (children) }
|
||||
}
|
||||
}
|
30
web/src/pages/recipe/view.rs
Normal file
30
web/src/pages/recipe/view.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2022 Jeremy Wall (Jeremy@marzhilsltudios.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use crate::components::recipe::Viewer;
|
||||
|
||||
use sycamore::prelude::*;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::{RecipePage, RecipePageProps};
|
||||
|
||||
#[instrument]
|
||||
#[component()]
|
||||
pub fn RecipeViewPage<G: Html>(cx: Scope, props: RecipePageProps) -> View<G> {
|
||||
view! {cx,
|
||||
RecipePage(
|
||||
selected=Some("View".to_owned()),
|
||||
recipe=props.recipe.clone(),
|
||||
) { Viewer(props.recipe) }
|
||||
}
|
||||
}
|
@ -13,71 +13,105 @@
|
||||
// limitations under the License.
|
||||
|
||||
use sycamore::prelude::*;
|
||||
//use sycamore_router::{HistoryIntegration, Route, Router};
|
||||
use sycamore_router::{HistoryIntegration, Route, Router};
|
||||
use tracing::instrument;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::pages::*;
|
||||
|
||||
//mod router;
|
||||
//use router::{HistoryIntegration, Router};
|
||||
|
||||
#[instrument]
|
||||
fn route_switch<'a, G: Html>(cx: Scope<'a>, route: &'a ReadSignal<Routes>) -> View<G> {
|
||||
// NOTE(jwall): This needs to not be a dynamic node. The rules around
|
||||
// this are somewhat unclear and underdocumented for Sycamore. But basically
|
||||
// avoid conditionals in the `view!` macro calls here.
|
||||
view! {cx,
|
||||
(match route.get().as_ref() {
|
||||
Routes::Plan => view! {cx,
|
||||
|
||||
let switcher = |cx: Scope, route: &Routes| {
|
||||
debug!(?route, "Dispatching for route");
|
||||
match route {
|
||||
Routes::Planning(Plan) => view! {cx,
|
||||
PlanPage()
|
||||
},
|
||||
Routes::Inventory => view! {cx,
|
||||
Routes::Planning(Inventory) => view! {cx,
|
||||
InventoryPage()
|
||||
},
|
||||
Routes::Planning(Cook) => view! {cx,
|
||||
CookPage()
|
||||
},
|
||||
Routes::Login => view! {cx,
|
||||
LoginPage()
|
||||
},
|
||||
Routes::Cook => view! {cx,
|
||||
CookPage()
|
||||
Routes::Recipe(RecipeRoutes::View(id)) => view! {cx,
|
||||
RecipeViewPage(recipe=id.clone())
|
||||
},
|
||||
Routes::Recipe(idx) => view! {cx,
|
||||
RecipePage(recipe=idx.clone())
|
||||
Routes::Recipe(RecipeRoutes::Edit(id)) => view! {cx,
|
||||
RecipeEditPage(recipe=id.clone())
|
||||
},
|
||||
Routes::Categories => view! {cx,
|
||||
Routes::Manage(ManageRoutes::Categories) => view! {cx,
|
||||
CategoryPage()
|
||||
},
|
||||
Routes::NewRecipe => view! {cx,
|
||||
Routes::Manage(ManageRoutes::NewRecipe) => view! {cx,
|
||||
AddRecipePage()
|
||||
},
|
||||
Routes::NotFound => view! {cx,
|
||||
Routes::NotFound
|
||||
| Routes::Manage(ManageRoutes::NotFound)
|
||||
| Routes::Planning(PlanningRoutes::NotFound)
|
||||
| Routes::Recipe(RecipeRoutes::NotFound) => view! {cx,
|
||||
// TODO(Create a real one)
|
||||
PlanPage()
|
||||
},
|
||||
})
|
||||
}
|
||||
};
|
||||
use PlanningRoutes::*;
|
||||
view! {cx,
|
||||
(switcher(cx, route.get().as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Route, Debug)]
|
||||
pub enum Routes {
|
||||
#[to("/ui/plan")]
|
||||
Plan,
|
||||
#[to("/ui/inventory")]
|
||||
Inventory,
|
||||
#[to("/ui/cook")]
|
||||
Cook,
|
||||
#[to("/ui/recipe/<id>")]
|
||||
Recipe(String),
|
||||
#[to("/ui/new_recipe")]
|
||||
NewRecipe,
|
||||
#[to("/ui/categories")]
|
||||
Categories,
|
||||
#[to("/ui/planning/<_..>")]
|
||||
Planning(PlanningRoutes),
|
||||
#[to("/ui/recipe/<_..>")]
|
||||
Recipe(RecipeRoutes),
|
||||
#[to("/ui/manage/<_..>")]
|
||||
Manage(ManageRoutes),
|
||||
#[to("/ui/login")]
|
||||
Login,
|
||||
#[not_found]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[derive(Route, Debug)]
|
||||
pub enum RecipeRoutes {
|
||||
#[to("/edit/<id>")]
|
||||
Edit(String),
|
||||
#[to("/view/<id>")]
|
||||
View(String),
|
||||
#[not_found]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[derive(Route, Debug)]
|
||||
pub enum ManageRoutes {
|
||||
#[to("/new_recipe")]
|
||||
NewRecipe,
|
||||
#[to("/categories")]
|
||||
Categories,
|
||||
#[not_found]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[derive(Route, Debug)]
|
||||
pub enum PlanningRoutes {
|
||||
#[to("/plan")]
|
||||
Plan,
|
||||
#[to("/inventory")]
|
||||
Inventory,
|
||||
#[to("/cook")]
|
||||
Cook,
|
||||
#[not_found]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Handler<G: Html>(cx: Scope) -> View<G> {
|
||||
view! {cx,
|
||||
|
Loading…
x
Reference in New Issue
Block a user