diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/async_fetch.rs | 32 | ||||
| -rw-r--r-- | src/components/banner.rs | 6 | ||||
| -rw-r--r-- | src/components/buy.rs | 193 | ||||
| -rw-r--r-- | src/components/container.rs | 47 | ||||
| -rw-r--r-- | src/components/input_placeholder.rs | 15 | ||||
| -rw-r--r-- | src/components/inventory.rs | 55 | ||||
| -rw-r--r-- | src/components/mod.rs | 4 | ||||
| -rw-r--r-- | src/components/product_overview.rs | 38 | ||||
| -rw-r--r-- | src/components/recipies.rs | 10 | ||||
| -rw-r--r-- | src/components/select_placeholder.rs | 22 |
10 files changed, 85 insertions, 337 deletions
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/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! { - <SiteHeader logo=icondata_io::IoPricetag back_location="/" name="Buy" /> - - <Show when=move || on_submit_errored.get().is_some()> - <Banner text=move || on_submit_errored.get().expect("Should be some") /> - </Show> - - { - Form! { - on_submit = |barcode_number, amount| { - let config = expect_context::<Store<ConfigState>>(); - 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"); - } - - }); - }; - - <Input - name=barcode_number, - rust_type=u32, - html_type="number", - label="Barcode Number", - /> - - <Input - name=amount, - rust_type=u16, - html_type="number", - label="Amount" - /> - } - } - } -} - -#[component] -pub fn AssociateBarcode() -> impl IntoView { - let product_name_signal; - - let (show_units, show_units_set) = signal(false); - - view! { - <SiteHeader logo=icondata_io::IoPricetag back_location="/" name="Buy" /> - - { - 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()); - }); - }; - - <Input - name=product_name, - rust_type=String, - html_type="text", - label="Product Name", - reactive=product_name_signal - auto_complete=generate_suggest_products - /> - - <Show - when=move || show_units.get(), - > - <Select - name=unit_id, - rust_type=Uuid, - label="Unit", - options=AsyncResource! { - ( - product_name: Option<String> = product_name_signal(), - show_units_set: WriteSignal<bool> = show_units_set - ) -> Result<Vec<(String, String)>, leptos::error::Error> { - let units = product_unit_fetcher(product_name).await?; - - show_units_set.set(units.is_some()); - if let Some(units) = units { - Ok( - units - .into_iter() - .map(|unit| (unit.full_name_singular, unit.id.to_string())) - .collect() - ) - } else { - Ok(vec![]) - } - } - }, - /> - </Show> - - <Input - name=amount, - rust_type=u16, - html_type="number", - label="Amount" - /> - } - } - } -} - -async fn generate_suggest_products( - optional_product_name: Option<String>, -) -> Result<Option<Vec<String>>, 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<String>, -) -> Result<Option<Vec<Unit>>, leptos::error::Error> { - if let Some(product_name) = optinal_product_name - && !product_name.is_empty() - { - let value: Option<Product> = { - 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! { - <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> |
