From 427ce16023613536b8176e6dee7c1580a5980c97 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Thu, 23 Oct 2025 02:24:29 +0200 Subject: feat(treewide): Make usage more intuitive --- src/api/mod.rs | 3 +- src/components/async_fetch.rs | 32 ++---- src/components/banner.rs | 6 +- src/components/buy.rs | 193 ----------------------------------- src/components/container.rs | 47 +++++---- src/components/input_placeholder.rs | 15 +-- src/components/inventory.rs | 55 ---------- src/components/mod.rs | 4 +- src/components/product_overview.rs | 38 ++++--- src/components/recipies.rs | 10 +- src/components/select_placeholder.rs | 22 ++-- src/lib.rs | 11 +- src/pages/buy.rs | 193 +++++++++++++++++++++++++++++++++++ src/pages/inventory.rs | 54 ++++++++++ src/pages/mod.rs | 3 + src/pages/recipies.rs | 8 ++ 16 files changed, 353 insertions(+), 341 deletions(-) delete mode 100644 src/components/buy.rs delete mode 100644 src/components/inventory.rs create mode 100644 src/pages/buy.rs create mode 100644 src/pages/inventory.rs create mode 100644 src/pages/recipies.rs (limited to 'src') diff --git a/src/api/mod.rs b/src/api/mod.rs index 3879223..bc800fb 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -11,7 +11,8 @@ use rocie_client::{ }, api_get_unit_api::unit_by_id, api_get_unit_property_api::unit_property_by_id, - api_set_barcode_api::buy_barcode, configuration::Configuration, + api_set_barcode_api::buy_barcode, + configuration::Configuration, }, models::{ BarcodeId, Product, ProductAmount, ProductId, Unit, UnitId, UnitProperty, UnitPropertyId, diff --git a/src/components/async_fetch.rs b/src/components/async_fetch.rs index f24e3a5..7bf44a0 100644 --- a/src/components/async_fetch.rs +++ b/src/components/async_fetch.rs @@ -15,31 +15,21 @@ pub(crate) use AsyncResource; macro_rules! AsyncFetch { ( - fetcher = $fetcher:block - producer = |$bound_variable:pat_param| $producer:block + @map_error_in_producer + fetcher = $fetcher:expr, + producer = $producer:expr $(,)? ) => {{ - use leptos::{ - prelude::{ElementChild, LocalResource, Suspend, Transition}, - view, - }; - - view! { - "Loading..."

} - }> - {move || Suspend::new(async move { - let resource = { LocalResource::new(move || $fetcher) }; - resource - .await - .map(|$bound_variable| $producer) - })} -
+ AsyncFetch! { + @map_error_in_producer + from_resource = leptos::prelude::LocalResource::new(move || $fetcher), + producer = $producer } }}; + ( @map_error_in_producer - from_resource = $resource:ident - producer = |$bound_variable:pat_param| $producer:block + from_resource = $resource:expr, + producer = $producer:expr $(,)? ) => {{ use leptos::prelude::{ElementChild, Suspend, Transition}; @@ -50,7 +40,7 @@ macro_rules! AsyncFetch { {move || Suspend::new(async move { $resource .await - .map(|$bound_variable| $producer) + .map($producer) })} } diff --git a/src/components/banner.rs b/src/components/banner.rs index acaaf62..3244a07 100644 --- a/src/components/banner.rs +++ b/src/components/banner.rs @@ -9,9 +9,5 @@ pub fn Banner(mut text: T) -> impl IntoView where T: FnMut() -> String + Send + 'static, { - view! { -

- {move || text()} -

- } + view! {

{move || text()}

} } diff --git a/src/components/buy.rs b/src/components/buy.rs deleted file mode 100644 index cb4cff4..0000000 --- a/src/components/buy.rs +++ /dev/null @@ -1,193 +0,0 @@ -use leptos::{ - IntoView, component, - prelude::{Get, Read, Show, WriteSignal, expect_context, signal}, - task::spawn_local, - view, -}; -use leptos_router::{NavigateOptions, hooks::use_navigate}; -use log::info; -use reactive_stores::Store; -use rocie_client::{ - apis::Error, - models::{Product, Unit}, -}; -use uuid::Uuid; - -use crate::{ - ConfigState, ConfigStateStoreFields, - api::{ - buy_barcode_wrapper, get_product_by_name, get_product_unit_by_id, - get_products_by_part_name, get_unit_by_id, - }, - components::{async_fetch::AsyncResource, banner::Banner, form::Form, site_header::SiteHeader}, -}; - -#[component] -pub fn Buy() -> impl IntoView { - let (on_submit_errored, on_submit_errored_set) = signal(None); - - view! { - - - - - - - { - Form! { - on_submit = |barcode_number, amount| { - let config = expect_context::>(); - let config = config.config().read(); - - spawn_local(async move { - if let Err(err) = buy_barcode_wrapper(&config, barcode_number).await { - let error = format!("Error in form on-submit for barcode `{barcode_number}`: {err}"); - on_submit_errored_set.set(Some(error)); - } else { - on_submit_errored_set.set(None); - - info!("Bought barcode {barcode_number} {amount} times"); - } - - }); - }; - - - - - } - } - } -} - -#[component] -pub fn AssociateBarcode() -> impl IntoView { - let product_name_signal; - - let (show_units, show_units_set) = signal(false); - - view! { - - - { - Form! { - on_submit = |product_name, amount, unit_id| { - spawn_local(async move { - let navigate = use_navigate(); - - info!("Got product barcode: {product_name} with amount: {amount}, and {unit_id}"); - - navigate("/", NavigateOptions::default()); - }); - }; - - - - - - } - } - } -} - -async fn generate_suggest_products( - optional_product_name: Option, -) -> Result>, leptos::error::Error> { - if let Some(product_name) = optional_product_name - && !product_name.is_empty() - { - let products = get_products_by_part_name(product_name).await?; - Ok(Some(products.into_iter().map(|prod| prod.name).collect())) - } else { - Ok(None) - } -} - -async fn product_unit_fetcher( - optinal_product_name: Option, -) -> Result>, leptos::error::Error> { - if let Some(product_name) = optinal_product_name - && !product_name.is_empty() - { - let value: Option = { - match get_product_by_name(product_name).await { - Ok(ok) => Ok::<_, leptos::error::Error>(Some(ok)), - Err(err) => match err { - Error::ResponseError(ref response_content) => { - match response_content.status.as_u16() { - 404 => Ok(None), - _ => Err(err.into()), - } - } - err => Err(err.into()), - }, - }? - }; - - if let Some(value) = value { - let (_, unit_property) = get_product_unit_by_id(value.id).await?; - - let mut units = Vec::with_capacity(unit_property.units.len()); - for unit_id in unit_property.units { - units.push(get_unit_by_id(unit_id).await?); - } - - Ok(Some(units)) - } else { - Ok(None) - } - } else { - Ok(None) - } -} diff --git a/src/components/container.rs b/src/components/container.rs index 7a4a64f..83b9584 100644 --- a/src/components/container.rs +++ b/src/components/container.rs @@ -11,25 +11,36 @@ pub fn Container( buttons: Vec<(impl IntoView, &'static str)>, children: Children, ) -> impl IntoView { + assert!(!buttons.is_empty()); + + let first_button_path = buttons.first().expect("Should have at least on button").1; + view! { -
-

{header}

- {children()} + - - } - }) - .collect::>()} - -
+
    + {buttons + .into_iter() + .map(|(name, path)| { + view! { +
  • + +
  • + } + }) + .collect::>()} +
