summary refs log tree commit diff stats
path: root/src/pages/create_recipe.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_recipe.rs
parentfeat(treewide): Add further buttons (diff)
downloadweb-client-f6a3fb9c4d8dd86f78c9f75a23c1ac35bf35d4eb.zip
feat(treewide): Commit MVP
Diffstat (limited to 'src/pages/create_recipe.rs')
-rw-r--r--src/pages/create_recipe.rs108
1 files changed, 108 insertions, 0 deletions
diff --git a/src/pages/create_recipe.rs b/src/pages/create_recipe.rs
new file mode 100644
index 0000000..20ec4ed
--- /dev/null
+++ b/src/pages/create_recipe.rs
@@ -0,0 +1,108 @@
+use std::{iter, str::FromStr};
+
+use leptos::{
+    IntoView, component,
+    prelude::{Get, Show, signal},
+    task::spawn_local,
+    view,
+};
+use leptos_router::{NavigateOptions, hooks::use_navigate};
+use log::info;
+use rocie_client::models::{RecipeParentId, RecipeStub};
+use rocie_macros::Form;
+
+use crate::{
+    api::{add_recipe_external_wrapped, get_config, recipe_parents_wrapped},
+    components::{
+        async_fetch::AsyncResource, banner::Banner, catch_errors::CatchErrors,
+        login_wall::LoginWall, site_header::SiteHeader,
+    },
+    pages::create_product::OptionalString,
+};
+
+struct OptionalParentId(Option<RecipeParentId>);
+
+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(RecipeParentId { value: s.parse()? })))
+        }
+    }
+}
+
+#[component]
+pub fn CreateRecipe() -> impl IntoView {
+    let (error_message, error_message_set) = signal(None);
+
+    view! {
+        <CatchErrors>
+            <LoginWall back=move || "/create-recipe".to_owned()>
+                <SiteHeader logo=icondata_io::IoArrowBack back_location="/" name="Create Recipe" />
+
+                <Show when=move || error_message.get().is_some()>
+                    <Banner text=move || error_message.get().expect("Is some") />
+                </Show>
+
+                {
+                    Form! {
+                        on_submit = |content, name, parent| {
+                        let config = get_config!();
+                        let navigate = use_navigate();
+
+                        spawn_local(async move {
+                            match add_recipe_external_wrapped(&config, RecipeStub {
+                                content: content.trim().to_owned(),
+                                name: name.trim().to_owned(),
+                                parent: parent.0,
+                            }).await {
+                                Ok(_id) => {
+                                    info!("Navigating");
+                                    navigate("/create-recipe", NavigateOptions::default());
+                                }
+                                Err(err) => error_message_set.set(Some(format!("Failed to create recipe: {err}"))),
+                            }
+                        });
+                    };
+
+                <Input
+                    name=name,
+                    rust_type=String,
+                    html_type="text",
+                    label="Recipe Name",
+                />
+
+                <Select
+                    name=parent,
+                    rust_type=OptionalParentId,
+                    label="Parent",
+                    options=AsyncResource! {
+                        () -> Result<Vec<(String, String)>, leptos::error::Error> {
+                            let parents = recipe_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()
+                            )
+                        }
+                    },
+                />
+
+                <Textarea
+                    name=content,
+                    label="Recipe",
+                />
+                    }
+                }
+            </LoginWall>
+        </CatchErrors>
+    }
+}