summary refs log tree commit diff stats
path: root/src/pages/associate_barcode.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages/associate_barcode.rs')
-rw-r--r--src/pages/associate_barcode.rs163
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)
+    }
+}