app,server: Allow to edit steps

This commit is contained in:
traxys 2023-06-25 22:02:58 +02:00
parent 0c7e52bd8b
commit 1ea5c8aafa
4 changed files with 141 additions and 5 deletions

View file

@ -123,3 +123,8 @@ pub struct RecipeIngredientEditRequest {
pub struct AddRecipeIngredientRequest {
pub amount: f64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct RecipeEditStepsRequest {
pub steps: String
}

View file

@ -1,11 +1,11 @@
use api::{
AddRecipeIngredientRequest, IngredientInfo, RecipeInfo, RecipeIngredientEditRequest,
RecipeRenameRequest,
AddRecipeIngredientRequest, IngredientInfo, RecipeEditStepsRequest, RecipeInfo,
RecipeIngredientEditRequest, RecipeRenameRequest,
};
use itertools::Itertools;
use uuid::Uuid;
use wasm_bindgen::JsCast;
use web_sys::HtmlInputElement;
use web_sys::{HtmlInputElement, HtmlTextAreaElement};
use yew::{
prelude::*,
suspense::{use_future, use_future_with_deps},
@ -519,6 +519,110 @@ async fn do_delete_ig(
Ok(())
}
async fn do_edit_steps(
token: String,
household: Uuid,
recipe: i64,
steps: String,
) -> anyhow::Result<()> {
let rsp = gloo_net::http::Request::patch(api!("household/{household}/recipe/{recipe}/steps"))
.json(&RecipeEditStepsRequest { steps })?
.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(Properties, PartialEq, Clone)]
struct EditStepsProps {
token: String,
household: Uuid,
recipe: i64,
steps: String,
update: Callback<()>,
}
#[function_component]
fn EditSteps(props: &EditStepsProps) -> Html {
let steps = use_state(|| props.steps.clone());
let error = use_state(|| None::<String>);
let onchange = {
let steps = steps.clone();
Callback::from(move |e: Event| {
let Some(target) = e.target() else {
return;
};
let Ok(target) = target.dyn_into::<HtmlTextAreaElement>() else {
return;
};
steps.set(target.value());
})
};
let on_submit = {
let steps = steps.clone();
let token = props.token.clone();
let household = props.household;
let recipe = props.recipe;
let error = error.clone();
let update = props.update.clone();
Callback::from(move |_| {
let token = token.clone();
let steps = steps.clone();
let error = error.clone();
let update = update.clone();
wasm_bindgen_futures::spawn_local(async move {
match do_edit_steps(token.clone(), household, recipe, (*steps).clone()).await {
Ok(_) => {
let modal = bs::Modal::get_instance("#rcpEditSteps");
modal.hide();
error.set(None);
update.emit(());
}
Err(e) => {
error.set(Some(format!("Could not edit steps: {e}")));
}
}
});
})
};
html! {<>
<ModalToggleButton modal_id="rcpEditSteps" classes={classes!("btn", "btn-secondary", "mb-2")}>
{"Edit Steps"}
</ModalToggleButton>
<FormModal
id="rcpEditSteps"
fade=true
centered=true
submit_label="Edit"
title="Edit steps"
{on_submit}
>
if let Some(e) = &*error {
<div class={classes!("alert", "alert-danger")} role="alert">
{e}
</div>
}
<textarea
class="form-control"
value={(*steps).clone()}
{onchange}
/>
</FormModal>
</>}
}
#[function_component]
fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
let recipe_render = use_state(|| 0u64);
@ -626,6 +730,13 @@ fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
<li class="list-group-item">{text}</li>
})}
</ul>
<EditSteps
token={props.token.clone()}
household={props.household}
recipe={props.id}
steps={r.steps.clone()}
update={update}
/>
</div>
</>},
Err(e) => html! {

View file

@ -228,6 +228,10 @@ pub(crate) fn router(api_allowed: Option<HeaderValue>) -> Router<AppState> {
.patch(recipe::edit_name)
.layer(mk_service(vec![Method::GET, Method::PATCH])),
)
.route(
"/household/:house_id/recipe/:recipe_id/steps",
patch(recipe::edit_step).layer(mk_service(vec![Method::PATCH])),
)
.route(
"/household/:house_id/recipe/:recipe_id/ingredients/:iid",
patch(recipe::edit_ig_amount)

View file

@ -1,7 +1,7 @@
use api::{
AddRecipeIngredientRequest, CreateRecipeRequest, CreateRecipeResponse, EmptyResponse,
IngredientInfo, ListRecipesResponse, RecipeInfo, RecipeIngredientEditRequest,
RecipeRenameRequest,
IngredientInfo, ListRecipesResponse, RecipeEditStepsRequest, RecipeInfo,
RecipeIngredientEditRequest, RecipeRenameRequest,
};
use axum::{
async_trait,
@ -210,3 +210,19 @@ pub(super) async fn add_ig_request(
Ok(EmptyResponse {}.into())
}
pub(super) async fn edit_step(
State(state): State<AppState>,
RecipeExtractor(recipe): RecipeExtractor,
Json(req): Json<RecipeEditStepsRequest>,
) -> JsonResult<EmptyResponse> {
let model = recipe::ActiveModel {
id: ActiveValue::Set(recipe.id),
steps: ActiveValue::Set(req.steps),
..Default::default()
};
model.update(&state.db).await?;
Ok(EmptyResponse {}.into())
}