app,server: Allow to add ingredients to existing recipes

This commit is contained in:
traxys 2023-06-25 18:48:49 +02:00
parent 6004520fb9
commit 27f9295aa3
5 changed files with 319 additions and 69 deletions

View file

@ -1,4 +1,7 @@
use api::{RecipeInfo, RecipeIngredientEditRequest, RecipeRenameRequest};
use api::{
AddRecipeIngredientRequest, IngredientInfo, RecipeInfo, RecipeIngredientEditRequest,
RecipeRenameRequest,
};
use itertools::Itertools;
use uuid::Uuid;
use wasm_bindgen::JsCast;
@ -12,6 +15,7 @@ use yew_router::prelude::*;
use crate::{
api,
bootstrap::{bs, ConfirmDangerModal, FormModal, ModalToggleButton},
recipe_creator::IngredientSelectBase,
RegaladeGlobalState, Route,
};
@ -350,6 +354,150 @@ fn EditIngredient(props: &EditIngredientProps) -> Html {
</>}
}
async fn do_add_ingredient_recipe(
token: String,
household: Uuid,
recipe: i64,
ingredient: i64,
amount: f64,
) -> anyhow::Result<()> {
let rsp = gloo_net::http::Request::put(api!(
"household/{household}/recipe/{recipe}/ingredients/{ingredient}"
))
.json(&AddRecipeIngredientRequest { amount })?
.header("Authorization", &format!("Bearer {token}"))
.send()
.await?;
if !rsp.ok() {
let body = rsp.text().await.unwrap_or_default();
anyhow::bail!("Could not get recipes (code={}): {body}", rsp.status());
}
Ok(())
}
#[derive(Clone, PartialEq, Properties)]
struct AddIngredientProps {
token: String,
household: Uuid,
recipe: i64,
update: Callback<()>,
}
#[function_component]
fn AddIngredientInner(props: &AddIngredientProps) -> HtmlResult {
let error = use_state(|| None::<String>);
let amount = use_state(|| None::<f64>);
let selected_ig = use_state(|| None::<(i64, IngredientInfo)>);
let s_ig = selected_ig.clone();
let am = amount.clone();
let err = error.clone();
let token = props.token.clone();
let household = props.household;
let recipe = props.recipe;
let update = props.update.clone();
let on_submit = Callback::from(move |_| match &*s_ig {
&Some((id, _)) => match &*am {
&Some(amount) => {
let fut = do_add_ingredient_recipe(token.clone(), household, recipe, id, amount);
let am = am.clone();
let s_ig = s_ig.clone();
let err = err.clone();
let update = update.clone();
wasm_bindgen_futures::spawn_local(async move {
match fut.await {
Ok(_) => {
err.set(None);
am.set(None);
s_ig.set(None);
update.emit(());
let modal = bs::Modal::get_instance("#rcpEditNewIg");
modal.hide();
}
Err(e) => {
err.set(Some(format!("Could not add ingredient: {e}")));
},
}
});
}
None => {
err.set(Some("Amount can't be empty".into()));
}
},
None => {
err.set(Some("Ingredient does not exist".into()));
}
});
let on_ig_change = {
let selected_ig = selected_ig.clone();
Callback::from(move |v| {
selected_ig.set(v);
})
};
let on_amount_change = {
let amount = amount.clone();
Callback::from(move |v| {
amount.set(Some(v));
})
};
Ok({
html! {<>
<FormModal
id="rcpEditNewIg"
fade=true
centered=true
submit_label="Add"
{on_submit}
title="Add ingredient"
>
if let Some(e) = &*error {
<div class={classes!("alert", "alert-danger")} role="alert">
{e}
</div>
}
<IngredientSelectBase
token={props.token.clone()}
household={props.household}
{on_ig_change}
{on_amount_change}
amount={*amount}
ig_select={(*selected_ig).as_ref().map(|(_, info)| AttrValue::from(info.name.clone()))}
/>
</FormModal>
<ModalToggleButton modal_id="rcpEditNewIg" classes={classes!("btn", "btn-secondary")}>
{"Add Ingredient"}
</ModalToggleButton>
</>}
})
}
#[function_component]
fn AddIngredient(props: &AddIngredientProps) -> Html {
let fallback = html! {
<div class="spinner-border" role="status">
<span class="visually-hidden">{"Loading ..."}</span>
</div>
};
html! {<>
<Suspense {fallback}>
<AddIngredientInner
token={props.token.clone()}
household={props.household}
recipe={props.recipe}
update={props.update.clone()}
/>
</Suspense>
</>}
}
async fn do_delete_ig(
token: String,
household: Uuid,
@ -427,7 +575,7 @@ fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
<hr />
<div class="text-start">
<h2>{"Ingredients"}</h2>
<ul class="list-group">
<ul class="list-group mb-2">
{for r.ingredients.iter().map(|(id, info, amount)| {
let delete_modal_id = format!("rcpRmIg{id}");
html!{
@ -463,6 +611,12 @@ fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
</li>
}})}
</ul>
<AddIngredient
token={props.token.clone()}
household={props.household}
recipe={props.id}
update={update.clone()}
/>
</div>
<hr />
<div class="text-start">