diff --git a/api/src/lib.rs b/api/src/lib.rs
index b0bd6c1..30fdfc9 100644
--- a/api/src/lib.rs
+++ b/api/src/lib.rs
@@ -129,3 +129,8 @@ pub struct AddRecipeIngredientRequest {
pub struct RecipeEditStepsRequest {
pub steps: String
}
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct RecipeEditRating {
+ pub rating: u8,
+}
diff --git a/app/src/recipe.rs b/app/src/recipe.rs
index b6e9478..5d161e5 100644
--- a/app/src/recipe.rs
+++ b/app/src/recipe.rs
@@ -1,6 +1,6 @@
use api::{
- AddRecipeIngredientRequest, IngredientInfo, RecipeEditStepsRequest, RecipeInfo,
- RecipeIngredientEditRequest, RecipeRenameRequest,
+ AddRecipeIngredientRequest, IngredientInfo, RecipeEditRating, RecipeEditStepsRequest,
+ RecipeInfo, RecipeIngredientEditRequest, RecipeRenameRequest,
};
use itertools::Itertools;
use uuid::Uuid;
@@ -19,6 +19,21 @@ use crate::{
RegaladeGlobalState, Route,
};
+#[derive(Debug, Clone, PartialEq, Properties)]
+struct RecipeRatingProps {
+ rating: u8,
+}
+
+#[function_component]
+fn RecipeRating(props: &RecipeRatingProps) -> Html {
+ let rating = (props.rating + 1).min(3);
+ html! {
+
- {for l.recipes.iter().sorted_by_key(|(_, name, _)| name).map(|(id, name, _)| html!{
+ {for l.recipes.iter().sorted_by_key(|(_, name, _)| name).map(|(id, name, rating)| html!{
@@ -64,6 +79,7 @@ fn RecipeListInner(props: &RecipeListProps) -> HtmlResult {
>
{name}
>
+
})}
@@ -623,19 +639,119 @@ fn EditSteps(props: &EditStepsProps) -> Html {
>}
}
-#[derive(Debug, Clone, PartialEq, Properties)]
-struct RecipeRatingProps {
+async fn do_edit_rating(
+ token: String,
+ household: Uuid,
+ recipe: i64,
rating: u8,
+) -> anyhow::Result<()> {
+ let rsp = gloo_net::http::Request::patch(api!("household/{household}/recipe/{recipe}/rating"))
+ .json(&RecipeEditRating { rating })?
+ .header("Authorization", &format!("Bearer {token}"))
+ .send()
+ .await?;
+
+ if !rsp.ok() {
+ let body = rsp.text().await.unwrap_or_default();
+ anyhow::bail!("Could not edit rating (code={}): {body}", rsp.status());
+ }
+
+ Ok(())
+}
+
+#[derive(Properties, PartialEq, Clone)]
+struct EditRatingProps {
+ token: String,
+ household: Uuid,
+ recipe: i64,
+ rating: u8,
+ update: Callback<()>,
}
#[function_component]
-fn RecipeRating(props: &RecipeRatingProps) -> Html {
- let rating = (props.rating + 1).min(3);
- html! {
-
- { for (0..rating).map(|_| html!{}) }
-
- }
+fn EditRating(props: &EditRatingProps) -> Html {
+ let rating = use_state(|| props.rating);
+
+ let error = use_state(|| None::
);
+
+ let onchange = {
+ let rating = rating.clone();
+ Callback::from(move |e: Event| {
+ let Some(target) = e.target() else {
+ return;
+ };
+
+ let Ok(target) = target.dyn_into::() else {
+ return;
+ };
+
+ if !target.report_validity() {
+ return;
+ }
+
+ rating.set(target.value().parse().expect("invalid number"));
+ })
+ };
+ let on_submit = {
+ let rating = rating.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 rating = rating.clone();
+ let error = error.clone();
+ let update = update.clone();
+
+ wasm_bindgen_futures::spawn_local(async move {
+ match do_edit_rating(token.clone(), household, recipe, *rating - 1).await {
+ Ok(_) => {
+ let modal = bs::Modal::get_instance("#rcpEditRating");
+ modal.hide();
+ error.set(None);
+ update.emit(());
+ }
+ Err(e) => {
+ error.set(Some(format!("Could not edit rating: {e}")));
+ }
+ }
+ });
+ })
+ };
+
+ html! {<>
+
+ {"Edit Rating"}
+
+
+ if let Some(e) = &*error {
+
+ {e}
+
+ }
+
+
+
+
+
+ >}
}
#[function_component]
@@ -686,8 +802,19 @@ fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
name={r.name.clone()}
update={update.clone()}
/>
-
-
+
if let Some(e) = &*error {
diff --git a/src/routes/mod.rs b/src/routes/mod.rs
index e01e649..c983bd9 100644
--- a/src/routes/mod.rs
+++ b/src/routes/mod.rs
@@ -232,6 +232,10 @@ pub(crate) fn router(api_allowed: Option
) -> Router {
"/household/:house_id/recipe/:recipe_id/steps",
patch(recipe::edit_step).layer(mk_service(vec![Method::PATCH])),
)
+ .route(
+ "/household/:house_id/recipe/:recipe_id/rating",
+ patch(recipe::edit_rating).layer(mk_service(vec![Method::PATCH])),
+ )
.route(
"/household/:house_id/recipe/:recipe_id/ingredients/:iid",
patch(recipe::edit_ig_amount)
diff --git a/src/routes/recipe.rs b/src/routes/recipe.rs
index 559748f..d2593e9 100644
--- a/src/routes/recipe.rs
+++ b/src/routes/recipe.rs
@@ -1,6 +1,6 @@
use api::{
AddRecipeIngredientRequest, CreateRecipeRequest, CreateRecipeResponse, EmptyResponse,
- IngredientInfo, ListRecipesResponse, RecipeEditStepsRequest, RecipeInfo,
+ IngredientInfo, ListRecipesResponse, RecipeEditRating, RecipeEditStepsRequest, RecipeInfo,
RecipeIngredientEditRequest, RecipeRenameRequest,
};
use axum::{
@@ -227,3 +227,19 @@ pub(super) async fn edit_step(
Ok(EmptyResponse {}.into())
}
+
+pub(super) async fn edit_rating(
+ State(state): State,
+ RecipeExtractor(recipe): RecipeExtractor,
+ Json(req): Json,
+) -> JsonResult {
+ let model = recipe::ActiveModel {
+ id: ActiveValue::Set(recipe.id),
+ ranking: ActiveValue::Set(req.rating as _),
+ ..Default::default()
+ };
+
+ model.update(&state.db).await?;
+
+ Ok(EmptyResponse {}.into())
+}