summary refs log tree commit diff stats
path: root/src/pages/create_product.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages/create_product.rs')
-rw-r--r--src/pages/create_product.rs109
1 files changed, 79 insertions, 30 deletions
diff --git a/src/pages/create_product.rs b/src/pages/create_product.rs
index fcd3b0b..fdf8f28 100644
--- a/src/pages/create_product.rs
+++ b/src/pages/create_product.rs
@@ -1,4 +1,4 @@
-use std::{convert::Infallible, str::FromStr};
+use std::{convert::Infallible, iter, str::FromStr};
 
 use leptos::{
     IntoView, component,
@@ -6,16 +6,23 @@ use leptos::{
     task::spawn_local,
     view,
 };
-use rocie_client::models::{ProductStub, UnitPropertyId};
+use leptos_router::{NavigateOptions, hooks::use_navigate};
+use rocie_client::models::{ProductParentId, ProductStub, UnitPropertyId};
 use rocie_macros::Form;
 use uuid::Uuid;
 
 use crate::{
-    api::{get_config, register_product_external_wrapped, unit_properties_wrapped},
-    components::{async_fetch::AsyncResource, banner::Banner, site_header::SiteHeader},
+    api::{
+        get_config, product_parents_wrapped, register_product_external_wrapped,
+        unit_properties_wrapped,
+    },
+    components::{
+        async_fetch::AsyncResource, banner::Banner, catch_errors::CatchErrors,
+        login_wall::LoginWall, site_header::SiteHeader,
+    },
 };
 
-struct OptionalString(Option<String>);
+pub(crate) struct OptionalString(pub(crate) Option<String>);
 
 impl FromStr for OptionalString {
     type Err = Infallible;
@@ -29,35 +36,54 @@ impl FromStr for OptionalString {
     }
 }
 
+struct OptionalParentId(Option<ProductParentId>);
+
+impl FromStr for OptionalParentId {
+    type Err = uuid::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if s.is_empty() {
+            Ok(Self(None))
+        } else {
+            Ok(Self(Some(ProductParentId { value: s.parse()? })))
+        }
+    }
+}
+
 #[component]
 pub fn CreateProduct() -> impl IntoView {
     let (error_message, error_message_set) = signal(None);
 
     view! {
-        <SiteHeader logo=icondata_io::IoArrowBack back_location="/" name="Create Product" />
-
-        <Show when=move || error_message.get().is_some()>
-            <Banner text=move || error_message.get().expect("Is some") />
-        </Show>
-
-        {
-            Form! {
-                on_submit = |product_name, product_description, unit_property_id| {
-                    let config = get_config!();
-
-                    spawn_local(async move {
-                        match register_product_external_wrapped(&config, ProductStub {
-                                description: Some(product_description.0),
-                                name: product_name,
-                                parent: None, // TODO: Add this <2025-10-25>
-                                unit_property: UnitPropertyId { value: unit_property_id },
+        <CatchErrors>
+            <LoginWall back=move || "/create-product".to_owned()>
+                <SiteHeader logo=icondata_io::IoArrowBack back_location="/" name="Create Product" />
+
+                <Show when=move || error_message.get().is_some()>
+                    <Banner text=move || error_message.get().expect("Is some") />
+                </Show>
+
+                {
+                    Form! {
+                        on_submit = |product_name, product_description, unit_property_id, parent| {
+                        let config = get_config!();
+                        let navigate = use_navigate();
+
+                        spawn_local(async move {
+                            match register_product_external_wrapped(&config, ProductStub {
+                                    description: product_description.0.map(|d| d.trim().to_owned()),
+                                    name: product_name.trim().to_owned(),
+                                    parent: parent.0,
+                                    unit_property: UnitPropertyId { value: unit_property_id },
+                                }
+                            ).await {
+                                Ok(_id) => {
+                                    navigate("/create-product", NavigateOptions::default());
+                                }
+                                Err(err) => error_message_set.set(Some(format!("Failed to create product: {err}"))),
                             }
-                        ).await {
-                            Ok(_id) => {}
-                            Err(err) => error_message_set.set(Some(format!("Failed to create product: {err}"))),
-                        }
-                    });
-                };
+                        });
+                    };
 
                 <Input
                     name=product_name,
@@ -74,6 +100,27 @@ pub fn CreateProduct() -> impl IntoView {
                 />
 
                 <Select
+                    name=parent,
+                    rust_type=OptionalParentId,
+                    label="Parent",
+                    options=AsyncResource! {
+                        () -> Result<Vec<(String, String)>, leptos::error::Error> {
+                            let parents = product_parents_wrapped().await?;
+
+                            Ok(
+                                iter::once(("No parent".to_owned(), String::new()))
+                                    .chain(
+                                        parents
+                                            .into_iter()
+                                            .map(|prop| (prop.name, prop.id.to_string()))
+                                    )
+                                    .collect()
+                            )
+                        }
+                    },
+                />
+
+                <Select
                     name=unit_property_id,
                     rust_type=Uuid,
                     label="Unit property",
@@ -90,7 +137,9 @@ pub fn CreateProduct() -> impl IntoView {
                         }
                     },
                 />
-            }
-        }
+                    }
+                }
+            </LoginWall>
+        </CatchErrors>
     }
 }