diff options
Diffstat (limited to 'src/pages/associate_barcode.rs')
| -rw-r--r-- | src/pages/associate_barcode.rs | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/src/pages/associate_barcode.rs b/src/pages/associate_barcode.rs new file mode 100644 index 0000000..20714ff --- /dev/null +++ b/src/pages/associate_barcode.rs @@ -0,0 +1,163 @@ +use leptos::{ + IntoView, component, + prelude::{Get, Show, WriteSignal, signal}, + task::spawn_local, + view, +}; +use rocie_client::models::{Barcode, BarcodeId, Product, Unit, UnitAmount, UnitId}; +use rocie_macros::Form; +use uuid::Uuid; + +use crate::{ + api::{ + associate_barcode_external_wrapped, get_config, product_by_id_wrapped, + product_by_name_404_wrapped, product_by_name_external_wrapped, + product_suggestion_by_name_wrapped, unit_by_id_wrapped, unit_property_by_id_wrapped, + }, + components::{async_fetch::AsyncResource, banner::Banner, site_header::SiteHeader}, +}; + +#[component] +pub fn AssociateBarcode() -> impl IntoView { + let product_name_signal; + + let (errors, errors_set) = signal(None); + + let (show_units, show_units_set) = signal(false); + + view! { + <SiteHeader logo=icondata_io::IoPricetag back_location="/" name="Buy" /> + + <Show when=move || errors.get().is_some()> + <Banner text=move || errors.get().expect("Was some") /> + </Show> + + { + Form! { + on_submit = |barcode_id, product_name, amount, unit_id| { + let config = get_config!(); + + spawn_local(async move { + let output = async { + let product = product_by_name_external_wrapped(&config, &product_name).await?; + + associate_barcode_external_wrapped(&config, product.id, Barcode { + amount:UnitAmount { + unit: UnitId { value: unit_id }, + value: u32::from(amount), + }, + id: BarcodeId { value: barcode_id }, + }).await?; + + Ok::<_, leptos::error::Error>(()) + }; + + match output.await { + Ok(()) => (), + Err(err) => { + errors_set.set( + Some( + format!("Could not associate barcode: {err}") + ) + ); + }, + } + }); + }; + + <Input + name=barcode_id, + rust_type=u32, + html_type="number", + label="Barcode number", + /> + + <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 = product_suggestion_by_name_wrapped(&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 maybe_product: Option<Product> = product_by_name_404_wrapped(&product_name).await?; + + if let Some(product) = maybe_product { + let unit_property = + unit_property_by_id_wrapped(product_by_id_wrapped(product.id).await?.unit_property) + .await?; + + let mut units = Vec::with_capacity(unit_property.units.len()); + for unit_id in unit_property.units { + units.push(unit_by_id_wrapped(unit_id).await?); + } + + Ok(Some(units)) + } else { + Ok(None) + } + } else { + Ok(None) + } +} |
