summary refs log tree commit diff stats
path: root/src/pages/create_product_parent.rs
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-03-19 07:45:14 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-03-19 07:45:14 +0100
commitf6a3fb9c4d8dd86f78c9f75a23c1ac35bf35d4eb (patch)
tree5f28fbca03d83921b568f7cb1708374456d9ec42 /src/pages/create_product_parent.rs
parentfeat(treewide): Add further buttons (diff)
downloadweb-client-f6a3fb9c4d8dd86f78c9f75a23c1ac35bf35d4eb.zip
feat(treewide): Commit MVP
Diffstat (limited to 'src/pages/create_product_parent.rs')
-rw-r--r--src/pages/create_product_parent.rs126
1 files changed, 126 insertions, 0 deletions
diff --git a/src/pages/create_product_parent.rs b/src/pages/create_product_parent.rs
new file mode 100644
index 0000000..152347a
--- /dev/null
+++ b/src/pages/create_product_parent.rs
@@ -0,0 +1,126 @@
+use std::{convert::Infallible, iter, str::FromStr};
+
+use leptos::{
+    IntoView, component,
+    prelude::{Get, Show, signal},
+    task::spawn_local,
+    view,
+};
+use leptos_router::{NavigateOptions, hooks::use_navigate};
+use rocie_client::models::{ProductParentId, ProductParentStub};
+use rocie_macros::Form;
+
+use crate::{
+    api::{get_config, product_parents_wrapped, register_product_parent_external_wrapped},
+    components::{
+        async_fetch::AsyncResource, banner::Banner, catch_errors::CatchErrors,
+        login_wall::LoginWall, site_header::SiteHeader,
+    },
+};
+
+struct OptionalString(Option<String>);
+
+impl FromStr for OptionalString {
+    type Err = Infallible;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if s.is_empty() {
+            Ok(Self(None))
+        } else {
+            Ok(Self(Some(s.to_owned())))
+        }
+    }
+}
+
+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 CreateProductParent() -> impl IntoView {
+    let (error_message, error_message_set) = signal(None);
+
+    view! {
+        <CatchErrors>
+            <LoginWall back=move || "/create-product-parent".to_owned()>
+                <SiteHeader
+                    logo=icondata_io::IoArrowBack
+                    back_location="/"
+                    name="Create Product Parent"
+                />
+
+                <Show when=move || error_message.get().is_some()>
+                    <Banner text=move || error_message.get().expect("Is some") />
+                </Show>
+
+                {
+                    Form! {
+                        on_submit = |name, description, parent| {
+                    let config = get_config!();
+                    let navigate = use_navigate();
+
+                    spawn_local(async move {
+                        match register_product_parent_external_wrapped(&config, ProductParentStub {
+                                description: description.0.map(|d| d.trim().to_owned()),
+                                name: name.trim().to_owned(),
+                                parent: parent.0,
+                            }
+                        ).await {
+                            Ok(_id) => {
+                                navigate("/create-product-parent", NavigateOptions::default());
+                            }
+                            Err(err) => error_message_set.set(Some(format!("Failed to create product: {err}"))),
+                        }
+                    });
+                };
+
+                <Input
+                    name=name,
+                    rust_type=String,
+                    html_type="text",
+                    label="Product Name",
+                />
+
+                <Input
+                    name=description,
+                    rust_type=OptionalString,
+                    html_type="text",
+                    label="Product Description"
+                />
+
+                <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()
+                            )
+                        }
+                    },
+                />
+                    }
+                }
+            </LoginWall>
+        </CatchErrors>
+    }
+}