app,server: Allow to edit recipe name
This commit is contained in:
parent
1a0ffb2d89
commit
47b547caf4
4 changed files with 203 additions and 19 deletions
|
|
@ -1,9 +1,18 @@
|
|||
use api::RecipeInfo;
|
||||
use api::{RecipeInfo, RecipeRenameRequest};
|
||||
use uuid::Uuid;
|
||||
use yew::{prelude::*, suspense::use_future};
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::{
|
||||
prelude::*,
|
||||
suspense::{use_future, use_future_with_deps},
|
||||
};
|
||||
use yew_router::prelude::*;
|
||||
|
||||
use crate::{api, RegaladeGlobalState, Route};
|
||||
use crate::{
|
||||
api,
|
||||
bootstrap::{FormModal, ModalToggleButton, bs},
|
||||
RegaladeGlobalState, Route,
|
||||
};
|
||||
|
||||
async fn get_all_recipes(
|
||||
token: String,
|
||||
|
|
@ -110,13 +119,139 @@ async fn fetch_recipe(token: String, household: Uuid, id: i64) -> anyhow::Result
|
|||
Ok(rsp.json().await?)
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
struct EditNameProps {
|
||||
token: String,
|
||||
household: Uuid,
|
||||
id: i64,
|
||||
name: String,
|
||||
update: Callback<()>,
|
||||
}
|
||||
|
||||
async fn do_rename_recipe(
|
||||
token: String,
|
||||
household: Uuid,
|
||||
recipe: i64,
|
||||
name: String,
|
||||
) -> anyhow::Result<()> {
|
||||
let rsp = gloo_net::http::Request::patch(api!("household/{household}/recipe/{recipe}"))
|
||||
.json(&RecipeRenameRequest { name })?
|
||||
.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(())
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn EditName(props: &EditNameProps) -> Html {
|
||||
let name = use_state(|| props.name.clone());
|
||||
|
||||
let nm = name.clone();
|
||||
let onchange = Callback::from(move |e: Event| {
|
||||
let Some(target) = e.target() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(target) = target.dyn_into::<HtmlInputElement>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
nm.set(target.value());
|
||||
});
|
||||
|
||||
let error = use_state(|| None::<String>);
|
||||
|
||||
let nm = name.clone();
|
||||
let err = error.clone();
|
||||
let token = props.token.clone();
|
||||
let household = props.household;
|
||||
let recipe = props.id;
|
||||
let update = props.update.clone();
|
||||
let on_submit = Callback::from(move |_| {
|
||||
if nm.is_empty() {
|
||||
err.set(Some("Name can't be empty".into()));
|
||||
return;
|
||||
}
|
||||
|
||||
let future = do_rename_recipe(token.clone(), household, recipe, (*nm).clone());
|
||||
|
||||
let err = err.clone();
|
||||
let update = update.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match future.await {
|
||||
Ok(_) => {
|
||||
let modal = bs::Modal::get_instance("#rcpEditName");
|
||||
modal.hide();
|
||||
|
||||
err.set(None);
|
||||
update.emit(());
|
||||
},
|
||||
Err(e) => {
|
||||
err.set(Some(format!("Could not edit name: {e}")));
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
html! {<>
|
||||
<ModalToggleButton classes={classes!("btn", "btn-secondary")} modal_id="rcpEditName">
|
||||
{"Edit name"}
|
||||
</ModalToggleButton>
|
||||
<FormModal
|
||||
id="rcpEditName"
|
||||
fade=true
|
||||
centered=true
|
||||
submit_label="Edit"
|
||||
title="Edit Name"
|
||||
{on_submit}
|
||||
>
|
||||
if let Some(e) = &*error {
|
||||
<div class={classes!("alert", "alert-danger")} role="alert">
|
||||
{e}
|
||||
</div>
|
||||
}
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control"
|
||||
id="rcpEditNameInp"
|
||||
placeholder={(*name).clone()}
|
||||
value={(*name).clone()}
|
||||
{onchange}
|
||||
/>
|
||||
<label for="rcpEditNameInp">{"Recipe Name"}</label>
|
||||
</div>
|
||||
</FormModal>
|
||||
</>}
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
|
||||
let recipe = use_future(|| fetch_recipe(props.token.clone(), props.household, props.id))?;
|
||||
let recipe_render = use_state(|| 0u64);
|
||||
let recipe = use_future_with_deps(
|
||||
|_| fetch_recipe(props.token.clone(), props.household, props.id),
|
||||
*recipe_render,
|
||||
)?;
|
||||
|
||||
let update = Callback::from(move |_| {
|
||||
recipe_render.set((*recipe_render).wrapping_add(1));
|
||||
});
|
||||
|
||||
Ok(match &*recipe {
|
||||
Ok(r) => html! {<>
|
||||
<h1>{&r.name}</h1>
|
||||
<EditName
|
||||
token={props.token.clone()}
|
||||
id={props.id}
|
||||
household={props.household}
|
||||
name={r.name.clone()}
|
||||
{update}
|
||||
/>
|
||||
<hr />
|
||||
<div class="text-start">
|
||||
<h2>{"Ingredients"}</h2>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue