1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
// rocie - An enterprise grocery management system - Web app
//
// Copyright (C) 2026 Benedikt Peetz <benedikt.peetz@b-peetz.de>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This file is part of Rocie.
//
// You should have received a copy of the License along with this program.
// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
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>
}
}
|