summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-10-23 02:24:29 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-10-23 02:24:29 +0200
commit427ce16023613536b8176e6dee7c1580a5980c97 (patch)
treeab39dffcf89eacbcb19a251d540c46d9ee5b38d0
parentfeat(buy): Provide basic buy interface (diff)
downloadweb-client-427ce16023613536b8176e6dee7c1580a5980c97.zip
feat(treewide): Make usage more intuitive
-rw-r--r--rocie-macros/src/form/mod.rs5
-rw-r--r--rocie-macros/src/form/parse.rs6
-rw-r--r--src/api/mod.rs3
-rw-r--r--src/components/async_fetch.rs32
-rw-r--r--src/components/banner.rs6
-rw-r--r--src/components/container.rs47
-rw-r--r--src/components/input_placeholder.rs15
-rw-r--r--src/components/inventory.rs55
-rw-r--r--src/components/mod.rs4
-rw-r--r--src/components/product_overview.rs38
-rw-r--r--src/components/recipies.rs10
-rw-r--r--src/components/select_placeholder.rs22
-rw-r--r--src/lib.rs11
-rw-r--r--src/pages/buy.rs (renamed from src/components/buy.rs)0
-rw-r--r--src/pages/inventory.rs54
-rw-r--r--src/pages/mod.rs3
-rw-r--r--src/pages/recipies.rs8
17 files changed, 169 insertions, 150 deletions
diff --git a/rocie-macros/src/form/mod.rs b/rocie-macros/src/form/mod.rs
index 7abd8f6..b165750 100644
--- a/rocie-macros/src/form/mod.rs
+++ b/rocie-macros/src/form/mod.rs
@@ -28,7 +28,10 @@ pub enum ParsedChild {
         rust_type: Type,
         options: Expr, // Vec<(String, Uuid)>
     },
-    Show { when: Expr, children: Vec<ParsedChild> },
+    Show {
+        when: Expr,
+        children: Vec<ParsedChild>,
+    },
 }
 
 #[derive(Debug)]
diff --git a/rocie-macros/src/form/parse.rs b/rocie-macros/src/form/parse.rs
index b0ca58c..59a82c1 100644
--- a/rocie-macros/src/form/parse.rs
+++ b/rocie-macros/src/form/parse.rs
@@ -162,6 +162,10 @@ impl Parse for ParsedOnSubmit {
 
         input.parse::<Token![;]>()?;
 
-        Ok(Self { inputs, block, should_use_move })
+        Ok(Self {
+            inputs,
+            block,
+            should_use_move,
+        })
     }
 }
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! {
-            <Transition fallback=|| {
-                view! { <p>"Loading..."</p> }
-            }>
-                {move || Suspend::new(async move {
-                    let resource = { LocalResource::new(move || $fetcher) };
-                    resource
-                        .await
-                        .map(|$bound_variable| $producer)
-                })}
-            </Transition>
+        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)
                 })}
             </Transition>
         }
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<T>(mut text: T) -> impl IntoView
 where
     T: FnMut() -> String + Send + 'static,
 {
-    view! {
-        <p class="text-white rounded-lg m-2 p-2 bg-red-600">
-            {move || text()}
-        </p>
-    }
+    view! { <p class="text-white rounded-lg m-2 p-2 bg-red-600">{move || text()}</p> }
 }
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! {
-        <div class="p-4 mt-4 mr-4 ml-4 md-2 rounded-lg border-gray-600 border">
-            <p class="text-lg text-bold">{header}</p>
-            {children()}
+        <button
+            type="button"
+            on:click=|_| {
+                use_navigate()(first_button_path, NavigateOptions::default());
+            }
+        >
+            <div class="p-4 mt-4 mr-4 ml-4 md-2 text-justify rounded-lg border-gray-600 border">
+                <p class="text-lg text-bold">{header}</p>
+                {children()}
 
-            <ul class="flex flex-row gap-1 pt-2 overflow-x-auto">
-                {buttons
-                    .into_iter()
-                    .map(|(name, path)| {
-                        view! {
-                            <li class="bg-green-400/40 p-2 first:rounded-l-full last:rounded-r-full">
-                                <button on:click=move |_| {
-                                    use_navigate()(path, NavigateOptions::default());
-                                }>{name}</button>
-                            </li>
-                        }
-                    })
-                    .collect::<Vec<_>>()}
-            </ul>
-        </div>
+                <ul class="flex flex-row gap-1 pt-2 overflow-x-auto">
+                    {buttons
+                        .into_iter()
+                        .map(|(name, path)| {
+                            view! {
+                                <li class="bg-green-400/40 p-2 first:rounded-l-full last:rounded-r-full">
+                                    <button on:click=move |_| {
+                                        use_navigate()(path, NavigateOptions::default());
+                                    }>{name}</button>
+                                </li>
+                            }
+                        })
+                        .collect::<Vec<_>>()}
+                </ul>
+            </div>
+        </button>
     }
 }
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}
             </label>
 
