From 4ed0b6f8fb6dd5da9779125c8e61bdcfaab64502 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Fri, 27 Sep 2024 19:42:35 -0400 Subject: [PATCH] feat: Allow restoring deleted inventory items --- web/src/app_state.rs | 7 ++ web/src/components/shopping_list.rs | 121 ++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/web/src/app_state.rs b/web/src/app_state.rs index 679313e..bb44012 100644 --- a/web/src/app_state.rs +++ b/web/src/app_state.rs @@ -86,6 +86,7 @@ pub enum Message { UpdateCategory(String, String, Option>), ResetInventory, AddFilteredIngredient(IngredientKey), + RemoveFilteredIngredient(IngredientKey), UpdateAmt(IngredientKey, String), SetUserData(UserData), SaveState(Option>), @@ -124,6 +125,9 @@ impl Debug for Message { Self::AddFilteredIngredient(arg0) => { f.debug_tuple("AddFilteredIngredient").field(arg0).finish() } + Self::RemoveFilteredIngredient(arg0) => { + f.debug_tuple("RemoveFilteredIngredient").field(arg0).finish() + } Self::UpdateAmt(arg0, arg1) => { f.debug_tuple("UpdateAmt").field(arg0).field(arg1).finish() } @@ -393,6 +397,9 @@ impl MessageMapper for StateMachine { Message::AddFilteredIngredient(key) => { original_copy.filtered_ingredients.insert(key); } + Message::RemoveFilteredIngredient(key) => { + original_copy.filtered_ingredients.remove(&key); + } Message::UpdateAmt(key, amt) => { original_copy.modified_amts.insert(key, amt); } diff --git a/web/src/components/shopping_list.rs b/web/src/components/shopping_list.rs index 4248a80..c83d5bf 100644 --- a/web/src/components/shopping_list.rs +++ b/web/src/components/shopping_list.rs @@ -19,6 +19,115 @@ use tracing::{debug, info, instrument}; use crate::app_state::{Message, StateHandler}; +#[instrument(skip_all)] +fn make_deleted_ingredients_rows<'ctx, G: Html>( + cx: Scope<'ctx>, + sh: StateHandler<'ctx>, + show_staples: &'ctx ReadSignal, +) -> View { + debug!("Making ingredients rows"); + let ingredients = sh.get_selector(cx, move |state| { + let state = state.get(); + let category_map = &state.category_map; + debug!("building ingredient list from state"); + let mut acc = IngredientAccumulator::new(); + for (id, count) in state.recipe_counts.iter() { + for _ in 0..(*count) { + acc.accumulate_from( + state + .recipes + .get(id) + .expect(&format!("No such recipe id exists: {}", id)), + ); + } + } + if *show_staples.get() { + if let Some(staples) = &state.staples { + acc.accumulate_ingredients_for("Staples", staples.iter()); + } + } + let mut ingredients = acc + .ingredients() + .into_iter() + // First we filter out any filtered ingredients + .filter(|(i, _)| state.filtered_ingredients.contains(i)) + // Then we take into account our modified amts + .map(|(k, (i, rs))| { + let category = category_map + .get(&i.name) + .cloned() + .unwrap_or_else(|| String::new()); + if state.modified_amts.contains_key(&k) { + ( + k.clone(), + ( + i.name, + i.form, + category, + state.modified_amts.get(&k).unwrap().clone(), + rs, + ), + ) + } else { + ( + k.clone(), + ( + i.name, + i.form, + category, + format!("{}", i.amt.normalize()), + rs, + ), + ) + } + }) + .collect::, String, String, BTreeSet), + )>>(); + ingredients.sort_by(|tpl1, tpl2| (&tpl1.1 .2, &tpl1.1 .0).cmp(&(&tpl2.1 .2, &tpl2.1 .0))); + ingredients + }); + view!( + cx, + Indexed( + iterable = ingredients, + view = move |cx, (k, (name, form, category, amt, rs))| { + let category = if category == "" { + "other".to_owned() + } else { + category + }; + let amt_signal = create_signal(cx, amt); + let k_clone = k.clone(); + let form = form.map(|form| format!("({})", form)).unwrap_or_default(); + let recipes = rs + .iter() + .fold(String::new(), |acc, s| format!("{}{},", acc, s)) + .trim_end_matches(",") + .to_owned(); + view! {cx, + tr { + td { + input(bind:value=amt_signal, class="width-5", type="text", on:change=move |_| { + sh.dispatch(cx, Message::UpdateAmt(k_clone.clone(), amt_signal.get_untracked().as_ref().clone())); + }) + } + td { + input(type="button", class="fit-content no-print", value="Undo", on:click={ + move |_| { + sh.dispatch(cx, Message::RemoveFilteredIngredient(k.clone())); + }}) + } + td { (name) " " (form) "" br {} "" (category) "" } + td { (recipes) } + } + } + } + ) + ) +} + #[instrument(skip_all)] fn make_ingredients_rows<'ctx, G: Html>( cx: Scope<'ctx>, @@ -188,6 +297,18 @@ fn make_shopping_table<'ctx, G: Html>( (make_extras_rows(cx, sh)) } } + ("Deleted Items") + table(class="pad-top shopping-list page-breaker container-fluid", role="grid") { + tr { + th { " Quantity " } + th { " Delete " } + th { " Ingredient " } + th { " Recipes " } + } + tbody { + (make_deleted_ingredients_rows(cx, sh, show_staples)) + } + } } }