+ + } } diff --git a/src/components/input_placeholder.rs b/src/components/input_placeholder.rs index 99b3196..0589363 100644 --- a/src/components/input_placeholder.rs +++ b/src/components/input_placeholder.rs @@ -96,12 +96,7 @@ pub fn InputPlaceholder( {label} - +
diff --git a/src/components/inventory.rs b/src/components/inventory.rs deleted file mode 100644 index 5855b33..0000000 --- a/src/components/inventory.rs +++ /dev/null @@ -1,55 +0,0 @@ -use leptos::{ - IntoView, component, - prelude::{ClassAttribute, ElementChild}, - view, -}; - -use crate::{ - api::{get_full_product_by_id, get_products}, - components::{async_fetch::AsyncFetch, site_header::SiteHeader}, -}; - -#[component] -pub fn Inventory() -> impl IntoView { - view! { - - -
    - { - AsyncFetch! { - fetcher = {get_products()} - producer = |products| { - products - .into_iter() - .map(|product| { - view! { - {AsyncFetch! { - fetcher = {get_full_product_by_id(product.id)} - producer = |(product, amount, unit)| { - view! { -
      -
    • {product.name}
    • -
    • - - { - format!( - "{} {}", - amount.amount.value, - unit.short_name - ) - } - -
    • -
    - } - } - }} - } - }) - .collect::>() - } - } - } -
- } -} diff --git a/src/components/mod.rs b/src/components/mod.rs index ca2ac10..efc4842 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -2,16 +2,14 @@ use std::sync::atomic::{AtomicU32, Ordering}; // Generic pub mod async_fetch; +pub mod banner; pub mod container; pub mod form; pub mod icon_p; pub mod input_placeholder; pub mod select_placeholder; -pub mod banner; // Specific -pub mod buy; -pub mod inventory; pub mod product_overview; pub mod recipies; pub mod site_header; diff --git a/src/components/product_overview.rs b/src/components/product_overview.rs index 777baef..5413dc1 100644 --- a/src/components/product_overview.rs +++ b/src/components/product_overview.rs @@ -11,33 +11,31 @@ pub fn ProductOverview() -> impl IntoView { }, - "inventory", - ), + (view! { }, "inventory"), (view! { }, "consume"), (view! { }, "buy"), ] > { AsyncFetch! { - fetcher = {get_products()} - producer = |products| { - let products_num = products.len(); - let plural_s = if products_num == 1 { "" } else { "s" }; - let products_value = 2; - let products_currency = "EUR"; + @map_error_in_producer + fetcher = get_products(), + producer = |products| { + let products_num = products.len(); + let plural_s = if products_num == 1 { "" } else { "s" }; + let products_value = 2; + let products_currency = "EUR"; - view! { -