-            <Show
-                when=move || {
-                    !autocomplete_signal.get().is_empty()
-                }
-                fallback=move || ()
-            >
+            <Show when=move || { !autocomplete_signal.get().is_empty() } fallback=move || ()>
                 <div class="\
                 absolute \
                 top-0 \
@@ -139,7 +134,7 @@ pub fn InputPlaceholder(
                                             provide_auto_completion(
                                                 auto_complete,
                                                 autocomplete_set,
-                                                reactive
+                                                reactive,
                                             )
                                         })
                                 }}
@@ -178,10 +173,10 @@ fn provide_auto_completion(
                                                 autocomplete_set.set(item2.clone());
                                                 reactive
                                                     .expect(
-                                                    "Should be set, \
-                                                    when autocomplete is used")
+                                                        "Should be set, \
+                                                    when autocomplete is used",
+                                                    )
                                                     .set(Some(item2.clone()));
-
                                                 info!("Set autocomplete to {item2}.");
                                             }
                                         >
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! {
-        <SiteHeader logo=icondata_io::IoArrowBack back_location="/" name="Inventory" />
-
-        <ul class="flex flex-col p-2 m-2">
-            {
-                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! {
-                                          <ul class="my-3">
-                                              <li class="m-2">{product.name}</li>
-                                              <li class="m-2">
-                                                  <span class="bg-gray-200 p-1 px-2 rounded-lg">
-                                                      {
-                                                        format!(
-                                                            "{} {}",
-                                                            amount.amount.value,
-                                                            unit.short_name
-                                                            )
-                                                        }
-                                                  </span>
-                                              </li>
-                                          </ul>
-                                      }
-                                    }
-                                }}
-                            }
-                        })
-                        .collect::<Vec<_>>()
-                    }
-                }
-            }
-        </ul>
-    }
-}
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 {
         <Container
             header="Inventory"
             buttons=vec![
-                (
-                    view! { <IconP icon=icondata_io::IoClipboardOutline text="Inventory" /> },
-                    "inventory",
-                ),
+                (view! { <IconP icon=icondata_io::IoClipboard text="Inventory" /> }, "inventory"),
                 (view! { <IconP icon=icondata_io::IoPricetags text="Consume" /> }, "consume"),
                 (view! { <IconP icon=icondata_io::IoStorefront text="Buy" /> }, "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! {
-                      <p>
-                          {format!(
-                              "You have {products_num} product{plural_s} \
-                                   in stock with a value \
-                                   of {products_value} {products_currency}.",
-                          )}
-                      </p>
-                  }
-            }
+                        view! {
+                            <p>
+                                {format!(
+                                    "You have {products_num} product{plural_s} \
+                                         in stock with a value \
+                                         of {products_value} {products_currency}.",
+                                )}
+                            </p>
+                        }
+                    }
                 }
             }
         </Container>
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! {
-        <Container header="Recipies" buttons=vec![("Mealplan", "mealplan")]>
+        <Container
+            header="Recipies"
+            buttons=vec![
+                (view! { <IconP icon=icondata_io::IoFastFood text="Recipies" /> }, "recipies"),
+                (view! { <IconP icon=icondata_io::IoCalendarSharp text="Mealplan" /> }, "mealplan"),
+            ]
+        >
             <p>"You have 0 recipies."</p>
         </Container>
     }
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! { <option value=value>{label}</option> }
-                            })
-                            .collect_view()
+                {move || {
+                    AsyncFetch! {
+                        @map_error_in_producer
+                        from_resource = options,
+                        producer = |options| {
+                            options
+                                .into_iter()
+                                .map(|(label, value)| {
+                                    view! { <option value=value>{label}</option> }
+                                })
+                                .collect_view()
+                        }
                     }
                 }}
             </select>
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! { <Buy /> }
                     }
                 />
+                <Route
+                    path=path!("/recipies")
+                    view=move || {
+                        view! { <Recipies /> }
+                    }
+                />
             </Routes>
         </Router>
     }
diff --git a/src/components/buy.rs b/src/pages/buy.rs
index cb4cff4..cb4cff4 100644
--- a/src/components/buy.rs
+++ b/src/pages/buy.rs
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! {
+        <SiteHeader logo=icondata_io::IoArrowBack back_location="/" name="Inventory" />
+
+        <ul class="flex flex-col p-2 m-2">
+            {
+                AsyncFetch! {
+                    @map_error_in_producer
+                    fetcher = get_products(),
+                    producer = render_products,
+                }
+            }
+        </ul>
+    }
+}
+
+fn render_products(products: Vec<Product>) -> 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::<Vec<_>>()
+}
+
+fn format_full_product((product, amount, unit): (Product, ProductAmount, Unit)) -> impl IntoView {
+    view! {
+        <ul class="my-3">
+            <li class="m-2">{product.name}</li>
+            <li class="m-2">
+                <span class="bg-gray-200 p-1 px-2 rounded-lg">
+                    {format!("{} {}", amount.amount.value, unit.short_name)}
+                </span>
+            </li>
+        </ul>
+    }
+}
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! { <SiteHeader logo=icondata_io::IoArrowBack back_location="/" name="Recipies" /> }
+}