mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-25 20:09:48 -04:00
Implement save categories functionality
This commit is contained in:
parent
3094dee9f7
commit
481e44911f
68
Cargo.lock
generated
68
Cargo.lock
generated
@ -146,24 +146,6 @@ dependencies = [
|
|||||||
"event-listener",
|
"event-listener",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-process"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c"
|
|
||||||
dependencies = [
|
|
||||||
"async-io",
|
|
||||||
"autocfg",
|
|
||||||
"blocking",
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"event-listener",
|
|
||||||
"futures-lite",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"signal-hook",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-session"
|
name = "async-session"
|
||||||
version = "3.0.0"
|
version = "3.0.0"
|
||||||
@ -195,7 +177,6 @@ dependencies = [
|
|||||||
"async-global-executor",
|
"async-global-executor",
|
||||||
"async-io",
|
"async-io",
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-process",
|
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -263,9 +244,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.5.15"
|
version = "0.5.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9de18bc5f2e9df8f52da03856bf40e29b747de5a84e43aefff90e3dc4a21529b"
|
checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
@ -307,9 +288,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.2.7"
|
version = "0.2.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e4f44a0e6200e9d11a1cdc989e4b358f6e3d354fbf48478f345a17f4e43f8635"
|
checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -317,6 +298,8 @@ dependencies = [
|
|||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"mime",
|
"mime",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1894,25 +1877,6 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook"
|
|
||||||
version = "0.3.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"signal-hook-registry",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook-registry"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@ -1955,9 +1919,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlformat"
|
name = "sqlformat"
|
||||||
version = "0.1.8"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
|
checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
"nom",
|
"nom",
|
||||||
@ -1966,9 +1930,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx"
|
name = "sqlx"
|
||||||
version = "0.6.1"
|
version = "0.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "788841def501aabde58d3666fcea11351ec3962e6ea75dbcd05c84a71d68bcd1"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-macros",
|
"sqlx-macros",
|
||||||
@ -1976,9 +1938,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-core"
|
name = "sqlx-core"
|
||||||
version = "0.6.1"
|
version = "0.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8c21d3b5e7cadfe9ba7cdc1295f72cc556c750b4419c27c219c0693198901f8e"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"atoi",
|
"atoi",
|
||||||
@ -2022,9 +1982,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-macros"
|
name = "sqlx-macros"
|
||||||
version = "0.6.1"
|
version = "0.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4adfd2df3557bddd3b91377fc7893e8fa899e9b4061737cbade4e1bb85f1b45c"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"either",
|
"either",
|
||||||
@ -2044,9 +2002,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-rt"
|
name = "sqlx-rt"
|
||||||
version = "0.6.1"
|
version = "0.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7be52fc7c96c136cedea840ed54f7d446ff31ad670c9dea95ebcb998530971a3"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"futures-rustls",
|
"futures-rustls",
|
||||||
|
@ -41,7 +41,7 @@ version = "1.0.144"
|
|||||||
features = ["serde", "v4"]
|
features = ["serde", "v4"]
|
||||||
|
|
||||||
[dependencies.axum]
|
[dependencies.axum]
|
||||||
version = "0.5.15"
|
version = "0.5.16"
|
||||||
features = ["headers", "http2"]
|
features = ["headers", "http2"]
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
@ -49,9 +49,10 @@ version = "3.2.16"
|
|||||||
features = [ "cargo" ]
|
features = [ "cargo" ]
|
||||||
|
|
||||||
[dependencies.async-std]
|
[dependencies.async-std]
|
||||||
version = "1.10.0"
|
version = "1.12.0"
|
||||||
features = ["tokio1"]
|
features = ["tokio1"]
|
||||||
|
|
||||||
[dependencies.sqlx]
|
[dependencies.sqlx]
|
||||||
version = "0.6.1"
|
path = "../../sqlx"
|
||||||
|
#version = "0.6.2"
|
||||||
features = ["sqlite", "runtime-async-std-rustls", "offline"]
|
features = ["sqlite", "runtime-async-std-rustls", "offline"]
|
@ -70,7 +70,7 @@ async fn ui_static_assets(Path(path): Path<String>) -> impl IntoResponse {
|
|||||||
|
|
||||||
let mut path = path.trim_start_matches("/");
|
let mut path = path.trim_start_matches("/");
|
||||||
path = match path {
|
path = match path {
|
||||||
"" | "inventory" | "plan" | "cook" | "login" => "index.html",
|
"" | "inventory" | "plan" | "cook" | "categories" | "login" => "index.html",
|
||||||
_ => {
|
_ => {
|
||||||
if path.starts_with("recipe") {
|
if path.starts_with("recipe") {
|
||||||
"index.html"
|
"index.html"
|
||||||
|
@ -18,6 +18,7 @@ pub enum AppRoutes {
|
|||||||
Inventory,
|
Inventory,
|
||||||
Cook,
|
Cook,
|
||||||
Recipe(String),
|
Recipe(String),
|
||||||
|
Categories,
|
||||||
Login,
|
Login,
|
||||||
Error(String),
|
Error(String),
|
||||||
NotFound,
|
NotFound,
|
||||||
|
107
web/src/components/categories.rs
Normal file
107
web/src/components/categories.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// 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 serde_json::{from_str, to_string};
|
||||||
|
use sycamore::{futures::spawn_local_in_scope, prelude::*};
|
||||||
|
use tracing::{debug, error, instrument};
|
||||||
|
use web_sys::HtmlDialogElement;
|
||||||
|
|
||||||
|
use recipes::parse;
|
||||||
|
|
||||||
|
use crate::{js_lib::get_element_by_id, service::get_appservice_from_context};
|
||||||
|
|
||||||
|
fn get_error_dialog() -> HtmlDialogElement {
|
||||||
|
get_element_by_id::<HtmlDialogElement>("error-dialog")
|
||||||
|
.expect("error-dialog isn't an html dialog element!")
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_category_text_parses(unparsed: &str, error_text: Signal<String>) -> bool {
|
||||||
|
let el = get_error_dialog();
|
||||||
|
if let Err(e) = parse::as_categories(unparsed) {
|
||||||
|
error!(?e, "Error parsing categories");
|
||||||
|
error_text.set(e);
|
||||||
|
el.show();
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
el.close();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
#[component(Categories<G>)]
|
||||||
|
pub fn categories() -> View<G> {
|
||||||
|
let app_service = get_appservice_from_context();
|
||||||
|
let save_signal = Signal::new(());
|
||||||
|
let error_text = Signal::new(String::new());
|
||||||
|
let category_text = Signal::new(
|
||||||
|
match app_service
|
||||||
|
.get_category_text()
|
||||||
|
.expect("Failed to get categories.")
|
||||||
|
{
|
||||||
|
Some(js) => from_str::<String>(&js)
|
||||||
|
.map_err(|e| format!("{}", e))
|
||||||
|
.expect("Failed to parse categories as json"),
|
||||||
|
None => String::new(),
|
||||||
|
}, //.unwrap_or_else(|| String::new()),
|
||||||
|
);
|
||||||
|
|
||||||
|
create_effect(
|
||||||
|
cloned!((app_service, category_text, save_signal, error_text) => move || {
|
||||||
|
// TODO(jwall): This is triggering on load which is not desired.
|
||||||
|
save_signal.get();
|
||||||
|
spawn_local_in_scope({
|
||||||
|
cloned!((app_service, category_text, error_text) => async move {
|
||||||
|
// TODO(jwall): Save the categories.
|
||||||
|
if let Err(e) = app_service.save_categories(category_text.get_untracked().as_ref().clone()).await {
|
||||||
|
error!(?e, "Failed to save categories");
|
||||||
|
error_text.set(format!("{:?}", e));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let dialog_view = cloned!((error_text) => view! {
|
||||||
|
dialog(id="error-dialog") {
|
||||||
|
article{
|
||||||
|
header {
|
||||||
|
a(href="#", on:click=|_| {
|
||||||
|
let el = get_error_dialog();
|
||||||
|
el.close();
|
||||||
|
}, class="close")
|
||||||
|
"Invalid Categories"
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
(error_text.get().clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cloned!((category_text, error_text) => view! {
|
||||||
|
(dialog_view)
|
||||||
|
textarea(bind:value=category_text.clone(), rows=20)
|
||||||
|
a(role="button", href="#", on:click=cloned!((category_text, error_text) => move |_| {
|
||||||
|
check_category_text_parses(category_text.get().as_str(), error_text.clone());
|
||||||
|
})) { "Check" } " "
|
||||||
|
a(role="button", href="#", on:click=cloned!((category_text, error_text) => move |_| {
|
||||||
|
// TODO(jwall): check and then save the categories.
|
||||||
|
if check_category_text_parses(category_text.get().as_str(), error_text.clone()) {
|
||||||
|
debug!("triggering category save");
|
||||||
|
save_signal.trigger_subscribers();
|
||||||
|
}
|
||||||
|
})) { "Save" }
|
||||||
|
})
|
||||||
|
}
|
@ -11,6 +11,7 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
pub mod categories;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod recipe;
|
pub mod recipe;
|
||||||
pub mod recipe_list;
|
pub mod recipe_list;
|
||||||
@ -19,6 +20,7 @@ pub mod recipe_selector;
|
|||||||
pub mod shopping_list;
|
pub mod shopping_list;
|
||||||
pub mod tabs;
|
pub mod tabs;
|
||||||
|
|
||||||
|
pub use categories::*;
|
||||||
pub use header::*;
|
pub use header::*;
|
||||||
pub use recipe::*;
|
pub use recipe::*;
|
||||||
pub use recipe_list::*;
|
pub use recipe_list::*;
|
||||||
|
@ -50,6 +50,7 @@ fn editor(recipe: RecipeEntry) -> View<G> {
|
|||||||
|
|
||||||
create_effect(
|
create_effect(
|
||||||
cloned!((id, app_service, text, save_signal, error_text) => move || {
|
cloned!((id, app_service, text, save_signal, error_text) => move || {
|
||||||
|
// TODO(jwall): This is triggering on load which is not desired.
|
||||||
save_signal.get();
|
save_signal.get();
|
||||||
spawn_local_in_scope({
|
spawn_local_in_scope({
|
||||||
cloned!((id, app_service, text, error_text) => async move {
|
cloned!((id, app_service, text, error_text) => async move {
|
||||||
|
@ -16,12 +16,11 @@ use sycamore::{futures::spawn_local_in_scope, prelude::*};
|
|||||||
use tracing::{error, instrument};
|
use tracing::{error, instrument};
|
||||||
|
|
||||||
use crate::components::recipe_selection::*;
|
use crate::components::recipe_selection::*;
|
||||||
use crate::service::get_appservice_from_context;
|
use crate::service::AppService;
|
||||||
|
|
||||||
#[instrument]
|
#[instrument]
|
||||||
#[component(RecipeSelector<G>)]
|
#[component(RecipeSelector<G>)]
|
||||||
pub fn recipe_selector() -> View<G> {
|
pub fn recipe_selector(app_service: AppService) -> View<G> {
|
||||||
let app_service = get_appservice_from_context();
|
|
||||||
let rows = create_memo(cloned!(app_service => move || {
|
let rows = create_memo(cloned!(app_service => move || {
|
||||||
let mut rows = Vec::new();
|
let mut rows = Vec::new();
|
||||||
for row in app_service.get_recipes().get().iter().map(|(k, v)| (k.clone(), v.clone())).collect::<Vec<(String, Signal<Recipe>)>>().chunks(4) {
|
for row in app_service.get_recipes().get().iter().map(|(k, v)| (k.clone(), v.clone())).collect::<Vec<(String, Signal<Recipe>)>>().chunks(4) {
|
||||||
|
@ -29,6 +29,8 @@ pub fn tabbed_view(state: TabState<G>) -> View<G> {
|
|||||||
li { a(href="/ui/inventory", class="no-print") { "Inventory" } " > "
|
li { a(href="/ui/inventory", class="no-print") { "Inventory" } " > "
|
||||||
}
|
}
|
||||||
li { a(href="/ui/cook", class="no-print") { "Cook" }
|
li { a(href="/ui/cook", class="no-print") { "Cook" }
|
||||||
|
} " | "
|
||||||
|
li { a(href="/ui/categories", class="no-print") { "Categories" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
|
30
web/src/pages/categories.rs
Normal file
30
web/src/pages/categories.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 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 crate::components::categories::*;
|
||||||
|
use crate::components::tabs::*;
|
||||||
|
|
||||||
|
use sycamore::prelude::*;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
#[component(CategoryPage<G>)]
|
||||||
|
pub fn category_page() -> View<G> {
|
||||||
|
view! {
|
||||||
|
TabbedView(TabState {
|
||||||
|
inner: view! {
|
||||||
|
Categories()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -11,14 +11,20 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
mod categories;
|
||||||
mod cook;
|
mod cook;
|
||||||
mod inventory;
|
mod inventory;
|
||||||
mod login;
|
mod login;
|
||||||
mod plan;
|
mod plan;
|
||||||
mod recipe;
|
mod recipe;
|
||||||
|
|
||||||
|
pub use categories::*;
|
||||||
pub use cook::*;
|
pub use cook::*;
|
||||||
pub use inventory::*;
|
pub use inventory::*;
|
||||||
pub use login::*;
|
pub use login::*;
|
||||||
pub use plan::*;
|
pub use plan::*;
|
||||||
pub use recipe::*;
|
pub use recipe::*;
|
||||||
|
|
||||||
|
pub struct PageProps {
|
||||||
|
service: crate::service::AppService,
|
||||||
|
}
|
||||||
|
@ -15,12 +15,14 @@ use crate::components::{recipe_selector::*, tabs::*};
|
|||||||
|
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
|
|
||||||
|
use super::PageProps;
|
||||||
|
|
||||||
#[component(PlanPage<G>)]
|
#[component(PlanPage<G>)]
|
||||||
pub fn plan_page() -> View<G> {
|
pub fn plan_page(props: PageProps) -> View<G> {
|
||||||
view! {
|
view! {
|
||||||
TabbedView(TabState {
|
TabbedView(TabState {
|
||||||
inner: view! {
|
inner: view! {
|
||||||
RecipeSelector()
|
RecipeSelector(props.service.clone())
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,7 @@ impl DeriveRoute for AppRoutes {
|
|||||||
"/ui/plan" => AppRoutes::Plan,
|
"/ui/plan" => AppRoutes::Plan,
|
||||||
"/ui/cook" => AppRoutes::Cook,
|
"/ui/cook" => AppRoutes::Cook,
|
||||||
"/ui/inventory" => AppRoutes::Inventory,
|
"/ui/inventory" => AppRoutes::Inventory,
|
||||||
|
"/ui/categories" => AppRoutes::Categories,
|
||||||
h => {
|
h => {
|
||||||
if h.starts_with("/ui/recipe/") {
|
if h.starts_with("/ui/recipe/") {
|
||||||
let parts: Vec<&str> = h.split("/").collect();
|
let parts: Vec<&str> = h.split("/").collect();
|
||||||
|
@ -86,15 +86,18 @@ impl AppService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_category_text(&self) -> Result<Option<String>, String> {
|
||||||
|
let storage = self.get_storage()?.unwrap();
|
||||||
|
storage
|
||||||
|
.get_item("categories")
|
||||||
|
.map_err(|e| format!("{:?}", e))
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub fn fetch_categories_from_storage(
|
pub fn fetch_categories_from_storage(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Option<BTreeMap<String, String>>, String> {
|
) -> Result<Option<BTreeMap<String, String>>, String> {
|
||||||
let storage = self.get_storage()?.unwrap();
|
match self.get_category_text()? {
|
||||||
match storage
|
|
||||||
.get_item("categories")
|
|
||||||
.map_err(|e| format!("{:?}", e))?
|
|
||||||
{
|
|
||||||
Some(s) => {
|
Some(s) => {
|
||||||
let parsed = from_str::<String>(&s).map_err(|e| format!("{}", e))?;
|
let parsed = from_str::<String>(&s).map_err(|e| format!("{}", e))?;
|
||||||
if parsed.is_empty() {
|
if parsed.is_empty() {
|
||||||
@ -224,7 +227,7 @@ impl AppService {
|
|||||||
.push((i.clone(), recipes.clone()));
|
.push((i.clone(), recipes.clone()));
|
||||||
}
|
}
|
||||||
debug!(?self.category_map);
|
debug!(?self.category_map);
|
||||||
// FIXM(jwall): Sort by categories and names.
|
// FIXME(jwall): Sort by categories and names.
|
||||||
groups
|
groups
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +372,7 @@ impl HttpStore {
|
|||||||
#[instrument(skip(categories))]
|
#[instrument(skip(categories))]
|
||||||
async fn save_categories(&self, categories: String) -> Result<(), Error> {
|
async fn save_categories(&self, categories: String) -> Result<(), Error> {
|
||||||
let mut path = self.root.clone();
|
let mut path = self.root.clone();
|
||||||
path.push_str("/recipes");
|
path.push_str("/categories");
|
||||||
let resp = reqwasm::http::Request::post(&path)
|
let resp = reqwasm::http::Request::post(&path)
|
||||||
.body(to_string(&categories).expect("Unable to encode categories as json"))
|
.body(to_string(&categories).expect("Unable to encode categories as json"))
|
||||||
.header("content-type", "application/json")
|
.header("content-type", "application/json")
|
||||||
|
@ -47,6 +47,9 @@ fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> {
|
|||||||
AppRoutes::Recipe(idx) => view! {
|
AppRoutes::Recipe(idx) => view! {
|
||||||
RecipePage(RecipePageProps { recipe: Signal::new(idx.clone()) })
|
RecipePage(RecipePageProps { recipe: Signal::new(idx.clone()) })
|
||||||
},
|
},
|
||||||
|
AppRoutes::Categories => view ! {
|
||||||
|
CategoryPage()
|
||||||
|
},
|
||||||
AppRoutes::NotFound => view! {
|
AppRoutes::NotFound => view! {
|
||||||
// TODO(Create a real one)
|
// TODO(Create a real one)
|
||||||
PlanPage()
|
PlanPage()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user