- {format!( - "You have {products_num} product{plural_s} \ - in stock with a value \ - of {products_value} {products_currency}.", - )} -

- } - } + view! { +

+ {format!( + "You have {products_num} product{plural_s} \ + in stock with a value \ + of {products_value} {products_currency}.", + )} +

+ } + } } }
diff --git a/src/components/recipies.rs b/src/components/recipies.rs index 1bd3a0d..f7903e4 100644 --- a/src/components/recipies.rs +++ b/src/components/recipies.rs @@ -1,11 +1,17 @@ use leptos::{IntoView, component, prelude::ElementChild, view}; -use crate::components::container::Container; +use crate::components::{container::Container, icon_p::IconP}; #[component] pub fn Recipies() -> impl IntoView { view! { - + }, "recipies"), + (view! { }, "mealplan"), + ] + >

"You have 0 recipies."

} diff --git a/src/components/select_placeholder.rs b/src/components/select_placeholder.rs index 2e0f783..7289793 100644 --- a/src/components/select_placeholder.rs +++ b/src/components/select_placeholder.rs @@ -41,16 +41,18 @@ pub fn SelectPlaceholder( " node_ref=node_ref > - {move || AsyncFetch! { - @map_error_in_producer - from_resource = options - producer = |options| { - options - .into_iter() - .map(|(label, value)| { - view! { } - }) - .collect_view() + {move || { + AsyncFetch! { + @map_error_in_producer + from_resource = options, + producer = |options| { + options + .into_iter() + .map(|(label, value)| { + view! { } + }) + .collect_view() + } } }} diff --git a/src/lib.rs b/src/lib.rs index 3813ebb..b5ca65a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,9 +30,8 @@ use leptos_router::{ use reactive_stores::Store; use rocie_client::apis::configuration::Configuration; -use crate::{ - components::{buy::Buy, inventory::Inventory}, - pages::{home::Home, not_found::NotFound}, +use crate::pages::{ + buy::Buy, home::Home, inventory::Inventory, not_found::NotFound, recipies::Recipies, }; #[derive(Debug, Clone, Store)] @@ -84,6 +83,12 @@ pub fn App() -> impl IntoView { view! { } } /> + } + } + /> } diff --git a/src/pages/buy.rs b/src/pages/buy.rs new file mode 100644 index 0000000..cb4cff4 --- /dev/null +++ b/src/pages/buy.rs @@ -0,0 +1,193 @@ +use leptos::{ + IntoView, component, + prelude::{Get, Read, Show, WriteSignal, expect_context, signal}, + task::spawn_local, + view, +}; +use leptos_router::{NavigateOptions, hooks::use_navigate}; +use log::info; +use reactive_stores::Store; +use rocie_client::{ + apis::Error, + models::{Product, Unit}, +}; +use uuid::Uuid; + +use crate::{ + ConfigState, ConfigStateStoreFields, + api::{ + buy_barcode_wrapper, get_product_by_name, get_product_unit_by_id, + get_products_by_part_name, get_unit_by_id, + }, + components::{async_fetch::AsyncResource, banner::Banner, form::Form, site_header::SiteHeader}, +}; + +#[component] +pub fn Buy() -> impl IntoView { + let (on_submit_errored, on_submit_errored_set) = signal(None); + + view! { + + + + + + + { + Form! { + on_submit = |barcode_number, amount| { + let config = expect_context::>(); + let config = config.config().read(); + + spawn_local(async move { + if let Err(err) = buy_barcode_wrapper(&config, barcode_number).await { + let error = format!("Error in form on-submit for barcode `{barcode_number}`: {err}"); + on_submit_errored_set.set(Some(error)); + } else { + on_submit_errored_set.set(None); + + info!("Bought barcode {barcode_number} {amount} times"); + } + + }); + }; + + + + + } + } + } +} + +#[component] +pub fn AssociateBarcode() -> impl IntoView { + let product_name_signal; + + let (show_units, show_units_set) = signal(false); + + view! { + + + { + Form! { + on_submit = |product_name, amount, unit_id| { + spawn_local(async move { + let navigate = use_navigate(); + + info!("Got product barcode: {product_name} with amount: {amount}, and {unit_id}"); + + navigate("/", NavigateOptions::default()); + }); + }; + + + + + + } + } + } +} + +async fn generate_suggest_products( + optional_product_name: Option, +) -> Result>, leptos::error::Error> { + if let Some(product_name) = optional_product_name + && !product_name.is_empty() + { + let products = get_products_by_part_name(product_name).await?; + Ok(Some(products.into_iter().map(|prod| prod.name).collect())) + } else { + Ok(None) + } +} + +async fn product_unit_fetcher( + optinal_product_name: Option, +) -> Result>, leptos::error::Error> { + if let Some(product_name) = optinal_product_name + && !product_name.is_empty() + { + let value: Option = { + match get_product_by_name(product_name).await { + Ok(ok) => Ok::<_, leptos::error::Error>(Some(ok)), + Err(err) => match err { + Error::ResponseError(ref response_content) => { + match response_content.status.as_u16() { + 404 => Ok(None), + _ => Err(err.into()), + } + } + err => Err(err.into()), + }, + }? + }; + + if let Some(value) = value { + let (_, unit_property) = get_product_unit_by_id(value.id).await?; + + let mut units = Vec::with_capacity(unit_property.units.len()); + for unit_id in unit_property.units { + units.push(get_unit_by_id(unit_id).await?); + } + + Ok(Some(units)) + } else { + Ok(None) + } + } else { + Ok(None) + } +} diff --git a/src/pages/inventory.rs b/src/pages/inventory.rs new file mode 100644 index 0000000..e5ff6ae --- /dev/null +++ b/src/pages/inventory.rs @@ -0,0 +1,54 @@ +use leptos::{ + IntoView, component, + prelude::{ClassAttribute, ElementChild}, + view, +}; +use rocie_client::models::{Product, ProductAmount, Unit}; + +use crate::{ + api::{get_full_product_by_id, get_products}, + components::{async_fetch::AsyncFetch, site_header::SiteHeader}, +}; + +#[component] +pub fn Inventory() -> impl IntoView { + view! { + + +
    + { + AsyncFetch! { + @map_error_in_producer + fetcher = get_products(), + producer = render_products, + } + } +
+ } +} + +fn render_products(products: Vec) -> impl IntoView { + products + .into_iter() + .map(|product| { + AsyncFetch! { + @map_error_in_producer + fetcher = get_full_product_by_id(product.id), + producer = format_full_product, + } + }) + .collect::>() +} + +fn format_full_product((product, amount, unit): (Product, ProductAmount, Unit)) -> impl IntoView { + view! { +
    +
  • {product.name}
  • +
  • + + {format!("{} {}", amount.amount.value, unit.short_name)} + +
  • +
+ } +} diff --git a/src/pages/mod.rs b/src/pages/mod.rs index 8829694..a6057cd 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -1,2 +1,5 @@ +pub mod buy; pub mod home; +pub mod inventory; pub mod not_found; +pub mod recipies; diff --git a/src/pages/recipies.rs b/src/pages/recipies.rs new file mode 100644 index 0000000..1fc9dcc --- /dev/null +++ b/src/pages/recipies.rs @@ -0,0 +1,8 @@ +use leptos::{IntoView, component, view}; + +use crate::components::site_header::SiteHeader; + +#[component] +pub fn Recipies() -> impl IntoView { + view! { } +} -- cgit 1.4.1